]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpHeader.cc
bugfix: Diskd has a loop that puts Squid to sleep for small amounts of
[thirdparty/squid.git] / src / HttpHeader.cc
CommitLineData
4f7e9dbb 1
cb69b4c7 2/*
a9925b40 3 * $Id: HttpHeader.cc,v 1.117 2006/05/06 22:13:18 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{
75faaa7a 358 assert(anOwner > hoNone && anOwner <= hoReply);
359 debug(55, 7) ("init-ing hdr: %p owner: %d\n", this, owner);
360 httpHeaderMaskInit(&mask, 0);
361}
362
363HttpHeader::~HttpHeader()
364{
519e0948 365 clean();
cb69b4c7 366}
367
368void
519e0948 369HttpHeader::clean()
cb69b4c7 370{
9bea1d5b 371 HttpHeaderPos pos = HttpHeaderInitPos;
372 HttpHeaderEntry *e;
373
519e0948 374 assert(owner > hoNone && owner <= hoReply);
375 debug(55, 7) ("cleaning hdr: %p owner: %d\n", this, owner);
9bea1d5b 376
51328da8 377 /*
519e0948 378 * An unfortunate bug. The entries array is initialized
51328da8 379 * such that count is set to zero. httpHeaderClean() seems to
380 * be called both when 'hdr' is created, and destroyed. Thus,
381 * we accumulate a large number of zero counts for 'hdr' before
382 * it is ever used. Can't think of a good way to fix it, except
383 * adding a state variable that indicates whether or not 'hdr'
384 * has been used. As a hack, just never count zero-sized header
385 * arrays.
386 */
387
519e0948 388 if (0 != entries.count)
389 statHistCount(&HttpHeaderStats[owner].hdrUCountDistr, entries.count);
51328da8 390
519e0948 391 HttpHeaderStats[owner].destroyedCount++;
51328da8 392
519e0948 393 HttpHeaderStats[owner].busyDestroyedCount += entries.count > 0;
62e76326 394
a9925b40 395 while ((e = getEntry(&pos))) {
62e76326 396 /* tmp hack to try to avoid coredumps */
397
398 if (e->id < 0 || e->id >= HDR_ENUM_END) {
399 debug(55, 0) ("httpHeaderClean BUG: entry[%d] is invalid (%d). Ignored.\n",
400 (int) pos, e->id);
401 } else {
519e0948 402 statHistCount(&HttpHeaderStats[owner].fieldTypeDistr, e->id);
51328da8 403 /* yes, this destroy() leaves us in an inconsistent state */
62e76326 404 httpHeaderEntryDestroy(e);
405 }
12cf1be2 406 }
62e76326 407
519e0948 408 entries.clean();
409 httpHeaderMaskInit(&mask, 0);
cb69b4c7 410}
411
2246b732 412/* append entries (also see httpHeaderUpdate) */
413void
a9925b40 414HttpHeader::append(const HttpHeader * src)
2246b732 415{
9bea1d5b 416 const HttpHeaderEntry *e;
417 HttpHeaderPos pos = HttpHeaderInitPos;
a9925b40 418 assert(src);
419 assert(src != this);
420 debug(55, 7) ("appending hdr: %p += %p\n", this, src);
9bea1d5b 421
a9925b40 422 while ((e = src->getEntry(&pos))) {
423 addEntry(httpHeaderEntryClone(e));
2246b732 424 }
425}
426
d8b249ef 427/* use fresh entries to replace old ones */
cb69b4c7 428void
9bea1d5b 429httpHeaderUpdate(HttpHeader * old, const HttpHeader * fresh, const HttpHeaderMask * denied_mask)
924f73bc 430{
431 assert (old);
432 old->update (fresh, denied_mask);
433}
434
435void
436HttpHeader::update (HttpHeader const *fresh, HttpHeaderMask const *denied_mask)
cb69b4c7 437{
9bea1d5b 438 const HttpHeaderEntry *e;
439 HttpHeaderPos pos = HttpHeaderInitPos;
a9925b40 440 assert(fresh);
924f73bc 441 assert(this != fresh);
442 debug(55, 7) ("updating hdr: %p <- %p\n", this, fresh);
9bea1d5b 443
a9925b40 444 while ((e = fresh->getEntry(&pos))) {
62e76326 445 /* deny bad guys (ok to check for HDR_OTHER) here */
446
447 if (denied_mask && CBIT_TEST(*denied_mask, e->id))
448 continue;
449
a9925b40 450 delByName(e->name.buf());
62e76326 451
a9925b40 452 addEntry(httpHeaderEntryClone(e));
cb69b4c7 453 }
cb69b4c7 454}
455
456/* just handy in parsing: resets and returns false */
9f4d4f65 457int
a9925b40 458HttpHeader::reset()
2ac76861 459{
d71e8477 460 http_hdr_owner_type ho;
a9925b40 461 ho = owner;
462 clean();
463 *this = HttpHeader(ho);
9bea1d5b 464 return 0;
cb69b4c7 465}
466
cb69b4c7 467int
a9925b40 468HttpHeader::parse(const char *header_start, const char *header_end)
cb69b4c7 469{
52d3f198 470 const char *field_ptr = header_start;
471 HttpHeaderEntry *e, *e2;
9bea1d5b 472
9bea1d5b 473 assert(header_start && header_end);
a9925b40 474 debug(55, 7) ("parsing hdr: (%p)\n%s\n", this, getStringPrefix(header_start, header_end));
475 HttpHeaderStats[owner].parsedCount++;
62e76326 476
52d3f198 477 if (memchr(header_start, '\0', header_end - header_start)) {
478 debug(55, 1) ("WARNING: HTTP header contains NULL characters {%s}\n",
479 getStringPrefix(header_start, header_end));
a9925b40 480 return reset();
52d3f198 481 }
482
483 /* common format headers are "<name>:[ws]<value>" lines delimited by <CRLF>.
484 * continuation lines start with a (single) space or tab */
485 while (field_ptr < header_end) {
486 const char *field_start = field_ptr;
62e76326 487 const char *field_end;
62e76326 488
489 do {
52d3f198 490 const char *this_line = field_ptr;
491 field_ptr = (const char *)memchr(field_ptr, '\n', header_end - field_ptr);
492
493 if (!field_ptr)
a9925b40 494 return reset(); /* missing <LF> */
52d3f198 495
496 field_end = field_ptr;
497
498 field_ptr++; /* Move to next line */
499
500 if (field_end > this_line && field_end[-1] == '\r') {
501 field_end--; /* Ignore CR LF */
502 /* Ignore CR CR LF in relaxed mode */
503
504 if (Config.onoff.relaxed_header_parser && field_end > this_line + 1 && field_end[-1] == '\r') {
505 debug(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2)
506 ("WARNING: Double CR characters in HTTP header {%s}\n", getStringPrefix(field_start, field_end));
507 field_end--;
508 }
509 }
510
511 /* Barf on stray CR characters */
512 if (memchr(this_line, '\r', field_end - this_line)) {
513 debug(55, 1) ("WARNING: suspicious CR characters in HTTP header {%s}\n",
514 getStringPrefix(field_start, field_end));
515
516 if (Config.onoff.relaxed_header_parser) {
517 char *p = (char *) this_line; /* XXX Warning! This destroys original header content and violates specifications somewhat */
518
519 while ((p = (char *)memchr(p, '\r', field_end - p)) != NULL)
520 *p++ = ' ';
521 } else
a9925b40 522 return reset();
52d3f198 523 }
524
525 if (this_line + 1 == field_end && this_line > field_start) {
526 debug(55, 1) ("WARNING: Blank continuation line in HTTP header {%s}\n",
527 getStringPrefix(header_start, header_end));
a9925b40 528 return reset();
52d3f198 529 }
530 } while (field_ptr < header_end && (*field_ptr == ' ' || *field_ptr == '\t'));
531
532 if (field_start == field_end) {
533 if (field_ptr < header_end) {
534 debug(55, 1) ("WARNING: unparseable HTTP header field near {%s}\n",
535 getStringPrefix(field_start, header_end));
a9925b40 536 return reset();
52d3f198 537 }
538
539 break; /* terminating blank line */
540 }
62e76326 541
542 e = httpHeaderEntryParseCreate(field_start, field_end);
543
47ac2ebe 544 if (NULL == e) {
52d3f198 545 debug(55, 1) ("WARNING: unparseable HTTP header field {%s}\n",
62e76326 546 getStringPrefix(field_start, field_end));
52d3f198 547 debug(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2)
548 (" in {%s}\n", getStringPrefix(header_start, header_end));
549
550 if (Config.onoff.relaxed_header_parser)
551 continue;
552 else
a9925b40 553 return reset();
47ac2ebe 554 }
62e76326 555
a9925b40 556 if (e->id == HDR_CONTENT_LENGTH && (e2 = findEntry(e->id)) != NULL) {
b3123159 557 if (e->value.cmp(e2->value.buf()) != 0) {
9331e4b4 558 ssize_t l1, l2;
559 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));
560
561 if (!Config.onoff.relaxed_header_parser) {
562 httpHeaderEntryDestroy(e);
a9925b40 563 return reset();
9331e4b4 564 }
565
566 if (!httpHeaderParseSize(e->value.buf(), &l1)) {
567 debug(55, 1)("WARNING: Unparseable content-length '%s'\n", e->value.buf());
568 httpHeaderEntryDestroy(e);
569 continue;
570 } else if (!httpHeaderParseSize(e2->value.buf(), &l2)) {
571 debug(55, 1)("WARNING: Unparseable content-length '%s'\n", e2->value.buf());
a9925b40 572 delById(e2->id);
9331e4b4 573 } else if (l1 > l2) {
a9925b40 574 delById(e2->id);
9331e4b4 575 } else {
576 httpHeaderEntryDestroy(e);
577 continue;
578 }
52d3f198 579 } else {
580 debug(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2)
581 ("NOTICE: found double content-length header\n");
b3123159 582
583 if (Config.onoff.relaxed_header_parser) {
584 httpHeaderEntryDestroy(e);
585 continue;
586 } else {
587 httpHeaderEntryDestroy(e);
a9925b40 588 return reset();
b3123159 589 }
52d3f198 590 }
591 }
62e76326 592
52d3f198 593 if (e->id == HDR_OTHER && stringHasWhitespace(e->name.buf())) {
594 debug(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2)
595 ("WARNING: found whitespace in HTTP header name {%s}\n", getStringPrefix(field_start, field_end));
47ac2ebe 596
52d3f198 597 if (!Config.onoff.relaxed_header_parser) {
598 httpHeaderEntryDestroy(e);
a9925b40 599 return reset();
52d3f198 600 }
601 }
62e76326 602
a9925b40 603 addEntry(e);
cb69b4c7 604 }
62e76326 605
9bea1d5b 606 return 1; /* even if no fields where found, it is a valid header */
cb69b4c7 607}
608
99edd1c3 609/* packs all the entries using supplied packer */
cb69b4c7 610void
a9925b40 611HttpHeader::packInto(Packer * p) const
cb69b4c7 612{
9bea1d5b 613 HttpHeaderPos pos = HttpHeaderInitPos;
614 const HttpHeaderEntry *e;
a9925b40 615 assert(p);
616 debug(55, 7) ("packing hdr: (%p)\n", this);
9bea1d5b 617 /* pack all entries one by one */
62e76326 618
a9925b40 619 while ((e = getEntry(&pos)))
62e76326 620 httpHeaderEntryPackInto(e, p);
cb69b4c7 621}
622
623/* returns next valid entry */
99edd1c3 624HttpHeaderEntry *
a9925b40 625HttpHeader::getEntry(HttpHeaderPos * pos) const
cb69b4c7 626{
a9925b40 627 assert(pos);
628 assert(*pos >= HttpHeaderInitPos && *pos < (ssize_t)entries.count);
62e76326 629
a9925b40 630 for ((*pos)++; *pos < (ssize_t)entries.count; (*pos)++) {
631 if (entries.items[*pos])
632 return (HttpHeaderEntry*)entries.items[*pos];
cb69b4c7 633 }
62e76326 634
9bea1d5b 635 return NULL;
cb69b4c7 636}
637
638/*
d8b249ef 639 * returns a pointer to a specified entry if any
640 * note that we return one entry so it does not make much sense to ask for
641 * "list" headers
cb69b4c7 642 */
de336bbe 643HttpHeaderEntry *
a9925b40 644HttpHeader::findEntry(http_hdr_type id) const
cb69b4c7 645{
9bea1d5b 646 HttpHeaderPos pos = HttpHeaderInitPos;
647 HttpHeaderEntry *e;
9bea1d5b 648 assert_eid(id);
649 assert(!CBIT_TEST(ListHeadersMask, id));
650
651 /* check mask first */
62e76326 652
a9925b40 653 if (!CBIT_TEST(mask, id))
62e76326 654 return NULL;
655
9bea1d5b 656 /* looks like we must have it, do linear search */
a9925b40 657 while ((e = getEntry(&pos))) {
62e76326 658 if (e->id == id)
659 return e;
cb69b4c7 660 }
62e76326 661
9bea1d5b 662 /* hm.. we thought it was there, but it was not found */
663 assert(0);
62e76326 664
9bea1d5b 665 return NULL; /* not reached */
cb69b4c7 666}
667
a622fff0 668/*
669 * same as httpHeaderFindEntry
670 */
a9925b40 671HttpHeaderEntry *
672HttpHeader::findLastEntry(http_hdr_type id) const
a622fff0 673{
9bea1d5b 674 HttpHeaderPos pos = HttpHeaderInitPos;
675 HttpHeaderEntry *e;
676 HttpHeaderEntry *result = NULL;
9bea1d5b 677 assert_eid(id);
678 assert(!CBIT_TEST(ListHeadersMask, id));
679
680 /* check mask first */
62e76326 681
a9925b40 682 if (!CBIT_TEST(mask, id))
62e76326 683 return NULL;
684
9bea1d5b 685 /* looks like we must have it, do linear search */
a9925b40 686 while ((e = getEntry(&pos))) {
62e76326 687 if (e->id == id)
688 result = e;
a622fff0 689 }
62e76326 690
9bea1d5b 691 assert(result); /* must be there! */
692 return result;
a622fff0 693}
694
cb69b4c7 695/*
d8b249ef 696 * deletes all fields with a given name if any, returns #fields deleted;
cb69b4c7 697 */
2ac76861 698int
a9925b40 699HttpHeader::delByName(const char *name)
cb69b4c7 700{
9bea1d5b 701 int count = 0;
702 HttpHeaderPos pos = HttpHeaderInitPos;
703 HttpHeaderEntry *e;
a9925b40 704 httpHeaderMaskInit(&mask, 0); /* temporal inconsistency */
705 debug(55, 9) ("deleting '%s' fields in hdr %p\n", name, this);
62e76326 706
a9925b40 707 while ((e = getEntry(&pos))) {
650c4b88 708 if (!e->name.caseCmp(name)) {
a9925b40 709 delAt(pos);
62e76326 710 count++;
711 } else
a9925b40 712 CBIT_SET(mask, e->id);
cb69b4c7 713 }
62e76326 714
9bea1d5b 715 return count;
cb69b4c7 716}
717
2246b732 718/* deletes all entries with a given id, returns the #entries deleted */
719int
a9925b40 720HttpHeader::delById(http_hdr_type id)
d8b249ef 721{
9bea1d5b 722 int count = 0;
723 HttpHeaderPos pos = HttpHeaderInitPos;
724 HttpHeaderEntry *e;
a9925b40 725 debug(55, 8) ("%p del-by-id %d\n", this, id);
9bea1d5b 726 assert_eid(id);
e6ca7a9b 727 assert(id != HDR_OTHER); /* does not make sense */
62e76326 728
a9925b40 729 if (!CBIT_TEST(mask, id))
62e76326 730 return 0;
731
a9925b40 732 while ((e = getEntry(&pos))) {
62e76326 733 if (e->id == id) {
a9925b40 734 delAt(pos);
62e76326 735 count++;
736 }
d8b249ef 737 }
62e76326 738
a9925b40 739 CBIT_CLR(mask, id);
9bea1d5b 740 assert(count);
741 return count;
d8b249ef 742}
d8b249ef 743
cb69b4c7 744/*
745 * deletes an entry at pos and leaves a gap; leaving a gap makes it
746 * possible to iterate(search) and delete fields at the same time
747 */
2246b732 748void
a9925b40 749HttpHeader::delAt(HttpHeaderPos pos)
cb69b4c7 750{
9bea1d5b 751 HttpHeaderEntry *e;
a9925b40 752 assert(pos >= HttpHeaderInitPos && pos < (ssize_t)entries.count);
753 e = (HttpHeaderEntry*)entries.items[pos];
754 entries.items[pos] = NULL;
9bea1d5b 755 /* decrement header length, allow for ": " and crlf */
a9925b40 756 len -= e->name.size() + 2 + e->value.size() + 2;
757 assert(len >= 0);
9bea1d5b 758 httpHeaderEntryDestroy(e);
cb69b4c7 759}
760
99edd1c3 761
62e76326 762/* appends an entry;
99edd1c3 763 * does not call httpHeaderEntryClone() so one should not reuse "*e"
cb69b4c7 764 */
99edd1c3 765void
a9925b40 766HttpHeader::addEntry(HttpHeaderEntry * e)
cb69b4c7 767{
a9925b40 768 assert(e);
9bea1d5b 769 assert_eid(e->id);
770
a9925b40 771 debugs(55, 9, this << " adding entry: " << e->id << " at " <<
772 entries.count);
62e76326 773
a9925b40 774 if (CBIT_TEST(mask, e->id))
62e76326 775 Headers[e->id].stat.repCount++;
9bea1d5b 776 else
a9925b40 777 CBIT_SET(mask, e->id);
62e76326 778
a9925b40 779 entries.push_back(e);
62e76326 780
9bea1d5b 781 /* increment header length, allow for ": " and crlf */
a9925b40 782 len += e->name.size() + 2 + e->value.size() + 2;
cb69b4c7 783}
784
bbe58ab5 785/* inserts an entry;
786 * does not call httpHeaderEntryClone() so one should not reuse "*e"
787 */
788void
a9925b40 789HttpHeader::insertEntry(HttpHeaderEntry * e)
bbe58ab5 790{
a9925b40 791 assert(e);
bbe58ab5 792 assert_eid(e->id);
793
a9925b40 794 debugs(55, 7, this << " adding entry: " << e->id << " at " <<
795 entries.count);
bbe58ab5 796
a9925b40 797 if (CBIT_TEST(mask, e->id))
bbe58ab5 798 Headers[e->id].stat.repCount++;
799 else
a9925b40 800 CBIT_SET(mask, e->id);
bbe58ab5 801
a9925b40 802 entries.insert(e);
bbe58ab5 803
804 /* increment header length, allow for ": " and crlf */
a9925b40 805 len += e->name.size() + 2 + e->value.size() + 2;
bbe58ab5 806}
807
99edd1c3 808/* return a list of entries with the same id separated by ',' and ws */
809String
a9925b40 810HttpHeader::getList(http_hdr_type id) const
cb69b4c7 811{
9bea1d5b 812 HttpHeaderEntry *e;
813 HttpHeaderPos pos = HttpHeaderInitPos;
a9925b40 814 debug(55, 9) ("%p: joining for id %d\n", this, id);
9bea1d5b 815 /* only fields from ListHeaders array can be "listed" */
816 assert(CBIT_TEST(ListHeadersMask, id));
62e76326 817
a9925b40 818 if (!CBIT_TEST(mask, id))
650c4b88 819 return String();
820
821 String s;
62e76326 822
a9925b40 823 while ((e = getEntry(&pos))) {
62e76326 824 if (e->id == id)
825 strListAdd(&s, e->value.buf(), ',');
d35b9a94 826 }
62e76326 827
9bea1d5b 828 /*
829 * note: we might get an empty (len==0) string if there was an "empty"
830 * header; we must not get a NULL string though.
831 */
528b2c61 832 assert(s.buf());
62e76326 833
9bea1d5b 834 /* temporary warning: remove it! @?@ @?@ @?@ */
528b2c61 835 if (!s.size())
62e76326 836 debug(55, 3) ("empty list header: %s (%d)\n", Headers[id].name.buf(), id);
837
a9925b40 838 debug(55, 6) ("%p: joined for id %d: %s\n", this, id, s.buf());
62e76326 839
9bea1d5b 840 return s;
cb69b4c7 841}
842
f66a9ef4 843/* return a string or list of entries with the same id separated by ',' and ws */
844String
a9925b40 845HttpHeader::getStrOrList(http_hdr_type id) const
f66a9ef4 846{
c7327fa0 847 HttpHeaderEntry *e;
9bea1d5b 848
849 if (CBIT_TEST(ListHeadersMask, id))
a9925b40 850 return getList(id);
62e76326 851
a9925b40 852 if ((e = findEntry(id)))
62e76326 853 return e->value;
854
650c4b88 855 return String();
f66a9ef4 856}
857
858/*
bd412580 859 * Returns the value of the specified header.
f66a9ef4 860 */
861String
a9925b40 862HttpHeader::getByName(const char *name) const
f66a9ef4 863{
9bea1d5b 864 http_hdr_type id;
865 HttpHeaderPos pos = HttpHeaderInitPos;
866 HttpHeaderEntry *e;
9bea1d5b 867
9bea1d5b 868 assert(name);
869
870 /* First try the quick path */
871 id = httpHeaderIdByNameDef(name, strlen(name));
62e76326 872
9bea1d5b 873 if (id != -1)
a9925b40 874 return getStrOrList(id);
9bea1d5b 875
650c4b88 876 String result;
877
9bea1d5b 878 /* Sorry, an unknown header name. Do linear search */
a9925b40 879 while ((e = getEntry(&pos))) {
650c4b88 880 if (e->id == HDR_OTHER && e->name.caseCmp(name) == 0) {
62e76326 881 strListAdd(&result, e->value.buf(), ',');
882 }
f66a9ef4 883 }
62e76326 884
9bea1d5b 885 return result;
f66a9ef4 886}
cb69b4c7 887
14b463aa 888/*
372fdfbf 889 * Returns a the value of the specified list member, if any.
14b463aa 890 */
891String
a9925b40 892HttpHeader::getByNameListMember(const char *name, const char *member, const char separator) const
14b463aa 893{
14b463aa 894 String header;
895 const char *pos = NULL;
896 const char *item;
897 int ilen;
898 int mlen = strlen(member);
899
14b463aa 900 assert(name);
901
a9925b40 902 header = getByName(name);
14b463aa 903
650c4b88 904 String result;
905
14b463aa 906 while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
62e76326 907 if (strncmp(item, member, mlen) == 0 && item[mlen] == '=') {
908 result.append(item + mlen + 1, ilen - mlen - 1);
909 break;
910 }
14b463aa 911 }
62e76326 912
14b463aa 913 return result;
914}
915
916/*
917 * returns a the value of the specified list member, if any.
918 */
919String
a9925b40 920HttpHeader::getListMember(http_hdr_type id, const char *member, const char separator) const
14b463aa 921{
14b463aa 922 String header;
923 const char *pos = NULL;
924 const char *item;
925 int ilen;
926 int mlen = strlen(member);
927
14b463aa 928 assert(id >= 0);
929
a9925b40 930 header = getStrOrList(id);
650c4b88 931 String result;
14b463aa 932
933 while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
62e76326 934 if (strncmp(item, member, mlen) == 0 && item[mlen] == '=') {
935 result.append(item + mlen + 1, ilen - mlen - 1);
936 break;
937 }
14b463aa 938 }
62e76326 939
528b2c61 940 header.clean();
14b463aa 941 return result;
942}
943
cb69b4c7 944/* test if a field is present */
2ac76861 945int
a9925b40 946HttpHeader::has(http_hdr_type id) const
cb69b4c7 947{
9bea1d5b 948 assert_eid(id);
949 assert(id != HDR_OTHER);
a9925b40 950 debug(55, 9) ("%p lookup for %d\n", this, id);
951 return CBIT_TEST(mask, id);
cb69b4c7 952}
953
954void
a9925b40 955HttpHeader::putInt(http_hdr_type id, int number)
cb69b4c7 956{
9bea1d5b 957 assert_eid(id);
958 assert(Headers[id].type == ftInt); /* must be of an appropriate type */
959 assert(number >= 0);
a9925b40 960 addEntry(httpHeaderEntryCreate(id, NULL, xitoa(number)));
cb69b4c7 961}
962
963void
a9925b40 964HttpHeader::putTime(http_hdr_type id, time_t htime)
cb69b4c7 965{
9bea1d5b 966 assert_eid(id);
967 assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */
a1d6870f 968 assert(htime >= 0);
a9925b40 969 addEntry(httpHeaderEntryCreate(id, NULL, mkrfc1123(htime)));
cb69b4c7 970}
2ac76861 971
bbe58ab5 972void
a9925b40 973HttpHeader::insertTime(http_hdr_type id, time_t htime)
bbe58ab5 974{
975 assert_eid(id);
976 assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */
977 assert(htime >= 0);
a9925b40 978 insertEntry(httpHeaderEntryCreate(id, NULL, mkrfc1123(htime)));
bbe58ab5 979}
980
cb69b4c7 981void
a9925b40 982HttpHeader::putStr(http_hdr_type id, const char *str)
cb69b4c7 983{
9bea1d5b 984 assert_eid(id);
985 assert(Headers[id].type == ftStr); /* must be of an appropriate type */
986 assert(str);
a9925b40 987 addEntry(httpHeaderEntryCreate(id, NULL, str));
cb69b4c7 988}
989
63259c34 990void
a9925b40 991HttpHeader::putAuth(const char *auth_scheme, const char *realm)
63259c34 992{
a9925b40 993 assert(auth_scheme && realm);
994 httpHeaderPutStrf(this, HDR_WWW_AUTHENTICATE, "%s realm=\"%s\"", auth_scheme, realm);
63259c34 995}
996
99edd1c3 997void
a9925b40 998HttpHeader::putCc(const HttpHdrCc * cc)
99edd1c3 999{
9bea1d5b 1000 MemBuf mb;
1001 Packer p;
a9925b40 1002 assert(cc);
9bea1d5b 1003 /* remove old directives if any */
a9925b40 1004 delById(HDR_CACHE_CONTROL);
9bea1d5b 1005 /* pack into mb */
2fe7eff9 1006 mb.init();
9bea1d5b 1007 packerToMemInit(&p, &mb);
1008 httpHdrCcPackInto(cc, &p);
1009 /* put */
a9925b40 1010 addEntry(httpHeaderEntryCreate(HDR_CACHE_CONTROL, NULL, mb.buf));
9bea1d5b 1011 /* cleanup */
1012 packerClean(&p);
2fe7eff9 1013 mb.clean();
99edd1c3 1014}
1015
d192d11f 1016void
a9925b40 1017HttpHeader::putContRange(const HttpHdrContRange * cr)
d192d11f 1018{
9bea1d5b 1019 MemBuf mb;
1020 Packer p;
a9925b40 1021 assert(cr);
9bea1d5b 1022 /* remove old directives if any */
a9925b40 1023 delById(HDR_CONTENT_RANGE);
9bea1d5b 1024 /* pack into mb */
2fe7eff9 1025 mb.init();
9bea1d5b 1026 packerToMemInit(&p, &mb);
1027 httpHdrContRangePackInto(cr, &p);
1028 /* put */
a9925b40 1029 addEntry(httpHeaderEntryCreate(HDR_CONTENT_RANGE, NULL, mb.buf));
9bea1d5b 1030 /* cleanup */
1031 packerClean(&p);
2fe7eff9 1032 mb.clean();
d192d11f 1033}
1034
1035void
a9925b40 1036HttpHeader::putRange(const HttpHdrRange * range)
d192d11f 1037{
9bea1d5b 1038 MemBuf mb;
1039 Packer p;
a9925b40 1040 assert(range);
9bea1d5b 1041 /* remove old directives if any */
a9925b40 1042 delById(HDR_RANGE);
9bea1d5b 1043 /* pack into mb */
2fe7eff9 1044 mb.init();
9bea1d5b 1045 packerToMemInit(&p, &mb);
528b2c61 1046 range->packInto(&p);
9bea1d5b 1047 /* put */
a9925b40 1048 addEntry(httpHeaderEntryCreate(HDR_RANGE, NULL, mb.buf));
9bea1d5b 1049 /* cleanup */
1050 packerClean(&p);
2fe7eff9 1051 mb.clean();
d192d11f 1052}
1053
43ae1d95 1054void
a9925b40 1055HttpHeader::putSc(HttpHdrSc *sc)
43ae1d95 1056{
1057 MemBuf mb;
1058 Packer p;
a9925b40 1059 assert(sc);
43ae1d95 1060 /* remove old directives if any */
a9925b40 1061 delById(HDR_RANGE);
43ae1d95 1062 /* pack into mb */
2fe7eff9 1063 mb.init();
43ae1d95 1064 packerToMemInit(&p, &mb);
1065 httpHdrScPackInto(sc, &p);
1066 /* put */
a9925b40 1067 addEntry(httpHeaderEntryCreate(HDR_SURROGATE_CONTROL, NULL, mb.buf));
43ae1d95 1068 /* cleanup */
1069 packerClean(&p);
2fe7eff9 1070 mb.clean();
43ae1d95 1071}
1072
cb69b4c7 1073/* add extension header (these fields are not parsed/analyzed/joined, etc.) */
1074void
a9925b40 1075HttpHeader::putExt(const char *name, const char *value)
cb69b4c7 1076{
9bea1d5b 1077 assert(name && value);
a9925b40 1078 debug(55, 8) ("%p adds ext entry '%s: %s'\n", this, name, value);
1079 addEntry(httpHeaderEntryCreate(HDR_OTHER, name, value));
cb69b4c7 1080}
1081
7c525cc2 1082int
528b2c61 1083httpHeaderEntryGetInt (const HttpHeaderEntry * e)
7c525cc2 1084{
9bea1d5b 1085 int value = -1;
1086 int ok;
528b2c61 1087 assert (e);
1088 assert_eid (e->id);
1089 assert (Headers[e->id].type == ftInt);
1090 ok = httpHeaderParseInt(e->value.buf(), &value);
1091 httpHeaderNoteParsedEntry(e->id, e->value, !ok);
1092 /* XXX: Should we check ok - ie
1093 * return ok ? -1 : value;
1094 */
1095 return value;
1096}
1097
1098int
a9925b40 1099HttpHeader::getInt(http_hdr_type id) const
528b2c61 1100{
9bea1d5b 1101 assert_eid(id);
1102 assert(Headers[id].type == ftInt); /* must be of an appropriate type */
528b2c61 1103 HttpHeaderEntry *e;
62e76326 1104
a9925b40 1105 if ((e = findEntry(id)))
62e76326 1106 return httpHeaderEntryGetInt (e);
1107
528b2c61 1108 return -1;
7c525cc2 1109}
1110
de336bbe 1111time_t
a9925b40 1112HttpHeader::getTime(http_hdr_type id) const
cb69b4c7 1113{
9bea1d5b 1114 HttpHeaderEntry *e;
1115 time_t value = -1;
1116 assert_eid(id);
1117 assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */
62e76326 1118
a9925b40 1119 if ((e = findEntry(id))) {
62e76326 1120 value = parse_rfc1123(e->value.buf());
1121 httpHeaderNoteParsedEntry(e->id, e->value, value < 0);
d8b249ef 1122 }
62e76326 1123
9bea1d5b 1124 return value;
cb69b4c7 1125}
1126
99edd1c3 1127/* sync with httpHeaderGetLastStr */
de336bbe 1128const char *
a9925b40 1129HttpHeader::getStr(http_hdr_type id) const
cb69b4c7 1130{
9bea1d5b 1131 HttpHeaderEntry *e;
1132 assert_eid(id);
1133 assert(Headers[id].type == ftStr); /* must be of an appropriate type */
62e76326 1134
a9925b40 1135 if ((e = findEntry(id))) {
62e76326 1136 httpHeaderNoteParsedEntry(e->id, e->value, 0); /* no errors are possible */
1137 return e->value.buf();
d8b249ef 1138 }
62e76326 1139
9bea1d5b 1140 return NULL;
cb69b4c7 1141}
1142
a622fff0 1143/* unusual */
1144const char *
a9925b40 1145HttpHeader::getLastStr(http_hdr_type id) const
a622fff0 1146{
9bea1d5b 1147 HttpHeaderEntry *e;
1148 assert_eid(id);
1149 assert(Headers[id].type == ftStr); /* must be of an appropriate type */
62e76326 1150
a9925b40 1151 if ((e = findLastEntry(id))) {
62e76326 1152 httpHeaderNoteParsedEntry(e->id, e->value, 0); /* no errors are possible */
1153 return e->value.buf();
a622fff0 1154 }
62e76326 1155
9bea1d5b 1156 return NULL;
a622fff0 1157}
1158
7faf2bdb 1159HttpHdrCc *
a9925b40 1160HttpHeader::getCc() const
cb69b4c7 1161{
9bea1d5b 1162 HttpHdrCc *cc;
1163 String s;
62e76326 1164
a9925b40 1165 if (!CBIT_TEST(mask, HDR_CACHE_CONTROL))
62e76326 1166 return NULL;
1167
a9925b40 1168 s = getList(HDR_CACHE_CONTROL);
62e76326 1169
9bea1d5b 1170 cc = httpHdrCcParseCreate(&s);
62e76326 1171
a9925b40 1172 HttpHeaderStats[owner].ccParsedCount++;
62e76326 1173
9bea1d5b 1174 if (cc)
a9925b40 1175 httpHdrCcUpdateStats(cc, &HttpHeaderStats[owner].ccTypeDistr);
62e76326 1176
9bea1d5b 1177 httpHeaderNoteParsedEntry(HDR_CACHE_CONTROL, s, !cc);
62e76326 1178
528b2c61 1179 s.clean();
62e76326 1180
9bea1d5b 1181 return cc;
cb69b4c7 1182}
1183
02922e76 1184HttpHdrRange *
a9925b40 1185HttpHeader::getRange() const
02922e76 1186{
9bea1d5b 1187 HttpHdrRange *r = NULL;
1188 HttpHeaderEntry *e;
1189 /* some clients will send "Request-Range" _and_ *matching* "Range"
1190 * who knows, some clients might send Request-Range only;
1191 * this "if" should work correctly in both cases;
1192 * hopefully no clients send mismatched headers! */
62e76326 1193
a9925b40 1194 if ((e = findEntry(HDR_RANGE)) ||
1195 (e = findEntry(HDR_REQUEST_RANGE))) {
62e76326 1196 r = HttpHdrRange::ParseCreate(&e->value);
1197 httpHeaderNoteParsedEntry(e->id, e->value, !r);
d192d11f 1198 }
62e76326 1199
9bea1d5b 1200 return r;
02922e76 1201}
1202
43ae1d95 1203HttpHdrSc *
a9925b40 1204HttpHeader::getSc() const
43ae1d95 1205{
a9925b40 1206 if (!CBIT_TEST(mask, HDR_SURROGATE_CONTROL))
43ae1d95 1207 return NULL;
1208
a9925b40 1209 String s (getList(HDR_SURROGATE_CONTROL));
43ae1d95 1210
1211 HttpHdrSc *sc = httpHdrScParseCreate(&s);
1212
a9925b40 1213 HttpHeaderStats[owner].ccParsedCount++;
43ae1d95 1214
1215 if (sc)
a9925b40 1216 httpHdrScUpdateStats(sc, &HttpHeaderStats[owner].scTypeDistr);
43ae1d95 1217
1218 httpHeaderNoteParsedEntry(HDR_SURROGATE_CONTROL, s, !sc);
1219
1220 return sc;
1221}
1222
d76fcfa7 1223HttpHdrContRange *
a9925b40 1224HttpHeader::getContRange() const
d76fcfa7 1225{
9bea1d5b 1226 HttpHdrContRange *cr = NULL;
1227 HttpHeaderEntry *e;
62e76326 1228
a9925b40 1229 if ((e = findEntry(HDR_CONTENT_RANGE))) {
62e76326 1230 cr = httpHdrContRangeParseCreate(e->value.buf());
1231 httpHeaderNoteParsedEntry(e->id, e->value, !cr);
d8b249ef 1232 }
62e76326 1233
9bea1d5b 1234 return cr;
cb69b4c7 1235}
1236
99edd1c3 1237const char *
a9925b40 1238HttpHeader::getAuth(http_hdr_type id, const char *auth_scheme) const
cb69b4c7 1239{
9bea1d5b 1240 const char *field;
1241 int l;
a9925b40 1242 assert(auth_scheme);
1243 field = getStr(id);
62e76326 1244
9bea1d5b 1245 if (!field) /* no authorization field */
62e76326 1246 return NULL;
1247
9bea1d5b 1248 l = strlen(auth_scheme);
62e76326 1249
9bea1d5b 1250 if (!l || strncasecmp(field, auth_scheme, l)) /* wrong scheme */
62e76326 1251 return NULL;
1252
9bea1d5b 1253 field += l;
62e76326 1254
9bea1d5b 1255 if (!xisspace(*field)) /* wrong scheme */
62e76326 1256 return NULL;
1257
9bea1d5b 1258 /* skip white space */
1259 field += xcountws(field);
62e76326 1260
9bea1d5b 1261 if (!*field) /* no authorization cookie */
62e76326 1262 return NULL;
1263
9bea1d5b 1264 return base64_decode(field);
cb69b4c7 1265}
1266
a9771e51 1267ETag
a9925b40 1268HttpHeader::getETag(http_hdr_type id) const
a9771e51 1269{
9bea1d5b 1270 ETag etag =
62e76326 1271 {NULL, -1};
9bea1d5b 1272 HttpHeaderEntry *e;
1273 assert(Headers[id].type == ftETag); /* must be of an appropriate type */
62e76326 1274
a9925b40 1275 if ((e = findEntry(id)))
62e76326 1276 etagParseInit(&etag, e->value.buf());
1277
9bea1d5b 1278 return etag;
a9771e51 1279}
1280
1281TimeOrTag
a9925b40 1282HttpHeader::getTimeOrTag(http_hdr_type id) const
a9771e51 1283{
9bea1d5b 1284 TimeOrTag tot;
1285 HttpHeaderEntry *e;
1286 assert(Headers[id].type == ftDate_1123_or_ETag); /* must be of an appropriate type */
1287 memset(&tot, 0, sizeof(tot));
62e76326 1288
a9925b40 1289 if ((e = findEntry(id))) {
62e76326 1290 const char *str = e->value.buf();
1291 /* try as an ETag */
1292
1293 if (etagParseInit(&tot.tag, str)) {
1294 tot.valid = tot.tag.str != NULL;
1295 tot.time = -1;
1296 } else {
1297 /* or maybe it is time? */
1298 tot.time = parse_rfc1123(str);
1299 tot.valid = tot.time >= 0;
1300 tot.tag.str = NULL;
1301 }
a9771e51 1302 }
62e76326 1303
9bea1d5b 1304 assert(tot.time < 0 || !tot.tag.str); /* paranoid */
1305 return tot;
a9771e51 1306}
1307
cb69b4c7 1308/*
1309 * HttpHeaderEntry
1310 */
1311
d8b249ef 1312static HttpHeaderEntry *
9bea1d5b 1313httpHeaderEntryCreate(http_hdr_type id, const char *name, const char *value)
cb69b4c7 1314{
9bea1d5b 1315 HttpHeaderEntry *e;
1316 assert_eid(id);
0353e724 1317 e = new HttpHeaderEntry;
9bea1d5b 1318 e->id = id;
62e76326 1319
9bea1d5b 1320 if (id != HDR_OTHER)
62e76326 1321 e->name = Headers[id].name;
9bea1d5b 1322 else
62e76326 1323 e->name = name;
1324
528b2c61 1325 e->value = value;
62e76326 1326
9bea1d5b 1327 Headers[id].stat.aliveCount++;
62e76326 1328
528b2c61 1329 debug(55, 9) ("created entry %p: '%s: %s'\n", e, e->name.buf(), e->value.buf());
62e76326 1330
9bea1d5b 1331 return e;
cb69b4c7 1332}
1333
de336bbe 1334static void
9bea1d5b 1335httpHeaderEntryDestroy(HttpHeaderEntry * e)
2ac76861 1336{
9bea1d5b 1337 assert(e);
1338 assert_eid(e->id);
528b2c61 1339 debug(55, 9) ("destroying entry %p: '%s: %s'\n", e, e->name.buf(), e->value.buf());
9bea1d5b 1340 /* clean name if needed */
62e76326 1341
9bea1d5b 1342 if (e->id == HDR_OTHER)
62e76326 1343 e->name.clean();
1344
528b2c61 1345 e->value.clean();
62e76326 1346
9bea1d5b 1347 assert(Headers[e->id].stat.aliveCount);
62e76326 1348
9bea1d5b 1349 Headers[e->id].stat.aliveCount--;
62e76326 1350
e6ccf245 1351 e->id = HDR_BAD_HDR;
62e76326 1352
0353e724 1353 delete e;
cb69b4c7 1354}
1355
d8b249ef 1356/* parses and inits header entry, returns new entry on success */
1357static HttpHeaderEntry *
9bea1d5b 1358httpHeaderEntryParseCreate(const char *field_start, const char *field_end)
cb69b4c7 1359{
9bea1d5b 1360 HttpHeaderEntry *e;
e6ccf245 1361 http_hdr_type id;
9bea1d5b 1362 /* note: name_start == field_start */
52d3f198 1363 const char *name_end = (const char *)memchr(field_start, ':', field_end - field_start);
1364 int name_len = name_end ? name_end - field_start : 0;
9bea1d5b 1365 const char *value_start = field_start + name_len + 1; /* skip ':' */
1366 /* note: value_end == field_end */
1367
1368 HeaderEntryParsedCount++;
1369
1370 /* do we have a valid field name within this field? */
62e76326 1371
9bea1d5b 1372 if (!name_len || name_end > field_end)
62e76326 1373 return NULL;
1374
67746eea 1375 if (name_len > 65534) {
1376 /* String must be LESS THAN 64K and it adds a terminating NULL */
62e76326 1377 debug(55, 1) ("WARNING: ignoring header name of %d bytes\n", name_len);
1378 return NULL;
25acfb53 1379 }
62e76326 1380
52d3f198 1381 if (Config.onoff.relaxed_header_parser && xisspace(field_start[name_len - 1])) {
1382 debug(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2)
1383 ("NOTICE: Whitespace after header name in '%s'\n", getStringPrefix(field_start, field_end));
1384
1385 while (name_len > 0 && xisspace(field_start[name_len - 1]))
1386 name_len--;
1387
1388 if (!name_len)
1389 return NULL;
1390 }
1391
9bea1d5b 1392 /* now we know we can parse it */
0353e724 1393 e = new HttpHeaderEntry;
62e76326 1394
9bea1d5b 1395 debug(55, 9) ("creating entry %p: near '%s'\n", e, getStringPrefix(field_start, field_end));
62e76326 1396
9bea1d5b 1397 /* is it a "known" field? */
1398 id = httpHeaderIdByName(field_start, name_len, Headers, HDR_ENUM_END);
62e76326 1399
9bea1d5b 1400 if (id < 0)
62e76326 1401 id = HDR_OTHER;
1402
9bea1d5b 1403 assert_eid(id);
62e76326 1404
9bea1d5b 1405 e->id = id;
62e76326 1406
9bea1d5b 1407 /* set field name */
1408 if (id == HDR_OTHER)
62e76326 1409 e->name.limitInit(field_start, name_len);
9bea1d5b 1410 else
62e76326 1411 e->name = Headers[id].name;
1412
9bea1d5b 1413 /* trim field value */
1414 while (value_start < field_end && xisspace(*value_start))
62e76326 1415 value_start++;
1416
52d3f198 1417 while (value_start < field_end && xisspace(field_end[-1]))
1418 field_end--;
1419
67746eea 1420 if (field_end - value_start > 65534) {
1421 /* String must be LESS THAN 64K and it adds a terminating NULL */
62e76326 1422 debug(55, 1) ("WARNING: ignoring '%s' header of %d bytes\n",
1423 e->name.buf(), (int) (field_end - value_start));
1424
1425 if (e->id == HDR_OTHER)
1426 e->name.clean();
1427
0353e724 1428 delete e;
62e76326 1429
1430 return NULL;
25acfb53 1431 }
62e76326 1432
9bea1d5b 1433 /* set field value */
528b2c61 1434 e->value.limitInit(value_start, field_end - value_start);
62e76326 1435
9bea1d5b 1436 Headers[id].stat.seenCount++;
62e76326 1437
9bea1d5b 1438 Headers[id].stat.aliveCount++;
62e76326 1439
528b2c61 1440 debug(55, 9) ("created entry %p: '%s: %s'\n", e, e->name.buf(), e->value.buf());
62e76326 1441
9bea1d5b 1442 return e;
cb69b4c7 1443}
1444
99edd1c3 1445HttpHeaderEntry *
9bea1d5b 1446httpHeaderEntryClone(const HttpHeaderEntry * e)
cb69b4c7 1447{
528b2c61 1448 return httpHeaderEntryCreate(e->id, e->name.buf(), e->value.buf());
cb69b4c7 1449}
1450
de336bbe 1451void
9bea1d5b 1452httpHeaderEntryPackInto(const HttpHeaderEntry * e, Packer * p)
cb69b4c7 1453{
9bea1d5b 1454 assert(e && p);
528b2c61 1455 packerAppend(p, e->name.buf(), e->name.size());
9bea1d5b 1456 packerAppend(p, ": ", 2);
528b2c61 1457 packerAppend(p, e->value.buf(), e->value.size());
9bea1d5b 1458 packerAppend(p, "\r\n", 2);
cb69b4c7 1459}
1460
1461static void
9bea1d5b 1462httpHeaderNoteParsedEntry(http_hdr_type id, String context, int error)
cb69b4c7 1463{
9bea1d5b 1464 Headers[id].stat.parsCount++;
62e76326 1465
9bea1d5b 1466 if (error) {
62e76326 1467 Headers[id].stat.errCount++;
1468 debug(55, 2) ("cannot parse hdr field: '%s: %s'\n",
1469 Headers[id].name.buf(), context.buf());
cb69b4c7 1470 }
cb69b4c7 1471}
1472
cb69b4c7 1473/*
12cf1be2 1474 * Reports
cb69b4c7 1475 */
cb69b4c7 1476
fcd2d3ef 1477/* tmp variable used to pass stat info to dumpers */
0cdcddb9 1478extern const HttpHeaderStat *dump_stat; /* argh! */
fcd2d3ef 1479const HttpHeaderStat *dump_stat = NULL;
1480
cb69b4c7 1481static void
9bea1d5b 1482httpHeaderFieldStatDumper(StoreEntry * sentry, int idx, double val, double size, int count)
cb69b4c7 1483{
9bea1d5b 1484 const int id = (int) val;
1485 const int valid_id = id >= 0 && id < HDR_ENUM_END;
528b2c61 1486 const char *name = valid_id ? Headers[id].name.buf() : "INVALID";
9bea1d5b 1487 int visible = count > 0;
1488 /* for entries with zero count, list only those that belong to current type of message */
62e76326 1489
9bea1d5b 1490 if (!visible && valid_id && dump_stat->owner_mask)
62e76326 1491 visible = CBIT_TEST(*dump_stat->owner_mask, id);
1492
9bea1d5b 1493 if (visible)
62e76326 1494 storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
1495 id, name, count, xdiv(count, dump_stat->busyDestroyedCount));
cb69b4c7 1496}
1497
12cf1be2 1498static void
9bea1d5b 1499httpHeaderFldsPerHdrDumper(StoreEntry * sentry, int idx, double val, double size, int count)
cb69b4c7 1500{
9bea1d5b 1501 if (count)
62e76326 1502 storeAppendPrintf(sentry, "%2d\t %5d\t %5d\t %6.2f\n",
1503 idx, (int) val, count,
1504 xpercent(count, dump_stat->destroyedCount));
cb69b4c7 1505}
1506
1507
1508static void
9bea1d5b 1509httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e)
12cf1be2 1510{
9bea1d5b 1511 assert(hs && e);
1512
1513 dump_stat = hs;
1514 storeAppendPrintf(e, "\nHeader Stats: %s\n", hs->label);
1515 storeAppendPrintf(e, "\nField type distribution\n");
1516 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n",
62e76326 1517 "id", "name", "count", "#/header");
9bea1d5b 1518 statHistDump(&hs->fieldTypeDistr, e, httpHeaderFieldStatDumper);
1519 storeAppendPrintf(e, "\nCache-control directives distribution\n");
1520 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n",
62e76326 1521 "id", "name", "count", "#/cc_field");
9bea1d5b 1522 statHistDump(&hs->ccTypeDistr, e, httpHdrCcStatDumper);
43ae1d95 1523 storeAppendPrintf(e, "\nSurrogate-control directives distribution\n");
1524 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n",
1525 "id", "name", "count", "#/sc_field");
1526 statHistDump(&hs->scTypeDistr, e, httpHdrScStatDumper);
9bea1d5b 1527 storeAppendPrintf(e, "\nNumber of fields per header distribution\n");
1528 storeAppendPrintf(e, "%2s\t %-5s\t %5s\t %6s\n",
62e76326 1529 "id", "#flds", "count", "%total");
9bea1d5b 1530 statHistDump(&hs->hdrUCountDistr, e, httpHeaderFldsPerHdrDumper);
1531 dump_stat = NULL;
cb69b4c7 1532}
1533
12cf1be2 1534void
9bea1d5b 1535httpHeaderStoreReport(StoreEntry * e)
cb69b4c7 1536{
9bea1d5b 1537 int i;
1538 http_hdr_type ht;
1539 assert(e);
1540
1541 HttpHeaderStats[0].parsedCount =
62e76326 1542 HttpHeaderStats[hoRequest].parsedCount + HttpHeaderStats[hoReply].parsedCount;
9bea1d5b 1543 HttpHeaderStats[0].ccParsedCount =
62e76326 1544 HttpHeaderStats[hoRequest].ccParsedCount + HttpHeaderStats[hoReply].ccParsedCount;
9bea1d5b 1545 HttpHeaderStats[0].destroyedCount =
62e76326 1546 HttpHeaderStats[hoRequest].destroyedCount + HttpHeaderStats[hoReply].destroyedCount;
9bea1d5b 1547 HttpHeaderStats[0].busyDestroyedCount =
62e76326 1548 HttpHeaderStats[hoRequest].busyDestroyedCount + HttpHeaderStats[hoReply].busyDestroyedCount;
9bea1d5b 1549
1550 for (i = 1; i < HttpHeaderStatCount; i++) {
62e76326 1551 httpHeaderStatDump(HttpHeaderStats + i, e);
1552 storeAppendPrintf(e, "%s\n", "<br>");
12cf1be2 1553 }
62e76326 1554
9bea1d5b 1555 /* field stats for all messages */
1556 storeAppendPrintf(e, "\nHttp Fields Stats (replies and requests)\n");
62e76326 1557
9bea1d5b 1558 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\t %6s\n",
62e76326 1559 "id", "name", "#alive", "%err", "%repeat");
1560
e6ccf245 1561 for (ht = (http_hdr_type)0; ht < HDR_ENUM_END; ++ht) {
62e76326 1562 HttpHeaderFieldInfo *f = Headers + ht;
1563 storeAppendPrintf(e, "%2d\t %-20s\t %5d\t %6.3f\t %6.3f\n",
1564 f->id, f->name.buf(), f->stat.aliveCount,
1565 xpercent(f->stat.errCount, f->stat.parsCount),
1566 xpercent(f->stat.repCount, f->stat.seenCount));
12cf1be2 1567 }
62e76326 1568
9bea1d5b 1569 storeAppendPrintf(e, "Headers Parsed: %d + %d = %d\n",
62e76326 1570 HttpHeaderStats[hoRequest].parsedCount,
1571 HttpHeaderStats[hoReply].parsedCount,
1572 HttpHeaderStats[0].parsedCount);
9bea1d5b 1573 storeAppendPrintf(e, "Hdr Fields Parsed: %d\n", HeaderEntryParsedCount);
cb69b4c7 1574}
97474590 1575
e6ccf245 1576http_hdr_type
9bea1d5b 1577httpHeaderIdByName(const char *name, int name_len, const HttpHeaderFieldInfo * info, int end)
97474590 1578{
9bea1d5b 1579 int i;
62e76326 1580
9bea1d5b 1581 for (i = 0; i < end; ++i) {
62e76326 1582 if (name_len >= 0 && name_len != info[i].name.size())
1583 continue;
1584
1585 if (!strncasecmp(name, info[i].name.buf(),
1586 name_len < 0 ? info[i].name.size() + 1 : name_len))
1587 return info[i].id;
97474590 1588 }
62e76326 1589
e6ccf245 1590 return HDR_BAD_HDR;
97474590 1591}
1592
e6ccf245 1593http_hdr_type
9bea1d5b 1594httpHeaderIdByNameDef(const char *name, int name_len)
97474590 1595{
9bea1d5b 1596 if (!Headers)
62e76326 1597 Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END);
1598
9bea1d5b 1599 return httpHeaderIdByName(name, name_len, Headers, HDR_ENUM_END);
97474590 1600}
efd900cb 1601
1602const char *
9bea1d5b 1603httpHeaderNameById(int id)
efd900cb 1604{
9bea1d5b 1605 if (!Headers)
62e76326 1606 Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END);
1607
9bea1d5b 1608 assert(id >= 0 && id < HDR_ENUM_END);
62e76326 1609
849b826a 1610 return Headers[id].name.buf();
efd900cb 1611}
0353e724 1612
924f73bc 1613int
a9925b40 1614HttpHeader::hasListMember(http_hdr_type id, const char *member, const char separator) const
924f73bc 1615{
1616 int result = 0;
1617 const char *pos = NULL;
1618 const char *item;
1619 int ilen;
1620 int mlen = strlen(member);
1621
924f73bc 1622 assert(id >= 0);
1623
a9925b40 1624 String header (getStrOrList(id));
924f73bc 1625
9d0cdbb9 1626 while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
1627 if (strncmp(item, member, mlen) == 0
1628 && (item[mlen] == '=' || item[mlen] == separator || item[mlen] == ';' || item[mlen] == '\0')) {
1629 result = 1;
1630 break;
1631 }
1632 }
1633
1634 return result;
1635}
1636
1637int
a9925b40 1638HttpHeader::hasByNameListMember(const char *name, const char *member, const char separator) const
9d0cdbb9 1639{
1640 int result = 0;
1641 const char *pos = NULL;
1642 const char *item;
1643 int ilen;
1644 int mlen = strlen(member);
1645
9d0cdbb9 1646 assert(name);
1647
a9925b40 1648 String header (getByName(name));
9d0cdbb9 1649
924f73bc 1650 while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
1651 if (strncmp(item, member, mlen) == 0
1652 && (item[mlen] == '=' || item[mlen] == separator || item[mlen] == ';' || item[mlen] == '\0')) {
1653 result = 1;
1654 break;
1655 }
1656 }
1657
1658 return result;
1659}
1660
1661void
1662HttpHeader::removeConnectionHeaderEntries()
1663{
a9925b40 1664 if (has(HDR_CONNECTION)) {
924f73bc 1665 /* anything that matches Connection list member will be deleted */
a9925b40 1666 String strConnection = getList(HDR_CONNECTION);
924f73bc 1667 const HttpHeaderEntry *e;
1668 HttpHeaderPos pos = HttpHeaderInitPos;
1669 /*
1670 * think: on-average-best nesting of the two loops (hdrEntry
1671 * and strListItem) @?@
1672 */
1673 /*
1674 * maybe we should delete standard stuff ("keep-alive","close")
1675 * from strConnection first?
1676 */
1677
a9925b40 1678 while ((e = getEntry(&pos))) {
924f73bc 1679 if (strListIsMember(&strConnection, e->name.buf(), ','))
a9925b40 1680 delAt(pos);
924f73bc 1681 }
1682
a9925b40 1683 delById(HDR_CONNECTION);
924f73bc 1684 strConnection.clean();
1685 }
1686}