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