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