117 "HTTP Header Statistics",
124 memset(mask, value,
sizeof(*mask));
131#define SHORT_PREFIX_SIZE 512
169 const char *end, *pos;
172 debugs(66, 2,
"failed to parse a quoted-string header field near '" << start <<
"'");
177 while (*pos !=
'"' && len > (pos-start)) {
181 if ((pos-start) > len || *pos !=
'\n') {
182 debugs(66, 2,
"failed to parse a quoted-string header field with '\\r' octet " << (start-pos)
183 <<
" bytes into '" << start <<
"'");
191 if ( (pos-start) > len || (*pos !=
' ' && *pos !=
'\t')) {
192 debugs(66, 2,
"failed to parse multiline quoted-string header field '" << start <<
"'");
199 debugs(66, 2,
"len < pos-start => " << len <<
" < " << (pos-start));
203 bool quoted = (*pos ==
'\\');
206 if (!*pos || (pos-start) > len) {
207 debugs(66, 2,
"failed to parse a quoted-string header field near '" << start <<
"'");
213 while (end < (start+len) && *end !=
'\\' && *end !=
'\"' && (
unsigned char)*end > 0x1F && *end != 0x7F)
215 if (((
unsigned char)*end <= 0x1F && *end !=
'\r' && *end !=
'\n') || *end == 0x7F) {
216 debugs(66, 2,
"failed to parse a quoted-string header field with CTL octet " << (start-pos)
217 <<
" bytes into '" << start <<
"'");
221 val->
append(pos, end-pos);
226 debugs(66, 2,
"failed to parse a quoted-string header field which did not end with \" ");
246 bool needInnerQuote =
false;
247 for (
const char *s = raw; !needInnerQuote && *s; ++s)
248 needInnerQuote = *s ==
'"' || *s ==
'\\';
253 if (needInnerQuote) {
254 for (
const char *s = raw; *s; ++s) {
255 if (*s ==
'"' || *s ==
'\\')
283 debugs(55, 7,
"init-ing hdr: " <<
this <<
" owner: " <<
owner);
305 if (
this != &other) {
322 debugs(55, 7,
"cleaning hdr: " <<
this <<
" owner: " <<
owner);
347 debugs(55,
DBG_CRITICAL,
"ERROR: Squid BUG: invalid entry (" << e->id <<
"). Ignored.");
368 debugs(55, 7,
"appending hdr: " <<
this <<
" += " << src);
379 for (
const auto e: fresh->
entries) {
408 while ((e = fresh->
getEntry(&pos))) {
421 while ((e = fresh->
getEntry(&pos))) {
440 const size_t end =
headersEnd(*parse_start, l);
443 *blk_start = *parse_start;
444 *blk_end = *parse_start + end - 1;
445 assert(**blk_end ==
'\n');
448 if (end > 1 && *(*blk_end - 1) ==
'\r')
458 const char *parse_start = buf;
459 const char *blk_start, *blk_end;
462 if (!
Isolate(&parse_start, buf_len, &blk_start, &blk_end)) {
467 blk_start = parse_start;
468 blk_end = blk_start + strlen(blk_start);
471 if (
parse(blk_start, blk_end - blk_start, clen)) {
472 hdr_sz = parse_start - buf;
484 const char *field_ptr = header_start;
485 const char *header_end = header_start + hdrLen;
488 assert(header_start && header_end);
489 debugs(55, 7,
"parsing hdr: (" <<
this <<
")" << std::endl <<
getStringPrefix(header_start, hdrLen));
493 if ((nulpos = (
char*)memchr(header_start,
'\0', hdrLen))) {
502 while (field_ptr < header_end) {
503 const char *field_start = field_ptr;
504 const char *field_end;
506 const char *hasBareCr =
nullptr;
509 const char *this_line = field_ptr;
510 field_ptr = (
const char *)memchr(field_ptr,
'\n', header_end - field_ptr);
519 field_end = field_ptr;
523 if (field_end > this_line && field_end[-1] ==
'\r') {
528 for (
const char *p = this_line; p < field_end && cr_only; ++p) {
534 "header field to prevent request smuggling attacks: {" <<
543 if (memchr(this_line,
'\r', field_end - this_line)) {
544 hasBareCr =
"bare CR";
545 debugs(55, warnOnError,
"WARNING: suspicious CR characters in HTTP header {" <<
549 char *p = (
char *) this_line;
551 while ((p = (
char *)memchr(p,
'\r', field_end - p)) !=
nullptr) {
561 if (this_line + 1 == field_end && this_line > field_start) {
562 debugs(55, warnOnError,
"WARNING: Blank continuation line in HTTP header {" <<
567 }
while (field_ptr < header_end && (*field_ptr ==
' ' || *field_ptr ==
'\t'));
569 if (field_start == field_end) {
570 if (field_ptr < header_end) {
571 debugs(55, warnOnError,
"WARNING: unparsable HTTP header field near {" <<
572 getStringPrefix(field_start, hdrLen-(field_start-header_start)) <<
"}");
582 debugs(55, warnOnError,
"WARNING: unparsable HTTP header field {" <<
590 if (lines > 1 || hasBareCr) {
594 debugs(55, warnOnError,
"WARNING: obs-fold in framing-sensitive " << e->name <<
": " << e->value);
616 " Content-Length field values in" <<
617 Raw(
"header", header_start, hdrLen));
643 if (rawTe.
caseCmp(
"chunked") == 0) {
645 }
else if (rawTe.
caseCmp(
"identity") == 0) {
649 debugs(55, warnOnError,
"WARNING: unsupported Transfer-Encoding used by client: " << rawTe);
663 debugs(55, 5,
"sanitized Content-Length to be " << clen.
value);
677 debugs(55, 7,
this <<
" into " << p <<
678 (mask_sensitive_info ?
" while masking" :
""));
681 if (!mask_sensitive_info) {
686 bool maskThisEntry =
false;
690 maskThisEntry =
true;
695 maskThisEntry = (cmd->value ==
"PASS");
703 p->
append(
": ** NOT DISPLAYED **\r\n", 23);
721 for (++(*pos); *pos < static_cast<ssize_t>(
entries.size()); ++(*pos)) {
737 assert(any_registered_header(
id));
747 if (e && e->id ==
id)
762 assert(any_registered_header(
id));
770 if (*e && (*e)->id ==
id)
785 debugs(55, 9,
"deleting '" << name <<
"' fields in hdr " <<
this);
788 if (!e->name.caseCmp(name))
801 debugs(55, 8,
this <<
" del-by-id " <<
id);
802 assert(any_registered_header(
id));
858 debugs(55, 7,
"refreshing the mask in hdr " <<
this);
872 assert(any_HdrType_enum_value(e->
id));
875 debugs(55, 7,
this <<
" adding entry: " << e->
id <<
" at " <<
entries.size());
893 debugs(55, 9,
this <<
" joining for id " <<
id);
901 if (e && e->id ==
id)
914 debugs(55, 6,
this <<
": joined for id " <<
id <<
": " << s);
925 debugs(55, 9,
this <<
"joining for id " <<
id);
948 debugs(55, 6,
this <<
": joined for id " <<
id <<
": " << s);
976 (void)
hasNamed(name, strlen(name), &result);
1054 return ::getListMember(header, member, separator);
1063 assert(any_registered_header(
id));
1065 return ::getListMember(header, member, separator);
1072 assert(any_registered_header(
id));
1073 debugs(55, 9,
this <<
" lookup for " <<
id);
1103 assert(any_registered_header(
id));
1112 assert(any_registered_header(
id));
1121 assert(any_registered_header(
id));
1130 assert(any_registered_header(
id));
1139 assert(auth_scheme && realm);
1211 debugs(55, 8,
this <<
" adds ext entry " << name <<
" : " << value);
1218 assert(any_registered_header(
id));
1225 auto newValueCopy = newValue;
1230 auto foundSameName =
false;
1232 if (!e || e->id !=
id)
1235 if (foundSameName) {
1242 if (newValue.
cmp(e->value.termedBuf()) != 0)
1245 foundSameName =
true;
1255 assert(any_registered_header(
id));
1268 assert(any_registered_header(
id));
1283 assert(any_registered_header(
id));
1299 assert(any_registered_header(
id));
1315 assert(any_registered_header(
id));
1337 if (!cc->
parse(s)) {
1415 static const SBuf nil;
1419 l = strlen(auth_scheme);
1421 if (!l || strncasecmp(field, auth_scheme, l))
1430 for (; field &&
xisspace(*field); ++field);
1435 const auto fieldLen = strlen(field);
1440 size_t decodedLen = 0;
1441 if (!
base64_decode_update(&ctx, &decodedLen,
reinterpret_cast<uint8_t*
>(decodedAuthToken), fieldLen, field) ||
1452 ETag etag = {
nullptr, -1};
1468 memset(&tot, 0,
sizeof(tot));
1495 assert(any_HdrType_enum_value(anId));
1508 debugs(55, 9,
"created HttpHeaderEntry " <<
this <<
": '" <<
name <<
" : " <<
value );
1513 debugs(55, 9,
"destroying entry " <<
this <<
": '" <<
name <<
": " <<
value <<
"'");
1528 const char *name_end = (
const char *)memchr(field_start,
':', field_end - field_start);
1529 int name_len = name_end ? name_end - field_start :0;
1530 const char *value_start = field_start + name_len + 1;
1537 if (!name_len || name_end > field_end)
1540 if (name_len > 65534) {
1543 debugs(55, 2,
"ignoring huge header field (" <<
Raw(
"field_start", field_start, 100) <<
"...)");
1556 if (
xisspace(field_start[name_len - 1])) {
1562 const bool stripWhitespace = (msgType ==
hoReply) ||
1564 if (!stripWhitespace)
1568 "WARNING: Whitespace after header name in '" <<
getStringPrefix(field_start, field_end-field_start) <<
"'");
1570 while (name_len > 0 &&
xisspace(field_start[name_len - 1]))
1574 debugs(55, 2,
"found header with only whitespace for name");
1585 for (
const char *pos = field_start; pos < (field_start+name_len); ++pos) {
1587 debugs(55, 2,
"found header with invalid characters in " <<
1588 Raw(
"field-name", field_start,
min(name_len,100)) <<
"...");
1595 debugs(55, 9,
"parsing HttpHeaderEntry: near '" <<
getStringPrefix(field_start, field_end-field_start) <<
"'");
1599 debugs(55, 9,
"got hdr-id=" <<
id);
1610 theName.
append(field_start, name_len);
1615 while (value_start < field_end &&
xisspace(*value_start))
1618 while (value_start < field_end &&
xisspace(field_end[-1]))
1621 if (field_end - value_start > 65534) {
1623 debugs(55, 2,
"WARNING: found '" << theName <<
"' header of " << (field_end - value_start) <<
" bytes");
1628 value.
assign(value_start, field_end - value_start);
1633 debugs(55, 9,
"parsed HttpHeaderEntry: '" << theName <<
": " <<
value <<
"'");
1699 const int id =
static_cast<int>(val);
1702 int visible = count > 0;
1718 idx, (
int) val, count,
1735 "id",
"name",
"count",
"#/header");
1739 "id",
"name",
"count",
"#/cc_field");
1743 "id",
"name",
"count",
"#/sc_field");
1747 "id",
"#flds",
"count",
"%total");
1774 "id",
"name",
"#alive",
"%err",
"%repeat");
1783 xpercent(stats.errCount, stats.parsCount),
1784 xpercent(stats.repCount, stats.seenCount));
1798 const char *pos =
nullptr;
1801 int mlen = strlen(member);
1803 assert(any_registered_header(
id));
1808 if (strncasecmp(item, member, mlen) == 0
1809 && (item[mlen] ==
'=' || item[mlen] == separator || item[mlen] ==
';' || item[mlen] ==
'\0')) {
1822 const char *pos =
nullptr;
1825 int mlen = strlen(member);
1832 if (strncasecmp(item, member, mlen) == 0
1833 && (item[mlen] ==
'=' || item[mlen] == separator || item[mlen] ==
';' || item[mlen] ==
'\0')) {
1849 int headers_deleted = 0;
1853 delAt(pos, headers_deleted);
1878 int headers_deleted = 0;
1881 delAt(pos, headers_deleted);
1883 if (headers_deleted)
#define Assure(condition)
int etagParseInit(ETag *etag, const char *str)
#define Here()
source code location of the caller
void httpHdrCcUpdateStats(const HttpHdrCc *cc, StatHist *hist)
void httpHdrCcStatDumper(StoreEntry *sentry, int, double val, double, int count)
void httpHdrContRangePackInto(const HttpHdrContRange *range, Packable *p)
HttpHdrContRange * httpHdrContRangeParseCreate(const char *str)
void httpHdrScStatDumper(StoreEntry *sentry, int, double val, double, int count)
HttpHdrSc * httpHdrScParseCreate(const String &str)
void httpHdrScInitModule(void)
int strListGetItem(const String *str, char del, const char **item, int *ilen, const char **pos)
void strListAdd(String &str, const char *item, const size_t itemSize, const char delimiter)
Appends the given item of a given size to a delimiter-separated list in str.
int strListIsMember(const String *list, const SBuf &m, char del)
SBuf StringToSBuf(const String &s)
create a new SBuf from a String by copying contents
void error(char *format,...)
void base64_decode_init(struct base64_decode_ctx *ctx)
int base64_decode_update(struct base64_decode_ctx *ctx, size_t *dst_length, uint8_t *dst, size_t src_length, const char *src)
int base64_decode_final(struct base64_decode_ctx *ctx)
#define BASE64_DECODE_LENGTH(length)
unsigned int major
major version number
ProtocolType protocol
which protocol this version is for
unsigned int minor
minor version number
static const CharacterSet TCHAR
const char * str
quoted-string
bool parse(const String &s)
parse a header-string and fill in appropriate values.
void packInto(Packable *p) const
void packInto(Packable *p) const
static HttpHdrRange * ParseCreate(const String *range_spec)
void packInto(Packable *p) const
void updateStats(StatHist *) const
const char * prohibitedAndIgnored() const
bool checkField(const String &field)
bool sawBad
whether a malformed Content-Length value was present
const char * headerWideProblem
worst header-wide problem found (or nil)
void init(mb_size_t szInit, mb_size_t szMax)
virtual void append(const char *buf, int size)=0
Appends a c-string to existing packed data.
char * rawAppendStart(size_type anticipatedSize)
int caseCmp(const SBuf &S, const size_type n) const
shorthand version for case-insensitive compare()
const char * rawContent() const
size_type length() const
Returns the number of bytes stored in SBuf.
SBuf & appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
int cmp(const SBuf &S, const size_type n) const
shorthand version for compare()
SBuf & append(const SBuf &S)
void rawAppendFinish(const char *start, size_type actualSize)
struct SquidConfig::@90 onoff
int relaxed_header_parser
void dump(StoreEntry *sentry, StatHistBinDumper *bd) const
static size_type SizeMaxXXX()
void assign(const char *str, int len)
char const * rawBuf() const
char const * termedBuf() const
void append(char const *buf, int len)
int caseCmp(char const *) const
an std::runtime_error with thrower location info
A const & min(A const &lhs, A const &rhs)
#define debugs(SECTION, LEVEL, CONTENT)
#define CBIT_SET(mask, bit)
#define CBIT_CLR(mask, bit)
#define CBIT_TEST(mask, bit)
char ThisCache[RFC2181_MAXHOSTNAMELEN<< 1]
const char * ProtocolType_str[]
SBuf SlowlyParseQuotedString(const char *description, const char *start, size_t length)
const HeaderLookupTable_t HeaderLookupTable
bool any_valid_header(const Http::HdrType id)
match any valid header type, including OTHER but not BAD
void RegisterAction(char const *action, char const *desc, OBJH *handler, Protected, Atomic, Format)
time_t ParseRfc1123(const char *)
Convert from RFC 1123 style time: "www, DD MMM YYYY hh:mm:ss ZZZ".
const char * FormatRfc1123(time_t)
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
#define LOCAL_ARRAY(type, name, size)
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
double xpercent(double part, double whole)
double xdiv(double nom, double denom)
const char * xitoa(int num)
const char * xint64toa(int64_t num)
char * xstrncpy(char *dst, const char *src, size_t n)