]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpHeader.cc
Minor cosmetic fix of squid_ldap_auth usage message
[thirdparty/squid.git] / src / HttpHeader.cc
CommitLineData
4f7e9dbb 1
cb69b4c7 2/*
d35b9a94 3 * $Id: HttpHeader.cc,v 1.71 2001/10/17 10:59:08 adrian 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{
d35b9a94 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
d35b9a94 129 {"X-Accelerator-Vary", HDR_X_ACCELERATOR_VARY, ftStr},
f66a9ef4 130#endif
d35b9a94 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{
d35b9a94 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, */
d35b9a94 153 HDR_UPGRADE,
154 HDR_VARY,
155 HDR_VIA,
99edd1c3 156 /* HDR_WARNING, */
d35b9a94 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
d35b9a94 162 HDR_X_ACCELERATOR_VARY,
f66a9ef4 163#endif
d35b9a94 164 HDR_X_FORWARDED_FOR
99edd1c3 165};
166
167/* general-headers */
168static http_hdr_type GeneralHeadersArr[] =
169{
d35b9a94 170 HDR_CACHE_CONTROL, HDR_CONNECTION, HDR_DATE, HDR_PRAGMA,
99edd1c3 171 /* HDR_TRANSFER_ENCODING, */
d35b9a94 172 HDR_UPGRADE,
99edd1c3 173 /* HDR_TRAILER, */
d35b9a94 174 HDR_VIA
99edd1c3 175};
176
177/* entity-headers */
178static http_hdr_type EntityHeadersArr[] =
179{
d35b9a94 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{
d35b9a94 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
d35b9a94 198 HDR_X_ACCELERATOR_VARY,
f66a9ef4 199#endif
d35b9a94 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{
d35b9a94 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{
d35b9a94 216 {"all"},
9f4d4f65 217#if USE_HTCP
d35b9a94 218 {"HTCP reply"},
9f4d4f65 219#endif
d35b9a94 220 {"request"},
221 {"reply"}
12cf1be2 222};
d35b9a94 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
d35b9a94 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
d35b9a94 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
d35b9a94 246httpHeaderInitModule (void)
cb69b4c7 247{
d35b9a94 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
d35b9a94 273 HttpHeaderStats[hoHtcpReply].owner_mask = &ReplyHeadersMask;
86aebcda 274#endif
d35b9a94 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
d35b9a94 283httpHeaderCleanModule (void)
7021844c 284{
d35b9a94 285 httpHeaderDestroyFieldsInfo (Headers, HDR_ENUM_END);
286 Headers = NULL;
287 httpHdrCcCleanModule ();
7021844c 288}
289
12cf1be2 290static void
d35b9a94 291httpHeaderStatInit (HttpHeaderStat * hs, const char *label)
12cf1be2 292{
d35b9a94 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
d35b9a94 307httpHeaderInit (HttpHeader * hdr, http_hdr_owner_type owner)
cb69b4c7 308{
d35b9a94 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
d35b9a94 318httpHeaderClean (HttpHeader * hdr)
cb69b4c7 319{
d35b9a94 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 {
332 /* tmp hack to try to avoid coredumps */
333 if (e->id < 0 || e->id >= HDR_ENUM_END)
334 {
335 debug (55, 0) ("httpHeaderClean BUG: entry[%d] is invalid (%d). Ignored.\n",
336 pos, e->id);
337 }
338 else
339 {
340 statHistCount (&HttpHeaderStats[hdr->owner].fieldTypeDistr, e->id);
341 /* yes, this destroy() leaves us in an incosistent state */
342 httpHeaderEntryDestroy (e);
c68e9c6b 343 }
12cf1be2 344 }
d35b9a94 345 arrayClean (&hdr->entries);
cb69b4c7 346}
347
2246b732 348/* append entries (also see httpHeaderUpdate) */
349void
d35b9a94 350httpHeaderAppend (HttpHeader * dest, const HttpHeader * src)
2246b732 351{
d35b9a94 352 const HttpHeaderEntry *e;
353 HttpHeaderPos pos = HttpHeaderInitPos;
354 assert (src && dest);
355 assert (src != dest);
356 debug (55, 7) ("appending hdr: %p += %p\n", dest, src);
357
358 while ((e = httpHeaderGetEntry (src, &pos)))
359 {
360 httpHeaderAddEntry (dest, httpHeaderEntryClone (e));
2246b732 361 }
362}
363
d8b249ef 364/* use fresh entries to replace old ones */
cb69b4c7 365void
d35b9a94 366httpHeaderUpdate (HttpHeader * old, const HttpHeader * fresh, const HttpHeaderMask * denied_mask)
cb69b4c7 367{
d35b9a94 368 const HttpHeaderEntry *e;
369 HttpHeaderPos pos = HttpHeaderInitPos;
370 assert (old && fresh);
371 assert (old != fresh);
372 debug (55, 7) ("updating hdr: %p <- %p\n", old, fresh);
373
374 while ((e = httpHeaderGetEntry (fresh, &pos)))
375 {
376 /* deny bad guys (ok to check for HDR_OTHER) here */
377 if (denied_mask && CBIT_TEST (*denied_mask, e->id))
378 continue;
379 httpHeaderDelByName (old, strBuf (e->name));
380 httpHeaderAddEntry (old, httpHeaderEntryClone (e));
cb69b4c7 381 }
cb69b4c7 382}
383
384/* just handy in parsing: resets and returns false */
9f4d4f65 385int
d35b9a94 386httpHeaderReset (HttpHeader * hdr)
2ac76861 387{
d35b9a94 388 http_hdr_owner_type ho = hdr->owner;
389 assert (hdr);
390 ho = hdr->owner;
391 httpHeaderClean (hdr);
392 httpHeaderInit (hdr, ho);
393 return 0;
cb69b4c7 394}
395
cb69b4c7 396int
d35b9a94 397httpHeaderParse (HttpHeader * hdr, const char *header_start, const char *header_end)
cb69b4c7 398{
d35b9a94 399 const char *field_start = header_start;
400 HttpHeaderEntry *e;
401
402 assert (hdr);
403 assert (header_start && header_end);
404 debug (55, 7) ("parsing hdr: (%p)\n%s\n", hdr, getStringPrefix (header_start, header_end));
405 HttpHeaderStats[hdr->owner].parsedCount++;
406 /* commonn format headers are "<name>:[ws]<value>" lines delimited by <CRLF> */
407 while (field_start < header_end)
408 {
409 const char *field_end;
410 const char *field_ptr = field_start;
411 do
412 {
413 field_end = field_ptr = field_ptr + strcspn (field_ptr, "\r\n");
414 /* skip CRLF */
415 if (*field_ptr == '\r')
416 field_ptr++;
417 if (*field_ptr == '\n')
418 field_ptr++;
419 }
420 while (*field_ptr == ' ' || *field_ptr == '\t');
421 if (!*field_end || field_end > header_end)
422 return httpHeaderReset (hdr); /* missing <CRLF> */
423 e = httpHeaderEntryParseCreate (field_start, field_end);
424 if (e != NULL)
425 httpHeaderAddEntry (hdr, e);
426 else
427 debug (55, 2) ("warning: ignoring unparseable http header field near '%s'\n",
428 getStringPrefix (field_start, field_end));
429 field_start = field_end;
430 /* skip CRLF */
431 if (*field_start == '\r')
432 field_start++;
433 if (*field_start == '\n')
434 field_start++;
cb69b4c7 435 }
d35b9a94 436 return 1; /* even if no fields where found, it is a valid header */
cb69b4c7 437}
438
99edd1c3 439/* packs all the entries using supplied packer */
cb69b4c7 440void
d35b9a94 441httpHeaderPackInto (const HttpHeader * hdr, Packer * p)
cb69b4c7 442{
d35b9a94 443 HttpHeaderPos pos = HttpHeaderInitPos;
444 const HttpHeaderEntry *e;
445 assert (hdr && p);
446 debug (55, 7) ("packing hdr: (%p)\n", hdr);
447 /* pack all entries one by one */
448 while ((e = httpHeaderGetEntry (hdr, &pos)))
449 httpHeaderEntryPackInto (e, p);
cb69b4c7 450}
451
452/* returns next valid entry */
99edd1c3 453HttpHeaderEntry *
d35b9a94 454httpHeaderGetEntry (const HttpHeader * hdr, HttpHeaderPos * pos)
cb69b4c7 455{
d35b9a94 456 assert (hdr && pos);
457 assert (*pos >= HttpHeaderInitPos && *pos < hdr->entries.count);
458 for ((*pos)++; *pos < hdr->entries.count; (*pos)++)
459 {
460 if (hdr->entries.items[*pos])
461 return hdr->entries.items[*pos];
cb69b4c7 462 }
d35b9a94 463 return NULL;
cb69b4c7 464}
465
466/*
d8b249ef 467 * returns a pointer to a specified entry if any
468 * note that we return one entry so it does not make much sense to ask for
469 * "list" headers
cb69b4c7 470 */
de336bbe 471HttpHeaderEntry *
d35b9a94 472httpHeaderFindEntry (const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 473{
d35b9a94 474 HttpHeaderPos pos = HttpHeaderInitPos;
475 HttpHeaderEntry *e;
476 assert (hdr);
477 assert_eid (id);
478 assert (!CBIT_TEST (ListHeadersMask, id));
479
480 /* check mask first */
481 if (!CBIT_TEST (hdr->mask, id))
482 return NULL;
483 /* looks like we must have it, do linear search */
484 while ((e = httpHeaderGetEntry (hdr, &pos)))
485 {
486 if (e->id == id)
487 return e;
cb69b4c7 488 }
d35b9a94 489 /* hm.. we thought it was there, but it was not found */
490 assert (0);
491 return NULL; /* not reached */
cb69b4c7 492}
493
a622fff0 494/*
495 * same as httpHeaderFindEntry
496 */
497static HttpHeaderEntry *
d35b9a94 498httpHeaderFindLastEntry (const HttpHeader * hdr, http_hdr_type id)
a622fff0 499{
d35b9a94 500 HttpHeaderPos pos = HttpHeaderInitPos;
501 HttpHeaderEntry *e;
502 HttpHeaderEntry *result = NULL;
503 assert (hdr);
504 assert_eid (id);
505 assert (!CBIT_TEST (ListHeadersMask, id));
506
507 /* check mask first */
508 if (!CBIT_TEST (hdr->mask, id))
509 return NULL;
510 /* looks like we must have it, do linear search */
511 while ((e = httpHeaderGetEntry (hdr, &pos)))
512 {
513 if (e->id == id)
514 result = e;
a622fff0 515 }
d35b9a94 516 assert (result); /* must be there! */
517 return result;
a622fff0 518}
519
cb69b4c7 520/*
d8b249ef 521 * deletes all fields with a given name if any, returns #fields deleted;
cb69b4c7 522 */
2ac76861 523int
d35b9a94 524httpHeaderDelByName (HttpHeader * hdr, const char *name)
cb69b4c7 525{
d35b9a94 526 int count = 0;
527 HttpHeaderPos pos = HttpHeaderInitPos;
528 HttpHeaderEntry *e;
529 httpHeaderMaskInit (&hdr->mask, 0); /* temporal inconsistency */
530 debug (55, 7) ("deleting '%s' fields in hdr %p\n", name, hdr);
531 while ((e = httpHeaderGetEntry (hdr, &pos)))
532 {
533 if (!strCaseCmp (e->name, name))
534 {
535 httpHeaderDelAt (hdr, pos);
536 count++;
537 }
538 else
539 CBIT_SET (hdr->mask, e->id);
cb69b4c7 540 }
d35b9a94 541 return count;
cb69b4c7 542}
543
2246b732 544/* deletes all entries with a given id, returns the #entries deleted */
545int
d35b9a94 546httpHeaderDelById (HttpHeader * hdr, http_hdr_type id)
d8b249ef 547{
d35b9a94 548 int count = 0;
549 HttpHeaderPos pos = HttpHeaderInitPos;
550 HttpHeaderEntry *e;
551 debug (55, 8) ("%p del-by-id %d\n", hdr, id);
552 assert (hdr);
553 assert_eid (id);
554 assert_eid (id != HDR_OTHER); /* does not make sense */
555 if (!CBIT_TEST (hdr->mask, id))
556 return 0;
557 while ((e = httpHeaderGetEntry (hdr, &pos)))
558 {
559 if (e->id == id)
560 {
561 httpHeaderDelAt (hdr, pos);
562 count++;
d8b249ef 563 }
564 }
d35b9a94 565 CBIT_CLR (hdr->mask, id);
566 assert (count);
567 return count;
d8b249ef 568}
d8b249ef 569
cb69b4c7 570/*
571 * deletes an entry at pos and leaves a gap; leaving a gap makes it
572 * possible to iterate(search) and delete fields at the same time
573 */
2246b732 574void
d35b9a94 575httpHeaderDelAt (HttpHeader * hdr, HttpHeaderPos pos)
cb69b4c7 576{
d35b9a94 577 HttpHeaderEntry *e;
578 assert (pos >= HttpHeaderInitPos && pos < hdr->entries.count);
579 e = hdr->entries.items[pos];
580 hdr->entries.items[pos] = NULL;
581 /* decrement header length, allow for ": " and crlf */
582 hdr->len -= strLen (e->name) + 2 + strLen (e->value) + 2;
583 assert (hdr->len >= 0);
584 httpHeaderEntryDestroy (e);
cb69b4c7 585}
586
99edd1c3 587
588/* appends an entry;
589 * does not call httpHeaderEntryClone() so one should not reuse "*e"
cb69b4c7 590 */
99edd1c3 591void
d35b9a94 592httpHeaderAddEntry (HttpHeader * hdr, HttpHeaderEntry * e)
cb69b4c7 593{
d35b9a94 594 assert (hdr && e);
595 assert_eid (e->id);
596
597 debug (55, 7) ("%p adding entry: %d at %d\n",
598 hdr, e->id, hdr->entries.count);
599 if (CBIT_TEST (hdr->mask, e->id))
600 Headers[e->id].stat.repCount++;
601 else
602 CBIT_SET (hdr->mask, e->id);
603 arrayAppend (&hdr->entries, e);
604 /* increment header length, allow for ": " and crlf */
605 hdr->len += strLen (e->name) + 2 + strLen (e->value) + 2;
cb69b4c7 606}
607
99edd1c3 608/* return a list of entries with the same id separated by ',' and ws */
609String
d35b9a94 610httpHeaderGetList (const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 611{
d35b9a94 612 String s = StringNull;
613 HttpHeaderEntry *e;
614 HttpHeaderPos pos = HttpHeaderInitPos;
615 debug (55, 6) ("%p: joining for id %d\n", hdr, id);
616 /* only fields from ListHeaders array can be "listed" */
617 assert (CBIT_TEST (ListHeadersMask, id));
618 if (!CBIT_TEST (hdr->mask, id))
d8b249ef 619 return s;
d35b9a94 620 while ((e = httpHeaderGetEntry (hdr, &pos)))
621 {
622 if (e->id == id)
623 strListAdd (&s, strBuf (e->value), ',');
624 }
625 /*
626 * note: we might get an empty (len==0) string if there was an "empty"
627 * header; we must not get a NULL string though.
628 */
629 assert (strBuf (s));
630 /* temporary warning: remove it! @?@ @?@ @?@ */
631 if (!strLen (s))
632 debug (55, 3) ("empty list header: %s (%d)\n", strBuf (Headers[id].name), id);
633 debug (55, 6) ("%p: joined for id %d: %s\n", hdr, id, strBuf (s));
634 return s;
cb69b4c7 635}
636
f66a9ef4 637/* return a string or list of entries with the same id separated by ',' and ws */
638String
d35b9a94 639httpHeaderGetStrOrList (const HttpHeader * hdr, http_hdr_type id)
f66a9ef4 640{
d35b9a94 641 const char *str;
642 String s;
643
644 if (CBIT_TEST (ListHeadersMask, id))
645 s = httpHeaderGetList (hdr, id);
646 else
647 {
648 str = httpHeaderGetStr (hdr, id);
649 stringInit (&s, str);
f66a9ef4 650 }
d35b9a94 651 return s;
f66a9ef4 652}
653
654/*
655 * returns a pointer to a specified entry if any
656 * note that we return one entry so it does not make much sense to ask for
657 * "list" headers
658 */
659String
d35b9a94 660httpHeaderGetByName (const HttpHeader * hdr, const char *name)
f66a9ef4 661{
d35b9a94 662 http_hdr_type id;
663 HttpHeaderPos pos = HttpHeaderInitPos;
664 HttpHeaderEntry *e;
665 String result = StringNull;
666
667 assert (hdr);
668 assert (name);
669
670 /* First try the quick path */
671 id = httpHeaderIdByNameDef (name, strlen (name));
672 if (id != -1)
673 return httpHeaderGetStrOrList (hdr, id);
674
675 /* Sorry, an unknown header name. Do linear search */
676 while ((e = httpHeaderGetEntry (hdr, &pos)))
677 {
678 if (e->id == HDR_OTHER && strCaseCmp (e->name, name) == 0)
679 {
680 strListAdd (&result, strBuf (e->value), ',');
f66a9ef4 681 }
682 }
d35b9a94 683 return result;
f66a9ef4 684}
cb69b4c7 685
cb69b4c7 686/* test if a field is present */
2ac76861 687int
d35b9a94 688httpHeaderHas (const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 689{
d35b9a94 690 assert (hdr);
691 assert_eid (id);
692 assert (id != HDR_OTHER);
693 debug (55, 7) ("%p lookup for %d\n", hdr, id);
694 return CBIT_TEST (hdr->mask, id);
cb69b4c7 695}
696
697void
d35b9a94 698httpHeaderPutInt (HttpHeader * hdr, http_hdr_type id, int number)
cb69b4c7 699{
d35b9a94 700 assert_eid (id);
701 assert (Headers[id].type == ftInt); /* must be of an appropriate type */
702 assert (number >= 0);
703 httpHeaderAddEntry (hdr, httpHeaderEntryCreate (id, NULL, xitoa (number)));
cb69b4c7 704}
705
706void
d35b9a94 707httpHeaderPutTime (HttpHeader * hdr, http_hdr_type id, time_t htime)
cb69b4c7 708{
d35b9a94 709 assert_eid (id);
710 assert (Headers[id].type == ftDate_1123); /* must be of an appropriate type */
711 assert (time >= 0);
712 httpHeaderAddEntry (hdr, httpHeaderEntryCreate (id, NULL, mkrfc1123 (htime)));
cb69b4c7 713}
2ac76861 714
cb69b4c7 715void
d35b9a94 716httpHeaderPutStr (HttpHeader * hdr, http_hdr_type id, const char *str)
cb69b4c7 717{
d35b9a94 718 assert_eid (id);
719 assert (Headers[id].type == ftStr); /* must be of an appropriate type */
720 assert (str);
721 httpHeaderAddEntry (hdr, httpHeaderEntryCreate (id, NULL, str));
cb69b4c7 722}
723
63259c34 724void
d35b9a94 725httpHeaderPutAuth (HttpHeader * hdr, const char *auth_scheme, const char *realm)
63259c34 726{
d35b9a94 727 assert (hdr && auth_scheme && realm);
728 httpHeaderPutStrf (hdr, HDR_WWW_AUTHENTICATE, "%s realm=\"%s\"", auth_scheme, realm);
63259c34 729}
730
99edd1c3 731void
d35b9a94 732httpHeaderPutCc (HttpHeader * hdr, const HttpHdrCc * cc)
99edd1c3 733{
d35b9a94 734 MemBuf mb;
735 Packer p;
736 assert (hdr && cc);
737 /* remove old directives if any */
738 httpHeaderDelById (hdr, HDR_CACHE_CONTROL);
739 /* pack into mb */
740 memBufDefInit (&mb);
741 packerToMemInit (&p, &mb);
742 httpHdrCcPackInto (cc, &p);
743 /* put */
744 httpHeaderAddEntry (hdr, httpHeaderEntryCreate (HDR_CACHE_CONTROL, NULL, mb.buf));
745 /* cleanup */
746 packerClean (&p);
747 memBufClean (&mb);
99edd1c3 748}
749
d192d11f 750void
d35b9a94 751httpHeaderPutContRange (HttpHeader * hdr, const HttpHdrContRange * cr)
d192d11f 752{
d35b9a94 753 MemBuf mb;
754 Packer p;
755 assert (hdr && cr);
756 /* remove old directives if any */
757 httpHeaderDelById (hdr, HDR_CONTENT_RANGE);
758 /* pack into mb */
759 memBufDefInit (&mb);
760 packerToMemInit (&p, &mb);
761 httpHdrContRangePackInto (cr, &p);
762 /* put */
763 httpHeaderAddEntry (hdr, httpHeaderEntryCreate (HDR_CONTENT_RANGE, NULL, mb.buf));
764 /* cleanup */
765 packerClean (&p);
766 memBufClean (&mb);
d192d11f 767}
768
769void
d35b9a94 770httpHeaderPutRange (HttpHeader * hdr, const HttpHdrRange * range)
d192d11f 771{
d35b9a94 772 MemBuf mb;
773 Packer p;
774 assert (hdr && range);
775 /* remove old directives if any */
776 httpHeaderDelById (hdr, HDR_RANGE);
777 /* pack into mb */
778 memBufDefInit (&mb);
779 packerToMemInit (&p, &mb);
780 httpHdrRangePackInto (range, &p);
781 /* put */
782 httpHeaderAddEntry (hdr, httpHeaderEntryCreate (HDR_RANGE, NULL, mb.buf));
783 /* cleanup */
784 packerClean (&p);
785 memBufClean (&mb);
d192d11f 786}
787
cb69b4c7 788/* add extension header (these fields are not parsed/analyzed/joined, etc.) */
789void
d35b9a94 790httpHeaderPutExt (HttpHeader * hdr, const char *name, const char *value)
cb69b4c7 791{
d35b9a94 792 assert (name && value);
793 debug (55, 8) ("%p adds ext entry '%s: %s'\n", hdr, name, value);
794 httpHeaderAddEntry (hdr, httpHeaderEntryCreate (HDR_OTHER, name, value));
cb69b4c7 795}
796
7c525cc2 797int
d35b9a94 798httpHeaderGetInt (const HttpHeader * hdr, http_hdr_type id)
7c525cc2 799{
d35b9a94 800 HttpHeaderEntry *e;
801 int value = -1;
802 int ok;
803 assert_eid (id);
804 assert (Headers[id].type == ftInt); /* must be of an appropriate type */
805 if ((e = httpHeaderFindEntry (hdr, id)))
806 {
807 ok = httpHeaderParseInt (strBuf (e->value), &value);
808 httpHeaderNoteParsedEntry (e->id, e->value, !ok);
d8b249ef 809 }
d35b9a94 810 return value;
7c525cc2 811}
812
de336bbe 813time_t
d35b9a94 814httpHeaderGetTime (const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 815{
d35b9a94 816 HttpHeaderEntry *e;
817 time_t value = -1;
818 assert_eid (id);
819 assert (Headers[id].type == ftDate_1123); /* must be of an appropriate type */
820 if ((e = httpHeaderFindEntry (hdr, id)))
821 {
822 value = parse_rfc1123 (strBuf (e->value));
823 httpHeaderNoteParsedEntry (e->id, e->value, value < 0);
d8b249ef 824 }
d35b9a94 825 return value;
cb69b4c7 826}
827
99edd1c3 828/* sync with httpHeaderGetLastStr */
de336bbe 829const char *
d35b9a94 830httpHeaderGetStr (const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 831{
d35b9a94 832 HttpHeaderEntry *e;
833 assert_eid (id);
834 assert (Headers[id].type == ftStr); /* must be of an appropriate type */
835 if ((e = httpHeaderFindEntry (hdr, id)))
836 {
837 httpHeaderNoteParsedEntry (e->id, e->value, 0); /* no errors are possible */
838 return strBuf (e->value);
d8b249ef 839 }
d35b9a94 840 return NULL;
cb69b4c7 841}
842
a622fff0 843/* unusual */
844const char *
d35b9a94 845httpHeaderGetLastStr (const HttpHeader * hdr, http_hdr_type id)
a622fff0 846{
d35b9a94 847 HttpHeaderEntry *e;
848 assert_eid (id);
849 assert (Headers[id].type == ftStr); /* must be of an appropriate type */
850 if ((e = httpHeaderFindLastEntry (hdr, id)))
851 {
852 httpHeaderNoteParsedEntry (e->id, e->value, 0); /* no errors are possible */
853 return strBuf (e->value);
a622fff0 854 }
d35b9a94 855 return NULL;
a622fff0 856}
857
7faf2bdb 858HttpHdrCc *
d35b9a94 859httpHeaderGetCc (const HttpHeader * hdr)
cb69b4c7 860{
d35b9a94 861 HttpHdrCc *cc;
862 String s;
863 if (!CBIT_TEST (hdr->mask, HDR_CACHE_CONTROL))
864 return NULL;
865 s = httpHeaderGetList (hdr, HDR_CACHE_CONTROL);
866 cc = httpHdrCcParseCreate (&s);
867 HttpHeaderStats[hdr->owner].ccParsedCount++;
868 if (cc)
869 httpHdrCcUpdateStats (cc, &HttpHeaderStats[hdr->owner].ccTypeDistr);
870 httpHeaderNoteParsedEntry (HDR_CACHE_CONTROL, s, !cc);
871 stringClean (&s);
872 return cc;
cb69b4c7 873}
874
02922e76 875HttpHdrRange *
d35b9a94 876httpHeaderGetRange (const HttpHeader * hdr)
02922e76 877{
d35b9a94 878 HttpHdrRange *r = NULL;
879 HttpHeaderEntry *e;
880 /* some clients will send "Request-Range" _and_ *matching* "Range"
881 * who knows, some clients might send Request-Range only;
882 * this "if" should work correctly in both cases;
883 * hopefully no clients send mismatched headers! */
884 if ((e = httpHeaderFindEntry (hdr, HDR_RANGE)) ||
885 (e = httpHeaderFindEntry (hdr, HDR_REQUEST_RANGE)))
886 {
887 r = httpHdrRangeParseCreate (&e->value);
888 httpHeaderNoteParsedEntry (e->id, e->value, !r);
d192d11f 889 }
d35b9a94 890 return r;
02922e76 891}
892
d76fcfa7 893HttpHdrContRange *
d35b9a94 894httpHeaderGetContRange (const HttpHeader * hdr)
d76fcfa7 895{
d35b9a94 896 HttpHdrContRange *cr = NULL;
897 HttpHeaderEntry *e;
898 if ((e = httpHeaderFindEntry (hdr, HDR_CONTENT_RANGE)))
899 {
900 cr = httpHdrContRangeParseCreate (strBuf (e->value));
901 httpHeaderNoteParsedEntry (e->id, e->value, !cr);
d8b249ef 902 }
d35b9a94 903 return cr;
cb69b4c7 904}
905
99edd1c3 906const char *
d35b9a94 907httpHeaderGetAuth (const HttpHeader * hdr, http_hdr_type id, const char *auth_scheme)
cb69b4c7 908{
d35b9a94 909 const char *field;
910 int l;
911 assert (hdr && auth_scheme);
912 field = httpHeaderGetStr (hdr, id);
913 if (!field) /* no authorization field */
914 return NULL;
915 l = strlen (auth_scheme);
916 if (!l || strncasecmp (field, auth_scheme, l)) /* wrong scheme */
917 return NULL;
918 field += l;
919 if (!xisspace (*field)) /* wrong scheme */
920 return NULL;
921 /* skip white space */
922 field += xcountws (field);
923 if (!*field) /* no authorization cookie */
924 return NULL;
925 return base64_decode (field);
cb69b4c7 926}
927
a9771e51 928ETag
d35b9a94 929httpHeaderGetETag (const HttpHeader * hdr, http_hdr_type id)
a9771e51 930{
d35b9a94 931 ETag etag =
932 {NULL, -1};
933 HttpHeaderEntry *e;
934 assert (Headers[id].type == ftETag); /* must be of an appropriate type */
935 if ((e = httpHeaderFindEntry (hdr, id)))
936 etagParseInit (&etag, strBuf (e->value));
937 return etag;
a9771e51 938}
939
940TimeOrTag
d35b9a94 941httpHeaderGetTimeOrTag (const HttpHeader * hdr, http_hdr_type id)
a9771e51 942{
d35b9a94 943 TimeOrTag tot;
944 HttpHeaderEntry *e;
945 assert (Headers[id].type == ftDate_1123_or_ETag); /* must be of an appropriate type */
946 memset (&tot, 0, sizeof (tot));
947 if ((e = httpHeaderFindEntry (hdr, id)))
948 {
949 const char *str = strBuf (e->value);
950 /* try as an ETag */
951 if (etagParseInit (&tot.tag, str))
952 {
953 tot.valid = tot.tag.str != NULL;
954 tot.time = -1;
955 }
956 else
957 {
958 /* or maybe it is time? */
959 tot.time = parse_rfc1123 (str);
960 tot.valid = tot.time >= 0;
961 tot.tag.str = NULL;
a9771e51 962 }
963 }
d35b9a94 964 assert (tot.time < 0 || !tot.tag.str); /* paranoid */
965 return tot;
a9771e51 966}
967
cb69b4c7 968/*
969 * HttpHeaderEntry
970 */
971
d8b249ef 972static HttpHeaderEntry *
d35b9a94 973httpHeaderEntryCreate (http_hdr_type id, const char *name, const char *value)
cb69b4c7 974{
d35b9a94 975 HttpHeaderEntry *e;
976 assert_eid (id);
977 e = memAllocate (MEM_HTTP_HDR_ENTRY);
978 e->id = id;
979 if (id != HDR_OTHER)
980 e->name = Headers[id].name;
981 else
982 stringInit (&e->name, name);
983 stringInit (&e->value, value);
984 Headers[id].stat.aliveCount++;
985 debug (55, 9) ("created entry %p: '%s: %s'\n", e, strBuf (e->name), strBuf (e->value));
986 return e;
cb69b4c7 987}
988
de336bbe 989static void
d35b9a94 990httpHeaderEntryDestroy (HttpHeaderEntry * e)
2ac76861 991{
d35b9a94 992 assert (e);
993 assert_eid (e->id);
994 debug (55, 9) ("destroying entry %p: '%s: %s'\n", e, strBuf (e->name), strBuf (e->value));
995 /* clean name if needed */
996 if (e->id == HDR_OTHER)
997 stringClean (&e->name);
998 stringClean (&e->value);
999 assert (Headers[e->id].stat.aliveCount);
1000 Headers[e->id].stat.aliveCount--;
1001 e->id = -1;
1002 memFree (e, MEM_HTTP_HDR_ENTRY);
cb69b4c7 1003}
1004
d8b249ef 1005/* parses and inits header entry, returns new entry on success */
1006static HttpHeaderEntry *
d35b9a94 1007httpHeaderEntryParseCreate (const char *field_start, const char *field_end)
cb69b4c7 1008{
d35b9a94 1009 HttpHeaderEntry *e;
1010 int id;
1011 /* note: name_start == field_start */
1012 const char *name_end = strchr (field_start, ':');
1013 const int name_len = name_end ? name_end - field_start : 0;
1014 const char *value_start = field_start + name_len + 1; /* skip ':' */
1015 /* note: value_end == field_end */
1016
1017 HeaderEntryParsedCount++;
1018
1019 /* do we have a valid field name within this field? */
1020 if (!name_len || name_end > field_end)
1021 return NULL;
1022 if (name_len > 65536)
1023 {
1024 /* String has a 64K limit */
1025 debug (55, 1) ("WARNING: ignoring header name of %d bytes\n", name_len);
1026 return NULL;
25acfb53 1027 }
d35b9a94 1028 /* now we know we can parse it */
1029 e = memAllocate (MEM_HTTP_HDR_ENTRY);
1030 debug (55, 9) ("creating entry %p: near '%s'\n", e, getStringPrefix (field_start, field_end));
1031 /* is it a "known" field? */
1032 id = httpHeaderIdByName (field_start, name_len, Headers, HDR_ENUM_END);
1033 if (id < 0)
1034 id = HDR_OTHER;
1035 assert_eid (id);
1036 e->id = id;
1037 /* set field name */
1038 if (id == HDR_OTHER)
1039 stringLimitInit (&e->name, field_start, name_len);
1040 else
1041 e->name = Headers[id].name;
1042 /* trim field value */
1043 while (value_start < field_end && xisspace (*value_start))
1044 value_start++;
1045 if (field_end - value_start > 65536)
1046 {
1047 /* String has a 64K limit */
1048 debug (55, 1) ("WARNING: ignoring '%s' header of %d bytes\n",
1049 strBuf (e->name), field_end - value_start);
1050 if (e->id == HDR_OTHER)
1051 stringClean (&e->name);
1052 memFree (e, MEM_HTTP_HDR_ENTRY);
1053 return NULL;
25acfb53 1054 }
d35b9a94 1055 /* set field value */
1056 stringLimitInit (&e->value, value_start, field_end - value_start);
1057 Headers[id].stat.seenCount++;
1058 Headers[id].stat.aliveCount++;
1059 debug (55, 9) ("created entry %p: '%s: %s'\n", e, strBuf (e->name), strBuf (e->value));
1060 return e;
cb69b4c7 1061}
1062
99edd1c3 1063HttpHeaderEntry *
d35b9a94 1064httpHeaderEntryClone (const HttpHeaderEntry * e)
cb69b4c7 1065{
d35b9a94 1066 return httpHeaderEntryCreate (e->id, strBuf (e->name), strBuf (e->value));
cb69b4c7 1067}
1068
de336bbe 1069void
d35b9a94 1070httpHeaderEntryPackInto (const HttpHeaderEntry * e, Packer * p)
cb69b4c7 1071{
d35b9a94 1072 assert (e && p);
1073 packerAppend (p, strBuf (e->name), strLen (e->name));
1074 packerAppend (p, ": ", 2);
1075 packerAppend (p, strBuf (e->value), strLen (e->value));
1076 packerAppend (p, "\r\n", 2);
cb69b4c7 1077}
1078
1079static void
d35b9a94 1080httpHeaderNoteParsedEntry (http_hdr_type id, String context, int error)
cb69b4c7 1081{
d35b9a94 1082 Headers[id].stat.parsCount++;
1083 if (error)
1084 {
1085 Headers[id].stat.errCount++;
1086 debug (55, 2) ("cannot parse hdr field: '%s: %s'\n",
1087 strBuf (Headers[id].name), strBuf (context));
cb69b4c7 1088 }
cb69b4c7 1089}
1090
cb69b4c7 1091/*
12cf1be2 1092 * Reports
cb69b4c7 1093 */
cb69b4c7 1094
fcd2d3ef 1095/* tmp variable used to pass stat info to dumpers */
0cdcddb9 1096extern const HttpHeaderStat *dump_stat; /* argh! */
fcd2d3ef 1097const HttpHeaderStat *dump_stat = NULL;
1098
cb69b4c7 1099static void
d35b9a94 1100httpHeaderFieldStatDumper (StoreEntry * sentry, int idx, double val, double size, int count)
cb69b4c7 1101{
d35b9a94 1102 const int id = (int) val;
1103 const int valid_id = id >= 0 && id < HDR_ENUM_END;
1104 const char *name = valid_id ? strBuf (Headers[id].name) : "INVALID";
1105 int visible = count > 0;
1106 /* for entries with zero count, list only those that belong to current type of message */
1107 if (!visible && valid_id && dump_stat->owner_mask)
1108 visible = CBIT_TEST (*dump_stat->owner_mask, id);
1109 if (visible)
1110 storeAppendPrintf (sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
1111 id, name, count, xdiv (count, dump_stat->busyDestroyedCount));
cb69b4c7 1112}
1113
12cf1be2 1114static void
d35b9a94 1115httpHeaderFldsPerHdrDumper (StoreEntry * sentry, int idx, double val, double size, int count)
cb69b4c7 1116{
d35b9a94 1117 if (count)
1118 storeAppendPrintf (sentry, "%2d\t %5d\t %5d\t %6.2f\n",
1119 idx, (int) val, count,
1120 xpercent (count, dump_stat->destroyedCount));
cb69b4c7 1121}
1122
1123
1124static void
d35b9a94 1125httpHeaderStatDump (const HttpHeaderStat * hs, StoreEntry * e)
12cf1be2 1126{
d35b9a94 1127 assert (hs && e);
1128
1129 dump_stat = hs;
1130 storeAppendPrintf (e, "\nHeader Stats: %s\n", hs->label);
1131 storeAppendPrintf (e, "\nField type distribution\n");
1132 storeAppendPrintf (e, "%2s\t %-20s\t %5s\t %6s\n",
1133 "id", "name", "count", "#/header");
1134 statHistDump (&hs->fieldTypeDistr, e, httpHeaderFieldStatDumper);
1135 storeAppendPrintf (e, "\nCache-control directives distribution\n");
1136 storeAppendPrintf (e, "%2s\t %-20s\t %5s\t %6s\n",
1137 "id", "name", "count", "#/cc_field");
1138 statHistDump (&hs->ccTypeDistr, e, httpHdrCcStatDumper);
1139 storeAppendPrintf (e, "\nNumber of fields per header distribution\n");
1140 storeAppendPrintf (e, "%2s\t %-5s\t %5s\t %6s\n",
1141 "id", "#flds", "count", "%total");
1142 statHistDump (&hs->hdrUCountDistr, e, httpHeaderFldsPerHdrDumper);
1143 dump_stat = NULL;
cb69b4c7 1144}
1145
12cf1be2 1146void
d35b9a94 1147httpHeaderStoreReport (StoreEntry * e)
cb69b4c7 1148{
d35b9a94 1149 int i;
1150 http_hdr_type ht;
1151 assert (e);
1152
1153 HttpHeaderStats[0].parsedCount =
1154 HttpHeaderStats[hoRequest].parsedCount + HttpHeaderStats[hoReply].parsedCount;
1155 HttpHeaderStats[0].ccParsedCount =
1156 HttpHeaderStats[hoRequest].ccParsedCount + HttpHeaderStats[hoReply].ccParsedCount;
1157 HttpHeaderStats[0].destroyedCount =
1158 HttpHeaderStats[hoRequest].destroyedCount + HttpHeaderStats[hoReply].destroyedCount;
1159 HttpHeaderStats[0].busyDestroyedCount =
1160 HttpHeaderStats[hoRequest].busyDestroyedCount + HttpHeaderStats[hoReply].busyDestroyedCount;
1161
1162 for (i = 1; i < HttpHeaderStatCount; i++)
1163 {
1164 httpHeaderStatDump (HttpHeaderStats + i, e);
1165 storeAppendPrintf (e, "%s\n", "<br>");
12cf1be2 1166 }
d35b9a94 1167 /* field stats for all messages */
1168 storeAppendPrintf (e, "\nHttp Fields Stats (replies and requests)\n");
1169 storeAppendPrintf (e, "%2s\t %-20s\t %5s\t %6s\t %6s\n",
1170 "id", "name", "#alive", "%err", "%repeat");
1171 for (ht = 0; ht < HDR_ENUM_END; ht++)
1172 {
1173 HttpHeaderFieldInfo *f = Headers + ht;
1174 storeAppendPrintf (e, "%2d\t %-20s\t %5d\t %6.3f\t %6.3f\n",
1175 f->id, strBuf (f->name), f->stat.aliveCount,
1176 xpercent (f->stat.errCount, f->stat.parsCount),
1177 xpercent (f->stat.repCount, f->stat.seenCount));
12cf1be2 1178 }
d35b9a94 1179 storeAppendPrintf (e, "Headers Parsed: %d + %d = %d\n",
1180 HttpHeaderStats[hoRequest].parsedCount,
1181 HttpHeaderStats[hoReply].parsedCount,
1182 HttpHeaderStats[0].parsedCount);
1183 storeAppendPrintf (e, "Hdr Fields Parsed: %d\n", HeaderEntryParsedCount);
cb69b4c7 1184}
97474590 1185
1186int
d35b9a94 1187httpHeaderIdByName (const char *name, int name_len, const HttpHeaderFieldInfo * info, int end)
97474590 1188{
d35b9a94 1189 int i;
1190 for (i = 0; i < end; ++i)
1191 {
1192 if (name_len >= 0 && name_len != strLen (info[i].name))
1193 continue;
1194 if (!strncasecmp (name, strBuf (info[i].name),
1195 name_len < 0 ? strLen (info[i].name) + 1 : name_len))
1196 return i;
97474590 1197 }
d35b9a94 1198 return -1;
97474590 1199}
1200
1201int
d35b9a94 1202httpHeaderIdByNameDef (const char *name, int name_len)
97474590 1203{
d35b9a94 1204 if (!Headers)
1205 Headers = httpHeaderBuildFieldsInfo (HeadersAttrs, HDR_ENUM_END);
1206 return httpHeaderIdByName (name, name_len, Headers, HDR_ENUM_END);
97474590 1207}
efd900cb 1208
1209const char *
d35b9a94 1210httpHeaderNameById (int id)
efd900cb 1211{
d35b9a94 1212 if (!Headers)
1213 Headers = httpHeaderBuildFieldsInfo (HeadersAttrs, HDR_ENUM_END);
1214 assert (id >= 0 && id < HDR_ENUM_END);
1215 return HeadersAttrs[id].name;
efd900cb 1216}