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