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