]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpHeader.cc
Document the 'carp' cache_peer option
[thirdparty/squid.git] / src / HttpHeader.cc
CommitLineData
4f7e9dbb 1
cb69b4c7 2/*
528b2c61 3 * $Id: HttpHeader.cc,v 1.84 2003/01/23 00:37:12 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[] =
2ac76861 71{
9bea1d5b 72 {"Accept", HDR_ACCEPT, ftStr},
73 {"Accept-Charset", HDR_ACCEPT_CHARSET, ftStr},
74 {"Accept-Encoding", HDR_ACCEPT_ENCODING, ftStr},
75 {"Accept-Language", HDR_ACCEPT_LANGUAGE, ftStr},
76 {"Accept-Ranges", HDR_ACCEPT_RANGES, ftStr},
77 {"Age", HDR_AGE, ftInt},
78 {"Allow", HDR_ALLOW, ftStr},
79 {"Authorization", HDR_AUTHORIZATION, ftStr}, /* for now */
80 {"Cache-Control", HDR_CACHE_CONTROL, ftPCc},
81 {"Connection", HDR_CONNECTION, ftStr},
82 {"Content-Base", HDR_CONTENT_BASE, ftStr},
83 {"Content-Encoding", HDR_CONTENT_ENCODING, ftStr},
84 {"Content-Language", HDR_CONTENT_LANGUAGE, ftStr},
85 {"Content-Length", HDR_CONTENT_LENGTH, ftInt},
86 {"Content-Location", HDR_CONTENT_LOCATION, ftStr},
87 {"Content-MD5", HDR_CONTENT_MD5, ftStr}, /* for now */
88 {"Content-Range", HDR_CONTENT_RANGE, ftPContRange},
89 {"Content-Type", HDR_CONTENT_TYPE, ftStr},
90 {"Cookie", HDR_COOKIE, ftStr},
91 {"Date", HDR_DATE, ftDate_1123},
92 {"ETag", HDR_ETAG, ftETag},
93 {"Expires", HDR_EXPIRES, ftDate_1123},
94 {"From", HDR_FROM, ftStr},
95 {"Host", HDR_HOST, ftStr},
96 {"If-Match", HDR_IF_MATCH, ftStr}, /* for now */
97 {"If-Modified-Since", HDR_IF_MODIFIED_SINCE, ftDate_1123},
98 {"If-None-Match", HDR_IF_NONE_MATCH, ftStr}, /* for now */
99 {"If-Range", HDR_IF_RANGE, ftDate_1123_or_ETag},
100 {"Last-Modified", HDR_LAST_MODIFIED, ftDate_1123},
101 {"Link", HDR_LINK, ftStr},
102 {"Location", HDR_LOCATION, ftStr},
103 {"Max-Forwards", HDR_MAX_FORWARDS, ftInt},
104 {"Mime-Version", HDR_MIME_VERSION, ftStr}, /* for now */
105 {"Pragma", HDR_PRAGMA, ftStr},
106 {"Proxy-Authenticate", HDR_PROXY_AUTHENTICATE, ftStr},
107 {"Proxy-Authentication-Info", HDR_PROXY_AUTHENTICATION_INFO, ftStr},
108 {"Proxy-Authorization", HDR_PROXY_AUTHORIZATION, ftStr},
109 {"Proxy-Connection", HDR_PROXY_CONNECTION, ftStr},
110 {"Public", HDR_PUBLIC, ftStr},
111 {"Range", HDR_RANGE, ftPRange},
112 {"Referer", HDR_REFERER, ftStr},
113 {"Request-Range", HDR_REQUEST_RANGE, ftPRange}, /* usually matches HDR_RANGE */
114 {"Retry-After", HDR_RETRY_AFTER, ftStr}, /* for now (ftDate_1123 or ftInt!) */
115 {"Server", HDR_SERVER, ftStr},
116 {"Set-Cookie", HDR_SET_COOKIE, ftStr},
117 {"Title", HDR_TITLE, ftStr},
5affedf3 118 {"Transfer-Encoding", HDR_TRANSFER_ENCODING, ftStr},
9bea1d5b 119 {"Upgrade", HDR_UPGRADE, ftStr}, /* for now */
120 {"User-Agent", HDR_USER_AGENT, ftStr},
121 {"Vary", HDR_VARY, ftStr}, /* for now */
122 {"Via", HDR_VIA, ftStr}, /* for now */
123 {"Warning", HDR_WARNING, ftStr}, /* for now */
124 {"WWW-Authenticate", HDR_WWW_AUTHENTICATE, ftStr},
125 {"Authentication-Info", HDR_AUTHENTICATION_INFO, ftStr},
126 {"X-Cache", HDR_X_CACHE, ftStr},
127 {"X-Cache-Lookup", HDR_X_CACHE_LOOKUP, ftStr},
128 {"X-Forwarded-For", HDR_X_FORWARDED_FOR, ftStr},
129 {"X-Request-URI", HDR_X_REQUEST_URI, ftStr},
130 {"X-Squid-Error", HDR_X_SQUID_ERROR, ftStr},
131 {"Negotiate", HDR_NEGOTIATE, ftStr},
f66a9ef4 132#if X_ACCELERATOR_VARY
9bea1d5b 133 {"X-Accelerator-Vary", HDR_X_ACCELERATOR_VARY, ftStr},
f66a9ef4 134#endif
a7ad6e4e 135 {"Front-End-Https", HDR_FRONT_END_HTTPS, ftStr},
9bea1d5b 136 {"Other:", HDR_OTHER, ftStr} /* ':' will not allow matches */
cb69b4c7 137};
de336bbe 138static HttpHeaderFieldInfo *Headers = NULL;
cb69b4c7 139
e6ccf245 140http_hdr_type &operator++ (http_hdr_type &aHeader)
141{
142 aHeader = (http_hdr_type)(++(int)aHeader);
143 return aHeader;
144}
145
146
cb69b4c7 147/*
148 * headers with field values defined as #(values) in HTTP/1.1
d8b249ef 149 * Headers that are currently not recognized, are commented out.
cb69b4c7 150 */
b644367b 151static HttpHeaderMask ListHeadersMask; /* set run-time using ListHeadersArr */
de336bbe 152static http_hdr_type ListHeadersArr[] =
2ac76861 153{
9bea1d5b 154 HDR_ACCEPT,
155 HDR_ACCEPT_CHARSET, HDR_ACCEPT_ENCODING, HDR_ACCEPT_LANGUAGE,
156 HDR_ACCEPT_RANGES, HDR_ALLOW,
157 HDR_CACHE_CONTROL,
158 HDR_CONTENT_ENCODING,
159 HDR_CONTENT_LANGUAGE,
160 HDR_CONNECTION,
161 HDR_IF_MATCH, HDR_IF_NONE_MATCH,
162 HDR_LINK, HDR_PRAGMA,
163 HDR_PROXY_CONNECTION,
5affedf3 164 HDR_TRANSFER_ENCODING,
9bea1d5b 165 HDR_UPGRADE,
166 HDR_VARY,
167 HDR_VIA,
99edd1c3 168 /* HDR_WARNING, */
9bea1d5b 169 HDR_WWW_AUTHENTICATE,
170 HDR_AUTHENTICATION_INFO,
171 HDR_PROXY_AUTHENTICATION_INFO,
cb69b4c7 172 /* HDR_EXPECT, HDR_TE, HDR_TRAILER */
f66a9ef4 173#if X_ACCELERATOR_VARY
9bea1d5b 174 HDR_X_ACCELERATOR_VARY,
f66a9ef4 175#endif
9bea1d5b 176 HDR_X_FORWARDED_FOR
99edd1c3 177};
178
179/* general-headers */
180static http_hdr_type GeneralHeadersArr[] =
181{
9bea1d5b 182 HDR_CACHE_CONTROL, HDR_CONNECTION, HDR_DATE, HDR_PRAGMA,
5affedf3 183 HDR_TRANSFER_ENCODING,
9bea1d5b 184 HDR_UPGRADE,
99edd1c3 185 /* HDR_TRAILER, */
9bea1d5b 186 HDR_VIA
99edd1c3 187};
188
189/* entity-headers */
190static http_hdr_type EntityHeadersArr[] =
191{
9bea1d5b 192 HDR_ALLOW, HDR_CONTENT_BASE, HDR_CONTENT_ENCODING, HDR_CONTENT_LANGUAGE,
193 HDR_CONTENT_LENGTH, HDR_CONTENT_LOCATION, HDR_CONTENT_MD5,
194 HDR_CONTENT_RANGE, HDR_CONTENT_TYPE, HDR_ETAG, HDR_EXPIRES, HDR_LAST_MODIFIED, HDR_LINK,
195 HDR_OTHER
cb69b4c7 196};
197
b644367b 198static HttpHeaderMask ReplyHeadersMask; /* set run-time using ReplyHeaders */
de336bbe 199static http_hdr_type ReplyHeadersArr[] =
2ac76861 200{
9bea1d5b 201 HDR_ACCEPT, HDR_ACCEPT_CHARSET, HDR_ACCEPT_ENCODING, HDR_ACCEPT_LANGUAGE,
202 HDR_ACCEPT_RANGES, HDR_AGE,
203 HDR_LOCATION, HDR_MAX_FORWARDS,
204 HDR_MIME_VERSION, HDR_PUBLIC, HDR_RETRY_AFTER, HDR_SERVER, HDR_SET_COOKIE,
205 HDR_VARY,
206 HDR_WARNING, HDR_PROXY_CONNECTION, HDR_X_CACHE,
207 HDR_X_CACHE_LOOKUP,
208 HDR_X_REQUEST_URI,
f66a9ef4 209#if X_ACCELERATOR_VARY
9bea1d5b 210 HDR_X_ACCELERATOR_VARY,
f66a9ef4 211#endif
9bea1d5b 212 HDR_X_SQUID_ERROR
cb69b4c7 213};
214
b644367b 215static HttpHeaderMask RequestHeadersMask; /* set run-time using RequestHeaders */
de336bbe 216static http_hdr_type RequestHeadersArr[] =
2ac76861 217{
9bea1d5b 218 HDR_AUTHORIZATION, HDR_FROM, HDR_HOST,
219 HDR_IF_MATCH, HDR_IF_MODIFIED_SINCE, HDR_IF_NONE_MATCH,
220 HDR_IF_RANGE, HDR_MAX_FORWARDS, HDR_PROXY_CONNECTION,
221 HDR_PROXY_AUTHORIZATION, HDR_RANGE, HDR_REFERER, HDR_REQUEST_RANGE,
222 HDR_USER_AGENT, HDR_X_FORWARDED_FOR
cb69b4c7 223};
224
12cf1be2 225/* header accounting */
2ac76861 226static HttpHeaderStat HttpHeaderStats[] =
227{
9bea1d5b 228 {"all"},
9f4d4f65 229#if USE_HTCP
9bea1d5b 230 {"HTCP reply"},
9f4d4f65 231#endif
9bea1d5b 232 {"request"},
233 {"reply"}
12cf1be2 234};
9bea1d5b 235static int HttpHeaderStatCount = countof(HttpHeaderStats);
12cf1be2 236
7faf2bdb 237static int HeaderEntryParsedCount = 0;
cb69b4c7 238
12cf1be2 239/*
240 * local routines
241 */
cb69b4c7 242
243#define assert_eid(id) assert((id) >= 0 && (id) < HDR_ENUM_END)
244
9bea1d5b 245static HttpHeaderEntry *httpHeaderEntryCreate(http_hdr_type id, const char *name, const char *value);
246static void httpHeaderEntryDestroy(HttpHeaderEntry * e);
247static HttpHeaderEntry *httpHeaderEntryParseCreate(const char *field_start, const char *field_end);
248static void httpHeaderNoteParsedEntry(http_hdr_type id, String value, int error);
cb69b4c7 249
9bea1d5b 250static void httpHeaderStatInit(HttpHeaderStat * hs, const char *label);
251static void httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e);
7c525cc2 252
cb69b4c7 253/*
254 * Module initialization routines
255 */
256
257void
9bea1d5b 258httpHeaderInitModule(void)
cb69b4c7 259{
9bea1d5b 260 int i;
261 /* check that we have enough space for masks */
262 assert(8 * sizeof(HttpHeaderMask) >= HDR_ENUM_END);
263 /* all headers must be described */
264 assert(countof(HeadersAttrs) == HDR_ENUM_END);
265 if (!Headers)
266 Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END);
267 /* create masks */
268 httpHeaderMaskInit(&ListHeadersMask, 0);
269 httpHeaderCalcMask(&ListHeadersMask, (const int *) ListHeadersArr, countof(ListHeadersArr));
270 httpHeaderMaskInit(&ReplyHeadersMask, 0);
271 httpHeaderCalcMask(&ReplyHeadersMask, (const int *) ReplyHeadersArr, countof(ReplyHeadersArr));
272 httpHeaderCalcMask(&ReplyHeadersMask, (const int *) GeneralHeadersArr, countof(GeneralHeadersArr));
273 httpHeaderCalcMask(&ReplyHeadersMask, (const int *) EntityHeadersArr, countof(EntityHeadersArr));
274 httpHeaderMaskInit(&RequestHeadersMask, 0);
275 httpHeaderCalcMask(&RequestHeadersMask, (const int *) RequestHeadersArr, countof(RequestHeadersArr));
276 httpHeaderCalcMask(&RequestHeadersMask, (const int *) GeneralHeadersArr, countof(GeneralHeadersArr));
277 httpHeaderCalcMask(&RequestHeadersMask, (const int *) EntityHeadersArr, countof(EntityHeadersArr));
278 /* init header stats */
279 assert(HttpHeaderStatCount == hoReply + 1);
280 for (i = 0; i < HttpHeaderStatCount; i++)
281 httpHeaderStatInit(HttpHeaderStats + i, HttpHeaderStats[i].label);
282 HttpHeaderStats[hoRequest].owner_mask = &RequestHeadersMask;
283 HttpHeaderStats[hoReply].owner_mask = &ReplyHeadersMask;
86aebcda 284#if USE_HTCP
9bea1d5b 285 HttpHeaderStats[hoHtcpReply].owner_mask = &ReplyHeadersMask;
86aebcda 286#endif
9bea1d5b 287 /* init dependent modules */
288 httpHdrCcInitModule();
289 /* register with cache manager */
290 cachemgrRegister("http_headers",
291 "HTTP Header Statistics", httpHeaderStoreReport, 0, 1);
cb69b4c7 292}
293
7021844c 294void
9bea1d5b 295httpHeaderCleanModule(void)
7021844c 296{
9bea1d5b 297 httpHeaderDestroyFieldsInfo(Headers, HDR_ENUM_END);
298 Headers = NULL;
299 httpHdrCcCleanModule();
7021844c 300}
301
12cf1be2 302static void
9bea1d5b 303httpHeaderStatInit(HttpHeaderStat * hs, const char *label)
12cf1be2 304{
9bea1d5b 305 assert(hs);
306 assert(label);
307 memset(hs, 0, sizeof(HttpHeaderStat));
308 hs->label = label;
309 statHistEnumInit(&hs->hdrUCountDistr, 32); /* not a real enum */
310 statHistEnumInit(&hs->fieldTypeDistr, HDR_ENUM_END);
311 statHistEnumInit(&hs->ccTypeDistr, CC_ENUM_END);
cb69b4c7 312}
313
cb69b4c7 314/*
315 * HttpHeader Implementation
316 */
317
cb69b4c7 318void
9bea1d5b 319httpHeaderInit(HttpHeader * hdr, http_hdr_owner_type owner)
cb69b4c7 320{
9bea1d5b 321 assert(hdr);
322 assert(owner > hoNone && owner <= hoReply);
323 debug(55, 7) ("init-ing hdr: %p owner: %d\n", hdr, owner);
324 memset(hdr, 0, sizeof(*hdr));
325 hdr->owner = owner;
326 arrayInit(&hdr->entries);
cb69b4c7 327}
328
329void
9bea1d5b 330httpHeaderClean(HttpHeader * hdr)
cb69b4c7 331{
9bea1d5b 332 HttpHeaderPos pos = HttpHeaderInitPos;
333 HttpHeaderEntry *e;
334
335 assert(hdr);
336 assert(hdr->owner > hoNone && hdr->owner <= hoReply);
337 debug(55, 7) ("cleaning hdr: %p owner: %d\n", hdr, hdr->owner);
338
339 statHistCount(&HttpHeaderStats[hdr->owner].hdrUCountDistr, hdr->entries.count);
340 HttpHeaderStats[hdr->owner].destroyedCount++;
341 HttpHeaderStats[hdr->owner].busyDestroyedCount += hdr->entries.count > 0;
342 while ((e = httpHeaderGetEntry(hdr, &pos))) {
343 /* tmp hack to try to avoid coredumps */
344 if (e->id < 0 || e->id >= HDR_ENUM_END) {
345 debug(55, 0) ("httpHeaderClean BUG: entry[%d] is invalid (%d). Ignored.\n",
ed19251a 346 (int) pos, e->id);
9bea1d5b 347 } else {
348 statHistCount(&HttpHeaderStats[hdr->owner].fieldTypeDistr, e->id);
349 /* yes, this destroy() leaves us in an incosistent state */
350 httpHeaderEntryDestroy(e);
c68e9c6b 351 }
12cf1be2 352 }
9bea1d5b 353 arrayClean(&hdr->entries);
cb69b4c7 354}
355
2246b732 356/* append entries (also see httpHeaderUpdate) */
357void
9bea1d5b 358httpHeaderAppend(HttpHeader * dest, const HttpHeader * src)
2246b732 359{
9bea1d5b 360 const HttpHeaderEntry *e;
361 HttpHeaderPos pos = HttpHeaderInitPos;
362 assert(src && dest);
363 assert(src != dest);
364 debug(55, 7) ("appending hdr: %p += %p\n", dest, src);
365
366 while ((e = httpHeaderGetEntry(src, &pos))) {
367 httpHeaderAddEntry(dest, httpHeaderEntryClone(e));
2246b732 368 }
369}
370
d8b249ef 371/* use fresh entries to replace old ones */
cb69b4c7 372void
9bea1d5b 373httpHeaderUpdate(HttpHeader * old, const HttpHeader * fresh, const HttpHeaderMask * denied_mask)
cb69b4c7 374{
9bea1d5b 375 const HttpHeaderEntry *e;
376 HttpHeaderPos pos = HttpHeaderInitPos;
377 assert(old && fresh);
378 assert(old != fresh);
379 debug(55, 7) ("updating hdr: %p <- %p\n", old, fresh);
380
381 while ((e = httpHeaderGetEntry(fresh, &pos))) {
382 /* deny bad guys (ok to check for HDR_OTHER) here */
383 if (denied_mask && CBIT_TEST(*denied_mask, e->id))
384 continue;
528b2c61 385 httpHeaderDelByName(old, e->name.buf());
9bea1d5b 386 httpHeaderAddEntry(old, httpHeaderEntryClone(e));
cb69b4c7 387 }
cb69b4c7 388}
389
390/* just handy in parsing: resets and returns false */
9f4d4f65 391int
9bea1d5b 392httpHeaderReset(HttpHeader * hdr)
2ac76861 393{
d71e8477 394 http_hdr_owner_type ho;
9bea1d5b 395 assert(hdr);
396 ho = hdr->owner;
397 httpHeaderClean(hdr);
398 httpHeaderInit(hdr, ho);
399 return 0;
cb69b4c7 400}
401
cb69b4c7 402int
9bea1d5b 403httpHeaderParse(HttpHeader * hdr, const char *header_start, const char *header_end)
cb69b4c7 404{
9bea1d5b 405 const char *field_start = header_start;
406 HttpHeaderEntry *e;
407
408 assert(hdr);
409 assert(header_start && header_end);
410 debug(55, 7) ("parsing hdr: (%p)\n%s\n", hdr, getStringPrefix(header_start, header_end));
411 HttpHeaderStats[hdr->owner].parsedCount++;
412 /* commonn format headers are "<name>:[ws]<value>" lines delimited by <CRLF> */
413 while (field_start < header_end) {
414 const char *field_end;
415 const char *field_ptr = field_start;
416 do {
417 field_end = field_ptr = field_ptr + strcspn(field_ptr, "\r\n");
418 /* skip CRLF */
419 if (*field_ptr == '\r')
420 field_ptr++;
421 if (*field_ptr == '\n')
422 field_ptr++;
d35b9a94 423 }
9bea1d5b 424 while (*field_ptr == ' ' || *field_ptr == '\t');
425 if (!*field_end || field_end > header_end)
426 return httpHeaderReset(hdr); /* missing <CRLF> */
427 e = httpHeaderEntryParseCreate(field_start, field_end);
428 if (e != NULL)
429 httpHeaderAddEntry(hdr, e);
430 else
431 debug(55, 2) ("warning: ignoring unparseable http header field near '%s'\n",
432 getStringPrefix(field_start, field_end));
433 field_start = field_end;
434 /* skip CRLF */
435 if (*field_start == '\r')
436 field_start++;
437 if (*field_start == '\n')
438 field_start++;
cb69b4c7 439 }
9bea1d5b 440 return 1; /* even if no fields where found, it is a valid header */
cb69b4c7 441}
442
99edd1c3 443/* packs all the entries using supplied packer */
cb69b4c7 444void
9bea1d5b 445httpHeaderPackInto(const HttpHeader * hdr, Packer * p)
cb69b4c7 446{
9bea1d5b 447 HttpHeaderPos pos = HttpHeaderInitPos;
448 const HttpHeaderEntry *e;
449 assert(hdr && p);
450 debug(55, 7) ("packing hdr: (%p)\n", hdr);
451 /* pack all entries one by one */
452 while ((e = httpHeaderGetEntry(hdr, &pos)))
453 httpHeaderEntryPackInto(e, p);
cb69b4c7 454}
455
456/* returns next valid entry */
99edd1c3 457HttpHeaderEntry *
9bea1d5b 458httpHeaderGetEntry(const HttpHeader * hdr, HttpHeaderPos * pos)
cb69b4c7 459{
9bea1d5b 460 assert(hdr && pos);
732735ed 461 assert(*pos >= HttpHeaderInitPos && *pos < (ssize_t)hdr->entries.count);
462 for ((*pos)++; *pos < (ssize_t)hdr->entries.count; (*pos)++) {
9bea1d5b 463 if (hdr->entries.items[*pos])
e6ccf245 464 return (HttpHeaderEntry*)hdr->entries.items[*pos];
cb69b4c7 465 }
9bea1d5b 466 return NULL;
cb69b4c7 467}
468
469/*
d8b249ef 470 * returns a pointer to a specified entry if any
471 * note that we return one entry so it does not make much sense to ask for
472 * "list" headers
cb69b4c7 473 */
de336bbe 474HttpHeaderEntry *
9bea1d5b 475httpHeaderFindEntry(const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 476{
9bea1d5b 477 HttpHeaderPos pos = HttpHeaderInitPos;
478 HttpHeaderEntry *e;
479 assert(hdr);
480 assert_eid(id);
481 assert(!CBIT_TEST(ListHeadersMask, id));
482
483 /* check mask first */
484 if (!CBIT_TEST(hdr->mask, id))
485 return NULL;
486 /* looks like we must have it, do linear search */
487 while ((e = httpHeaderGetEntry(hdr, &pos))) {
488 if (e->id == id)
489 return e;
cb69b4c7 490 }
9bea1d5b 491 /* hm.. we thought it was there, but it was not found */
492 assert(0);
493 return NULL; /* not reached */
cb69b4c7 494}
495
a622fff0 496/*
497 * same as httpHeaderFindEntry
498 */
499static HttpHeaderEntry *
9bea1d5b 500httpHeaderFindLastEntry(const HttpHeader * hdr, http_hdr_type id)
a622fff0 501{
9bea1d5b 502 HttpHeaderPos pos = HttpHeaderInitPos;
503 HttpHeaderEntry *e;
504 HttpHeaderEntry *result = NULL;
505 assert(hdr);
506 assert_eid(id);
507 assert(!CBIT_TEST(ListHeadersMask, id));
508
509 /* check mask first */
510 if (!CBIT_TEST(hdr->mask, id))
511 return NULL;
512 /* looks like we must have it, do linear search */
513 while ((e = httpHeaderGetEntry(hdr, &pos))) {
514 if (e->id == id)
515 result = e;
a622fff0 516 }
9bea1d5b 517 assert(result); /* must be there! */
518 return result;
a622fff0 519}
520
cb69b4c7 521/*
d8b249ef 522 * deletes all fields with a given name if any, returns #fields deleted;
cb69b4c7 523 */
2ac76861 524int
9bea1d5b 525httpHeaderDelByName(HttpHeader * hdr, const char *name)
cb69b4c7 526{
9bea1d5b 527 int count = 0;
528 HttpHeaderPos pos = HttpHeaderInitPos;
529 HttpHeaderEntry *e;
530 httpHeaderMaskInit(&hdr->mask, 0); /* temporal inconsistency */
531 debug(55, 7) ("deleting '%s' fields in hdr %p\n", name, hdr);
532 while ((e = httpHeaderGetEntry(hdr, &pos))) {
533 if (!strCaseCmp(e->name, name)) {
534 httpHeaderDelAt(hdr, pos);
535 count++;
536 } else
537 CBIT_SET(hdr->mask, e->id);
cb69b4c7 538 }
9bea1d5b 539 return count;
cb69b4c7 540}
541
2246b732 542/* deletes all entries with a given id, returns the #entries deleted */
543int
9bea1d5b 544httpHeaderDelById(HttpHeader * hdr, http_hdr_type id)
d8b249ef 545{
9bea1d5b 546 int count = 0;
547 HttpHeaderPos pos = HttpHeaderInitPos;
548 HttpHeaderEntry *e;
549 debug(55, 8) ("%p del-by-id %d\n", hdr, id);
550 assert(hdr);
551 assert_eid(id);
552 assert_eid(id != HDR_OTHER); /* does not make sense */
553 if (!CBIT_TEST(hdr->mask, id))
554 return 0;
555 while ((e = httpHeaderGetEntry(hdr, &pos))) {
556 if (e->id == id) {
557 httpHeaderDelAt(hdr, pos);
558 count++;
d8b249ef 559 }
560 }
9bea1d5b 561 CBIT_CLR(hdr->mask, id);
562 assert(count);
563 return count;
d8b249ef 564}
d8b249ef 565
cb69b4c7 566/*
567 * deletes an entry at pos and leaves a gap; leaving a gap makes it
568 * possible to iterate(search) and delete fields at the same time
569 */
2246b732 570void
9bea1d5b 571httpHeaderDelAt(HttpHeader * hdr, HttpHeaderPos pos)
cb69b4c7 572{
9bea1d5b 573 HttpHeaderEntry *e;
732735ed 574 assert(pos >= HttpHeaderInitPos && pos < (ssize_t)hdr->entries.count);
e6ccf245 575 e = (HttpHeaderEntry*)hdr->entries.items[pos];
9bea1d5b 576 hdr->entries.items[pos] = NULL;
577 /* decrement header length, allow for ": " and crlf */
528b2c61 578 hdr->len -= e->name.size() + 2 + e->value.size() + 2;
9bea1d5b 579 assert(hdr->len >= 0);
580 httpHeaderEntryDestroy(e);
cb69b4c7 581}
582
99edd1c3 583
584/* appends an entry;
585 * does not call httpHeaderEntryClone() so one should not reuse "*e"
cb69b4c7 586 */
99edd1c3 587void
9bea1d5b 588httpHeaderAddEntry(HttpHeader * hdr, HttpHeaderEntry * e)
cb69b4c7 589{
9bea1d5b 590 assert(hdr && e);
591 assert_eid(e->id);
592
593 debug(55, 7) ("%p adding entry: %d at %d\n",
594 hdr, e->id, hdr->entries.count);
595 if (CBIT_TEST(hdr->mask, e->id))
596 Headers[e->id].stat.repCount++;
597 else
598 CBIT_SET(hdr->mask, e->id);
599 arrayAppend(&hdr->entries, e);
600 /* increment header length, allow for ": " and crlf */
528b2c61 601 hdr->len += e->name.size() + 2 + e->value.size() + 2;
cb69b4c7 602}
603
99edd1c3 604/* return a list of entries with the same id separated by ',' and ws */
605String
9bea1d5b 606httpHeaderGetList(const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 607{
9bea1d5b 608 String s = StringNull;
609 HttpHeaderEntry *e;
610 HttpHeaderPos pos = HttpHeaderInitPos;
611 debug(55, 6) ("%p: joining for id %d\n", hdr, id);
612 /* only fields from ListHeaders array can be "listed" */
613 assert(CBIT_TEST(ListHeadersMask, id));
614 if (!CBIT_TEST(hdr->mask, id))
615 return s;
616 while ((e = httpHeaderGetEntry(hdr, &pos))) {
617 if (e->id == id)
528b2c61 618 strListAdd(&s, e->value.buf(), ',');
d35b9a94 619 }
9bea1d5b 620 /*
621 * note: we might get an empty (len==0) string if there was an "empty"
622 * header; we must not get a NULL string though.
623 */
528b2c61 624 assert(s.buf());
9bea1d5b 625 /* temporary warning: remove it! @?@ @?@ @?@ */
528b2c61 626 if (!s.size())
627 debug(55, 3) ("empty list header: %s (%d)\n", Headers[id].name.buf(), id);
628 debug(55, 6) ("%p: joined for id %d: %s\n", hdr, id, s.buf());
9bea1d5b 629 return s;
cb69b4c7 630}
631
f66a9ef4 632/* return a string or list of entries with the same id separated by ',' and ws */
633String
9bea1d5b 634httpHeaderGetStrOrList(const HttpHeader * hdr, http_hdr_type id)
f66a9ef4 635{
c7327fa0 636 HttpHeaderEntry *e;
9bea1d5b 637
638 if (CBIT_TEST(ListHeadersMask, id))
c7327fa0 639 return httpHeaderGetList(hdr, id);
528b2c61 640 if ((e = httpHeaderFindEntry(hdr, id)))
641 return e->value;
642 return String::Null;
f66a9ef4 643}
644
645/*
bd412580 646 * Returns the value of the specified header.
f66a9ef4 647 */
648String
9bea1d5b 649httpHeaderGetByName(const HttpHeader * hdr, const char *name)
f66a9ef4 650{
9bea1d5b 651 http_hdr_type id;
652 HttpHeaderPos pos = HttpHeaderInitPos;
653 HttpHeaderEntry *e;
654 String result = StringNull;
655
656 assert(hdr);
657 assert(name);
658
659 /* First try the quick path */
660 id = httpHeaderIdByNameDef(name, strlen(name));
661 if (id != -1)
662 return httpHeaderGetStrOrList(hdr, id);
663
664 /* Sorry, an unknown header name. Do linear search */
665 while ((e = httpHeaderGetEntry(hdr, &pos))) {
666 if (e->id == HDR_OTHER && strCaseCmp(e->name, name) == 0) {
528b2c61 667 strListAdd(&result, e->value.buf(), ',');
f66a9ef4 668 }
669 }
9bea1d5b 670 return result;
f66a9ef4 671}
cb69b4c7 672
14b463aa 673/*
372fdfbf 674 * Returns a the value of the specified list member, if any.
14b463aa 675 */
676String
677httpHeaderGetByNameListMember(const HttpHeader * hdr, const char *name, const char *member, const char separator)
678{
679 String result = StringNull;
680 String header;
681 const char *pos = NULL;
682 const char *item;
683 int ilen;
684 int mlen = strlen(member);
685
686 assert(hdr);
687 assert(name);
688
689 header = httpHeaderGetByName(hdr, name);
690
691 while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
692 if (strncmp(item, member, mlen) == 0 && item[mlen] == '=') {
528b2c61 693 result.append(item + mlen + 1, ilen - mlen - 1);
14b463aa 694 break;
695 }
696 }
697 return result;
698}
699
700/*
701 * returns a the value of the specified list member, if any.
702 */
703String
704httpHeaderGetListMember(const HttpHeader * hdr, http_hdr_type id, const char *member, const char separator)
705{
706 String result = StringNull;
707 String header;
708 const char *pos = NULL;
709 const char *item;
710 int ilen;
711 int mlen = strlen(member);
712
713 assert(hdr);
714 assert(id >= 0);
715
716 header = httpHeaderGetStrOrList(hdr, id);
717
718 while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
719 if (strncmp(item, member, mlen) == 0 && item[mlen] == '=') {
528b2c61 720 result.append(item + mlen + 1, ilen - mlen - 1);
14b463aa 721 break;
722 }
723 }
528b2c61 724 header.clean();
14b463aa 725 return result;
726}
727
cb69b4c7 728/* test if a field is present */
2ac76861 729int
9bea1d5b 730httpHeaderHas(const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 731{
9bea1d5b 732 assert(hdr);
733 assert_eid(id);
734 assert(id != HDR_OTHER);
735 debug(55, 7) ("%p lookup for %d\n", hdr, id);
736 return CBIT_TEST(hdr->mask, id);
cb69b4c7 737}
738
739void
9bea1d5b 740httpHeaderPutInt(HttpHeader * hdr, http_hdr_type id, int number)
cb69b4c7 741{
9bea1d5b 742 assert_eid(id);
743 assert(Headers[id].type == ftInt); /* must be of an appropriate type */
744 assert(number >= 0);
745 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(id, NULL, xitoa(number)));
cb69b4c7 746}
747
748void
9bea1d5b 749httpHeaderPutTime(HttpHeader * hdr, http_hdr_type id, time_t htime)
cb69b4c7 750{
9bea1d5b 751 assert_eid(id);
752 assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */
a1d6870f 753 assert(htime >= 0);
9bea1d5b 754 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(id, NULL, mkrfc1123(htime)));
cb69b4c7 755}
2ac76861 756
cb69b4c7 757void
9bea1d5b 758httpHeaderPutStr(HttpHeader * hdr, http_hdr_type id, const char *str)
cb69b4c7 759{
9bea1d5b 760 assert_eid(id);
761 assert(Headers[id].type == ftStr); /* must be of an appropriate type */
762 assert(str);
763 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(id, NULL, str));
cb69b4c7 764}
765
63259c34 766void
9bea1d5b 767httpHeaderPutAuth(HttpHeader * hdr, const char *auth_scheme, const char *realm)
63259c34 768{
9bea1d5b 769 assert(hdr && auth_scheme && realm);
770 httpHeaderPutStrf(hdr, HDR_WWW_AUTHENTICATE, "%s realm=\"%s\"", auth_scheme, realm);
63259c34 771}
772
99edd1c3 773void
9bea1d5b 774httpHeaderPutCc(HttpHeader * hdr, const HttpHdrCc * cc)
99edd1c3 775{
9bea1d5b 776 MemBuf mb;
777 Packer p;
778 assert(hdr && cc);
779 /* remove old directives if any */
780 httpHeaderDelById(hdr, HDR_CACHE_CONTROL);
781 /* pack into mb */
782 memBufDefInit(&mb);
783 packerToMemInit(&p, &mb);
784 httpHdrCcPackInto(cc, &p);
785 /* put */
786 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_CACHE_CONTROL, NULL, mb.buf));
787 /* cleanup */
788 packerClean(&p);
789 memBufClean(&mb);
99edd1c3 790}
791
d192d11f 792void
9bea1d5b 793httpHeaderPutContRange(HttpHeader * hdr, const HttpHdrContRange * cr)
d192d11f 794{
9bea1d5b 795 MemBuf mb;
796 Packer p;
797 assert(hdr && cr);
798 /* remove old directives if any */
799 httpHeaderDelById(hdr, HDR_CONTENT_RANGE);
800 /* pack into mb */
801 memBufDefInit(&mb);
802 packerToMemInit(&p, &mb);
803 httpHdrContRangePackInto(cr, &p);
804 /* put */
805 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_CONTENT_RANGE, NULL, mb.buf));
806 /* cleanup */
807 packerClean(&p);
808 memBufClean(&mb);
d192d11f 809}
810
811void
9bea1d5b 812httpHeaderPutRange(HttpHeader * hdr, const HttpHdrRange * range)
d192d11f 813{
9bea1d5b 814 MemBuf mb;
815 Packer p;
816 assert(hdr && range);
817 /* remove old directives if any */
818 httpHeaderDelById(hdr, HDR_RANGE);
819 /* pack into mb */
820 memBufDefInit(&mb);
821 packerToMemInit(&p, &mb);
528b2c61 822 range->packInto(&p);
9bea1d5b 823 /* put */
824 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_RANGE, NULL, mb.buf));
825 /* cleanup */
826 packerClean(&p);
827 memBufClean(&mb);
d192d11f 828}
829
cb69b4c7 830/* add extension header (these fields are not parsed/analyzed/joined, etc.) */
831void
9bea1d5b 832httpHeaderPutExt(HttpHeader * hdr, const char *name, const char *value)
cb69b4c7 833{
9bea1d5b 834 assert(name && value);
835 debug(55, 8) ("%p adds ext entry '%s: %s'\n", hdr, name, value);
836 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_OTHER, name, value));
cb69b4c7 837}
838
7c525cc2 839int
528b2c61 840httpHeaderEntryGetInt (const HttpHeaderEntry * e)
7c525cc2 841{
9bea1d5b 842 int value = -1;
843 int ok;
528b2c61 844 assert (e);
845 assert_eid (e->id);
846 assert (Headers[e->id].type == ftInt);
847 ok = httpHeaderParseInt(e->value.buf(), &value);
848 httpHeaderNoteParsedEntry(e->id, e->value, !ok);
849 /* XXX: Should we check ok - ie
850 * return ok ? -1 : value;
851 */
852 return value;
853}
854
855int
856httpHeaderGetInt(const HttpHeader * hdr, http_hdr_type id)
857{
9bea1d5b 858 assert_eid(id);
859 assert(Headers[id].type == ftInt); /* must be of an appropriate type */
528b2c61 860 HttpHeaderEntry *e;
861 if ((e = httpHeaderFindEntry(hdr, id)))
862 return httpHeaderEntryGetInt (e);
863 return -1;
7c525cc2 864}
865
de336bbe 866time_t
9bea1d5b 867httpHeaderGetTime(const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 868{
9bea1d5b 869 HttpHeaderEntry *e;
870 time_t value = -1;
871 assert_eid(id);
872 assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */
873 if ((e = httpHeaderFindEntry(hdr, id))) {
528b2c61 874 value = parse_rfc1123(e->value.buf());
9bea1d5b 875 httpHeaderNoteParsedEntry(e->id, e->value, value < 0);
d8b249ef 876 }
9bea1d5b 877 return value;
cb69b4c7 878}
879
99edd1c3 880/* sync with httpHeaderGetLastStr */
de336bbe 881const char *
9bea1d5b 882httpHeaderGetStr(const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 883{
9bea1d5b 884 HttpHeaderEntry *e;
885 assert_eid(id);
886 assert(Headers[id].type == ftStr); /* must be of an appropriate type */
887 if ((e = httpHeaderFindEntry(hdr, id))) {
888 httpHeaderNoteParsedEntry(e->id, e->value, 0); /* no errors are possible */
528b2c61 889 return e->value.buf();
d8b249ef 890 }
9bea1d5b 891 return NULL;
cb69b4c7 892}
893
a622fff0 894/* unusual */
895const char *
9bea1d5b 896httpHeaderGetLastStr(const HttpHeader * hdr, http_hdr_type id)
a622fff0 897{
9bea1d5b 898 HttpHeaderEntry *e;
899 assert_eid(id);
900 assert(Headers[id].type == ftStr); /* must be of an appropriate type */
901 if ((e = httpHeaderFindLastEntry(hdr, id))) {
902 httpHeaderNoteParsedEntry(e->id, e->value, 0); /* no errors are possible */
528b2c61 903 return e->value.buf();
a622fff0 904 }
9bea1d5b 905 return NULL;
a622fff0 906}
907
7faf2bdb 908HttpHdrCc *
9bea1d5b 909httpHeaderGetCc(const HttpHeader * hdr)
cb69b4c7 910{
9bea1d5b 911 HttpHdrCc *cc;
912 String s;
913 if (!CBIT_TEST(hdr->mask, HDR_CACHE_CONTROL))
914 return NULL;
915 s = httpHeaderGetList(hdr, HDR_CACHE_CONTROL);
916 cc = httpHdrCcParseCreate(&s);
917 HttpHeaderStats[hdr->owner].ccParsedCount++;
918 if (cc)
919 httpHdrCcUpdateStats(cc, &HttpHeaderStats[hdr->owner].ccTypeDistr);
920 httpHeaderNoteParsedEntry(HDR_CACHE_CONTROL, s, !cc);
528b2c61 921 s.clean();
9bea1d5b 922 return cc;
cb69b4c7 923}
924
02922e76 925HttpHdrRange *
9bea1d5b 926httpHeaderGetRange(const HttpHeader * hdr)
02922e76 927{
9bea1d5b 928 HttpHdrRange *r = NULL;
929 HttpHeaderEntry *e;
930 /* some clients will send "Request-Range" _and_ *matching* "Range"
931 * who knows, some clients might send Request-Range only;
932 * this "if" should work correctly in both cases;
933 * hopefully no clients send mismatched headers! */
934 if ((e = httpHeaderFindEntry(hdr, HDR_RANGE)) ||
935 (e = httpHeaderFindEntry(hdr, HDR_REQUEST_RANGE))) {
528b2c61 936 r = HttpHdrRange::ParseCreate(&e->value);
9bea1d5b 937 httpHeaderNoteParsedEntry(e->id, e->value, !r);
d192d11f 938 }
9bea1d5b 939 return r;
02922e76 940}
941
d76fcfa7 942HttpHdrContRange *
9bea1d5b 943httpHeaderGetContRange(const HttpHeader * hdr)
d76fcfa7 944{
9bea1d5b 945 HttpHdrContRange *cr = NULL;
946 HttpHeaderEntry *e;
947 if ((e = httpHeaderFindEntry(hdr, HDR_CONTENT_RANGE))) {
528b2c61 948 cr = httpHdrContRangeParseCreate(e->value.buf());
9bea1d5b 949 httpHeaderNoteParsedEntry(e->id, e->value, !cr);
d8b249ef 950 }
9bea1d5b 951 return cr;
cb69b4c7 952}
953
99edd1c3 954const char *
9bea1d5b 955httpHeaderGetAuth(const HttpHeader * hdr, http_hdr_type id, const char *auth_scheme)
cb69b4c7 956{
9bea1d5b 957 const char *field;
958 int l;
959 assert(hdr && auth_scheme);
960 field = httpHeaderGetStr(hdr, id);
961 if (!field) /* no authorization field */
962 return NULL;
963 l = strlen(auth_scheme);
964 if (!l || strncasecmp(field, auth_scheme, l)) /* wrong scheme */
965 return NULL;
966 field += l;
967 if (!xisspace(*field)) /* wrong scheme */
968 return NULL;
969 /* skip white space */
970 field += xcountws(field);
971 if (!*field) /* no authorization cookie */
972 return NULL;
973 return base64_decode(field);
cb69b4c7 974}
975
a9771e51 976ETag
9bea1d5b 977httpHeaderGetETag(const HttpHeader * hdr, http_hdr_type id)
a9771e51 978{
9bea1d5b 979 ETag etag =
980 {NULL, -1};
981 HttpHeaderEntry *e;
982 assert(Headers[id].type == ftETag); /* must be of an appropriate type */
983 if ((e = httpHeaderFindEntry(hdr, id)))
528b2c61 984 etagParseInit(&etag, e->value.buf());
9bea1d5b 985 return etag;
a9771e51 986}
987
988TimeOrTag
9bea1d5b 989httpHeaderGetTimeOrTag(const HttpHeader * hdr, http_hdr_type id)
a9771e51 990{
9bea1d5b 991 TimeOrTag tot;
992 HttpHeaderEntry *e;
993 assert(Headers[id].type == ftDate_1123_or_ETag); /* must be of an appropriate type */
994 memset(&tot, 0, sizeof(tot));
995 if ((e = httpHeaderFindEntry(hdr, id))) {
528b2c61 996 const char *str = e->value.buf();
9bea1d5b 997 /* try as an ETag */
998 if (etagParseInit(&tot.tag, str)) {
999 tot.valid = tot.tag.str != NULL;
1000 tot.time = -1;
1001 } else {
1002 /* or maybe it is time? */
1003 tot.time = parse_rfc1123(str);
1004 tot.valid = tot.time >= 0;
1005 tot.tag.str = NULL;
a9771e51 1006 }
1007 }
9bea1d5b 1008 assert(tot.time < 0 || !tot.tag.str); /* paranoid */
1009 return tot;
a9771e51 1010}
1011
cb69b4c7 1012/*
1013 * HttpHeaderEntry
1014 */
1015
d8b249ef 1016static HttpHeaderEntry *
9bea1d5b 1017httpHeaderEntryCreate(http_hdr_type id, const char *name, const char *value)
cb69b4c7 1018{
9bea1d5b 1019 HttpHeaderEntry *e;
1020 assert_eid(id);
e6ccf245 1021 e = (HttpHeaderEntry *)memAllocate(MEM_HTTP_HDR_ENTRY);
9bea1d5b 1022 e->id = id;
1023 if (id != HDR_OTHER)
1024 e->name = Headers[id].name;
1025 else
528b2c61 1026 e->name = name;
1027 e->value = value;
9bea1d5b 1028 Headers[id].stat.aliveCount++;
528b2c61 1029 debug(55, 9) ("created entry %p: '%s: %s'\n", e, e->name.buf(), e->value.buf());
9bea1d5b 1030 return e;
cb69b4c7 1031}
1032
de336bbe 1033static void
9bea1d5b 1034httpHeaderEntryDestroy(HttpHeaderEntry * e)
2ac76861 1035{
9bea1d5b 1036 assert(e);
1037 assert_eid(e->id);
528b2c61 1038 debug(55, 9) ("destroying entry %p: '%s: %s'\n", e, e->name.buf(), e->value.buf());
9bea1d5b 1039 /* clean name if needed */
1040 if (e->id == HDR_OTHER)
528b2c61 1041 e->name.clean();
1042 e->value.clean();
9bea1d5b 1043 assert(Headers[e->id].stat.aliveCount);
1044 Headers[e->id].stat.aliveCount--;
e6ccf245 1045 e->id = HDR_BAD_HDR;
9bea1d5b 1046 memFree(e, MEM_HTTP_HDR_ENTRY);
cb69b4c7 1047}
1048
d8b249ef 1049/* parses and inits header entry, returns new entry on success */
1050static HttpHeaderEntry *
9bea1d5b 1051httpHeaderEntryParseCreate(const char *field_start, const char *field_end)
cb69b4c7 1052{
9bea1d5b 1053 HttpHeaderEntry *e;
e6ccf245 1054 http_hdr_type id;
9bea1d5b 1055 /* note: name_start == field_start */
1056 const char *name_end = strchr(field_start, ':');
1057 const int name_len = name_end ? name_end - field_start : 0;
1058 const char *value_start = field_start + name_len + 1; /* skip ':' */
1059 /* note: value_end == field_end */
1060
1061 HeaderEntryParsedCount++;
1062
1063 /* do we have a valid field name within this field? */
1064 if (!name_len || name_end > field_end)
1065 return NULL;
1066 if (name_len > 65536) {
1067 /* String has a 64K limit */
1068 debug(55, 1) ("WARNING: ignoring header name of %d bytes\n", name_len);
1069 return NULL;
25acfb53 1070 }
9bea1d5b 1071 /* now we know we can parse it */
e6ccf245 1072 e = (HttpHeaderEntry *)memAllocate(MEM_HTTP_HDR_ENTRY);
9bea1d5b 1073 debug(55, 9) ("creating entry %p: near '%s'\n", e, getStringPrefix(field_start, field_end));
1074 /* is it a "known" field? */
1075 id = httpHeaderIdByName(field_start, name_len, Headers, HDR_ENUM_END);
1076 if (id < 0)
1077 id = HDR_OTHER;
1078 assert_eid(id);
1079 e->id = id;
1080 /* set field name */
1081 if (id == HDR_OTHER)
528b2c61 1082 e->name.limitInit(field_start, name_len);
9bea1d5b 1083 else
1084 e->name = Headers[id].name;
1085 /* trim field value */
1086 while (value_start < field_end && xisspace(*value_start))
1087 value_start++;
1088 if (field_end - value_start > 65536) {
1089 /* String has a 64K limit */
1090 debug(55, 1) ("WARNING: ignoring '%s' header of %d bytes\n",
528b2c61 1091 e->name.buf(), (int) (field_end - value_start));
9bea1d5b 1092 if (e->id == HDR_OTHER)
528b2c61 1093 e->name.clean();
9bea1d5b 1094 memFree(e, MEM_HTTP_HDR_ENTRY);
1095 return NULL;
25acfb53 1096 }
9bea1d5b 1097 /* set field value */
528b2c61 1098 e->value.limitInit(value_start, field_end - value_start);
9bea1d5b 1099 Headers[id].stat.seenCount++;
1100 Headers[id].stat.aliveCount++;
528b2c61 1101 debug(55, 9) ("created entry %p: '%s: %s'\n", e, e->name.buf(), e->value.buf());
9bea1d5b 1102 return e;
cb69b4c7 1103}
1104
99edd1c3 1105HttpHeaderEntry *
9bea1d5b 1106httpHeaderEntryClone(const HttpHeaderEntry * e)
cb69b4c7 1107{
528b2c61 1108 return httpHeaderEntryCreate(e->id, e->name.buf(), e->value.buf());
cb69b4c7 1109}
1110
de336bbe 1111void
9bea1d5b 1112httpHeaderEntryPackInto(const HttpHeaderEntry * e, Packer * p)
cb69b4c7 1113{
9bea1d5b 1114 assert(e && p);
528b2c61 1115 packerAppend(p, e->name.buf(), e->name.size());
9bea1d5b 1116 packerAppend(p, ": ", 2);
528b2c61 1117 packerAppend(p, e->value.buf(), e->value.size());
9bea1d5b 1118 packerAppend(p, "\r\n", 2);
cb69b4c7 1119}
1120
1121static void
9bea1d5b 1122httpHeaderNoteParsedEntry(http_hdr_type id, String context, int error)
cb69b4c7 1123{
9bea1d5b 1124 Headers[id].stat.parsCount++;
1125 if (error) {
1126 Headers[id].stat.errCount++;
1127 debug(55, 2) ("cannot parse hdr field: '%s: %s'\n",
528b2c61 1128 Headers[id].name.buf(), context.buf());
cb69b4c7 1129 }
cb69b4c7 1130}
1131
cb69b4c7 1132/*
12cf1be2 1133 * Reports
cb69b4c7 1134 */
cb69b4c7 1135
fcd2d3ef 1136/* tmp variable used to pass stat info to dumpers */
0cdcddb9 1137extern const HttpHeaderStat *dump_stat; /* argh! */
fcd2d3ef 1138const HttpHeaderStat *dump_stat = NULL;
1139
cb69b4c7 1140static void
9bea1d5b 1141httpHeaderFieldStatDumper(StoreEntry * sentry, int idx, double val, double size, int count)
cb69b4c7 1142{
9bea1d5b 1143 const int id = (int) val;
1144 const int valid_id = id >= 0 && id < HDR_ENUM_END;
528b2c61 1145 const char *name = valid_id ? Headers[id].name.buf() : "INVALID";
9bea1d5b 1146 int visible = count > 0;
1147 /* for entries with zero count, list only those that belong to current type of message */
1148 if (!visible && valid_id && dump_stat->owner_mask)
1149 visible = CBIT_TEST(*dump_stat->owner_mask, id);
1150 if (visible)
1151 storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
1152 id, name, count, xdiv(count, dump_stat->busyDestroyedCount));
cb69b4c7 1153}
1154
12cf1be2 1155static void
9bea1d5b 1156httpHeaderFldsPerHdrDumper(StoreEntry * sentry, int idx, double val, double size, int count)
cb69b4c7 1157{
9bea1d5b 1158 if (count)
1159 storeAppendPrintf(sentry, "%2d\t %5d\t %5d\t %6.2f\n",
1160 idx, (int) val, count,
1161 xpercent(count, dump_stat->destroyedCount));
cb69b4c7 1162}
1163
1164
1165static void
9bea1d5b 1166httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e)
12cf1be2 1167{
9bea1d5b 1168 assert(hs && e);
1169
1170 dump_stat = hs;
1171 storeAppendPrintf(e, "\nHeader Stats: %s\n", hs->label);
1172 storeAppendPrintf(e, "\nField type distribution\n");
1173 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n",
1174 "id", "name", "count", "#/header");
1175 statHistDump(&hs->fieldTypeDistr, e, httpHeaderFieldStatDumper);
1176 storeAppendPrintf(e, "\nCache-control directives distribution\n");
1177 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n",
1178 "id", "name", "count", "#/cc_field");
1179 statHistDump(&hs->ccTypeDistr, e, httpHdrCcStatDumper);
1180 storeAppendPrintf(e, "\nNumber of fields per header distribution\n");
1181 storeAppendPrintf(e, "%2s\t %-5s\t %5s\t %6s\n",
1182 "id", "#flds", "count", "%total");
1183 statHistDump(&hs->hdrUCountDistr, e, httpHeaderFldsPerHdrDumper);
1184 dump_stat = NULL;
cb69b4c7 1185}
1186
12cf1be2 1187void
9bea1d5b 1188httpHeaderStoreReport(StoreEntry * e)
cb69b4c7 1189{
9bea1d5b 1190 int i;
1191 http_hdr_type ht;
1192 assert(e);
1193
1194 HttpHeaderStats[0].parsedCount =
1195 HttpHeaderStats[hoRequest].parsedCount + HttpHeaderStats[hoReply].parsedCount;
1196 HttpHeaderStats[0].ccParsedCount =
1197 HttpHeaderStats[hoRequest].ccParsedCount + HttpHeaderStats[hoReply].ccParsedCount;
1198 HttpHeaderStats[0].destroyedCount =
1199 HttpHeaderStats[hoRequest].destroyedCount + HttpHeaderStats[hoReply].destroyedCount;
1200 HttpHeaderStats[0].busyDestroyedCount =
1201 HttpHeaderStats[hoRequest].busyDestroyedCount + HttpHeaderStats[hoReply].busyDestroyedCount;
1202
1203 for (i = 1; i < HttpHeaderStatCount; i++) {
1204 httpHeaderStatDump(HttpHeaderStats + i, e);
1205 storeAppendPrintf(e, "%s\n", "<br>");
12cf1be2 1206 }
9bea1d5b 1207 /* field stats for all messages */
1208 storeAppendPrintf(e, "\nHttp Fields Stats (replies and requests)\n");
1209 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\t %6s\n",
1210 "id", "name", "#alive", "%err", "%repeat");
e6ccf245 1211 for (ht = (http_hdr_type)0; ht < HDR_ENUM_END; ++ht) {
9bea1d5b 1212 HttpHeaderFieldInfo *f = Headers + ht;
1213 storeAppendPrintf(e, "%2d\t %-20s\t %5d\t %6.3f\t %6.3f\n",
528b2c61 1214 f->id, f->name.buf(), f->stat.aliveCount,
9bea1d5b 1215 xpercent(f->stat.errCount, f->stat.parsCount),
1216 xpercent(f->stat.repCount, f->stat.seenCount));
12cf1be2 1217 }
9bea1d5b 1218 storeAppendPrintf(e, "Headers Parsed: %d + %d = %d\n",
1219 HttpHeaderStats[hoRequest].parsedCount,
1220 HttpHeaderStats[hoReply].parsedCount,
1221 HttpHeaderStats[0].parsedCount);
1222 storeAppendPrintf(e, "Hdr Fields Parsed: %d\n", HeaderEntryParsedCount);
cb69b4c7 1223}
97474590 1224
e6ccf245 1225http_hdr_type
9bea1d5b 1226httpHeaderIdByName(const char *name, int name_len, const HttpHeaderFieldInfo * info, int end)
97474590 1227{
9bea1d5b 1228 int i;
1229 for (i = 0; i < end; ++i) {
528b2c61 1230 if (name_len >= 0 && name_len != info[i].name.size())
9bea1d5b 1231 continue;
528b2c61 1232 if (!strncasecmp(name, info[i].name.buf(),
1233 name_len < 0 ? info[i].name.size() + 1 : name_len))
e6ccf245 1234 return info[i].id;
97474590 1235 }
e6ccf245 1236 return HDR_BAD_HDR;
97474590 1237}
1238
e6ccf245 1239http_hdr_type
9bea1d5b 1240httpHeaderIdByNameDef(const char *name, int name_len)
97474590 1241{
9bea1d5b 1242 if (!Headers)
1243 Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END);
1244 return httpHeaderIdByName(name, name_len, Headers, HDR_ENUM_END);
97474590 1245}
efd900cb 1246
1247const char *
9bea1d5b 1248httpHeaderNameById(int id)
efd900cb 1249{
9bea1d5b 1250 if (!Headers)
1251 Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END);
1252 assert(id >= 0 && id < HDR_ENUM_END);
1253 return HeadersAttrs[id].name;
efd900cb 1254}