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