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