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) {
647 debugs(55, warnOnError,
"WARNING: unsupported Transfer-Encoding used by client: " << rawTe);
661 debugs(55, 5,
"sanitized Content-Length to be " << clen.
value);
675 debugs(55, 7,
this <<
" into " << p <<
676 (mask_sensitive_info ?
" while masking" :
""));
679 if (!mask_sensitive_info) {
684 bool maskThisEntry =
false;
688 maskThisEntry =
true;
693 maskThisEntry = (cmd->value ==
"PASS");
701 p->
append(
": ** NOT DISPLAYED **\r\n", 23);
719 for (++(*pos); *pos < static_cast<ssize_t>(
entries.size()); ++(*pos)) {
735 assert(any_registered_header(
id));
745 if (e && e->id ==
id)
760 assert(any_registered_header(
id));
768 if (*e && (*e)->id ==
id)
783 debugs(55, 9,
"deleting '" << name <<
"' fields in hdr " <<
this);
786 if (!e->name.caseCmp(name))
799 debugs(55, 8,
this <<
" del-by-id " <<
id);
800 assert(any_registered_header(
id));
856 debugs(55, 7,
"refreshing the mask in hdr " <<
this);
870 assert(any_HdrType_enum_value(e->
id));
873 debugs(55, 7,
this <<
" adding entry: " << e->
id <<
" at " <<
entries.size());
891 debugs(55, 9,
this <<
" joining for id " <<
id);
899 if (e && e->id ==
id)
912 debugs(55, 6,
this <<
": joined for id " <<
id <<
": " << s);
923 debugs(55, 9,
this <<
"joining for id " <<
id);
946 debugs(55, 6,
this <<
": joined for id " <<
id <<
": " << s);
974 (void)
hasNamed(name, strlen(name), &result);
1052 return ::getListMember(header, member, separator);
1061 assert(any_registered_header(
id));
1063 return ::getListMember(header, member, separator);
1070 assert(any_registered_header(
id));
1071 debugs(55, 9,
this <<
" lookup for " <<
id);
1101 assert(any_registered_header(
id));
1110 assert(any_registered_header(
id));
1119 assert(any_registered_header(
id));
1128 assert(any_registered_header(
id));
1137 assert(auth_scheme && realm);
1209 debugs(55, 8,
this <<
" adds ext entry " << name <<
" : " << value);
1216 assert(any_registered_header(
id));
1223 auto newValueCopy = newValue;
1228 auto foundSameName =
false;
1230 if (!e || e->id !=
id)
1233 if (foundSameName) {
1240 if (newValue.
cmp(e->value.termedBuf()) != 0)
1243 foundSameName =
true;
1253 assert(any_registered_header(
id));
1266 assert(any_registered_header(
id));
1281 assert(any_registered_header(
id));
1297 assert(any_registered_header(
id));
1313 assert(any_registered_header(
id));
1335 if (!cc->
parse(s)) {
1413 static const SBuf nil;
1417 l = strlen(auth_scheme);
1419 if (!l || strncasecmp(field, auth_scheme, l))
1428 for (; field &&
xisspace(*field); ++field);
1433 const auto fieldLen = strlen(field);
1438 size_t decodedLen = 0;
1439 if (!
base64_decode_update(&ctx, &decodedLen,
reinterpret_cast<uint8_t*
>(decodedAuthToken), fieldLen, field) ||
1450 ETag etag = {
nullptr, -1};
1466 memset(&tot, 0,
sizeof(tot));
1493 assert(any_HdrType_enum_value(anId));
1506 debugs(55, 9,
"created HttpHeaderEntry " <<
this <<
": '" <<
name <<
" : " <<
value );
1511 debugs(55, 9,
"destroying entry " <<
this <<
": '" <<
name <<
": " <<
value <<
"'");
1526 const char *name_end = (
const char *)memchr(field_start,
':', field_end - field_start);
1527 int name_len = name_end ? name_end - field_start :0;
1528 const char *value_start = field_start + name_len + 1;
1535 if (!name_len || name_end > field_end)
1538 if (name_len > 65534) {
1541 debugs(55, 2,
"ignoring huge header field (" <<
Raw(
"field_start", field_start, 100) <<
"...)");
1554 if (
xisspace(field_start[name_len - 1])) {
1560 const bool stripWhitespace = (msgType ==
hoReply) ||
1562 if (!stripWhitespace)
1566 "WARNING: Whitespace after header name in '" <<
getStringPrefix(field_start, field_end-field_start) <<
"'");
1568 while (name_len > 0 &&
xisspace(field_start[name_len - 1]))
1572 debugs(55, 2,
"found header with only whitespace for name");
1583 for (
const char *pos = field_start; pos < (field_start+name_len); ++pos) {
1585 debugs(55, 2,
"found header with invalid characters in " <<
1586 Raw(
"field-name", field_start,
min(name_len,100)) <<
"...");
1593 debugs(55, 9,
"parsing HttpHeaderEntry: near '" <<
getStringPrefix(field_start, field_end-field_start) <<
"'");
1597 debugs(55, 9,
"got hdr-id=" <<
id);
1608 theName.
append(field_start, name_len);
1613 while (value_start < field_end &&
xisspace(*value_start))
1616 while (value_start < field_end &&
xisspace(field_end[-1]))
1619 if (field_end - value_start > 65534) {
1621 debugs(55, 2,
"WARNING: found '" << theName <<
"' header of " << (field_end - value_start) <<
" bytes");
1626 value.
assign(value_start, field_end - value_start);
1631 debugs(55, 9,
"parsed HttpHeaderEntry: '" << theName <<
": " <<
value <<
"'");
1697 const int id =
static_cast<int>(val);
1700 int visible = count > 0;
1716 idx, (
int) val, count,
1733 "id",
"name",
"count",
"#/header");
1737 "id",
"name",
"count",
"#/cc_field");
1741 "id",
"name",
"count",
"#/sc_field");
1745 "id",
"#flds",
"count",
"%total");
1772 "id",
"name",
"#alive",
"%err",
"%repeat");
1781 xpercent(stats.errCount, stats.parsCount),
1782 xpercent(stats.repCount, stats.seenCount));
1796 const char *pos =
nullptr;
1799 int mlen = strlen(member);
1801 assert(any_registered_header(
id));
1806 if (strncasecmp(item, member, mlen) == 0
1807 && (item[mlen] ==
'=' || item[mlen] == separator || item[mlen] ==
';' || item[mlen] ==
'\0')) {
1820 const char *pos =
nullptr;
1823 int mlen = strlen(member);
1830 if (strncasecmp(item, member, mlen) == 0
1831 && (item[mlen] ==
'=' || item[mlen] == separator || item[mlen] ==
';' || item[mlen] ==
'\0')) {
1847 int headers_deleted = 0;
1851 delAt(pos, headers_deleted);
1876 int headers_deleted = 0;
1879 delAt(pos, headers_deleted);
1881 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)