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