]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpHeader.cc
Rename http_hdr_cc_type to HttpHdrCcType and reference it by full qualifier.
[thirdparty/squid.git] / src / HttpHeader.cc
CommitLineData
cb69b4c7 1/*
bde978a6 2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
cb69b4c7 3 *
bbc27441
AJ
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
cb69b4c7 7 */
8
bbc27441
AJ
9/* DEBUG: section 55 HTTP Header */
10
582c2af2 11#include "squid.h"
1da82544 12//#include "base/LookupTable.h" // pulled by HttpHdrCc.h
25f98340 13#include "base64.h"
67679543 14#include "globals.h"
7ebe76de 15#include "HttpHdrCc.h"
582c2af2 16#include "HttpHdrContRange.h"
1da82544 17#include "HttpHdrScTarget.h" // also includes HttpHdrSc.h
8822ebee 18#include "HttpHeader.h"
79cb238d 19#include "HttpHeaderFieldInfo.h"
e1656dc4 20#include "HttpHeaderStat.h"
a5bac1d2 21#include "HttpHeaderTools.h"
0eb49b6d 22#include "MemBuf.h"
8822ebee 23#include "mgr/Registration.h"
582c2af2 24#include "profiler/Profiler.h"
25f98340 25#include "rfc1123.h"
602d9612 26#include "SquidConfig.h"
1da82544 27//#include "SquidString.h" // pulled by HttpHdrCc.h
00a7574e 28#include "StatHist.h"
8822ebee 29#include "Store.h"
28204b3b 30#include "StrList.h"
81a94152 31#include "TimeOrTag.h"
ed6e9fb9 32#include "util.h"
cb69b4c7 33
42865d25
FC
34#include <algorithm>
35
6fda1515
FC
36/* XXX: the whole set of API managing the entries vector should be rethought
37 * after the parse4r-ng effort is complete.
38 */
39
cb69b4c7 40/*
2ac76861 41 * On naming conventions:
26ac0430
AJ
42 *
43 * HTTP/1.1 defines message-header as
44 *
2ac76861 45 * message-header = field-name ":" [ field-value ] CRLF
46 * field-name = token
47 * field-value = *( field-content | LWS )
26ac0430 48 *
2ac76861 49 * HTTP/1.1 does not give a name name a group of all message-headers in a message.
50 * Squid 1.1 seems to refer to that group _plus_ start-line as "headers".
26ac0430 51 *
2ac76861 52 * HttpHeader is an object that represents all message-headers in a message.
53 * HttpHeader does not manage start-line.
26ac0430 54 *
d8b249ef 55 * HttpHeader is implemented as a collection of header "entries".
56 * An entry is a (field_id, field_name, field_value) triplet.
2ac76861 57 */
cb69b4c7 58
cb69b4c7 59/*
60 * local constants and vars
61 */
62
789217a2
FC
63// statistics counters for headers. clients must not allow Http::HdrType::BAD_HDR to be counted
64std::vector<HttpHeaderFieldStat> headerStatsTable(Http::HdrType::ENUM_END);
cb69b4c7 65
cb69b4c7 66/*
67 * headers with field values defined as #(values) in HTTP/1.1
d8b249ef 68 * Headers that are currently not recognized, are commented out.
cb69b4c7 69 */
f53969cc 70static HttpHeaderMask ListHeadersMask; /* set run-time using ListHeadersArr */
789217a2
FC
71static Http::HdrType ListHeadersArr[] = {
72 Http::HdrType::ACCEPT,
73 Http::HdrType::ACCEPT_CHARSET,
74 Http::HdrType::ACCEPT_ENCODING,
75 Http::HdrType::ACCEPT_LANGUAGE,
76 Http::HdrType::ACCEPT_RANGES,
77 Http::HdrType::ALLOW,
78 Http::HdrType::CACHE_CONTROL,
79 Http::HdrType::CONTENT_ENCODING,
80 Http::HdrType::CONTENT_LANGUAGE,
81 Http::HdrType::CONNECTION,
82 Http::HdrType::EXPECT,
83 Http::HdrType::IF_MATCH,
84 Http::HdrType::IF_NONE_MATCH,
85 Http::HdrType::KEY,
86 Http::HdrType::LINK,
87 Http::HdrType::PRAGMA,
88 Http::HdrType::PROXY_CONNECTION,
89 Http::HdrType::PROXY_SUPPORT,
90 Http::HdrType::TRANSFER_ENCODING,
91 Http::HdrType::UPGRADE,
92 Http::HdrType::VARY,
93 Http::HdrType::VIA,
94 Http::HdrType::WARNING,
95 Http::HdrType::WWW_AUTHENTICATE,
96 Http::HdrType::AUTHENTICATION_INFO,
97 Http::HdrType::PROXY_AUTHENTICATION_INFO,
98 /* Http::HdrType::TE, Http::HdrType::TRAILER */
f66a9ef4 99#if X_ACCELERATOR_VARY
789217a2 100 Http::HdrType::HDR_X_ACCELERATOR_VARY,
a22e6cd3
AR
101#endif
102#if USE_ADAPTATION
789217a2 103 Http::HdrType::X_NEXT_SERVICES,
f66a9ef4 104#endif
789217a2
FC
105 Http::HdrType::SURROGATE_CAPABILITY,
106 Http::HdrType::SURROGATE_CONTROL,
107 Http::HdrType::FORWARDED,
108 Http::HdrType::X_FORWARDED_FOR
26ac0430 109};
99edd1c3 110
111/* general-headers */
789217a2
FC
112static Http::HdrType GeneralHeadersArr[] = {
113 Http::HdrType::CACHE_CONTROL,
114 Http::HdrType::CONNECTION,
115 Http::HdrType::DATE,
116 Http::HdrType::FORWARDED,
117 Http::HdrType::X_FORWARDED_FOR,
118 Http::HdrType::MIME_VERSION,
119 Http::HdrType::PRAGMA,
120 Http::HdrType::PROXY_CONNECTION,
121 Http::HdrType::TRANSFER_ENCODING,
122 Http::HdrType::UPGRADE,
123 /* Http::HdrType::TRAILER, */
124 Http::HdrType::VIA,
26ac0430 125};
99edd1c3 126
127/* entity-headers */
789217a2
FC
128static Http::HdrType EntityHeadersArr[] = {
129 Http::HdrType::ALLOW,
130 Http::HdrType::CONTENT_BASE,
131 Http::HdrType::CONTENT_ENCODING,
132 Http::HdrType::CONTENT_LANGUAGE,
133 Http::HdrType::CONTENT_LENGTH,
134 Http::HdrType::CONTENT_LOCATION,
135 Http::HdrType::CONTENT_MD5,
136 Http::HdrType::CONTENT_RANGE,
137 Http::HdrType::CONTENT_TYPE,
138 Http::HdrType::ETAG,
139 Http::HdrType::EXPIRES,
140 Http::HdrType::LAST_MODIFIED,
141 Http::HdrType::LINK,
142 Http::HdrType::OTHER
26ac0430 143};
cb69b4c7 144
16d3204a 145/* request-only headers */
f53969cc 146static HttpHeaderMask RequestHeadersMask; /* set run-time using RequestHeaders */
789217a2
FC
147static Http::HdrType RequestHeadersArr[] = {
148 Http::HdrType::ACCEPT,
149 Http::HdrType::ACCEPT_CHARSET,
150 Http::HdrType::ACCEPT_ENCODING,
151 Http::HdrType::ACCEPT_LANGUAGE,
152 Http::HdrType::AUTHORIZATION,
153 Http::HdrType::EXPECT,
154 Http::HdrType::FROM,
155 Http::HdrType::HOST,
156 Http::HdrType::HTTP2_SETTINGS,
157 Http::HdrType::IF_MATCH,
158 Http::HdrType::IF_MODIFIED_SINCE,
159 Http::HdrType::IF_NONE_MATCH,
160 Http::HdrType::IF_RANGE,
161 Http::HdrType::IF_UNMODIFIED_SINCE,
162 Http::HdrType::MAX_FORWARDS,
163 Http::HdrType::ORIGIN,
164 Http::HdrType::PROXY_AUTHORIZATION,
165 Http::HdrType::RANGE,
166 Http::HdrType::REFERER,
167 Http::HdrType::REQUEST_RANGE,
168 Http::HdrType::TE,
169 Http::HdrType::USER_AGENT,
170 Http::HdrType::SURROGATE_CAPABILITY
16d3204a
AJ
171};
172
173/* reply-only headers */
f53969cc 174static HttpHeaderMask ReplyHeadersMask; /* set run-time using ReplyHeaders */
789217a2
FC
175static Http::HdrType ReplyHeadersArr[] = {
176 Http::HdrType::ACCEPT_ENCODING,
177 Http::HdrType::ACCEPT_RANGES,
178 Http::HdrType::AGE,
179 Http::HdrType::KEY,
180 Http::HdrType::LOCATION,
181 Http::HdrType::PROXY_AUTHENTICATE,
182 Http::HdrType::PUBLIC,
183 Http::HdrType::RETRY_AFTER,
184 Http::HdrType::SERVER,
185 Http::HdrType::SET_COOKIE,
186 Http::HdrType::SET_COOKIE2,
187 Http::HdrType::VARY,
188 Http::HdrType::WARNING,
189 Http::HdrType::WWW_AUTHENTICATE,
190 Http::HdrType::X_CACHE,
191 Http::HdrType::X_CACHE_LOOKUP,
192 Http::HdrType::X_REQUEST_URI,
f66a9ef4 193#if X_ACCELERATOR_VARY
789217a2 194 Http::HdrType::HDR_X_ACCELERATOR_VARY,
a22e6cd3
AR
195#endif
196#if USE_ADAPTATION
789217a2 197 Http::HdrType::X_NEXT_SERVICES,
f66a9ef4 198#endif
789217a2
FC
199 Http::HdrType::X_SQUID_ERROR,
200 Http::HdrType::SURROGATE_CONTROL
26ac0430 201};
cb69b4c7 202
16d3204a 203/* hop-by-hop headers */
dcf3665b 204static HttpHeaderMask HopByHopHeadersMask;
789217a2
FC
205static Http::HdrType HopByHopHeadersArr[] = {
206 Http::HdrType::ALTERNATE_PROTOCOL,
207 Http::HdrType::CONNECTION,
208 Http::HdrType::HTTP2_SETTINGS,
209 Http::HdrType::KEEP_ALIVE,
210 /*Http::HdrType::PROXY_AUTHENTICATE, // removal handled specially for peer login */
211 Http::HdrType::PROXY_AUTHORIZATION,
212 Http::HdrType::TE,
213 Http::HdrType::TRAILER,
214 Http::HdrType::TRANSFER_ENCODING,
215 Http::HdrType::UPGRADE,
216 Http::HdrType::PROXY_CONNECTION
26ac0430 217};
2cdeea82 218
12cf1be2 219/* header accounting */
26735116 220// NP: keep in sync with enum http_hdr_owner_type
26ac0430 221static HttpHeaderStat HttpHeaderStats[] = {
95cc1e3e 222 HttpHeaderStat(/*hoNone*/ "all", NULL),
9f4d4f65 223#if USE_HTCP
95cc1e3e 224 HttpHeaderStat(/*hoHtcpReply*/ "HTCP reply", &ReplyHeadersMask),
9f4d4f65 225#endif
95cc1e3e
FC
226 HttpHeaderStat(/*hoRequest*/ "request", &RequestHeadersMask),
227 HttpHeaderStat(/*hoReply*/ "reply", &ReplyHeadersMask)
26735116
AJ
228#if USE_OPENSSL
229 /* hoErrorDetail */
230#endif
231 /* hoEnd */
26ac0430 232};
9bea1d5b 233static int HttpHeaderStatCount = countof(HttpHeaderStats);
12cf1be2 234
7faf2bdb 235static int HeaderEntryParsedCount = 0;
cb69b4c7 236
12cf1be2 237/*
f734cda2 238 * forward declarations and local routines
12cf1be2 239 */
cb69b4c7 240
f734cda2 241class StoreEntry;
cb69b4c7 242
789217a2 243static void httpHeaderNoteParsedEntry(Http::HdrType id, String const &value, int error);
73b2aa87 244static void httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e);
f734cda2
FC
245/** store report about current header usage and other stats */
246static void httpHeaderStoreReport(StoreEntry * e);
247
cb69b4c7 248/*
249 * Module initialization routines
250 */
251
6b7d87bb
FC
252static void
253httpHeaderRegisterWithCacheManager(void)
254{
8822ebee 255 Mgr::RegisterAction("http_headers",
d9fc6862
A
256 "HTTP Header Statistics",
257 httpHeaderStoreReport, 0, 1);
6b7d87bb
FC
258}
259
cb69b4c7 260void
9bea1d5b 261httpHeaderInitModule(void)
cb69b4c7 262{
9bea1d5b 263 /* check that we have enough space for masks */
789217a2 264 assert(8 * sizeof(HttpHeaderMask) >= Http::HdrType::ENUM_END);
3ff8a1b3
FC
265
266 // check invariant: for each index in headerTable, (int)headerTable[index] = index
92991271
FC
267 for (int i = 0; Http::HeaderTable[i].name; ++i)
268 assert(Http::HeaderTable[i].id == i);
3ff8a1b3 269
383154d7 270 /* create masks. XXX: migrate to std::vector<bool>? */
9bea1d5b 271 httpHeaderMaskInit(&ListHeadersMask, 0);
8abf232c 272 httpHeaderCalcMask(&ListHeadersMask, ListHeadersArr, countof(ListHeadersArr));
62e76326 273
9bea1d5b 274 httpHeaderMaskInit(&ReplyHeadersMask, 0);
8abf232c 275 httpHeaderCalcMask(&ReplyHeadersMask, ReplyHeadersArr, countof(ReplyHeadersArr));
8abf232c 276 httpHeaderCalcMask(&ReplyHeadersMask, GeneralHeadersArr, countof(GeneralHeadersArr));
8abf232c 277 httpHeaderCalcMask(&ReplyHeadersMask, EntityHeadersArr, countof(EntityHeadersArr));
62e76326 278
9bea1d5b 279 httpHeaderMaskInit(&RequestHeadersMask, 0);
8abf232c 280 httpHeaderCalcMask(&RequestHeadersMask, RequestHeadersArr, countof(RequestHeadersArr));
8abf232c 281 httpHeaderCalcMask(&RequestHeadersMask, GeneralHeadersArr, countof(GeneralHeadersArr));
8abf232c 282 httpHeaderCalcMask(&RequestHeadersMask, EntityHeadersArr, countof(EntityHeadersArr));
62e76326 283
c90e2fcb 284 httpHeaderMaskInit(&HopByHopHeadersMask, 0);
dcf3665b
AJ
285 httpHeaderCalcMask(&HopByHopHeadersMask, HopByHopHeadersArr, countof(HopByHopHeadersArr));
286
26735116 287 /* header stats initialized by class constructor */
9bea1d5b 288 assert(HttpHeaderStatCount == hoReply + 1);
16d3204a 289
9bea1d5b 290 /* init dependent modules */
291 httpHdrCcInitModule();
43ae1d95 292 httpHdrScInitModule();
ea391f18
FC
293
294 httpHeaderRegisterWithCacheManager();
62ee09ca 295}
43ae1d95 296
cb69b4c7 297/*
298 * HttpHeader Implementation
299 */
300
75faaa7a 301HttpHeader::HttpHeader() : owner (hoNone), len (0)
cb69b4c7 302{
75faaa7a 303 httpHeaderMaskInit(&mask, 0);
304}
305
5e5fa5b1 306HttpHeader::HttpHeader(const http_hdr_owner_type anOwner): owner(anOwner), len(0)
75faaa7a 307{
02259ff8 308 assert(anOwner > hoNone && anOwner < hoEnd);
efc26e8e 309 debugs(55, 7, "init-ing hdr: " << this << " owner: " << owner);
75faaa7a 310 httpHeaderMaskInit(&mask, 0);
311}
312
5e5fa5b1
AR
313HttpHeader::HttpHeader(const HttpHeader &other): owner(other.owner), len(other.len)
314{
315 httpHeaderMaskInit(&mask, 0);
316 update(&other, NULL); // will update the mask as well
317}
318
75faaa7a 319HttpHeader::~HttpHeader()
320{
519e0948 321 clean();
cb69b4c7 322}
323
5e5fa5b1
AR
324HttpHeader &
325HttpHeader::operator =(const HttpHeader &other)
326{
327 if (this != &other) {
328 // we do not really care, but the caller probably does
329 assert(owner == other.owner);
330 clean();
331 update(&other, NULL); // will update the mask as well
332 len = other.len;
333 }
334 return *this;
335}
336
cb69b4c7 337void
519e0948 338HttpHeader::clean()
cb69b4c7 339{
9bea1d5b 340
02259ff8 341 assert(owner > hoNone && owner < hoEnd);
efc26e8e 342 debugs(55, 7, "cleaning hdr: " << this << " owner: " << owner);
9bea1d5b 343
3a9cf2d5 344 PROF_start(HttpHeaderClean);
345
0de73a38 346 if (owner <= hoReply) {
c594d76c
A
347 /*
348 * An unfortunate bug. The entries array is initialized
349 * such that count is set to zero. httpHeaderClean() seems to
350 * be called both when 'hdr' is created, and destroyed. Thus,
351 * we accumulate a large number of zero counts for 'hdr' before
352 * it is ever used. Can't think of a good way to fix it, except
353 * adding a state variable that indicates whether or not 'hdr'
354 * has been used. As a hack, just never count zero-sized header
355 * arrays.
356 */
4c9eadc2
FC
357 if (!entries.empty())
358 HttpHeaderStats[owner].hdrUCountDistr.count(entries.size());
51328da8 359
95dc7ff4 360 ++ HttpHeaderStats[owner].destroyedCount;
51328da8 361
4c9eadc2 362 HttpHeaderStats[owner].busyDestroyedCount += entries.size() > 0;
0de73a38 363 } // if (owner <= hoReply)
62e76326 364
1da82544
FC
365 for(HttpHeaderEntry *e : entries) {
366 if (e == nullptr)
6fda1515 367 continue;
1da82544 368 if (!Http::any_valid_header(e->id)) {
6fda1515 369 debugs(55, DBG_CRITICAL, "BUG: invalid entry (" << e->id << "). Ignored.");
c594d76c
A
370 } else {
371 if (owner <= hoReply)
f30f7998 372 HttpHeaderStats[owner].fieldTypeDistr.count(e->id);
c594d76c 373 delete e;
62e76326 374 }
c594d76c 375 }
6fda1515 376
c33a88ca 377 entries.clear();
519e0948 378 httpHeaderMaskInit(&mask, 0);
5e5fa5b1 379 len = 0;
3a9cf2d5 380 PROF_stop(HttpHeaderClean);
cb69b4c7 381}
382
2246b732 383/* append entries (also see httpHeaderUpdate) */
384void
a9925b40 385HttpHeader::append(const HttpHeader * src)
2246b732 386{
9bea1d5b 387 const HttpHeaderEntry *e;
388 HttpHeaderPos pos = HttpHeaderInitPos;
a9925b40 389 assert(src);
390 assert(src != this);
efc26e8e 391 debugs(55, 7, "appending hdr: " << this << " += " << src);
9bea1d5b 392
a9925b40 393 while ((e = src->getEntry(&pos))) {
eede25e7 394 addEntry(e->clone());
2246b732 395 }
396}
397
d8b249ef 398/* use fresh entries to replace old ones */
cb69b4c7 399void
9bea1d5b 400httpHeaderUpdate(HttpHeader * old, const HttpHeader * fresh, const HttpHeaderMask * denied_mask)
924f73bc 401{
402 assert (old);
403 old->update (fresh, denied_mask);
404}
405
406void
407HttpHeader::update (HttpHeader const *fresh, HttpHeaderMask const *denied_mask)
cb69b4c7 408{
9bea1d5b 409 const HttpHeaderEntry *e;
410 HttpHeaderPos pos = HttpHeaderInitPos;
a9925b40 411 assert(fresh);
924f73bc 412 assert(this != fresh);
9bea1d5b 413
a9925b40 414 while ((e = fresh->getEntry(&pos))) {
789217a2 415 /* deny bad guys (ok to check for Http::HdrType::OTHER) here */
62e76326 416
417 if (denied_mask && CBIT_TEST(*denied_mask, e->id))
418 continue;
419
789217a2 420 if (e->id != Http::HdrType::OTHER)
ba9fb01d 421 delById(e->id);
422 else
9d7a89a5 423 delByName(e->name.termedBuf());
9917847f 424 }
425
426 pos = HttpHeaderInitPos;
427 while ((e = fresh->getEntry(&pos))) {
789217a2 428 /* deny bad guys (ok to check for Http::HdrType::OTHER) here */
9917847f 429
430 if (denied_mask && CBIT_TEST(*denied_mask, e->id))
431 continue;
432
92991271 433 debugs(55, 7, "Updating header '" << Http::HeaderTable[e->id].name << "' in cached entry");
62e76326 434
eede25e7 435 addEntry(e->clone());
cb69b4c7 436 }
cb69b4c7 437}
438
439/* just handy in parsing: resets and returns false */
9f4d4f65 440int
a9925b40 441HttpHeader::reset()
2ac76861 442{
a9925b40 443 clean();
9bea1d5b 444 return 0;
cb69b4c7 445}
446
cb69b4c7 447int
784619e6 448HttpHeader::parse(const char *header_start, size_t hdrLen)
cb69b4c7 449{
52d3f198 450 const char *field_ptr = header_start;
784619e6 451 const char *header_end = header_start + hdrLen; // XXX: remove
52d3f198 452 HttpHeaderEntry *e, *e2;
56cd29e6 453 int warnOnError = (Config.onoff.relaxed_header_parser <= 0 ? DBG_IMPORTANT : 2);
9bea1d5b 454
3a9cf2d5 455 PROF_start(HttpHeaderParse);
456
9bea1d5b 457 assert(header_start && header_end);
81858ebc 458 debugs(55, 7, "parsing hdr: (" << this << ")" << std::endl << getStringPrefix(header_start, hdrLen));
95dc7ff4 459 ++ HttpHeaderStats[owner].parsedCount;
62e76326 460
1d8a896b 461 char *nulpos;
784619e6 462 if ((nulpos = (char*)memchr(header_start, '\0', hdrLen))) {
e0236918 463 debugs(55, DBG_IMPORTANT, "WARNING: HTTP header contains NULL characters {" <<
81858ebc 464 getStringPrefix(header_start, nulpos-header_start) << "}\nNULL\n{" << getStringPrefix(nulpos+1, hdrLen-(nulpos-header_start)-1));
6d7bc97b
AJ
465 PROF_stop(HttpHeaderParse);
466 return reset();
52d3f198 467 }
468
469 /* common format headers are "<name>:[ws]<value>" lines delimited by <CRLF>.
470 * continuation lines start with a (single) space or tab */
471 while (field_ptr < header_end) {
472 const char *field_start = field_ptr;
62e76326 473 const char *field_end;
62e76326 474
475 do {
52d3f198 476 const char *this_line = field_ptr;
477 field_ptr = (const char *)memchr(field_ptr, '\n', header_end - field_ptr);
478
6d7bc97b
AJ
479 if (!field_ptr) {
480 // missing <LF>
481 PROF_stop(HttpHeaderParse);
482 return reset();
483 }
52d3f198 484
485 field_end = field_ptr;
486
f53969cc 487 ++field_ptr; /* Move to next line */
52d3f198 488
489 if (field_end > this_line && field_end[-1] == '\r') {
f53969cc 490 --field_end; /* Ignore CR LF */
52d3f198 491
e10272fc
AR
492 if (owner == hoRequest && field_end > this_line) {
493 bool cr_only = true;
494 for (const char *p = this_line; p < field_end && cr_only; ++p) {
495 if (*p != '\r')
496 cr_only = false;
497 }
498 if (cr_only) {
6e09ed7b 499 debugs(55, DBG_IMPORTANT, "SECURITY WARNING: Rejecting HTTP request with a CR+ "
e10272fc 500 "header field to prevent request smuggling attacks: {" <<
81858ebc 501 getStringPrefix(header_start, hdrLen) << "}");
6d7bc97b
AJ
502 PROF_stop(HttpHeaderParse);
503 return reset();
e10272fc 504 }
52d3f198 505 }
506 }
507
508 /* Barf on stray CR characters */
509 if (memchr(this_line, '\r', field_end - this_line)) {
6e09ed7b 510 debugs(55, warnOnError, "WARNING: suspicious CR characters in HTTP header {" <<
81858ebc 511 getStringPrefix(field_start, field_end-field_start) << "}");
52d3f198 512
513 if (Config.onoff.relaxed_header_parser) {
f53969cc 514 char *p = (char *) this_line; /* XXX Warning! This destroys original header content and violates specifications somewhat */
52d3f198 515
a38ec4b1
FC
516 while ((p = (char *)memchr(p, '\r', field_end - p)) != NULL) {
517 *p = ' ';
518 ++p;
519 }
6d7bc97b
AJ
520 } else {
521 PROF_stop(HttpHeaderParse);
522 return reset();
523 }
52d3f198 524 }
525
526 if (this_line + 1 == field_end && this_line > field_start) {
6e09ed7b 527 debugs(55, warnOnError, "WARNING: Blank continuation line in HTTP header {" <<
81858ebc 528 getStringPrefix(header_start, hdrLen) << "}");
6d7bc97b
AJ
529 PROF_stop(HttpHeaderParse);
530 return reset();
52d3f198 531 }
532 } while (field_ptr < header_end && (*field_ptr == ' ' || *field_ptr == '\t'));
533
534 if (field_start == field_end) {
535 if (field_ptr < header_end) {
6e09ed7b 536 debugs(55, warnOnError, "WARNING: unparseable HTTP header field near {" <<
81858ebc 537 getStringPrefix(field_start, hdrLen-(field_start-header_start)) << "}");
6d7bc97b
AJ
538 PROF_stop(HttpHeaderParse);
539 return reset();
52d3f198 540 }
541
f53969cc 542 break; /* terminating blank line */
52d3f198 543 }
62e76326 544
cdce6c61 545 if ((e = HttpHeaderEntry::parse(field_start, field_end)) == NULL) {
6e09ed7b 546 debugs(55, warnOnError, "WARNING: unparseable HTTP header field {" <<
81858ebc
AJ
547 getStringPrefix(field_start, field_end-field_start) << "}");
548 debugs(55, warnOnError, " in {" << getStringPrefix(header_start, hdrLen) << "}");
52d3f198 549
550 if (Config.onoff.relaxed_header_parser)
551 continue;
6e09ed7b 552
6d7bc97b
AJ
553 PROF_stop(HttpHeaderParse);
554 return reset();
47ac2ebe 555 }
62e76326 556
1da82544 557 if (e->id == Http::HdrType::CONTENT_LENGTH && (e2 = findEntry(e->id)) != nullptr) {
9d7a89a5 558 if (e->value != e2->value) {
3e62bd58 559 int64_t l1, l2;
6e09ed7b 560 debugs(55, warnOnError, "WARNING: found two conflicting content-length headers in {" <<
81858ebc 561 getStringPrefix(header_start, hdrLen) << "}");
9331e4b4 562
563 if (!Config.onoff.relaxed_header_parser) {
eede25e7 564 delete e;
6d7bc97b
AJ
565 PROF_stop(HttpHeaderParse);
566 return reset();
9331e4b4 567 }
568
9d7a89a5 569 if (!httpHeaderParseOffset(e->value.termedBuf(), &l1)) {
e0236918 570 debugs(55, DBG_IMPORTANT, "WARNING: Unparseable content-length '" << e->value << "'");
eede25e7 571 delete e;
9331e4b4 572 continue;
9d7a89a5 573 } else if (!httpHeaderParseOffset(e2->value.termedBuf(), &l2)) {
e0236918 574 debugs(55, DBG_IMPORTANT, "WARNING: Unparseable content-length '" << e2->value << "'");
a9925b40 575 delById(e2->id);
9331e4b4 576 } else if (l1 > l2) {
a9925b40 577 delById(e2->id);
9331e4b4 578 } else {
eede25e7 579 delete e;
9331e4b4 580 continue;
581 }
52d3f198 582 } else {
6e09ed7b
AJ
583 debugs(55, warnOnError, "NOTICE: found double content-length header");
584 delete e;
b3123159 585
6e09ed7b 586 if (Config.onoff.relaxed_header_parser)
b3123159 587 continue;
6e09ed7b 588
6d7bc97b
AJ
589 PROF_stop(HttpHeaderParse);
590 return reset();
52d3f198 591 }
592 }
62e76326 593
789217a2 594 if (e->id == Http::HdrType::OTHER && stringHasWhitespace(e->name.termedBuf())) {
6e09ed7b 595 debugs(55, warnOnError, "WARNING: found whitespace in HTTP header name {" <<
81858ebc 596 getStringPrefix(field_start, field_end-field_start) << "}");
47ac2ebe 597
52d3f198 598 if (!Config.onoff.relaxed_header_parser) {
eede25e7 599 delete e;
6d7bc97b
AJ
600 PROF_stop(HttpHeaderParse);
601 return reset();
52d3f198 602 }
603 }
62e76326 604
a9925b40 605 addEntry(e);
cb69b4c7 606 }
62e76326 607
c3d0ba0c
AR
608 if (chunked()) {
609 // RFC 2616 section 4.4: ignore Content-Length with Transfer-Encoding
789217a2 610 delById(Http::HdrType::CONTENT_LENGTH);
c3d0ba0c
AR
611 }
612
3a9cf2d5 613 PROF_stop(HttpHeaderParse);
f53969cc 614 return 1; /* even if no fields where found, it is a valid header */
cb69b4c7 615}
616
99edd1c3 617/* packs all the entries using supplied packer */
cb69b4c7 618void
17802cf1 619HttpHeader::packInto(Packable * p, bool mask_sensitive_info) const
cb69b4c7 620{
9bea1d5b 621 HttpHeaderPos pos = HttpHeaderInitPos;
622 const HttpHeaderEntry *e;
a9925b40 623 assert(p);
3cc0f4e7
AR
624 debugs(55, 7, this << " into " << p <<
625 (mask_sensitive_info ? " while masking" : ""));
9bea1d5b 626 /* pack all entries one by one */
d8f6c79c
FC
627 while ((e = getEntry(&pos))) {
628 if (!mask_sensitive_info) {
629 e->packInto(p);
630 continue;
631 }
3cc0f4e7
AR
632
633 bool maskThisEntry = false;
d8f6c79c 634 switch (e->id) {
789217a2
FC
635 case Http::HdrType::AUTHORIZATION:
636 case Http::HdrType::PROXY_AUTHORIZATION:
3cc0f4e7 637 maskThisEntry = true;
d3823ea7 638 break;
3cc0f4e7 639
789217a2
FC
640 case Http::HdrType::FTP_ARGUMENTS:
641 if (const HttpHeaderEntry *cmd = findEntry(Http::HdrType::FTP_COMMAND))
3cc0f4e7
AR
642 maskThisEntry = (cmd->value == "PASS");
643 break;
644
d3823ea7 645 default:
d3823ea7 646 break;
d8f6c79c 647 }
3cc0f4e7 648 if (maskThisEntry) {
785b508d
AJ
649 p->append(e->name.rawBuf(), e->name.size());
650 p->append(": ** NOT DISPLAYED **\r\n", 23);
3cc0f4e7
AR
651 } else {
652 e->packInto(p);
653 }
654
d8f6c79c 655 }
35b88ed2 656 /* Pack in the "special" entries */
657
658 /* Cache-Control */
cb69b4c7 659}
660
661/* returns next valid entry */
99edd1c3 662HttpHeaderEntry *
a9925b40 663HttpHeader::getEntry(HttpHeaderPos * pos) const
cb69b4c7 664{
a9925b40 665 assert(pos);
c0abe2ec 666 assert(*pos >= HttpHeaderInitPos && *pos < static_cast<ssize_t>(entries.size()));
62e76326 667
c0abe2ec 668 for (++(*pos); *pos < static_cast<ssize_t>(entries.size()); ++(*pos)) {
4c9eadc2 669 if (entries[*pos])
c0abe2ec 670 return static_cast<HttpHeaderEntry*>(entries[*pos]);
cb69b4c7 671 }
62e76326 672
9bea1d5b 673 return NULL;
cb69b4c7 674}
675
676/*
26ac0430 677 * returns a pointer to a specified entry if any
d8b249ef 678 * note that we return one entry so it does not make much sense to ask for
679 * "list" headers
cb69b4c7 680 */
de336bbe 681HttpHeaderEntry *
789217a2 682HttpHeader::findEntry(Http::HdrType id) const
cb69b4c7 683{
9bea1d5b 684 HttpHeaderPos pos = HttpHeaderInitPos;
685 HttpHeaderEntry *e;
229eb96e 686 assert(any_valid_header(id));
9bea1d5b 687 assert(!CBIT_TEST(ListHeadersMask, id));
688
689 /* check mask first */
62e76326 690
a9925b40 691 if (!CBIT_TEST(mask, id))
62e76326 692 return NULL;
693
9bea1d5b 694 /* looks like we must have it, do linear search */
a9925b40 695 while ((e = getEntry(&pos))) {
62e76326 696 if (e->id == id)
697 return e;
cb69b4c7 698 }
62e76326 699
9bea1d5b 700 /* hm.. we thought it was there, but it was not found */
701 assert(0);
62e76326 702
f53969cc 703 return NULL; /* not reached */
cb69b4c7 704}
705
a622fff0 706/*
707 * same as httpHeaderFindEntry
708 */
a9925b40 709HttpHeaderEntry *
789217a2 710HttpHeader::findLastEntry(Http::HdrType id) const
a622fff0 711{
9bea1d5b 712 HttpHeaderPos pos = HttpHeaderInitPos;
713 HttpHeaderEntry *e;
714 HttpHeaderEntry *result = NULL;
229eb96e 715 assert(any_valid_header(id));
9bea1d5b 716 assert(!CBIT_TEST(ListHeadersMask, id));
717
718 /* check mask first */
62e76326 719
a9925b40 720 if (!CBIT_TEST(mask, id))
62e76326 721 return NULL;
722
9bea1d5b 723 /* looks like we must have it, do linear search */
a9925b40 724 while ((e = getEntry(&pos))) {
62e76326 725 if (e->id == id)
726 result = e;
a622fff0 727 }
62e76326 728
f53969cc 729 assert(result); /* must be there! */
9bea1d5b 730 return result;
a622fff0 731}
732
cb69b4c7 733/*
26ac0430 734 * deletes all fields with a given name if any, returns #fields deleted;
cb69b4c7 735 */
2ac76861 736int
a9925b40 737HttpHeader::delByName(const char *name)
cb69b4c7 738{
9bea1d5b 739 int count = 0;
740 HttpHeaderPos pos = HttpHeaderInitPos;
741 HttpHeaderEntry *e;
f53969cc 742 httpHeaderMaskInit(&mask, 0); /* temporal inconsistency */
efc26e8e 743 debugs(55, 9, "deleting '" << name << "' fields in hdr " << this);
62e76326 744
a9925b40 745 while ((e = getEntry(&pos))) {
30abd221 746 if (!e->name.caseCmp(name))
ba9fb01d 747 delAt(pos, count);
748 else
a9925b40 749 CBIT_SET(mask, e->id);
cb69b4c7 750 }
62e76326 751
9bea1d5b 752 return count;
cb69b4c7 753}
754
2246b732 755/* deletes all entries with a given id, returns the #entries deleted */
756int
789217a2 757HttpHeader::delById(Http::HdrType id)
d8b249ef 758{
9bea1d5b 759 int count = 0;
760 HttpHeaderPos pos = HttpHeaderInitPos;
761 HttpHeaderEntry *e;
efc26e8e 762 debugs(55, 8, this << " del-by-id " << id);
229eb96e 763 assert(any_valid_header(id));
789217a2 764 assert(id != Http::HdrType::OTHER); /* does not make sense */
62e76326 765
a9925b40 766 if (!CBIT_TEST(mask, id))
62e76326 767 return 0;
768
a9925b40 769 while ((e = getEntry(&pos))) {
ba9fb01d 770 if (e->id == id)
771 delAt(pos, count);
d8b249ef 772 }
62e76326 773
a9925b40 774 CBIT_CLR(mask, id);
9bea1d5b 775 assert(count);
776 return count;
d8b249ef 777}
d8b249ef 778
cb69b4c7 779/*
780 * deletes an entry at pos and leaves a gap; leaving a gap makes it
781 * possible to iterate(search) and delete fields at the same time
ba9fb01d 782 * NOTE: Does not update the header mask. Caller must follow up with
783 * a call to refreshMask() if headers_deleted was incremented.
cb69b4c7 784 */
2246b732 785void
ba9fb01d 786HttpHeader::delAt(HttpHeaderPos pos, int &headers_deleted)
cb69b4c7 787{
9bea1d5b 788 HttpHeaderEntry *e;
c0abe2ec
FC
789 assert(pos >= HttpHeaderInitPos && pos < static_cast<ssize_t>(entries.size()));
790 e = static_cast<HttpHeaderEntry*>(entries[pos]);
4c9eadc2 791 entries[pos] = NULL;
9bea1d5b 792 /* decrement header length, allow for ": " and crlf */
a9925b40 793 len -= e->name.size() + 2 + e->value.size() + 2;
794 assert(len >= 0);
eede25e7 795 delete e;
ba9fb01d 796 ++headers_deleted;
cb69b4c7 797}
798
394499bd 799/*
800 * Compacts the header storage
801 */
802void
803HttpHeader::compact()
804{
6e1410f9
FC
805 // TODO: optimize removal, or possibly make it so that's not needed.
806 std::vector<HttpHeaderEntry *>::iterator newend;
807 newend = std::remove(entries.begin(), entries.end(), static_cast<HttpHeaderEntry *>(NULL));
808 entries.resize(newend-entries.begin());
394499bd 809}
810
ba9fb01d 811/*
812 * Refreshes the header mask. Required after delAt() calls.
813 */
814void
815HttpHeader::refreshMask()
816{
817 httpHeaderMaskInit(&mask, 0);
818 debugs(55, 7, "refreshing the mask in hdr " << this);
819 HttpHeaderPos pos = HttpHeaderInitPos;
820 while (HttpHeaderEntry *e = getEntry(&pos)) {
26ac0430 821 CBIT_SET(mask, e->id);
ba9fb01d 822 }
823}
99edd1c3 824
62e76326 825/* appends an entry;
eede25e7 826 * does not call e->clone() so one should not reuse "*e"
cb69b4c7 827 */
99edd1c3 828void
a9925b40 829HttpHeader::addEntry(HttpHeaderEntry * e)
cb69b4c7 830{
a9925b40 831 assert(e);
1da82544 832 assert(any_HdrType_enum_value(e->id));
59c50530 833 assert(e->name.size());
9bea1d5b 834
4c9eadc2 835 debugs(55, 7, this << " adding entry: " << e->id << " at " << entries.size());
62e76326 836
789217a2 837 if (e->id != Http::HdrType::BAD_HDR) {
73b2aa87
FC
838 if (CBIT_TEST(mask, e->id)) {
839 ++ headerStatsTable[e->id].repCount;
840 } else {
841 CBIT_SET(mask, e->id);
842 }
b88a45ed 843 }
62e76326 844
a9925b40 845 entries.push_back(e);
62e76326 846
9bea1d5b 847 /* increment header length, allow for ": " and crlf */
a9925b40 848 len += e->name.size() + 2 + e->value.size() + 2;
cb69b4c7 849}
850
bbe58ab5 851/* inserts an entry;
eede25e7 852 * does not call e->clone() so one should not reuse "*e"
bbe58ab5 853 */
854void
a9925b40 855HttpHeader::insertEntry(HttpHeaderEntry * e)
bbe58ab5 856{
a9925b40 857 assert(e);
229eb96e 858 assert(any_valid_header(e->id));
bbe58ab5 859
f9514ae8 860 debugs(55, 7, this << " adding entry: " << e->id << " at " << entries.size());
bbe58ab5 861
789217a2 862 // Http::HdrType::BAD_HDR is filtered out by assert_any_valid_header
b88a45ed 863 if (CBIT_TEST(mask, e->id)) {
73b2aa87 864 ++ headerStatsTable[e->id].repCount;
b88a45ed 865 } else {
a9925b40 866 CBIT_SET(mask, e->id);
b88a45ed 867 }
bbe58ab5 868
42865d25 869 entries.insert(entries.begin(),e);
bbe58ab5 870
871 /* increment header length, allow for ": " and crlf */
a9925b40 872 len += e->name.size() + 2 + e->value.size() + 2;
bbe58ab5 873}
874
35b88ed2 875bool
789217a2 876HttpHeader::getList(Http::HdrType id, String *s) const
35b88ed2 877{
878 HttpHeaderEntry *e;
879 HttpHeaderPos pos = HttpHeaderInitPos;
efc26e8e 880 debugs(55, 9, this << " joining for id " << id);
35b88ed2 881 /* only fields from ListHeaders array can be "listed" */
882 assert(CBIT_TEST(ListHeadersMask, id));
883
884 if (!CBIT_TEST(mask, id))
885 return false;
886
887 while ((e = getEntry(&pos))) {
888 if (e->id == id)
9d7a89a5 889 strListAdd(s, e->value.termedBuf(), ',');
35b88ed2 890 }
891
892 /*
893 * note: we might get an empty (size==0) string if there was an "empty"
894 * header. This results in an empty length String, which may have a NULL
895 * buffer.
896 */
897 /* temporary warning: remove it? (Is it useful for diagnostics ?) */
898 if (!s->size())
92991271 899 debugs(55, 3, "empty list header: " << Http::HeaderTable[id].name << "(" << id << ")");
35b88ed2 900 else
901 debugs(55, 6, this << ": joined for id " << id << ": " << s);
902
903 return true;
904}
905
99edd1c3 906/* return a list of entries with the same id separated by ',' and ws */
30abd221 907String
789217a2 908HttpHeader::getList(Http::HdrType id) const
cb69b4c7 909{
9bea1d5b 910 HttpHeaderEntry *e;
911 HttpHeaderPos pos = HttpHeaderInitPos;
efc26e8e 912 debugs(55, 9, this << "joining for id " << id);
9bea1d5b 913 /* only fields from ListHeaders array can be "listed" */
914 assert(CBIT_TEST(ListHeadersMask, id));
62e76326 915
a9925b40 916 if (!CBIT_TEST(mask, id))
30abd221 917 return String();
650c4b88 918
30abd221 919 String s;
62e76326 920
a9925b40 921 while ((e = getEntry(&pos))) {
62e76326 922 if (e->id == id)
9d7a89a5 923 strListAdd(&s, e->value.termedBuf(), ',');
d35b9a94 924 }
62e76326 925
9bea1d5b 926 /*
948078e3 927 * note: we might get an empty (size==0) string if there was an "empty"
928 * header. This results in an empty length String, which may have a NULL
929 * buffer.
9bea1d5b 930 */
948078e3 931 /* temporary warning: remove it? (Is it useful for diagnostics ?) */
528b2c61 932 if (!s.size())
92991271 933 debugs(55, 3, "empty list header: " << Http::HeaderTable[id].name << "(" << id << ")");
948078e3 934 else
935 debugs(55, 6, this << ": joined for id " << id << ": " << s);
62e76326 936
9bea1d5b 937 return s;
cb69b4c7 938}
939
f66a9ef4 940/* return a string or list of entries with the same id separated by ',' and ws */
30abd221 941String
789217a2 942HttpHeader::getStrOrList(Http::HdrType id) const
f66a9ef4 943{
c7327fa0 944 HttpHeaderEntry *e;
9bea1d5b 945
946 if (CBIT_TEST(ListHeadersMask, id))
a9925b40 947 return getList(id);
62e76326 948
a9925b40 949 if ((e = findEntry(id)))
62e76326 950 return e->value;
951
30abd221 952 return String();
f66a9ef4 953}
954
955/*
b2c44718 956 * Returns the value of the specified header and/or an undefined String.
f66a9ef4 957 */
30abd221 958String
a9925b40 959HttpHeader::getByName(const char *name) const
b2c44718
AR
960{
961 String result;
962 // ignore presence: return undefined string if an empty header is present
963 (void)getByNameIfPresent(name, result);
964 return result;
965}
966
967bool
968HttpHeader::getByNameIfPresent(const char *name, String &result) const
f66a9ef4 969{
789217a2 970 Http::HdrType id;
9bea1d5b 971 HttpHeaderPos pos = HttpHeaderInitPos;
972 HttpHeaderEntry *e;
9bea1d5b 973
9bea1d5b 974 assert(name);
975
976 /* First try the quick path */
92991271 977 id = Http::HeaderLookupTable.lookup(SBuf(name));
62e76326 978
789217a2 979 if (id != Http::HdrType::BAD_HDR) {
b2c44718
AR
980 if (!has(id))
981 return false;
982 result = getStrOrList(id);
983 return true;
984 }
650c4b88 985
9bea1d5b 986 /* Sorry, an unknown header name. Do linear search */
b2c44718 987 bool found = false;
a9925b40 988 while ((e = getEntry(&pos))) {
789217a2 989 if (e->id == Http::HdrType::OTHER && e->name.caseCmp(name) == 0) {
b2c44718 990 found = true;
9d7a89a5 991 strListAdd(&result, e->value.termedBuf(), ',');
62e76326 992 }
f66a9ef4 993 }
62e76326 994
b2c44718 995 return found;
f66a9ef4 996}
cb69b4c7 997
14b463aa 998/*
372fdfbf 999 * Returns a the value of the specified list member, if any.
14b463aa 1000 */
30abd221 1001String
a9925b40 1002HttpHeader::getByNameListMember(const char *name, const char *member, const char separator) const
14b463aa 1003{
30abd221 1004 String header;
14b463aa 1005 const char *pos = NULL;
1006 const char *item;
1007 int ilen;
1008 int mlen = strlen(member);
1009
14b463aa 1010 assert(name);
1011
a9925b40 1012 header = getByName(name);
14b463aa 1013
30abd221 1014 String result;
650c4b88 1015
14b463aa 1016 while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
62e76326 1017 if (strncmp(item, member, mlen) == 0 && item[mlen] == '=') {
1018 result.append(item + mlen + 1, ilen - mlen - 1);
1019 break;
1020 }
14b463aa 1021 }
62e76326 1022
14b463aa 1023 return result;
1024}
1025
1026/*
1027 * returns a the value of the specified list member, if any.
1028 */
30abd221 1029String
789217a2 1030HttpHeader::getListMember(Http::HdrType id, const char *member, const char separator) const
14b463aa 1031{
30abd221 1032 String header;
14b463aa 1033 const char *pos = NULL;
1034 const char *item;
1035 int ilen;
1036 int mlen = strlen(member);
1037
229eb96e 1038 assert(any_valid_header(id));
14b463aa 1039
a9925b40 1040 header = getStrOrList(id);
30abd221 1041 String result;
14b463aa 1042
1043 while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
62e76326 1044 if (strncmp(item, member, mlen) == 0 && item[mlen] == '=') {
1045 result.append(item + mlen + 1, ilen - mlen - 1);
1046 break;
1047 }
14b463aa 1048 }
62e76326 1049
30abd221 1050 header.clean();
14b463aa 1051 return result;
1052}
1053
cb69b4c7 1054/* test if a field is present */
2ac76861 1055int
789217a2 1056HttpHeader::has(Http::HdrType id) const
cb69b4c7 1057{
229eb96e 1058 assert(any_valid_header(id));
789217a2 1059 assert(id != Http::HdrType::OTHER);
efc26e8e 1060 debugs(55, 9, this << " lookup for " << id);
a9925b40 1061 return CBIT_TEST(mask, id);
cb69b4c7 1062}
1063
1064void
789217a2 1065HttpHeader::putInt(Http::HdrType id, int number)
cb69b4c7 1066{
229eb96e 1067 assert(any_valid_header(id));
92991271 1068 assert(Http::HeaderTable[id].type == Http::HdrFieldType::ftInt); /* must be of an appropriate type */
9bea1d5b 1069 assert(number >= 0);
eede25e7 1070 addEntry(new HttpHeaderEntry(id, NULL, xitoa(number)));
cb69b4c7 1071}
1072
47f6e231 1073void
789217a2 1074HttpHeader::putInt64(Http::HdrType id, int64_t number)
47f6e231 1075{
229eb96e 1076 assert(any_valid_header(id));
92991271 1077 assert(Http::HeaderTable[id].type == Http::HdrFieldType::ftInt64); /* must be of an appropriate type */
47f6e231 1078 assert(number >= 0);
1079 addEntry(new HttpHeaderEntry(id, NULL, xint64toa(number)));
1080}
1081
cb69b4c7 1082void
789217a2 1083HttpHeader::putTime(Http::HdrType id, time_t htime)
cb69b4c7 1084{
229eb96e 1085 assert(any_valid_header(id));
92991271 1086 assert(Http::HeaderTable[id].type == Http::HdrFieldType::ftDate_1123); /* must be of an appropriate type */
a1d6870f 1087 assert(htime >= 0);
eede25e7 1088 addEntry(new HttpHeaderEntry(id, NULL, mkrfc1123(htime)));
cb69b4c7 1089}
2ac76861 1090
bbe58ab5 1091void
789217a2 1092HttpHeader::insertTime(Http::HdrType id, time_t htime)
bbe58ab5 1093{
229eb96e 1094 assert(any_valid_header(id));
92991271 1095 assert(Http::HeaderTable[id].type == Http::HdrFieldType::ftDate_1123); /* must be of an appropriate type */
bbe58ab5 1096 assert(htime >= 0);
eede25e7 1097 insertEntry(new HttpHeaderEntry(id, NULL, mkrfc1123(htime)));
bbe58ab5 1098}
1099
cb69b4c7 1100void
789217a2 1101HttpHeader::putStr(Http::HdrType id, const char *str)
cb69b4c7 1102{
229eb96e 1103 assert(any_valid_header(id));
92991271 1104 assert(Http::HeaderTable[id].type == Http::HdrFieldType::ftStr); /* must be of an appropriate type */
9bea1d5b 1105 assert(str);
eede25e7 1106 addEntry(new HttpHeaderEntry(id, NULL, str));
cb69b4c7 1107}
1108
63259c34 1109void
a9925b40 1110HttpHeader::putAuth(const char *auth_scheme, const char *realm)
63259c34 1111{
a9925b40 1112 assert(auth_scheme && realm);
789217a2 1113 httpHeaderPutStrf(this, Http::HdrType::WWW_AUTHENTICATE, "%s realm=\"%s\"", auth_scheme, realm);
63259c34 1114}
1115
99edd1c3 1116void
a9925b40 1117HttpHeader::putCc(const HttpHdrCc * cc)
99edd1c3 1118{
a9925b40 1119 assert(cc);
9bea1d5b 1120 /* remove old directives if any */
789217a2 1121 delById(Http::HdrType::CACHE_CONTROL);
9bea1d5b 1122 /* pack into mb */
10201568 1123 MemBuf mb;
2fe7eff9 1124 mb.init();
10201568 1125 cc->packInto(&mb);
9bea1d5b 1126 /* put */
789217a2 1127 addEntry(new HttpHeaderEntry(Http::HdrType::CACHE_CONTROL, NULL, mb.buf));
9bea1d5b 1128 /* cleanup */
2fe7eff9 1129 mb.clean();
99edd1c3 1130}
1131
d192d11f 1132void
a9925b40 1133HttpHeader::putContRange(const HttpHdrContRange * cr)
d192d11f 1134{
a9925b40 1135 assert(cr);
9bea1d5b 1136 /* remove old directives if any */
789217a2 1137 delById(Http::HdrType::CONTENT_RANGE);
9bea1d5b 1138 /* pack into mb */
10201568 1139 MemBuf mb;
2fe7eff9 1140 mb.init();
10201568 1141 httpHdrContRangePackInto(cr, &mb);
9bea1d5b 1142 /* put */
789217a2 1143 addEntry(new HttpHeaderEntry(Http::HdrType::CONTENT_RANGE, NULL, mb.buf));
9bea1d5b 1144 /* cleanup */
2fe7eff9 1145 mb.clean();
d192d11f 1146}
1147
1148void
a9925b40 1149HttpHeader::putRange(const HttpHdrRange * range)
d192d11f 1150{
a9925b40 1151 assert(range);
9bea1d5b 1152 /* remove old directives if any */
789217a2 1153 delById(Http::HdrType::RANGE);
9bea1d5b 1154 /* pack into mb */
10201568 1155 MemBuf mb;
2fe7eff9 1156 mb.init();
10201568 1157 range->packInto(&mb);
9bea1d5b 1158 /* put */
789217a2 1159 addEntry(new HttpHeaderEntry(Http::HdrType::RANGE, NULL, mb.buf));
9bea1d5b 1160 /* cleanup */
2fe7eff9 1161 mb.clean();
d192d11f 1162}
1163
43ae1d95 1164void
a9925b40 1165HttpHeader::putSc(HttpHdrSc *sc)
43ae1d95 1166{
a9925b40 1167 assert(sc);
43ae1d95 1168 /* remove old directives if any */
789217a2 1169 delById(Http::HdrType::SURROGATE_CONTROL);
43ae1d95 1170 /* pack into mb */
10201568 1171 MemBuf mb;
2fe7eff9 1172 mb.init();
10201568 1173 sc->packInto(&mb);
43ae1d95 1174 /* put */
789217a2 1175 addEntry(new HttpHeaderEntry(Http::HdrType::SURROGATE_CONTROL, NULL, mb.buf));
43ae1d95 1176 /* cleanup */
2fe7eff9 1177 mb.clean();
43ae1d95 1178}
1179
bcfba8bd
AR
1180void
1181HttpHeader::putWarning(const int code, const char *const text)
1182{
1183 char buf[512];
1184 snprintf(buf, sizeof(buf), "%i %s \"%s\"", code, visible_appname_string, text);
789217a2 1185 putStr(Http::HdrType::WARNING, buf);
bcfba8bd
AR
1186}
1187
cb69b4c7 1188/* add extension header (these fields are not parsed/analyzed/joined, etc.) */
1189void
a9925b40 1190HttpHeader::putExt(const char *name, const char *value)
cb69b4c7 1191{
9bea1d5b 1192 assert(name && value);
efc26e8e 1193 debugs(55, 8, this << " adds ext entry " << name << " : " << value);
789217a2 1194 addEntry(new HttpHeaderEntry(Http::HdrType::OTHER, name, value));
528b2c61 1195}
1196
1197int
789217a2 1198HttpHeader::getInt(Http::HdrType id) const
528b2c61 1199{
229eb96e 1200 assert(any_valid_header(id));
92991271 1201 assert(Http::HeaderTable[id].type == Http::HdrFieldType::ftInt); /* must be of an appropriate type */
528b2c61 1202 HttpHeaderEntry *e;
62e76326 1203
a9925b40 1204 if ((e = findEntry(id)))
eede25e7 1205 return e->getInt();
62e76326 1206
528b2c61 1207 return -1;
7c525cc2 1208}
1209
47f6e231 1210int64_t
789217a2 1211HttpHeader::getInt64(Http::HdrType id) const
47f6e231 1212{
229eb96e 1213 assert(any_valid_header(id));
92991271 1214 assert(Http::HeaderTable[id].type == Http::HdrFieldType::ftInt64); /* must be of an appropriate type */
47f6e231 1215 HttpHeaderEntry *e;
1216
1217 if ((e = findEntry(id)))
1218 return e->getInt64();
1219
1220 return -1;
1221}
1222
de336bbe 1223time_t
789217a2 1224HttpHeader::getTime(Http::HdrType id) const
cb69b4c7 1225{
9bea1d5b 1226 HttpHeaderEntry *e;
1227 time_t value = -1;
229eb96e 1228 assert(any_valid_header(id));
92991271 1229 assert(Http::HeaderTable[id].type == Http::HdrFieldType::ftDate_1123); /* must be of an appropriate type */
62e76326 1230
a9925b40 1231 if ((e = findEntry(id))) {
9d7a89a5 1232 value = parse_rfc1123(e->value.termedBuf());
62e76326 1233 httpHeaderNoteParsedEntry(e->id, e->value, value < 0);
d8b249ef 1234 }
62e76326 1235
9bea1d5b 1236 return value;
cb69b4c7 1237}
1238
99edd1c3 1239/* sync with httpHeaderGetLastStr */
de336bbe 1240const char *
789217a2 1241HttpHeader::getStr(Http::HdrType id) const
cb69b4c7 1242{
9bea1d5b 1243 HttpHeaderEntry *e;
229eb96e 1244 assert(any_valid_header(id));
92991271 1245 assert(Http::HeaderTable[id].type == Http::HdrFieldType::ftStr); /* must be of an appropriate type */
62e76326 1246
a9925b40 1247 if ((e = findEntry(id))) {
f53969cc 1248 httpHeaderNoteParsedEntry(e->id, e->value, 0); /* no errors are possible */
9d7a89a5 1249 return e->value.termedBuf();
d8b249ef 1250 }
62e76326 1251
9bea1d5b 1252 return NULL;
cb69b4c7 1253}
1254
a622fff0 1255/* unusual */
1256const char *
789217a2 1257HttpHeader::getLastStr(Http::HdrType id) const
a622fff0 1258{
9bea1d5b 1259 HttpHeaderEntry *e;
229eb96e 1260 assert(any_valid_header(id));
92991271 1261 assert(Http::HeaderTable[id].type == Http::HdrFieldType::ftStr); /* must be of an appropriate type */
62e76326 1262
a9925b40 1263 if ((e = findLastEntry(id))) {
f53969cc 1264 httpHeaderNoteParsedEntry(e->id, e->value, 0); /* no errors are possible */
9d7a89a5 1265 return e->value.termedBuf();
a622fff0 1266 }
62e76326 1267
9bea1d5b 1268 return NULL;
a622fff0 1269}
1270
7faf2bdb 1271HttpHdrCc *
a9925b40 1272HttpHeader::getCc() const
cb69b4c7 1273{
789217a2 1274 if (!CBIT_TEST(mask, Http::HdrType::CACHE_CONTROL))
62e76326 1275 return NULL;
35b88ed2 1276 PROF_start(HttpHeader_getCc);
62e76326 1277
c57b8f05 1278 String s;
789217a2 1279 getList(Http::HdrType::CACHE_CONTROL, &s);
62e76326 1280
c57b8f05 1281 HttpHdrCc *cc=new HttpHdrCc();
7ebe76de
FC
1282
1283 if (!cc->parse(s)) {
cf66f10a
FC
1284 delete cc;
1285 cc = NULL;
1286 }
62e76326 1287
95dc7ff4 1288 ++ HttpHeaderStats[owner].ccParsedCount;
62e76326 1289
9bea1d5b 1290 if (cc)
a9925b40 1291 httpHdrCcUpdateStats(cc, &HttpHeaderStats[owner].ccTypeDistr);
62e76326 1292
789217a2 1293 httpHeaderNoteParsedEntry(Http::HdrType::CACHE_CONTROL, s, !cc);
62e76326 1294
35b88ed2 1295 PROF_stop(HttpHeader_getCc);
62e76326 1296
9bea1d5b 1297 return cc;
cb69b4c7 1298}
1299
02922e76 1300HttpHdrRange *
a9925b40 1301HttpHeader::getRange() const
02922e76 1302{
9bea1d5b 1303 HttpHdrRange *r = NULL;
1304 HttpHeaderEntry *e;
1305 /* some clients will send "Request-Range" _and_ *matching* "Range"
1306 * who knows, some clients might send Request-Range only;
1307 * this "if" should work correctly in both cases;
1308 * hopefully no clients send mismatched headers! */
62e76326 1309
789217a2
FC
1310 if ((e = findEntry(Http::HdrType::RANGE)) ||
1311 (e = findEntry(Http::HdrType::REQUEST_RANGE))) {
62e76326 1312 r = HttpHdrRange::ParseCreate(&e->value);
1313 httpHeaderNoteParsedEntry(e->id, e->value, !r);
d192d11f 1314 }
62e76326 1315
9bea1d5b 1316 return r;
02922e76 1317}
1318
43ae1d95 1319HttpHdrSc *
a9925b40 1320HttpHeader::getSc() const
43ae1d95 1321{
789217a2 1322 if (!CBIT_TEST(mask, Http::HdrType::SURROGATE_CONTROL))
43ae1d95 1323 return NULL;
1324
30abd221 1325 String s;
26ac0430 1326
789217a2 1327 (void) getList(Http::HdrType::SURROGATE_CONTROL, &s);
43ae1d95 1328
45a58345 1329 HttpHdrSc *sc = httpHdrScParseCreate(s);
43ae1d95 1330
95dc7ff4 1331 ++ HttpHeaderStats[owner].ccParsedCount;
43ae1d95 1332
1333 if (sc)
45a58345 1334 sc->updateStats(&HttpHeaderStats[owner].scTypeDistr);
43ae1d95 1335
789217a2 1336 httpHeaderNoteParsedEntry(Http::HdrType::SURROGATE_CONTROL, s, !sc);
43ae1d95 1337
1338 return sc;
1339}
1340
d76fcfa7 1341HttpHdrContRange *
a9925b40 1342HttpHeader::getContRange() const
d76fcfa7 1343{
9bea1d5b 1344 HttpHdrContRange *cr = NULL;
1345 HttpHeaderEntry *e;
62e76326 1346
789217a2 1347 if ((e = findEntry(Http::HdrType::CONTENT_RANGE))) {
9d7a89a5 1348 cr = httpHdrContRangeParseCreate(e->value.termedBuf());
62e76326 1349 httpHeaderNoteParsedEntry(e->id, e->value, !cr);
d8b249ef 1350 }
62e76326 1351
9bea1d5b 1352 return cr;
cb69b4c7 1353}
1354
99edd1c3 1355const char *
789217a2 1356HttpHeader::getAuth(Http::HdrType id, const char *auth_scheme) const
cb69b4c7 1357{
9bea1d5b 1358 const char *field;
1359 int l;
a9925b40 1360 assert(auth_scheme);
1361 field = getStr(id);
62e76326 1362
f53969cc 1363 if (!field) /* no authorization field */
62e76326 1364 return NULL;
1365
9bea1d5b 1366 l = strlen(auth_scheme);
62e76326 1367
f53969cc 1368 if (!l || strncasecmp(field, auth_scheme, l)) /* wrong scheme */
62e76326 1369 return NULL;
1370
9bea1d5b 1371 field += l;
62e76326 1372
f53969cc 1373 if (!xisspace(*field)) /* wrong scheme */
62e76326 1374 return NULL;
1375
9bea1d5b 1376 /* skip white space */
95dc7ff4 1377 for (; field && xisspace(*field); ++field);
62e76326 1378
f53969cc 1379 if (!*field) /* no authorization cookie */
62e76326 1380 return NULL;
1381
8bdd0cec 1382 static char decodedAuthToken[8192];
aadbbd7d
AJ
1383 struct base64_decode_ctx ctx;
1384 base64_decode_init(&ctx);
1385 size_t decodedLen = 0;
1386 if (!base64_decode_update(&ctx, &decodedLen, reinterpret_cast<uint8_t*>(decodedAuthToken), strlen(field), reinterpret_cast<const uint8_t*>(field)) ||
1387 !base64_decode_final(&ctx)) {
1388 return NULL;
1389 }
8bdd0cec
AJ
1390 decodedAuthToken[decodedLen] = '\0';
1391 return decodedAuthToken;
cb69b4c7 1392}
1393
a9771e51 1394ETag
789217a2 1395HttpHeader::getETag(Http::HdrType id) const
a9771e51 1396{
26ac0430 1397 ETag etag = {NULL, -1};
9bea1d5b 1398 HttpHeaderEntry *e;
92991271 1399 assert(Http::HeaderTable[id].type == Http::HdrFieldType::ftETag); /* must be of an appropriate type */
62e76326 1400
a9925b40 1401 if ((e = findEntry(id)))
9d7a89a5 1402 etagParseInit(&etag, e->value.termedBuf());
62e76326 1403
9bea1d5b 1404 return etag;
a9771e51 1405}
1406
1407TimeOrTag
789217a2 1408HttpHeader::getTimeOrTag(Http::HdrType id) const
a9771e51 1409{
9bea1d5b 1410 TimeOrTag tot;
1411 HttpHeaderEntry *e;
92991271 1412 assert(Http::HeaderTable[id].type == Http::HdrFieldType::ftDate_1123_or_ETag); /* must be of an appropriate type */
9bea1d5b 1413 memset(&tot, 0, sizeof(tot));
62e76326 1414
a9925b40 1415 if ((e = findEntry(id))) {
9d7a89a5 1416 const char *str = e->value.termedBuf();
62e76326 1417 /* try as an ETag */
1418
1419 if (etagParseInit(&tot.tag, str)) {
1420 tot.valid = tot.tag.str != NULL;
1421 tot.time = -1;
1422 } else {
1423 /* or maybe it is time? */
1424 tot.time = parse_rfc1123(str);
1425 tot.valid = tot.time >= 0;
1426 tot.tag.str = NULL;
1427 }
a9771e51 1428 }
62e76326 1429
f53969cc 1430 assert(tot.time < 0 || !tot.tag.str); /* paranoid */
9bea1d5b 1431 return tot;
a9771e51 1432}
1433
cb69b4c7 1434/*
1435 * HttpHeaderEntry
1436 */
1437
789217a2 1438HttpHeaderEntry::HttpHeaderEntry(Http::HdrType anId, const char *aName, const char *aValue)
cb69b4c7 1439{
1da82544 1440 assert(any_HdrType_enum_value(anId));
eede25e7 1441 id = anId;
62e76326 1442
789217a2 1443 if (id != Http::HdrType::OTHER)
92991271 1444 name = Http::HeaderTable[id].name;
9bea1d5b 1445 else
eede25e7 1446 name = aName;
62e76326 1447
eede25e7 1448 value = aValue;
62e76326 1449
1da82544 1450 if (id != Http::HdrType::BAD_HDR)
73b2aa87 1451 ++ headerStatsTable[id].aliveCount;
62e76326 1452
9d7a89a5 1453 debugs(55, 9, "created HttpHeaderEntry " << this << ": '" << name << " : " << value );
eede25e7 1454}
62e76326 1455
eede25e7 1456HttpHeaderEntry::~HttpHeaderEntry()
2ac76861 1457{
9d7a89a5 1458 debugs(55, 9, "destroying entry " << this << ": '" << name << ": " << value << "'");
62e76326 1459
1da82544
FC
1460 if (id != Http::HdrType::BAD_HDR) {
1461 assert(headerStatsTable[id].aliveCount);
1462 -- headerStatsTable[id].aliveCount;
1463 id = Http::HdrType::BAD_HDR; // it already is BAD_HDR, no sense in resetting it
1464 }
62e76326 1465
cb69b4c7 1466}
1467
eede25e7 1468/* parses and inits header entry, returns true/false */
cdce6c61 1469HttpHeaderEntry *
eede25e7 1470HttpHeaderEntry::parse(const char *field_start, const char *field_end)
cb69b4c7 1471{
9bea1d5b 1472 /* note: name_start == field_start */
52d3f198 1473 const char *name_end = (const char *)memchr(field_start, ':', field_end - field_start);
26ac0430 1474 int name_len = name_end ? name_end - field_start :0;
f53969cc 1475 const char *value_start = field_start + name_len + 1; /* skip ':' */
9bea1d5b 1476 /* note: value_end == field_end */
1477
95dc7ff4 1478 ++ HeaderEntryParsedCount;
9bea1d5b 1479
1480 /* do we have a valid field name within this field? */
62e76326 1481
9bea1d5b 1482 if (!name_len || name_end > field_end)
cdce6c61 1483 return NULL;
62e76326 1484
67746eea 1485 if (name_len > 65534) {
1486 /* String must be LESS THAN 64K and it adds a terminating NULL */
e0236918 1487 debugs(55, DBG_IMPORTANT, "WARNING: ignoring header name of " << name_len << " bytes");
cdce6c61 1488 return NULL;
25acfb53 1489 }
62e76326 1490
52d3f198 1491 if (Config.onoff.relaxed_header_parser && xisspace(field_start[name_len - 1])) {
bf8fe701 1492 debugs(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2,
81858ebc 1493 "NOTICE: Whitespace after header name in '" << getStringPrefix(field_start, field_end-field_start) << "'");
52d3f198 1494
1495 while (name_len > 0 && xisspace(field_start[name_len - 1]))
5e263176 1496 --name_len;
52d3f198 1497
1498 if (!name_len)
cdce6c61 1499 return NULL;
52d3f198 1500 }
1501
9bea1d5b 1502 /* now we know we can parse it */
62e76326 1503
81858ebc 1504 debugs(55, 9, "parsing HttpHeaderEntry: near '" << getStringPrefix(field_start, field_end-field_start) << "'");
62e76326 1505
9bea1d5b 1506 /* is it a "known" field? */
92991271
FC
1507 Http::HdrType id = Http::HeaderLookupTable.lookup(SBuf(field_start,name_len));
1508 debugs(55, 9, "got hdr-id=" << id);
cdce6c61 1509
30abd221 1510 String name;
cdce6c61 1511
30abd221 1512 String value;
62e76326 1513
789217a2
FC
1514 if (id == Http::HdrType::BAD_HDR)
1515 id = Http::HdrType::OTHER;
62e76326 1516
9bea1d5b 1517 /* set field name */
789217a2 1518 if (id == Http::HdrType::OTHER)
eede25e7 1519 name.limitInit(field_start, name_len);
9bea1d5b 1520 else
92991271 1521 name = Http::HeaderTable[id].name;
62e76326 1522
9bea1d5b 1523 /* trim field value */
1524 while (value_start < field_end && xisspace(*value_start))
95dc7ff4 1525 ++value_start;
62e76326 1526
52d3f198 1527 while (value_start < field_end && xisspace(field_end[-1]))
5e263176 1528 --field_end;
52d3f198 1529
67746eea 1530 if (field_end - value_start > 65534) {
1531 /* String must be LESS THAN 64K and it adds a terminating NULL */
e0236918 1532 debugs(55, DBG_IMPORTANT, "WARNING: ignoring '" << name << "' header of " << (field_end - value_start) << " bytes");
62e76326 1533
789217a2 1534 if (id == Http::HdrType::OTHER)
30abd221 1535 name.clean();
62e76326 1536
cdce6c61 1537 return NULL;
25acfb53 1538 }
62e76326 1539
9bea1d5b 1540 /* set field value */
eede25e7 1541 value.limitInit(value_start, field_end - value_start);
62e76326 1542
789217a2 1543 if (id != Http::HdrType::BAD_HDR)
73b2aa87 1544 ++ headerStatsTable[id].seenCount;
62e76326 1545
9d7a89a5 1546 debugs(55, 9, "parsed HttpHeaderEntry: '" << name << ": " << value << "'");
62e76326 1547
9d7a89a5 1548 return new HttpHeaderEntry(id, name.termedBuf(), value.termedBuf());
cb69b4c7 1549}
1550
99edd1c3 1551HttpHeaderEntry *
eede25e7 1552HttpHeaderEntry::clone() const
cb69b4c7 1553{
9d7a89a5 1554 return new HttpHeaderEntry(id, name.termedBuf(), value.termedBuf());
cb69b4c7 1555}
1556
de336bbe 1557void
17802cf1 1558HttpHeaderEntry::packInto(Packable * p) const
cb69b4c7 1559{
eede25e7 1560 assert(p);
785b508d
AJ
1561 p->append(name.rawBuf(), name.size());
1562 p->append(": ", 2);
1563 p->append(value.rawBuf(), value.size());
1564 p->append("\r\n", 2);
cb69b4c7 1565}
1566
eede25e7 1567int
1568HttpHeaderEntry::getInt() const
1569{
eede25e7 1570 int val = -1;
9d7a89a5 1571 int ok = httpHeaderParseInt(value.termedBuf(), &val);
eede25e7 1572 httpHeaderNoteParsedEntry(id, value, !ok);
1573 /* XXX: Should we check ok - ie
1574 * return ok ? -1 : value;
1575 */
1576 return val;
1577}
1578
47f6e231 1579int64_t
1580HttpHeaderEntry::getInt64() const
1581{
47f6e231 1582 int64_t val = -1;
9d7a89a5 1583 int ok = httpHeaderParseOffset(value.termedBuf(), &val);
47f6e231 1584 httpHeaderNoteParsedEntry(id, value, !ok);
1585 /* XXX: Should we check ok - ie
1586 * return ok ? -1 : value;
1587 */
1588 return val;
1589}
1590
cb69b4c7 1591static void
789217a2 1592httpHeaderNoteParsedEntry(Http::HdrType id, String const &context, int error)
cb69b4c7 1593{
789217a2 1594 if (id != Http::HdrType::BAD_HDR)
73b2aa87 1595 ++ headerStatsTable[id].parsCount;
62e76326 1596
9bea1d5b 1597 if (error) {
789217a2 1598 if (id != Http::HdrType::BAD_HDR)
73b2aa87 1599 ++ headerStatsTable[id].errCount;
92991271 1600 debugs(55, 2, "cannot parse hdr field: '" << Http::HeaderTable[id].name << ": " << context << "'");
cb69b4c7 1601 }
cb69b4c7 1602}
1603
cb69b4c7 1604/*
12cf1be2 1605 * Reports
cb69b4c7 1606 */
cb69b4c7 1607
fcd2d3ef 1608/* tmp variable used to pass stat info to dumpers */
f53969cc 1609extern const HttpHeaderStat *dump_stat; /* argh! */
fcd2d3ef 1610const HttpHeaderStat *dump_stat = NULL;
1611
59a09b98 1612void
ced8def3 1613httpHeaderFieldStatDumper(StoreEntry * sentry, int, double val, double, int count)
cb69b4c7 1614{
92991271 1615 const int id = static_cast<int>(val);
1da82544 1616 const bool valid_id = Http::any_valid_header(static_cast<Http::HdrType>(id));
92991271 1617 const char *name = valid_id ? Http::HeaderTable[id].name : "INVALID";
9bea1d5b 1618 int visible = count > 0;
1619 /* for entries with zero count, list only those that belong to current type of message */
62e76326 1620
9bea1d5b 1621 if (!visible && valid_id && dump_stat->owner_mask)
62e76326 1622 visible = CBIT_TEST(*dump_stat->owner_mask, id);
1623
9bea1d5b 1624 if (visible)
62e76326 1625 storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
1626 id, name, count, xdiv(count, dump_stat->busyDestroyedCount));
cb69b4c7 1627}
1628
12cf1be2 1629static void
ced8def3 1630httpHeaderFldsPerHdrDumper(StoreEntry * sentry, int idx, double val, double, int count)
cb69b4c7 1631{
9bea1d5b 1632 if (count)
62e76326 1633 storeAppendPrintf(sentry, "%2d\t %5d\t %5d\t %6.2f\n",
1634 idx, (int) val, count,
1635 xpercent(count, dump_stat->destroyedCount));
cb69b4c7 1636}
1637
cb69b4c7 1638static void
9bea1d5b 1639httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e)
12cf1be2 1640{
73b2aa87
FC
1641 assert(hs);
1642 assert(e);
9bea1d5b 1643
1644 dump_stat = hs;
1645 storeAppendPrintf(e, "\nHeader Stats: %s\n", hs->label);
1646 storeAppendPrintf(e, "\nField type distribution\n");
1647 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n",
62e76326 1648 "id", "name", "count", "#/header");
96886986 1649 hs->fieldTypeDistr.dump(e, httpHeaderFieldStatDumper);
9bea1d5b 1650 storeAppendPrintf(e, "\nCache-control directives distribution\n");
1651 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n",
62e76326 1652 "id", "name", "count", "#/cc_field");
96886986 1653 hs->ccTypeDistr.dump(e, httpHdrCcStatDumper);
43ae1d95 1654 storeAppendPrintf(e, "\nSurrogate-control directives distribution\n");
1655 storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n",
1656 "id", "name", "count", "#/sc_field");
96886986 1657 hs->scTypeDistr.dump(e, httpHdrScStatDumper);
9bea1d5b 1658 storeAppendPrintf(e, "\nNumber of fields per header distribution\n");
1659 storeAppendPrintf(e, "%2s\t %-5s\t %5s\t %6s\n",
62e76326 1660 "id", "#flds", "count", "%total");
96886986 1661 hs->hdrUCountDistr.dump(e, httpHeaderFldsPerHdrDumper);
41b43e70 1662 storeAppendPrintf(e, "\n");
9bea1d5b 1663 dump_stat = NULL;
cb69b4c7 1664}
1665
12cf1be2 1666void
9bea1d5b 1667httpHeaderStoreReport(StoreEntry * e)
cb69b4c7 1668{
9bea1d5b 1669 int i;
9bea1d5b 1670 assert(e);
1671
1672 HttpHeaderStats[0].parsedCount =
62e76326 1673 HttpHeaderStats[hoRequest].parsedCount + HttpHeaderStats[hoReply].parsedCount;
9bea1d5b 1674 HttpHeaderStats[0].ccParsedCount =
62e76326 1675 HttpHeaderStats[hoRequest].ccParsedCount + HttpHeaderStats[hoReply].ccParsedCount;
9bea1d5b 1676 HttpHeaderStats[0].destroyedCount =
62e76326 1677 HttpHeaderStats[hoRequest].destroyedCount + HttpHeaderStats[hoReply].destroyedCount;
9bea1d5b 1678 HttpHeaderStats[0].busyDestroyedCount =
62e76326 1679 HttpHeaderStats[hoRequest].busyDestroyedCount + HttpHeaderStats[hoReply].busyDestroyedCount;
9bea1d5b 1680
95dc7ff4 1681 for (i = 1; i < HttpHeaderStatCount; ++i) {
62e76326 1682 httpHeaderStatDump(HttpHeaderStats + i, e);
12cf1be2 1683 }
62e76326 1684
9bea1d5b 1685 /* field stats for all messages */
1686 storeAppendPrintf(e, "\nHttp Fields Stats (replies and requests)\n");
62e76326 1687
077fe581 1688 storeAppendPrintf(e, "%2s\t %-25s\t %5s\t %6s\t %6s\n",
62e76326 1689 "id", "name", "#alive", "%err", "%repeat");
1690
fda6d769 1691 // scan heaaderTable and output
92991271 1692 for (int j = 0; Http::HeaderTable[j].name != nullptr; ++j) {
fda6d769
FC
1693 auto stats = headerStatsTable[j];
1694 storeAppendPrintf(e, "%2d\t %-25s\t %5d\t %6.3f\t %6.3f\n",
92991271
FC
1695 Http::HeaderTable[j].id,
1696 Http::HeaderTable[j].name,
1697 stats.aliveCount,
f7ad4af5
FC
1698 xpercent(stats.errCount, stats.parsCount),
1699 xpercent(stats.repCount, stats.seenCount));
fda6d769 1700 }
62e76326 1701
9bea1d5b 1702 storeAppendPrintf(e, "Headers Parsed: %d + %d = %d\n",
62e76326 1703 HttpHeaderStats[hoRequest].parsedCount,
1704 HttpHeaderStats[hoReply].parsedCount,
1705 HttpHeaderStats[0].parsedCount);
9bea1d5b 1706 storeAppendPrintf(e, "Hdr Fields Parsed: %d\n", HeaderEntryParsedCount);
cb69b4c7 1707}
97474590 1708
924f73bc 1709int
789217a2 1710HttpHeader::hasListMember(Http::HdrType id, const char *member, const char separator) const
924f73bc 1711{
1712 int result = 0;
1713 const char *pos = NULL;
1714 const char *item;
1715 int ilen;
1716 int mlen = strlen(member);
1717
229eb96e 1718 assert(any_valid_header(id));
924f73bc 1719
30abd221 1720 String header (getStrOrList(id));
924f73bc 1721
9d0cdbb9 1722 while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
380e7faf 1723 if (strncasecmp(item, member, mlen) == 0
9d0cdbb9 1724 && (item[mlen] == '=' || item[mlen] == separator || item[mlen] == ';' || item[mlen] == '\0')) {
1725 result = 1;
1726 break;
1727 }
1728 }
1729
1730 return result;
1731}
1732
1733int
a9925b40 1734HttpHeader::hasByNameListMember(const char *name, const char *member, const char separator) const
9d0cdbb9 1735{
1736 int result = 0;
1737 const char *pos = NULL;
1738 const char *item;
1739 int ilen;
1740 int mlen = strlen(member);
1741
9d0cdbb9 1742 assert(name);
1743
30abd221 1744 String header (getByName(name));
9d0cdbb9 1745
924f73bc 1746 while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
380e7faf 1747 if (strncasecmp(item, member, mlen) == 0
924f73bc 1748 && (item[mlen] == '=' || item[mlen] == separator || item[mlen] == ';' || item[mlen] == '\0')) {
1749 result = 1;
1750 break;
1751 }
1752 }
1753
1754 return result;
1755}
1756
2cdeea82 1757void
1758HttpHeader::removeHopByHopEntries()
1759{
1760 removeConnectionHeaderEntries();
26ac0430 1761
dcf3665b
AJ
1762 const HttpHeaderEntry *e;
1763 HttpHeaderPos pos = HttpHeaderInitPos;
1764 int headers_deleted = 0;
1765 while ((e = getEntry(&pos))) {
789217a2 1766 Http::HdrType id = e->id;
26ac0430
AJ
1767 if (CBIT_TEST(HopByHopHeadersMask, id)) {
1768 delAt(pos, headers_deleted);
1769 CBIT_CLR(mask, id);
1770 }
dcf3665b 1771 }
2cdeea82 1772}
1773
924f73bc 1774void
1775HttpHeader::removeConnectionHeaderEntries()
1776{
789217a2 1777 if (has(Http::HdrType::CONNECTION)) {
924f73bc 1778 /* anything that matches Connection list member will be deleted */
30abd221 1779 String strConnection;
26ac0430 1780
789217a2 1781 (void) getList(Http::HdrType::CONNECTION, &strConnection);
924f73bc 1782 const HttpHeaderEntry *e;
1783 HttpHeaderPos pos = HttpHeaderInitPos;
1784 /*
1785 * think: on-average-best nesting of the two loops (hdrEntry
1786 * and strListItem) @?@
1787 */
1788 /*
1789 * maybe we should delete standard stuff ("keep-alive","close")
1790 * from strConnection first?
1791 */
1792
ba9fb01d 1793 int headers_deleted = 0;
a9925b40 1794 while ((e = getEntry(&pos))) {
9d7a89a5 1795 if (strListIsMember(&strConnection, e->name.termedBuf(), ','))
ba9fb01d 1796 delAt(pos, headers_deleted);
924f73bc 1797 }
ba9fb01d 1798 if (headers_deleted)
1799 refreshMask();
924f73bc 1800 }
1801}
f53969cc 1802