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