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