]> git.ipfire.org Git - thirdparty/squid.git/blame_incremental - src/HttpHeader.cc
bugzilla #710: round-robin cache_dir selection incorrectly compares max-size
[thirdparty/squid.git] / src / HttpHeader.cc
... / ...
CommitLineData
1
2/*
3 * $Id: HttpHeader.cc,v 1.93 2003/07/15 06:50:39 robertc Exp $
4 *
5 * DEBUG: section 55 HTTP Header
6 * AUTHOR: Alex Rousskov
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
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.
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
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36#include "squid.h"
37#include "Store.h"
38#include "HttpHeader.h"
39#include "HttpHdrContRange.h"
40
41/*
42 * On naming conventions:
43 *
44 * HTTP/1.1 defines message-header as
45 *
46 * message-header = field-name ":" [ field-value ] CRLF
47 * field-name = token
48 * field-value = *( field-content | LWS )
49 *
50 * HTTP/1.1 does not give a name name a group of all message-headers in a message.
51 * Squid 1.1 seems to refer to that group _plus_ start-line as "headers".
52 *
53 * HttpHeader is an object that represents all message-headers in a message.
54 * HttpHeader does not manage start-line.
55 *
56 * HttpHeader is implemented as a collection of header "entries".
57 * An entry is a (field_id, field_name, field_value) triplet.
58 */
59
60
61/*
62 * local constants and vars
63 */
64
65/*
66 * A table with major attributes for every known field.
67 * We calculate name lengths and reorganize this array on start up.
68 * After reorganization, field id can be used as an index to the table.
69 */
70static const HttpHeaderFieldAttrs HeadersAttrs[] =
71 {
72 {"Accept", HDR_ACCEPT, ftStr},
73
74 {"Accept-Charset", HDR_ACCEPT_CHARSET, ftStr},
75 {"Accept-Encoding", HDR_ACCEPT_ENCODING, ftStr},
76 {"Accept-Language", HDR_ACCEPT_LANGUAGE, ftStr},
77 {"Accept-Ranges", HDR_ACCEPT_RANGES, ftStr},
78 {"Age", HDR_AGE, ftInt},
79 {"Allow", HDR_ALLOW, ftStr},
80 {"Authorization", HDR_AUTHORIZATION, ftStr}, /* for now */
81 {"Cache-Control", HDR_CACHE_CONTROL, ftPCc},
82 {"Connection", HDR_CONNECTION, ftStr},
83 {"Content-Base", HDR_CONTENT_BASE, ftStr},
84 {"Content-Encoding", HDR_CONTENT_ENCODING, ftStr},
85 {"Content-Language", HDR_CONTENT_LANGUAGE, ftStr},
86 {"Content-Length", HDR_CONTENT_LENGTH, ftInt},
87 {"Content-Location", HDR_CONTENT_LOCATION, ftStr},
88 {"Content-MD5", HDR_CONTENT_MD5, ftStr}, /* for now */
89 {"Content-Range", HDR_CONTENT_RANGE, ftPContRange},
90 {"Content-Type", HDR_CONTENT_TYPE, ftStr},
91 {"Cookie", HDR_COOKIE, ftStr},
92 {"Date", HDR_DATE, ftDate_1123},
93 {"ETag", HDR_ETAG, ftETag},
94 {"Expires", HDR_EXPIRES, ftDate_1123},
95 {"From", HDR_FROM, ftStr},
96 {"Host", HDR_HOST, ftStr},
97 {"If-Match", HDR_IF_MATCH, ftStr}, /* for now */
98 {"If-Modified-Since", HDR_IF_MODIFIED_SINCE, ftDate_1123},
99 {"If-None-Match", HDR_IF_NONE_MATCH, ftStr}, /* for now */
100 {"If-Range", HDR_IF_RANGE, ftDate_1123_or_ETag},
101 {"Last-Modified", HDR_LAST_MODIFIED, ftDate_1123},
102 {"Link", HDR_LINK, ftStr},
103 {"Location", HDR_LOCATION, ftStr},
104 {"Max-Forwards", HDR_MAX_FORWARDS, ftInt},
105 {"Mime-Version", HDR_MIME_VERSION, ftStr}, /* for now */
106 {"Pragma", HDR_PRAGMA, ftStr},
107 {"Proxy-Authenticate", HDR_PROXY_AUTHENTICATE, ftStr},
108 {"Proxy-Authentication-Info", HDR_PROXY_AUTHENTICATION_INFO, ftStr},
109 {"Proxy-Authorization", HDR_PROXY_AUTHORIZATION, ftStr},
110 {"Proxy-Connection", HDR_PROXY_CONNECTION, ftStr},
111 {"Public", HDR_PUBLIC, ftStr},
112 {"Range", HDR_RANGE, ftPRange},
113 {"Referer", HDR_REFERER, ftStr},
114 {"Request-Range", HDR_REQUEST_RANGE, ftPRange}, /* usually matches HDR_RANGE */
115 {"Retry-After", HDR_RETRY_AFTER, ftStr}, /* for now (ftDate_1123 or ftInt!) */
116 {"Server", HDR_SERVER, ftStr},
117 {"Set-Cookie", HDR_SET_COOKIE, ftStr},
118 {"Title", HDR_TITLE, ftStr},
119 {"Transfer-Encoding", HDR_TRANSFER_ENCODING, ftStr},
120 {"Upgrade", HDR_UPGRADE, ftStr}, /* for now */
121 {"User-Agent", HDR_USER_AGENT, ftStr},
122 {"Vary", HDR_VARY, ftStr}, /* for now */
123 {"Via", HDR_VIA, ftStr}, /* for now */
124 {"Warning", HDR_WARNING, ftStr}, /* for now */
125 {"WWW-Authenticate", HDR_WWW_AUTHENTICATE, ftStr},
126 {"Authentication-Info", HDR_AUTHENTICATION_INFO, ftStr},
127 {"X-Cache", HDR_X_CACHE, ftStr},
128 {"X-Cache-Lookup", HDR_X_CACHE_LOOKUP, ftStr},
129 {"X-Forwarded-For", HDR_X_FORWARDED_FOR, ftStr},
130 {"X-Request-URI", HDR_X_REQUEST_URI, ftStr},
131 {"X-Squid-Error", HDR_X_SQUID_ERROR, ftStr},
132 {"Negotiate", HDR_NEGOTIATE, ftStr},
133#if X_ACCELERATOR_VARY
134 {"X-Accelerator-Vary", HDR_X_ACCELERATOR_VARY, ftStr},
135#endif
136 {"Surrogate-Capability", HDR_SURROGATE_CAPABILITY, ftStr},
137 {"Surrogate-Control", HDR_SURROGATE_CONTROL, ftPSc},
138 {"Front-End-Https", HDR_FRONT_END_HTTPS, ftStr},
139 {"Other:", HDR_OTHER, ftStr} /* ':' will not allow matches */
140 };
141
142static HttpHeaderFieldInfo *Headers = NULL;
143
144http_hdr_type &operator++ (http_hdr_type &aHeader)
145{
146 int tmp = (int)aHeader;
147 aHeader = (http_hdr_type)(++tmp);
148 return aHeader;
149}
150
151
152/*
153 * headers with field values defined as #(values) in HTTP/1.1
154 * Headers that are currently not recognized, are commented out.
155 */
156static HttpHeaderMask ListHeadersMask; /* set run-time using ListHeadersArr */
157static http_hdr_type ListHeadersArr[] =
158 {
159 HDR_ACCEPT,
160 HDR_ACCEPT_CHARSET, HDR_ACCEPT_ENCODING, HDR_ACCEPT_LANGUAGE,
161 HDR_ACCEPT_RANGES, HDR_ALLOW,
162 HDR_CACHE_CONTROL,
163 HDR_CONTENT_ENCODING,
164 HDR_CONTENT_LANGUAGE,
165 HDR_CONNECTION,
166 HDR_IF_MATCH, HDR_IF_NONE_MATCH,
167 HDR_LINK, HDR_PRAGMA,
168 HDR_PROXY_CONNECTION,
169 HDR_TRANSFER_ENCODING,
170 HDR_UPGRADE,
171 HDR_VARY,
172 HDR_VIA,
173 /* HDR_WARNING, */
174 HDR_WWW_AUTHENTICATE,
175 HDR_AUTHENTICATION_INFO,
176 HDR_PROXY_AUTHENTICATION_INFO,
177 /* HDR_EXPECT, HDR_TE, HDR_TRAILER */
178#if X_ACCELERATOR_VARY
179 HDR_X_ACCELERATOR_VARY,
180#endif
181 HDR_SURROGATE_CAPABILITY,
182 HDR_SURROGATE_CONTROL,
183 HDR_X_FORWARDED_FOR
184 };
185
186/* general-headers */
187static http_hdr_type GeneralHeadersArr[] =
188 {
189 HDR_CACHE_CONTROL, HDR_CONNECTION, HDR_DATE, HDR_PRAGMA,
190 HDR_TRANSFER_ENCODING,
191 HDR_UPGRADE,
192 /* HDR_TRAILER, */
193 HDR_VIA,
194 };
195
196/* entity-headers */
197static http_hdr_type EntityHeadersArr[] =
198 {
199 HDR_ALLOW, HDR_CONTENT_BASE, HDR_CONTENT_ENCODING, HDR_CONTENT_LANGUAGE,
200 HDR_CONTENT_LENGTH, HDR_CONTENT_LOCATION, HDR_CONTENT_MD5,
201 HDR_CONTENT_RANGE, HDR_CONTENT_TYPE, HDR_ETAG, HDR_EXPIRES, HDR_LAST_MODIFIED, HDR_LINK,
202 HDR_OTHER
203 };
204
205static HttpHeaderMask ReplyHeadersMask; /* set run-time using ReplyHeaders */
206static http_hdr_type ReplyHeadersArr[] =
207 {
208 HDR_ACCEPT, HDR_ACCEPT_CHARSET, HDR_ACCEPT_ENCODING, HDR_ACCEPT_LANGUAGE,
209 HDR_ACCEPT_RANGES, HDR_AGE,
210 HDR_LOCATION, HDR_MAX_FORWARDS,
211 HDR_MIME_VERSION, HDR_PUBLIC, HDR_RETRY_AFTER, HDR_SERVER, HDR_SET_COOKIE,
212 HDR_VARY,
213 HDR_WARNING, HDR_PROXY_CONNECTION, HDR_X_CACHE,
214 HDR_X_CACHE_LOOKUP,
215 HDR_X_REQUEST_URI,
216#if X_ACCELERATOR_VARY
217 HDR_X_ACCELERATOR_VARY,
218#endif
219 HDR_X_SQUID_ERROR,
220 HDR_SURROGATE_CONTROL
221 };
222
223static HttpHeaderMask RequestHeadersMask; /* set run-time using RequestHeaders */
224static http_hdr_type RequestHeadersArr[] =
225 {
226 HDR_AUTHORIZATION, HDR_FROM, HDR_HOST,
227 HDR_IF_MATCH, HDR_IF_MODIFIED_SINCE, HDR_IF_NONE_MATCH,
228 HDR_IF_RANGE, HDR_MAX_FORWARDS, HDR_PROXY_CONNECTION,
229 HDR_PROXY_AUTHORIZATION, HDR_RANGE, HDR_REFERER, HDR_REQUEST_RANGE,
230 HDR_USER_AGENT, HDR_X_FORWARDED_FOR, HDR_SURROGATE_CAPABILITY
231 };
232
233/* header accounting */
234static HttpHeaderStat HttpHeaderStats[] =
235 {
236 {"all"},
237#if USE_HTCP
238 {"HTCP reply"},
239#endif
240 {"request"},
241 {"reply"}
242 };
243static int HttpHeaderStatCount = countof(HttpHeaderStats);
244
245static int HeaderEntryParsedCount = 0;
246
247/*
248 * local routines
249 */
250
251#define assert_eid(id) assert((id) >= 0 && (id) < HDR_ENUM_END)
252
253static HttpHeaderEntry *httpHeaderEntryCreate(http_hdr_type id, const char *name, const char *value);
254static void httpHeaderEntryDestroy(HttpHeaderEntry * e);
255static HttpHeaderEntry *httpHeaderEntryParseCreate(const char *field_start, const char *field_end);
256static void httpHeaderNoteParsedEntry(http_hdr_type id, String value, int error);
257
258static void httpHeaderStatInit(HttpHeaderStat * hs, const char *label);
259static void httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e);
260
261/*
262 * Module initialization routines
263 */
264
265void
266httpHeaderInitModule(void)
267{
268 int i;
269 /* check that we have enough space for masks */
270 assert(8 * sizeof(HttpHeaderMask) >= HDR_ENUM_END);
271 /* all headers must be described */
272 assert(countof(HeadersAttrs) == HDR_ENUM_END);
273
274 if (!Headers)
275 Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END);
276
277 /* create masks */
278 httpHeaderMaskInit(&ListHeadersMask, 0);
279
280 httpHeaderCalcMask(&ListHeadersMask, (const int *) ListHeadersArr, countof(ListHeadersArr));
281
282 httpHeaderMaskInit(&ReplyHeadersMask, 0);
283
284 httpHeaderCalcMask(&ReplyHeadersMask, (const int *) ReplyHeadersArr, countof(ReplyHeadersArr));
285
286 httpHeaderCalcMask(&ReplyHeadersMask, (const int *) GeneralHeadersArr, countof(GeneralHeadersArr));
287
288 httpHeaderCalcMask(&ReplyHeadersMask, (const int *) EntityHeadersArr, countof(EntityHeadersArr));
289
290 httpHeaderMaskInit(&RequestHeadersMask, 0);
291
292 httpHeaderCalcMask(&RequestHeadersMask, (const int *) RequestHeadersArr, countof(RequestHeadersArr));
293
294 httpHeaderCalcMask(&RequestHeadersMask, (const int *) GeneralHeadersArr, countof(GeneralHeadersArr));
295
296 httpHeaderCalcMask(&RequestHeadersMask, (const int *) EntityHeadersArr, countof(EntityHeadersArr));
297
298 /* init header stats */
299 assert(HttpHeaderStatCount == hoReply + 1);
300
301 for (i = 0; i < HttpHeaderStatCount; i++)
302 httpHeaderStatInit(HttpHeaderStats + i, HttpHeaderStats[i].label);
303
304 HttpHeaderStats[hoRequest].owner_mask = &RequestHeadersMask;
305
306 HttpHeaderStats[hoReply].owner_mask = &ReplyHeadersMask;
307
308#if USE_HTCP
309
310 HttpHeaderStats[hoHtcpReply].owner_mask = &ReplyHeadersMask;
311
312#endif
313 /* init dependent modules */
314 httpHdrCcInitModule();
315
316 httpHdrScInitModule();
317
318 /* register with cache manager */
319 cachemgrRegister("http_headers",
320 "HTTP Header Statistics", httpHeaderStoreReport, 0, 1);
321}
322
323void
324httpHeaderCleanModule(void)
325{
326 httpHeaderDestroyFieldsInfo(Headers, HDR_ENUM_END);
327 Headers = NULL;
328 httpHdrCcCleanModule();
329 httpHdrScCleanModule();
330}
331
332static void
333httpHeaderStatInit(HttpHeaderStat * hs, const char *label)
334{
335 assert(hs);
336 assert(label);
337 memset(hs, 0, sizeof(HttpHeaderStat));
338 hs->label = label;
339 statHistEnumInit(&hs->hdrUCountDistr, 32); /* not a real enum */
340 statHistEnumInit(&hs->fieldTypeDistr, HDR_ENUM_END);
341 statHistEnumInit(&hs->ccTypeDistr, CC_ENUM_END);
342 statHistEnumInit(&hs->scTypeDistr, SC_ENUM_END);
343}
344
345/*
346 * HttpHeader Implementation
347 */
348
349HttpHeader::HttpHeader() : owner (hoNone), len (0)
350{
351 httpHeaderMaskInit(&mask, 0);
352}
353
354HttpHeader::HttpHeader(http_hdr_owner_type const &anOwner) : owner (anOwner), len (0)
355{
356 assert(this);
357 assert(anOwner > hoNone && anOwner <= hoReply);
358 debug(55, 7) ("init-ing hdr: %p owner: %d\n", this, owner);
359 httpHeaderMaskInit(&mask, 0);
360}
361
362HttpHeader::~HttpHeader()
363{
364 httpHeaderClean (this);
365}
366
367void
368httpHeaderClean(HttpHeader * hdr)
369{
370 HttpHeaderPos pos = HttpHeaderInitPos;
371 HttpHeaderEntry *e;
372
373 assert(hdr);
374 assert(hdr->owner > hoNone && hdr->owner <= hoReply);
375 debug(55, 7) ("cleaning hdr: %p owner: %d\n", hdr, hdr->owner);
376
377 statHistCount(&HttpHeaderStats[hdr->owner].hdrUCountDistr, hdr->entries.count);
378 HttpHeaderStats[hdr->owner].destroyedCount++;
379 HttpHeaderStats[hdr->owner].busyDestroyedCount += hdr->entries.count > 0;
380
381 while ((e = httpHeaderGetEntry(hdr, &pos))) {
382 /* tmp hack to try to avoid coredumps */
383
384 if (e->id < 0 || e->id >= HDR_ENUM_END) {
385 debug(55, 0) ("httpHeaderClean BUG: entry[%d] is invalid (%d). Ignored.\n",
386 (int) pos, e->id);
387 } else {
388 statHistCount(&HttpHeaderStats[hdr->owner].fieldTypeDistr, e->id);
389 /* yes, this destroy() leaves us in an incosistent state */
390 httpHeaderEntryDestroy(e);
391 }
392 }
393
394 hdr->entries.clean();
395}
396
397/* append entries (also see httpHeaderUpdate) */
398void
399httpHeaderAppend(HttpHeader * dest, const HttpHeader * src)
400{
401 const HttpHeaderEntry *e;
402 HttpHeaderPos pos = HttpHeaderInitPos;
403 assert(src && dest);
404 assert(src != dest);
405 debug(55, 7) ("appending hdr: %p += %p\n", dest, src);
406
407 while ((e = httpHeaderGetEntry(src, &pos))) {
408 httpHeaderAddEntry(dest, httpHeaderEntryClone(e));
409 }
410}
411
412/* use fresh entries to replace old ones */
413void
414httpHeaderUpdate(HttpHeader * old, const HttpHeader * fresh, const HttpHeaderMask * denied_mask)
415{
416 assert (old);
417 old->update (fresh, denied_mask);
418}
419
420void
421HttpHeader::update (HttpHeader const *fresh, HttpHeaderMask const *denied_mask)
422{
423 const HttpHeaderEntry *e;
424 HttpHeaderPos pos = HttpHeaderInitPos;
425 assert(this && fresh);
426 assert(this != fresh);
427 debug(55, 7) ("updating hdr: %p <- %p\n", this, fresh);
428
429 while ((e = httpHeaderGetEntry(fresh, &pos))) {
430 /* deny bad guys (ok to check for HDR_OTHER) here */
431
432 if (denied_mask && CBIT_TEST(*denied_mask, e->id))
433 continue;
434
435 httpHeaderDelByName(this, e->name.buf());
436
437 httpHeaderAddEntry(this, httpHeaderEntryClone(e));
438 }
439}
440
441/* just handy in parsing: resets and returns false */
442int
443httpHeaderReset(HttpHeader * hdr)
444{
445 http_hdr_owner_type ho;
446 assert(hdr);
447 ho = hdr->owner;
448 httpHeaderClean(hdr);
449 *hdr = HttpHeader(ho);
450 return 0;
451}
452
453int
454httpHeaderParse(HttpHeader * hdr, const char *header_start, const char *header_end)
455{
456 const char *field_start = header_start;
457 HttpHeaderEntry *e;
458
459 assert(hdr);
460 assert(header_start && header_end);
461 debug(55, 7) ("parsing hdr: (%p)\n%s\n", hdr, getStringPrefix(header_start, header_end));
462 HttpHeaderStats[hdr->owner].parsedCount++;
463 /* commonn format headers are "<name>:[ws]<value>" lines delimited by <CRLF> */
464
465 while (field_start < header_end) {
466 const char *field_end;
467 const char *field_ptr = field_start;
468
469 do {
470 field_end = field_ptr = field_ptr + strcspn(field_ptr, "\r\n");
471 /* skip CRLF */
472
473 if (*field_ptr == '\r')
474 field_ptr++;
475
476 if (*field_ptr == '\n')
477 field_ptr++;
478 } while (*field_ptr == ' ' || *field_ptr == '\t');
479
480 if (!*field_end || field_end > header_end)
481 return httpHeaderReset(hdr); /* missing <CRLF> */
482
483 e = httpHeaderEntryParseCreate(field_start, field_end);
484
485 if (e != NULL)
486 httpHeaderAddEntry(hdr, e);
487 else
488 debug(55, 2) ("warning: ignoring unparseable http header field near '%s'\n",
489 getStringPrefix(field_start, field_end));
490
491 field_start = field_end;
492
493 /* skip CRLF */
494 if (*field_start == '\r')
495 field_start++;
496
497 if (*field_start == '\n')
498 field_start++;
499 }
500
501 return 1; /* even if no fields where found, it is a valid header */
502}
503
504/* packs all the entries using supplied packer */
505void
506httpHeaderPackInto(const HttpHeader * hdr, Packer * p)
507{
508 HttpHeaderPos pos = HttpHeaderInitPos;
509 const HttpHeaderEntry *e;
510 assert(hdr && p);
511 debug(55, 7) ("packing hdr: (%p)\n", hdr);
512 /* pack all entries one by one */
513
514 while ((e = httpHeaderGetEntry(hdr, &pos)))
515 httpHeaderEntryPackInto(e, p);
516}
517
518/* returns next valid entry */
519HttpHeaderEntry *
520httpHeaderGetEntry(const HttpHeader * hdr, HttpHeaderPos * pos)
521{
522 assert(hdr && pos);
523 assert(*pos >= HttpHeaderInitPos && *pos < (ssize_t)hdr->entries.count);
524
525 for ((*pos)++; *pos < (ssize_t)hdr->entries.count; (*pos)++) {
526 if (hdr->entries.items[*pos])
527 return (HttpHeaderEntry*)hdr->entries.items[*pos];
528 }
529
530 return NULL;
531}
532
533/*
534 * returns a pointer to a specified entry if any
535 * note that we return one entry so it does not make much sense to ask for
536 * "list" headers
537 */
538HttpHeaderEntry *
539httpHeaderFindEntry(const HttpHeader * hdr, http_hdr_type id)
540{
541 HttpHeaderPos pos = HttpHeaderInitPos;
542 HttpHeaderEntry *e;
543 assert(hdr);
544 assert_eid(id);
545 assert(!CBIT_TEST(ListHeadersMask, id));
546
547 /* check mask first */
548
549 if (!CBIT_TEST(hdr->mask, id))
550 return NULL;
551
552 /* looks like we must have it, do linear search */
553 while ((e = httpHeaderGetEntry(hdr, &pos))) {
554 if (e->id == id)
555 return e;
556 }
557
558 /* hm.. we thought it was there, but it was not found */
559 assert(0);
560
561 return NULL; /* not reached */
562}
563
564/*
565 * same as httpHeaderFindEntry
566 */
567static HttpHeaderEntry *
568httpHeaderFindLastEntry(const HttpHeader * hdr, http_hdr_type id)
569{
570 HttpHeaderPos pos = HttpHeaderInitPos;
571 HttpHeaderEntry *e;
572 HttpHeaderEntry *result = NULL;
573 assert(hdr);
574 assert_eid(id);
575 assert(!CBIT_TEST(ListHeadersMask, id));
576
577 /* check mask first */
578
579 if (!CBIT_TEST(hdr->mask, id))
580 return NULL;
581
582 /* looks like we must have it, do linear search */
583 while ((e = httpHeaderGetEntry(hdr, &pos))) {
584 if (e->id == id)
585 result = e;
586 }
587
588 assert(result); /* must be there! */
589 return result;
590}
591
592/*
593 * deletes all fields with a given name if any, returns #fields deleted;
594 */
595int
596httpHeaderDelByName(HttpHeader * hdr, const char *name)
597{
598 int count = 0;
599 HttpHeaderPos pos = HttpHeaderInitPos;
600 HttpHeaderEntry *e;
601 httpHeaderMaskInit(&hdr->mask, 0); /* temporal inconsistency */
602 debug(55, 7) ("deleting '%s' fields in hdr %p\n", name, hdr);
603
604 while ((e = httpHeaderGetEntry(hdr, &pos))) {
605 if (!e->name.caseCmp(name)) {
606 httpHeaderDelAt(hdr, pos);
607 count++;
608 } else
609 CBIT_SET(hdr->mask, e->id);
610 }
611
612 return count;
613}
614
615/* deletes all entries with a given id, returns the #entries deleted */
616int
617httpHeaderDelById(HttpHeader * hdr, http_hdr_type id)
618{
619 int count = 0;
620 HttpHeaderPos pos = HttpHeaderInitPos;
621 HttpHeaderEntry *e;
622 debug(55, 8) ("%p del-by-id %d\n", hdr, id);
623 assert(hdr);
624 assert_eid(id);
625 assert(id != HDR_OTHER); /* does not make sense */
626
627 if (!CBIT_TEST(hdr->mask, id))
628 return 0;
629
630 while ((e = httpHeaderGetEntry(hdr, &pos))) {
631 if (e->id == id) {
632 httpHeaderDelAt(hdr, pos);
633 count++;
634 }
635 }
636
637 CBIT_CLR(hdr->mask, id);
638 assert(count);
639 return count;
640}
641
642/*
643 * deletes an entry at pos and leaves a gap; leaving a gap makes it
644 * possible to iterate(search) and delete fields at the same time
645 */
646void
647httpHeaderDelAt(HttpHeader * hdr, HttpHeaderPos pos)
648{
649 HttpHeaderEntry *e;
650 assert(pos >= HttpHeaderInitPos && pos < (ssize_t)hdr->entries.count);
651 e = (HttpHeaderEntry*)hdr->entries.items[pos];
652 hdr->entries.items[pos] = NULL;
653 /* decrement header length, allow for ": " and crlf */
654 hdr->len -= e->name.size() + 2 + e->value.size() + 2;
655 assert(hdr->len >= 0);
656 httpHeaderEntryDestroy(e);
657}
658
659
660/* appends an entry;
661 * does not call httpHeaderEntryClone() so one should not reuse "*e"
662 */
663void
664httpHeaderAddEntry(HttpHeader * hdr, HttpHeaderEntry * e)
665{
666 assert(hdr && e);
667 assert_eid(e->id);
668
669 debug(55, 7) ("%p adding entry: %d at %d\n",
670 hdr, e->id, hdr->entries.count);
671
672 if (CBIT_TEST(hdr->mask, e->id))
673 Headers[e->id].stat.repCount++;
674 else
675 CBIT_SET(hdr->mask, e->id);
676
677 hdr->entries.push_back(e);
678
679 /* increment header length, allow for ": " and crlf */
680 hdr->len += e->name.size() + 2 + e->value.size() + 2;
681}
682
683/* return a list of entries with the same id separated by ',' and ws */
684String
685httpHeaderGetList(const HttpHeader * hdr, http_hdr_type id)
686{
687 HttpHeaderEntry *e;
688 HttpHeaderPos pos = HttpHeaderInitPos;
689 debug(55, 6) ("%p: joining for id %d\n", hdr, id);
690 /* only fields from ListHeaders array can be "listed" */
691 assert(CBIT_TEST(ListHeadersMask, id));
692
693 if (!CBIT_TEST(hdr->mask, id))
694 return String();
695
696 String s;
697
698 while ((e = httpHeaderGetEntry(hdr, &pos))) {
699 if (e->id == id)
700 strListAdd(&s, e->value.buf(), ',');
701 }
702
703 /*
704 * note: we might get an empty (len==0) string if there was an "empty"
705 * header; we must not get a NULL string though.
706 */
707 assert(s.buf());
708
709 /* temporary warning: remove it! @?@ @?@ @?@ */
710 if (!s.size())
711 debug(55, 3) ("empty list header: %s (%d)\n", Headers[id].name.buf(), id);
712
713 debug(55, 6) ("%p: joined for id %d: %s\n", hdr, id, s.buf());
714
715 return s;
716}
717
718/* return a string or list of entries with the same id separated by ',' and ws */
719String
720httpHeaderGetStrOrList(const HttpHeader * hdr, http_hdr_type id)
721{
722 HttpHeaderEntry *e;
723
724 if (CBIT_TEST(ListHeadersMask, id))
725 return httpHeaderGetList(hdr, id);
726
727 if ((e = httpHeaderFindEntry(hdr, id)))
728 return e->value;
729
730 return String();
731}
732
733/*
734 * Returns the value of the specified header.
735 */
736String
737httpHeaderGetByName(const HttpHeader * hdr, const char *name)
738{
739 http_hdr_type id;
740 HttpHeaderPos pos = HttpHeaderInitPos;
741 HttpHeaderEntry *e;
742
743 assert(hdr);
744 assert(name);
745
746 /* First try the quick path */
747 id = httpHeaderIdByNameDef(name, strlen(name));
748
749 if (id != -1)
750 return httpHeaderGetStrOrList(hdr, id);
751
752 String result;
753
754 /* Sorry, an unknown header name. Do linear search */
755 while ((e = httpHeaderGetEntry(hdr, &pos))) {
756 if (e->id == HDR_OTHER && e->name.caseCmp(name) == 0) {
757 strListAdd(&result, e->value.buf(), ',');
758 }
759 }
760
761 return result;
762}
763
764/*
765 * Returns a the value of the specified list member, if any.
766 */
767String
768httpHeaderGetByNameListMember(const HttpHeader * hdr, const char *name, const char *member, const char separator)
769{
770 String header;
771 const char *pos = NULL;
772 const char *item;
773 int ilen;
774 int mlen = strlen(member);
775
776 assert(hdr);
777 assert(name);
778
779 header = httpHeaderGetByName(hdr, name);
780
781 String result;
782
783 while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
784 if (strncmp(item, member, mlen) == 0 && item[mlen] == '=') {
785 result.append(item + mlen + 1, ilen - mlen - 1);
786 break;
787 }
788 }
789
790 return result;
791}
792
793/*
794 * returns a the value of the specified list member, if any.
795 */
796String
797httpHeaderGetListMember(const HttpHeader * hdr, http_hdr_type id, const char *member, const char separator)
798{
799 String header;
800 const char *pos = NULL;
801 const char *item;
802 int ilen;
803 int mlen = strlen(member);
804
805 assert(hdr);
806 assert(id >= 0);
807
808 header = httpHeaderGetStrOrList(hdr, id);
809 String result;
810
811 while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
812 if (strncmp(item, member, mlen) == 0 && item[mlen] == '=') {
813 result.append(item + mlen + 1, ilen - mlen - 1);
814 break;
815 }
816 }
817
818 header.clean();
819 return result;
820}
821
822/* test if a field is present */
823int
824httpHeaderHas(const HttpHeader * hdr, http_hdr_type id)
825{
826 assert(hdr);
827 assert_eid(id);
828 assert(id != HDR_OTHER);
829 debug(55, 7) ("%p lookup for %d\n", hdr, id);
830 return CBIT_TEST(hdr->mask, id);
831}
832
833void
834httpHeaderPutInt(HttpHeader * hdr, http_hdr_type id, int number)
835{
836 assert_eid(id);
837 assert(Headers[id].type == ftInt); /* must be of an appropriate type */
838 assert(number >= 0);
839 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(id, NULL, xitoa(number)));
840}
841
842void
843httpHeaderPutTime(HttpHeader * hdr, http_hdr_type id, time_t htime)
844{
845 assert_eid(id);
846 assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */
847 assert(htime >= 0);
848 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(id, NULL, mkrfc1123(htime)));
849}
850
851void
852httpHeaderPutStr(HttpHeader * hdr, http_hdr_type id, const char *str)
853{
854 assert_eid(id);
855 assert(Headers[id].type == ftStr); /* must be of an appropriate type */
856 assert(str);
857 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(id, NULL, str));
858}
859
860void
861httpHeaderPutAuth(HttpHeader * hdr, const char *auth_scheme, const char *realm)
862{
863 assert(hdr && auth_scheme && realm);
864 httpHeaderPutStrf(hdr, HDR_WWW_AUTHENTICATE, "%s realm=\"%s\"", auth_scheme, realm);
865}
866
867void
868httpHeaderPutCc(HttpHeader * hdr, const HttpHdrCc * cc)
869{
870 MemBuf mb;
871 Packer p;
872 assert(hdr && cc);
873 /* remove old directives if any */
874 httpHeaderDelById(hdr, HDR_CACHE_CONTROL);
875 /* pack into mb */
876 memBufDefInit(&mb);
877 packerToMemInit(&p, &mb);
878 httpHdrCcPackInto(cc, &p);
879 /* put */
880 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_CACHE_CONTROL, NULL, mb.buf));
881 /* cleanup */
882 packerClean(&p);
883 memBufClean(&mb);
884}
885
886void
887httpHeaderPutContRange(HttpHeader * hdr, const HttpHdrContRange * cr)
888{
889 MemBuf mb;
890 Packer p;
891 assert(hdr && cr);
892 /* remove old directives if any */
893 httpHeaderDelById(hdr, HDR_CONTENT_RANGE);
894 /* pack into mb */
895 memBufDefInit(&mb);
896 packerToMemInit(&p, &mb);
897 httpHdrContRangePackInto(cr, &p);
898 /* put */
899 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_CONTENT_RANGE, NULL, mb.buf));
900 /* cleanup */
901 packerClean(&p);
902 memBufClean(&mb);
903}
904
905void
906httpHeaderPutRange(HttpHeader * hdr, const HttpHdrRange * range)
907{
908 MemBuf mb;
909 Packer p;
910 assert(hdr && range);
911 /* remove old directives if any */
912 httpHeaderDelById(hdr, HDR_RANGE);
913 /* pack into mb */
914 memBufDefInit(&mb);
915 packerToMemInit(&p, &mb);
916 range->packInto(&p);
917 /* put */
918 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_RANGE, NULL, mb.buf));
919 /* cleanup */
920 packerClean(&p);
921 memBufClean(&mb);
922}
923
924void
925httpHeaderPutSc(HttpHeader *hdr, const HttpHdrSc *sc)
926{
927 MemBuf mb;
928 Packer p;
929 assert(hdr && sc);
930 /* remove old directives if any */
931 httpHeaderDelById(hdr, HDR_RANGE);
932 /* pack into mb */
933 memBufDefInit(&mb);
934 packerToMemInit(&p, &mb);
935 httpHdrScPackInto(sc, &p);
936 /* put */
937 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_SURROGATE_CONTROL, NULL, mb.buf));
938 /* cleanup */
939 packerClean(&p);
940 memBufClean(&mb);
941}
942
943/* add extension header (these fields are not parsed/analyzed/joined, etc.) */
944void
945httpHeaderPutExt(HttpHeader * hdr, const char *name, const char *value)
946{
947 assert(name && value);
948 debug(55, 8) ("%p adds ext entry '%s: %s'\n", hdr, name, value);
949 httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_OTHER, name, value));
950}
951
952int
953httpHeaderEntryGetInt (const HttpHeaderEntry * e)
954{
955 int value = -1;
956 int ok;
957 assert (e);
958 assert_eid (e->id);
959 assert (Headers[e->id].type == ftInt);
960 ok = httpHeaderParseInt(e->value.buf(), &value);
961 httpHeaderNoteParsedEntry(e->id, e->value, !ok);
962 /* XXX: Should we check ok - ie
963 * return ok ? -1 : value;
964 */
965 return value;
966}
967
968int
969httpHeaderGetInt(const HttpHeader * hdr, http_hdr_type id)
970{
971 assert_eid(id);
972 assert(Headers[id].type == ftInt); /* must be of an appropriate type */
973 HttpHeaderEntry *e;
974
975 if ((e = httpHeaderFindEntry(hdr, id)))
976 return httpHeaderEntryGetInt (e);
977
978 return -1;
979}
980
981time_t
982httpHeaderGetTime(const HttpHeader * hdr, http_hdr_type id)
983{
984 HttpHeaderEntry *e;
985 time_t value = -1;
986 assert_eid(id);
987 assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */
988
989 if ((e = httpHeaderFindEntry(hdr, id))) {
990 value = parse_rfc1123(e->value.buf());
991 httpHeaderNoteParsedEntry(e->id, e->value, value < 0);
992 }
993
994 return value;
995}
996
997/* sync with httpHeaderGetLastStr */
998const char *
999httpHeaderGetStr(const HttpHeader * hdr, http_hdr_type id)
1000{
1001 HttpHeaderEntry *e;
1002 assert_eid(id);
1003 assert(Headers[id].type == ftStr); /* must be of an appropriate type */
1004
1005 if ((e = httpHeaderFindEntry(hdr, id))) {
1006 httpHeaderNoteParsedEntry(e->id, e->value, 0); /* no errors are possible */
1007 return e->value.buf();
1008 }
1009
1010 return NULL;
1011}
1012
1013/* unusual */
1014const char *
1015httpHeaderGetLastStr(const HttpHeader * hdr, http_hdr_type id)
1016{
1017 HttpHeaderEntry *e;
1018 assert_eid(id);
1019 assert(Headers[id].type == ftStr); /* must be of an appropriate type */
1020
1021 if ((e = httpHeaderFindLastEntry(hdr, id))) {
1022 httpHeaderNoteParsedEntry(e->id, e->value, 0); /* no errors are possible */
1023 return e->value.buf();
1024 }
1025
1026 return NULL;
1027}
1028
1029HttpHdrCc *
1030httpHeaderGetCc(const HttpHeader * hdr)
1031{
1032 HttpHdrCc *cc;
1033 String s;
1034
1035 if (!CBIT_TEST(hdr->mask, HDR_CACHE_CONTROL))
1036 return NULL;
1037
1038 s = httpHeaderGetList(hdr, HDR_CACHE_CONTROL);
1039
1040 cc = httpHdrCcParseCreate(&s);
1041
1042 HttpHeaderStats[hdr->owner].ccParsedCount++;
1043
1044 if (cc)
1045 httpHdrCcUpdateStats(cc, &HttpHeaderStats[hdr->owner].ccTypeDistr);
1046
1047 httpHeaderNoteParsedEntry(HDR_CACHE_CONTROL, s, !cc);
1048
1049 s.clean();
1050
1051 return cc;
1052}
1053
1054HttpHdrRange *
1055httpHeaderGetRange(const HttpHeader * hdr)
1056{
1057 HttpHdrRange *r = NULL;
1058 HttpHeaderEntry *e;
1059 /* some clients will send "Request-Range" _and_ *matching* "Range"
1060 * who knows, some clients might send Request-Range only;
1061 * this "if" should work correctly in both cases;
1062 * hopefully no clients send mismatched headers! */
1063
1064 if ((e = httpHeaderFindEntry(hdr, HDR_RANGE)) ||
1065 (e = httpHeaderFindEntry(hdr, HDR_REQUEST_RANGE))) {
1066 r = HttpHdrRange::ParseCreate(&e->value);
1067 httpHeaderNoteParsedEntry(e->id, e->value, !r);
1068 }
1069
1070 return r;
1071}
1072
1073HttpHdrSc *
1074httpHeaderGetSc(const HttpHeader *hdr)
1075{
1076 if (!CBIT_TEST(hdr->mask, HDR_SURROGATE_CONTROL))
1077 return NULL;
1078
1079 String s (httpHeaderGetList(hdr, HDR_SURROGATE_CONTROL));
1080
1081 HttpHdrSc *sc = httpHdrScParseCreate(&s);
1082
1083 HttpHeaderStats[hdr->owner].ccParsedCount++;
1084
1085 if (sc)
1086 httpHdrScUpdateStats(sc, &HttpHeaderStats[hdr->owner].scTypeDistr);
1087
1088 httpHeaderNoteParsedEntry(HDR_SURROGATE_CONTROL, s, !sc);
1089
1090 return sc;
1091}
1092
1093HttpHdrContRange *
1094httpHeaderGetContRange(const HttpHeader * hdr)
1095{
1096 HttpHdrContRange *cr = NULL;
1097 HttpHeaderEntry *e;
1098
1099 if ((e = httpHeaderFindEntry(hdr, HDR_CONTENT_RANGE))) {
1100 cr = httpHdrContRangeParseCreate(e->value.buf());
1101 httpHeaderNoteParsedEntry(e->id, e->value, !cr);
1102 }
1103
1104 return cr;
1105}
1106
1107const char *
1108httpHeaderGetAuth(const HttpHeader * hdr, http_hdr_type id, const char *auth_scheme)
1109{
1110 const char *field;
1111 int l;
1112 assert(hdr && auth_scheme);
1113 field = httpHeaderGetStr(hdr, id);
1114
1115 if (!field) /* no authorization field */
1116 return NULL;
1117
1118 l = strlen(auth_scheme);
1119
1120 if (!l || strncasecmp(field, auth_scheme, l)) /* wrong scheme */
1121 return NULL;
1122
1123 field += l;
1124
1125 if (!xisspace(*field)) /* wrong scheme */
1126 return NULL;
1127
1128 /* skip white space */
1129 field += xcountws(field);
1130
1131 if (!*field) /* no authorization cookie */
1132 return NULL;
1133
1134 return base64_decode(field);
1135}
1136
1137ETag
1138httpHeaderGetETag(const HttpHeader * hdr, http_hdr_type id)
1139{
1140 ETag etag =
1141 {NULL, -1};
1142 HttpHeaderEntry *e;
1143 assert(Headers[id].type == ftETag); /* must be of an appropriate type */
1144
1145 if ((e = httpHeaderFindEntry(hdr, id)))
1146 etagParseInit(&etag, e->value.buf());
1147
1148 return etag;
1149}
1150
1151TimeOrTag
1152httpHeaderGetTimeOrTag(const HttpHeader * hdr, http_hdr_type id)
1153{
1154 TimeOrTag tot;
1155 HttpHeaderEntry *e;
1156 assert(Headers[id].type == ftDate_1123_or_ETag); /* must be of an appropriate type */
1157 memset(&tot, 0, sizeof(tot));
1158
1159 if ((e = httpHeaderFindEntry(hdr, id))) {
1160 const char *str = e->value.buf();
1161 /* try as an ETag */
1162
1163 if (etagParseInit(&tot.tag, str)) {
1164 tot.valid = tot.tag.str != NULL;
1165 tot.time = -1;
1166 } else {
1167 /* or maybe it is time? */
1168 tot.time = parse_rfc1123(str);
1169 tot.valid = tot.time >= 0;
1170 tot.tag.str = NULL;
1171 }
1172 }
1173
1174 assert(tot.time < 0 || !tot.tag.str); /* paranoid */
1175 return tot;
1176}
1177
1178/*
1179 * HttpHeaderEntry
1180 */
1181
1182static HttpHeaderEntry *
1183httpHeaderEntryCreate(http_hdr_type id, const char *name, const char *value)
1184{
1185 HttpHeaderEntry *e;
1186 assert_eid(id);
1187 e = new HttpHeaderEntry;
1188 e->id = id;
1189
1190 if (id != HDR_OTHER)
1191 e->name = Headers[id].name;
1192 else
1193 e->name = name;
1194
1195 e->value = value;
1196
1197 Headers[id].stat.aliveCount++;
1198
1199 debug(55, 9) ("created entry %p: '%s: %s'\n", e, e->name.buf(), e->value.buf());
1200
1201 return e;
1202}
1203
1204static void
1205httpHeaderEntryDestroy(HttpHeaderEntry * e)
1206{
1207 assert(e);
1208 assert_eid(e->id);
1209 debug(55, 9) ("destroying entry %p: '%s: %s'\n", e, e->name.buf(), e->value.buf());
1210 /* clean name if needed */
1211
1212 if (e->id == HDR_OTHER)
1213 e->name.clean();
1214
1215 e->value.clean();
1216
1217 assert(Headers[e->id].stat.aliveCount);
1218
1219 Headers[e->id].stat.aliveCount--;
1220
1221 e->id = HDR_BAD_HDR;
1222
1223 delete e;
1224}
1225
1226/* parses and inits header entry, returns new entry on success */
1227static HttpHeaderEntry *
1228httpHeaderEntryParseCreate(const char *field_start, const char *field_end)
1229{
1230 HttpHeaderEntry *e;
1231 http_hdr_type id;
1232 /* note: name_start == field_start */
1233 const char *name_end = strchr(field_start, ':');
1234 const int name_len = name_end ? name_end - field_start : 0;
1235 const char *value_start = field_start + name_len + 1; /* skip ':' */
1236 /* note: value_end == field_end */
1237
1238 HeaderEntryParsedCount++;
1239
1240 /* do we have a valid field name within this field? */
1241
1242 if (!name_len || name_end > field_end)
1243 return NULL;
1244
1245 if (name_len > 65536) {
1246 /* String has a 64K limit */
1247 debug(55, 1) ("WARNING: ignoring header name of %d bytes\n", name_len);
1248 return NULL;
1249 }
1250
1251 /* now we know we can parse it */
1252 e = new HttpHeaderEntry;
1253
1254 debug(55, 9) ("creating entry %p: near '%s'\n", e, getStringPrefix(field_start, field_end));
1255
1256 /* is it a "known" field? */
1257 id = httpHeaderIdByName(field_start, name_len, Headers, HDR_ENUM_END);
1258
1259 if (id < 0)
1260 id = HDR_OTHER;
1261
1262 assert_eid(id);
1263
1264 e->id = id;
1265
1266 /* set field name */
1267 if (id == HDR_OTHER)
1268 e->name.limitInit(field_start, name_len);
1269 else
1270 e->name = Headers[id].name;
1271
1272 /* trim field value */
1273 while (value_start < field_end && xisspace(*value_start))
1274 value_start++;
1275
1276 if (field_end - value_start > 65536) {
1277 /* String has a 64K limit */
1278 debug(55, 1) ("WARNING: ignoring '%s' header of %d bytes\n",
1279 e->name.buf(), (int) (field_end - value_start));
1280
1281 if (e->id == HDR_OTHER)
1282 e->name.clean();
1283
1284 delete e;
1285
1286 return NULL;
1287 }
1288
1289 /* set field value */
1290 e->value.limitInit(value_start, field_end - value_start);
1291
1292 Headers[id].stat.seenCount++;
1293
1294 Headers[id].stat.aliveCount++;
1295
1296 debug(55, 9) ("created entry %p: '%s: %s'\n", e, e->name.buf(), e->value.buf());
1297
1298 return e;
1299}
1300
1301HttpHeaderEntry *
1302httpHeaderEntryClone(const HttpHeaderEntry * e)
1303{
1304 return httpHeaderEntryCreate(e->id, e->name.buf(), e->value.buf());
1305}
1306
1307void
1308httpHeaderEntryPackInto(const HttpHeaderEntry * e, Packer * p)
1309{
1310 assert(e && p);
1311 packerAppend(p, e->name.buf(), e->name.size());
1312 packerAppend(p, ": ", 2);
1313 packerAppend(p, e->value.buf(), e->value.size());
1314 packerAppend(p, "\r\n", 2);
1315}
1316
1317static void
1318httpHeaderNoteParsedEntry(http_hdr_type id, String context, int error)
1319{
1320 Headers[id].stat.parsCount++;
1321
1322 if (error) {
1323 Headers[id].stat.errCount++;
1324 debug(55, 2) ("cannot parse hdr field: '%s: %s'\n",
1325 Headers[id].name.buf(), context.buf());
1326 }
1327}
1328
1329/*
1330 * Reports
1331 */
1332
1333/* tmp variable used to pass stat info to dumpers */
1334extern const HttpHeaderStat *dump_stat; /* argh! */
1335const HttpHeaderStat *dump_stat = NULL;
1336
1337static void
1338httpHeaderFieldStatDumper(StoreEntry * sentry, int idx, double val, double size, int count)
1339{
1340 const int id = (int) val;
1341 const int valid_id = id >= 0 && id < HDR_ENUM_END;
1342 const char *name = valid_id ? Headers[id].name.buf() : "INVALID";
1343 int visible = count > 0;
1344 /* for entries with zero count, list only those that belong to current type of message */
1345
1346 if (!visible && valid_id && dump_stat->owner_mask)
1347 visible = CBIT_TEST(*dump_stat->owner_mask, id);
1348
1349 if (visible)
1350 storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
1351 id, name, count, xdiv(count, dump_stat->busyDestroyedCount));
1352}
1353
1354static void
1355httpHeaderFldsPerHdrDumper(StoreEntry * sentry, int idx, double val, double size, int count)
1356{
1357 if (count)
1358 storeAppendPrintf(sentry, "%2d\t %5d\t %5d\t %6.2f\n",
1359 idx, (int) val, count,
1360 xpercent(count, dump_stat->destroyedCount));
1361}
1362
1363
1364static void
1365httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e)
1366{
1367 assert(hs && e);
1368
1369 dump_stat = hs;
1370 storeAppendPrintf(e, "\nHeader Stats: %s\n", hs->label);
1371 storeAppendPrintf(e, "\nField type distribution\n");
1372 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n",
1373 "id", "name", "count", "#/header");
1374 statHistDump(&hs->fieldTypeDistr, e, httpHeaderFieldStatDumper);
1375 storeAppendPrintf(e, "\nCache-control directives distribution\n");
1376 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n",
1377 "id", "name", "count", "#/cc_field");
1378 statHistDump(&hs->ccTypeDistr, e, httpHdrCcStatDumper);
1379 storeAppendPrintf(e, "\nSurrogate-control directives distribution\n");
1380 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n",
1381 "id", "name", "count", "#/sc_field");
1382 statHistDump(&hs->scTypeDistr, e, httpHdrScStatDumper);
1383 storeAppendPrintf(e, "\nNumber of fields per header distribution\n");
1384 storeAppendPrintf(e, "%2s\t %-5s\t %5s\t %6s\n",
1385 "id", "#flds", "count", "%total");
1386 statHistDump(&hs->hdrUCountDistr, e, httpHeaderFldsPerHdrDumper);
1387 dump_stat = NULL;
1388}
1389
1390void
1391httpHeaderStoreReport(StoreEntry * e)
1392{
1393 int i;
1394 http_hdr_type ht;
1395 assert(e);
1396
1397 HttpHeaderStats[0].parsedCount =
1398 HttpHeaderStats[hoRequest].parsedCount + HttpHeaderStats[hoReply].parsedCount;
1399 HttpHeaderStats[0].ccParsedCount =
1400 HttpHeaderStats[hoRequest].ccParsedCount + HttpHeaderStats[hoReply].ccParsedCount;
1401 HttpHeaderStats[0].destroyedCount =
1402 HttpHeaderStats[hoRequest].destroyedCount + HttpHeaderStats[hoReply].destroyedCount;
1403 HttpHeaderStats[0].busyDestroyedCount =
1404 HttpHeaderStats[hoRequest].busyDestroyedCount + HttpHeaderStats[hoReply].busyDestroyedCount;
1405
1406 for (i = 1; i < HttpHeaderStatCount; i++) {
1407 httpHeaderStatDump(HttpHeaderStats + i, e);
1408 storeAppendPrintf(e, "%s\n", "<br>");
1409 }
1410
1411 /* field stats for all messages */
1412 storeAppendPrintf(e, "\nHttp Fields Stats (replies and requests)\n");
1413
1414 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\t %6s\n",
1415 "id", "name", "#alive", "%err", "%repeat");
1416
1417 for (ht = (http_hdr_type)0; ht < HDR_ENUM_END; ++ht) {
1418 HttpHeaderFieldInfo *f = Headers + ht;
1419 storeAppendPrintf(e, "%2d\t %-20s\t %5d\t %6.3f\t %6.3f\n",
1420 f->id, f->name.buf(), f->stat.aliveCount,
1421 xpercent(f->stat.errCount, f->stat.parsCount),
1422 xpercent(f->stat.repCount, f->stat.seenCount));
1423 }
1424
1425 storeAppendPrintf(e, "Headers Parsed: %d + %d = %d\n",
1426 HttpHeaderStats[hoRequest].parsedCount,
1427 HttpHeaderStats[hoReply].parsedCount,
1428 HttpHeaderStats[0].parsedCount);
1429 storeAppendPrintf(e, "Hdr Fields Parsed: %d\n", HeaderEntryParsedCount);
1430}
1431
1432http_hdr_type
1433httpHeaderIdByName(const char *name, int name_len, const HttpHeaderFieldInfo * info, int end)
1434{
1435 int i;
1436
1437 for (i = 0; i < end; ++i) {
1438 if (name_len >= 0 && name_len != info[i].name.size())
1439 continue;
1440
1441 if (!strncasecmp(name, info[i].name.buf(),
1442 name_len < 0 ? info[i].name.size() + 1 : name_len))
1443 return info[i].id;
1444 }
1445
1446 return HDR_BAD_HDR;
1447}
1448
1449http_hdr_type
1450httpHeaderIdByNameDef(const char *name, int name_len)
1451{
1452 if (!Headers)
1453 Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END);
1454
1455 return httpHeaderIdByName(name, name_len, Headers, HDR_ENUM_END);
1456}
1457
1458const char *
1459httpHeaderNameById(int id)
1460{
1461 if (!Headers)
1462 Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END);
1463
1464 assert(id >= 0 && id < HDR_ENUM_END);
1465
1466 return HeadersAttrs[id].name;
1467}
1468
1469MemPool (*HttpHeaderEntry::Pool)(NULL);
1470void *
1471HttpHeaderEntry::operator new (size_t byteCount)
1472{
1473 /* derived classes with different sizes must implement their own new */
1474 assert (byteCount == sizeof (HttpHeaderEntry));
1475
1476 if (!Pool)
1477 Pool = memPoolCreate("HttpHeaderEntry", sizeof (HttpHeaderEntry));
1478
1479 return memPoolAlloc(Pool);
1480}
1481
1482void
1483HttpHeaderEntry::operator delete (void *address)
1484{
1485 memPoolFree (Pool, address);
1486}
1487
1488int
1489httpHeaderHasListMember(const HttpHeader * hdr, http_hdr_type id, const char *member, const char separator)
1490{
1491 int result = 0;
1492 const char *pos = NULL;
1493 const char *item;
1494 int ilen;
1495 int mlen = strlen(member);
1496
1497 assert(hdr);
1498 assert(id >= 0);
1499
1500 String header (httpHeaderGetStrOrList(hdr, id));
1501
1502 while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
1503 if (strncmp(item, member, mlen) == 0
1504 && (item[mlen] == '=' || item[mlen] == separator || item[mlen] == ';' || item[mlen] == '\0')) {
1505 result = 1;
1506 break;
1507 }
1508 }
1509
1510 return result;
1511}
1512
1513void
1514HttpHeader::removeConnectionHeaderEntries()
1515{
1516 if (httpHeaderHas(this, HDR_CONNECTION)) {
1517 /* anything that matches Connection list member will be deleted */
1518 String strConnection = httpHeaderGetList(this, HDR_CONNECTION);
1519 const HttpHeaderEntry *e;
1520 HttpHeaderPos pos = HttpHeaderInitPos;
1521 /*
1522 * think: on-average-best nesting of the two loops (hdrEntry
1523 * and strListItem) @?@
1524 */
1525 /*
1526 * maybe we should delete standard stuff ("keep-alive","close")
1527 * from strConnection first?
1528 */
1529
1530 while ((e = httpHeaderGetEntry(this, &pos))) {
1531 if (strListIsMember(&strConnection, e->name.buf(), ','))
1532 httpHeaderDelAt(this, pos);
1533 }
1534
1535 httpHeaderDelById(this, HDR_CONNECTION);
1536 strConnection.clean();
1537 }
1538}