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