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