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