* HttpHeader Implementation
*/
-HttpHeader::HttpHeader() : owner (hoNone), len (0)
+HttpHeader::HttpHeader() : owner (hoNone), len (0), conflictingContentLength_(false)
{
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);
httpHeaderMaskInit(&mask, 0);
}
-HttpHeader::HttpHeader(const HttpHeader &other): owner(other.owner), len(other.len)
+HttpHeader::HttpHeader(const HttpHeader &other): owner(other.owner), len(other.len), conflictingContentLength_(false)
{
httpHeaderMaskInit(&mask, 0);
update(&other, NULL); // will update the mask as well
clean();
update(&other, NULL); // will update the mask as well
len = other.len;
+ conflictingContentLength_ = other.conflictingContentLength_;
}
return *this;
}
entries.clear();
httpHeaderMaskInit(&mask, 0);
len = 0;
+ conflictingContentLength_ = false;
PROF_stop(HttpHeaderClean);
}
return reset();
}
+ // XXX: RFC 7230 Section 3.3.3 item #4 requires sending a 502 error in
+ // several cases that we do not yet cover. TODO: Rewrite to cover more.
if (e->id == Http::HdrType::CONTENT_LENGTH && (e2 = findEntry(e->id)) != nullptr) {
if (e->value != e2->value) {
int64_t l1, l2;
} 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 {
+ if (l1 != l2)
+ conflictingContentLength_ = true;
delete e;
continue;
}
if (chunked()) {
// RFC 2616 section 4.4: ignore Content-Length with Transfer-Encoding
delById(Http::HdrType::CONTENT_LENGTH);
+ // RFC 7230 section 3.3.3 #4: ignore Content-Length conflicts with Transfer-Encoding
+ conflictingContentLength_ = false;
+ } else
+ if (conflictingContentLength_) {
+ // ensure our callers do not see the conflicting Content-Length value
+ delById(Http::HdrType::CONTENT_LENGTH);
}
PROF_stop(HttpHeaderParse);
void insertEntry(HttpHeaderEntry * e);
String getList(Http::HdrType id) const;
bool getList(Http::HdrType id, String *s) const;
+ bool conflictingContentLength() const { return conflictingContentLength_; }
String getStrOrList(Http::HdrType id) const;
String getByName(const char *name) const;
/// sets value and returns true iff a [possibly empty] named field is there
private:
HttpHeaderEntry *findLastEntry(Http::HdrType id) const;
+ bool conflictingContentLength_; ///< found different Content-Length fields
};
int httpHeaderParseQuotedString(const char *start, const int len, String *val);
request->flags.proxyKeepalive = request->persistent();
}
+/// checks body length of non-chunked requests
static int
clientIsContentLengthValid(HttpRequest * r)
{
+ // No Content-Length means this request just has no body, but conflicting
+ // Content-Lengths mean a message framing error (RFC 7230 Section 3.3.3 #4).
+ if (r->header.conflictingContentLength())
+ return 0;
+
switch (r->method.id()) {
case Http::METHOD_GET: