]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpHeader.cc
- Changed %t to %T.
[thirdparty/squid.git] / src / HttpHeader.cc
CommitLineData
cb69b4c7 1/*
b5107edb 2 * $Id: HttpHeader.cc,v 1.17 1998/03/05 20:55:56 rousskov Exp $
cb69b4c7 3 *
123abbe1 4 * DEBUG: section 55 HTTP Header
cb69b4c7 5 * AUTHOR: Alex Rousskov
6 *
7 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
8 * --------------------------------------------------------
9 *
10 * Squid is the result of efforts by numerous individuals from the
11 * Internet community. Development is led by Duane Wessels of the
12 * National Laboratory for Applied Network Research and funded by
13 * the National Science Foundation.
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 *
29 */
30
31#include "squid.h"
cb69b4c7 32
33/*
2ac76861 34 * On naming conventions:
35 *
36 * HTTP/1.1 defines message-header as
37 *
38 * message-header = field-name ":" [ field-value ] CRLF
39 * field-name = token
40 * field-value = *( field-content | LWS )
41 *
42 * HTTP/1.1 does not give a name name a group of all message-headers in a message.
43 * Squid 1.1 seems to refer to that group _plus_ start-line as "headers".
44 *
45 * HttpHeader is an object that represents all message-headers in a message.
46 * HttpHeader does not manage start-line.
47 *
48 * HttpHeader is implemented as a collection of header "entries"
49 * An entry is a (field_id, field) pair where
50 * - field_id is one of the http_hdr_type ids,
51 * - field is a compiled(parsed) image of message-header.
52 */
cb69b4c7 53
54
55/*
56 * local types
57 */
58
59/* HTTP/1.1 extension-header */
60struct _HttpHeaderExtField {
2ac76861 61 char *name; /* field-name from HTTP/1.1 (no column after name!) */
62 char *value; /* field-value from HTTP/1.1 */
cb69b4c7 63};
64
cb69b4c7 65/*
66 * HttpHeader entry
67 * ( the concrete type of entry.field is Headers[id].type )
68 */
69struct _HttpHeaderEntry {
70 field_store field;
71 http_hdr_type id;
72};
73
12cf1be2 74
12cf1be2 75/* per header statistics */
76typedef struct {
77 const char *label;
78 StatHist hdrUCountDistr;
79 StatHist fieldTypeDistr;
80 StatHist ccTypeDistr;
81} HttpHeaderStat;
82
83
cb69b4c7 84/* use HttpHeaderPos as opaque type, do not interpret */
85typedef ssize_t HttpHeaderPos;
86/* use this and only this to initialize HttpHeaderPos */
87#define HttpHeaderInitPos (-1)
88
89
cb69b4c7 90/*
91 * local constants and vars
92 */
93
94/*
95 * A table with major attributes for every known field.
96 * We calculate name lengths and reorganize this array on start up.
97 * After reorganization, field id can be used as an index to the table.
98 */
2ac76861 99static field_attrs_t Headers[] =
100{
101 {"Accept", HDR_ACCEPT, ftPChar},
102 {"Age", HDR_AGE, ftInt},
103 {"Cache-Control", HDR_CACHE_CONTROL, ftPSCC},
104 {"Connection", HDR_CONNECTION, ftPChar}, /* for now */
105 {"Content-Encoding", HDR_CONTENT_ENCODING, ftPChar},
106 {"Content-Length", HDR_CONTENT_LENGTH, ftInt},
107 {"Content-MD5", HDR_CONTENT_MD5, ftPChar}, /* for now */
108 {"Content-Type", HDR_CONTENT_TYPE, ftPChar},
109 {"Date", HDR_DATE, ftDate_1123},
110 {"Etag", HDR_ETAG, ftPChar}, /* for now */
111 {"Expires", HDR_EXPIRES, ftDate_1123},
112 {"Host", HDR_HOST, ftPChar},
113 {"If-Modified-Since", HDR_IMS, ftDate_1123},
114 {"Last-Modified", HDR_LAST_MODIFIED, ftDate_1123},
115 {"Location", HDR_LOCATION, ftPChar},
116 {"Max-Forwards", HDR_MAX_FORWARDS, ftInt},
117 {"Proxy-Authenticate", HDR_PROXY_AUTHENTICATE, ftPChar},
118 {"Public", HDR_PUBLIC, ftPChar},
119 {"Retry-After", HDR_RETRY_AFTER, ftPChar}, /* for now */
cb69b4c7 120 /* fix this: make count-but-treat as OTHER mask @?@ @?@ */
2ac76861 121 {"Set-Cookie:", HDR_SET_COOKIE, ftPChar},
122 {"Upgrade", HDR_UPGRADE, ftPChar}, /* for now */
123 {"Warning", HDR_WARNING, ftPChar}, /* for now */
124 {"WWW-Authenticate", HDR_WWW_AUTHENTICATE, ftPChar},
125 {"Proxy-Connection", HDR_PROXY_KEEPALIVE, ftInt}, /* true/false */
126 {"Other:", HDR_OTHER, ftPExtField} /* ':' will not allow matches */
cb69b4c7 127};
128
cb69b4c7 129/*
130 * headers with field values defined as #(values) in HTTP/1.1
131 *
132 * We have listed all possible list headers according to
133 * draft-ietf-http-v11-spec-rev-01.txt. Headers that are currently not
134 * recognized, are commented out.
135 */
2ac76861 136static int ListHeadersMask = 0; /* set run-time using ListHeaders */
137static http_hdr_type ListHeaders[] =
138{
139 HDR_ACCEPT,
cb69b4c7 140 /* HDR_ACCEPT_CHARSET, HDR_ACCEPT_ENCODING, HDR_ACCEPT_LANGUAGE, */
141 /* HDR_ACCEPT_RANGES, */
142 /* HDR_ALLOW, */
143 HDR_CACHE_CONTROL, HDR_CONNECTION,
2ac76861 144 HDR_CONTENT_ENCODING,
cb69b4c7 145 /* HDR_CONTENT_LANGUAGE, HDR_IF_MATCH, HDR_IF_NONE_MATCH,
2ac76861 146 * HDR_PRAGMA, HDR_TRANSFER_ENCODING, */
147 HDR_UPGRADE, /* HDR_VARY, */
cb69b4c7 148 /* HDR_VIA, HDR_WARNING, */
2ac76861 149 HDR_WWW_AUTHENTICATE,
cb69b4c7 150 /* HDR_EXPECT, HDR_TE, HDR_TRAILER */
151};
152
2ac76861 153static int ReplyHeadersMask = 0; /* set run-time using ReplyHeaders */
154static http_hdr_type ReplyHeaders[] =
155{
cb69b4c7 156 HDR_ACCEPT, HDR_AGE, HDR_CACHE_CONTROL, HDR_CONTENT_LENGTH,
2ac76861 157 HDR_CONTENT_MD5, HDR_CONTENT_TYPE, HDR_DATE, HDR_ETAG, HDR_EXPIRES,
cb69b4c7 158 HDR_LAST_MODIFIED, HDR_LOCATION, HDR_MAX_FORWARDS, HDR_PUBLIC, HDR_RETRY_AFTER,
159 HDR_SET_COOKIE, HDR_UPGRADE, HDR_WARNING, HDR_PROXY_KEEPALIVE, HDR_OTHER
160};
161
2ac76861 162static int RequestHeadersMask = 0; /* set run-time using RequestHeaders */
163static http_hdr_type RequestHeaders[] =
164{
cb69b4c7 165 HDR_OTHER
166};
167
12cf1be2 168/* when first field is added, this is how much entries we allocate */
cb69b4c7 169#define INIT_FIELDS_PER_HEADER 8
cb69b4c7 170
12cf1be2 171/* recycle bin for short strings (32KB total only) */
2ac76861 172static const size_t shortStrSize = 32; /* max size of a recyclable string */
173static const size_t shortStrPoolCount = (32 * 1024) / 32; /* sync this with shortStrSize */
cb69b4c7 174static MemPool *shortStrings = NULL;
175
12cf1be2 176/* header accounting */
2ac76861 177static HttpHeaderStat HttpHeaderStats[] =
178{
179 {"reply"},
180 {"request"},
181 {"all"}
12cf1be2 182};
2ac76861 183static int HttpHeaderStatCount = sizeof(HttpHeaderStats) / sizeof(*HttpHeaderStats);
12cf1be2 184
185/* global counters */
7faf2bdb 186static int HeaderParsedCount = 0;
187static int HeaderEntryParsedCount = 0;
12cf1be2 188
cb69b4c7 189/* long strings accounting */
7faf2bdb 190static int longStrAliveCount = 0;
191static int longStrHighWaterCount = 0;
192static size_t longStrAliveSize = 0;
193static size_t longStrHighWaterSize = 0;
cb69b4c7 194
195
12cf1be2 196/*
197 * local routines
198 */
cb69b4c7 199
200#define assert_eid(id) assert((id) >= 0 && (id) < HDR_ENUM_END)
201
2ac76861 202static HttpHeaderEntry *httpHeaderGetEntry(const HttpHeader * hdr, HttpHeaderPos * pos);
203static void httpHeaderDelAt(HttpHeader * hdr, HttpHeaderPos pos);
204static void httpHeaderAddParsedEntry(HttpHeader * hdr, HttpHeaderEntry * e);
205static void httpHeaderAddNewEntry(HttpHeader * hdr, const HttpHeaderEntry * e);
7c525cc2 206static field_store httpHeaderGet(const HttpHeader * hdr, http_hdr_type id);
2ac76861 207static void httpHeaderSet(HttpHeader * hdr, http_hdr_type id, const field_store value);
208static void httpHeaderSyncMasks(HttpHeader * hdr, const HttpHeaderEntry * e, int add);
2ac76861 209static void httpHeaderGrow(HttpHeader * hdr);
210
211static void httpHeaderEntryInit(HttpHeaderEntry * e, http_hdr_type id, field_store field);
212static void httpHeaderEntryClean(HttpHeaderEntry * e);
213static int httpHeaderEntryParseInit(HttpHeaderEntry * e, const char *field_start, const char *field_end, int mask);
214static int httpHeaderEntryParseExtFieldInit(HttpHeaderEntry * e, int id, const HttpHeaderExtField * f);
215static int httpHeaderEntryParseByTypeInit(HttpHeaderEntry * e, int id, const HttpHeaderExtField * f);
216static HttpHeaderEntry httpHeaderEntryClone(const HttpHeaderEntry * e);
217static void httpHeaderEntryPackInto(const HttpHeaderEntry * e, Packer * p);
218static void httpHeaderEntryPackByType(const HttpHeaderEntry * e, Packer * p);
219static void httpHeaderEntryJoinWith(HttpHeaderEntry * e, const HttpHeaderEntry * newe);
220static int httpHeaderEntryIsValid(const HttpHeaderEntry * e);
221static const char *httpHeaderEntryName(const HttpHeaderEntry * e);
222
223static void httpHeaderFieldInit(field_store * field);
cb69b4c7 224static field_store httpHeaderFieldDup(field_type type, field_store value);
225static field_store httpHeaderFieldBadValue(field_type type);
226
7faf2bdb 227#if 0
cb69b4c7 228static HttpScc *httpSccCreate();
229static HttpScc *httpSccParseCreate(const char *str);
2ac76861 230static void httpSccParseInit(HttpScc * scc, const char *str);
231static void httpSccDestroy(HttpScc * scc);
232static HttpScc *httpSccDup(HttpScc * scc);
233static void httpSccUpdateStats(const HttpScc * scc, StatHist * hist);
2ac76861 234static void httpSccPackValueInto(HttpScc * scc, Packer * p);
235static void httpSccJoinWith(HttpScc * scc, HttpScc * new_scc);
7faf2bdb 236#endif
cb69b4c7 237
238static HttpHeaderExtField *httpHeaderExtFieldCreate(const char *name, const char *value);
239static HttpHeaderExtField *httpHeaderExtFieldParseCreate(const char *field_start, const char *field_end);
2ac76861 240static void httpHeaderExtFieldDestroy(HttpHeaderExtField * f);
241static HttpHeaderExtField *httpHeaderExtFieldDup(HttpHeaderExtField * f);
cb69b4c7 242
7faf2bdb 243static void httpHeaderStatInit(HttpHeaderStat * hs, const char *label);
2ac76861 244static void httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e);
245static void shortStringStatDump(StoreEntry * e);
cb69b4c7 246
247static char *dupShortStr(const char *str);
248static char *dupShortBuf(const char *str, size_t len);
249static char *appShortStr(char *str, const char *app_str);
250static char *allocShortBuf(size_t size);
251static void freeShortString(char *str);
252
7faf2bdb 253#if 0
cb69b4c7 254static int strListGetItem(const char *str, char del, const char **item, int *ilen, const char **pos);
255static const char *getStringPrefix(const char *str);
7faf2bdb 256#endif
cb69b4c7 257
7c525cc2 258
cb69b4c7 259/* handy to determine the #elements in a static array */
260#define countof(arr) (sizeof(arr)/sizeof(*arr))
261
7021844c 262/*
263 * some compilers do not want to convert a type into a union which that type
264 * belongs to
265 */
399e85ea 266field_store
267intField(int n)
268{
269 field_store f;
270 f.v_int = n;
271 return f;
272}
273field_store
274timeField(time_t t)
275{
276 field_store f;
277 f.v_time = t;
278 return f;
279}
280field_store
281ptrField(void *p)
282{
283 field_store f;
284 f.v_pchar = (char *) p;
285 return f;
286}
7021844c 287
cb69b4c7 288/*
289 * Module initialization routines
290 */
291
292void
293httpHeaderInitModule()
294{
12cf1be2 295 int i;
cb69b4c7 296 /* paranoid check if smbd put a big object into field_store */
2ac76861 297 assert(sizeof(field_store) == sizeof(char *));
cb69b4c7 298 /* have to force removal of const here */
2ac76861 299 httpHeaderInitAttrTable((field_attrs_t *) Headers, countof(Headers));
cb69b4c7 300 /* create masks */
2ac76861 301 ListHeadersMask = httpHeaderCalcMask((const int *) ListHeaders, countof(ListHeaders));
302 ReplyHeadersMask = httpHeaderCalcMask((const int *) ReplyHeaders, countof(ReplyHeaders));
303 RequestHeadersMask = httpHeaderCalcMask((const int *) RequestHeaders, countof(RequestHeaders));
cb69b4c7 304 /* create a pool of short strings @?@ we never destroy it! */
7021844c 305 shortStrings = memPoolCreate("'short http hdr strs'", shortStrSize);
12cf1be2 306 /* init header stats */
307 for (i = 0; i < HttpHeaderStatCount; i++)
2ac76861 308 httpHeaderStatInit(HttpHeaderStats + i, HttpHeaderStats[i].label);
7faf2bdb 309 httpHdrCcInitModule();
12cf1be2 310 cachemgrRegister("http_headers",
311 "HTTP Header Statistics", httpHeaderStoreReport, 0);
cb69b4c7 312}
313
7021844c 314void
315httpHeaderCleanModule()
316{
317 if (shortStrings) {
318 memPoolDestroy(shortStrings);
319 shortStrings = NULL;
320 }
321}
322
12cf1be2 323static void
2ac76861 324httpHeaderStatInit(HttpHeaderStat * hs, const char *label)
12cf1be2 325{
326 assert(hs);
327 assert(label);
328 hs->label = label;
2ac76861 329 statHistEnumInit(&hs->hdrUCountDistr, 32); /* not a real enum */
12cf1be2 330 statHistEnumInit(&hs->fieldTypeDistr, HDR_ENUM_END);
7faf2bdb 331 statHistEnumInit(&hs->ccTypeDistr, CC_ENUM_END);
cb69b4c7 332}
333
cb69b4c7 334/*
335 * HttpHeader Implementation
336 */
337
338
339HttpHeader *
340httpHeaderCreate()
341{
342 HttpHeader *hdr = xmalloc(sizeof(HttpHeader));
343 httpHeaderInit(hdr);
344 return hdr;
345}
346
347
348/* "create" for non-alloc objects; also used by real Create */
349void
2ac76861 350httpHeaderInit(HttpHeader * hdr)
cb69b4c7 351{
352 assert(hdr);
353 memset(hdr, 0, sizeof(*hdr));
cb69b4c7 354}
355
356void
2ac76861 357httpHeaderClean(HttpHeader * hdr)
cb69b4c7 358{
359 HttpHeaderPos pos = HttpHeaderInitPos;
12cf1be2 360 HttpHeaderEntry *e;
cb69b4c7 361
8d2e9b4a 362 debug(55, 7) ("cleaning hdr: %p\n", hdr);
cb69b4c7 363 assert(hdr);
364
12cf1be2 365 statHistCount(&HttpHeaderStats[0].hdrUCountDistr, hdr->ucount);
366 while ((e = httpHeaderGetEntry(hdr, &pos))) {
367 /* fix this (for scc too) for req headers @?@ */
368 statHistCount(&HttpHeaderStats[0].fieldTypeDistr, e->id);
369 if (e->id == HDR_CACHE_CONTROL)
7faf2bdb 370 httpHdrCcUpdateStats(e->field.v_pcc, &HttpHeaderStats[0].ccTypeDistr);
cb69b4c7 371 httpHeaderDelAt(hdr, pos);
12cf1be2 372 }
cb69b4c7 373 xfree(hdr->entries);
374 hdr->emask = 0;
375 hdr->entries = NULL;
376 hdr->capacity = hdr->ucount = 0;
377}
378
379void
2ac76861 380httpHeaderDestroy(HttpHeader * hdr)
cb69b4c7 381{
382 httpHeaderClean(hdr);
383 xfree(hdr);
384}
385
386/* create a copy of self */
387HttpHeader *
2ac76861 388httpHeaderClone(HttpHeader * hdr)
cb69b4c7 389{
390 HttpHeader *clone = httpHeaderCreate();
391 HttpHeaderEntry *e;
392 HttpHeaderPos pos = HttpHeaderInitPos;
393
2ac76861 394 debug(55, 7) ("cloning hdr: %p -> %p\n", hdr, clone);
cb69b4c7 395
396 while ((e = httpHeaderGetEntry(hdr, &pos))) {
397 HttpHeaderEntry e_clone = httpHeaderEntryClone(e);
398 httpHeaderAddNewEntry(clone, &e_clone);
399 }
400
401 return clone;
402}
403
404/* just handy in parsing: resets and returns false */
405static int
2ac76861 406httpHeaderReset(HttpHeader * hdr)
407{
cb69b4c7 408 httpHeaderClean(hdr);
409 httpHeaderInit(hdr);
410 return 0;
411}
412
413/*
414 * Note: currently, in most cases, we discard a field if we cannot parse it. We
415 * also truncate some field values (e.g. content-type). Thus, we may not
416 * forward exactly what was received. However, Squid keeps a copy of "raw"
417 * headers anyway, so we are safe until that changes. A possible alternative
418 * would be to store any buggy field as HDR_OTHER, but that still leaves a
419 * problem with truncated fields. The later one requires a better parser and
420 * additional storage, I guess.
421 */
422int
2ac76861 423httpHeaderParse(HttpHeader * hdr, const char *header_start, const char *header_end)
cb69b4c7 424{
425 const char *field_start = header_start;
426 HttpHeaderEntry e;
427 int mask = 0;
428
429 assert(hdr);
430 assert(header_start && header_end);
2ac76861 431 debug(55, 7) ("parsing hdr: (%p) '%s'\n...\n", hdr, getStringPrefix(header_start));
cb69b4c7 432 /* select appropriate field mask */
2ac76861 433 mask = ( /* fix this @?@ @?@ */ 1) ? ReplyHeadersMask : RequestHeadersMask;
cb69b4c7 434 /* commonn format headers are "<name>:[ws]<value>" lines delimited by <CRLF> */
435 while (field_start < header_end) {
436 const char *field_end = field_start + strcspn(field_start, "\r\n");
2ac76861 437 /*tmp_debug(here) ("found end of field: %d\n", (int)*field_end); */
438 if (!*field_end)
439 return httpHeaderReset(hdr); /* missing <CRLF> */
cb69b4c7 440 /*
441 * If we fail to parse a field, we ignore that field. We also could
442 * claim that the whole header is invalid. The latter is safer, but less
443 * robust. Note that we should be able to parse any commonn format field
444 */
445 if (!httpHeaderEntryParseInit(&e, field_start, field_end, mask))
20198c7a 446 debug(55, 2) ("warning: ignoring unparseable http header field near '%s'\n",
cb69b4c7 447 getStringPrefix(field_start));
448 else
449 httpHeaderAddParsedEntry(hdr, &e);
450 /*
451 * Note that we init() e, bit never clean() it which is equivalent to *
452 * creating a fresh entry on each loop iteration; thus, it is safe to *
453 * add e without dup()-ing it.
454 */
455 field_start = field_end;
456 /* skip CRLF */
2ac76861 457 if (*field_start == '\r')
458 field_start++;
459 if (*field_start == '\n')
460 field_start++;
cb69b4c7 461 }
2ac76861 462 return 1; /* even if no fields where found, they could be optional! */
cb69b4c7 463}
464
465/*
466 * packs all the entries into the buffer,
467 * returns number of bytes packed including terminating '\0'
468 */
469void
2ac76861 470httpHeaderPackInto(const HttpHeader * hdr, Packer * p)
cb69b4c7 471{
472 HttpHeaderPos pos = HttpHeaderInitPos;
473 const HttpHeaderEntry *e;
474 assert(hdr && p);
2ac76861 475 debug(55, 7) ("packing hdr: (%p)\n", hdr);
cb69b4c7 476 /* pack all entries one by one */
477 while ((e = httpHeaderGetEntry(hdr, &pos))) {
478 httpHeaderEntryPackInto(e, p);
479 }
480}
481
482/* returns next valid entry */
483static HttpHeaderEntry *
2ac76861 484httpHeaderGetEntry(const HttpHeader * hdr, HttpHeaderPos * pos)
cb69b4c7 485{
486 assert(hdr && pos);
487 assert(*pos >= HttpHeaderInitPos && *pos < hdr->capacity);
399e85ea 488 debug(55, 8) ("searching next e in hdr %p from %d\n", hdr, *pos);
cb69b4c7 489 for ((*pos)++; *pos < hdr->ucount; (*pos)++) {
490 HttpHeaderEntry *e = hdr->entries + *pos;
2ac76861 491 if (httpHeaderEntryIsValid(e)) {
492 debug(55, 8) ("%p returning entry: %s at %d\n",
cb69b4c7 493 hdr, httpHeaderEntryName(e), *pos);
2ac76861 494 return e;
cb69b4c7 495 }
496 }
2ac76861 497 debug(55, 8) ("no more entries in hdr %p\n", hdr);
cb69b4c7 498 return NULL;
499}
500
501/*
502 * returns a pointer to a specified entry and updates pos;
503 * note that we search from the very begining so it does not make much sense to
504 * ask for HDR_OTHER entries since there could be more than one.
505 */
506static HttpHeaderEntry *
2ac76861 507httpHeaderFindEntry(const HttpHeader * hdr, http_hdr_type id, HttpHeaderPos * pos)
cb69b4c7 508{
509 HttpHeaderPos p;
510 HttpHeaderEntry *e;
511 int is_absent;
512 assert(hdr);
513 assert_eid(id);
514 assert(id != HDR_OTHER);
515
2ac76861 516 debug(55, 8) ("finding entry %d in hdr %p\n", id, hdr);
cb69b4c7 517 /* check mask first @?@ @?@ remove double checking and asserts when done */
518 is_absent = (id != HDR_OTHER && !EBIT_TEST(hdr->emask, id));
2ac76861 519 if (!pos)
520 pos = &p;
cb69b4c7 521 *pos = HttpHeaderInitPos;
522 while ((e = httpHeaderGetEntry(hdr, pos))) {
2ac76861 523 if (e->id == id) {
cb69b4c7 524 assert(!is_absent);
2ac76861 525 return e;
cb69b4c7 526 }
527 }
528 assert(!EBIT_TEST(hdr->emask, id));
529 return NULL;
530}
531
532/*
533 * deletes all field(s) with a given name if any, returns #fields deleted;
534 * used to process Connection: header and delete fields in "paranoid" setup
535 */
2ac76861 536int
537httpHeaderDelFields(HttpHeader * hdr, const char *name)
cb69b4c7 538{
539 int count = 0;
540 HttpHeaderPos pos = HttpHeaderInitPos;
541 HttpHeaderEntry *e;
542
2ac76861 543 debug(55, 7) ("deleting '%s' fields in hdr %p\n", name, hdr);
cb69b4c7 544 while ((e = httpHeaderGetEntry(hdr, &pos))) {
2ac76861 545 if (!strcmp(httpHeaderEntryName(e), name)) {
cb69b4c7 546 httpHeaderDelAt(hdr, pos);
547 count++;
548 }
549 }
550 return count;
551}
552
553/*
554 * deletes an entry at pos and leaves a gap; leaving a gap makes it
555 * possible to iterate(search) and delete fields at the same time
556 */
557static void
2ac76861 558httpHeaderDelAt(HttpHeader * hdr, HttpHeaderPos pos)
cb69b4c7 559{
560 HttpHeaderEntry *e;
561 assert(hdr);
562 assert(pos >= 0 && pos < hdr->ucount);
563 e = hdr->entries + pos;
2ac76861 564 debug(55, 7) ("%p deling entry at %d: id: %d (%p:%p)\n",
cb69b4c7 565 hdr, pos, e->id, hdr->entries, e);
566 /* sync masks */
567 httpHeaderSyncMasks(hdr, e, 0);
568 httpHeaderEntryClean(e);
569 if (pos == hdr->ucount)
570 hdr->ucount--;
571}
572
573/*
574 * adds parsed entry (joins entries if neeeded); assumes e.value is dup()-ed and
575 * clean()s it if needed. Thus, "e" should be treated as uninitialized after
576 * this function returns.
577 */
578static void
2ac76861 579httpHeaderAddParsedEntry(HttpHeader * hdr, HttpHeaderEntry * e)
cb69b4c7 580{
581 HttpHeaderEntry *olde;
582 assert(hdr);
8d2e9b4a 583 assert(e);
cb69b4c7 584 assert_eid(e->id);
585
2ac76861 586 debug(55, 7) ("%p adding parsed entry %d\n", hdr, e->id);
cb69b4c7 587
588 /* there is no good reason to add invalid entries */
589 if (!httpHeaderEntryIsValid(e))
590 return;
591
592 olde = (e->id == HDR_OTHER) ? NULL : httpHeaderFindEntry(hdr, e->id, NULL);
593 if (olde) {
594 if (EBIT_TEST(ListHeadersMask, e->id))
595 httpHeaderEntryJoinWith(olde, e);
12cf1be2 596 else {
597 debug(55, 2) ("ignoring duplicate header: %s\n", httpHeaderEntryName(e));
598 Headers[e->id].stat.repCount++;
599 }
cb69b4c7 600 httpHeaderEntryClean(e);
601 } else {
602 /* actual add */
603 httpHeaderAddNewEntry(hdr, e);
2ac76861 604 debug(55, 6) ("%p done adding parsed entry %d (%s)\n", hdr, e->id, httpHeaderEntryName(e));
cb69b4c7 605 }
cb69b4c7 606}
607
608/*
609 * adds a new entry (low level append, does not check if entry is new) note: we
610 * copy e value, thus, e can point to a tmp variable (but e->field is not dupped!)
611 */
612static void
2ac76861 613httpHeaderAddNewEntry(HttpHeader * hdr, const HttpHeaderEntry * e)
cb69b4c7 614{
615 assert(hdr && e);
2ac76861 616 debug(55, 8) ("%p adding entry: %d at %d, (%p:%p)\n",
617 hdr, e->id, hdr->ucount,
cb69b4c7 618 hdr->entries, hdr->entries + hdr->ucount);
12cf1be2 619 if (!hdr->ucount)
620 HeaderParsedCount++;
621 if (hdr->ucount >= hdr->capacity)
622 httpHeaderGrow(hdr);
cb69b4c7 623 hdr->entries[hdr->ucount++] = *e;
624 /* sync masks */
625 httpHeaderSyncMasks(hdr, e, 1);
cb69b4c7 626}
627
2ac76861 628#if 0 /* save for parts */
cb69b4c7 629/*
630 * Splits list field and appends all entries separately;
631 * Warning: This is internal function, never call this directly,
632 * only for httpHeaderAddField use.
633 */
634static void
2ac76861 635httpHeaderAddListField(HttpHeader * hdr, HttpHeaderField * fld)
cb69b4c7 636{
637 const char *v;
638 assert(hdr);
639 assert(fld);
640 /*
641 * Note: assume that somebody already checked that we can split. The danger
642 * is in splitting something that is not a list field but contains ','s in
643 * its value.
644 */
645 /* we got a fld.value that is a list of values separated by ',' */
646 v = strtok(fld->value, ",");
2ac76861 647 httpHeaderAddSingleField(hdr, fld); /* first strtok() did its job! */
cb69b4c7 648 while ((v = strtok(NULL, ","))) {
649 /* ltrim and skip empty fields */
2ac76861 650 while (isspace(*v) || *v == ',')
651 v++;
cb69b4c7 652 if (*v)
653 httpHeaderAddSingleField(hdr, httpHeaderFieldCreate(fld->name, v));
654 }
655}
656#endif
657
658/*
659 * Global (user level) routines
660 */
661
662/* test if a field is present */
2ac76861 663int
664httpHeaderHas(const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 665{
666 assert(hdr);
667 assert_eid(id);
668 assert(id != HDR_OTHER);
2ac76861 669 debug(55, 7) ("%p lookup for %d\n", hdr, id);
cb69b4c7 670 return EBIT_TEST(hdr->emask, id);
671
672#ifdef SLOW_BUT_SAFE
673 return httpHeaderFindEntry(hdr, id, NULL) != NULL;
674#endif
675}
676
677/* delete a field if any */
2ac76861 678void
679httpHeaderDel(HttpHeader * hdr, http_hdr_type id)
cb69b4c7 680{
681 HttpHeaderPos pos = HttpHeaderInitPos;
682 assert(id != HDR_OTHER);
2ac76861 683 debug(55, 8) ("%p del-by-id %d\n", hdr, id);
cb69b4c7 684 if (httpHeaderFindEntry(hdr, id, &pos)) {
685 httpHeaderDelAt(hdr, pos);
686 }
687}
688
689/*
690 * set a field
691 * setting an invaid value is equivalent to deleting a field
692 * (if field is not present, it is added; otherwise, old content is destroyed).
693 */
694static void
2ac76861 695httpHeaderSet(HttpHeader * hdr, http_hdr_type id, const field_store value)
cb69b4c7 696{
697 HttpHeaderPos pos;
698 HttpHeaderEntry e;
699 assert(hdr);
700 assert_eid(id);
2ac76861 701
702 debug(55, 7) ("%p sets entry with id: %d\n", hdr, id);
703 if (httpHeaderFindEntry(hdr, id, &pos)) /* delete old entry */
cb69b4c7 704 httpHeaderDelAt(hdr, pos);
705
706 httpHeaderEntryInit(&e, id, httpHeaderFieldDup(Headers[id].type, value));
707 if (httpHeaderEntryIsValid(&e))
708 httpHeaderAddNewEntry(hdr, &e);
709 else
710 httpHeaderEntryClean(&e);
711}
712
713void
2ac76861 714httpHeaderSetInt(HttpHeader * hdr, http_hdr_type id, int number)
cb69b4c7 715{
716 field_store value;
717 assert_eid(id);
2ac76861 718 assert(Headers[id].type == ftInt); /* must be of an appropriatre type */
cb69b4c7 719 value.v_int = number;
720 httpHeaderSet(hdr, id, value);
721}
722
723void
2ac76861 724httpHeaderSetTime(HttpHeader * hdr, http_hdr_type id, time_t time)
cb69b4c7 725{
726 field_store value;
727 assert_eid(id);
2ac76861 728 assert(Headers[id].type == ftDate_1123); /* must be of an appropriatre type */
cb69b4c7 729 value.v_time = time;
730 httpHeaderSet(hdr, id, value);
731}
2ac76861 732
cb69b4c7 733void
2ac76861 734httpHeaderSetStr(HttpHeader * hdr, http_hdr_type id, const char *str)
cb69b4c7 735{
736 field_store value;
737 assert_eid(id);
2ac76861 738 assert(Headers[id].type == ftPChar); /* must be of a string type */
cb69b4c7 739 value.v_pcchar = str;
740 httpHeaderSet(hdr, id, value);
741}
742
63259c34 743void
2ac76861 744httpHeaderSetAuth(HttpHeader * hdr, const char *authScheme, const char *realm)
63259c34 745{
746 MemBuf mb;
747 assert(hdr && authScheme && realm);
748 memBufDefInit(&mb);
749 memBufPrintf(&mb, "%s realm=\"%s\"", authScheme, realm);
750 httpHeaderSetStr(hdr, HDR_WWW_AUTHENTICATE, mb.buf);
751 memBufClean(&mb);
752}
753
cb69b4c7 754/* add extension header (these fields are not parsed/analyzed/joined, etc.) */
755void
2ac76861 756httpHeaderAddExt(HttpHeader * hdr, const char *name, const char *value)
cb69b4c7 757{
758 HttpHeaderExtField *ext = httpHeaderExtFieldCreate(name, value);
759 HttpHeaderEntry e;
760
2ac76861 761 debug(55, 8) ("%p adds ext entry '%s:%s'\n", hdr, name, value);
7021844c 762 httpHeaderEntryInit(&e, HDR_OTHER, ptrField(ext));
cb69b4c7 763 httpHeaderAddNewEntry(hdr, &e);
764}
765
766/* get a value of a field (not lvalue though) */
7c525cc2 767static field_store
2ac76861 768httpHeaderGet(const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 769{
770 HttpHeaderEntry *e;
771 assert_eid(id);
2ac76861 772 assert(id != HDR_OTHER); /* there is no single value for HDR_OTHER */
cb69b4c7 773
2ac76861 774 debug(55, 7) ("%p get for id %d\n", hdr, id);
cb69b4c7 775 if ((e = httpHeaderFindEntry(hdr, id, NULL)))
776 return e->field;
777 else
778 return httpHeaderFieldBadValue(Headers[id].type);
779}
780
7c525cc2 781int
782httpHeaderGetInt(const HttpHeader * hdr, http_hdr_type id)
783{
784 assert_eid(id);
399e85ea 785 assert(Headers[id].type == ftInt); /* must be of an apropriate type */
7c525cc2 786 return httpHeaderGet(hdr, id).v_int;
787}
788
cb69b4c7 789const char *
2ac76861 790httpHeaderGetStr(const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 791{
792 assert_eid(id);
399e85ea 793 assert(Headers[id].type == ftPChar); /* must be of an apropriate type */
cb69b4c7 794 return httpHeaderGet(hdr, id).v_pchar;
795}
796
797time_t
2ac76861 798httpHeaderGetTime(const HttpHeader * hdr, http_hdr_type id)
cb69b4c7 799{
800 assert_eid(id);
2ac76861 801 assert(Headers[id].type == ftDate_1123); /* must be of an apropriate type */
cb69b4c7 802 return httpHeaderGet(hdr, id).v_time;
803}
804
7faf2bdb 805HttpHdrCc *
806httpHeaderGetCc(const HttpHeader * hdr)
cb69b4c7 807{
7faf2bdb 808 return httpHeaderGet(hdr, HDR_CACHE_CONTROL).v_pcc;
cb69b4c7 809}
810
811/* updates header masks */
812static void
2ac76861 813httpHeaderSyncMasks(HttpHeader * hdr, const HttpHeaderEntry * e, int add)
cb69b4c7 814{
815 int isSet;
816 assert(hdr && e);
817 assert_eid(e->id);
818
819 /* we cannot mask HDR_OTHER because it may not be unique */
820 if (e->id == HDR_OTHER)
821 return;
822 isSet = EBIT_TEST(hdr->emask, e->id) != 0;
823 add = add != 0;
824 assert(isSet ^ add);
825 add ? EBIT_SET(hdr->emask, e->id) : EBIT_CLR(hdr->emask, e->id);
826}
827
cb69b4c7 828/* doubles the size of the fields index, starts with INIT_FIELDS_PER_HEADER */
829static void
2ac76861 830httpHeaderGrow(HttpHeader * hdr)
cb69b4c7 831{
832 int new_cap;
833 int new_size;
834 assert(hdr);
2ac76861 835 new_cap = (hdr->capacity) ? 2 * hdr->capacity : INIT_FIELDS_PER_HEADER;
836 new_size = new_cap * sizeof(HttpHeaderEntry);
cb69b4c7 837
2ac76861 838 debug(55, 9) ("%p grow (%p) %d->%d\n", hdr, hdr->entries, hdr->capacity, new_cap);
cb69b4c7 839 hdr->entries = hdr->entries ?
840 xrealloc(hdr->entries, new_size) :
841 xmalloc(new_size);
2ac76861 842 memset(hdr->entries + hdr->capacity, 0, (new_cap - hdr->capacity) * sizeof(HttpHeaderEntry));
cb69b4c7 843 hdr->capacity = new_cap;
2ac76861 844 debug(55, 9) ("%p grew (%p)\n", hdr, hdr->entries);
cb69b4c7 845}
846
847/*
848 * HttpHeaderEntry
849 */
850
851static void
2ac76861 852httpHeaderEntryInit(HttpHeaderEntry * e, http_hdr_type id, field_store field)
cb69b4c7 853{
854 assert(e);
855 assert_eid(id);
856 e->id = id;
857 e->field = field;
12cf1be2 858 Headers[id].stat.aliveCount++;
cb69b4c7 859}
860
861static void
2ac76861 862httpHeaderEntryClean(HttpHeaderEntry * e)
863{
cb69b4c7 864 assert(e);
865 assert_eid(e->id);
866 /* type-based cleanup */
867 switch (Headers[e->id].type) {
2ac76861 868 case ftInvalid:
869 case ftInt:
870 case ftDate_1123:
871 /* no special cleaning is necessary */
872 break;
873 case ftPChar:
874 freeShortString(e->field.v_pchar);
875 break;
876 case ftPSCC:
7faf2bdb 877 if (e->field.v_pcc)
878 httpHdrCcDestroy(e->field.v_pcc);
2ac76861 879 break;
880 case ftPExtField:
881 if (e->field.v_pefield)
882 httpHeaderExtFieldDestroy(e->field.v_pefield);
883 break;
884 default:
885 assert(0); /* somebody added a new type? */
cb69b4c7 886 }
12cf1be2 887 Headers[e->id].stat.aliveCount--;
cb69b4c7 888 /* we have to do that so entry will be _invlaid_ */
889 e->id = -1;
890 e->field.v_pchar = NULL;
891}
892
893/* parses and inits header entry, returns true on success */
894static int
2ac76861 895httpHeaderEntryParseInit(HttpHeaderEntry * e, const char *field_start, const char *field_end, int mask)
cb69b4c7 896{
897 HttpHeaderExtField *f;
898 int id;
899 int result;
900
12cf1be2 901 HeaderEntryParsedCount++;
8d2e9b4a 902 /* paranoid reset */
903 e->id = -1;
904 e->field.v_pchar = NULL;
cb69b4c7 905 /* first assume it is just an extension field */
906 f = httpHeaderExtFieldParseCreate(field_start, field_end);
2ac76861 907 if (!f) /* total parsing failure */
cb69b4c7 908 return 0;
909 id = httpHeaderIdByName(f->name, -1, Headers, countof(Headers), mask);
910 if (id < 0)
911 id = HDR_OTHER;
12cf1be2 912 Headers[id].stat.parsCount++;
cb69b4c7 913 if (id == HDR_OTHER) {
914 /* hm.. it is an extension field indeed */
7021844c 915 httpHeaderEntryInit(e, id, ptrField(f));
cb69b4c7 916 return 1;
917 }
918 /* ok, we got something interesting, parse it further */
919 result = httpHeaderEntryParseExtFieldInit(e, id, f);
920 /* do not need it anymore */
921 httpHeaderExtFieldDestroy(f);
922 return result;
923}
924
925static int
2ac76861 926httpHeaderEntryParseExtFieldInit(HttpHeaderEntry * e, int id, const HttpHeaderExtField * f)
cb69b4c7 927{
8d2e9b4a 928 assert(e && f);
929 assert_eid(id);
12cf1be2 930 e->id = -1;
cb69b4c7 931 /*
932 * check for exceptions first (parsing is not determined by value type)
933 * then parse using value type if needed
934 */
935 switch (id) {
2ac76861 936 case HDR_PROXY_KEEPALIVE:
937 /* we treat Proxy-Connection as "keep alive" only if it says so */
7021844c 938 httpHeaderEntryInit(e, id, intField(!strcasecmp(f->value, "Keep-Alive")));
2ac76861 939 break;
940 default:
941 /* if we got here, it is something that can be parsed based on value type */
942 if (!httpHeaderEntryParseByTypeInit(e, id, f))
943 return 0;
cb69b4c7 944 }
945 /* parsing was successful, post-processing maybe required */
946 switch (id) {
2ac76861 947 case HDR_CONTENT_TYPE:{
cb69b4c7 948 /* cut off "; parameter" from Content-Type @?@ why? */
949 const int l = strcspn(e->field.v_pchar, ";\t ");
950 if (l > 0)
951 e->field.v_pchar[l] = '\0';
952 break;
953 }
2ac76861 954 case HDR_EXPIRES:
955 /*
956 * The HTTP/1.0 specs says that robust implementations should
957 * consider bad or malformed Expires header as equivalent to
958 * "expires immediately."
959 */
960 if (!httpHeaderEntryIsValid(e))
961 e->field.v_time = squid_curtime;
962 /*
963 * real expiration value also depends on max-age too, but it is not
964 * of our business (HttpReply should handle it)
965 */
966 break;
cb69b4c7 967 }
968 return 1;
969}
970
971static int
2ac76861 972httpHeaderEntryParseByTypeInit(HttpHeaderEntry * e, int id, const HttpHeaderExtField * f)
cb69b4c7 973{
974 int type;
975 field_store field;
976 assert(e && f);
977 assert_eid(id);
978 type = Headers[id].type;
979
980 httpHeaderFieldInit(&field);
2ac76861 981 switch (type) {
982 case ftInt:
b5107edb 983 if (!httpHeaderParseInt(f->value, &field.v_int)) {
2ac76861 984 Headers[id].stat.errCount++;
985 return 0;
986 }
987 break;
cb69b4c7 988
2ac76861 989 case ftPChar:
990 field.v_pchar = dupShortStr(f->value);
991 break;
cb69b4c7 992
2ac76861 993 case ftDate_1123:
994 field.v_time = parse_rfc1123(f->value);
995 if (field.v_time <= 0)
996 Headers[id].stat.errCount++;
997 /*
998 * if parse_rfc1123 fails we fall through anyway so upper levels
999 * will notice invalid date
1000 */
1001 break;
1002
1003 case ftPSCC:
7faf2bdb 1004 field.v_pcc = httpHdrCcParseCreate(f->value);
1005 if (!field.v_pcc) {
20198c7a 1006 debug(55, 2) ("failed to parse scc hdr: id: %d, field: '%s: %s'\n",
2ac76861 1007 id, f->name, f->value);
1008 Headers[id].stat.errCount++;
1009 return 0;
1010 }
1011 break;
cb69b4c7 1012
2ac76861 1013 default:
20198c7a 1014 debug(55, 2) ("something went wrong with hdr field type analysis: id: %d, type: %d, field: '%s: %s'\n",
2ac76861 1015 id, type, f->name, f->value);
1016 assert(0);
cb69b4c7 1017 }
1018 /* success, do actual init */
1019 httpHeaderEntryInit(e, id, field);
1020 return 1;
1021}
1022
1023
1024static HttpHeaderEntry
2ac76861 1025httpHeaderEntryClone(const HttpHeaderEntry * e)
cb69b4c7 1026{
1027 HttpHeaderEntry clone;
1028 assert(e);
1029 assert_eid(e->id);
1030 httpHeaderEntryInit(&clone, e->id,
1031 httpHeaderFieldDup(Headers[e->id].type, e->field));
1032 return clone;
1033}
1034
1035static void
2ac76861 1036httpHeaderEntryPackInto(const HttpHeaderEntry * e, Packer * p)
cb69b4c7 1037{
1038 assert(e && p);
1039
1040 /* swap the field_name: */
1041 packerPrintf(p, "%s: ", httpHeaderEntryName(e));
1042 /*
1043 * swap the value
1044 * check for exceptions (packing is not determined by value type)
1045 * then swap using value type
1046 */
1047 switch (e->id) {
2ac76861 1048 case HDR_PROXY_KEEPALIVE:
1049 packerPrintf(p, "%s", "Keep-Alive");
1050 break;
1051 default:
1052 /* if we got here, it is something that can be swap based on value type */
1053 httpHeaderEntryPackByType(e, p);
cb69b4c7 1054 }
1055 /* add CRLF */
1056 packerPrintf(p, "%s", "\r\n");
1057}
1058
1059static void
2ac76861 1060httpHeaderEntryPackByType(const HttpHeaderEntry * e, Packer * p)
cb69b4c7 1061{
1062 field_type type;
1063 assert(e && p);
1064 assert_eid(e->id);
2ac76861 1065 type = Headers[e->id].type;
1066 switch (type) {
1067 case ftInt:
1068 packerPrintf(p, "%d", e->field.v_int);
1069 break;
1070 case ftPChar:
1071 packerPrintf(p, "%s", e->field.v_pchar);
1072 break;
1073 case ftDate_1123:
1074 packerPrintf(p, "%s", mkrfc1123(e->field.v_time));
1075 break;
1076 case ftPSCC:
7faf2bdb 1077 httpHdrCcPackValueInto(e->field.v_pcc, p);
2ac76861 1078 break;
1079 case ftPExtField:
1080 packerPrintf(p, "%s", e->field.v_pefield->value);
1081 break;
1082 default:
1083 assert(0 && type); /* pack for invalid/unknown type */
cb69b4c7 1084 }
1085}
1086
1087static void
2ac76861 1088httpHeaderEntryJoinWith(HttpHeaderEntry * e, const HttpHeaderEntry * newe)
cb69b4c7 1089{
1090 field_type type;
1091 assert(e && newe);
1092 assert_eid(e->id);
1093 assert(e->id == newe->id);
1094
2ac76861 1095 debug(55, 6) ("joining entry (%p) with (%p)\n", e, newe);
cb69b4c7 1096 /* type-based join */
1097 type = Headers[e->id].type;
2ac76861 1098 switch (type) {
1099 case ftPChar:
1100 e->field.v_pchar = appShortStr(e->field.v_pchar, newe->field.v_pchar);
1101 break;
1102 case ftPSCC:
7faf2bdb 1103 httpHdrCcJoinWith(e->field.v_pcc, newe->field.v_pcc);
2ac76861 1104 break;
1105 default:
1106 debug(55, 0) ("join for invalid/unknown type: id: %d, type: %d\n", e->id, type);
1107 assert(0);
cb69b4c7 1108 }
1109}
1110
1111
1112static int
2ac76861 1113httpHeaderEntryIsValid(const HttpHeaderEntry * e)
cb69b4c7 1114{
1115 assert(e);
1116 if (e->id == -1)
1117 return 0;
1118 assert_eid(e->id);
1119 /* type-based analysis */
2ac76861 1120 switch (Headers[e->id].type) {
1121 case ftInvalid:
1122 return 0;
1123 case ftInt:
1124 return e->field.v_int >= 0;
1125 case ftPChar:
1126 return e->field.v_pchar != NULL;
2ac76861 1127 case ftDate_1123:
1128 return e->field.v_time >= 0;
2ac76861 1129 case ftPSCC:
7faf2bdb 1130 return e->field.v_pcc != NULL;
2ac76861 1131 case ftPExtField:
1132 return e->field.v_pefield != NULL;
2ac76861 1133 default:
1134 assert(0); /* query for invalid/unknown type */
cb69b4c7 1135 }
2ac76861 1136 return 0; /* not reached */
cb69b4c7 1137}
1138
1139static const char *
2ac76861 1140httpHeaderEntryName(const HttpHeaderEntry * e)
cb69b4c7 1141{
1142 assert(e);
1143 assert_eid(e->id);
1144
2ac76861 1145 return (e->id == HDR_OTHER) ?
cb69b4c7 1146 e->field.v_pefield->name : Headers[e->id].name;
1147}
1148
1149/*
1150 * HttpHeaderField
1151 */
1152
1153static void
2ac76861 1154httpHeaderFieldInit(field_store * field)
cb69b4c7 1155{
1156 assert(field);
1157 memset(field, 0, sizeof(field_store));
1158}
1159
1160static field_store
1161httpHeaderFieldDup(field_type type, field_store value)
1162{
1163 /* type based duplication */
2ac76861 1164 switch (type) {
1165 case ftInt:
2ac76861 1166 case ftDate_1123:
7021844c 1167 return value;
1168 case ftPChar:
1169 return ptrField(dupShortStr(value.v_pchar));
2ac76861 1170 case ftPSCC:
7faf2bdb 1171 return ptrField(httpHdrCcDup(value.v_pcc));
2ac76861 1172 case ftPExtField:
7021844c 1173 return ptrField(httpHeaderExtFieldDup(value.v_pefield));
2ac76861 1174 default:
1175 assert(0); /* dup of invalid/unknown type */
cb69b4c7 1176 }
7021844c 1177 return ptrField(NULL); /* not reached */
cb69b4c7 1178}
1179
1180/*
1181 * bad value table; currently bad values are determined by field type, but this
1182 * can be changed in the future to reflect dependence on entry id if any
1183 */
1184static field_store
1185httpHeaderFieldBadValue(field_type type)
1186{
2ac76861 1187 switch (type) {
1188 case ftInt:
7021844c 1189 return intField(-1);
2ac76861 1190 case ftDate_1123:
7021844c 1191 return timeField(-1);
2ac76861 1192 case ftPChar:
1193 case ftPSCC:
1194 case ftPExtField:
7021844c 1195 return ptrField(NULL);
2ac76861 1196 case ftInvalid:
1197 default:
1198 assert(0); /* query for invalid/unknown type */
cb69b4c7 1199 }
7021844c 1200 return ptrField(NULL); /* not reached */
cb69b4c7 1201}
1202
399e85ea 1203#if 0 /* moved to HttpHdrCC.c */
7faf2bdb 1204
cb69b4c7 1205/*
1206 * HttpScc (server cache control)
1207 */
1208
1209static HttpScc *
1210httpSccCreate()
1211{
7021844c 1212 HttpScc *scc = memAllocate(MEM_HTTP_SCC);
cb69b4c7 1213 scc->max_age = -1;
1214 return scc;
1215}
1216
2ac76861 1217/* creates an scc object from a 0-terminating string */
cb69b4c7 1218static HttpScc *
1219httpSccParseCreate(const char *str)
1220{
1221 HttpScc *scc = httpSccCreate();
1222 httpSccParseInit(scc, str);
1223 return scc;
1224}
1225
1226/* parses a 0-terminating string and inits scc */
1227static void
2ac76861 1228httpSccParseInit(HttpScc * scc, const char *str)
cb69b4c7 1229{
1230 const char *item;
2ac76861 1231 const char *p; /* '=' parameter */
cb69b4c7 1232 const char *pos = NULL;
1233 int type;
1234 int ilen;
1235 assert(scc && str);
1236
12cf1be2 1237 CcPasredCount++;
cb69b4c7 1238 /* iterate through comma separated list */
2ac76861 1239 while (strListGetItem(str, ',', &item, &ilen, &pos)) {
cb69b4c7 1240 /* strip '=' statements @?@ */
2ac76861 1241 if ((p = strchr(item, '=')) && (p - item < ilen))
cb69b4c7 1242 ilen = p++ - item;
1243 /* find type */
1244 type = httpHeaderIdByName(item, ilen,
1245 SccAttrs, SCC_ENUM_END, -1);
1246 if (type < 0) {
20198c7a 1247 debug(55, 2) ("cc: unknown cache-directive: near '%s' in '%s'\n", item, str);
cb69b4c7 1248 continue;
1249 }
1250 if (EBIT_TEST(scc->mask, type)) {
20198c7a 1251 debug(55, 2) ("cc: ignoring duplicate cache-directive: near '%s' in '%s'\n", item, str);
12cf1be2 1252 SccAttrs[type].stat.repCount++;
cb69b4c7 1253 continue;
1254 }
1255 /* update mask */
2ac76861 1256 EBIT_SET(scc->mask, type);
cb69b4c7 1257 /* post-processing special cases */
1258 switch (type) {
2ac76861 1259 case SCC_MAX_AGE:
1260 if (p)
1261 scc->max_age = (time_t) atoi(p);
1262 if (scc->max_age < 0) {
20198c7a 1263 debug(55, 2) ("scc: invalid max-age specs near '%s'\n", item);
2ac76861 1264 scc->max_age = -1;
1265 EBIT_CLR(scc->mask, type);
1266 }
1267 break;
1268 default:
1269 /* note that we ignore most of '=' specs @?@ */
1270 break;
cb69b4c7 1271 }
1272 }
1273 return;
1274}
1275
1276static void
2ac76861 1277httpSccDestroy(HttpScc * scc)
cb69b4c7 1278{
1279 assert(scc);
1280 memFree(MEM_HTTP_SCC, scc);
1281}
1282
1283static HttpScc *
2ac76861 1284httpSccDup(HttpScc * scc)
cb69b4c7 1285{
1286 HttpScc *dup;
1287 assert(scc);
1288 dup = httpSccCreate();
1289 dup->mask = scc->mask;
1290 dup->max_age = scc->max_age;
1291 return dup;
1292}
1293
1294static void
2ac76861 1295httpSccPackValueInto(HttpScc * scc, Packer * p)
cb69b4c7 1296{
1297 http_scc_type flag;
1298 int pcount = 0;
1299 assert(scc && p);
1300 if (scc->max_age >= 0) {
2ac76861 1301 packerPrintf(p, "max-age=%d", scc->max_age);
cb69b4c7 1302 pcount++;
1303 }
1304 for (flag = 0; flag < SCC_ENUM_END; flag++) {
1305 if (EBIT_TEST(scc->mask, flag)) {
1306 packerPrintf(p, pcount ? ", %s" : "%s", SccAttrs[flag].name);
1307 pcount++;
1308 }
1309 }
1310}
1311
1312static void
2ac76861 1313httpSccJoinWith(HttpScc * scc, HttpScc * new_scc)
cb69b4c7 1314{
1315 assert(scc && new_scc);
1316 if (scc->max_age < 0)
1317 scc->max_age = new_scc->max_age;
1318 scc->mask |= new_scc->mask;
1319}
1320
12cf1be2 1321static void
2ac76861 1322httpSccUpdateStats(const HttpScc * scc, StatHist * hist)
12cf1be2 1323{
1324 http_scc_type c;
1325 assert(scc);
1326 for (c = 0; c < SCC_ENUM_END; c++)
1327 if (EBIT_TEST(scc->mask, c))
1328 statHistCount(hist, c);
1329}
cb69b4c7 1330
7faf2bdb 1331#endif
1332
cb69b4c7 1333/*
1334 * HttpHeaderExtField
1335 */
1336
1337static HttpHeaderExtField *
1338httpHeaderExtFieldCreate(const char *name, const char *value)
1339{
1340 HttpHeaderExtField *f = xcalloc(1, sizeof(HttpHeaderExtField));
1341 f->name = dupShortStr(name);
1342 f->value = dupShortStr(value);
1343 return f;
1344}
1345
1346/* parses ext field; returns fresh ext field on success and NULL on failure */
1347static HttpHeaderExtField *
1348httpHeaderExtFieldParseCreate(const char *field_start, const char *field_end)
1349{
1350 HttpHeaderExtField *f = NULL;
1351 /* note: name_start == field_start */
1352 const char *name_end = strchr(field_start, ':');
1353 const char *value_start;
1354 /* note: value_end == field_end */
1355
2ac76861 1356 if (!name_end || name_end <= field_start || name_end > field_end)
cb69b4c7 1357 return NULL;
1358
2ac76861 1359 value_start = name_end + 1; /* skip ':' */
cb69b4c7 1360 /* skip white space */
2ac76861 1361 while (value_start < field_end && isspace(*value_start))
cb69b4c7 1362 value_start++;
1363
1364 /* cut off "; parameter" from Content-Type @?@ why? */
1365 if (!strncasecmp(field_start, "Content-Type:", 13)) {
1366 const int l = strcspn(value_start, ";\t ");
1367 if (l > 0 && value_start + l < field_end)
1368 field_end = value_start + l;
1369 }
cb69b4c7 1370 f = xcalloc(1, sizeof(HttpHeaderExtField));
2ac76861 1371 f->name = dupShortBuf(field_start, name_end - field_start);
1372 f->value = dupShortBuf(value_start, field_end - value_start);
1373 debug(55, 8) ("got field: '%s: %s' (%p)\n", f->name, f->value, f);
cb69b4c7 1374 return f;
1375}
1376
1377static void
2ac76861 1378httpHeaderExtFieldDestroy(HttpHeaderExtField * f)
cb69b4c7 1379{
1380 assert(f);
1381 freeShortString(f->name);
1382 freeShortString(f->value);
1383 xfree(f);
1384}
1385
1386static HttpHeaderExtField *
2ac76861 1387httpHeaderExtFieldDup(HttpHeaderExtField * f)
cb69b4c7 1388{
1389 assert(f);
1390 return httpHeaderExtFieldCreate(f->name, f->value);
1391}
1392
cb69b4c7 1393/*
12cf1be2 1394 * Reports
cb69b4c7 1395 */
cb69b4c7 1396
1397static void
12cf1be2 1398httpHeaderFieldStatDumper(StoreEntry * sentry, int idx, double val, double size, int count)
cb69b4c7 1399{
12cf1be2 1400 const int id = (int) val;
1401 const int valid_id = id >= 0 && id < HDR_ENUM_END;
1402 const char *name = valid_id ? Headers[id].name : "INVALID";
1403 if (count || valid_id)
7021844c 1404 storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
12cf1be2 1405 id, name, count, xdiv(count, HeaderParsedCount));
cb69b4c7 1406}
1407
7faf2bdb 1408#if 0
12cf1be2 1409static void
1410httpHeaderCCStatDumper(StoreEntry * sentry, int idx, double val, double size, int count)
cb69b4c7 1411{
12cf1be2 1412 const int id = (int) val;
1413 const int valid_id = id >= 0 && id < SCC_ENUM_END;
1414 const char *name = valid_id ? SccAttrs[id].name : "INVALID";
1415 if (count || valid_id)
7021844c 1416 storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
12cf1be2 1417 id, name, count, xdiv(count, CcPasredCount));
cb69b4c7 1418}
7faf2bdb 1419#endif
cb69b4c7 1420
12cf1be2 1421static void
1422httpHeaderFldsPerHdrDumper(StoreEntry * sentry, int idx, double val, double size, int count)
cb69b4c7 1423{
12cf1be2 1424 if (count)
7021844c 1425 storeAppendPrintf(sentry, "%2d\t %5d\t %5d\t %6.2f\n",
2ac76861 1426 idx, ((int) (val + size)), count, xpercent(count, HeaderEntryParsedCount));
cb69b4c7 1427}
1428
1429
1430static void
2ac76861 1431httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e)
12cf1be2 1432{
1433 assert(hs && e);
1434
1435 storeAppendPrintf(e, "\n<h3>Header Stats: %s</h3>\n", hs->label);
7021844c 1436 storeAppendPrintf(e, "<h3>Field type distribution</h3>\n");
12cf1be2 1437 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n",
1438 "id", "name", "count", "#/header");
1439 statHistDump(&hs->fieldTypeDistr, e, httpHeaderFieldStatDumper);
7021844c 1440 storeAppendPrintf(e, "<h3>Cache-control directives distribution</h3>\n");
12cf1be2 1441 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n",
1442 "id", "name", "count", "#/cc_field");
7faf2bdb 1443 statHistDump(&hs->ccTypeDistr, e, httpHdrCcStatDumper);
7021844c 1444 storeAppendPrintf(e, "<h3>Number of fields per header distribution (init size: %d)</h3>\n",
12cf1be2 1445 INIT_FIELDS_PER_HEADER);
1446 storeAppendPrintf(e, "%2s\t %-5s\t %5s\t %6s\n",
1447 "id", "#flds", "count", "%total");
1448 statHistDump(&hs->hdrUCountDistr, e, httpHeaderFldsPerHdrDumper);
cb69b4c7 1449}
1450
1451static void
2ac76861 1452shortStringStatDump(StoreEntry * e)
12cf1be2 1453{
7021844c 1454 storeAppendPrintf(e, "<h3>Short String Stats</h3>\n<p>");
399e85ea 1455 memPoolReport(shortStrings, e);
7021844c 1456 storeAppendPrintf(e, "\n</p>\n");
12cf1be2 1457 storeAppendPrintf(e, "<br><h3>Long String Stats</h3>\n");
7021844c 1458 storeAppendPrintf(e, "alive: %3d (%5.1f KB) high-water: %3d (%5.1f KB)\n",
2ac76861 1459 longStrAliveCount, (double) longStrAliveSize / 1024.,
1460 longStrHighWaterCount, (double) longStrHighWaterSize / 1024.);
cb69b4c7 1461}
1462
12cf1be2 1463void
2ac76861 1464httpHeaderStoreReport(StoreEntry * e)
cb69b4c7 1465{
12cf1be2 1466 int i;
1467 http_hdr_type ht;
1468 assert(e);
cb69b4c7 1469
12cf1be2 1470 /* fix this (including summing for totals) for req hdrs @?@ */
2ac76861 1471 for (i = 0; i < 1 /*HttpHeaderStatCount */ ; i++) {
1472 httpHeaderStatDump(HttpHeaderStats + i, e);
12cf1be2 1473 storeAppendPrintf(e, "%s\n", "<br>");
1474 }
1475 storeAppendPrintf(e, "%s\n", "<hr size=1 noshade>");
1476 /* field stats */
1477 storeAppendPrintf(e, "<h3>Http Fields Stats (replies and requests)</h3>\n");
1478 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\t %6s\n",
1479 "id", "name", "#alive", "%err", "%repeat");
1480 for (ht = 0; ht < HDR_ENUM_END; ht++) {
2ac76861 1481 field_attrs_t *f = Headers + ht;
7021844c 1482 storeAppendPrintf(e, "%2d\t %-20s\t %5d\t %6.3f\t %6.3f\n",
12cf1be2 1483 f->id, f->name, f->stat.aliveCount,
2ac76861 1484 xpercent(f->stat.errCount, f->stat.parsCount),
12cf1be2 1485 xpercent(f->stat.repCount, f->stat.parsCount));
1486 }
1487 storeAppendPrintf(e, "%s\n", "<hr size=1 noshade>");
1488 /* short strings */
1489 shortStringStatDump(e);
cb69b4c7 1490}
cb69b4c7 1491
12cf1be2 1492
1493/*
1494 * "short string" routines below are trying to recycle memory for short strings
1495 */
1496
cb69b4c7 1497static char *
1498dupShortStr(const char *str)
1499{
1500 return dupShortBuf(str, strlen(str));
1501}
1502
1503static char *
1504dupShortBuf(const char *str, size_t len)
1505{
1506 char *buf;
1507 assert(str);
cb69b4c7 1508 buf = allocShortBuf(len + 1);
1509 assert(buf);
1510 if (len)
2ac76861 1511 xmemcpy(buf, str, len); /* may not have terminating 0 */
1512 buf[len] = '\0'; /* terminate */
1513 debug(55, 9) ("dupped short buf[%d] (%p): '%s'\n", len + 1, buf, buf);
cb69b4c7 1514 return buf;
1515}
1516
1517static char *
1518appShortStr(char *str, const char *app_str)
1519{
2ac76861 1520 const size_t size = strlen(str) + strlen(app_str) + 1;
cb69b4c7 1521 char *buf = allocShortBuf(size);
1522 snprintf(buf, size, "%s, %s", str, app_str);
1523 freeShortString(str);
1524 return buf;
1525}
1526
1527static char *
1528allocShortBuf(size_t sz)
1529{
1530 char *buf = NULL;
1531 assert(shortStrings);
7021844c 1532 if (sz > shortStrSize) {
cb69b4c7 1533 buf = xmalloc(sz);
12cf1be2 1534 longStrAliveCount++;
1535 longStrAliveSize += sz;
1536 if (longStrHighWaterCount < longStrAliveCount)
1537 longStrHighWaterCount = longStrAliveCount;
1538 if (longStrHighWaterSize < longStrAliveSize)
1539 longStrHighWaterSize = longStrAliveSize;
cb69b4c7 1540 } else
7021844c 1541 buf = memPoolAlloc(shortStrings);
cb69b4c7 1542 return buf;
1543}
1544
1545static void
1546freeShortString(char *str)
1547{
1548 assert(shortStrings);
1549 if (str) {
2ac76861 1550 const size_t sz = strlen(str) + 1;
7021844c 1551 debug(55, 9) ("freeing short str of size %d (max: %d) '%s' (%p)\n", sz, shortStrSize, str, str);
1552 if (sz > shortStrSize) {
1553 debug(55, 9) ("LONG short string[%d>%d]: %s\n", sz, shortStrSize, str);
12cf1be2 1554 assert(longStrAliveCount);
cb69b4c7 1555 xfree(str);
12cf1be2 1556 longStrAliveCount--;
1557 longStrAliveSize -= sz;
cb69b4c7 1558 } else
7021844c 1559 memPoolFree(shortStrings, str);
cb69b4c7 1560 }
1561}
1562
7faf2bdb 1563#if 0
cb69b4c7 1564/*
1565 * other routines (move these into lib if you need them somewhere else?)
1566 */
1567
1568/*
123abbe1 1569 * iterates through a 0-terminated string of items separated by 'del's.
cb69b4c7 1570 * white space around 'del' is considered to be a part of 'del'
123abbe1 1571 * like strtok, but preserves the source.
cb69b4c7 1572 *
123abbe1 1573 * returns true if next item is found.
1574 * init pos with NULL to start iteration.
cb69b4c7 1575 */
1576static int
1577strListGetItem(const char *str, char del, const char **item, int *ilen, const char **pos)
1578{
1579 size_t len;
1580 assert(str && item && pos);
1581 if (*pos)
2ac76861 1582 if (!**pos) /* end of string */
cb69b4c7 1583 return 0;
1584 else
1585 (*pos)++;
1586 else
1587 *pos = str;
1588
1589 /* skip leading ws (ltrim) */
1590 *pos += xcountws(*pos);
2ac76861 1591 *item = *pos; /* remember item's start */
cb69b4c7 1592 /* find next delimiter */
1593 *pos = strchr(*item, del);
2ac76861 1594 if (!*pos) /* last item */
cb69b4c7 1595 *pos = *item + strlen(*item);
2ac76861 1596 len = *pos - *item; /* *pos points to del or '\0' */
cb69b4c7 1597 /* rtrim */
2ac76861 1598 while (len > 0 && isspace((*item)[len - 1]))
1599 len--;
cb69b4c7 1600 if (ilen)
1601 *ilen = len;
1602 return len > 0;
1603}
1604
1605/* handy to printf prefixes of potentially very long buffers */
1606static const char *
2ac76861 1607getStringPrefix(const char *str)
1608{
cb69b4c7 1609#define SHORT_PREFIX_SIZE 256
1610 LOCAL_ARRAY(char, buf, SHORT_PREFIX_SIZE);
1611 xstrncpy(buf, str, SHORT_PREFIX_SIZE);
1612 return buf;
1613}
7faf2bdb 1614#endif