]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpHeader.cc
Fix for recent "clientReplyContext keeps a FwdState refcount" patch.
[thirdparty/squid.git] / src / HttpHeader.cc
CommitLineData
4f7e9dbb 1
cb69b4c7 2/*
ead5ed09 3 * $Id: HttpHeader.cc,v 1.115 2006/05/05 20:13:04 wessels Exp $
cb69b4c7 4 *
123abbe1 5 * DEBUG: section 55 HTTP Header
cb69b4c7 6 * AUTHOR: Alex Rousskov
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
cb69b4c7 10 *
2b6662ba 11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
cb69b4c7 19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
cb69b4c7 34 */
35
36#include "squid.h"
e6ccf245 37#include "Store.h"
38#include "HttpHeader.h"
528b2c61 39#include "HttpHdrContRange.h"
25b6a907 40#include "HttpHdrSc.h"
0eb49b6d 41#include "MemBuf.h"
cb69b4c7 42
43/*
2ac76861 44 * On naming conventions:
45 *
46 * HTTP/1.1 defines message-header as
47 *
48 * message-header = field-name ":" [ field-value ] CRLF
49 * field-name = token
50 * field-value = *( field-content | LWS )
51 *
52 * HTTP/1.1 does not give a name name a group of all message-headers in a message.
53 * Squid 1.1 seems to refer to that group _plus_ start-line as "headers".
54 *
55 * HttpHeader is an object that represents all message-headers in a message.
56 * HttpHeader does not manage start-line.
57 *
d8b249ef 58 * HttpHeader is implemented as a collection of header "entries".
59 * An entry is a (field_id, field_name, field_value) triplet.
2ac76861 60 */
cb69b4c7 61
62
cb69b4c7 63/*
64 * local constants and vars
65 */
66
67/*
68 * A table with major attributes for every known field.
69 * We calculate name lengths and reorganize this array on start up.
70 * After reorganization, field id can be used as an index to the table.
71 */
de336bbe 72static const HttpHeaderFieldAttrs HeadersAttrs[] =
62e76326 73 {
74 {"Accept", HDR_ACCEPT, ftStr},
75
76 {"Accept-Charset", HDR_ACCEPT_CHARSET, ftStr},
77 {"Accept-Encoding", HDR_ACCEPT_ENCODING, ftStr},
78 {"Accept-Language", HDR_ACCEPT_LANGUAGE, ftStr},
79 {"Accept-Ranges", HDR_ACCEPT_RANGES, ftStr},
80 {"Age", HDR_AGE, ftInt},
81 {"Allow", HDR_ALLOW, ftStr},
82 {"Authorization", HDR_AUTHORIZATION, ftStr}, /* for now */
83 {"Cache-Control", HDR_CACHE_CONTROL, ftPCc},
84 {"Connection", HDR_CONNECTION, ftStr},
85 {"Content-Base", HDR_CONTENT_BASE, ftStr},
86 {"Content-Encoding", HDR_CONTENT_ENCODING, ftStr},
87 {"Content-Language", HDR_CONTENT_LANGUAGE, ftStr},
88 {"Content-Length", HDR_CONTENT_LENGTH, ftInt},
89 {"Content-Location", HDR_CONTENT_LOCATION, ftStr},
90 {"Content-MD5", HDR_CONTENT_MD5, ftStr}, /* for now */
91 {"Content-Range", HDR_CONTENT_RANGE, ftPContRange},
92 {"Content-Type", HDR_CONTENT_TYPE, ftStr},
93 {"Cookie", HDR_COOKIE, ftStr},
94 {"Date", HDR_DATE, ftDate_1123},
95 {"ETag", HDR_ETAG, ftETag},
96 {"Expires", HDR_EXPIRES, ftDate_1123},
97 {"From", HDR_FROM, ftStr},
98 {"Host", HDR_HOST, ftStr},
99 {"If-Match", HDR_IF_MATCH, ftStr}, /* for now */
100 {"If-Modified-Since", HDR_IF_MODIFIED_SINCE, ftDate_1123},
101 {"If-None-Match", HDR_IF_NONE_MATCH, ftStr}, /* for now */
102 {"If-Range", HDR_IF_RANGE, ftDate_1123_or_ETag},
103 {"Last-Modified", HDR_LAST_MODIFIED, ftDate_1123},
104 {"Link", HDR_LINK, ftStr},
105 {"Location", HDR_LOCATION, ftStr},
106 {"Max-Forwards", HDR_MAX_FORWARDS, ftInt},
107 {"Mime-Version", HDR_MIME_VERSION, ftStr}, /* for now */
108 {"Pragma", HDR_PRAGMA, ftStr},
109 {"Proxy-Authenticate", HDR_PROXY_AUTHENTICATE, ftStr},
110 {"Proxy-Authentication-Info", HDR_PROXY_AUTHENTICATION_INFO, ftStr},
111 {"Proxy-Authorization", HDR_PROXY_AUTHORIZATION, ftStr},
112 {"Proxy-Connection", HDR_PROXY_CONNECTION, ftStr},
113 {"Public", HDR_PUBLIC, ftStr},
114 {"Range", HDR_RANGE, ftPRange},
115 {"Referer", HDR_REFERER, ftStr},
116 {"Request-Range", HDR_REQUEST_RANGE, ftPRange}, /* usually matches HDR_RANGE */
117 {"Retry-After", HDR_RETRY_AFTER, ftStr}, /* for now (ftDate_1123 or ftInt!) */
118 {"Server", HDR_SERVER, ftStr},
119 {"Set-Cookie", HDR_SET_COOKIE, ftStr},
120 {"Title", HDR_TITLE, ftStr},
121 {"Transfer-Encoding", HDR_TRANSFER_ENCODING, ftStr},
122 {"Upgrade", HDR_UPGRADE, ftStr}, /* for now */
123 {"User-Agent", HDR_USER_AGENT, ftStr},
124 {"Vary", HDR_VARY, ftStr}, /* for now */
125 {"Via", HDR_VIA, ftStr}, /* for now */
126 {"Warning", HDR_WARNING, ftStr}, /* for now */
127 {"WWW-Authenticate", HDR_WWW_AUTHENTICATE, ftStr},
128 {"Authentication-Info", HDR_AUTHENTICATION_INFO, ftStr},
129 {"X-Cache", HDR_X_CACHE, ftStr},
130 {"X-Cache-Lookup", HDR_X_CACHE_LOOKUP, ftStr},
131 {"X-Forwarded-For", HDR_X_FORWARDED_FOR, ftStr},
132 {"X-Request-URI", HDR_X_REQUEST_URI, ftStr},
133 {"X-Squid-Error", HDR_X_SQUID_ERROR, ftStr},
134 {"Negotiate", HDR_NEGOTIATE, ftStr},
f66a9ef4 135#if X_ACCELERATOR_VARY
62e76326 136 {"X-Accelerator-Vary", HDR_X_ACCELERATOR_VARY, ftStr},
f66a9ef4 137#endif
43ae1d95 138 {"Surrogate-Capability", HDR_SURROGATE_CAPABILITY, ftStr},
139 {"Surrogate-Control", HDR_SURROGATE_CONTROL, ftPSc},
62e76326 140 {"Front-End-Https", HDR_FRONT_END_HTTPS, ftStr},
141 {"Other:", HDR_OTHER, ftStr} /* ':' will not allow matches */
142 };
143
de336bbe 144static HttpHeaderFieldInfo *Headers = NULL;
cb69b4c7 145
e6ccf245 146http_hdr_type &operator++ (http_hdr_type &aHeader)
147{
1f1ae50a 148 int tmp = (int)aHeader;
149 aHeader = (http_hdr_type)(++tmp);
e6ccf245 150 return aHeader;
151}
152
153
cb69b4c7 154/*
155 * headers with field values defined as #(values) in HTTP/1.1
d8b249ef 156 * Headers that are currently not recognized, are commented out.
cb69b4c7 157 */
b644367b 158static HttpHeaderMask ListHeadersMask; /* set run-time using ListHeadersArr */
de336bbe 159static http_hdr_type ListHeadersArr[] =
62e76326 160 {
161 HDR_ACCEPT,
162 HDR_ACCEPT_CHARSET, HDR_ACCEPT_ENCODING, HDR_ACCEPT_LANGUAGE,
163 HDR_ACCEPT_RANGES, HDR_ALLOW,
164 HDR_CACHE_CONTROL,
165 HDR_CONTENT_ENCODING,
166 HDR_CONTENT_LANGUAGE,
167 HDR_CONNECTION,
168 HDR_IF_MATCH, HDR_IF_NONE_MATCH,
169 HDR_LINK, HDR_PRAGMA,
170 HDR_PROXY_CONNECTION,
171 HDR_TRANSFER_ENCODING,
172 HDR_UPGRADE,
173 HDR_VARY,
174 HDR_VIA,
175 /* HDR_WARNING, */
176 HDR_WWW_AUTHENTICATE,
177 HDR_AUTHENTICATION_INFO,
178 HDR_PROXY_AUTHENTICATION_INFO,
179 /* HDR_EXPECT, HDR_TE, HDR_TRAILER */
f66a9ef4 180#if X_ACCELERATOR_VARY
62e76326 181 HDR_X_ACCELERATOR_VARY,
f66a9ef4 182#endif
43ae1d95 183 HDR_SURROGATE_CAPABILITY,
184 HDR_SURROGATE_CONTROL,
62e76326 185 HDR_X_FORWARDED_FOR
186 };
99edd1c3 187
188/* general-headers */
189static http_hdr_type GeneralHeadersArr[] =
62e76326 190 {
191 HDR_CACHE_CONTROL, HDR_CONNECTION, HDR_DATE, HDR_PRAGMA,
192 HDR_TRANSFER_ENCODING,
193 HDR_UPGRADE,
194 /* HDR_TRAILER, */
43ae1d95 195 HDR_VIA,
62e76326 196 };
99edd1c3 197
198/* entity-headers */
199static http_hdr_type EntityHeadersArr[] =
62e76326 200 {
201 HDR_ALLOW, HDR_CONTENT_BASE, HDR_CONTENT_ENCODING, HDR_CONTENT_LANGUAGE,
202 HDR_CONTENT_LENGTH, HDR_CONTENT_LOCATION, HDR_CONTENT_MD5,
203 HDR_CONTENT_RANGE, HDR_CONTENT_TYPE, HDR_ETAG, HDR_EXPIRES, HDR_LAST_MODIFIED, HDR_LINK,
204 HDR_OTHER
205 };
cb69b4c7 206
b644367b 207static HttpHeaderMask ReplyHeadersMask; /* set run-time using ReplyHeaders */
de336bbe 208static http_hdr_type ReplyHeadersArr[] =
62e76326 209 {
210 HDR_ACCEPT, HDR_ACCEPT_CHARSET, HDR_ACCEPT_ENCODING, HDR_ACCEPT_LANGUAGE,
211 HDR_ACCEPT_RANGES, HDR_AGE,
212 HDR_LOCATION, HDR_MAX_FORWARDS,
213 HDR_MIME_VERSION, HDR_PUBLIC, HDR_RETRY_AFTER, HDR_SERVER, HDR_SET_COOKIE,
214 HDR_VARY,
215 HDR_WARNING, HDR_PROXY_CONNECTION, HDR_X_CACHE,
216 HDR_X_CACHE_LOOKUP,
217 HDR_X_REQUEST_URI,
f66a9ef4 218#if X_ACCELERATOR_VARY
62e76326 219 HDR_X_ACCELERATOR_VARY,
f66a9ef4 220#endif
43ae1d95 221 HDR_X_SQUID_ERROR,
222 HDR_SURROGATE_CONTROL
62e76326 223 };
cb69b4c7 224
b644367b 225static HttpHeaderMask RequestHeadersMask; /* set run-time using RequestHeaders */
de336bbe 226static http_hdr_type RequestHeadersArr[] =
62e76326 227 {
228 HDR_AUTHORIZATION, HDR_FROM, HDR_HOST,
229 HDR_IF_MATCH, HDR_IF_MODIFIED_SINCE, HDR_IF_NONE_MATCH,
230 HDR_IF_RANGE, HDR_MAX_FORWARDS, HDR_PROXY_CONNECTION,
231 HDR_PROXY_AUTHORIZATION, HDR_RANGE, HDR_REFERER, HDR_REQUEST_RANGE,
43ae1d95 232 HDR_USER_AGENT, HDR_X_FORWARDED_FOR, HDR_SURROGATE_CAPABILITY
62e76326 233 };
cb69b4c7 234
12cf1be2 235/* header accounting */
2ac76861 236static HttpHeaderStat HttpHeaderStats[] =
62e76326 237 {
238 {"all"},
9f4d4f65 239#if USE_HTCP
62e76326 240 {"HTCP reply"},
9f4d4f65 241#endif
62e76326 242 {"request"},
243 {"reply"}
244 };
9bea1d5b 245static int HttpHeaderStatCount = countof(HttpHeaderStats);
12cf1be2 246
7faf2bdb 247static int HeaderEntryParsedCount = 0;
cb69b4c7 248
12cf1be2 249/*
250 * local routines
251 */
cb69b4c7 252
253#define assert_eid(id) assert((id) >= 0 && (id) < HDR_ENUM_END)
254
9bea1d5b 255static HttpHeaderEntry *httpHeaderEntryCreate(http_hdr_type id, const char *name, const char *value);
256static void httpHeaderEntryDestroy(HttpHeaderEntry * e);
257static HttpHeaderEntry *httpHeaderEntryParseCreate(const char *field_start, const char *field_end);
258static void httpHeaderNoteParsedEntry(http_hdr_type id, String value, int error);
cb69b4c7 259
9bea1d5b 260static void httpHeaderStatInit(HttpHeaderStat * hs, const char *label);
261static void httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e);
7c525cc2 262
cb69b4c7 263/*
264 * Module initialization routines
265 */
266
267void
9bea1d5b 268httpHeaderInitModule(void)
cb69b4c7 269{
9bea1d5b 270 int i;
271 /* check that we have enough space for masks */
272 assert(8 * sizeof(HttpHeaderMask) >= HDR_ENUM_END);
273 /* all headers must be described */
274 assert(countof(HeadersAttrs) == HDR_ENUM_END);
62e76326 275
9bea1d5b 276 if (!Headers)
62e76326 277 Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END);
278
9bea1d5b 279 /* create masks */
280 httpHeaderMaskInit(&ListHeadersMask, 0);
62e76326 281
8abf232c 282 httpHeaderCalcMask(&ListHeadersMask, ListHeadersArr, countof(ListHeadersArr));
62e76326 283
9bea1d5b 284 httpHeaderMaskInit(&ReplyHeadersMask, 0);
62e76326 285
8abf232c 286 httpHeaderCalcMask(&ReplyHeadersMask, ReplyHeadersArr, countof(ReplyHeadersArr));
62e76326 287
8abf232c 288 httpHeaderCalcMask(&ReplyHeadersMask, GeneralHeadersArr, countof(GeneralHeadersArr));
62e76326 289
8abf232c 290 httpHeaderCalcMask(&ReplyHeadersMask, EntityHeadersArr, countof(EntityHeadersArr));
62e76326 291
9bea1d5b 292 httpHeaderMaskInit(&RequestHeadersMask, 0);
62e76326 293
8abf232c 294 httpHeaderCalcMask(&RequestHeadersMask, RequestHeadersArr, countof(RequestHeadersArr));
62e76326 295
8abf232c 296 httpHeaderCalcMask(&RequestHeadersMask, GeneralHeadersArr, countof(GeneralHeadersArr));
62e76326 297
8abf232c 298 httpHeaderCalcMask(&RequestHeadersMask, EntityHeadersArr, countof(EntityHeadersArr));
62e76326 299
9bea1d5b 300 /* init header stats */
301 assert(HttpHeaderStatCount == hoReply + 1);
62e76326 302
9bea1d5b 303 for (i = 0; i < HttpHeaderStatCount; i++)
62e76326 304 httpHeaderStatInit(HttpHeaderStats + i, HttpHeaderStats[i].label);
305
9bea1d5b 306 HttpHeaderStats[hoRequest].owner_mask = &RequestHeadersMask;
62e76326 307
9bea1d5b 308 HttpHeaderStats[hoReply].owner_mask = &ReplyHeadersMask;
62e76326 309
86aebcda 310#if USE_HTCP
62e76326 311
9bea1d5b 312 HttpHeaderStats[hoHtcpReply].owner_mask = &ReplyHeadersMask;
62e76326 313
86aebcda 314#endif
9bea1d5b 315 /* init dependent modules */
316 httpHdrCcInitModule();
62e76326 317
43ae1d95 318 httpHdrScInitModule();
319
9bea1d5b 320 /* register with cache manager */
321 cachemgrRegister("http_headers",
62e76326 322 "HTTP Header Statistics", httpHeaderStoreReport, 0, 1);
cb69b4c7 323}
324
7021844c 325void
9bea1d5b 326httpHeaderCleanModule(void)
7021844c 327{
9bea1d5b 328 httpHeaderDestroyFieldsInfo(Headers, HDR_ENUM_END);
329 Headers = NULL;
330 httpHdrCcCleanModule();
43ae1d95 331 httpHdrScCleanModule();
7021844c 332}
333
12cf1be2 334static void
9bea1d5b 335httpHeaderStatInit(HttpHeaderStat * hs, const char *label)
12cf1be2 336{
9bea1d5b 337 assert(hs);
338 assert(label);
339 memset(hs, 0, sizeof(HttpHeaderStat));
340 hs->label = label;
341 statHistEnumInit(&hs->hdrUCountDistr, 32); /* not a real enum */
342 statHistEnumInit(&hs->fieldTypeDistr, HDR_ENUM_END);
343 statHistEnumInit(&hs->ccTypeDistr, CC_ENUM_END);
43ae1d95 344 statHistEnumInit(&hs->scTypeDistr, SC_ENUM_END);
cb69b4c7 345}
346
cb69b4c7 347/*
348 * HttpHeader Implementation
349 */
350
75faaa7a 351HttpHeader::HttpHeader() : owner (hoNone), len (0)
cb69b4c7 352{
75faaa7a 353 httpHeaderMaskInit(&mask, 0);
354}
355
356HttpHeader::HttpHeader(http_hdr_owner_type const &anOwner) : owner (anOwner), len (0)
357{
358 assert(this);
359 assert(anOwner > hoNone && anOwner <= hoReply);
360 debug(55, 7) ("init-ing hdr: %p owner: %d\n", this, owner);
361 httpHeaderMaskInit(&mask, 0);
362}
363
364HttpHeader::~HttpHeader()
365{
366 httpHeaderClean (this);
cb69b4c7 367}
368
369void
9bea1d5b 370httpHeaderClean(HttpHeader * hdr)
cb69b4c7 371{
9bea1d5b 372 HttpHeaderPos pos = HttpHeaderInitPos;
373 HttpHeaderEntry *e;
374
375 assert(hdr);
376 assert(hdr->owner > hoNone && hdr->owner <= hoReply);
377 debug(55, 7) ("cleaning hdr: %p owner: %d\n", hdr, hdr->owner);
378
51328da8 379 /*
380 * An unfortunate bug. The hdr->entries array is initialized
381 * such that count is set to zero. httpHeaderClean() seems to
382 * be called both when 'hdr' is created, and destroyed. Thus,
383 * we accumulate a large number of zero counts for 'hdr' before
384 * it is ever used. Can't think of a good way to fix it, except
385 * adding a state variable that indicates whether or not 'hdr'
386 * has been used. As a hack, just never count zero-sized header
387 * arrays.
388 */
389
390 if (0 != hdr->entries.count)
391 statHistCount(&HttpHeaderStats[hdr->owner].hdrUCountDistr, hdr->entries.count);
392
9bea1d5b 393 HttpHeaderStats[hdr->owner].destroyedCount++;
51328da8 394
9bea1d5b 395 HttpHeaderStats[hdr->owner].busyDestroyedCount += hdr->entries.count > 0;
62e76326 396
9bea1d5b 397 while ((e = httpHeaderGetEntry(hdr, &pos))) {
62e76326 398 /* tmp hack to try to avoid coredumps */
399
400 if (e->id < 0 || e->id >= HDR_ENUM_END) {
401 debug(55, 0) ("httpHeaderClean BUG: entry[%d] is invalid (%d). Ignored.\n",
402 (int) pos, e->id);
403 } else {
404 statHistCount(&HttpHeaderStats[hdr->owner].fieldTypeDistr, e->id);
51328da8 405 /* yes, this destroy() leaves us in an inconsistent state */
62e76326 406 httpHeaderEntryDestroy(e);
407 }
12cf1be2 408 }
62e76326 409
75faaa7a 410 hdr->entries.clean();
ead5ed09 411 httpHeaderMaskInit(&hdr->mask, 0);
cb69b4c7 412}
413
2246b732 414/* append entries (also see httpHeaderUpdate) */
415void
9bea1d5b 416httpHeaderAppend(HttpHeader * dest, const HttpHeader * src)
2246b732 417{
9bea1d5b 418 const HttpHeaderEntry *e;
419 HttpHeaderPos pos = HttpHeaderInitPos;
420 assert(src && dest);
421 assert(src != dest);
422 debug(55, 7) ("appending hdr: %p += %p\n", dest, src);
423
424 while ((e = httpHeaderGetEntry(src, &pos))) {
62e76326 425 httpHeaderAddEntry(dest, httpHeaderEntryClone(e));
2246b732 426 }
427}
428
d8b249ef 429/* use fresh entries to replace old ones */
cb69b4c7 430void
9bea1d5b 431httpHeaderUpdate(HttpHeader * old, const HttpHeader * fresh, const HttpHeaderMask * denied_mask)
924f73bc 432{
433 assert (old);
434 old->update (fresh, denied_mask);
435}
436
437void
438HttpHeader::update (HttpHeader const *fresh, HttpHeaderMask const *denied_mask)
cb69b4c7 439{
9bea1d5b 440 const HttpHeaderEntry *e;
441 HttpHeaderPos pos = HttpHeaderInitPos;
924f73bc 442 assert(this && fresh);
443 assert(this != fresh);
444 debug(55, 7) ("updating hdr: %p <- %p\n", this, fresh);
9bea1d5b 445
446 while ((e = httpHeaderGetEntry(fresh, &pos))) {
62e76326 447 /* deny bad guys (ok to check for HDR_OTHER) here */
448
449 if (denied_mask && CBIT_TEST(*denied_mask, e->id))
450 continue;
451
924f73bc 452 httpHeaderDelByName(this, e->name.buf());
62e76326 453
924f73bc 454 httpHeaderAddEntry(this, httpHeaderEntryClone(e));
cb69b4c7 455 }
cb69b4c7 456}
457
458/* just handy in parsing: resets and returns false */
9f4d4f65 459int
9bea1d5b 460httpHeaderReset(HttpHeader * hdr)
2ac76861 461{
d71e8477 462 http_hdr_owner_type ho;
9bea1d5b 463 assert(hdr);
464 ho = hdr->owner;
465 httpHeaderClean(hdr);
75faaa7a 466 *hdr = HttpHeader(ho);
9bea1d5b 467 return 0;
cb69b4c7 468}
469
cb69b4c7 470int
9bea1d5b 471httpHeaderParse(HttpHeader * hdr, const char *header_start, const char *header_end)
cb69b4c7 472{
52d3f198 473 const char *field_ptr = header_start;
474 HttpHeaderEntry *e, *e2;
9bea1d5b 475
476 assert(hdr);
477 assert(header_start && header_end);
478 debug(55, 7) ("parsing hdr: (%p)\n%s\n", hdr, getStringPrefix(header_start, header_end));
479 HttpHeaderStats[hdr->owner].parsedCount++;
62e76326 480
52d3f198 481 if (memchr(header_start, '\0', header_end - header_start)) {
482 debug(55, 1) ("WARNING: HTTP header contains NULL characters {%s}\n",
483 getStringPrefix(header_start, header_end));
484 return httpHeaderReset(hdr);
485 }
486
487 /* common format headers are "<name>:[ws]<value>" lines delimited by <CRLF>.
488 * continuation lines start with a (single) space or tab */
489 while (field_ptr < header_end) {
490 const char *field_start = field_ptr;
62e76326 491 const char *field_end;
62e76326 492
493 do {
52d3f198 494 const char *this_line = field_ptr;
495 field_ptr = (const char *)memchr(field_ptr, '\n', header_end - field_ptr);
496
497 if (!field_ptr)
498 return httpHeaderReset(hdr); /* missing <LF> */
499
500 field_end = field_ptr;
501
502 field_ptr++; /* Move to next line */
503
504 if (field_end > this_line && field_end[-1] == '\r') {
505 field_end--; /* Ignore CR LF */
506 /* Ignore CR CR LF in relaxed mode */
507
508 if (Config.onoff.relaxed_header_parser && field_end > this_line + 1 && field_end[-1] == '\r') {
509 debug(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2)
510 ("WARNING: Double CR characters in HTTP header {%s}\n", getStringPrefix(field_start, field_end));
511 field_end--;
512 }
513 }
514
515 /* Barf on stray CR characters */
516 if (memchr(this_line, '\r', field_end - this_line)) {
517 debug(55, 1) ("WARNING: suspicious CR characters in HTTP header {%s}\n",
518 getStringPrefix(field_start, field_end));
519
520 if (Config.onoff.relaxed_header_parser) {
521 char *p = (char *) this_line; /* XXX Warning! This destroys original header content and violates specifications somewhat */
522
523 while ((p = (char *)memchr(p, '\r', field_end - p)) != NULL)
524 *p++ = ' ';
525 } else
526 return httpHeaderReset(hdr);
527 }
528
529 if (this_line + 1 == field_end && this_line > field_start) {
530 debug(55, 1) ("WARNING: Blank continuation line in HTTP header {%s}\n",
531 getStringPrefix(header_start, header_end));
532 return httpHeaderReset(hdr);
533 }
534 } while (field_ptr < header_end && (*field_ptr == ' ' || *field_ptr == '\t'));
535
536 if (field_start == field_end) {
537 if (field_ptr < header_end) {
538 debug(55, 1) ("WARNING: unparseable HTTP header field near {%s}\n",
539 getStringPrefix(field_start, header_end));
540 return httpHeaderReset(hdr);
541 }
542
543 break; /* terminating blank line */
544 }
62e76326 545
546 e = httpHeaderEntryParseCreate(field_start, field_end);
547
47ac2ebe 548 if (NULL == e) {
52d3f198 549 debug(55, 1) ("WARNING: unparseable HTTP header field {%s}\n",
62e76326 550 getStringPrefix(field_start, field_end));
52d3f198 551 debug(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2)
552 (" in {%s}\n", getStringPrefix(header_start, header_end));
553
554 if (Config.onoff.relaxed_header_parser)
555 continue;
556 else
557 return httpHeaderReset(hdr);
47ac2ebe 558 }
62e76326 559
52d3f198 560 if (e->id == HDR_CONTENT_LENGTH && (e2 = httpHeaderFindEntry(hdr, e->id)) != NULL) {
b3123159 561 if (e->value.cmp(e2->value.buf()) != 0) {
9331e4b4 562 ssize_t l1, l2;
563 debug(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2) ("WARNING: found two conflicting content-length headers in {%s}\n", getStringPrefix(header_start, header_end));
564
565 if (!Config.onoff.relaxed_header_parser) {
566 httpHeaderEntryDestroy(e);
567 return httpHeaderReset(hdr);
568 }
569
570 if (!httpHeaderParseSize(e->value.buf(), &l1)) {
571 debug(55, 1)("WARNING: Unparseable content-length '%s'\n", e->value.buf());
572 httpHeaderEntryDestroy(e);
573 continue;
574 } else if (!httpHeaderParseSize(e2->value.buf(), &l2)) {
575 debug(55, 1)("WARNING: Unparseable content-length '%s'\n", e2->value.buf());
576 httpHeaderDelById(hdr, e2->id);
577 } else if (l1 > l2) {
578 httpHeaderDelById(hdr, e2->id);
579 } else {
580 httpHeaderEntryDestroy(e);
581 continue;
582 }
52d3f198 583 } else {
584 debug(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2)
585 ("NOTICE: found double content-length header\n");
b3123159 586
587 if (Config.onoff.relaxed_header_parser) {
588 httpHeaderEntryDestroy(e);
589 continue;
590 } else {
591 httpHeaderEntryDestroy(e);
592 return httpHeaderReset(hdr);
593 }
52d3f198 594 }
595 }
62e76326 596
52d3f198 597 if (e->id == HDR_OTHER && stringHasWhitespace(e->name.buf())) {
598 debug(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2)
599 ("WARNING: found whitespace in HTTP header name {%s}\n", getStringPrefix(field_start, field_end));
47ac2ebe 600
52d3f198 601 if (!Config.onoff.relaxed_header_parser) {
602 httpHeaderEntryDestroy(e);
603 return httpHeaderReset(hdr);
604 }
605 }
62e76326 606
52d3f198 607 httpHeaderAddEntry(hdr, e);
cb69b4c7 608 }
62e76326 609
9bea1d5b 610 return 1; /* even if no fields where found, it is a valid header */
cb69b4c7 611}
612
99edd1c3 613/* packs all the entries using supplied packer */
cb69b4c7 614void
9bea1d5b 615httpHeaderPackInto(const HttpHeader * hdr, Packer * p)
cb69b4c7 616{
9bea1d5b 617 HttpHeaderPos pos = HttpHeaderInitPos;
618 const HttpHeaderEntry *e;
619 assert(hdr && p);
620 debug(55, 7) ("packing hdr: (%p)\n", hdr);
621 /* pack all entries one by one */
62e76326 622
9bea1d5b 623 while ((e = httpHeaderGetEntry(hdr, &pos)))
62e76326 624 httpHeaderEntryPackInto(e, p);
cb69b4c7 625}
626
627/* returns next valid entry */
99edd1c3 628HttpHeaderEntry *
9bea1d5b 629httpHeaderGetEntry(const HttpHeader * hdr, HttpHeaderPos * pos)
cb69b4c7 630{
9bea1d5b 631 assert(hdr && pos);
732735ed 632 assert(*pos >= HttpHeaderInitPos && *pos < (ssize_t)hdr->entries.count);
62e76326 633
732735ed 634 for ((*pos)++; *pos < (ssize_t)hdr->entries.count; (*pos)++) {
62e76326 635 if (hdr->entries.items[*pos])
636 return (HttpHeaderEntry*)hdr->entries.items[*pos];
cb69b4c7 637 }
62e76326 638
9bea1d5b 639 return NULL;
cb69b4c7 640}
641
642/*
d8b249ef 643 * returns a pointer to a specified entry if any
644 * note that we return one entry so it does not make much sense to ask for
645 * "list" headers
cb69b4c7 646 */
de336bbe 647HttpHeaderEntry *
9bea1d5b 648httpHeaderFindEntry(const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 649{
9bea1d5b 650 HttpHeaderPos pos = HttpHeaderInitPos;
651 HttpHeaderEntry *e;
652 assert(hdr);
653 assert_eid(id);
654 assert(!CBIT_TEST(ListHeadersMask, id));
655
656 /* check mask first */
62e76326 657
9bea1d5b 658 if (!CBIT_TEST(hdr->mask, id))
62e76326 659 return NULL;
660
9bea1d5b 661 /* looks like we must have it, do linear search */
662 while ((e = httpHeaderGetEntry(hdr, &pos))) {
62e76326 663 if (e->id == id)
664 return e;
cb69b4c7 665 }
62e76326 666
9bea1d5b 667 /* hm.. we thought it was there, but it was not found */
668 assert(0);
62e76326 669
9bea1d5b 670 return NULL; /* not reached */
cb69b4c7 671}
672
a622fff0 673/*
674 * same as httpHeaderFindEntry
675 */
676static HttpHeaderEntry *
9bea1d5b 677httpHeaderFindLastEntry(const HttpHeader * hdr, http_hdr_type id)
a622fff0 678{
9bea1d5b 679 HttpHeaderPos pos = HttpHeaderInitPos;
680 HttpHeaderEntry *e;
681 HttpHeaderEntry *result = NULL;
682 assert(hdr);
683 assert_eid(id);
684 assert(!CBIT_TEST(ListHeadersMask, id));
685
686 /* check mask first */
62e76326 687
9bea1d5b 688 if (!CBIT_TEST(hdr->mask, id))
62e76326 689 return NULL;
690
9bea1d5b 691 /* looks like we must have it, do linear search */
692 while ((e = httpHeaderGetEntry(hdr, &pos))) {
62e76326 693 if (e->id == id)
694 result = e;
a622fff0 695 }
62e76326 696
9bea1d5b 697 assert(result); /* must be there! */
698 return result;
a622fff0 699}
700
cb69b4c7 701/*
d8b249ef 702 * deletes all fields with a given name if any, returns #fields deleted;
cb69b4c7 703 */
2ac76861 704int
9bea1d5b 705httpHeaderDelByName(HttpHeader * hdr, const char *name)
cb69b4c7 706{
9bea1d5b 707 int count = 0;
708 HttpHeaderPos pos = HttpHeaderInitPos;
709 HttpHeaderEntry *e;
710 httpHeaderMaskInit(&hdr->mask, 0); /* temporal inconsistency */
d7298d24 711 debug(55, 9) ("deleting '%s' fields in hdr %p\n", name, hdr);
62e76326 712
9bea1d5b 713 while ((e = httpHeaderGetEntry(hdr, &pos))) {
650c4b88 714 if (!e->name.caseCmp(name)) {
62e76326 715 httpHeaderDelAt(hdr, pos);
716 count++;
717 } else
718 CBIT_SET(hdr->mask, e->id);
cb69b4c7 719 }
62e76326 720
9bea1d5b 721 return count;
cb69b4c7 722}
723
2246b732 724/* deletes all entries with a given id, returns the #entries deleted */
725int
9bea1d5b 726httpHeaderDelById(HttpHeader * hdr, http_hdr_type id)
d8b249ef 727{
9bea1d5b 728 int count = 0;
729 HttpHeaderPos pos = HttpHeaderInitPos;
730 HttpHeaderEntry *e;
731 debug(55, 8) ("%p del-by-id %d\n", hdr, id);
732 assert(hdr);
733 assert_eid(id);
e6ca7a9b 734 assert(id != HDR_OTHER); /* does not make sense */
62e76326 735
9bea1d5b 736 if (!CBIT_TEST(hdr->mask, id))
62e76326 737 return 0;
738
9bea1d5b 739 while ((e = httpHeaderGetEntry(hdr, &pos))) {
62e76326 740 if (e->id == id) {
741 httpHeaderDelAt(hdr, pos);
742 count++;
743 }
d8b249ef 744 }
62e76326 745
9bea1d5b 746 CBIT_CLR(hdr->mask, id);
747 assert(count);
748 return count;
d8b249ef 749}
d8b249ef 750
cb69b4c7 751/*
752 * deletes an entry at pos and leaves a gap; leaving a gap makes it
753 * possible to iterate(search) and delete fields at the same time
754 */
2246b732 755void
9bea1d5b 756httpHeaderDelAt(HttpHeader * hdr, HttpHeaderPos pos)
cb69b4c7 757{
9bea1d5b 758 HttpHeaderEntry *e;
732735ed 759 assert(pos >= HttpHeaderInitPos && pos < (ssize_t)hdr->entries.count);
e6ccf245 760 e = (HttpHeaderEntry*)hdr->entries.items[pos];
9bea1d5b 761 hdr->entries.items[pos] = NULL;
762 /* decrement header length, allow for ": " and crlf */
528b2c61 763 hdr->len -= e->name.size() + 2 + e->value.size() + 2;
9bea1d5b 764 assert(hdr->len >= 0);
765 httpHeaderEntryDestroy(e);
cb69b4c7 766}
767
99edd1c3 768
62e76326 769/* appends an entry;
99edd1c3 770 * does not call httpHeaderEntryClone() so one should not reuse "*e"
cb69b4c7 771 */
99edd1c3 772void
9bea1d5b 773httpHeaderAddEntry(HttpHeader * hdr, HttpHeaderEntry * e)
cb69b4c7 774{
9bea1d5b 775 assert(hdr && e);
776 assert_eid(e->id);
777
d7298d24 778 debugs(55, 9, hdr << " adding entry: " << e->id << " at " <<
e4049756 779 hdr->entries.count);
62e76326 780
9bea1d5b 781 if (CBIT_TEST(hdr->mask, e->id))
62e76326 782 Headers[e->id].stat.repCount++;
9bea1d5b 783 else
62e76326 784 CBIT_SET(hdr->mask, e->id);
785
75faaa7a 786 hdr->entries.push_back(e);
62e76326 787
9bea1d5b 788 /* increment header length, allow for ": " and crlf */
528b2c61 789 hdr->len += e->name.size() + 2 + e->value.size() + 2;
cb69b4c7 790}
791
bbe58ab5 792/* inserts an entry;
793 * does not call httpHeaderEntryClone() so one should not reuse "*e"
794 */
795void
796httpHeaderInsertEntry(HttpHeader * hdr, HttpHeaderEntry * e)
797{
798 assert(hdr && e);
799 assert_eid(e->id);
800
801 debugs(55, 7, hdr << " adding entry: " << e->id << " at " <<
802 hdr->entries.count);
803
804 if (CBIT_TEST(hdr->mask, e->id))
805 Headers[e->id].stat.repCount++;
806 else
807 CBIT_SET(hdr->mask, e->id);
808
809 hdr->entries.insert(e);
810
811 /* increment header length, allow for ": " and crlf */
812 hdr->len += e->name.size() + 2 + e->value.size() + 2;
813}
814
99edd1c3 815/* return a list of entries with the same id separated by ',' and ws */
816String
9bea1d5b 817httpHeaderGetList(const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 818{
9bea1d5b 819 HttpHeaderEntry *e;
820 HttpHeaderPos pos = HttpHeaderInitPos;
d7298d24 821 debug(55, 9) ("%p: joining for id %d\n", hdr, id);
9bea1d5b 822 /* only fields from ListHeaders array can be "listed" */
823 assert(CBIT_TEST(ListHeadersMask, id));
62e76326 824
9bea1d5b 825 if (!CBIT_TEST(hdr->mask, id))
650c4b88 826 return String();
827
828 String s;
62e76326 829
9bea1d5b 830 while ((e = httpHeaderGetEntry(hdr, &pos))) {
62e76326 831 if (e->id == id)
832 strListAdd(&s, e->value.buf(), ',');
d35b9a94 833 }
62e76326 834
9bea1d5b 835 /*
836 * note: we might get an empty (len==0) string if there was an "empty"
837 * header; we must not get a NULL string though.
838 */
528b2c61 839 assert(s.buf());
62e76326 840
9bea1d5b 841 /* temporary warning: remove it! @?@ @?@ @?@ */
528b2c61 842 if (!s.size())
62e76326 843 debug(55, 3) ("empty list header: %s (%d)\n", Headers[id].name.buf(), id);
844
528b2c61 845 debug(55, 6) ("%p: joined for id %d: %s\n", hdr, id, s.buf());
62e76326 846
9bea1d5b 847 return s;
cb69b4c7 848}
849
f66a9ef4 850/* return a string or list of entries with the same id separated by ',' and ws */
851String
9bea1d5b 852httpHeaderGetStrOrList(const HttpHeader * hdr, http_hdr_type id)
f66a9ef4 853{
c7327fa0 854 HttpHeaderEntry *e;
9bea1d5b 855
856 if (CBIT_TEST(ListHeadersMask, id))
62e76326 857 return httpHeaderGetList(hdr, id);
858
528b2c61 859 if ((e = httpHeaderFindEntry(hdr, id)))
62e76326 860 return e->value;
861
650c4b88 862 return String();
f66a9ef4 863}
864
865/*
bd412580 866 * Returns the value of the specified header.
f66a9ef4 867 */
868String
9bea1d5b 869httpHeaderGetByName(const HttpHeader * hdr, const char *name)
f66a9ef4 870{
9bea1d5b 871 http_hdr_type id;
872 HttpHeaderPos pos = HttpHeaderInitPos;
873 HttpHeaderEntry *e;
9bea1d5b 874
875 assert(hdr);
876 assert(name);
877
878 /* First try the quick path */
879 id = httpHeaderIdByNameDef(name, strlen(name));
62e76326 880
9bea1d5b 881 if (id != -1)
62e76326 882 return httpHeaderGetStrOrList(hdr, id);
9bea1d5b 883
650c4b88 884 String result;
885
9bea1d5b 886 /* Sorry, an unknown header name. Do linear search */
887 while ((e = httpHeaderGetEntry(hdr, &pos))) {
650c4b88 888 if (e->id == HDR_OTHER && e->name.caseCmp(name) == 0) {
62e76326 889 strListAdd(&result, e->value.buf(), ',');
890 }
f66a9ef4 891 }
62e76326 892
9bea1d5b 893 return result;
f66a9ef4 894}
cb69b4c7 895
14b463aa 896/*
372fdfbf 897 * Returns a the value of the specified list member, if any.
14b463aa 898 */
899String
900httpHeaderGetByNameListMember(const HttpHeader * hdr, const char *name, const char *member, const char separator)
901{
14b463aa 902 String header;
903 const char *pos = NULL;
904 const char *item;
905 int ilen;
906 int mlen = strlen(member);
907
908 assert(hdr);
909 assert(name);
910
911 header = httpHeaderGetByName(hdr, name);
912
650c4b88 913 String result;
914
14b463aa 915 while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
62e76326 916 if (strncmp(item, member, mlen) == 0 && item[mlen] == '=') {
917 result.append(item + mlen + 1, ilen - mlen - 1);
918 break;
919 }
14b463aa 920 }
62e76326 921
14b463aa 922 return result;
923}
924
925/*
926 * returns a the value of the specified list member, if any.
927 */
928String
929httpHeaderGetListMember(const HttpHeader * hdr, http_hdr_type id, const char *member, const char separator)
930{
14b463aa 931 String header;
932 const char *pos = NULL;
933 const char *item;
934 int ilen;
935 int mlen = strlen(member);
936
937 assert(hdr);
938 assert(id >= 0);
939
940 header = httpHeaderGetStrOrList(hdr, id);
650c4b88 941 String result;
14b463aa 942
943 while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
62e76326 944 if (strncmp(item, member, mlen) == 0 && item[mlen] == '=') {
945 result.append(item + mlen + 1, ilen - mlen - 1);
946 break;
947 }
14b463aa 948 }
62e76326 949
528b2c61 950 header.clean();
14b463aa 951 return result;
952}
953
cb69b4c7 954/* test if a field is present */
2ac76861 955int
9bea1d5b 956httpHeaderHas(const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 957{
9bea1d5b 958 assert(hdr);
959 assert_eid(id);
960 assert(id != HDR_OTHER);
d7298d24 961 debug(55, 9) ("%p lookup for %d\n", hdr, id);
9bea1d5b 962 return CBIT_TEST(hdr->mask, id);
cb69b4c7 963}
964
965void
9bea1d5b 966httpHeaderPutInt(HttpHeader * hdr, http_hdr_type id, int number)
cb69b4c7 967{
9bea1d5b 968 assert_eid(id);
969 assert(Headers[id].type == ftInt); /* must be of an appropriate type */
970 assert(number >= 0);
971 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(id, NULL, xitoa(number)));
cb69b4c7 972}
973
974void
9bea1d5b 975httpHeaderPutTime(HttpHeader * hdr, http_hdr_type id, time_t htime)
cb69b4c7 976{
9bea1d5b 977 assert_eid(id);
978 assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */
a1d6870f 979 assert(htime >= 0);
9bea1d5b 980 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(id, NULL, mkrfc1123(htime)));
cb69b4c7 981}
2ac76861 982
bbe58ab5 983void
984httpHeaderInsertTime(HttpHeader * hdr, http_hdr_type id, time_t htime)
985{
986 assert_eid(id);
987 assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */
988 assert(htime >= 0);
989 httpHeaderInsertEntry(hdr, httpHeaderEntryCreate(id, NULL, mkrfc1123(htime)));
990}
991
cb69b4c7 992void
9bea1d5b 993httpHeaderPutStr(HttpHeader * hdr, http_hdr_type id, const char *str)
cb69b4c7 994{
9bea1d5b 995 assert_eid(id);
996 assert(Headers[id].type == ftStr); /* must be of an appropriate type */
997 assert(str);
998 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(id, NULL, str));
cb69b4c7 999}
1000
63259c34 1001void
9bea1d5b 1002httpHeaderPutAuth(HttpHeader * hdr, const char *auth_scheme, const char *realm)
63259c34 1003{
9bea1d5b 1004 assert(hdr && auth_scheme && realm);
1005 httpHeaderPutStrf(hdr, HDR_WWW_AUTHENTICATE, "%s realm=\"%s\"", auth_scheme, realm);
63259c34 1006}
1007
99edd1c3 1008void
9bea1d5b 1009httpHeaderPutCc(HttpHeader * hdr, const HttpHdrCc * cc)
99edd1c3 1010{
9bea1d5b 1011 MemBuf mb;
1012 Packer p;
1013 assert(hdr && cc);
1014 /* remove old directives if any */
1015 httpHeaderDelById(hdr, HDR_CACHE_CONTROL);
1016 /* pack into mb */
2fe7eff9 1017 mb.init();
9bea1d5b 1018 packerToMemInit(&p, &mb);
1019 httpHdrCcPackInto(cc, &p);
1020 /* put */
1021 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_CACHE_CONTROL, NULL, mb.buf));
1022 /* cleanup */
1023 packerClean(&p);
2fe7eff9 1024 mb.clean();
99edd1c3 1025}
1026
d192d11f 1027void
9bea1d5b 1028httpHeaderPutContRange(HttpHeader * hdr, const HttpHdrContRange * cr)
d192d11f 1029{
9bea1d5b 1030 MemBuf mb;
1031 Packer p;
1032 assert(hdr && cr);
1033 /* remove old directives if any */
1034 httpHeaderDelById(hdr, HDR_CONTENT_RANGE);
1035 /* pack into mb */
2fe7eff9 1036 mb.init();
9bea1d5b 1037 packerToMemInit(&p, &mb);
1038 httpHdrContRangePackInto(cr, &p);
1039 /* put */
1040 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_CONTENT_RANGE, NULL, mb.buf));
1041 /* cleanup */
1042 packerClean(&p);
2fe7eff9 1043 mb.clean();
d192d11f 1044}
1045
1046void
9bea1d5b 1047httpHeaderPutRange(HttpHeader * hdr, const HttpHdrRange * range)
d192d11f 1048{
9bea1d5b 1049 MemBuf mb;
1050 Packer p;
1051 assert(hdr && range);
1052 /* remove old directives if any */
1053 httpHeaderDelById(hdr, HDR_RANGE);
1054 /* pack into mb */
2fe7eff9 1055 mb.init();
9bea1d5b 1056 packerToMemInit(&p, &mb);
528b2c61 1057 range->packInto(&p);
9bea1d5b 1058 /* put */
1059 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_RANGE, NULL, mb.buf));
1060 /* cleanup */
1061 packerClean(&p);
2fe7eff9 1062 mb.clean();
d192d11f 1063}
1064
43ae1d95 1065void
1066httpHeaderPutSc(HttpHeader *hdr, const HttpHdrSc *sc)
1067{
1068 MemBuf mb;
1069 Packer p;
1070 assert(hdr && sc);
1071 /* remove old directives if any */
1072 httpHeaderDelById(hdr, HDR_RANGE);
1073 /* pack into mb */
2fe7eff9 1074 mb.init();
43ae1d95 1075 packerToMemInit(&p, &mb);
1076 httpHdrScPackInto(sc, &p);
1077 /* put */
1078 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_SURROGATE_CONTROL, NULL, mb.buf));
1079 /* cleanup */
1080 packerClean(&p);
2fe7eff9 1081 mb.clean();
43ae1d95 1082}
1083
cb69b4c7 1084/* add extension header (these fields are not parsed/analyzed/joined, etc.) */
1085void
9bea1d5b 1086httpHeaderPutExt(HttpHeader * hdr, const char *name, const char *value)
cb69b4c7 1087{
9bea1d5b 1088 assert(name && value);
1089 debug(55, 8) ("%p adds ext entry '%s: %s'\n", hdr, name, value);
1090 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_OTHER, name, value));
cb69b4c7 1091}
1092
7c525cc2 1093int
528b2c61 1094httpHeaderEntryGetInt (const HttpHeaderEntry * e)
7c525cc2 1095{
9bea1d5b 1096 int value = -1;
1097 int ok;
528b2c61 1098 assert (e);
1099 assert_eid (e->id);
1100 assert (Headers[e->id].type == ftInt);
1101 ok = httpHeaderParseInt(e->value.buf(), &value);
1102 httpHeaderNoteParsedEntry(e->id, e->value, !ok);
1103 /* XXX: Should we check ok - ie
1104 * return ok ? -1 : value;
1105 */
1106 return value;
1107}
1108
1109int
1110httpHeaderGetInt(const HttpHeader * hdr, http_hdr_type id)
1111{
9bea1d5b 1112 assert_eid(id);
1113 assert(Headers[id].type == ftInt); /* must be of an appropriate type */
528b2c61 1114 HttpHeaderEntry *e;
62e76326 1115
528b2c61 1116 if ((e = httpHeaderFindEntry(hdr, id)))
62e76326 1117 return httpHeaderEntryGetInt (e);
1118
528b2c61 1119 return -1;
7c525cc2 1120}
1121
de336bbe 1122time_t
9bea1d5b 1123httpHeaderGetTime(const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 1124{
9bea1d5b 1125 HttpHeaderEntry *e;
1126 time_t value = -1;
1127 assert_eid(id);
1128 assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */
62e76326 1129
9bea1d5b 1130 if ((e = httpHeaderFindEntry(hdr, id))) {
62e76326 1131 value = parse_rfc1123(e->value.buf());
1132 httpHeaderNoteParsedEntry(e->id, e->value, value < 0);
d8b249ef 1133 }
62e76326 1134
9bea1d5b 1135 return value;
cb69b4c7 1136}
1137
99edd1c3 1138/* sync with httpHeaderGetLastStr */
de336bbe 1139const char *
9bea1d5b 1140httpHeaderGetStr(const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 1141{
9bea1d5b 1142 HttpHeaderEntry *e;
1143 assert_eid(id);
1144 assert(Headers[id].type == ftStr); /* must be of an appropriate type */
62e76326 1145
9bea1d5b 1146 if ((e = httpHeaderFindEntry(hdr, id))) {
62e76326 1147 httpHeaderNoteParsedEntry(e->id, e->value, 0); /* no errors are possible */
1148 return e->value.buf();
d8b249ef 1149 }
62e76326 1150
9bea1d5b 1151 return NULL;
cb69b4c7 1152}
1153
a622fff0 1154/* unusual */
1155const char *
9bea1d5b 1156httpHeaderGetLastStr(const HttpHeader * hdr, http_hdr_type id)
a622fff0 1157{
9bea1d5b 1158 HttpHeaderEntry *e;
1159 assert_eid(id);
1160 assert(Headers[id].type == ftStr); /* must be of an appropriate type */
62e76326 1161
9bea1d5b 1162 if ((e = httpHeaderFindLastEntry(hdr, id))) {
62e76326 1163 httpHeaderNoteParsedEntry(e->id, e->value, 0); /* no errors are possible */
1164 return e->value.buf();
a622fff0 1165 }
62e76326 1166
9bea1d5b 1167 return NULL;
a622fff0 1168}
1169
7faf2bdb 1170HttpHdrCc *
9bea1d5b 1171httpHeaderGetCc(const HttpHeader * hdr)
cb69b4c7 1172{
9bea1d5b 1173 HttpHdrCc *cc;
1174 String s;
62e76326 1175
9bea1d5b 1176 if (!CBIT_TEST(hdr->mask, HDR_CACHE_CONTROL))
62e76326 1177 return NULL;
1178
9bea1d5b 1179 s = httpHeaderGetList(hdr, HDR_CACHE_CONTROL);
62e76326 1180
9bea1d5b 1181 cc = httpHdrCcParseCreate(&s);
62e76326 1182
9bea1d5b 1183 HttpHeaderStats[hdr->owner].ccParsedCount++;
62e76326 1184
9bea1d5b 1185 if (cc)
62e76326 1186 httpHdrCcUpdateStats(cc, &HttpHeaderStats[hdr->owner].ccTypeDistr);
1187
9bea1d5b 1188 httpHeaderNoteParsedEntry(HDR_CACHE_CONTROL, s, !cc);
62e76326 1189
528b2c61 1190 s.clean();
62e76326 1191
9bea1d5b 1192 return cc;
cb69b4c7 1193}
1194
02922e76 1195HttpHdrRange *
9bea1d5b 1196httpHeaderGetRange(const HttpHeader * hdr)
02922e76 1197{
9bea1d5b 1198 HttpHdrRange *r = NULL;
1199 HttpHeaderEntry *e;
1200 /* some clients will send "Request-Range" _and_ *matching* "Range"
1201 * who knows, some clients might send Request-Range only;
1202 * this "if" should work correctly in both cases;
1203 * hopefully no clients send mismatched headers! */
62e76326 1204
9bea1d5b 1205 if ((e = httpHeaderFindEntry(hdr, HDR_RANGE)) ||
62e76326 1206 (e = httpHeaderFindEntry(hdr, HDR_REQUEST_RANGE))) {
1207 r = HttpHdrRange::ParseCreate(&e->value);
1208 httpHeaderNoteParsedEntry(e->id, e->value, !r);
d192d11f 1209 }
62e76326 1210
9bea1d5b 1211 return r;
02922e76 1212}
1213
43ae1d95 1214HttpHdrSc *
1215httpHeaderGetSc(const HttpHeader *hdr)
1216{
1217 if (!CBIT_TEST(hdr->mask, HDR_SURROGATE_CONTROL))
1218 return NULL;
1219
1220 String s (httpHeaderGetList(hdr, HDR_SURROGATE_CONTROL));
1221
1222 HttpHdrSc *sc = httpHdrScParseCreate(&s);
1223
1224 HttpHeaderStats[hdr->owner].ccParsedCount++;
1225
1226 if (sc)
1227 httpHdrScUpdateStats(sc, &HttpHeaderStats[hdr->owner].scTypeDistr);
1228
1229 httpHeaderNoteParsedEntry(HDR_SURROGATE_CONTROL, s, !sc);
1230
1231 return sc;
1232}
1233
d76fcfa7 1234HttpHdrContRange *
9bea1d5b 1235httpHeaderGetContRange(const HttpHeader * hdr)
d76fcfa7 1236{
9bea1d5b 1237 HttpHdrContRange *cr = NULL;
1238 HttpHeaderEntry *e;
62e76326 1239
9bea1d5b 1240 if ((e = httpHeaderFindEntry(hdr, HDR_CONTENT_RANGE))) {
62e76326 1241 cr = httpHdrContRangeParseCreate(e->value.buf());
1242 httpHeaderNoteParsedEntry(e->id, e->value, !cr);
d8b249ef 1243 }
62e76326 1244
9bea1d5b 1245 return cr;
cb69b4c7 1246}
1247
99edd1c3 1248const char *
9bea1d5b 1249httpHeaderGetAuth(const HttpHeader * hdr, http_hdr_type id, const char *auth_scheme)
cb69b4c7 1250{
9bea1d5b 1251 const char *field;
1252 int l;
1253 assert(hdr && auth_scheme);
1254 field = httpHeaderGetStr(hdr, id);
62e76326 1255
9bea1d5b 1256 if (!field) /* no authorization field */
62e76326 1257 return NULL;
1258
9bea1d5b 1259 l = strlen(auth_scheme);
62e76326 1260
9bea1d5b 1261 if (!l || strncasecmp(field, auth_scheme, l)) /* wrong scheme */
62e76326 1262 return NULL;
1263
9bea1d5b 1264 field += l;
62e76326 1265
9bea1d5b 1266 if (!xisspace(*field)) /* wrong scheme */
62e76326 1267 return NULL;
1268
9bea1d5b 1269 /* skip white space */
1270 field += xcountws(field);
62e76326 1271
9bea1d5b 1272 if (!*field) /* no authorization cookie */
62e76326 1273 return NULL;
1274
9bea1d5b 1275 return base64_decode(field);
cb69b4c7 1276}
1277
a9771e51 1278ETag
9bea1d5b 1279httpHeaderGetETag(const HttpHeader * hdr, http_hdr_type id)
a9771e51 1280{
9bea1d5b 1281 ETag etag =
62e76326 1282 {NULL, -1};
9bea1d5b 1283 HttpHeaderEntry *e;
1284 assert(Headers[id].type == ftETag); /* must be of an appropriate type */
62e76326 1285
9bea1d5b 1286 if ((e = httpHeaderFindEntry(hdr, id)))
62e76326 1287 etagParseInit(&etag, e->value.buf());
1288
9bea1d5b 1289 return etag;
a9771e51 1290}
1291
1292TimeOrTag
9bea1d5b 1293httpHeaderGetTimeOrTag(const HttpHeader * hdr, http_hdr_type id)
a9771e51 1294{
9bea1d5b 1295 TimeOrTag tot;
1296 HttpHeaderEntry *e;
1297 assert(Headers[id].type == ftDate_1123_or_ETag); /* must be of an appropriate type */
1298 memset(&tot, 0, sizeof(tot));
62e76326 1299
9bea1d5b 1300 if ((e = httpHeaderFindEntry(hdr, id))) {
62e76326 1301 const char *str = e->value.buf();
1302 /* try as an ETag */
1303
1304 if (etagParseInit(&tot.tag, str)) {
1305 tot.valid = tot.tag.str != NULL;
1306 tot.time = -1;
1307 } else {
1308 /* or maybe it is time? */
1309 tot.time = parse_rfc1123(str);
1310 tot.valid = tot.time >= 0;
1311 tot.tag.str = NULL;
1312 }
a9771e51 1313 }
62e76326 1314
9bea1d5b 1315 assert(tot.time < 0 || !tot.tag.str); /* paranoid */
1316 return tot;
a9771e51 1317}
1318
cb69b4c7 1319/*
1320 * HttpHeaderEntry
1321 */
1322
d8b249ef 1323static HttpHeaderEntry *
9bea1d5b 1324httpHeaderEntryCreate(http_hdr_type id, const char *name, const char *value)
cb69b4c7 1325{
9bea1d5b 1326 HttpHeaderEntry *e;
1327 assert_eid(id);
0353e724 1328 e = new HttpHeaderEntry;
9bea1d5b 1329 e->id = id;
62e76326 1330
9bea1d5b 1331 if (id != HDR_OTHER)
62e76326 1332 e->name = Headers[id].name;
9bea1d5b 1333 else
62e76326 1334 e->name = name;
1335
528b2c61 1336 e->value = value;
62e76326 1337
9bea1d5b 1338 Headers[id].stat.aliveCount++;
62e76326 1339
528b2c61 1340 debug(55, 9) ("created entry %p: '%s: %s'\n", e, e->name.buf(), e->value.buf());
62e76326 1341
9bea1d5b 1342 return e;
cb69b4c7 1343}
1344
de336bbe 1345static void
9bea1d5b 1346httpHeaderEntryDestroy(HttpHeaderEntry * e)
2ac76861 1347{
9bea1d5b 1348 assert(e);
1349 assert_eid(e->id);
528b2c61 1350 debug(55, 9) ("destroying entry %p: '%s: %s'\n", e, e->name.buf(), e->value.buf());
9bea1d5b 1351 /* clean name if needed */
62e76326 1352
9bea1d5b 1353 if (e->id == HDR_OTHER)
62e76326 1354 e->name.clean();
1355
528b2c61 1356 e->value.clean();
62e76326 1357
9bea1d5b 1358 assert(Headers[e->id].stat.aliveCount);
62e76326 1359
9bea1d5b 1360 Headers[e->id].stat.aliveCount--;
62e76326 1361
e6ccf245 1362 e->id = HDR_BAD_HDR;
62e76326 1363
0353e724 1364 delete e;
cb69b4c7 1365}
1366
d8b249ef 1367/* parses and inits header entry, returns new entry on success */
1368static HttpHeaderEntry *
9bea1d5b 1369httpHeaderEntryParseCreate(const char *field_start, const char *field_end)
cb69b4c7 1370{
9bea1d5b 1371 HttpHeaderEntry *e;
e6ccf245 1372 http_hdr_type id;
9bea1d5b 1373 /* note: name_start == field_start */
52d3f198 1374 const char *name_end = (const char *)memchr(field_start, ':', field_end - field_start);
1375 int name_len = name_end ? name_end - field_start : 0;
9bea1d5b 1376 const char *value_start = field_start + name_len + 1; /* skip ':' */
1377 /* note: value_end == field_end */
1378
1379 HeaderEntryParsedCount++;
1380
1381 /* do we have a valid field name within this field? */
62e76326 1382
9bea1d5b 1383 if (!name_len || name_end > field_end)
62e76326 1384 return NULL;
1385
67746eea 1386 if (name_len > 65534) {
1387 /* String must be LESS THAN 64K and it adds a terminating NULL */
62e76326 1388 debug(55, 1) ("WARNING: ignoring header name of %d bytes\n", name_len);
1389 return NULL;
25acfb53 1390 }
62e76326 1391
52d3f198 1392 if (Config.onoff.relaxed_header_parser && xisspace(field_start[name_len - 1])) {
1393 debug(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2)
1394 ("NOTICE: Whitespace after header name in '%s'\n", getStringPrefix(field_start, field_end));
1395
1396 while (name_len > 0 && xisspace(field_start[name_len - 1]))
1397 name_len--;
1398
1399 if (!name_len)
1400 return NULL;
1401 }
1402
9bea1d5b 1403 /* now we know we can parse it */
0353e724 1404 e = new HttpHeaderEntry;
62e76326 1405
9bea1d5b 1406 debug(55, 9) ("creating entry %p: near '%s'\n", e, getStringPrefix(field_start, field_end));
62e76326 1407
9bea1d5b 1408 /* is it a "known" field? */
1409 id = httpHeaderIdByName(field_start, name_len, Headers, HDR_ENUM_END);
62e76326 1410
9bea1d5b 1411 if (id < 0)
62e76326 1412 id = HDR_OTHER;
1413
9bea1d5b 1414 assert_eid(id);
62e76326 1415
9bea1d5b 1416 e->id = id;
62e76326 1417
9bea1d5b 1418 /* set field name */
1419 if (id == HDR_OTHER)
62e76326 1420 e->name.limitInit(field_start, name_len);
9bea1d5b 1421 else
62e76326 1422 e->name = Headers[id].name;
1423
9bea1d5b 1424 /* trim field value */
1425 while (value_start < field_end && xisspace(*value_start))
62e76326 1426 value_start++;
1427
52d3f198 1428 while (value_start < field_end && xisspace(field_end[-1]))
1429 field_end--;
1430
67746eea 1431 if (field_end - value_start > 65534) {
1432 /* String must be LESS THAN 64K and it adds a terminating NULL */
62e76326 1433 debug(55, 1) ("WARNING: ignoring '%s' header of %d bytes\n",
1434 e->name.buf(), (int) (field_end - value_start));
1435
1436 if (e->id == HDR_OTHER)
1437 e->name.clean();
1438
0353e724 1439 delete e;
62e76326 1440
1441 return NULL;
25acfb53 1442 }
62e76326 1443
9bea1d5b 1444 /* set field value */
528b2c61 1445 e->value.limitInit(value_start, field_end - value_start);
62e76326 1446
9bea1d5b 1447 Headers[id].stat.seenCount++;
62e76326 1448
9bea1d5b 1449 Headers[id].stat.aliveCount++;
62e76326 1450
528b2c61 1451 debug(55, 9) ("created entry %p: '%s: %s'\n", e, e->name.buf(), e->value.buf());
62e76326 1452
9bea1d5b 1453 return e;
cb69b4c7 1454}
1455
99edd1c3 1456HttpHeaderEntry *
9bea1d5b 1457httpHeaderEntryClone(const HttpHeaderEntry * e)
cb69b4c7 1458{
528b2c61 1459 return httpHeaderEntryCreate(e->id, e->name.buf(), e->value.buf());
cb69b4c7 1460}
1461
de336bbe 1462void
9bea1d5b 1463httpHeaderEntryPackInto(const HttpHeaderEntry * e, Packer * p)
cb69b4c7 1464{
9bea1d5b 1465 assert(e && p);
528b2c61 1466 packerAppend(p, e->name.buf(), e->name.size());
9bea1d5b 1467 packerAppend(p, ": ", 2);
528b2c61 1468 packerAppend(p, e->value.buf(), e->value.size());
9bea1d5b 1469 packerAppend(p, "\r\n", 2);
cb69b4c7 1470}
1471
1472static void
9bea1d5b 1473httpHeaderNoteParsedEntry(http_hdr_type id, String context, int error)
cb69b4c7 1474{
9bea1d5b 1475 Headers[id].stat.parsCount++;
62e76326 1476
9bea1d5b 1477 if (error) {
62e76326 1478 Headers[id].stat.errCount++;
1479 debug(55, 2) ("cannot parse hdr field: '%s: %s'\n",
1480 Headers[id].name.buf(), context.buf());
cb69b4c7 1481 }
cb69b4c7 1482}
1483
cb69b4c7 1484/*
12cf1be2 1485 * Reports
cb69b4c7 1486 */
cb69b4c7 1487
fcd2d3ef 1488/* tmp variable used to pass stat info to dumpers */
0cdcddb9 1489extern const HttpHeaderStat *dump_stat; /* argh! */
fcd2d3ef 1490const HttpHeaderStat *dump_stat = NULL;
1491
cb69b4c7 1492static void
9bea1d5b 1493httpHeaderFieldStatDumper(StoreEntry * sentry, int idx, double val, double size, int count)
cb69b4c7 1494{
9bea1d5b 1495 const int id = (int) val;
1496 const int valid_id = id >= 0 && id < HDR_ENUM_END;
528b2c61 1497 const char *name = valid_id ? Headers[id].name.buf() : "INVALID";
9bea1d5b 1498 int visible = count > 0;
1499 /* for entries with zero count, list only those that belong to current type of message */
62e76326 1500
9bea1d5b 1501 if (!visible && valid_id && dump_stat->owner_mask)
62e76326 1502 visible = CBIT_TEST(*dump_stat->owner_mask, id);
1503
9bea1d5b 1504 if (visible)
62e76326 1505 storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
1506 id, name, count, xdiv(count, dump_stat->busyDestroyedCount));
cb69b4c7 1507}
1508
12cf1be2 1509static void
9bea1d5b 1510httpHeaderFldsPerHdrDumper(StoreEntry * sentry, int idx, double val, double size, int count)
cb69b4c7 1511{
9bea1d5b 1512 if (count)
62e76326 1513 storeAppendPrintf(sentry, "%2d\t %5d\t %5d\t %6.2f\n",
1514 idx, (int) val, count,
1515 xpercent(count, dump_stat->destroyedCount));
cb69b4c7 1516}
1517
1518
1519static void
9bea1d5b 1520httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e)
12cf1be2 1521{
9bea1d5b 1522 assert(hs && e);
1523
1524 dump_stat = hs;
1525 storeAppendPrintf(e, "\nHeader Stats: %s\n", hs->label);
1526 storeAppendPrintf(e, "\nField type distribution\n");
1527 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n",
62e76326 1528 "id", "name", "count", "#/header");
9bea1d5b 1529 statHistDump(&hs->fieldTypeDistr, e, httpHeaderFieldStatDumper);
1530 storeAppendPrintf(e, "\nCache-control directives distribution\n");
1531 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n",
62e76326 1532 "id", "name", "count", "#/cc_field");
9bea1d5b 1533 statHistDump(&hs->ccTypeDistr, e, httpHdrCcStatDumper);
43ae1d95 1534 storeAppendPrintf(e, "\nSurrogate-control directives distribution\n");
1535 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n",
1536 "id", "name", "count", "#/sc_field");
1537 statHistDump(&hs->scTypeDistr, e, httpHdrScStatDumper);
9bea1d5b 1538 storeAppendPrintf(e, "\nNumber of fields per header distribution\n");
1539 storeAppendPrintf(e, "%2s\t %-5s\t %5s\t %6s\n",
62e76326 1540 "id", "#flds", "count", "%total");
9bea1d5b 1541 statHistDump(&hs->hdrUCountDistr, e, httpHeaderFldsPerHdrDumper);
1542 dump_stat = NULL;
cb69b4c7 1543}
1544
12cf1be2 1545void
9bea1d5b 1546httpHeaderStoreReport(StoreEntry * e)
cb69b4c7 1547{
9bea1d5b 1548 int i;
1549 http_hdr_type ht;
1550 assert(e);
1551
1552 HttpHeaderStats[0].parsedCount =
62e76326 1553 HttpHeaderStats[hoRequest].parsedCount + HttpHeaderStats[hoReply].parsedCount;
9bea1d5b 1554 HttpHeaderStats[0].ccParsedCount =
62e76326 1555 HttpHeaderStats[hoRequest].ccParsedCount + HttpHeaderStats[hoReply].ccParsedCount;
9bea1d5b 1556 HttpHeaderStats[0].destroyedCount =
62e76326 1557 HttpHeaderStats[hoRequest].destroyedCount + HttpHeaderStats[hoReply].destroyedCount;
9bea1d5b 1558 HttpHeaderStats[0].busyDestroyedCount =
62e76326 1559 HttpHeaderStats[hoRequest].busyDestroyedCount + HttpHeaderStats[hoReply].busyDestroyedCount;
9bea1d5b 1560
1561 for (i = 1; i < HttpHeaderStatCount; i++) {
62e76326 1562 httpHeaderStatDump(HttpHeaderStats + i, e);
1563 storeAppendPrintf(e, "%s\n", "<br>");
12cf1be2 1564 }
62e76326 1565
9bea1d5b 1566 /* field stats for all messages */
1567 storeAppendPrintf(e, "\nHttp Fields Stats (replies and requests)\n");
62e76326 1568
9bea1d5b 1569 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\t %6s\n",
62e76326 1570 "id", "name", "#alive", "%err", "%repeat");
1571
e6ccf245 1572 for (ht = (http_hdr_type)0; ht < HDR_ENUM_END; ++ht) {
62e76326 1573 HttpHeaderFieldInfo *f = Headers + ht;
1574 storeAppendPrintf(e, "%2d\t %-20s\t %5d\t %6.3f\t %6.3f\n",
1575 f->id, f->name.buf(), f->stat.aliveCount,
1576 xpercent(f->stat.errCount, f->stat.parsCount),
1577 xpercent(f->stat.repCount, f->stat.seenCount));
12cf1be2 1578 }
62e76326 1579
9bea1d5b 1580 storeAppendPrintf(e, "Headers Parsed: %d + %d = %d\n",
62e76326 1581 HttpHeaderStats[hoRequest].parsedCount,
1582 HttpHeaderStats[hoReply].parsedCount,
1583 HttpHeaderStats[0].parsedCount);
9bea1d5b 1584 storeAppendPrintf(e, "Hdr Fields Parsed: %d\n", HeaderEntryParsedCount);
cb69b4c7 1585}
97474590 1586
e6ccf245 1587http_hdr_type
9bea1d5b 1588httpHeaderIdByName(const char *name, int name_len, const HttpHeaderFieldInfo * info, int end)
97474590 1589{
9bea1d5b 1590 int i;
62e76326 1591
9bea1d5b 1592 for (i = 0; i < end; ++i) {
62e76326 1593 if (name_len >= 0 && name_len != info[i].name.size())
1594 continue;
1595
1596 if (!strncasecmp(name, info[i].name.buf(),
1597 name_len < 0 ? info[i].name.size() + 1 : name_len))
1598 return info[i].id;
97474590 1599 }
62e76326 1600
e6ccf245 1601 return HDR_BAD_HDR;
97474590 1602}
1603
e6ccf245 1604http_hdr_type
9bea1d5b 1605httpHeaderIdByNameDef(const char *name, int name_len)
97474590 1606{
9bea1d5b 1607 if (!Headers)
62e76326 1608 Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END);
1609
9bea1d5b 1610 return httpHeaderIdByName(name, name_len, Headers, HDR_ENUM_END);
97474590 1611}
efd900cb 1612
1613const char *
9bea1d5b 1614httpHeaderNameById(int id)
efd900cb 1615{
9bea1d5b 1616 if (!Headers)
62e76326 1617 Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END);
1618
9bea1d5b 1619 assert(id >= 0 && id < HDR_ENUM_END);
62e76326 1620
849b826a 1621 return Headers[id].name.buf();
efd900cb 1622}
0353e724 1623
924f73bc 1624int
1625httpHeaderHasListMember(const HttpHeader * hdr, http_hdr_type id, const char *member, const char separator)
1626{
1627 int result = 0;
1628 const char *pos = NULL;
1629 const char *item;
1630 int ilen;
1631 int mlen = strlen(member);
1632
1633 assert(hdr);
1634 assert(id >= 0);
1635
1636 String header (httpHeaderGetStrOrList(hdr, id));
1637
9d0cdbb9 1638 while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
1639 if (strncmp(item, member, mlen) == 0
1640 && (item[mlen] == '=' || item[mlen] == separator || item[mlen] == ';' || item[mlen] == '\0')) {
1641 result = 1;
1642 break;
1643 }
1644 }
1645
1646 return result;
1647}
1648
1649int
1650httpHeaderHasByNameListMember(const HttpHeader * hdr, const char *name, const char *member, const char separator)
1651{
1652 int result = 0;
1653 const char *pos = NULL;
1654 const char *item;
1655 int ilen;
1656 int mlen = strlen(member);
1657
1658 assert(hdr);
1659 assert(name);
1660
1661 String header (httpHeaderGetByName(hdr, name));
1662
924f73bc 1663 while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
1664 if (strncmp(item, member, mlen) == 0
1665 && (item[mlen] == '=' || item[mlen] == separator || item[mlen] == ';' || item[mlen] == '\0')) {
1666 result = 1;
1667 break;
1668 }
1669 }
1670
1671 return result;
1672}
1673
1674void
1675HttpHeader::removeConnectionHeaderEntries()
1676{
1677 if (httpHeaderHas(this, HDR_CONNECTION)) {
1678 /* anything that matches Connection list member will be deleted */
1679 String strConnection = httpHeaderGetList(this, HDR_CONNECTION);
1680 const HttpHeaderEntry *e;
1681 HttpHeaderPos pos = HttpHeaderInitPos;
1682 /*
1683 * think: on-average-best nesting of the two loops (hdrEntry
1684 * and strListItem) @?@
1685 */
1686 /*
1687 * maybe we should delete standard stuff ("keep-alive","close")
1688 * from strConnection first?
1689 */
1690
1691 while ((e = httpHeaderGetEntry(this, &pos))) {
1692 if (strListIsMember(&strConnection, e->name.buf(), ','))
1693 httpHeaderDelAt(this, pos);
1694 }
1695
1696 httpHeaderDelById(this, HDR_CONNECTION);
1697 strConnection.clean();
1698 }
1699}