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