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