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