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