/*
- * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
/* DEBUG: section 55 HTTP Header */
#include "squid.h"
-#include "base/LookupTable.h"
+#include "base/Assure.h"
+#include "base/CharacterSet.h"
+#include "base/EnumIterator.h"
+#include "base/Raw.h"
#include "base64.h"
#include "globals.h"
+#include "http/ContentLengthInterpreter.h"
#include "HttpHdrCc.h"
#include "HttpHdrContRange.h"
-#include "HttpHdrSc.h"
+#include "HttpHdrScTarget.h" // also includes HttpHdrSc.h
#include "HttpHeader.h"
-#include "HttpHeaderFieldInfo.h"
+#include "HttpHeaderFieldStat.h"
#include "HttpHeaderStat.h"
#include "HttpHeaderTools.h"
#include "MemBuf.h"
#include "mgr/Registration.h"
-#include "profiler/Profiler.h"
-#include "rfc1123.h"
+#include "mime_header.h"
+#include "sbuf/StringConvert.h"
#include "SquidConfig.h"
-#include "SquidString.h"
#include "StatHist.h"
#include "Store.h"
#include "StrList.h"
+#include "time/gadgets.h"
#include "TimeOrTag.h"
#include "util.h"
#include <algorithm>
+#include <array>
/* XXX: the whole set of API managing the entries vector should be rethought
* after the parse4r-ng effort is complete.
* local constants and vars
*/
-/* TODO:
- * DONE 1. shift HDR_BAD_HDR to end of enum
- * DONE 2. shift headers data array to http/RegistredHeaders.cc
- * DONE 3. creatign LookupTable object from teh enum and array
- * (with HDR_BAD_HDR as invalid value)
- * DONE 4. replacing httpHeaderIdByName() uses with the lookup table
- * 5. merge HDR_BAD_HDR and HDR_ENUM_END into one thing
- * DONE 6. replacing httpHeaderNameById with direct array lookups
- * 7. being looking at the other arrays removal
- */
-
-
-LookupTable<http_hdr_type, HeaderTableRecord> headerLookupTable(HDR_BAD_HDR, headerTable);
-std::vector<HttpHeaderFieldStat> headerStatsTable(HDR_OTHER+1);
-
-/*
- * headers with field values defined as #(values) in HTTP/1.1
- * Headers that are currently not recognized, are commented out.
- */
-static HttpHeaderMask ListHeadersMask; /* set run-time using ListHeadersArr */
-static http_hdr_type ListHeadersArr[] = {
- HDR_ACCEPT,
- HDR_ACCEPT_CHARSET,
- HDR_ACCEPT_ENCODING,
- HDR_ACCEPT_LANGUAGE,
- HDR_ACCEPT_RANGES,
- HDR_ALLOW,
- HDR_CACHE_CONTROL,
- HDR_CONTENT_ENCODING,
- HDR_CONTENT_LANGUAGE,
- HDR_CONNECTION,
- HDR_EXPECT,
- HDR_IF_MATCH,
- HDR_IF_NONE_MATCH,
- HDR_KEY,
- HDR_LINK,
- HDR_PRAGMA,
- HDR_PROXY_CONNECTION,
- HDR_PROXY_SUPPORT,
- HDR_TRANSFER_ENCODING,
- HDR_UPGRADE,
- HDR_VARY,
- HDR_VIA,
- HDR_WARNING,
- HDR_WWW_AUTHENTICATE,
- HDR_AUTHENTICATION_INFO,
- HDR_PROXY_AUTHENTICATION_INFO,
- /* HDR_TE, HDR_TRAILER */
-#if X_ACCELERATOR_VARY
- HDR_X_ACCELERATOR_VARY,
-#endif
-#if USE_ADAPTATION
- HDR_X_NEXT_SERVICES,
-#endif
- HDR_SURROGATE_CAPABILITY,
- HDR_SURROGATE_CONTROL,
- HDR_FORWARDED,
- HDR_X_FORWARDED_FOR
-};
+// statistics counters for headers. clients must not allow Http::HdrType::BAD_HDR to be counted
+std::vector<HttpHeaderFieldStat> headerStatsTable(Http::HdrType::enumEnd_);
-/* general-headers */
-static http_hdr_type GeneralHeadersArr[] = {
- HDR_CACHE_CONTROL,
- HDR_CONNECTION,
- HDR_DATE,
- HDR_FORWARDED,
- HDR_X_FORWARDED_FOR,
- HDR_MIME_VERSION,
- HDR_PRAGMA,
- HDR_PROXY_CONNECTION,
- HDR_TRANSFER_ENCODING,
- HDR_UPGRADE,
- /* HDR_TRAILER, */
- HDR_VIA,
-};
-
-/* entity-headers */
-static http_hdr_type EntityHeadersArr[] = {
- HDR_ALLOW,
- HDR_CONTENT_BASE,
- HDR_CONTENT_ENCODING,
- HDR_CONTENT_LANGUAGE,
- HDR_CONTENT_LENGTH,
- HDR_CONTENT_LOCATION,
- HDR_CONTENT_MD5,
- HDR_CONTENT_RANGE,
- HDR_CONTENT_TYPE,
- HDR_ETAG,
- HDR_EXPIRES,
- HDR_LAST_MODIFIED,
- HDR_LINK,
- HDR_OTHER
-};
-
-/* request-only headers */
+/* request-only headers. Used for cachemgr */
static HttpHeaderMask RequestHeadersMask; /* set run-time using RequestHeaders */
-static http_hdr_type RequestHeadersArr[] = {
- HDR_ACCEPT,
- HDR_ACCEPT_CHARSET,
- HDR_ACCEPT_ENCODING,
- HDR_ACCEPT_LANGUAGE,
- HDR_AUTHORIZATION,
- HDR_EXPECT,
- HDR_FROM,
- HDR_HOST,
- HDR_HTTP2_SETTINGS,
- HDR_IF_MATCH,
- HDR_IF_MODIFIED_SINCE,
- HDR_IF_NONE_MATCH,
- HDR_IF_RANGE,
- HDR_IF_UNMODIFIED_SINCE,
- HDR_MAX_FORWARDS,
- HDR_ORIGIN,
- HDR_PROXY_AUTHORIZATION,
- HDR_RANGE,
- HDR_REFERER,
- HDR_REQUEST_RANGE,
- HDR_TE,
- HDR_USER_AGENT,
- HDR_SURROGATE_CAPABILITY
-};
-/* reply-only headers */
+/* reply-only headers. Used for cachemgr */
static HttpHeaderMask ReplyHeadersMask; /* set run-time using ReplyHeaders */
-static http_hdr_type ReplyHeadersArr[] = {
- HDR_ACCEPT_ENCODING,
- HDR_ACCEPT_RANGES,
- HDR_AGE,
- HDR_KEY,
- HDR_LOCATION,
- HDR_PROXY_AUTHENTICATE,
- HDR_PUBLIC,
- HDR_RETRY_AFTER,
- HDR_SERVER,
- HDR_SET_COOKIE,
- HDR_SET_COOKIE2,
- HDR_VARY,
- HDR_WARNING,
- HDR_WWW_AUTHENTICATE,
- HDR_X_CACHE,
- HDR_X_CACHE_LOOKUP,
- HDR_X_REQUEST_URI,
-#if X_ACCELERATOR_VARY
- HDR_X_ACCELERATOR_VARY,
-#endif
-#if USE_ADAPTATION
- HDR_X_NEXT_SERVICES,
-#endif
- HDR_X_SQUID_ERROR,
- HDR_SURROGATE_CONTROL
-};
-
-/* hop-by-hop headers */
-static HttpHeaderMask HopByHopHeadersMask;
-static http_hdr_type HopByHopHeadersArr[] = {
- HDR_ALTERNATE_PROTOCOL,
- HDR_CONNECTION,
- HDR_HTTP2_SETTINGS,
- HDR_KEEP_ALIVE,
- /*HDR_PROXY_AUTHENTICATE, // removal handled specially for peer login */
- HDR_PROXY_AUTHORIZATION,
- HDR_TE,
- HDR_TRAILER,
- HDR_TRANSFER_ENCODING,
- HDR_UPGRADE,
- HDR_PROXY_CONNECTION
-};
/* header accounting */
// NP: keep in sync with enum http_hdr_owner_type
-static HttpHeaderStat HttpHeaderStats[] = {
- HttpHeaderStat(/*hoNone*/ "all", NULL),
+static std::array<HttpHeaderStat, hoEnd> HttpHeaderStats = {{
+ HttpHeaderStat(/*hoNone*/ "all", nullptr),
#if USE_HTCP
- HttpHeaderStat(/*hoHtcpReply*/ "HTCP reply", &ReplyHeadersMask),
+ HttpHeaderStat(/*hoHtcpReply*/ "HTCP reply", &ReplyHeadersMask),
#endif
- HttpHeaderStat(/*hoRequest*/ "request", &RequestHeadersMask),
- HttpHeaderStat(/*hoReply*/ "reply", &ReplyHeadersMask)
+ HttpHeaderStat(/*hoRequest*/ "request", &RequestHeadersMask),
+ HttpHeaderStat(/*hoReply*/ "reply", &ReplyHeadersMask)
#if USE_OPENSSL
- /* hoErrorDetail */
+ , HttpHeaderStat(/*hoErrorDetail*/ "error detail templates", nullptr)
#endif
- /* hoEnd */
+ /* hoEnd */
+ }
};
-static int HttpHeaderStatCount = countof(HttpHeaderStats);
static int HeaderEntryParsedCount = 0;
*/
class StoreEntry;
-#define assert_eid(id) assert((id) >= 0 && (id) < HDR_ENUM_END)
-
-static void httpHeaderNoteParsedEntry(http_hdr_type id, String const &value, int error);
+// update parse statistics for header id; if error is true also account
+// for errors and write to debug log what happened
+static void httpHeaderNoteParsedEntry(Http::HdrType id, String const &value, bool error);
static void httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e);
-
/** store report about current header usage and other stats */
static void httpHeaderStoreReport(StoreEntry * e);
httpHeaderInitModule(void)
{
/* check that we have enough space for masks */
- assert(8 * sizeof(HttpHeaderMask) >= HDR_ENUM_END);
-
- // check invariant: for each index in headerTable, (int)headerTable[index] = index
- for (int i = 0; headerTable[i].name; ++i)
- assert(headerTable[i].id == i);
-
- // use headerLookupTable in place of Headers
-
- /* create masks */
- httpHeaderMaskInit(&ListHeadersMask, 0);
- httpHeaderCalcMask(&ListHeadersMask, ListHeadersArr, countof(ListHeadersArr));
-
- httpHeaderMaskInit(&ReplyHeadersMask, 0);
- httpHeaderCalcMask(&ReplyHeadersMask, ReplyHeadersArr, countof(ReplyHeadersArr));
- httpHeaderCalcMask(&ReplyHeadersMask, GeneralHeadersArr, countof(GeneralHeadersArr));
- httpHeaderCalcMask(&ReplyHeadersMask, EntityHeadersArr, countof(EntityHeadersArr));
-
- httpHeaderMaskInit(&RequestHeadersMask, 0);
- httpHeaderCalcMask(&RequestHeadersMask, RequestHeadersArr, countof(RequestHeadersArr));
- httpHeaderCalcMask(&RequestHeadersMask, GeneralHeadersArr, countof(GeneralHeadersArr));
- httpHeaderCalcMask(&RequestHeadersMask, EntityHeadersArr, countof(EntityHeadersArr));
-
- httpHeaderMaskInit(&HopByHopHeadersMask, 0);
- httpHeaderCalcMask(&HopByHopHeadersMask, HopByHopHeadersArr, countof(HopByHopHeadersArr));
+ assert(8 * sizeof(HttpHeaderMask) >= Http::HdrType::enumEnd_);
+
+ // masks are needed for stats page still
+ for (auto h : WholeEnum<Http::HdrType>()) {
+ if (Http::HeaderLookupTable.lookup(h).request)
+ CBIT_SET(RequestHeadersMask,h);
+ if (Http::HeaderLookupTable.lookup(h).reply)
+ CBIT_SET(ReplyHeadersMask,h);
+ }
- /* header stats initialized by class constructor */
- assert(HttpHeaderStatCount == hoReply + 1);
+ assert(HttpHeaderStats[0].label && "httpHeaderInitModule() called via main()");
+ assert(HttpHeaderStats[hoEnd-1].label && "HttpHeaderStats created with all elements");
/* init dependent modules */
- httpHdrCcInitModule();
httpHdrScInitModule();
httpHeaderRegisterWithCacheManager();
}
-void
-httpHeaderCleanModule(void)
-{
- httpHdrCcCleanModule();
- httpHdrScCleanModule();
-}
-
/*
* HttpHeader Implementation
*/
-HttpHeader::HttpHeader() : owner (hoNone), len (0)
-{
- httpHeaderMaskInit(&mask, 0);
-}
-
-HttpHeader::HttpHeader(const http_hdr_owner_type anOwner): owner(anOwner), len(0)
+HttpHeader::HttpHeader(const http_hdr_owner_type anOwner): owner(anOwner), len(0), conflictingContentLength_(false)
{
assert(anOwner > hoNone && anOwner < hoEnd);
debugs(55, 7, "init-ing hdr: " << this << " owner: " << owner);
+ entries.reserve(32);
httpHeaderMaskInit(&mask, 0);
}
-HttpHeader::HttpHeader(const HttpHeader &other): owner(other.owner), len(other.len)
+// XXX: Delete as unused, expensive, and violating copy semantics by skipping Warnings
+HttpHeader::HttpHeader(const HttpHeader &other): owner(other.owner), len(other.len), conflictingContentLength_(false)
{
+ entries.reserve(other.entries.capacity());
httpHeaderMaskInit(&mask, 0);
- update(&other, NULL); // will update the mask as well
+ update(&other); // will update the mask as well
}
HttpHeader::~HttpHeader()
clean();
}
+// XXX: Delete as unused, expensive, and violating assignment semantics by skipping Warnings
HttpHeader &
HttpHeader::operator =(const HttpHeader &other)
{
// we do not really care, but the caller probably does
assert(owner == other.owner);
clean();
- update(&other, NULL); // will update the mask as well
+ update(&other); // will update the mask as well
len = other.len;
+ conflictingContentLength_ = other.conflictingContentLength_;
+ teUnsupported_ = other.teUnsupported_;
}
return *this;
}
assert(owner > hoNone && owner < hoEnd);
debugs(55, 7, "cleaning hdr: " << this << " owner: " << owner);
- PROF_start(HttpHeaderClean);
-
if (owner <= hoReply) {
/*
* An unfortunate bug. The entries array is initialized
HttpHeaderStats[owner].busyDestroyedCount += entries.size() > 0;
} // if (owner <= hoReply)
- for (std::vector<HttpHeaderEntry *>::iterator i = entries.begin(); i != entries.end(); ++i) {
- HttpHeaderEntry *e = *i;
- if (e == NULL)
+ for (HttpHeaderEntry *e : entries) {
+ if (e == nullptr)
continue;
- if (e->id < 0 || e->id >= HDR_ENUM_END) {
- debugs(55, DBG_CRITICAL, "BUG: invalid entry (" << e->id << "). Ignored.");
+ if (!Http::any_valid_header(e->id)) {
+ debugs(55, DBG_CRITICAL, "ERROR: Squid BUG: invalid entry (" << e->id << "). Ignored.");
} else {
if (owner <= hoReply)
HttpHeaderStats[owner].fieldTypeDistr.count(e->id);
entries.clear();
httpHeaderMaskInit(&mask, 0);
len = 0;
- PROF_stop(HttpHeaderClean);
+ conflictingContentLength_ = false;
+ teUnsupported_ = false;
}
/* append entries (also see httpHeaderUpdate) */
void
HttpHeader::append(const HttpHeader * src)
{
- const HttpHeaderEntry *e;
- HttpHeaderPos pos = HttpHeaderInitPos;
assert(src);
assert(src != this);
debugs(55, 7, "appending hdr: " << this << " += " << src);
- while ((e = src->getEntry(&pos))) {
- addEntry(e->clone());
+ for (auto e : src->entries) {
+ if (e)
+ addEntry(e->clone());
}
}
-/* use fresh entries to replace old ones */
-void
-httpHeaderUpdate(HttpHeader * old, const HttpHeader * fresh, const HttpHeaderMask * denied_mask)
+bool
+HttpHeader::needUpdate(HttpHeader const *fresh) const
{
- assert (old);
- old->update (fresh, denied_mask);
+ for (const auto e: fresh->entries) {
+ if (!e || skipUpdateHeader(e->id))
+ continue;
+ String value;
+ if (!hasNamed(e->name, &value) ||
+ (value != fresh->getByName(e->name)))
+ return true;
+ }
+ return false;
+}
+
+bool
+HttpHeader::skipUpdateHeader(const Http::HdrType id) const
+{
+ return
+ // TODO: Consider updating Vary headers after comparing the magnitude of
+ // the required changes (and/or cache losses) with compliance gains.
+ (id == Http::HdrType::VARY);
}
void
-HttpHeader::update (HttpHeader const *fresh, HttpHeaderMask const *denied_mask)
+HttpHeader::update(HttpHeader const *fresh)
{
- const HttpHeaderEntry *e;
- HttpHeaderPos pos = HttpHeaderInitPos;
assert(fresh);
assert(this != fresh);
+ const HttpHeaderEntry *e;
+ HttpHeaderPos pos = HttpHeaderInitPos;
+
while ((e = fresh->getEntry(&pos))) {
- /* deny bad guys (ok to check for HDR_OTHER) here */
+ /* deny bad guys (ok to check for Http::HdrType::OTHER) here */
- if (denied_mask && CBIT_TEST(*denied_mask, e->id))
+ if (skipUpdateHeader(e->id))
continue;
- if (e->id != HDR_OTHER)
+ if (e->id != Http::HdrType::OTHER)
delById(e->id);
else
- delByName(e->name.termedBuf());
+ delByName(e->name);
}
pos = HttpHeaderInitPos;
while ((e = fresh->getEntry(&pos))) {
- /* deny bad guys (ok to check for HDR_OTHER) here */
+ /* deny bad guys (ok to check for Http::HdrType::OTHER) here */
- if (denied_mask && CBIT_TEST(*denied_mask, e->id))
+ if (skipUpdateHeader(e->id))
continue;
- debugs(55, 7, "Updating header '" << headerTable[e->id].name << "' in cached entry");
+ debugs(55, 7, "Updating header '" << Http::HeaderLookupTable.lookup(e->id).name << "' in cached entry");
addEntry(e->clone());
}
}
-/* just handy in parsing: resets and returns false */
-int
-HttpHeader::reset()
+bool
+HttpHeader::Isolate(const char **parse_start, size_t l, const char **blk_start, const char **blk_end)
{
- clean();
- return 0;
+ /*
+ * parse_start points to the first line of HTTP message *headers*,
+ * not including the request or status lines
+ */
+ const size_t end = headersEnd(*parse_start, l);
+
+ if (end) {
+ *blk_start = *parse_start;
+ *blk_end = *parse_start + end - 1;
+ assert(**blk_end == '\n');
+ // Point blk_end to the first character after the last header field.
+ // In other words, blk_end should point to the CR?LF header terminator.
+ if (end > 1 && *(*blk_end - 1) == '\r')
+ --(*blk_end);
+ *parse_start += end;
+ }
+ return end;
+}
+
+int
+HttpHeader::parse(const char *buf, size_t buf_len, bool atEnd, size_t &hdr_sz, Http::ContentLengthInterpreter &clen)
+{
+ const char *parse_start = buf;
+ const char *blk_start, *blk_end;
+ hdr_sz = 0;
+
+ if (!Isolate(&parse_start, buf_len, &blk_start, &blk_end)) {
+ // XXX: do not parse non-isolated headers even if the connection is closed.
+ // Treat unterminated headers as "partial headers" framing errors.
+ if (!atEnd)
+ return 0;
+ blk_start = parse_start;
+ blk_end = blk_start + strlen(blk_start);
+ }
+
+ if (parse(blk_start, blk_end - blk_start, clen)) {
+ hdr_sz = parse_start - buf;
+ return 1;
+ }
+ return -1;
}
+// XXX: callers treat this return as boolean.
+// XXX: A better mechanism is needed to signal different types of error.
+// lexicon, syntax, semantics, validation, access policy - are all (ab)using 'return 0'
int
-HttpHeader::parse(const char *header_start, size_t hdrLen)
+HttpHeader::parse(const char *header_start, size_t hdrLen, Http::ContentLengthInterpreter &clen)
{
const char *field_ptr = header_start;
const char *header_end = header_start + hdrLen; // XXX: remove
- HttpHeaderEntry *e, *e2;
int warnOnError = (Config.onoff.relaxed_header_parser <= 0 ? DBG_IMPORTANT : 2);
- PROF_start(HttpHeaderParse);
-
assert(header_start && header_end);
debugs(55, 7, "parsing hdr: (" << this << ")" << std::endl << getStringPrefix(header_start, hdrLen));
++ HttpHeaderStats[owner].parsedCount;
if ((nulpos = (char*)memchr(header_start, '\0', hdrLen))) {
debugs(55, DBG_IMPORTANT, "WARNING: HTTP header contains NULL characters {" <<
getStringPrefix(header_start, nulpos-header_start) << "}\nNULL\n{" << getStringPrefix(nulpos+1, hdrLen-(nulpos-header_start)-1));
- PROF_stop(HttpHeaderParse);
- return reset();
+ clean();
+ return 0;
}
/* common format headers are "<name>:[ws]<value>" lines delimited by <CRLF>.
const char *field_start = field_ptr;
const char *field_end;
+ const char *hasBareCr = nullptr;
+ size_t lines = 0;
do {
const char *this_line = field_ptr;
field_ptr = (const char *)memchr(field_ptr, '\n', header_end - field_ptr);
+ ++lines;
if (!field_ptr) {
// missing <LF>
- PROF_stop(HttpHeaderParse);
- return reset();
+ clean();
+ return 0;
}
field_end = field_ptr;
debugs(55, DBG_IMPORTANT, "SECURITY WARNING: Rejecting HTTP request with a CR+ "
"header field to prevent request smuggling attacks: {" <<
getStringPrefix(header_start, hdrLen) << "}");
- PROF_stop(HttpHeaderParse);
- return reset();
+ clean();
+ return 0;
}
}
}
/* Barf on stray CR characters */
if (memchr(this_line, '\r', field_end - this_line)) {
+ hasBareCr = "bare CR";
debugs(55, warnOnError, "WARNING: suspicious CR characters in HTTP header {" <<
getStringPrefix(field_start, field_end-field_start) << "}");
if (Config.onoff.relaxed_header_parser) {
char *p = (char *) this_line; /* XXX Warning! This destroys original header content and violates specifications somewhat */
- while ((p = (char *)memchr(p, '\r', field_end - p)) != NULL) {
+ while ((p = (char *)memchr(p, '\r', field_end - p)) != nullptr) {
*p = ' ';
++p;
}
} else {
- PROF_stop(HttpHeaderParse);
- return reset();
+ clean();
+ return 0;
}
}
if (this_line + 1 == field_end && this_line > field_start) {
debugs(55, warnOnError, "WARNING: Blank continuation line in HTTP header {" <<
getStringPrefix(header_start, hdrLen) << "}");
- PROF_stop(HttpHeaderParse);
- return reset();
+ clean();
+ return 0;
}
} while (field_ptr < header_end && (*field_ptr == ' ' || *field_ptr == '\t'));
if (field_start == field_end) {
if (field_ptr < header_end) {
- debugs(55, warnOnError, "WARNING: unparseable HTTP header field near {" <<
+ debugs(55, warnOnError, "WARNING: unparsable HTTP header field near {" <<
getStringPrefix(field_start, hdrLen-(field_start-header_start)) << "}");
- PROF_stop(HttpHeaderParse);
- return reset();
+ clean();
+ return 0;
}
break; /* terminating blank line */
}
- if ((e = HttpHeaderEntry::parse(field_start, field_end)) == NULL) {
- debugs(55, warnOnError, "WARNING: unparseable HTTP header field {" <<
+ const auto e = HttpHeaderEntry::parse(field_start, field_end, owner);
+ if (!e) {
+ debugs(55, warnOnError, "WARNING: unparsable HTTP header field {" <<
getStringPrefix(field_start, field_end-field_start) << "}");
debugs(55, warnOnError, " in {" << getStringPrefix(header_start, hdrLen) << "}");
- if (Config.onoff.relaxed_header_parser)
- continue;
-
- PROF_stop(HttpHeaderParse);
- return reset();
+ clean();
+ return 0;
}
- if (e->id == HDR_CONTENT_LENGTH && (e2 = findEntry(e->id)) != NULL) {
- if (e->value != e2->value) {
- int64_t l1, l2;
- debugs(55, warnOnError, "WARNING: found two conflicting content-length headers in {" <<
- getStringPrefix(header_start, hdrLen) << "}");
-
- if (!Config.onoff.relaxed_header_parser) {
- delete e;
- PROF_stop(HttpHeaderParse);
- return reset();
- }
-
- if (!httpHeaderParseOffset(e->value.termedBuf(), &l1)) {
- debugs(55, DBG_IMPORTANT, "WARNING: Unparseable content-length '" << e->value << "'");
- delete e;
- continue;
- } else if (!httpHeaderParseOffset(e2->value.termedBuf(), &l2)) {
- debugs(55, DBG_IMPORTANT, "WARNING: Unparseable content-length '" << e2->value << "'");
- delById(e2->id);
- } else if (l1 > l2) {
- delById(e2->id);
- } else {
- delete e;
- continue;
- }
- } else {
- debugs(55, warnOnError, "NOTICE: found double content-length header");
+ if (lines > 1 || hasBareCr) {
+ const auto framingHeader = (e->id == Http::HdrType::CONTENT_LENGTH || e->id == Http::HdrType::TRANSFER_ENCODING);
+ if (framingHeader) {
+ if (!hasBareCr) // already warned about bare CRs
+ debugs(55, warnOnError, "WARNING: obs-fold in framing-sensitive " << e->name << ": " << e->value);
delete e;
-
- if (Config.onoff.relaxed_header_parser)
- continue;
-
- PROF_stop(HttpHeaderParse);
- return reset();
+ clean();
+ return 0;
}
}
- if (e->id == HDR_OTHER && stringHasWhitespace(e->name.termedBuf())) {
- debugs(55, warnOnError, "WARNING: found whitespace in HTTP header name {" <<
- getStringPrefix(field_start, field_end-field_start) << "}");
+ if (e->id == Http::HdrType::CONTENT_LENGTH && !clen.checkField(e->value)) {
+ delete e;
- if (!Config.onoff.relaxed_header_parser) {
- delete e;
- PROF_stop(HttpHeaderParse);
- return reset();
- }
+ if (Config.onoff.relaxed_header_parser)
+ continue; // clen has printed any necessary warnings
+
+ clean();
+ return 0;
}
addEntry(e);
}
- if (chunked()) {
+ if (clen.headerWideProblem) {
+ debugs(55, warnOnError, "WARNING: " << clen.headerWideProblem <<
+ " Content-Length field values in" <<
+ Raw("header", header_start, hdrLen));
+ }
+
+ String rawTe;
+ if (clen.prohibitedAndIgnored()) {
+ // prohibitedAndIgnored() includes trailer header blocks
+ // being parsed as a case to forbid/ignore these headers.
+
+ // RFC 7230 section 3.3.2: A server MUST NOT send a Content-Length
+ // header field in any response with a status code of 1xx (Informational)
+ // or 204 (No Content). And RFC 7230 3.3.3#1 tells recipients to ignore
+ // such Content-Lengths.
+ if (delById(Http::HdrType::CONTENT_LENGTH))
+ debugs(55, 3, "Content-Length is " << clen.prohibitedAndIgnored());
+
+ // The same RFC 7230 3.3.3#1-based logic applies to Transfer-Encoding
+ // banned by RFC 7230 section 3.3.1.
+ if (delById(Http::HdrType::TRANSFER_ENCODING))
+ debugs(55, 3, "Transfer-Encoding is " << clen.prohibitedAndIgnored());
+
+ } else if (getByIdIfPresent(Http::HdrType::TRANSFER_ENCODING, &rawTe)) {
// RFC 2616 section 4.4: ignore Content-Length with Transfer-Encoding
- delById(HDR_CONTENT_LENGTH);
+ // RFC 7230 section 3.3.3 #3: Transfer-Encoding overwrites Content-Length
+ delById(Http::HdrType::CONTENT_LENGTH);
+ // and clen state becomes irrelevant
+
+ if (rawTe.caseCmp("chunked") == 0) {
+ ; // leave header present for chunked() method
+ } else if (rawTe.caseCmp("identity") == 0) { // deprecated. no coding
+ delById(Http::HdrType::TRANSFER_ENCODING);
+ } else {
+ // This also rejects multiple encodings until we support them properly.
+ debugs(55, warnOnError, "WARNING: unsupported Transfer-Encoding used by client: " << rawTe);
+ teUnsupported_ = true;
+ }
+
+ } else if (clen.sawBad) {
+ // ensure our callers do not accidentally see bad Content-Length values
+ delById(Http::HdrType::CONTENT_LENGTH);
+ conflictingContentLength_ = true; // TODO: Rename to badContentLength_.
+ } else if (clen.needsSanitizing) {
+ // RFC 7230 section 3.3.2: MUST either reject or ... [sanitize];
+ // ensure our callers see a clean Content-Length value or none at all
+ delById(Http::HdrType::CONTENT_LENGTH);
+ if (clen.sawGood) {
+ putInt64(Http::HdrType::CONTENT_LENGTH, clen.value);
+ debugs(55, 5, "sanitized Content-Length to be " << clen.value);
+ }
}
- PROF_stop(HttpHeaderParse);
return 1; /* even if no fields where found, it is a valid header */
}
bool maskThisEntry = false;
switch (e->id) {
- case HDR_AUTHORIZATION:
- case HDR_PROXY_AUTHORIZATION:
+ case Http::HdrType::AUTHORIZATION:
+ case Http::HdrType::PROXY_AUTHORIZATION:
maskThisEntry = true;
break;
- case HDR_FTP_ARGUMENTS:
- if (const HttpHeaderEntry *cmd = findEntry(HDR_FTP_COMMAND))
+ case Http::HdrType::FTP_ARGUMENTS:
+ if (const HttpHeaderEntry *cmd = findEntry(Http::HdrType::FTP_COMMAND))
maskThisEntry = (cmd->value == "PASS");
break;
break;
}
if (maskThisEntry) {
- p->append(e->name.rawBuf(), e->name.size());
+ p->append(e->name.rawContent(), e->name.length());
p->append(": ** NOT DISPLAYED **\r\n", 23);
} else {
e->packInto(p);
return static_cast<HttpHeaderEntry*>(entries[*pos]);
}
- return NULL;
+ return nullptr;
}
/*
* "list" headers
*/
HttpHeaderEntry *
-HttpHeader::findEntry(http_hdr_type id) const
+HttpHeader::findEntry(Http::HdrType id) const
{
- HttpHeaderPos pos = HttpHeaderInitPos;
- HttpHeaderEntry *e;
- assert_eid(id);
- assert(!CBIT_TEST(ListHeadersMask, id));
+ assert(any_registered_header(id));
+ assert(!Http::HeaderLookupTable.lookup(id).list);
/* check mask first */
if (!CBIT_TEST(mask, id))
- return NULL;
+ return nullptr;
/* looks like we must have it, do linear search */
- while ((e = getEntry(&pos))) {
- if (e->id == id)
+ for (auto e : entries) {
+ if (e && e->id == id)
return e;
}
/* hm.. we thought it was there, but it was not found */
- assert(0);
-
- return NULL; /* not reached */
+ assert(false);
+ return nullptr; /* not reached */
}
/*
* same as httpHeaderFindEntry
*/
HttpHeaderEntry *
-HttpHeader::findLastEntry(http_hdr_type id) const
+HttpHeader::findLastEntry(Http::HdrType id) const
{
- HttpHeaderPos pos = HttpHeaderInitPos;
- HttpHeaderEntry *e;
- HttpHeaderEntry *result = NULL;
- assert_eid(id);
- assert(!CBIT_TEST(ListHeadersMask, id));
+ assert(any_registered_header(id));
+ assert(!Http::HeaderLookupTable.lookup(id).list);
/* check mask first */
-
if (!CBIT_TEST(mask, id))
- return NULL;
+ return nullptr;
- /* looks like we must have it, do linear search */
- while ((e = getEntry(&pos))) {
- if (e->id == id)
- result = e;
+ for (auto e = entries.rbegin(); e != entries.rend(); ++e) {
+ if (*e && (*e)->id == id)
+ return *e;
}
- assert(result); /* must be there! */
- return result;
+ /* hm.. we thought it was there, but it was not found */
+ assert(false);
+ return nullptr; /* not reached */
}
-/*
- * deletes all fields with a given name if any, returns #fields deleted;
- */
int
-HttpHeader::delByName(const char *name)
+HttpHeader::delByName(const SBuf &name)
{
int count = 0;
HttpHeaderPos pos = HttpHeaderInitPos;
- HttpHeaderEntry *e;
httpHeaderMaskInit(&mask, 0); /* temporal inconsistency */
debugs(55, 9, "deleting '" << name << "' fields in hdr " << this);
- while ((e = getEntry(&pos))) {
+ while (const HttpHeaderEntry *e = getEntry(&pos)) {
if (!e->name.caseCmp(name))
delAt(pos, count);
else
/* deletes all entries with a given id, returns the #entries deleted */
int
-HttpHeader::delById(http_hdr_type id)
+HttpHeader::delById(Http::HdrType id)
{
- int count = 0;
- HttpHeaderPos pos = HttpHeaderInitPos;
- HttpHeaderEntry *e;
debugs(55, 8, this << " del-by-id " << id);
- assert_eid(id);
- assert(id != HDR_OTHER); /* does not make sense */
+ assert(any_registered_header(id));
if (!CBIT_TEST(mask, id))
return 0;
- while ((e = getEntry(&pos))) {
+ int count = 0;
+
+ HttpHeaderPos pos = HttpHeaderInitPos;
+ while (HttpHeaderEntry *e = getEntry(&pos)) {
if (e->id == id)
- delAt(pos, count);
+ delAt(pos, count); // deletes e
}
CBIT_CLR(mask, id);
HttpHeaderEntry *e;
assert(pos >= HttpHeaderInitPos && pos < static_cast<ssize_t>(entries.size()));
e = static_cast<HttpHeaderEntry*>(entries[pos]);
- entries[pos] = NULL;
+ entries[pos] = nullptr;
/* decrement header length, allow for ": " and crlf */
- len -= e->name.size() + 2 + e->value.size() + 2;
+ len -= e->name.length() + 2 + e->value.size() + 2;
assert(len >= 0);
delete e;
++headers_deleted;
HttpHeader::compact()
{
// TODO: optimize removal, or possibly make it so that's not needed.
- std::vector<HttpHeaderEntry *>::iterator newend;
- newend = std::remove(entries.begin(), entries.end(), static_cast<HttpHeaderEntry *>(NULL));
- entries.resize(newend-entries.begin());
+ entries.erase( std::remove(entries.begin(), entries.end(), nullptr),
+ entries.end());
}
/*
{
httpHeaderMaskInit(&mask, 0);
debugs(55, 7, "refreshing the mask in hdr " << this);
- HttpHeaderPos pos = HttpHeaderInitPos;
- while (HttpHeaderEntry *e = getEntry(&pos)) {
- CBIT_SET(mask, e->id);
+ for (auto e : entries) {
+ if (e)
+ CBIT_SET(mask, e->id);
}
}
HttpHeader::addEntry(HttpHeaderEntry * e)
{
assert(e);
- assert_eid(e->id);
- assert(e->name.size());
+ assert(any_HdrType_enum_value(e->id));
+ assert(e->name.length());
debugs(55, 7, this << " adding entry: " << e->id << " at " << entries.size());
- if (CBIT_TEST(mask, e->id)) {
- ++ headerStatsTable[e->id].repCount;
- } else {
- CBIT_SET(mask, e->id);
+ if (e->id != Http::HdrType::BAD_HDR) {
+ if (CBIT_TEST(mask, e->id)) {
+ ++ headerStatsTable[e->id].repCount;
+ } else {
+ CBIT_SET(mask, e->id);
+ }
}
entries.push_back(e);
- /* increment header length, allow for ": " and crlf */
- len += e->name.size() + 2 + e->value.size() + 2;
-}
-
-/* inserts an entry;
- * does not call e->clone() so one should not reuse "*e"
- */
-void
-HttpHeader::insertEntry(HttpHeaderEntry * e)
-{
- assert(e);
- assert_eid(e->id);
-
- debugs(55, 7, this << " adding entry: " << e->id << " at " << entries.size());
-
- if (CBIT_TEST(mask, e->id)) {
- ++ headerStatsTable[e->id].repCount; //TODO: use operator[] ?
- } else {
- CBIT_SET(mask, e->id);
- }
-
- entries.insert(entries.begin(),e);
-
- /* increment header length, allow for ": " and crlf */
- len += e->name.size() + 2 + e->value.size() + 2;
+ len += e->length();
}
bool
-HttpHeader::getList(http_hdr_type id, String *s) const
+HttpHeader::getList(Http::HdrType id, String *s) const
{
- HttpHeaderEntry *e;
- HttpHeaderPos pos = HttpHeaderInitPos;
debugs(55, 9, this << " joining for id " << id);
/* only fields from ListHeaders array can be "listed" */
- assert(CBIT_TEST(ListHeadersMask, id));
+ assert(Http::HeaderLookupTable.lookup(id).list);
if (!CBIT_TEST(mask, id))
return false;
- while ((e = getEntry(&pos))) {
- if (e->id == id)
+ for (auto e: entries) {
+ if (e && e->id == id)
strListAdd(s, e->value.termedBuf(), ',');
}
*/
/* temporary warning: remove it? (Is it useful for diagnostics ?) */
if (!s->size())
- debugs(55, 3, "empty list header: " << headerTable[id].name << "(" << id << ")");
+ debugs(55, 3, "empty list header: " << Http::HeaderLookupTable.lookup(id).name << "(" << id << ")");
else
debugs(55, 6, this << ": joined for id " << id << ": " << s);
/* return a list of entries with the same id separated by ',' and ws */
String
-HttpHeader::getList(http_hdr_type id) const
+HttpHeader::getList(Http::HdrType id) const
{
HttpHeaderEntry *e;
HttpHeaderPos pos = HttpHeaderInitPos;
debugs(55, 9, this << "joining for id " << id);
/* only fields from ListHeaders array can be "listed" */
- assert(CBIT_TEST(ListHeadersMask, id));
+ assert(Http::HeaderLookupTable.lookup(id).list);
if (!CBIT_TEST(mask, id))
return String();
*/
/* temporary warning: remove it? (Is it useful for diagnostics ?) */
if (!s.size())
- debugs(55, 3, "empty list header: " << headerTable[id].name << "(" << id << ")");
+ debugs(55, 3, "empty list header: " << Http::HeaderLookupTable.lookup(id).name << "(" << id << ")");
else
debugs(55, 6, this << ": joined for id " << id << ": " << s);
/* return a string or list of entries with the same id separated by ',' and ws */
String
-HttpHeader::getStrOrList(http_hdr_type id) const
+HttpHeader::getStrOrList(Http::HdrType id) const
{
HttpHeaderEntry *e;
- if (CBIT_TEST(ListHeadersMask, id))
+ if (Http::HeaderLookupTable.lookup(id).list)
return getList(id);
if ((e = findEntry(id)))
{
String result;
// ignore presence: return undefined string if an empty header is present
- (void)getByNameIfPresent(name, result);
+ (void)hasNamed(name, strlen(name), &result);
+ return result;
+}
+
+String
+HttpHeader::getByName(const SBuf &name) const
+{
+ String result;
+ // ignore presence: return undefined string if an empty header is present
+ (void)hasNamed(name, &result);
return result;
}
+String
+HttpHeader::getById(Http::HdrType id) const
+{
+ String result;
+ (void)getByIdIfPresent(id, &result);
+ return result;
+}
+
+bool
+HttpHeader::hasNamed(const SBuf &s, String *result) const
+{
+ return hasNamed(s.rawContent(), s.length(), result);
+}
+
+bool
+HttpHeader::getByIdIfPresent(Http::HdrType id, String *result) const
+{
+ if (id == Http::HdrType::BAD_HDR)
+ return false;
+ if (!has(id))
+ return false;
+ if (result)
+ *result = getStrOrList(id);
+ return true;
+}
+
bool
-HttpHeader::getByNameIfPresent(const char *name, String &result) const
+HttpHeader::hasNamed(const char *name, unsigned int namelen, String *result) const
{
- http_hdr_type id;
+ Http::HdrType id;
HttpHeaderPos pos = HttpHeaderInitPos;
HttpHeaderEntry *e;
assert(name);
/* First try the quick path */
- id = httpHeaderIdByNameDef(SBuf(name));
+ id = Http::HeaderLookupTable.lookup(name,namelen).id;
- if (id != -1) {
- if (!has(id))
- return false;
- result = getStrOrList(id);
- return true;
+ if (id != Http::HdrType::BAD_HDR) {
+ if (getByIdIfPresent(id, result))
+ return true;
}
/* Sorry, an unknown header name. Do linear search */
bool found = false;
while ((e = getEntry(&pos))) {
- if (e->id == HDR_OTHER && e->name.caseCmp(name) == 0) {
+ if (e->id == Http::HdrType::OTHER && e->name.length() == namelen && e->name.caseCmp(name, namelen) == 0) {
found = true;
- strListAdd(&result, e->value.termedBuf(), ',');
+ if (!result)
+ break;
+ strListAdd(result, e->value.termedBuf(), ',');
}
}
/*
* Returns a the value of the specified list member, if any.
*/
-String
+SBuf
HttpHeader::getByNameListMember(const char *name, const char *member, const char separator) const
{
- String header;
- const char *pos = NULL;
- const char *item;
- int ilen;
- int mlen = strlen(member);
-
assert(name);
-
- header = getByName(name);
-
- String result;
-
- while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
- if (strncmp(item, member, mlen) == 0 && item[mlen] == '=') {
- result.append(item + mlen + 1, ilen - mlen - 1);
- break;
- }
- }
-
- return result;
+ const auto header = getByName(name);
+ return ::getListMember(header, member, separator);
}
/*
* returns a the value of the specified list member, if any.
*/
-String
-HttpHeader::getListMember(http_hdr_type id, const char *member, const char separator) const
+SBuf
+HttpHeader::getListMember(Http::HdrType id, const char *member, const char separator) const
{
- String header;
- const char *pos = NULL;
- const char *item;
- int ilen;
- int mlen = strlen(member);
-
- assert(id >= 0);
-
- header = getStrOrList(id);
- String result;
-
- while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
- if (strncmp(item, member, mlen) == 0 && item[mlen] == '=') {
- result.append(item + mlen + 1, ilen - mlen - 1);
- break;
- }
- }
-
- header.clean();
- return result;
+ assert(any_registered_header(id));
+ const auto header = getStrOrList(id);
+ return ::getListMember(header, member, separator);
}
/* test if a field is present */
int
-HttpHeader::has(http_hdr_type id) const
+HttpHeader::has(Http::HdrType id) const
{
- assert_eid(id);
- assert(id != HDR_OTHER);
+ assert(any_registered_header(id));
debugs(55, 9, this << " lookup for " << id);
return CBIT_TEST(mask, id);
}
void
-HttpHeader::putInt(http_hdr_type id, int number)
-{
- assert_eid(id);
- assert(headerTable[id].type == ftInt); /* must be of an appropriate type */
- assert(number >= 0);
- addEntry(new HttpHeaderEntry(id, NULL, xitoa(number)));
+HttpHeader::addVia(const AnyP::ProtocolVersion &ver, const HttpHeader *from)
+{
+ // TODO: do not add Via header for messages where Squid itself
+ // generated the message (i.e., Downloader or ESI) there should be no Via header added at all.
+
+ if (Config.onoff.via) {
+ SBuf buf;
+ // RFC 7230 section 5.7.1.: protocol-name is omitted when
+ // the received protocol is HTTP.
+ if (ver.protocol > AnyP::PROTO_NONE && ver.protocol < AnyP::PROTO_UNKNOWN &&
+ ver.protocol != AnyP::PROTO_HTTP && ver.protocol != AnyP::PROTO_HTTPS)
+ buf.appendf("%s/", AnyP::ProtocolType_str[ver.protocol]);
+ buf.appendf("%d.%d %s", ver.major, ver.minor, ThisCache);
+ const HttpHeader *hdr = from ? from : this;
+ SBuf strVia = StringToSBuf(hdr->getList(Http::HdrType::VIA));
+ if (!strVia.isEmpty())
+ strVia.append(", ", 2);
+ strVia.append(buf);
+ updateOrAddStr(Http::HdrType::VIA, strVia);
+ }
}
void
-HttpHeader::putInt64(http_hdr_type id, int64_t number)
+HttpHeader::putInt(Http::HdrType id, int number)
{
- assert_eid(id);
- assert(headerTable[id].type == ftInt64); /* must be of an appropriate type */
+ assert(any_registered_header(id));
+ assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftInt); /* must be of an appropriate type */
assert(number >= 0);
- addEntry(new HttpHeaderEntry(id, NULL, xint64toa(number)));
+ addEntry(new HttpHeaderEntry(id, SBuf(), xitoa(number)));
}
void
-HttpHeader::putTime(http_hdr_type id, time_t htime)
+HttpHeader::putInt64(Http::HdrType id, int64_t number)
{
- assert_eid(id);
- assert(headerTable[id].type == ftDate_1123); /* must be of an appropriate type */
- assert(htime >= 0);
- addEntry(new HttpHeaderEntry(id, NULL, mkrfc1123(htime)));
+ assert(any_registered_header(id));
+ assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftInt64); /* must be of an appropriate type */
+ assert(number >= 0);
+ addEntry(new HttpHeaderEntry(id, SBuf(), xint64toa(number)));
}
void
-HttpHeader::insertTime(http_hdr_type id, time_t htime)
+HttpHeader::putTime(Http::HdrType id, time_t htime)
{
- assert_eid(id);
- assert(headerTable[id].type == ftDate_1123); /* must be of an appropriate type */
+ assert(any_registered_header(id));
+ assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftDate_1123); /* must be of an appropriate type */
assert(htime >= 0);
- insertEntry(new HttpHeaderEntry(id, NULL, mkrfc1123(htime)));
+ addEntry(new HttpHeaderEntry(id, SBuf(), Time::FormatRfc1123(htime)));
}
void
-HttpHeader::putStr(http_hdr_type id, const char *str)
+HttpHeader::putStr(Http::HdrType id, const char *str)
{
- assert_eid(id);
- assert(headerTable[id].type == ftStr); /* must be of an appropriate type */
+ assert(any_registered_header(id));
+ assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftStr); /* must be of an appropriate type */
assert(str);
- addEntry(new HttpHeaderEntry(id, NULL, str));
+ addEntry(new HttpHeaderEntry(id, SBuf(), str));
}
void
HttpHeader::putAuth(const char *auth_scheme, const char *realm)
{
assert(auth_scheme && realm);
- httpHeaderPutStrf(this, HDR_WWW_AUTHENTICATE, "%s realm=\"%s\"", auth_scheme, realm);
+ httpHeaderPutStrf(this, Http::HdrType::WWW_AUTHENTICATE, "%s realm=\"%s\"", auth_scheme, realm);
}
void
-HttpHeader::putCc(const HttpHdrCc * cc)
+HttpHeader::putCc(const HttpHdrCc &cc)
{
- assert(cc);
/* remove old directives if any */
- delById(HDR_CACHE_CONTROL);
+ delById(Http::HdrType::CACHE_CONTROL);
/* pack into mb */
MemBuf mb;
mb.init();
- cc->packInto(&mb);
+ cc.packInto(&mb);
/* put */
- addEntry(new HttpHeaderEntry(HDR_CACHE_CONTROL, NULL, mb.buf));
+ addEntry(new HttpHeaderEntry(Http::HdrType::CACHE_CONTROL, SBuf(), mb.buf));
/* cleanup */
mb.clean();
}
{
assert(cr);
/* remove old directives if any */
- delById(HDR_CONTENT_RANGE);
+ delById(Http::HdrType::CONTENT_RANGE);
/* pack into mb */
MemBuf mb;
mb.init();
httpHdrContRangePackInto(cr, &mb);
/* put */
- addEntry(new HttpHeaderEntry(HDR_CONTENT_RANGE, NULL, mb.buf));
+ addEntry(new HttpHeaderEntry(Http::HdrType::CONTENT_RANGE, SBuf(), mb.buf));
/* cleanup */
mb.clean();
}
{
assert(range);
/* remove old directives if any */
- delById(HDR_RANGE);
+ delById(Http::HdrType::RANGE);
/* pack into mb */
MemBuf mb;
mb.init();
range->packInto(&mb);
/* put */
- addEntry(new HttpHeaderEntry(HDR_RANGE, NULL, mb.buf));
+ addEntry(new HttpHeaderEntry(Http::HdrType::RANGE, SBuf(), mb.buf));
/* cleanup */
mb.clean();
}
{
assert(sc);
/* remove old directives if any */
- delById(HDR_SURROGATE_CONTROL);
+ delById(Http::HdrType::SURROGATE_CONTROL);
/* pack into mb */
MemBuf mb;
mb.init();
sc->packInto(&mb);
/* put */
- addEntry(new HttpHeaderEntry(HDR_SURROGATE_CONTROL, NULL, mb.buf));
+ addEntry(new HttpHeaderEntry(Http::HdrType::SURROGATE_CONTROL, SBuf(), mb.buf));
/* cleanup */
mb.clean();
}
-void
-HttpHeader::putWarning(const int code, const char *const text)
-{
- char buf[512];
- snprintf(buf, sizeof(buf), "%i %s \"%s\"", code, visible_appname_string, text);
- putStr(HDR_WARNING, buf);
-}
-
/* add extension header (these fields are not parsed/analyzed/joined, etc.) */
void
HttpHeader::putExt(const char *name, const char *value)
{
assert(name && value);
debugs(55, 8, this << " adds ext entry " << name << " : " << value);
- addEntry(new HttpHeaderEntry(HDR_OTHER, name, value));
+ addEntry(new HttpHeaderEntry(Http::HdrType::OTHER, SBuf(name), value));
+}
+
+void
+HttpHeader::updateOrAddStr(const Http::HdrType id, const SBuf &newValue)
+{
+ assert(any_registered_header(id));
+ assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftStr);
+
+ // XXX: HttpHeaderEntry::value suffers from String size limits
+ Assure(newValue.length() < String::SizeMaxXXX());
+
+ if (!CBIT_TEST(mask, id)) {
+ auto newValueCopy = newValue; // until HttpHeaderEntry::value becomes SBuf
+ addEntry(new HttpHeaderEntry(id, SBuf(), newValueCopy.c_str()));
+ return;
+ }
+
+ auto foundSameName = false;
+ for (auto &e: entries) {
+ if (!e || e->id != id)
+ continue;
+
+ if (foundSameName) {
+ // get rid of this repeated same-name entry
+ delete e;
+ e = nullptr;
+ continue;
+ }
+
+ if (newValue.cmp(e->value.termedBuf()) != 0)
+ e->value.assign(newValue.rawContent(), newValue.plength());
+
+ foundSameName = true;
+ // continue to delete any repeated same-name entries
+ }
+ assert(foundSameName);
+ debugs(55, 5, "synced: " << Http::HeaderLookupTable.lookup(id).name << ": " << newValue);
}
int
-HttpHeader::getInt(http_hdr_type id) const
+HttpHeader::getInt(Http::HdrType id) const
{
- assert_eid(id);
- assert(headerTable[id].type == ftInt); /* must be of an appropriate type */
+ assert(any_registered_header(id));
+ assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftInt); /* must be of an appropriate type */
HttpHeaderEntry *e;
if ((e = findEntry(id)))
}
int64_t
-HttpHeader::getInt64(http_hdr_type id) const
+HttpHeader::getInt64(Http::HdrType id) const
{
- assert_eid(id);
- assert(headerTable[id].type == ftInt64); /* must be of an appropriate type */
+ assert(any_registered_header(id));
+ assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftInt64); /* must be of an appropriate type */
HttpHeaderEntry *e;
if ((e = findEntry(id)))
}
time_t
-HttpHeader::getTime(http_hdr_type id) const
+HttpHeader::getTime(Http::HdrType id) const
{
HttpHeaderEntry *e;
time_t value = -1;
- assert_eid(id);
- assert(headerTable[id].type == ftDate_1123); /* must be of an appropriate type */
+ assert(any_registered_header(id));
+ assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftDate_1123); /* must be of an appropriate type */
if ((e = findEntry(id))) {
- value = parse_rfc1123(e->value.termedBuf());
+ value = Time::ParseRfc1123(e->value.termedBuf());
httpHeaderNoteParsedEntry(e->id, e->value, value < 0);
}
/* sync with httpHeaderGetLastStr */
const char *
-HttpHeader::getStr(http_hdr_type id) const
+HttpHeader::getStr(Http::HdrType id) const
{
HttpHeaderEntry *e;
- assert_eid(id);
- assert(headerTable[id].type == ftStr); /* must be of an appropriate type */
+ assert(any_registered_header(id));
+ assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftStr); /* must be of an appropriate type */
if ((e = findEntry(id))) {
- httpHeaderNoteParsedEntry(e->id, e->value, 0); /* no errors are possible */
+ httpHeaderNoteParsedEntry(e->id, e->value, false); /* no errors are possible */
return e->value.termedBuf();
}
- return NULL;
+ return nullptr;
}
/* unusual */
const char *
-HttpHeader::getLastStr(http_hdr_type id) const
+HttpHeader::getLastStr(Http::HdrType id) const
{
HttpHeaderEntry *e;
- assert_eid(id);
- assert(headerTable[id].type == ftStr); /* must be of an appropriate type */
+ assert(any_registered_header(id));
+ assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftStr); /* must be of an appropriate type */
if ((e = findLastEntry(id))) {
- httpHeaderNoteParsedEntry(e->id, e->value, 0); /* no errors are possible */
+ httpHeaderNoteParsedEntry(e->id, e->value, false); /* no errors are possible */
return e->value.termedBuf();
}
- return NULL;
+ return nullptr;
}
HttpHdrCc *
HttpHeader::getCc() const
{
- if (!CBIT_TEST(mask, HDR_CACHE_CONTROL))
- return NULL;
- PROF_start(HttpHeader_getCc);
+ if (!CBIT_TEST(mask, Http::HdrType::CACHE_CONTROL))
+ return nullptr;
String s;
- getList(HDR_CACHE_CONTROL, &s);
+ getList(Http::HdrType::CACHE_CONTROL, &s);
HttpHdrCc *cc=new HttpHdrCc();
if (!cc->parse(s)) {
delete cc;
- cc = NULL;
+ cc = nullptr;
}
++ HttpHeaderStats[owner].ccParsedCount;
if (cc)
httpHdrCcUpdateStats(cc, &HttpHeaderStats[owner].ccTypeDistr);
- httpHeaderNoteParsedEntry(HDR_CACHE_CONTROL, s, !cc);
-
- PROF_stop(HttpHeader_getCc);
+ httpHeaderNoteParsedEntry(Http::HdrType::CACHE_CONTROL, s, !cc);
return cc;
}
HttpHdrRange *
HttpHeader::getRange() const
{
- HttpHdrRange *r = NULL;
+ HttpHdrRange *r = nullptr;
HttpHeaderEntry *e;
/* some clients will send "Request-Range" _and_ *matching* "Range"
* who knows, some clients might send Request-Range only;
* this "if" should work correctly in both cases;
* hopefully no clients send mismatched headers! */
- if ((e = findEntry(HDR_RANGE)) ||
- (e = findEntry(HDR_REQUEST_RANGE))) {
+ if ((e = findEntry(Http::HdrType::RANGE)) ||
+ (e = findEntry(Http::HdrType::REQUEST_RANGE))) {
r = HttpHdrRange::ParseCreate(&e->value);
httpHeaderNoteParsedEntry(e->id, e->value, !r);
}
HttpHdrSc *
HttpHeader::getSc() const
{
- if (!CBIT_TEST(mask, HDR_SURROGATE_CONTROL))
- return NULL;
+ if (!CBIT_TEST(mask, Http::HdrType::SURROGATE_CONTROL))
+ return nullptr;
String s;
- (void) getList(HDR_SURROGATE_CONTROL, &s);
+ (void) getList(Http::HdrType::SURROGATE_CONTROL, &s);
HttpHdrSc *sc = httpHdrScParseCreate(s);
if (sc)
sc->updateStats(&HttpHeaderStats[owner].scTypeDistr);
- httpHeaderNoteParsedEntry(HDR_SURROGATE_CONTROL, s, !sc);
+ httpHeaderNoteParsedEntry(Http::HdrType::SURROGATE_CONTROL, s, !sc);
return sc;
}
HttpHdrContRange *
HttpHeader::getContRange() const
{
- HttpHdrContRange *cr = NULL;
+ HttpHdrContRange *cr = nullptr;
HttpHeaderEntry *e;
- if ((e = findEntry(HDR_CONTENT_RANGE))) {
+ if ((e = findEntry(Http::HdrType::CONTENT_RANGE))) {
cr = httpHdrContRangeParseCreate(e->value.termedBuf());
httpHeaderNoteParsedEntry(e->id, e->value, !cr);
}
return cr;
}
-const char *
-HttpHeader::getAuth(http_hdr_type id, const char *auth_scheme) const
+SBuf
+HttpHeader::getAuthToken(Http::HdrType id, const char *auth_scheme) const
{
const char *field;
int l;
assert(auth_scheme);
field = getStr(id);
+ static const SBuf nil;
if (!field) /* no authorization field */
- return NULL;
+ return nil;
l = strlen(auth_scheme);
if (!l || strncasecmp(field, auth_scheme, l)) /* wrong scheme */
- return NULL;
+ return nil;
field += l;
if (!xisspace(*field)) /* wrong scheme */
- return NULL;
+ return nil;
/* skip white space */
for (; field && xisspace(*field); ++field);
if (!*field) /* no authorization cookie */
- return NULL;
+ return nil;
- static char decodedAuthToken[8192];
+ const auto fieldLen = strlen(field);
+ SBuf result;
+ char *decodedAuthToken = result.rawAppendStart(BASE64_DECODE_LENGTH(fieldLen));
struct base64_decode_ctx ctx;
base64_decode_init(&ctx);
size_t decodedLen = 0;
- if (!base64_decode_update(&ctx, &decodedLen, reinterpret_cast<uint8_t*>(decodedAuthToken), strlen(field), reinterpret_cast<const uint8_t*>(field)) ||
+ if (!base64_decode_update(&ctx, &decodedLen, reinterpret_cast<uint8_t*>(decodedAuthToken), fieldLen, field) ||
!base64_decode_final(&ctx)) {
- return NULL;
+ return nil;
}
- decodedAuthToken[decodedLen] = '\0';
- return decodedAuthToken;
+ result.rawAppendFinish(decodedAuthToken, decodedLen);
+ return result;
}
ETag
-HttpHeader::getETag(http_hdr_type id) const
+HttpHeader::getETag(Http::HdrType id) const
{
- ETag etag = {NULL, -1};
+ ETag etag = {nullptr, -1};
HttpHeaderEntry *e;
- assert(headerTable[id].type == ftETag); /* must be of an appropriate type */
+ assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftETag); /* must be of an appropriate type */
if ((e = findEntry(id)))
etagParseInit(&etag, e->value.termedBuf());
}
TimeOrTag
-HttpHeader::getTimeOrTag(http_hdr_type id) const
+HttpHeader::getTimeOrTag(Http::HdrType id) const
{
TimeOrTag tot;
HttpHeaderEntry *e;
- assert(headerTable[id].type == ftDate_1123_or_ETag); /* must be of an appropriate type */
+ assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftDate_1123_or_ETag); /* must be of an appropriate type */
memset(&tot, 0, sizeof(tot));
if ((e = findEntry(id))) {
/* try as an ETag */
if (etagParseInit(&tot.tag, str)) {
- tot.valid = tot.tag.str != NULL;
+ tot.valid = tot.tag.str != nullptr;
tot.time = -1;
} else {
/* or maybe it is time? */
- tot.time = parse_rfc1123(str);
+ tot.time = Time::ParseRfc1123(str);
tot.valid = tot.time >= 0;
- tot.tag.str = NULL;
+ tot.tag.str = nullptr;
}
}
* HttpHeaderEntry
*/
-HttpHeaderEntry::HttpHeaderEntry(http_hdr_type anId, const char *aName, const char *aValue)
+HttpHeaderEntry::HttpHeaderEntry(Http::HdrType anId, const SBuf &aName, const char *aValue)
{
- assert_eid(anId);
+ assert(any_HdrType_enum_value(anId));
id = anId;
- if (id != HDR_OTHER)
- name = headerTable[id].name;
+ if (id != Http::HdrType::OTHER)
+ name = Http::HeaderLookupTable.lookup(id).name;
else
name = aName;
value = aValue;
- ++ headerStatsTable[id].aliveCount;
+ if (id != Http::HdrType::BAD_HDR)
+ ++ headerStatsTable[id].aliveCount;
debugs(55, 9, "created HttpHeaderEntry " << this << ": '" << name << " : " << value );
}
HttpHeaderEntry::~HttpHeaderEntry()
{
- assert_eid(id);
debugs(55, 9, "destroying entry " << this << ": '" << name << ": " << value << "'");
- assert(headerStatsTable[id].aliveCount); // is this really needed?
-
- -- headerStatsTable[id].aliveCount;
+ if (id != Http::HdrType::BAD_HDR) {
+ assert(headerStatsTable[id].aliveCount);
+ -- headerStatsTable[id].aliveCount;
+ id = Http::HdrType::BAD_HDR; // it already is BAD_HDR, no sense in resetting it
+ }
- id = HDR_BAD_HDR;
}
/* parses and inits header entry, returns true/false */
HttpHeaderEntry *
-HttpHeaderEntry::parse(const char *field_start, const char *field_end)
+HttpHeaderEntry::parse(const char *field_start, const char *field_end, const http_hdr_owner_type msgType)
{
/* note: name_start == field_start */
const char *name_end = (const char *)memchr(field_start, ':', field_end - field_start);
/* do we have a valid field name within this field? */
if (!name_len || name_end > field_end)
- return NULL;
+ return nullptr;
if (name_len > 65534) {
/* String must be LESS THAN 64K and it adds a terminating NULL */
- debugs(55, DBG_IMPORTANT, "WARNING: ignoring header name of " << name_len << " bytes");
- return NULL;
+ // TODO: update this to show proper name_len in Raw markup, but not print all that
+ debugs(55, 2, "ignoring huge header field (" << Raw("field_start", field_start, 100) << "...)");
+ return nullptr;
}
- if (Config.onoff.relaxed_header_parser && xisspace(field_start[name_len - 1])) {
+ /*
+ * RFC 7230 section 3.2.4:
+ * "No whitespace is allowed between the header field-name and colon.
+ * ...
+ * A server MUST reject any received request message that contains
+ * whitespace between a header field-name and colon with a response code
+ * of 400 (Bad Request). A proxy MUST remove any such whitespace from a
+ * response message before forwarding the message downstream."
+ */
+ if (xisspace(field_start[name_len - 1])) {
+
+ if (msgType == hoRequest)
+ return nullptr;
+
+ // for now, also let relaxed parser remove this BWS from any non-HTTP messages
+ const bool stripWhitespace = (msgType == hoReply) ||
+ Config.onoff.relaxed_header_parser;
+ if (!stripWhitespace)
+ return nullptr; // reject if we cannot strip
+
debugs(55, Config.onoff.relaxed_header_parser <= 0 ? 1 : 2,
- "NOTICE: Whitespace after header name in '" << getStringPrefix(field_start, field_end-field_start) << "'");
+ "WARNING: Whitespace after header name in '" << getStringPrefix(field_start, field_end-field_start) << "'");
while (name_len > 0 && xisspace(field_start[name_len - 1]))
--name_len;
- if (!name_len)
- return NULL;
+ if (!name_len) {
+ debugs(55, 2, "found header with only whitespace for name");
+ return nullptr;
+ }
+ }
+
+ /* RFC 7230 section 3.2:
+ *
+ * header-field = field-name ":" OWS field-value OWS
+ * field-name = token
+ * token = 1*TCHAR
+ */
+ for (const char *pos = field_start; pos < (field_start+name_len); ++pos) {
+ if (!CharacterSet::TCHAR[*pos]) {
+ debugs(55, 2, "found header with invalid characters in " <<
+ Raw("field-name", field_start, min(name_len,100)) << "...");
+ return nullptr;
+ }
}
/* now we know we can parse it */
debugs(55, 9, "parsing HttpHeaderEntry: near '" << getStringPrefix(field_start, field_end-field_start) << "'");
/* is it a "known" field? */
- http_hdr_type id = headerLookupTable.lookup(SBuf(field_start,name_len));
- debugs(55, 9, "got hdr id hdr: " << id);
+ Http::HdrType id = Http::HeaderLookupTable.lookup(field_start,name_len).id;
+ debugs(55, 9, "got hdr-id=" << id);
- String name;
+ SBuf theName;
String value;
- if (id < 0)
- id = HDR_OTHER;
-
- assert_eid(id);
+ if (id == Http::HdrType::BAD_HDR)
+ id = Http::HdrType::OTHER;
/* set field name */
- if (id == HDR_OTHER)
- name.limitInit(field_start, name_len);
+ if (id == Http::HdrType::OTHER)
+ theName.append(field_start, name_len);
else
- name = headerTable[id].name;
+ theName = Http::HeaderLookupTable.lookup(id).name;
/* trim field value */
while (value_start < field_end && xisspace(*value_start))
if (field_end - value_start > 65534) {
/* String must be LESS THAN 64K and it adds a terminating NULL */
- debugs(55, DBG_IMPORTANT, "WARNING: ignoring '" << name << "' header of " << (field_end - value_start) << " bytes");
-
- if (id == HDR_OTHER)
- name.clean();
-
- return NULL;
+ debugs(55, 2, "WARNING: found '" << theName << "' header of " << (field_end - value_start) << " bytes");
+ return nullptr;
}
/* set field value */
- value.limitInit(value_start, field_end - value_start);
+ value.assign(value_start, field_end - value_start);
- ++ headerStatsTable[id].seenCount;
+ if (id != Http::HdrType::BAD_HDR)
+ ++ headerStatsTable[id].seenCount;
- debugs(55, 9, "parsed HttpHeaderEntry: '" << name << ": " << value << "'");
+ debugs(55, 9, "parsed HttpHeaderEntry: '" << theName << ": " << value << "'");
- return new HttpHeaderEntry(id, name.termedBuf(), value.termedBuf());
+ return new HttpHeaderEntry(id, theName, value.termedBuf());
}
HttpHeaderEntry *
HttpHeaderEntry::clone() const
{
- return new HttpHeaderEntry(id, name.termedBuf(), value.termedBuf());
+ return new HttpHeaderEntry(id, name, value.termedBuf());
}
void
HttpHeaderEntry::packInto(Packable * p) const
{
assert(p);
- p->append(name.rawBuf(), name.size());
+ p->append(name.rawContent(), name.length());
p->append(": ", 2);
p->append(value.rawBuf(), value.size());
p->append("\r\n", 2);
int
HttpHeaderEntry::getInt() const
{
- assert_eid (id);
int val = -1;
int ok = httpHeaderParseInt(value.termedBuf(), &val);
- httpHeaderNoteParsedEntry(id, value, !ok);
+ httpHeaderNoteParsedEntry(id, value, ok == 0);
/* XXX: Should we check ok - ie
* return ok ? -1 : value;
*/
int64_t
HttpHeaderEntry::getInt64() const
{
- assert_eid (id);
int64_t val = -1;
- int ok = httpHeaderParseOffset(value.termedBuf(), &val);
+ const bool ok = httpHeaderParseOffset(value.termedBuf(), &val);
httpHeaderNoteParsedEntry(id, value, !ok);
- /* XXX: Should we check ok - ie
- * return ok ? -1 : value;
- */
- return val;
+ return val; // remains -1 if !ok (XXX: bad method API)
}
static void
-httpHeaderNoteParsedEntry(http_hdr_type id, String const &context, int error)
+httpHeaderNoteParsedEntry(Http::HdrType id, String const &context, bool error)
{
- ++ headerStatsTable[id].parsCount;
+ if (id != Http::HdrType::BAD_HDR)
+ ++ headerStatsTable[id].parsCount;
if (error) {
- ++ headerStatsTable[id].errCount;
- debugs(55, 2, "cannot parse hdr field: '" << headerTable[id].name << ": " << context << "'");
+ if (id != Http::HdrType::BAD_HDR)
+ ++ headerStatsTable[id].errCount;
+ debugs(55, 2, "cannot parse hdr field: '" << Http::HeaderLookupTable.lookup(id).name << ": " << context << "'");
}
}
/* tmp variable used to pass stat info to dumpers */
extern const HttpHeaderStat *dump_stat; /* argh! */
-const HttpHeaderStat *dump_stat = NULL;
+const HttpHeaderStat *dump_stat = nullptr;
-void
+static void
httpHeaderFieldStatDumper(StoreEntry * sentry, int, double val, double, int count)
{
- const int id = (int) val;
- const int valid_id = id >= 0 && id < HDR_ENUM_END;
- const char *name = valid_id ? headerTable[id].name : "INVALID";
+ const int id = static_cast<int>(val);
+ const bool valid_id = Http::any_valid_header(static_cast<Http::HdrType>(id));
+ const char *name = valid_id ? Http::HeaderLookupTable.lookup(static_cast<Http::HdrType>(id)).name : "INVALID";
int visible = count > 0;
/* for entries with zero count, list only those that belong to current type of message */
static void
httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e)
{
- assert(hs && e);
+ assert(hs);
+ assert(e);
+
+ if (!hs->owner_mask)
+ return; // these HttpHeaderStat objects were not meant to be dumped here
dump_stat = hs;
storeAppendPrintf(e, "\nHeader Stats: %s\n", hs->label);
"id", "#flds", "count", "%total");
hs->hdrUCountDistr.dump(e, httpHeaderFldsPerHdrDumper);
storeAppendPrintf(e, "\n");
- dump_stat = NULL;
+ dump_stat = nullptr;
}
void
httpHeaderStoreReport(StoreEntry * e)
{
- int i;
-// http_hdr_type ht;
assert(e);
HttpHeaderStats[0].parsedCount =
HttpHeaderStats[0].busyDestroyedCount =
HttpHeaderStats[hoRequest].busyDestroyedCount + HttpHeaderStats[hoReply].busyDestroyedCount;
- for (i = 1; i < HttpHeaderStatCount; ++i) {
- httpHeaderStatDump(HttpHeaderStats + i, e);
- }
+ for (const auto &stats: HttpHeaderStats)
+ httpHeaderStatDump(&stats, e);
/* field stats for all messages */
storeAppendPrintf(e, "\nHttp Fields Stats (replies and requests)\n");
"id", "name", "#alive", "%err", "%repeat");
// scan heaaderTable and output
- for (int j = 0; headerTable[j].name != nullptr; ++j) {
- auto stats = headerStatsTable[j];
+ for (auto h : WholeEnum<Http::HdrType>()) {
+ auto stats = headerStatsTable[h];
storeAppendPrintf(e, "%2d\t %-25s\t %5d\t %6.3f\t %6.3f\n",
- headerTable[j].id, headerTable[j].name, stats.aliveCount,
- xpercent(stats.errCount, stats.parsCount),
- xpercent(stats.repCount, stats.seenCount));
+ Http::HeaderLookupTable.lookup(h).id,
+ Http::HeaderLookupTable.lookup(h).name,
+ stats.aliveCount,
+ xpercent(stats.errCount, stats.parsCount),
+ xpercent(stats.repCount, stats.seenCount));
}
storeAppendPrintf(e, "Headers Parsed: %d + %d = %d\n",
storeAppendPrintf(e, "Hdr Fields Parsed: %d\n", HeaderEntryParsedCount);
}
-// (ab)used by other modules.
-http_hdr_type
-httpHeaderIdByName(const char *name, size_t name_len, const HttpHeaderFieldInfo * info, int end)
-{
- if (name_len > 0) {
- for (int i = 0; i < end; ++i) {
- if (name_len != info[i].name.size())
- continue;
-
- if (!strncasecmp(name, info[i].name.rawBuf(), name_len))
- return info[i].id;
- }
- }
-
- return HDR_BAD_HDR;
-}
-
-http_hdr_type
-httpHeaderIdByNameDef(const SBuf &name)
-{
- return headerLookupTable.lookup(name);
-}
-
-http_hdr_type
-httpHeaderIdByNameDef(const char *name, int name_len)
-{
- return headerLookupTable.lookup(SBuf(name,name_len));
-}
-
-const char *
-httpHeaderNameById(int id)
-{
- assert(id >= 0 && id < HDR_ENUM_END);
- return headerTable[id].name;
-}
-
int
-HttpHeader::hasListMember(http_hdr_type id, const char *member, const char separator) const
+HttpHeader::hasListMember(Http::HdrType id, const char *member, const char separator) const
{
int result = 0;
- const char *pos = NULL;
+ const char *pos = nullptr;
const char *item;
int ilen;
int mlen = strlen(member);
- assert(id >= 0);
+ assert(any_registered_header(id));
String header (getStrOrList(id));
HttpHeader::hasByNameListMember(const char *name, const char *member, const char separator) const
{
int result = 0;
- const char *pos = NULL;
+ const char *pos = nullptr;
const char *item;
int ilen;
int mlen = strlen(member);
HttpHeaderPos pos = HttpHeaderInitPos;
int headers_deleted = 0;
while ((e = getEntry(&pos))) {
- int id = e->id;
- if (CBIT_TEST(HopByHopHeadersMask, id)) {
+ Http::HdrType id = e->id;
+ if (Http::HeaderLookupTable.lookup(id).hopbyhop) {
delAt(pos, headers_deleted);
CBIT_CLR(mask, id);
}
void
HttpHeader::removeConnectionHeaderEntries()
{
- if (has(HDR_CONNECTION)) {
+ if (has(Http::HdrType::CONNECTION)) {
/* anything that matches Connection list member will be deleted */
String strConnection;
- (void) getList(HDR_CONNECTION, &strConnection);
+ (void) getList(Http::HdrType::CONNECTION, &strConnection);
const HttpHeaderEntry *e;
HttpHeaderPos pos = HttpHeaderInitPos;
/*
int headers_deleted = 0;
while ((e = getEntry(&pos))) {
- if (strListIsMember(&strConnection, e->name.termedBuf(), ','))
+ if (strListIsMember(&strConnection, e->name, ','))
delAt(pos, headers_deleted);
}
if (headers_deleted)