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