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