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