]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpHeader.cc
cf_gen should use xcalloc() instead of calloc(), if for no other reason
[thirdparty/squid.git] / src / HttpHeader.cc
CommitLineData
4f7e9dbb 1
cb69b4c7 2/*
bbe58ab5 3 * $Id: HttpHeader.cc,v 1.106 2005/06/09 07:07:30 hno 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) {
9331e4b4 559 ssize_t l1, l2;
560 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));
561
562 if (!Config.onoff.relaxed_header_parser) {
563 httpHeaderEntryDestroy(e);
564 return httpHeaderReset(hdr);
565 }
566
567 if (!httpHeaderParseSize(e->value.buf(), &l1)) {
568 debug(55, 1)("WARNING: Unparseable content-length '%s'\n", e->value.buf());
569 httpHeaderEntryDestroy(e);
570 continue;
571 } else if (!httpHeaderParseSize(e2->value.buf(), &l2)) {
572 debug(55, 1)("WARNING: Unparseable content-length '%s'\n", e2->value.buf());
573 httpHeaderDelById(hdr, e2->id);
574 } else if (l1 > l2) {
575 httpHeaderDelById(hdr, e2->id);
576 } else {
577 httpHeaderEntryDestroy(e);
578 continue;
579 }
52d3f198 580 } else {
581 debug(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2)
582 ("NOTICE: found double content-length header\n");
b3123159 583
584 if (Config.onoff.relaxed_header_parser) {
585 httpHeaderEntryDestroy(e);
586 continue;
587 } else {
588 httpHeaderEntryDestroy(e);
589 return httpHeaderReset(hdr);
590 }
52d3f198 591 }
592 }
62e76326 593
52d3f198 594 if (e->id == HDR_OTHER && stringHasWhitespace(e->name.buf())) {
595 debug(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2)
596 ("WARNING: found whitespace in HTTP header name {%s}\n", getStringPrefix(field_start, field_end));
47ac2ebe 597
52d3f198 598 if (!Config.onoff.relaxed_header_parser) {
599 httpHeaderEntryDestroy(e);
600 return httpHeaderReset(hdr);
601 }
602 }
62e76326 603
52d3f198 604 httpHeaderAddEntry(hdr, e);
cb69b4c7 605 }
62e76326 606
9bea1d5b 607 return 1; /* even if no fields where found, it is a valid header */
cb69b4c7 608}
609
99edd1c3 610/* packs all the entries using supplied packer */
cb69b4c7 611void
9bea1d5b 612httpHeaderPackInto(const HttpHeader * hdr, Packer * p)
cb69b4c7 613{
9bea1d5b 614 HttpHeaderPos pos = HttpHeaderInitPos;
615 const HttpHeaderEntry *e;
616 assert(hdr && p);
617 debug(55, 7) ("packing hdr: (%p)\n", hdr);
618 /* pack all entries one by one */
62e76326 619
9bea1d5b 620 while ((e = httpHeaderGetEntry(hdr, &pos)))
62e76326 621 httpHeaderEntryPackInto(e, p);
cb69b4c7 622}
623
624/* returns next valid entry */
99edd1c3 625HttpHeaderEntry *
9bea1d5b 626httpHeaderGetEntry(const HttpHeader * hdr, HttpHeaderPos * pos)
cb69b4c7 627{
9bea1d5b 628 assert(hdr && pos);
732735ed 629 assert(*pos >= HttpHeaderInitPos && *pos < (ssize_t)hdr->entries.count);
62e76326 630
732735ed 631 for ((*pos)++; *pos < (ssize_t)hdr->entries.count; (*pos)++) {
62e76326 632 if (hdr->entries.items[*pos])
633 return (HttpHeaderEntry*)hdr->entries.items[*pos];
cb69b4c7 634 }
62e76326 635
9bea1d5b 636 return NULL;
cb69b4c7 637}
638
639/*
d8b249ef 640 * returns a pointer to a specified entry if any
641 * note that we return one entry so it does not make much sense to ask for
642 * "list" headers
cb69b4c7 643 */
de336bbe 644HttpHeaderEntry *
9bea1d5b 645httpHeaderFindEntry(const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 646{
9bea1d5b 647 HttpHeaderPos pos = HttpHeaderInitPos;
648 HttpHeaderEntry *e;
649 assert(hdr);
650 assert_eid(id);
651 assert(!CBIT_TEST(ListHeadersMask, id));
652
653 /* check mask first */
62e76326 654
9bea1d5b 655 if (!CBIT_TEST(hdr->mask, id))
62e76326 656 return NULL;
657
9bea1d5b 658 /* looks like we must have it, do linear search */
659 while ((e = httpHeaderGetEntry(hdr, &pos))) {
62e76326 660 if (e->id == id)
661 return e;
cb69b4c7 662 }
62e76326 663
9bea1d5b 664 /* hm.. we thought it was there, but it was not found */
665 assert(0);
62e76326 666
9bea1d5b 667 return NULL; /* not reached */
cb69b4c7 668}
669
a622fff0 670/*
671 * same as httpHeaderFindEntry
672 */
673static HttpHeaderEntry *
9bea1d5b 674httpHeaderFindLastEntry(const HttpHeader * hdr, http_hdr_type id)
a622fff0 675{
9bea1d5b 676 HttpHeaderPos pos = HttpHeaderInitPos;
677 HttpHeaderEntry *e;
678 HttpHeaderEntry *result = NULL;
679 assert(hdr);
680 assert_eid(id);
681 assert(!CBIT_TEST(ListHeadersMask, id));
682
683 /* check mask first */
62e76326 684
9bea1d5b 685 if (!CBIT_TEST(hdr->mask, id))
62e76326 686 return NULL;
687
9bea1d5b 688 /* looks like we must have it, do linear search */
689 while ((e = httpHeaderGetEntry(hdr, &pos))) {
62e76326 690 if (e->id == id)
691 result = e;
a622fff0 692 }
62e76326 693
9bea1d5b 694 assert(result); /* must be there! */
695 return result;
a622fff0 696}
697
cb69b4c7 698/*
d8b249ef 699 * deletes all fields with a given name if any, returns #fields deleted;
cb69b4c7 700 */
2ac76861 701int
9bea1d5b 702httpHeaderDelByName(HttpHeader * hdr, const char *name)
cb69b4c7 703{
9bea1d5b 704 int count = 0;
705 HttpHeaderPos pos = HttpHeaderInitPos;
706 HttpHeaderEntry *e;
707 httpHeaderMaskInit(&hdr->mask, 0); /* temporal inconsistency */
708 debug(55, 7) ("deleting '%s' fields in hdr %p\n", name, hdr);
62e76326 709
9bea1d5b 710 while ((e = httpHeaderGetEntry(hdr, &pos))) {
650c4b88 711 if (!e->name.caseCmp(name)) {
62e76326 712 httpHeaderDelAt(hdr, pos);
713 count++;
714 } else
715 CBIT_SET(hdr->mask, e->id);
cb69b4c7 716 }
62e76326 717
9bea1d5b 718 return count;
cb69b4c7 719}
720
2246b732 721/* deletes all entries with a given id, returns the #entries deleted */
722int
9bea1d5b 723httpHeaderDelById(HttpHeader * hdr, http_hdr_type id)
d8b249ef 724{
9bea1d5b 725 int count = 0;
726 HttpHeaderPos pos = HttpHeaderInitPos;
727 HttpHeaderEntry *e;
728 debug(55, 8) ("%p del-by-id %d\n", hdr, id);
729 assert(hdr);
730 assert_eid(id);
e6ca7a9b 731 assert(id != HDR_OTHER); /* does not make sense */
62e76326 732
9bea1d5b 733 if (!CBIT_TEST(hdr->mask, id))
62e76326 734 return 0;
735
9bea1d5b 736 while ((e = httpHeaderGetEntry(hdr, &pos))) {
62e76326 737 if (e->id == id) {
738 httpHeaderDelAt(hdr, pos);
739 count++;
740 }
d8b249ef 741 }
62e76326 742
9bea1d5b 743 CBIT_CLR(hdr->mask, id);
744 assert(count);
745 return count;
d8b249ef 746}
d8b249ef 747
cb69b4c7 748/*
749 * deletes an entry at pos and leaves a gap; leaving a gap makes it
750 * possible to iterate(search) and delete fields at the same time
751 */
2246b732 752void
9bea1d5b 753httpHeaderDelAt(HttpHeader * hdr, HttpHeaderPos pos)
cb69b4c7 754{
9bea1d5b 755 HttpHeaderEntry *e;
732735ed 756 assert(pos >= HttpHeaderInitPos && pos < (ssize_t)hdr->entries.count);
e6ccf245 757 e = (HttpHeaderEntry*)hdr->entries.items[pos];
9bea1d5b 758 hdr->entries.items[pos] = NULL;
759 /* decrement header length, allow for ": " and crlf */
528b2c61 760 hdr->len -= e->name.size() + 2 + e->value.size() + 2;
9bea1d5b 761 assert(hdr->len >= 0);
762 httpHeaderEntryDestroy(e);
cb69b4c7 763}
764
99edd1c3 765
62e76326 766/* appends an entry;
99edd1c3 767 * does not call httpHeaderEntryClone() so one should not reuse "*e"
cb69b4c7 768 */
99edd1c3 769void
9bea1d5b 770httpHeaderAddEntry(HttpHeader * hdr, HttpHeaderEntry * e)
cb69b4c7 771{
9bea1d5b 772 assert(hdr && e);
773 assert_eid(e->id);
774
e4049756 775 debugs(55, 7, hdr << " adding entry: " << e->id << " at " <<
776 hdr->entries.count);
62e76326 777
9bea1d5b 778 if (CBIT_TEST(hdr->mask, e->id))
62e76326 779 Headers[e->id].stat.repCount++;
9bea1d5b 780 else
62e76326 781 CBIT_SET(hdr->mask, e->id);
782
75faaa7a 783 hdr->entries.push_back(e);
62e76326 784
9bea1d5b 785 /* increment header length, allow for ": " and crlf */
528b2c61 786 hdr->len += e->name.size() + 2 + e->value.size() + 2;
cb69b4c7 787}
788
bbe58ab5 789/* inserts an entry;
790 * does not call httpHeaderEntryClone() so one should not reuse "*e"
791 */
792void
793httpHeaderInsertEntry(HttpHeader * hdr, HttpHeaderEntry * e)
794{
795 assert(hdr && e);
796 assert_eid(e->id);
797
798 debugs(55, 7, hdr << " adding entry: " << e->id << " at " <<
799 hdr->entries.count);
800
801 if (CBIT_TEST(hdr->mask, e->id))
802 Headers[e->id].stat.repCount++;
803 else
804 CBIT_SET(hdr->mask, e->id);
805
806 hdr->entries.insert(e);
807
808 /* increment header length, allow for ": " and crlf */
809 hdr->len += e->name.size() + 2 + e->value.size() + 2;
810}
811
99edd1c3 812/* return a list of entries with the same id separated by ',' and ws */
813String
9bea1d5b 814httpHeaderGetList(const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 815{
9bea1d5b 816 HttpHeaderEntry *e;
817 HttpHeaderPos pos = HttpHeaderInitPos;
818 debug(55, 6) ("%p: joining for id %d\n", hdr, id);
819 /* only fields from ListHeaders array can be "listed" */
820 assert(CBIT_TEST(ListHeadersMask, id));
62e76326 821
9bea1d5b 822 if (!CBIT_TEST(hdr->mask, id))
650c4b88 823 return String();
824
825 String s;
62e76326 826
9bea1d5b 827 while ((e = httpHeaderGetEntry(hdr, &pos))) {
62e76326 828 if (e->id == id)
829 strListAdd(&s, e->value.buf(), ',');
d35b9a94 830 }
62e76326 831
9bea1d5b 832 /*
833 * note: we might get an empty (len==0) string if there was an "empty"
834 * header; we must not get a NULL string though.
835 */
528b2c61 836 assert(s.buf());
62e76326 837
9bea1d5b 838 /* temporary warning: remove it! @?@ @?@ @?@ */
528b2c61 839 if (!s.size())
62e76326 840 debug(55, 3) ("empty list header: %s (%d)\n", Headers[id].name.buf(), id);
841
528b2c61 842 debug(55, 6) ("%p: joined for id %d: %s\n", hdr, id, s.buf());
62e76326 843
9bea1d5b 844 return s;
cb69b4c7 845}
846
f66a9ef4 847/* return a string or list of entries with the same id separated by ',' and ws */
848String
9bea1d5b 849httpHeaderGetStrOrList(const HttpHeader * hdr, http_hdr_type id)
f66a9ef4 850{
c7327fa0 851 HttpHeaderEntry *e;
9bea1d5b 852
853 if (CBIT_TEST(ListHeadersMask, id))
62e76326 854 return httpHeaderGetList(hdr, id);
855
528b2c61 856 if ((e = httpHeaderFindEntry(hdr, id)))
62e76326 857 return e->value;
858
650c4b88 859 return String();
f66a9ef4 860}
861
862/*
bd412580 863 * Returns the value of the specified header.
f66a9ef4 864 */
865String
9bea1d5b 866httpHeaderGetByName(const HttpHeader * hdr, const char *name)
f66a9ef4 867{
9bea1d5b 868 http_hdr_type id;
869 HttpHeaderPos pos = HttpHeaderInitPos;
870 HttpHeaderEntry *e;
9bea1d5b 871
872 assert(hdr);
873 assert(name);
874
875 /* First try the quick path */
876 id = httpHeaderIdByNameDef(name, strlen(name));
62e76326 877
9bea1d5b 878 if (id != -1)
62e76326 879 return httpHeaderGetStrOrList(hdr, id);
9bea1d5b 880
650c4b88 881 String result;
882
9bea1d5b 883 /* Sorry, an unknown header name. Do linear search */
884 while ((e = httpHeaderGetEntry(hdr, &pos))) {
650c4b88 885 if (e->id == HDR_OTHER && e->name.caseCmp(name) == 0) {
62e76326 886 strListAdd(&result, e->value.buf(), ',');
887 }
f66a9ef4 888 }
62e76326 889
9bea1d5b 890 return result;
f66a9ef4 891}
cb69b4c7 892
14b463aa 893/*
372fdfbf 894 * Returns a the value of the specified list member, if any.
14b463aa 895 */
896String
897httpHeaderGetByNameListMember(const HttpHeader * hdr, const char *name, const char *member, const char separator)
898{
14b463aa 899 String header;
900 const char *pos = NULL;
901 const char *item;
902 int ilen;
903 int mlen = strlen(member);
904
905 assert(hdr);
906 assert(name);
907
908 header = httpHeaderGetByName(hdr, name);
909
650c4b88 910 String result;
911
14b463aa 912 while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
62e76326 913 if (strncmp(item, member, mlen) == 0 && item[mlen] == '=') {
914 result.append(item + mlen + 1, ilen - mlen - 1);
915 break;
916 }
14b463aa 917 }
62e76326 918
14b463aa 919 return result;
920}
921
922/*
923 * returns a the value of the specified list member, if any.
924 */
925String
926httpHeaderGetListMember(const HttpHeader * hdr, http_hdr_type id, const char *member, const char separator)
927{
14b463aa 928 String header;
929 const char *pos = NULL;
930 const char *item;
931 int ilen;
932 int mlen = strlen(member);
933
934 assert(hdr);
935 assert(id >= 0);
936
937 header = httpHeaderGetStrOrList(hdr, id);
650c4b88 938 String result;
14b463aa 939
940 while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
62e76326 941 if (strncmp(item, member, mlen) == 0 && item[mlen] == '=') {
942 result.append(item + mlen + 1, ilen - mlen - 1);
943 break;
944 }
14b463aa 945 }
62e76326 946
528b2c61 947 header.clean();
14b463aa 948 return result;
949}
950
cb69b4c7 951/* test if a field is present */
2ac76861 952int
9bea1d5b 953httpHeaderHas(const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 954{
9bea1d5b 955 assert(hdr);
956 assert_eid(id);
957 assert(id != HDR_OTHER);
958 debug(55, 7) ("%p lookup for %d\n", hdr, id);
959 return CBIT_TEST(hdr->mask, id);
cb69b4c7 960}
961
962void
9bea1d5b 963httpHeaderPutInt(HttpHeader * hdr, http_hdr_type id, int number)
cb69b4c7 964{
9bea1d5b 965 assert_eid(id);
966 assert(Headers[id].type == ftInt); /* must be of an appropriate type */
967 assert(number >= 0);
968 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(id, NULL, xitoa(number)));
cb69b4c7 969}
970
971void
9bea1d5b 972httpHeaderPutTime(HttpHeader * hdr, http_hdr_type id, time_t htime)
cb69b4c7 973{
9bea1d5b 974 assert_eid(id);
975 assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */
a1d6870f 976 assert(htime >= 0);
9bea1d5b 977 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(id, NULL, mkrfc1123(htime)));
cb69b4c7 978}
2ac76861 979
bbe58ab5 980void
981httpHeaderInsertTime(HttpHeader * hdr, http_hdr_type id, time_t htime)
982{
983 assert_eid(id);
984 assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */
985 assert(htime >= 0);
986 httpHeaderInsertEntry(hdr, httpHeaderEntryCreate(id, NULL, mkrfc1123(htime)));
987}
988
cb69b4c7 989void
9bea1d5b 990httpHeaderPutStr(HttpHeader * hdr, http_hdr_type id, const char *str)
cb69b4c7 991{
9bea1d5b 992 assert_eid(id);
993 assert(Headers[id].type == ftStr); /* must be of an appropriate type */
994 assert(str);
995 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(id, NULL, str));
cb69b4c7 996}
997
63259c34 998void
9bea1d5b 999httpHeaderPutAuth(HttpHeader * hdr, const char *auth_scheme, const char *realm)
63259c34 1000{
9bea1d5b 1001 assert(hdr && auth_scheme && realm);
1002 httpHeaderPutStrf(hdr, HDR_WWW_AUTHENTICATE, "%s realm=\"%s\"", auth_scheme, realm);
63259c34 1003}
1004
99edd1c3 1005void
9bea1d5b 1006httpHeaderPutCc(HttpHeader * hdr, const HttpHdrCc * cc)
99edd1c3 1007{
9bea1d5b 1008 MemBuf mb;
1009 Packer p;
1010 assert(hdr && cc);
1011 /* remove old directives if any */
1012 httpHeaderDelById(hdr, HDR_CACHE_CONTROL);
1013 /* pack into mb */
1014 memBufDefInit(&mb);
1015 packerToMemInit(&p, &mb);
1016 httpHdrCcPackInto(cc, &p);
1017 /* put */
1018 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_CACHE_CONTROL, NULL, mb.buf));
1019 /* cleanup */
1020 packerClean(&p);
1021 memBufClean(&mb);
99edd1c3 1022}
1023
d192d11f 1024void
9bea1d5b 1025httpHeaderPutContRange(HttpHeader * hdr, const HttpHdrContRange * cr)
d192d11f 1026{
9bea1d5b 1027 MemBuf mb;
1028 Packer p;
1029 assert(hdr && cr);
1030 /* remove old directives if any */
1031 httpHeaderDelById(hdr, HDR_CONTENT_RANGE);
1032 /* pack into mb */
1033 memBufDefInit(&mb);
1034 packerToMemInit(&p, &mb);
1035 httpHdrContRangePackInto(cr, &p);
1036 /* put */
1037 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_CONTENT_RANGE, NULL, mb.buf));
1038 /* cleanup */
1039 packerClean(&p);
1040 memBufClean(&mb);
d192d11f 1041}
1042
1043void
9bea1d5b 1044httpHeaderPutRange(HttpHeader * hdr, const HttpHdrRange * range)
d192d11f 1045{
9bea1d5b 1046 MemBuf mb;
1047 Packer p;
1048 assert(hdr && range);
1049 /* remove old directives if any */
1050 httpHeaderDelById(hdr, HDR_RANGE);
1051 /* pack into mb */
1052 memBufDefInit(&mb);
1053 packerToMemInit(&p, &mb);
528b2c61 1054 range->packInto(&p);
9bea1d5b 1055 /* put */
1056 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_RANGE, NULL, mb.buf));
1057 /* cleanup */
1058 packerClean(&p);
1059 memBufClean(&mb);
d192d11f 1060}
1061
43ae1d95 1062void
1063httpHeaderPutSc(HttpHeader *hdr, const HttpHdrSc *sc)
1064{
1065 MemBuf mb;
1066 Packer p;
1067 assert(hdr && sc);
1068 /* remove old directives if any */
1069 httpHeaderDelById(hdr, HDR_RANGE);
1070 /* pack into mb */
1071 memBufDefInit(&mb);
1072 packerToMemInit(&p, &mb);
1073 httpHdrScPackInto(sc, &p);
1074 /* put */
1075 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_SURROGATE_CONTROL, NULL, mb.buf));
1076 /* cleanup */
1077 packerClean(&p);
1078 memBufClean(&mb);
1079}
1080
cb69b4c7 1081/* add extension header (these fields are not parsed/analyzed/joined, etc.) */
1082void
9bea1d5b 1083httpHeaderPutExt(HttpHeader * hdr, const char *name, const char *value)
cb69b4c7 1084{
9bea1d5b 1085 assert(name && value);
1086 debug(55, 8) ("%p adds ext entry '%s: %s'\n", hdr, name, value);
1087 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_OTHER, name, value));
cb69b4c7 1088}
1089
7c525cc2 1090int
528b2c61 1091httpHeaderEntryGetInt (const HttpHeaderEntry * e)
7c525cc2 1092{
9bea1d5b 1093 int value = -1;
1094 int ok;
528b2c61 1095 assert (e);
1096 assert_eid (e->id);
1097 assert (Headers[e->id].type == ftInt);
1098 ok = httpHeaderParseInt(e->value.buf(), &value);
1099 httpHeaderNoteParsedEntry(e->id, e->value, !ok);
1100 /* XXX: Should we check ok - ie
1101 * return ok ? -1 : value;
1102 */
1103 return value;
1104}
1105
1106int
1107httpHeaderGetInt(const HttpHeader * hdr, http_hdr_type id)
1108{
9bea1d5b 1109 assert_eid(id);
1110 assert(Headers[id].type == ftInt); /* must be of an appropriate type */
528b2c61 1111 HttpHeaderEntry *e;
62e76326 1112
528b2c61 1113 if ((e = httpHeaderFindEntry(hdr, id)))
62e76326 1114 return httpHeaderEntryGetInt (e);
1115
528b2c61 1116 return -1;
7c525cc2 1117}
1118
de336bbe 1119time_t
9bea1d5b 1120httpHeaderGetTime(const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 1121{
9bea1d5b 1122 HttpHeaderEntry *e;
1123 time_t value = -1;
1124 assert_eid(id);
1125 assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */
62e76326 1126
9bea1d5b 1127 if ((e = httpHeaderFindEntry(hdr, id))) {
62e76326 1128 value = parse_rfc1123(e->value.buf());
1129 httpHeaderNoteParsedEntry(e->id, e->value, value < 0);
d8b249ef 1130 }
62e76326 1131
9bea1d5b 1132 return value;
cb69b4c7 1133}
1134
99edd1c3 1135/* sync with httpHeaderGetLastStr */
de336bbe 1136const char *
9bea1d5b 1137httpHeaderGetStr(const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 1138{
9bea1d5b 1139 HttpHeaderEntry *e;
1140 assert_eid(id);
1141 assert(Headers[id].type == ftStr); /* must be of an appropriate type */
62e76326 1142
9bea1d5b 1143 if ((e = httpHeaderFindEntry(hdr, id))) {
62e76326 1144 httpHeaderNoteParsedEntry(e->id, e->value, 0); /* no errors are possible */
1145 return e->value.buf();
d8b249ef 1146 }
62e76326 1147
9bea1d5b 1148 return NULL;
cb69b4c7 1149}
1150
a622fff0 1151/* unusual */
1152const char *
9bea1d5b 1153httpHeaderGetLastStr(const HttpHeader * hdr, http_hdr_type id)
a622fff0 1154{
9bea1d5b 1155 HttpHeaderEntry *e;
1156 assert_eid(id);
1157 assert(Headers[id].type == ftStr); /* must be of an appropriate type */
62e76326 1158
9bea1d5b 1159 if ((e = httpHeaderFindLastEntry(hdr, id))) {
62e76326 1160 httpHeaderNoteParsedEntry(e->id, e->value, 0); /* no errors are possible */
1161 return e->value.buf();
a622fff0 1162 }
62e76326 1163
9bea1d5b 1164 return NULL;
a622fff0 1165}
1166
7faf2bdb 1167HttpHdrCc *
9bea1d5b 1168httpHeaderGetCc(const HttpHeader * hdr)
cb69b4c7 1169{
9bea1d5b 1170 HttpHdrCc *cc;
1171 String s;
62e76326 1172
9bea1d5b 1173 if (!CBIT_TEST(hdr->mask, HDR_CACHE_CONTROL))
62e76326 1174 return NULL;
1175
9bea1d5b 1176 s = httpHeaderGetList(hdr, HDR_CACHE_CONTROL);
62e76326 1177
9bea1d5b 1178 cc = httpHdrCcParseCreate(&s);
62e76326 1179
9bea1d5b 1180 HttpHeaderStats[hdr->owner].ccParsedCount++;
62e76326 1181
9bea1d5b 1182 if (cc)
62e76326 1183 httpHdrCcUpdateStats(cc, &HttpHeaderStats[hdr->owner].ccTypeDistr);
1184
9bea1d5b 1185 httpHeaderNoteParsedEntry(HDR_CACHE_CONTROL, s, !cc);
62e76326 1186
528b2c61 1187 s.clean();
62e76326 1188
9bea1d5b 1189 return cc;
cb69b4c7 1190}
1191
02922e76 1192HttpHdrRange *
9bea1d5b 1193httpHeaderGetRange(const HttpHeader * hdr)
02922e76 1194{
9bea1d5b 1195 HttpHdrRange *r = NULL;
1196 HttpHeaderEntry *e;
1197 /* some clients will send "Request-Range" _and_ *matching* "Range"
1198 * who knows, some clients might send Request-Range only;
1199 * this "if" should work correctly in both cases;
1200 * hopefully no clients send mismatched headers! */
62e76326 1201
9bea1d5b 1202 if ((e = httpHeaderFindEntry(hdr, HDR_RANGE)) ||
62e76326 1203 (e = httpHeaderFindEntry(hdr, HDR_REQUEST_RANGE))) {
1204 r = HttpHdrRange::ParseCreate(&e->value);
1205 httpHeaderNoteParsedEntry(e->id, e->value, !r);
d192d11f 1206 }
62e76326 1207
9bea1d5b 1208 return r;
02922e76 1209}
1210
43ae1d95 1211HttpHdrSc *
1212httpHeaderGetSc(const HttpHeader *hdr)
1213{
1214 if (!CBIT_TEST(hdr->mask, HDR_SURROGATE_CONTROL))
1215 return NULL;
1216
1217 String s (httpHeaderGetList(hdr, HDR_SURROGATE_CONTROL));
1218
1219 HttpHdrSc *sc = httpHdrScParseCreate(&s);
1220
1221 HttpHeaderStats[hdr->owner].ccParsedCount++;
1222
1223 if (sc)
1224 httpHdrScUpdateStats(sc, &HttpHeaderStats[hdr->owner].scTypeDistr);
1225
1226 httpHeaderNoteParsedEntry(HDR_SURROGATE_CONTROL, s, !sc);
1227
1228 return sc;
1229}
1230
d76fcfa7 1231HttpHdrContRange *
9bea1d5b 1232httpHeaderGetContRange(const HttpHeader * hdr)
d76fcfa7 1233{
9bea1d5b 1234 HttpHdrContRange *cr = NULL;
1235 HttpHeaderEntry *e;
62e76326 1236
9bea1d5b 1237 if ((e = httpHeaderFindEntry(hdr, HDR_CONTENT_RANGE))) {
62e76326 1238 cr = httpHdrContRangeParseCreate(e->value.buf());
1239 httpHeaderNoteParsedEntry(e->id, e->value, !cr);
d8b249ef 1240 }
62e76326 1241
9bea1d5b 1242 return cr;
cb69b4c7 1243}
1244
99edd1c3 1245const char *
9bea1d5b 1246httpHeaderGetAuth(const HttpHeader * hdr, http_hdr_type id, const char *auth_scheme)
cb69b4c7 1247{
9bea1d5b 1248 const char *field;
1249 int l;
1250 assert(hdr && auth_scheme);
1251 field = httpHeaderGetStr(hdr, id);
62e76326 1252
9bea1d5b 1253 if (!field) /* no authorization field */
62e76326 1254 return NULL;
1255
9bea1d5b 1256 l = strlen(auth_scheme);
62e76326 1257
9bea1d5b 1258 if (!l || strncasecmp(field, auth_scheme, l)) /* wrong scheme */
62e76326 1259 return NULL;
1260
9bea1d5b 1261 field += l;
62e76326 1262
9bea1d5b 1263 if (!xisspace(*field)) /* wrong scheme */
62e76326 1264 return NULL;
1265
9bea1d5b 1266 /* skip white space */
1267 field += xcountws(field);
62e76326 1268
9bea1d5b 1269 if (!*field) /* no authorization cookie */
62e76326 1270 return NULL;
1271
9bea1d5b 1272 return base64_decode(field);
cb69b4c7 1273}
1274
a9771e51 1275ETag
9bea1d5b 1276httpHeaderGetETag(const HttpHeader * hdr, http_hdr_type id)
a9771e51 1277{
9bea1d5b 1278 ETag etag =
62e76326 1279 {NULL, -1};
9bea1d5b 1280 HttpHeaderEntry *e;
1281 assert(Headers[id].type == ftETag); /* must be of an appropriate type */
62e76326 1282
9bea1d5b 1283 if ((e = httpHeaderFindEntry(hdr, id)))
62e76326 1284 etagParseInit(&etag, e->value.buf());
1285
9bea1d5b 1286 return etag;
a9771e51 1287}
1288
1289TimeOrTag
9bea1d5b 1290httpHeaderGetTimeOrTag(const HttpHeader * hdr, http_hdr_type id)
a9771e51 1291{
9bea1d5b 1292 TimeOrTag tot;
1293 HttpHeaderEntry *e;
1294 assert(Headers[id].type == ftDate_1123_or_ETag); /* must be of an appropriate type */
1295 memset(&tot, 0, sizeof(tot));
62e76326 1296
9bea1d5b 1297 if ((e = httpHeaderFindEntry(hdr, id))) {
62e76326 1298 const char *str = e->value.buf();
1299 /* try as an ETag */
1300
1301 if (etagParseInit(&tot.tag, str)) {
1302 tot.valid = tot.tag.str != NULL;
1303 tot.time = -1;
1304 } else {
1305 /* or maybe it is time? */
1306 tot.time = parse_rfc1123(str);
1307 tot.valid = tot.time >= 0;
1308 tot.tag.str = NULL;
1309 }
a9771e51 1310 }
62e76326 1311
9bea1d5b 1312 assert(tot.time < 0 || !tot.tag.str); /* paranoid */
1313 return tot;
a9771e51 1314}
1315
cb69b4c7 1316/*
1317 * HttpHeaderEntry
1318 */
1319
d8b249ef 1320static HttpHeaderEntry *
9bea1d5b 1321httpHeaderEntryCreate(http_hdr_type id, const char *name, const char *value)
cb69b4c7 1322{
9bea1d5b 1323 HttpHeaderEntry *e;
1324 assert_eid(id);
0353e724 1325 e = new HttpHeaderEntry;
9bea1d5b 1326 e->id = id;
62e76326 1327
9bea1d5b 1328 if (id != HDR_OTHER)
62e76326 1329 e->name = Headers[id].name;
9bea1d5b 1330 else
62e76326 1331 e->name = name;
1332
528b2c61 1333 e->value = value;
62e76326 1334
9bea1d5b 1335 Headers[id].stat.aliveCount++;
62e76326 1336
528b2c61 1337 debug(55, 9) ("created entry %p: '%s: %s'\n", e, e->name.buf(), e->value.buf());
62e76326 1338
9bea1d5b 1339 return e;
cb69b4c7 1340}
1341
de336bbe 1342static void
9bea1d5b 1343httpHeaderEntryDestroy(HttpHeaderEntry * e)
2ac76861 1344{
9bea1d5b 1345 assert(e);
1346 assert_eid(e->id);
528b2c61 1347 debug(55, 9) ("destroying entry %p: '%s: %s'\n", e, e->name.buf(), e->value.buf());
9bea1d5b 1348 /* clean name if needed */
62e76326 1349
9bea1d5b 1350 if (e->id == HDR_OTHER)
62e76326 1351 e->name.clean();
1352
528b2c61 1353 e->value.clean();
62e76326 1354
9bea1d5b 1355 assert(Headers[e->id].stat.aliveCount);
62e76326 1356
9bea1d5b 1357 Headers[e->id].stat.aliveCount--;
62e76326 1358
e6ccf245 1359 e->id = HDR_BAD_HDR;
62e76326 1360
0353e724 1361 delete e;
cb69b4c7 1362}
1363
d8b249ef 1364/* parses and inits header entry, returns new entry on success */
1365static HttpHeaderEntry *
9bea1d5b 1366httpHeaderEntryParseCreate(const char *field_start, const char *field_end)
cb69b4c7 1367{
9bea1d5b 1368 HttpHeaderEntry *e;
e6ccf245 1369 http_hdr_type id;
9bea1d5b 1370 /* note: name_start == field_start */
52d3f198 1371 const char *name_end = (const char *)memchr(field_start, ':', field_end - field_start);
1372 int name_len = name_end ? name_end - field_start : 0;
9bea1d5b 1373 const char *value_start = field_start + name_len + 1; /* skip ':' */
1374 /* note: value_end == field_end */
1375
1376 HeaderEntryParsedCount++;
1377
1378 /* do we have a valid field name within this field? */
62e76326 1379
9bea1d5b 1380 if (!name_len || name_end > field_end)
62e76326 1381 return NULL;
1382
67746eea 1383 if (name_len > 65534) {
1384 /* String must be LESS THAN 64K and it adds a terminating NULL */
62e76326 1385 debug(55, 1) ("WARNING: ignoring header name of %d bytes\n", name_len);
1386 return NULL;
25acfb53 1387 }
62e76326 1388
52d3f198 1389 if (Config.onoff.relaxed_header_parser && xisspace(field_start[name_len - 1])) {
1390 debug(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2)
1391 ("NOTICE: Whitespace after header name in '%s'\n", getStringPrefix(field_start, field_end));
1392
1393 while (name_len > 0 && xisspace(field_start[name_len - 1]))
1394 name_len--;
1395
1396 if (!name_len)
1397 return NULL;
1398 }
1399
9bea1d5b 1400 /* now we know we can parse it */
0353e724 1401 e = new HttpHeaderEntry;
62e76326 1402
9bea1d5b 1403 debug(55, 9) ("creating entry %p: near '%s'\n", e, getStringPrefix(field_start, field_end));
62e76326 1404
9bea1d5b 1405 /* is it a "known" field? */
1406 id = httpHeaderIdByName(field_start, name_len, Headers, HDR_ENUM_END);
62e76326 1407
9bea1d5b 1408 if (id < 0)
62e76326 1409 id = HDR_OTHER;
1410
9bea1d5b 1411 assert_eid(id);
62e76326 1412
9bea1d5b 1413 e->id = id;
62e76326 1414
9bea1d5b 1415 /* set field name */
1416 if (id == HDR_OTHER)
62e76326 1417 e->name.limitInit(field_start, name_len);
9bea1d5b 1418 else
62e76326 1419 e->name = Headers[id].name;
1420
9bea1d5b 1421 /* trim field value */
1422 while (value_start < field_end && xisspace(*value_start))
62e76326 1423 value_start++;
1424
52d3f198 1425 while (value_start < field_end && xisspace(field_end[-1]))
1426 field_end--;
1427
67746eea 1428 if (field_end - value_start > 65534) {
1429 /* String must be LESS THAN 64K and it adds a terminating NULL */
62e76326 1430 debug(55, 1) ("WARNING: ignoring '%s' header of %d bytes\n",
1431 e->name.buf(), (int) (field_end - value_start));
1432
1433 if (e->id == HDR_OTHER)
1434 e->name.clean();
1435
0353e724 1436 delete e;
62e76326 1437
1438 return NULL;
25acfb53 1439 }
62e76326 1440
9bea1d5b 1441 /* set field value */
528b2c61 1442 e->value.limitInit(value_start, field_end - value_start);
62e76326 1443
9bea1d5b 1444 Headers[id].stat.seenCount++;
62e76326 1445
9bea1d5b 1446 Headers[id].stat.aliveCount++;
62e76326 1447
528b2c61 1448 debug(55, 9) ("created entry %p: '%s: %s'\n", e, e->name.buf(), e->value.buf());
62e76326 1449
9bea1d5b 1450 return e;
cb69b4c7 1451}
1452
99edd1c3 1453HttpHeaderEntry *
9bea1d5b 1454httpHeaderEntryClone(const HttpHeaderEntry * e)
cb69b4c7 1455{
528b2c61 1456 return httpHeaderEntryCreate(e->id, e->name.buf(), e->value.buf());
cb69b4c7 1457}
1458
de336bbe 1459void
9bea1d5b 1460httpHeaderEntryPackInto(const HttpHeaderEntry * e, Packer * p)
cb69b4c7 1461{
9bea1d5b 1462 assert(e && p);
528b2c61 1463 packerAppend(p, e->name.buf(), e->name.size());
9bea1d5b 1464 packerAppend(p, ": ", 2);
528b2c61 1465 packerAppend(p, e->value.buf(), e->value.size());
9bea1d5b 1466 packerAppend(p, "\r\n", 2);
cb69b4c7 1467}
1468
1469static void
9bea1d5b 1470httpHeaderNoteParsedEntry(http_hdr_type id, String context, int error)
cb69b4c7 1471{
9bea1d5b 1472 Headers[id].stat.parsCount++;
62e76326 1473
9bea1d5b 1474 if (error) {
62e76326 1475 Headers[id].stat.errCount++;
1476 debug(55, 2) ("cannot parse hdr field: '%s: %s'\n",
1477 Headers[id].name.buf(), context.buf());
cb69b4c7 1478 }
cb69b4c7 1479}
1480
cb69b4c7 1481/*
12cf1be2 1482 * Reports
cb69b4c7 1483 */
cb69b4c7 1484
fcd2d3ef 1485/* tmp variable used to pass stat info to dumpers */
0cdcddb9 1486extern const HttpHeaderStat *dump_stat; /* argh! */
fcd2d3ef 1487const HttpHeaderStat *dump_stat = NULL;
1488
cb69b4c7 1489static void
9bea1d5b 1490httpHeaderFieldStatDumper(StoreEntry * sentry, int idx, double val, double size, int count)
cb69b4c7 1491{
9bea1d5b 1492 const int id = (int) val;
1493 const int valid_id = id >= 0 && id < HDR_ENUM_END;
528b2c61 1494 const char *name = valid_id ? Headers[id].name.buf() : "INVALID";
9bea1d5b 1495 int visible = count > 0;
1496 /* for entries with zero count, list only those that belong to current type of message */
62e76326 1497
9bea1d5b 1498 if (!visible && valid_id && dump_stat->owner_mask)
62e76326 1499 visible = CBIT_TEST(*dump_stat->owner_mask, id);
1500
9bea1d5b 1501 if (visible)
62e76326 1502 storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
1503 id, name, count, xdiv(count, dump_stat->busyDestroyedCount));
cb69b4c7 1504}
1505
12cf1be2 1506static void
9bea1d5b 1507httpHeaderFldsPerHdrDumper(StoreEntry * sentry, int idx, double val, double size, int count)
cb69b4c7 1508{
9bea1d5b 1509 if (count)
62e76326 1510 storeAppendPrintf(sentry, "%2d\t %5d\t %5d\t %6.2f\n",
1511 idx, (int) val, count,
1512 xpercent(count, dump_stat->destroyedCount));
cb69b4c7 1513}
1514
1515
1516static void
9bea1d5b 1517httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e)
12cf1be2 1518{
9bea1d5b 1519 assert(hs && e);
1520
1521 dump_stat = hs;
1522 storeAppendPrintf(e, "\nHeader Stats: %s\n", hs->label);
1523 storeAppendPrintf(e, "\nField type distribution\n");
1524 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n",
62e76326 1525 "id", "name", "count", "#/header");
9bea1d5b 1526 statHistDump(&hs->fieldTypeDistr, e, httpHeaderFieldStatDumper);
1527 storeAppendPrintf(e, "\nCache-control directives distribution\n");
1528 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n",
62e76326 1529 "id", "name", "count", "#/cc_field");
9bea1d5b 1530 statHistDump(&hs->ccTypeDistr, e, httpHdrCcStatDumper);
43ae1d95 1531 storeAppendPrintf(e, "\nSurrogate-control directives distribution\n");
1532 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n",
1533 "id", "name", "count", "#/sc_field");
1534 statHistDump(&hs->scTypeDistr, e, httpHdrScStatDumper);
9bea1d5b 1535 storeAppendPrintf(e, "\nNumber of fields per header distribution\n");
1536 storeAppendPrintf(e, "%2s\t %-5s\t %5s\t %6s\n",
62e76326 1537 "id", "#flds", "count", "%total");
9bea1d5b 1538 statHistDump(&hs->hdrUCountDistr, e, httpHeaderFldsPerHdrDumper);
1539 dump_stat = NULL;
cb69b4c7 1540}
1541
12cf1be2 1542void
9bea1d5b 1543httpHeaderStoreReport(StoreEntry * e)
cb69b4c7 1544{
9bea1d5b 1545 int i;
1546 http_hdr_type ht;
1547 assert(e);
1548
1549 HttpHeaderStats[0].parsedCount =
62e76326 1550 HttpHeaderStats[hoRequest].parsedCount + HttpHeaderStats[hoReply].parsedCount;
9bea1d5b 1551 HttpHeaderStats[0].ccParsedCount =
62e76326 1552 HttpHeaderStats[hoRequest].ccParsedCount + HttpHeaderStats[hoReply].ccParsedCount;
9bea1d5b 1553 HttpHeaderStats[0].destroyedCount =
62e76326 1554 HttpHeaderStats[hoRequest].destroyedCount + HttpHeaderStats[hoReply].destroyedCount;
9bea1d5b 1555 HttpHeaderStats[0].busyDestroyedCount =
62e76326 1556 HttpHeaderStats[hoRequest].busyDestroyedCount + HttpHeaderStats[hoReply].busyDestroyedCount;
9bea1d5b 1557
1558 for (i = 1; i < HttpHeaderStatCount; i++) {
62e76326 1559 httpHeaderStatDump(HttpHeaderStats + i, e);
1560 storeAppendPrintf(e, "%s\n", "<br>");
12cf1be2 1561 }
62e76326 1562
9bea1d5b 1563 /* field stats for all messages */
1564 storeAppendPrintf(e, "\nHttp Fields Stats (replies and requests)\n");
62e76326 1565
9bea1d5b 1566 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\t %6s\n",
62e76326 1567 "id", "name", "#alive", "%err", "%repeat");
1568
e6ccf245 1569 for (ht = (http_hdr_type)0; ht < HDR_ENUM_END; ++ht) {
62e76326 1570 HttpHeaderFieldInfo *f = Headers + ht;
1571 storeAppendPrintf(e, "%2d\t %-20s\t %5d\t %6.3f\t %6.3f\n",
1572 f->id, f->name.buf(), f->stat.aliveCount,
1573 xpercent(f->stat.errCount, f->stat.parsCount),
1574 xpercent(f->stat.repCount, f->stat.seenCount));
12cf1be2 1575 }
62e76326 1576
9bea1d5b 1577 storeAppendPrintf(e, "Headers Parsed: %d + %d = %d\n",
62e76326 1578 HttpHeaderStats[hoRequest].parsedCount,
1579 HttpHeaderStats[hoReply].parsedCount,
1580 HttpHeaderStats[0].parsedCount);
9bea1d5b 1581 storeAppendPrintf(e, "Hdr Fields Parsed: %d\n", HeaderEntryParsedCount);
cb69b4c7 1582}
97474590 1583
e6ccf245 1584http_hdr_type
9bea1d5b 1585httpHeaderIdByName(const char *name, int name_len, const HttpHeaderFieldInfo * info, int end)
97474590 1586{
9bea1d5b 1587 int i;
62e76326 1588
9bea1d5b 1589 for (i = 0; i < end; ++i) {
62e76326 1590 if (name_len >= 0 && name_len != info[i].name.size())
1591 continue;
1592
1593 if (!strncasecmp(name, info[i].name.buf(),
1594 name_len < 0 ? info[i].name.size() + 1 : name_len))
1595 return info[i].id;
97474590 1596 }
62e76326 1597
e6ccf245 1598 return HDR_BAD_HDR;
97474590 1599}
1600
e6ccf245 1601http_hdr_type
9bea1d5b 1602httpHeaderIdByNameDef(const char *name, int name_len)
97474590 1603{
9bea1d5b 1604 if (!Headers)
62e76326 1605 Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END);
1606
9bea1d5b 1607 return httpHeaderIdByName(name, name_len, Headers, HDR_ENUM_END);
97474590 1608}
efd900cb 1609
1610const char *
9bea1d5b 1611httpHeaderNameById(int id)
efd900cb 1612{
9bea1d5b 1613 if (!Headers)
62e76326 1614 Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END);
1615
9bea1d5b 1616 assert(id >= 0 && id < HDR_ENUM_END);
62e76326 1617
849b826a 1618 return Headers[id].name.buf();
efd900cb 1619}
0353e724 1620
924f73bc 1621int
1622httpHeaderHasListMember(const HttpHeader * hdr, http_hdr_type id, const char *member, const char separator)
1623{
1624 int result = 0;
1625 const char *pos = NULL;
1626 const char *item;
1627 int ilen;
1628 int mlen = strlen(member);
1629
1630 assert(hdr);
1631 assert(id >= 0);
1632
1633 String header (httpHeaderGetStrOrList(hdr, id));
1634
1635 while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
1636 if (strncmp(item, member, mlen) == 0
1637 && (item[mlen] == '=' || item[mlen] == separator || item[mlen] == ';' || item[mlen] == '\0')) {
1638 result = 1;
1639 break;
1640 }
1641 }
1642
1643 return result;
1644}
1645
1646void
1647HttpHeader::removeConnectionHeaderEntries()
1648{
1649 if (httpHeaderHas(this, HDR_CONNECTION)) {
1650 /* anything that matches Connection list member will be deleted */
1651 String strConnection = httpHeaderGetList(this, HDR_CONNECTION);
1652 const HttpHeaderEntry *e;
1653 HttpHeaderPos pos = HttpHeaderInitPos;
1654 /*
1655 * think: on-average-best nesting of the two loops (hdrEntry
1656 * and strListItem) @?@
1657 */
1658 /*
1659 * maybe we should delete standard stuff ("keep-alive","close")
1660 * from strConnection first?
1661 */
1662
1663 while ((e = httpHeaderGetEntry(this, &pos))) {
1664 if (strListIsMember(&strConnection, e->name.buf(), ','))
1665 httpHeaderDelAt(this, pos);
1666 }
1667
1668 httpHeaderDelById(this, HDR_CONNECTION);
1669 strConnection.clean();
1670 }
1671}