HttpHeader.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2017 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 "base/CharacterSet.h"
13 #include "base/EnumIterator.h"
14 #include "base64.h"
15 #include "globals.h"
17 #include "HttpHdrCc.h"
18 #include "HttpHdrContRange.h"
19 #include "HttpHdrScTarget.h" // also includes HttpHdrSc.h
20 #include "HttpHeader.h"
21 #include "HttpHeaderFieldInfo.h"
22 #include "HttpHeaderStat.h"
23 #include "HttpHeaderTools.h"
24 #include "MemBuf.h"
25 #include "mgr/Registration.h"
26 #include "mime_header.h"
27 #include "profiler/Profiler.h"
28 #include "rfc1123.h"
29 #include "sbuf/StringConvert.h"
30 #include "SquidConfig.h"
31 #include "StatHist.h"
32 #include "Store.h"
33 #include "StrList.h"
34 #include "TimeOrTag.h"
35 #include "util.h"
36 
37 #include <algorithm>
38 
39 /* XXX: the whole set of API managing the entries vector should be rethought
40  * after the parse4r-ng effort is complete.
41  */
42 
43 /*
44  * On naming conventions:
45  *
46  * HTTP/1.1 defines message-header as
47  *
48  * message-header = field-name ":" [ field-value ] CRLF
49  * field-name = token
50  * field-value = *( field-content | LWS )
51  *
52  * HTTP/1.1 does not give a name name a group of all message-headers in a message.
53  * Squid 1.1 seems to refer to that group _plus_ start-line as "headers".
54  *
55  * HttpHeader is an object that represents all message-headers in a message.
56  * HttpHeader does not manage start-line.
57  *
58  * HttpHeader is implemented as a collection of header "entries".
59  * An entry is a (field_id, field_name, field_value) triplet.
60  */
61 
62 /*
63  * local constants and vars
64  */
65 
66 // statistics counters for headers. clients must not allow Http::HdrType::BAD_HDR to be counted
67 std::vector<HttpHeaderFieldStat> headerStatsTable(Http::HdrType::enumEnd_);
68 
69 /* request-only headers. Used for cachemgr */
70 static HttpHeaderMask RequestHeadersMask; /* set run-time using RequestHeaders */
71 
72 /* reply-only headers. Used for cachemgr */
73 static HttpHeaderMask ReplyHeadersMask; /* set run-time using ReplyHeaders */
74 
75 /* header accounting */
76 // NP: keep in sync with enum http_hdr_owner_type
77 static HttpHeaderStat HttpHeaderStats[] = {
78  HttpHeaderStat(/*hoNone*/ "all", NULL),
79 #if USE_HTCP
80  HttpHeaderStat(/*hoHtcpReply*/ "HTCP reply", &ReplyHeadersMask),
81 #endif
82  HttpHeaderStat(/*hoRequest*/ "request", &RequestHeadersMask),
83  HttpHeaderStat(/*hoReply*/ "reply", &ReplyHeadersMask)
84 #if USE_OPENSSL
85  /* hoErrorDetail */
86 #endif
87  /* hoEnd */
88 };
89 static int HttpHeaderStatCount = countof(HttpHeaderStats);
90 
91 static int HeaderEntryParsedCount = 0;
92 
93 /*
94  * forward declarations and local routines
95  */
96 
97 class StoreEntry;
98 
99 // update parse statistics for header id; if error is true also account
100 // for errors and write to debug log what happened
101 static void httpHeaderNoteParsedEntry(Http::HdrType id, String const &value, bool error);
102 static void httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e);
104 static void httpHeaderStoreReport(StoreEntry * e);
105 
106 /*
107  * Module initialization routines
108  */
109 
110 static void
112 {
113  Mgr::RegisterAction("http_headers",
114  "HTTP Header Statistics",
115  httpHeaderStoreReport, 0, 1);
116 }
117 
118 void
120 {
121  /* check that we have enough space for masks */
123 
124  // masks are needed for stats page still
125  for (auto h : WholeEnum<Http::HdrType>()) {
126  if (Http::HeaderLookupTable.lookup(h).request)
128  if (Http::HeaderLookupTable.lookup(h).reply)
130  }
131 
132  /* header stats initialized by class constructor */
134 
135  /* init dependent modules */
138 
140 }
141 
142 /*
143  * HttpHeader Implementation
144  */
145 
146 HttpHeader::HttpHeader() : owner (hoNone), len (0), conflictingContentLength_(false)
147 {
149 }
150 
151 HttpHeader::HttpHeader(const http_hdr_owner_type anOwner): owner(anOwner), len(0), conflictingContentLength_(false)
152 {
153  assert(anOwner > hoNone && anOwner < hoEnd);
154  debugs(55, 7, "init-ing hdr: " << this << " owner: " << owner);
156 }
157 
158 HttpHeader::HttpHeader(const HttpHeader &other): owner(other.owner), len(other.len), conflictingContentLength_(false)
159 {
161  update(&other); // will update the mask as well
162 }
163 
165 {
166  clean();
167 }
168 
169 HttpHeader &
171 {
172  if (this != &other) {
173  // we do not really care, but the caller probably does
174  assert(owner == other.owner);
175  clean();
176  update(&other); // will update the mask as well
177  len = other.len;
179  }
180  return *this;
181 }
182 
183 void
185 {
186 
187  assert(owner > hoNone && owner < hoEnd);
188  debugs(55, 7, "cleaning hdr: " << this << " owner: " << owner);
189 
190  PROF_start(HttpHeaderClean);
191 
192  if (owner <= hoReply) {
193  /*
194  * An unfortunate bug. The entries array is initialized
195  * such that count is set to zero. httpHeaderClean() seems to
196  * be called both when 'hdr' is created, and destroyed. Thus,
197  * we accumulate a large number of zero counts for 'hdr' before
198  * it is ever used. Can't think of a good way to fix it, except
199  * adding a state variable that indicates whether or not 'hdr'
200  * has been used. As a hack, just never count zero-sized header
201  * arrays.
202  */
203  if (!entries.empty())
204  HttpHeaderStats[owner].hdrUCountDistr.count(entries.size());
205 
206  ++ HttpHeaderStats[owner].destroyedCount;
207 
208  HttpHeaderStats[owner].busyDestroyedCount += entries.size() > 0;
209  } // if (owner <= hoReply)
210 
211  for (HttpHeaderEntry *e : entries) {
212  if (e == nullptr)
213  continue;
214  if (!Http::any_valid_header(e->id)) {
215  debugs(55, DBG_CRITICAL, "BUG: invalid entry (" << e->id << "). Ignored.");
216  } else {
217  if (owner <= hoReply)
218  HttpHeaderStats[owner].fieldTypeDistr.count(e->id);
219  delete e;
220  }
221  }
222 
223  entries.clear();
225  len = 0;
227  PROF_stop(HttpHeaderClean);
228 }
229 
230 /* append entries (also see httpHeaderUpdate) */
231 void
233 {
234  assert(src);
235  assert(src != this);
236  debugs(55, 7, "appending hdr: " << this << " += " << src);
237 
238  for (auto e : src->entries) {
239  if (e)
240  addEntry(e->clone());
241  }
242 }
243 
245 bool
247 {
248  for (const auto e: fresh->entries) {
249  if (!e || skipUpdateHeader(e->id))
250  continue;
251  String value;
252  if (!hasNamed(e->name, &value) ||
253  (value != fresh->getByName(e->name)))
254  return true;
255  }
256  return false;
257 }
258 
259 void
261 {
262  int count = 0;
264 
265  // RFC 7234, section 4.3.4: delete 1xx warnings and retain 2xx warnings
266  while (HttpHeaderEntry *e = getEntry(&pos)) {
267  if (e->id == Http::HdrType::WARNING && (e->getInt()/100 == 1) )
268  delAt(pos, count);
269  }
270 }
271 
272 bool
274 {
275  // RFC 7234, section 4.3.4: use fields other from Warning for update
276  return id == Http::HdrType::WARNING;
277 }
278 
279 bool
281 {
282  assert(fresh);
283  assert(this != fresh);
284 
285  // Optimization: Finding whether a header field changed is expensive
286  // and probably not worth it except for collapsed revalidation needs.
288  return false;
289 
290  updateWarnings();
291 
292  const HttpHeaderEntry *e;
294 
295  while ((e = fresh->getEntry(&pos))) {
296  /* deny bad guys (ok to check for Http::HdrType::OTHER) here */
297 
298  if (skipUpdateHeader(e->id))
299  continue;
300 
301  if (e->id != Http::HdrType::OTHER)
302  delById(e->id);
303  else
304  delByName(e->name);
305  }
306 
307  pos = HttpHeaderInitPos;
308  while ((e = fresh->getEntry(&pos))) {
309  /* deny bad guys (ok to check for Http::HdrType::OTHER) here */
310 
311  if (skipUpdateHeader(e->id))
312  continue;
313 
314  debugs(55, 7, "Updating header '" << Http::HeaderLookupTable.lookup(e->id).name << "' in cached entry");
315 
316  addEntry(e->clone());
317  }
318  return true;
319 }
320 
321 bool
322 HttpHeader::Isolate(const char **parse_start, size_t l, const char **blk_start, const char **blk_end)
323 {
324  /*
325  * parse_start points to the first line of HTTP message *headers*,
326  * not including the request or status lines
327  */
328  const size_t end = headersEnd(*parse_start, l);
329 
330  if (end) {
331  *blk_start = *parse_start;
332  *blk_end = *parse_start + end - 1;
333  assert(**blk_end == '\n');
334  // Point blk_end to the first character after the last header field.
335  // In other words, blk_end should point to the CR?LF header terminator.
336  if (end > 1 && *(*blk_end - 1) == '\r')
337  --(*blk_end);
338  *parse_start += end;
339  }
340  return end;
341 }
342 
343 int
344 HttpHeader::parse(const char *buf, size_t buf_len, bool atEnd, size_t &hdr_sz)
345 {
346  const char *parse_start = buf;
347  const char *blk_start, *blk_end;
348  hdr_sz = 0;
349 
350  if (!Isolate(&parse_start, buf_len, &blk_start, &blk_end)) {
351  // XXX: do not parse non-isolated headers even if the connection is closed.
352  // Treat unterminated headers as "partial headers" framing errors.
353  if (!atEnd)
354  return 0;
355  blk_start = parse_start;
356  blk_end = blk_start + strlen(blk_start);
357  }
358 
359  if (parse(blk_start, blk_end - blk_start)) {
360  hdr_sz = parse_start - buf;
361  return 1;
362  }
363  return -1;
364 }
365 
366 int
367 HttpHeader::parse(const char *header_start, size_t hdrLen)
368 {
369  const char *field_ptr = header_start;
370  const char *header_end = header_start + hdrLen; // XXX: remove
371  int warnOnError = (Config.onoff.relaxed_header_parser <= 0 ? DBG_IMPORTANT : 2);
372 
373  PROF_start(HttpHeaderParse);
374 
375  assert(header_start && header_end);
376  debugs(55, 7, "parsing hdr: (" << this << ")" << std::endl << getStringPrefix(header_start, hdrLen));
377  ++ HttpHeaderStats[owner].parsedCount;
378 
379  char *nulpos;
380  if ((nulpos = (char*)memchr(header_start, '\0', hdrLen))) {
381  debugs(55, DBG_IMPORTANT, "WARNING: HTTP header contains NULL characters {" <<
382  getStringPrefix(header_start, nulpos-header_start) << "}\nNULL\n{" << getStringPrefix(nulpos+1, hdrLen-(nulpos-header_start)-1));
383  PROF_stop(HttpHeaderParse);
384  clean();
385  return 0;
386  }
387 
388  Http::ContentLengthInterpreter clen(warnOnError);
389  /* common format headers are "<name>:[ws]<value>" lines delimited by <CRLF>.
390  * continuation lines start with a (single) space or tab */
391  while (field_ptr < header_end) {
392  const char *field_start = field_ptr;
393  const char *field_end;
394 
395  do {
396  const char *this_line = field_ptr;
397  field_ptr = (const char *)memchr(field_ptr, '\n', header_end - field_ptr);
398 
399  if (!field_ptr) {
400  // missing <LF>
401  PROF_stop(HttpHeaderParse);
402  clean();
403  return 0;
404  }
405 
406  field_end = field_ptr;
407 
408  ++field_ptr; /* Move to next line */
409 
410  if (field_end > this_line && field_end[-1] == '\r') {
411  --field_end; /* Ignore CR LF */
412 
413  if (owner == hoRequest && field_end > this_line) {
414  bool cr_only = true;
415  for (const char *p = this_line; p < field_end && cr_only; ++p) {
416  if (*p != '\r')
417  cr_only = false;
418  }
419  if (cr_only) {
420  debugs(55, DBG_IMPORTANT, "SECURITY WARNING: Rejecting HTTP request with a CR+ "
421  "header field to prevent request smuggling attacks: {" <<
422  getStringPrefix(header_start, hdrLen) << "}");
423  PROF_stop(HttpHeaderParse);
424  clean();
425  return 0;
426  }
427  }
428  }
429 
430  /* Barf on stray CR characters */
431  if (memchr(this_line, '\r', field_end - this_line)) {
432  debugs(55, warnOnError, "WARNING: suspicious CR characters in HTTP header {" <<
433  getStringPrefix(field_start, field_end-field_start) << "}");
434 
436  char *p = (char *) this_line; /* XXX Warning! This destroys original header content and violates specifications somewhat */
437 
438  while ((p = (char *)memchr(p, '\r', field_end - p)) != NULL) {
439  *p = ' ';
440  ++p;
441  }
442  } else {
443  PROF_stop(HttpHeaderParse);
444  clean();
445  return 0;
446  }
447  }
448 
449  if (this_line + 1 == field_end && this_line > field_start) {
450  debugs(55, warnOnError, "WARNING: Blank continuation line in HTTP header {" <<
451  getStringPrefix(header_start, hdrLen) << "}");
452  PROF_stop(HttpHeaderParse);
453  clean();
454  return 0;
455  }
456  } while (field_ptr < header_end && (*field_ptr == ' ' || *field_ptr == '\t'));
457 
458  if (field_start == field_end) {
459  if (field_ptr < header_end) {
460  debugs(55, warnOnError, "WARNING: unparseable HTTP header field near {" <<
461  getStringPrefix(field_start, hdrLen-(field_start-header_start)) << "}");
462  PROF_stop(HttpHeaderParse);
463  clean();
464  return 0;
465  }
466 
467  break; /* terminating blank line */
468  }
469 
470  HttpHeaderEntry *e;
471  if ((e = HttpHeaderEntry::parse(field_start, field_end)) == NULL) {
472  debugs(55, warnOnError, "WARNING: unparseable HTTP header field {" <<
473  getStringPrefix(field_start, field_end-field_start) << "}");
474  debugs(55, warnOnError, " in {" << getStringPrefix(header_start, hdrLen) << "}");
475 
477  continue;
478 
479  PROF_stop(HttpHeaderParse);
480  clean();
481  return 0;
482  }
483 
484  if (e->id == Http::HdrType::CONTENT_LENGTH && !clen.checkField(e->value)) {
485  delete e;
486 
488  continue; // clen has printed any necessary warnings
489 
490  PROF_stop(HttpHeaderParse);
491  clean();
492  return 0;
493  }
494 
495  /* AYJ 2017-05-23: I suspect we need to change this whitespace check to conform to the
496  updated WSP character set in RFC 7230/7231. For now I left it as the
497  characters in w_space which the previous code was using. */
499  if (e->id == Http::HdrType::OTHER && e->name.findFirstOf(wsp) != SBuf::npos) {
500  debugs(55, warnOnError, "WARNING: found whitespace in HTTP header name {" <<
501  getStringPrefix(field_start, field_end-field_start) << "}");
502 
504  delete e;
505  PROF_stop(HttpHeaderParse);
506  clean();
507  return 0;
508  }
509  }
510 
511  addEntry(e);
512  }
513 
514  if (clen.headerWideProblem) {
515  debugs(55, warnOnError, "WARNING: " << clen.headerWideProblem <<
516  " Content-Length field values in" <<
517  Raw("header", header_start, hdrLen));
518  }
519 
520  if (chunked()) {
521  // RFC 2616 section 4.4: ignore Content-Length with Transfer-Encoding
522  // RFC 7230 section 3.3.3 #3: Transfer-Encoding overwrites Content-Length
524  // and clen state becomes irrelevant
525  } else if (clen.sawBad) {
526  // ensure our callers do not accidentally see bad Content-Length values
528  conflictingContentLength_ = true; // TODO: Rename to badContentLength_.
529  } else if (clen.needsSanitizing) {
530  // RFC 7230 section 3.3.2: MUST either reject or ... [sanitize];
531  // ensure our callers see a clean Content-Length value or none at all
533  if (clen.sawGood) {
535  debugs(55, 5, "sanitized Content-Length to be " << clen.value);
536  }
537  }
538 
539  PROF_stop(HttpHeaderParse);
540  return 1; /* even if no fields where found, it is a valid header */
541 }
542 
543 /* packs all the entries using supplied packer */
544 void
545 HttpHeader::packInto(Packable * p, bool mask_sensitive_info) const
546 {
548  const HttpHeaderEntry *e;
549  assert(p);
550  debugs(55, 7, this << " into " << p <<
551  (mask_sensitive_info ? " while masking" : ""));
552  /* pack all entries one by one */
553  while ((e = getEntry(&pos))) {
554  if (!mask_sensitive_info) {
555  e->packInto(p);
556  continue;
557  }
558 
559  bool maskThisEntry = false;
560  switch (e->id) {
563  maskThisEntry = true;
564  break;
565 
568  maskThisEntry = (cmd->value == "PASS");
569  break;
570 
571  default:
572  break;
573  }
574  if (maskThisEntry) {
575  p->append(e->name.rawContent(), e->name.length());
576  p->append(": ** NOT DISPLAYED **\r\n", 23);
577  } else {
578  e->packInto(p);
579  }
580 
581  }
582  /* Pack in the "special" entries */
583 
584  /* Cache-Control */
585 }
586 
587 /* returns next valid entry */
590 {
591  assert(pos);
592  assert(*pos >= HttpHeaderInitPos && *pos < static_cast<ssize_t>(entries.size()));
593 
594  for (++(*pos); *pos < static_cast<ssize_t>(entries.size()); ++(*pos)) {
595  if (entries[*pos])
596  return static_cast<HttpHeaderEntry*>(entries[*pos]);
597  }
598 
599  return NULL;
600 }
601 
602 /*
603  * returns a pointer to a specified entry if any
604  * note that we return one entry so it does not make much sense to ask for
605  * "list" headers
606  */
609 {
611  assert(!Http::HeaderLookupTable.lookup(id).list);
612 
613  /* check mask first */
614 
615  if (!CBIT_TEST(mask, id))
616  return NULL;
617 
618  /* looks like we must have it, do linear search */
619  for (auto e : entries) {
620  if (e && e->id == id)
621  return e;
622  }
623 
624  /* hm.. we thought it was there, but it was not found */
625  assert(false);
626  return nullptr; /* not reached */
627 }
628 
629 /*
630  * same as httpHeaderFindEntry
631  */
634 {
636  assert(!Http::HeaderLookupTable.lookup(id).list);
637 
638  /* check mask first */
639  if (!CBIT_TEST(mask, id))
640  return NULL;
641 
642  for (auto e = entries.rbegin(); e != entries.rend(); ++e) {
643  if (*e && (*e)->id == id)
644  return *e;
645  }
646 
647  /* hm.. we thought it was there, but it was not found */
648  assert(false);
649  return nullptr; /* not reached */
650 }
651 
652 int
654 {
655  int count = 0;
657  httpHeaderMaskInit(&mask, 0); /* temporal inconsistency */
658  debugs(55, 9, "deleting '" << name << "' fields in hdr " << this);
659 
660  while (const HttpHeaderEntry *e = getEntry(&pos)) {
661  if (!e->name.caseCmp(name))
662  delAt(pos, count);
663  else
664  CBIT_SET(mask, e->id);
665  }
666 
667  return count;
668 }
669 
670 /* deletes all entries with a given id, returns the #entries deleted */
671 int
673 {
674  debugs(55, 8, this << " del-by-id " << id);
676 
677  if (!CBIT_TEST(mask, id))
678  return 0;
679 
680  int count = 0;
681 
683  while (HttpHeaderEntry *e = getEntry(&pos)) {
684  if (e->id == id)
685  delAt(pos, count); // deletes e
686  }
687 
688  CBIT_CLR(mask, id);
689  assert(count);
690  return count;
691 }
692 
693 /*
694  * deletes an entry at pos and leaves a gap; leaving a gap makes it
695  * possible to iterate(search) and delete fields at the same time
696  * NOTE: Does not update the header mask. Caller must follow up with
697  * a call to refreshMask() if headers_deleted was incremented.
698  */
699 void
700 HttpHeader::delAt(HttpHeaderPos pos, int &headers_deleted)
701 {
702  HttpHeaderEntry *e;
703  assert(pos >= HttpHeaderInitPos && pos < static_cast<ssize_t>(entries.size()));
704  e = static_cast<HttpHeaderEntry*>(entries[pos]);
705  entries[pos] = NULL;
706  /* decrement header length, allow for ": " and crlf */
707  len -= e->name.length() + 2 + e->value.size() + 2;
708  assert(len >= 0);
709  delete e;
710  ++headers_deleted;
711 }
712 
713 /*
714  * Compacts the header storage
715  */
716 void
718 {
719  // TODO: optimize removal, or possibly make it so that's not needed.
720  entries.erase( std::remove(entries.begin(), entries.end(), nullptr),
721  entries.end());
722 }
723 
724 /*
725  * Refreshes the header mask. Required after delAt() calls.
726  */
727 void
729 {
731  debugs(55, 7, "refreshing the mask in hdr " << this);
732  for (auto e : entries) {
733  if (e)
734  CBIT_SET(mask, e->id);
735  }
736 }
737 
738 /* appends an entry;
739  * does not call e->clone() so one should not reuse "*e"
740  */
741 void
743 {
744  assert(e);
746  assert(e->name.length());
747 
748  debugs(55, 7, this << " adding entry: " << e->id << " at " << entries.size());
749 
750  if (e->id != Http::HdrType::BAD_HDR) {
751  if (CBIT_TEST(mask, e->id)) {
752  ++ headerStatsTable[e->id].repCount;
753  } else {
754  CBIT_SET(mask, e->id);
755  }
756  }
757 
758  entries.push_back(e);
759 
760  /* increment header length, allow for ": " and crlf */
761  len += e->name.length() + 2 + e->value.size() + 2;
762 }
763 
764 /* inserts an entry;
765  * does not call e->clone() so one should not reuse "*e"
766  */
767 void
769 {
770  assert(e);
772 
773  debugs(55, 7, this << " adding entry: " << e->id << " at " << entries.size());
774 
775  // Http::HdrType::BAD_HDR is filtered out by assert_any_valid_header
776  if (CBIT_TEST(mask, e->id)) {
777  ++ headerStatsTable[e->id].repCount;
778  } else {
779  CBIT_SET(mask, e->id);
780  }
781 
782  entries.insert(entries.begin(),e);
783 
784  /* increment header length, allow for ": " and crlf */
785  len += e->name.length() + 2 + e->value.size() + 2;
786 }
787 
788 bool
790 {
791  debugs(55, 9, this << " joining for id " << id);
792  /* only fields from ListHeaders array can be "listed" */
793  assert(Http::HeaderLookupTable.lookup(id).list);
794 
795  if (!CBIT_TEST(mask, id))
796  return false;
797 
798  for (auto e: entries) {
799  if (e && e->id == id)
800  strListAdd(s, e->value.termedBuf(), ',');
801  }
802 
803  /*
804  * note: we might get an empty (size==0) string if there was an "empty"
805  * header. This results in an empty length String, which may have a NULL
806  * buffer.
807  */
808  /* temporary warning: remove it? (Is it useful for diagnostics ?) */
809  if (!s->size())
810  debugs(55, 3, "empty list header: " << Http::HeaderLookupTable.lookup(id).name << "(" << id << ")");
811  else
812  debugs(55, 6, this << ": joined for id " << id << ": " << s);
813 
814  return true;
815 }
816 
817 /* return a list of entries with the same id separated by ',' and ws */
818 String
820 {
821  HttpHeaderEntry *e;
823  debugs(55, 9, this << "joining for id " << id);
824  /* only fields from ListHeaders array can be "listed" */
825  assert(Http::HeaderLookupTable.lookup(id).list);
826 
827  if (!CBIT_TEST(mask, id))
828  return String();
829 
830  String s;
831 
832  while ((e = getEntry(&pos))) {
833  if (e->id == id)
834  strListAdd(&s, e->value.termedBuf(), ',');
835  }
836 
837  /*
838  * note: we might get an empty (size==0) string if there was an "empty"
839  * header. This results in an empty length String, which may have a NULL
840  * buffer.
841  */
842  /* temporary warning: remove it? (Is it useful for diagnostics ?) */
843  if (!s.size())
844  debugs(55, 3, "empty list header: " << Http::HeaderLookupTable.lookup(id).name << "(" << id << ")");
845  else
846  debugs(55, 6, this << ": joined for id " << id << ": " << s);
847 
848  return s;
849 }
850 
851 /* return a string or list of entries with the same id separated by ',' and ws */
852 String
854 {
855  HttpHeaderEntry *e;
856 
857  if (Http::HeaderLookupTable.lookup(id).list)
858  return getList(id);
859 
860  if ((e = findEntry(id)))
861  return e->value;
862 
863  return String();
864 }
865 
866 /*
867  * Returns the value of the specified header and/or an undefined String.
868  */
869 String
870 HttpHeader::getByName(const char *name) const
871 {
872  String result;
873  // ignore presence: return undefined string if an empty header is present
874  (void)hasNamed(name, strlen(name), &result);
875  return result;
876 }
877 
878 String
879 HttpHeader::getByName(const SBuf &name) const
880 {
881  String result;
882  // ignore presence: return undefined string if an empty header is present
883  (void)hasNamed(name, &result);
884  return result;
885 }
886 
887 String
889 {
890  String result;
891  (void)getByIdIfPresent(id, &result);
892  return result;
893 }
894 
895 bool
896 HttpHeader::hasNamed(const SBuf &s, String *result) const
897 {
898  return hasNamed(s.rawContent(), s.length(), result);
899 }
900 
901 bool
903 {
904  if (id == Http::HdrType::BAD_HDR)
905  return false;
906  if (!has(id))
907  return false;
908  if (result)
909  *result = getStrOrList(id);
910  return true;
911 }
912 
913 bool
914 HttpHeader::hasNamed(const char *name, unsigned int namelen, String *result) const
915 {
916  Http::HdrType id;
918  HttpHeaderEntry *e;
919 
920  assert(name);
921 
922  /* First try the quick path */
923  id = Http::HeaderLookupTable.lookup(name,namelen).id;
924 
925  if (id != Http::HdrType::BAD_HDR) {
926  if (getByIdIfPresent(id, result))
927  return true;
928  }
929 
930  /* Sorry, an unknown header name. Do linear search */
931  bool found = false;
932  while ((e = getEntry(&pos))) {
933  if (e->id == Http::HdrType::OTHER && e->name.length() == namelen && e->name.caseCmp(name, namelen) == 0) {
934  found = true;
935  if (!result)
936  break;
937  strListAdd(result, e->value.termedBuf(), ',');
938  }
939  }
940 
941  return found;
942 }
943 
944 /*
945  * Returns a the value of the specified list member, if any.
946  */
947 String
948 HttpHeader::getByNameListMember(const char *name, const char *member, const char separator) const
949 {
950  String header;
951  const char *pos = NULL;
952  const char *item;
953  int ilen;
954  int mlen = strlen(member);
955 
956  assert(name);
957 
958  header = getByName(name);
959 
960  String result;
961 
962  while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
963  if (strncmp(item, member, mlen) == 0 && item[mlen] == '=') {
964  result.append(item + mlen + 1, ilen - mlen - 1);
965  break;
966  }
967  }
968 
969  return result;
970 }
971 
972 /*
973  * returns a the value of the specified list member, if any.
974  */
975 String
976 HttpHeader::getListMember(Http::HdrType id, const char *member, const char separator) const
977 {
978  String header;
979  const char *pos = NULL;
980  const char *item;
981  int ilen;
982  int mlen = strlen(member);
983 
985 
986  header = getStrOrList(id);
987  String result;
988 
989  while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
990  if (strncmp(item, member, mlen) == 0 && item[mlen] == '=') {
991  result.append(item + mlen + 1, ilen - mlen - 1);
992  break;
993  }
994  }
995 
996  header.clean();
997  return result;
998 }
999 
1000 /* test if a field is present */
1001 int
1003 {
1005  debugs(55, 9, this << " lookup for " << id);
1006  return CBIT_TEST(mask, id);
1007 }
1008 
1009 void
1011 {
1012  // TODO: do not add Via header for messages where Squid itself
1013  // generated the message (i.e., Downloader or ESI) there should be no Via header added at all.
1014 
1015  if (Config.onoff.via) {
1016  SBuf buf;
1017  // RFC 7230 section 5.7.1.: protocol-name is omitted when
1018  // the received protocol is HTTP.
1021  buf.appendf("%s/", AnyP::ProtocolType_str[ver.protocol]);
1022  buf.appendf("%d.%d %s", ver.major, ver.minor, ThisCache);
1023  const HttpHeader *hdr = from ? from : this;
1024  SBuf strVia = StringToSBuf(hdr->getList(Http::HdrType::VIA));
1025  if (!strVia.isEmpty())
1026  strVia.append(", ", 2);
1027  strVia.append(buf);
1028  // XXX: putStr() still suffers from String size limits
1029  Must(strVia.length() < String::SizeMaxXXX());
1031  putStr(Http::HdrType::VIA, strVia.c_str());
1032  }
1033 }
1034 
1035 void
1037 {
1039  assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftInt); /* must be of an appropriate type */
1040  assert(number >= 0);
1041  addEntry(new HttpHeaderEntry(id, SBuf(), xitoa(number)));
1042 }
1043 
1044 void
1046 {
1048  assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftInt64); /* must be of an appropriate type */
1049  assert(number >= 0);
1050  addEntry(new HttpHeaderEntry(id, SBuf(), xint64toa(number)));
1051 }
1052 
1053 void
1055 {
1057  assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftDate_1123); /* must be of an appropriate type */
1058  assert(htime >= 0);
1059  addEntry(new HttpHeaderEntry(id, SBuf(), mkrfc1123(htime)));
1060 }
1061 
1062 void
1063 HttpHeader::putStr(Http::HdrType id, const char *str)
1064 {
1066  assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftStr); /* must be of an appropriate type */
1067  assert(str);
1068  addEntry(new HttpHeaderEntry(id, SBuf(), str));
1069 }
1070 
1071 void
1072 HttpHeader::putAuth(const char *auth_scheme, const char *realm)
1073 {
1074  assert(auth_scheme && realm);
1075  httpHeaderPutStrf(this, Http::HdrType::WWW_AUTHENTICATE, "%s realm=\"%s\"", auth_scheme, realm);
1076 }
1077 
1078 void
1080 {
1081  assert(cc);
1082  /* remove old directives if any */
1084  /* pack into mb */
1085  MemBuf mb;
1086  mb.init();
1087  cc->packInto(&mb);
1088  /* put */
1090  /* cleanup */
1091  mb.clean();
1092 }
1093 
1094 void
1096 {
1097  assert(cr);
1098  /* remove old directives if any */
1100  /* pack into mb */
1101  MemBuf mb;
1102  mb.init();
1103  httpHdrContRangePackInto(cr, &mb);
1104  /* put */
1106  /* cleanup */
1107  mb.clean();
1108 }
1109 
1110 void
1112 {
1113  assert(range);
1114  /* remove old directives if any */
1116  /* pack into mb */
1117  MemBuf mb;
1118  mb.init();
1119  range->packInto(&mb);
1120  /* put */
1122  /* cleanup */
1123  mb.clean();
1124 }
1125 
1126 void
1128 {
1129  assert(sc);
1130  /* remove old directives if any */
1132  /* pack into mb */
1133  MemBuf mb;
1134  mb.init();
1135  sc->packInto(&mb);
1136  /* put */
1138  /* cleanup */
1139  mb.clean();
1140 }
1141 
1142 void
1143 HttpHeader::putWarning(const int code, const char *const text)
1144 {
1145  char buf[512];
1146  snprintf(buf, sizeof(buf), "%i %s \"%s\"", code, visible_appname_string, text);
1148 }
1149 
1150 /* add extension header (these fields are not parsed/analyzed/joined, etc.) */
1151 void
1152 HttpHeader::putExt(const char *name, const char *value)
1153 {
1154  assert(name && value);
1155  debugs(55, 8, this << " adds ext entry " << name << " : " << value);
1156  addEntry(new HttpHeaderEntry(Http::HdrType::OTHER, SBuf(name), value));
1157 }
1158 
1159 int
1161 {
1163  assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftInt); /* must be of an appropriate type */
1164  HttpHeaderEntry *e;
1165 
1166  if ((e = findEntry(id)))
1167  return e->getInt();
1168 
1169  return -1;
1170 }
1171 
1172 int64_t
1174 {
1176  assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftInt64); /* must be of an appropriate type */
1177  HttpHeaderEntry *e;
1178 
1179  if ((e = findEntry(id)))
1180  return e->getInt64();
1181 
1182  return -1;
1183 }
1184 
1185 time_t
1187 {
1188  HttpHeaderEntry *e;
1189  time_t value = -1;
1191  assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftDate_1123); /* must be of an appropriate type */
1192 
1193  if ((e = findEntry(id))) {
1194  value = parse_rfc1123(e->value.termedBuf());
1195  httpHeaderNoteParsedEntry(e->id, e->value, value < 0);
1196  }
1197 
1198  return value;
1199 }
1200 
1201 /* sync with httpHeaderGetLastStr */
1202 const char *
1204 {
1205  HttpHeaderEntry *e;
1207  assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftStr); /* must be of an appropriate type */
1208 
1209  if ((e = findEntry(id))) {
1210  httpHeaderNoteParsedEntry(e->id, e->value, false); /* no errors are possible */
1211  return e->value.termedBuf();
1212  }
1213 
1214  return NULL;
1215 }
1216 
1217 /* unusual */
1218 const char *
1220 {
1221  HttpHeaderEntry *e;
1223  assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftStr); /* must be of an appropriate type */
1224 
1225  if ((e = findLastEntry(id))) {
1226  httpHeaderNoteParsedEntry(e->id, e->value, false); /* no errors are possible */
1227  return e->value.termedBuf();
1228  }
1229 
1230  return NULL;
1231 }
1232 
1233 HttpHdrCc *
1235 {
1237  return NULL;
1238  PROF_start(HttpHeader_getCc);
1239 
1240  String s;
1242 
1243  HttpHdrCc *cc=new HttpHdrCc();
1244 
1245  if (!cc->parse(s)) {
1246  delete cc;
1247  cc = NULL;
1248  }
1249 
1250  ++ HttpHeaderStats[owner].ccParsedCount;
1251 
1252  if (cc)
1253  httpHdrCcUpdateStats(cc, &HttpHeaderStats[owner].ccTypeDistr);
1254 
1256 
1257  PROF_stop(HttpHeader_getCc);
1258 
1259  return cc;
1260 }
1261 
1262 HttpHdrRange *
1264 {
1265  HttpHdrRange *r = NULL;
1266  HttpHeaderEntry *e;
1267  /* some clients will send "Request-Range" _and_ *matching* "Range"
1268  * who knows, some clients might send Request-Range only;
1269  * this "if" should work correctly in both cases;
1270  * hopefully no clients send mismatched headers! */
1271 
1272  if ((e = findEntry(Http::HdrType::RANGE)) ||
1275  httpHeaderNoteParsedEntry(e->id, e->value, !r);
1276  }
1277 
1278  return r;
1279 }
1280 
1281 HttpHdrSc *
1283 {
1285  return NULL;
1286 
1287  String s;
1288 
1290 
1292 
1293  ++ HttpHeaderStats[owner].ccParsedCount;
1294 
1295  if (sc)
1296  sc->updateStats(&HttpHeaderStats[owner].scTypeDistr);
1297 
1299 
1300  return sc;
1301 }
1302 
1305 {
1306  HttpHdrContRange *cr = NULL;
1307  HttpHeaderEntry *e;
1308 
1311  httpHeaderNoteParsedEntry(e->id, e->value, !cr);
1312  }
1313 
1314  return cr;
1315 }
1316 
1317 const char *
1318 HttpHeader::getAuth(Http::HdrType id, const char *auth_scheme) const
1319 {
1320  const char *field;
1321  int l;
1322  assert(auth_scheme);
1323  field = getStr(id);
1324 
1325  if (!field) /* no authorization field */
1326  return NULL;
1327 
1328  l = strlen(auth_scheme);
1329 
1330  if (!l || strncasecmp(field, auth_scheme, l)) /* wrong scheme */
1331  return NULL;
1332 
1333  field += l;
1334 
1335  if (!xisspace(*field)) /* wrong scheme */
1336  return NULL;
1337 
1338  /* skip white space */
1339  for (; field && xisspace(*field); ++field);
1340 
1341  if (!*field) /* no authorization cookie */
1342  return NULL;
1343 
1344  static char decodedAuthToken[8192];
1345  struct base64_decode_ctx ctx;
1346  base64_decode_init(&ctx);
1347  size_t decodedLen = 0;
1348  if (!base64_decode_update(&ctx, &decodedLen, reinterpret_cast<uint8_t*>(decodedAuthToken), strlen(field), field) ||
1349  !base64_decode_final(&ctx)) {
1350  return NULL;
1351  }
1352  decodedAuthToken[decodedLen] = '\0';
1353  return decodedAuthToken;
1354 }
1355 
1356 ETag
1358 {
1359  ETag etag = {NULL, -1};
1360  HttpHeaderEntry *e;
1361  assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftETag); /* must be of an appropriate type */
1362 
1363  if ((e = findEntry(id)))
1364  etagParseInit(&etag, e->value.termedBuf());
1365 
1366  return etag;
1367 }
1368 
1369 TimeOrTag
1371 {
1372  TimeOrTag tot;
1373  HttpHeaderEntry *e;
1374  assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftDate_1123_or_ETag); /* must be of an appropriate type */
1375  memset(&tot, 0, sizeof(tot));
1376 
1377  if ((e = findEntry(id))) {
1378  const char *str = e->value.termedBuf();
1379  /* try as an ETag */
1380 
1381  if (etagParseInit(&tot.tag, str)) {
1382  tot.valid = tot.tag.str != NULL;
1383  tot.time = -1;
1384  } else {
1385  /* or maybe it is time? */
1386  tot.time = parse_rfc1123(str);
1387  tot.valid = tot.time >= 0;
1388  tot.tag.str = NULL;
1389  }
1390  }
1391 
1392  assert(tot.time < 0 || !tot.tag.str); /* paranoid */
1393  return tot;
1394 }
1395 
1396 /*
1397  * HttpHeaderEntry
1398  */
1399 
1400 HttpHeaderEntry::HttpHeaderEntry(Http::HdrType anId, const SBuf &aName, const char *aValue)
1401 {
1403  id = anId;
1404 
1405  if (id != Http::HdrType::OTHER)
1407  else
1408  name = aName;
1409 
1410  value = aValue;
1411 
1412  if (id != Http::HdrType::BAD_HDR)
1413  ++ headerStatsTable[id].aliveCount;
1414 
1415  debugs(55, 9, "created HttpHeaderEntry " << this << ": '" << name << " : " << value );
1416 }
1417 
1419 {
1420  debugs(55, 9, "destroying entry " << this << ": '" << name << ": " << value << "'");
1421 
1422  if (id != Http::HdrType::BAD_HDR) {
1423  assert(headerStatsTable[id].aliveCount);
1424  -- headerStatsTable[id].aliveCount;
1425  id = Http::HdrType::BAD_HDR; // it already is BAD_HDR, no sense in resetting it
1426  }
1427 
1428 }
1429 
1430 /* parses and inits header entry, returns true/false */
1432 HttpHeaderEntry::parse(const char *field_start, const char *field_end)
1433 {
1434  /* note: name_start == field_start */
1435  const char *name_end = (const char *)memchr(field_start, ':', field_end - field_start);
1436  int name_len = name_end ? name_end - field_start :0;
1437  const char *value_start = field_start + name_len + 1; /* skip ':' */
1438  /* note: value_end == field_end */
1439 
1441 
1442  /* do we have a valid field name within this field? */
1443 
1444  if (!name_len || name_end > field_end)
1445  return NULL;
1446 
1447  if (name_len > 65534) {
1448  /* String must be LESS THAN 64K and it adds a terminating NULL */
1449  debugs(55, DBG_IMPORTANT, "WARNING: ignoring header name of " << name_len << " bytes");
1450  return NULL;
1451  }
1452 
1453  if (Config.onoff.relaxed_header_parser && xisspace(field_start[name_len - 1])) {
1454  debugs(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2,
1455  "NOTICE: Whitespace after header name in '" << getStringPrefix(field_start, field_end-field_start) << "'");
1456 
1457  while (name_len > 0 && xisspace(field_start[name_len - 1]))
1458  --name_len;
1459 
1460  if (!name_len)
1461  return NULL;
1462  }
1463 
1464  /* now we know we can parse it */
1465 
1466  debugs(55, 9, "parsing HttpHeaderEntry: near '" << getStringPrefix(field_start, field_end-field_start) << "'");
1467 
1468  /* is it a "known" field? */
1469  Http::HdrType id = Http::HeaderLookupTable.lookup(field_start,name_len).id;
1470  debugs(55, 9, "got hdr-id=" << id);
1471 
1472  SBuf theName;
1473 
1474  String value;
1475 
1476  if (id == Http::HdrType::BAD_HDR)
1477  id = Http::HdrType::OTHER;
1478 
1479  /* set field name */
1480  if (id == Http::HdrType::OTHER)
1481  theName.append(field_start, name_len);
1482  else
1483  theName = Http::HeaderLookupTable.lookup(id).name;
1484 
1485  /* trim field value */
1486  while (value_start < field_end && xisspace(*value_start))
1487  ++value_start;
1488 
1489  while (value_start < field_end && xisspace(field_end[-1]))
1490  --field_end;
1491 
1492  if (field_end - value_start > 65534) {
1493  /* String must be LESS THAN 64K and it adds a terminating NULL */
1494  debugs(55, DBG_IMPORTANT, "WARNING: ignoring '" << theName << "' header of " << (field_end - value_start) << " bytes");
1495  return NULL;
1496  }
1497 
1498  /* set field value */
1499  value.limitInit(value_start, field_end - value_start);
1500 
1501  if (id != Http::HdrType::BAD_HDR)
1502  ++ headerStatsTable[id].seenCount;
1503 
1504  debugs(55, 9, "parsed HttpHeaderEntry: '" << theName << ": " << value << "'");
1505 
1506  return new HttpHeaderEntry(id, theName, value.termedBuf());
1507 }
1508 
1511 {
1512  return new HttpHeaderEntry(id, name, value.termedBuf());
1513 }
1514 
1515 void
1517 {
1518  assert(p);
1519  p->append(name.rawContent(), name.length());
1520  p->append(": ", 2);
1521  p->append(value.rawBuf(), value.size());
1522  p->append("\r\n", 2);
1523 }
1524 
1525 int
1527 {
1528  int val = -1;
1529  int ok = httpHeaderParseInt(value.termedBuf(), &val);
1530  httpHeaderNoteParsedEntry(id, value, ok == 0);
1531  /* XXX: Should we check ok - ie
1532  * return ok ? -1 : value;
1533  */
1534  return val;
1535 }
1536 
1537 int64_t
1539 {
1540  int64_t val = -1;
1541  const bool ok = httpHeaderParseOffset(value.termedBuf(), &val);
1542  httpHeaderNoteParsedEntry(id, value, !ok);
1543  return val; // remains -1 if !ok (XXX: bad method API)
1544 }
1545 
1546 static void
1548 {
1549  if (id != Http::HdrType::BAD_HDR)
1550  ++ headerStatsTable[id].parsCount;
1551 
1552  if (error) {
1553  if (id != Http::HdrType::BAD_HDR)
1554  ++ headerStatsTable[id].errCount;
1555  debugs(55, 2, "cannot parse hdr field: '" << Http::HeaderLookupTable.lookup(id).name << ": " << context << "'");
1556  }
1557 }
1558 
1559 /*
1560  * Reports
1561  */
1562 
1563 /* tmp variable used to pass stat info to dumpers */
1564 extern const HttpHeaderStat *dump_stat; /* argh! */
1565 const HttpHeaderStat *dump_stat = NULL;
1566 
1567 void
1568 httpHeaderFieldStatDumper(StoreEntry * sentry, int, double val, double, int count)
1569 {
1570  const int id = static_cast<int>(val);
1571  const bool valid_id = Http::any_valid_header(static_cast<Http::HdrType>(id));
1572  const char *name = valid_id ? Http::HeaderLookupTable.lookup(static_cast<Http::HdrType>(id)).name : "INVALID";
1573  int visible = count > 0;
1574  /* for entries with zero count, list only those that belong to current type of message */
1575 
1576  if (!visible && valid_id && dump_stat->owner_mask)
1577  visible = CBIT_TEST(*dump_stat->owner_mask, id);
1578 
1579  if (visible)
1580  storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
1581  id, name, count, xdiv(count, dump_stat->busyDestroyedCount));
1582 }
1583 
1584 static void
1585 httpHeaderFldsPerHdrDumper(StoreEntry * sentry, int idx, double val, double, int count)
1586 {
1587  if (count)
1588  storeAppendPrintf(sentry, "%2d\t %5d\t %5d\t %6.2f\n",
1589  idx, (int) val, count,
1590  xpercent(count, dump_stat->destroyedCount));
1591 }
1592 
1593 static void
1595 {
1596  assert(hs);
1597  assert(e);
1598 
1599  dump_stat = hs;
1600  storeAppendPrintf(e, "\nHeader Stats: %s\n", hs->label);
1601  storeAppendPrintf(e, "\nField type distribution\n");
1602  storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n",
1603  "id", "name", "count", "#/header");
1605  storeAppendPrintf(e, "\nCache-control directives distribution\n");
1606  storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n",
1607  "id", "name", "count", "#/cc_field");
1609  storeAppendPrintf(e, "\nSurrogate-control directives distribution\n");
1610  storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n",
1611  "id", "name", "count", "#/sc_field");
1613  storeAppendPrintf(e, "\nNumber of fields per header distribution\n");
1614  storeAppendPrintf(e, "%2s\t %-5s\t %5s\t %6s\n",
1615  "id", "#flds", "count", "%total");
1617  storeAppendPrintf(e, "\n");
1618  dump_stat = NULL;
1619 }
1620 
1621 void
1623 {
1624  int i;
1625  assert(e);
1626 
1627  HttpHeaderStats[0].parsedCount =
1628  HttpHeaderStats[hoRequest].parsedCount + HttpHeaderStats[hoReply].parsedCount;
1629  HttpHeaderStats[0].ccParsedCount =
1630  HttpHeaderStats[hoRequest].ccParsedCount + HttpHeaderStats[hoReply].ccParsedCount;
1631  HttpHeaderStats[0].destroyedCount =
1632  HttpHeaderStats[hoRequest].destroyedCount + HttpHeaderStats[hoReply].destroyedCount;
1633  HttpHeaderStats[0].busyDestroyedCount =
1634  HttpHeaderStats[hoRequest].busyDestroyedCount + HttpHeaderStats[hoReply].busyDestroyedCount;
1635 
1636  for (i = 1; i < HttpHeaderStatCount; ++i) {
1637  httpHeaderStatDump(HttpHeaderStats + i, e);
1638  }
1639 
1640  /* field stats for all messages */
1641  storeAppendPrintf(e, "\nHttp Fields Stats (replies and requests)\n");
1642 
1643  storeAppendPrintf(e, "%2s\t %-25s\t %5s\t %6s\t %6s\n",
1644  "id", "name", "#alive", "%err", "%repeat");
1645 
1646  // scan heaaderTable and output
1647  for (auto h : WholeEnum<Http::HdrType>()) {
1648  auto stats = headerStatsTable[h];
1649  storeAppendPrintf(e, "%2d\t %-25s\t %5d\t %6.3f\t %6.3f\n",
1650  Http::HeaderLookupTable.lookup(h).id,
1651  Http::HeaderLookupTable.lookup(h).name,
1652  stats.aliveCount,
1653  xpercent(stats.errCount, stats.parsCount),
1654  xpercent(stats.repCount, stats.seenCount));
1655  }
1656 
1657  storeAppendPrintf(e, "Headers Parsed: %d + %d = %d\n",
1658  HttpHeaderStats[hoRequest].parsedCount,
1659  HttpHeaderStats[hoReply].parsedCount,
1660  HttpHeaderStats[0].parsedCount);
1661  storeAppendPrintf(e, "Hdr Fields Parsed: %d\n", HeaderEntryParsedCount);
1662 }
1663 
1664 int
1665 HttpHeader::hasListMember(Http::HdrType id, const char *member, const char separator) const
1666 {
1667  int result = 0;
1668  const char *pos = NULL;
1669  const char *item;
1670  int ilen;
1671  int mlen = strlen(member);
1672 
1674 
1675  String header (getStrOrList(id));
1676 
1677  while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
1678  if (strncasecmp(item, member, mlen) == 0
1679  && (item[mlen] == '=' || item[mlen] == separator || item[mlen] == ';' || item[mlen] == '\0')) {
1680  result = 1;
1681  break;
1682  }
1683  }
1684 
1685  return result;
1686 }
1687 
1688 int
1689 HttpHeader::hasByNameListMember(const char *name, const char *member, const char separator) const
1690 {
1691  int result = 0;
1692  const char *pos = NULL;
1693  const char *item;
1694  int ilen;
1695  int mlen = strlen(member);
1696 
1697  assert(name);
1698 
1699  String header (getByName(name));
1700 
1701  while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
1702  if (strncasecmp(item, member, mlen) == 0
1703  && (item[mlen] == '=' || item[mlen] == separator || item[mlen] == ';' || item[mlen] == '\0')) {
1704  result = 1;
1705  break;
1706  }
1707  }
1708 
1709  return result;
1710 }
1711 
1712 void
1714 {
1716 
1717  const HttpHeaderEntry *e;
1719  int headers_deleted = 0;
1720  while ((e = getEntry(&pos))) {
1721  Http::HdrType id = e->id;
1722  if (Http::HeaderLookupTable.lookup(id).hopbyhop) {
1723  delAt(pos, headers_deleted);
1724  CBIT_CLR(mask, id);
1725  }
1726  }
1727 }
1728 
1729 void
1731 {
1733  /* anything that matches Connection list member will be deleted */
1734  String strConnection;
1735 
1736  (void) getList(Http::HdrType::CONNECTION, &strConnection);
1737  const HttpHeaderEntry *e;
1739  /*
1740  * think: on-average-best nesting of the two loops (hdrEntry
1741  * and strListItem) @?@
1742  */
1743  /*
1744  * maybe we should delete standard stuff ("keep-alive","close")
1745  * from strConnection first?
1746  */
1747 
1748  int headers_deleted = 0;
1749  while ((e = getEntry(&pos))) {
1750  if (strListIsMember(&strConnection, e->name, ','))
1751  delAt(pos, headers_deleted);
1752  }
1753  if (headers_deleted)
1754  refreshMask();
1755  }
1756 }
1757 
int delById(Http::HdrType id)
Definition: HttpHeader.cc:672
int caseCmp(const SBuf &S, const size_type n) const
shorthand version for case-insensitive compare()
Definition: SBuf.h:273
HttpHdrSc * httpHdrScParseCreate(const String &str)
Definition: HttpHdrSc.cc:60
void removeConnectionHeaderEntries()
Definition: HttpHeader.cc:1730
int has(Http::HdrType id) const
Definition: HttpHeader.cc:1002
bool httpHeaderParseOffset(const char *start, int64_t *value, char **endPtr)
void count(double val)
Definition: StatHist.cc:57
void httpHeaderFieldStatDumper(StoreEntry *sentry, int, double val, double, int count)
Definition: HttpHeader.cc:1568
int strListGetItem(const String *str, char del, const char **item, int *ilen, const char **pos)
Definition: StrList.cc:77
void limitInit(const char *str, int len)
Definition: String.cc:94
bool needUpdate(const HttpHeader *fresh) const
check whether the fresh header has any new/changed updatable fields
Definition: HttpHeader.cc:246
void httpHdrScInitModule(void)
Definition: HttpHdrSc.cc:49
#define assert(EX)
Definition: assert.h:17
String getListMember(Http::HdrType id, const char *member, const char separator) const
Definition: HttpHeader.cc:976
SBuf & appendf(const char *fmt,...)
Definition: SBuf.cc:251
time_t parse_rfc1123(const char *str)
Definition: rfc1123.c:159
static int sc[16]
Definition: smbdes.c:121
#define HttpHeaderInitPos
Definition: HttpHeader.h:48
const char * mkrfc1123(time_t)
Definition: rfc1123.c:202
http_hdr_owner_type
Definition: HttpHeader.h:31
HttpHdrCc * getCc() const
Definition: HttpHeader.cc:1234
SQUIDCEXTERN double xpercent(double part, double whole)
Definition: util.c:54
#define CBIT_TEST(mask, bit)
Definition: defines.h:114
static const CharacterSet LF
Definition: CharacterSet.h:89
SQUIDCEXTERN const char * xitoa(int num)
Definition: util.c:79
bool hasNamed(const SBuf &s, String *value=0) const
Definition: HttpHeader.cc:896
Definition: ETag.h:17
int type
Definition: errorpage.cc:79
Definition: SBuf.h:87
void putContRange(const HttpHdrContRange *cr)
Definition: HttpHeader.cc:1095
std::vector< HttpHeaderEntry * > entries
Definition: HttpHeader.h:156
int collapsed_forwarding
Definition: SquidConfig.h:323
int etagParseInit(ETag *etag, const char *str)
Definition: ETag.cc:29
bool any_HdrType_enum_value(const Http::HdrType id)
match any known header type, including OTHER and BAD
time_t time
Definition: TimeOrTag.h:21
struct _request * request(char *urlin)
Definition: tcp-banger2.c:291
void error(char *format,...)
int delByName(const SBuf &name)
Definition: HttpHeader.cc:653
int i
Definition: membanger.c:49
void removeHopByHopEntries()
Definition: HttpHeader.cc:1713
SBuf & append(const SBuf &S)
Definition: SBuf.cc:207
static bool Isolate(const char **parse_start, size_t l, const char **blk_start, const char **blk_end)
Definition: HttpHeader.cc:322
HttpHeaderMask mask
Definition: HttpHeader.h:157
void putExt(const char *name, const char *value)
Definition: HttpHeader.cc:1152
class Ping::pingStats_ stats
void base64_decode_init(struct base64_decode_ctx *ctx)
Definition: base64.c:54
void httpHdrCcStatDumper(StoreEntry *sentry, int, double val, double, int count)
Definition: HttpHdrCc.cc:330
static size_type SizeMaxXXX()
Definition: SquidString.h:69
HttpHeaderEntry * getEntry(HttpHeaderPos *pos) const
Definition: HttpHeader.cc:589
void strListAdd(String *str, const char *item, char del)
Definition: StrList.cc:19
bool conflictingContentLength_
found different Content-Length fields
Definition: HttpHeader.h:177
int64_t getInt64() const
Definition: HttpHeader.cc:1538
#define xisspace(x)
Definition: xis.h:17
HttpHdrRange * getRange() const
Definition: HttpHeader.cc:1263
bool isEmpty() const
Definition: SBuf.h:422
#define DBG_CRITICAL
Definition: Debug.h:44
char * p
Definition: membanger.c:43
int getInt() const
Definition: HttpHeader.cc:1526
String getByName(const SBuf &name) const
Definition: HttpHeader.cc:879
int strListIsMember(const String *list, const SBuf &m, char del)
Definition: StrList.cc:37
int getInt(Http::HdrType id) const
Definition: HttpHeader.cc:1160
size_type size() const
Definition: SquidString.h:71
void append(char const *buf, int len)
Definition: String.cc:161
virtual void append(const char *buf, int size)=0
Appends a c-string to existing packed data.
#define CBIT_SET(mask, bit)
Definition: defines.h:112
static void httpHeaderFldsPerHdrDumper(StoreEntry *sentry, int idx, double val, double, int count)
Definition: HttpHeader.cc:1585
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:105
ETag tag
Definition: TimeOrTag.h:20
bool skipUpdateHeader(const Http::HdrType id) const
Definition: HttpHeader.cc:273
static const CharacterSet WSP
Definition: CharacterSet.h:95
Http::HdrType id
Definition: HttpHeader.h:63
const char * headerWideProblem
worst header-wide problem found (or nil)
int hasByNameListMember(const char *name, const char *member, const char separator) const
Definition: HttpHeader.cc:1689
void httpHeaderPutStrf(HttpHeader *hdr, Http::HdrType id, const char *fmt,...)
void delAt(HttpHeaderPos pos, int &headers_deleted)
Definition: HttpHeader.cc:700
void packInto(Packable *p) const
void append(const HttpHeader *src)
Definition: HttpHeader.cc:232
TimeOrTag getTimeOrTag(Http::HdrType id) const
Definition: HttpHeader.cc:1370
unsigned int major
major version number
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:405
const char * getAuth(Http::HdrType id, const char *auth_scheme) const
Definition: HttpHeader.cc:1318
int relaxed_header_parser
Definition: SquidConfig.h:315
const char * getLastStr(Http::HdrType id) const
Definition: HttpHeader.cc:1219
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:123
String getByNameListMember(const char *name, const char *member, const char separator) const
Definition: HttpHeader.cc:948
void httpHdrScStatDumper(StoreEntry *sentry, int, double val, double, int count)
Definition: HttpHdrSc.cc:301
#define DBG_IMPORTANT
Definition: Debug.h:45
void RegisterAction(char const *action, char const *desc, OBJH *handler, int pw_req_flag, int atomic)
Definition: Registration.cc:16
char const * rawBuf() const
Definition: SquidString.h:84
HttpHdrSc * getSc() const
Definition: HttpHeader.cc:1282
const HeaderTableRecord & lookup(const char *buf, const std::size_t len) const
look record type up by name (C-string and length)
HttpHeaderEntry * findEntry(Http::HdrType id) const
Definition: HttpHeader.cc:608
int64_t getInt64(Http::HdrType id) const
Definition: HttpHeader.cc:1173
void putTime(Http::HdrType id, time_t htime)
Definition: HttpHeader.cc:1054
static HttpHeaderMask ReplyHeadersMask
Definition: HttpHeader.cc:73
HttpHeaderEntry(Http::HdrType id, const SBuf &name, const char *value)
Definition: HttpHeader.cc:1400
char const * termedBuf() const
Definition: SquidString.h:90
String getList(Http::HdrType id) const
Definition: HttpHeader.cc:819
static void httpHeaderRegisterWithCacheManager(void)
Definition: HttpHeader.cc:111
void updateStats(StatHist *) const
Definition: HttpHdrSc.cc:277
#define countof(arr)
Definition: defines.h:186
const char * label
static const CharacterSet CR
Definition: CharacterSet.h:77
http_hdr_owner_type owner
Definition: HttpHeader.h:158
char HttpHeaderMask[12]
bool parse(const String &s)
parse a header-string and fill in appropriate values.
Definition: HttpHdrCc.cc:94
const char * c_str()
Definition: SBuf.cc:546
optimized set of C chars, with quick membership test and merge support
Definition: CharacterSet.h:17
void putSc(HttpHdrSc *sc)
Definition: HttpHeader.cc:1127
void putWarning(const int code, const char *const text)
add a Warning header
Definition: HttpHeader.cc:1143
static HttpHdrRange * ParseCreate(const String *range_spec)
int parse(const char *header_start, size_t len)
Definition: HttpHeader.cc:367
bool update(HttpHeader const *fresh)
Definition: HttpHeader.cc:280
void refreshMask()
Definition: HttpHeader.cc:728
const char * getStringPrefix(const char *str, size_t sz)
void putInt(Http::HdrType id, int number)
Definition: HttpHeader.cc:1036
StatHist ccTypeDistr
int hasListMember(Http::HdrType id, const char *member, const char separator) const
Definition: HttpHeader.cc:1665
HttpHdrContRange * httpHdrContRangeParseCreate(const char *str)
HttpHeaderEntry * findLastEntry(Http::HdrType id) const
Definition: HttpHeader.cc:633
String getById(Http::HdrType id) const
Definition: HttpHeader.cc:888
int unsigned int const char *desc STUB void int len
Definition: stub_fd.cc:20
void addVia(const AnyP::ProtocolVersion &ver, const HttpHeader *from=0)
Definition: HttpHeader.cc:1010
unsigned char code
Definition: html_quote.c:20
void const char * buf
Definition: stub_helper.cc:16
ssize_t HttpHeaderPos
Definition: HttpHeader.h:45
struct SquidConfig::@112 onoff
String getStrOrList(Http::HdrType id) const
Definition: HttpHeader.cc:853
static int HeaderEntryParsedCount
Definition: HttpHeader.cc:91
#define Must(cond)
Definition: TextException.h:89
void httpHeaderInitModule(void)
Definition: HttpHeader.cc:119
HTTP per header statistics.
void packInto(Packable *p) const
Definition: HttpHeader.cc:1516
void clean()
Definition: MemBuf.cc:122
void httpHdrCcInitModule(void)
Module initialization hook.
Definition: HttpHdrCc.cc:60
StatHist fieldTypeDistr
void putInt64(Http::HdrType id, int64_t number)
Definition: HttpHeader.cc:1045
size_type findFirstOf(const CharacterSet &set, size_type startPos=0) const
Definition: SBuf.cc:753
char * buf
Definition: MemBuf.h:134
SQUIDCEXTERN const char * xint64toa(int64_t num)
Definition: util.c:88
#define CBIT_CLR(mask, bit)
Definition: defines.h:113
number
Definition: testStatHist.cc:16
void putStr(Http::HdrType id, const char *str)
Definition: HttpHeader.cc:1063
void clean()
Definition: HttpHeader.cc:184
int valid
Definition: TimeOrTag.h:22
const HeaderLookupTable_t HeaderLookupTable
bool any_valid_header(const Http::HdrType id)
match any valid header type, including OTHER but not BAD
#define PROF_start(probename)
Definition: Profiler.h:62
void putCc(const HttpHdrCc *cc)
Definition: HttpHeader.cc:1079
SQUIDCEXTERN double xdiv(double nom, double denom)
Definition: util.c:72
void httpHdrContRangePackInto(const HttpHdrContRange *range, Packable *p)
void updateWarnings()
Definition: HttpHeader.cc:260
void packInto(Packable *p) const
Definition: HttpHdrSc.cc:250
static void httpHeaderStatDump(const HttpHeaderStat *hs, StoreEntry *e)
Definition: HttpHeader.cc:1594
char ThisCache[RFC2181_MAXHOSTNAMELEN<< 1]
HttpHeader & operator=(const HttpHeader &other)
Definition: HttpHeader.cc:170
void compact()
Definition: HttpHeader.cc:717
bool chunked() const
whether message uses chunked Transfer-Encoding
Definition: HttpHeader.h:188
static const size_type npos
Definition: SBuf.h:93
std::vector< HttpHeaderFieldStat > headerStatsTable(Http::HdrType::enumEnd_)
void packInto(Packable *p) const
Definition: HttpHdrCc.cc:246
void httpHdrCcUpdateStats(const HttpHdrCc *cc, StatHist *hist)
Definition: HttpHdrCc.cc:320
Definition: MemBuf.h:23
bool any_registered_header(const Http::HdrType id)
time_t getTime(Http::HdrType id) const
Definition: HttpHeader.cc:1186
void packInto(Packable *p, bool mask_sensitive_info=false) const
Definition: HttpHeader.cc:545
const char * ProtocolType_str[]
static HttpHeaderEntry * parse(const char *field_start, const char *field_end)
Definition: HttpHeader.cc:1432
const char * str
quoted-string
Definition: ETag.h:20
bool sawBad
whether a malformed Content-Length value was present
const char * getStr(Http::HdrType id) const
Definition: HttpHeader.cc:1203
static int HttpHeaderStatCount
Definition: HttpHeader.cc:89
void clean()
Definition: String.cc:125
Definition: Debug.h:179
HttpHdrContRange * getContRange() const
Definition: HttpHeader.cc:1304
unsigned int minor
minor version number
#define PROF_stop(probename)
Definition: Profiler.h:63
SBuf StringToSBuf(const String &s)
create a new SBuf from a String by copying contents
Definition: StringConvert.h:17
void addEntry(HttpHeaderEntry *e)
Definition: HttpHeader.cc:742
static void httpHeaderNoteParsedEntry(Http::HdrType id, String const &value, bool error)
Definition: HttpHeader.cc:1547
void dump(StoreEntry *sentry, StatHistBinDumper *bd) const
Definition: StatHist.cc:173
void insertEntry(HttpHeaderEntry *e)
Definition: HttpHeader.cc:768
void httpHeaderMaskInit(HttpHeaderMask *mask, int value)
int base64_decode_update(struct base64_decode_ctx *ctx, size_t *dst_length, uint8_t *dst, size_t src_length, const char *src)
Definition: base64.c:129
const HttpHeaderStat * dump_stat
Definition: HttpHeader.cc:1565
ProtocolType protocol
which protocol this version is for
StatHist hdrUCountDistr
const char * rawContent() const
Definition: SBuf.cc:539
size_t headersEnd(const char *mime, size_t l, bool &containsObsFold)
Definition: mime_header.cc:16
void putRange(const HttpHdrRange *range)
Definition: HttpHeader.cc:1111
int base64_decode_final(struct base64_decode_ctx *ctx)
Definition: base64.c:159
HttpHeaderMask * owner_mask
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition: store.cc:904
char const * visible_appname_string
StatHist scTypeDistr
static HttpHeaderMask RequestHeadersMask
Definition: HttpHeader.cc:70
void putAuth(const char *auth_scheme, const char *realm)
Definition: HttpHeader.cc:1072
class SquidConfig Config
Definition: SquidConfig.cc:12
#define NULL
Definition: types.h:166
const char * text
Definition: errorpage.cc:80
static void httpHeaderStoreReport(StoreEntry *e)
Definition: HttpHeader.cc:1622
int httpHeaderParseInt(const char *start, int *value)
#define false
Definition: GnuRegex.c:233
bool getByIdIfPresent(Http::HdrType id, String *result) const
Definition: HttpHeader.cc:902
HttpHeaderEntry * clone() const
Definition: HttpHeader.cc:1510
ETag getETag(Http::HdrType id) const
Definition: HttpHeader.cc:1357

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors