|
|
07d5a6 |
diff --git a/src/HttpHeader.cc b/src/HttpHeader.cc
|
|
|
07d5a6 |
index ce55a6f..6ce06f2 100644
|
|
|
07d5a6 |
--- a/src/HttpHeader.cc
|
|
|
07d5a6 |
+++ b/src/HttpHeader.cc
|
|
|
07d5a6 |
@@ -470,6 +470,7 @@ HttpHeader::operator =(const HttpHeader &other)
|
|
|
07d5a6 |
update(&other, NULL); // will update the mask as well
|
|
|
07d5a6 |
len = other.len;
|
|
|
07d5a6 |
conflictingContentLength_ = other.conflictingContentLength_;
|
|
|
07d5a6 |
+ teUnsupported_ = other.teUnsupported_;
|
|
|
07d5a6 |
}
|
|
|
07d5a6 |
return *this;
|
|
|
07d5a6 |
}
|
|
|
07d5a6 |
@@ -519,6 +520,7 @@ HttpHeader::clean()
|
|
|
07d5a6 |
httpHeaderMaskInit(&mask, 0);
|
|
|
07d5a6 |
len = 0;
|
|
|
07d5a6 |
conflictingContentLength_ = false;
|
|
|
07d5a6 |
+ teUnsupported_ = false;
|
|
|
07d5a6 |
PROF_stop(HttpHeaderClean);
|
|
|
07d5a6 |
}
|
|
|
07d5a6 |
|
|
|
07d5a6 |
@@ -717,12 +719,24 @@ HttpHeader::parse(const char *header_start, const char *header_end)
|
|
|
07d5a6 |
Raw("header", header_start, (size_t)(header_end - header_start)));
|
|
|
07d5a6 |
}
|
|
|
07d5a6 |
|
|
|
07d5a6 |
- if (chunked()) {
|
|
|
07d5a6 |
+
|
|
|
07d5a6 |
+
|
|
|
07d5a6 |
+ String rawTe;
|
|
|
07d5a6 |
+ if (getByIdIfPresent(HDR_TRANSFER_ENCODING, &rawTe)) {
|
|
|
07d5a6 |
// RFC 2616 section 4.4: ignore Content-Length with Transfer-Encoding
|
|
|
07d5a6 |
// RFC 7230 section 3.3.3 #3: Transfer-Encoding overwrites Content-Length
|
|
|
07d5a6 |
delById(HDR_CONTENT_LENGTH);
|
|
|
07d5a6 |
-
|
|
|
07d5a6 |
// and clen state becomes irrelevant
|
|
|
07d5a6 |
+
|
|
|
07d5a6 |
+ if (rawTe == "chunked") {
|
|
|
07d5a6 |
+ ; // leave header present for chunked() method
|
|
|
07d5a6 |
+ } else if (rawTe == "identity") { // deprecated. no coding
|
|
|
07d5a6 |
+ delById(HDR_TRANSFER_ENCODING);
|
|
|
07d5a6 |
+ } else {
|
|
|
07d5a6 |
+ // This also rejects multiple encodings until we support them properly.
|
|
|
07d5a6 |
+ debugs(55, warnOnError, "WARNING: unsupported Transfer-Encoding used by client: " << rawTe);
|
|
|
07d5a6 |
+ teUnsupported_ = true;
|
|
|
07d5a6 |
+ }
|
|
|
07d5a6 |
} else if (clen.sawBad) {
|
|
|
07d5a6 |
// ensure our callers do not accidentally see bad Content-Length values
|
|
|
07d5a6 |
delById(HDR_CONTENT_LENGTH);
|
|
|
07d5a6 |
@@ -1084,6 +1098,18 @@ HttpHeader::getStrOrList(http_hdr_type id) const
|
|
|
07d5a6 |
return String();
|
|
|
07d5a6 |
}
|
|
|
07d5a6 |
|
|
|
07d5a6 |
+bool
|
|
|
07d5a6 |
+HttpHeader::getByIdIfPresent(http_hdr_type id, String *result) const
|
|
|
07d5a6 |
+{
|
|
|
07d5a6 |
+ if (id == HDR_BAD_HDR)
|
|
|
07d5a6 |
+ return false;
|
|
|
07d5a6 |
+ if (!has(id))
|
|
|
07d5a6 |
+ return false;
|
|
|
07d5a6 |
+ if (result)
|
|
|
07d5a6 |
+ *result = getStrOrList(id);
|
|
|
07d5a6 |
+ return true;
|
|
|
07d5a6 |
+}
|
|
|
07d5a6 |
+
|
|
|
07d5a6 |
/*
|
|
|
07d5a6 |
* Returns the value of the specified header and/or an undefined String.
|
|
|
07d5a6 |
*/
|
|
|
07d5a6 |
diff --git a/src/HttpHeader.h b/src/HttpHeader.h
|
|
|
07d5a6 |
index 836a26f..c49b105 100644
|
|
|
07d5a6 |
--- a/src/HttpHeader.h
|
|
|
07d5a6 |
+++ b/src/HttpHeader.h
|
|
|
07d5a6 |
@@ -239,6 +239,9 @@ public:
|
|
|
07d5a6 |
bool getByNameIfPresent(const char *name, String &value) const;
|
|
|
07d5a6 |
String getByNameListMember(const char *name, const char *member, const char separator) const;
|
|
|
07d5a6 |
String getListMember(http_hdr_type id, const char *member, const char separator) const;
|
|
|
07d5a6 |
+ /// returns true iff a [possibly empty] field identified by id is there
|
|
|
07d5a6 |
+ /// when returning true, also sets the `result` parameter (if it is not nil)
|
|
|
07d5a6 |
+ bool getByIdIfPresent(http_hdr_type id, String *result) const;
|
|
|
07d5a6 |
int has(http_hdr_type id) const;
|
|
|
07d5a6 |
void putInt(http_hdr_type id, int number);
|
|
|
07d5a6 |
void putInt64(http_hdr_type id, int64_t number);
|
|
|
07d5a6 |
@@ -267,7 +270,13 @@ public:
|
|
|
07d5a6 |
int hasListMember(http_hdr_type id, const char *member, const char separator) const;
|
|
|
07d5a6 |
int hasByNameListMember(const char *name, const char *member, const char separator) const;
|
|
|
07d5a6 |
void removeHopByHopEntries();
|
|
|
07d5a6 |
- inline bool chunked() const; ///< whether message uses chunked Transfer-Encoding
|
|
|
07d5a6 |
+
|
|
|
07d5a6 |
+ /// whether the message uses chunked Transfer-Encoding
|
|
|
07d5a6 |
+ /// optimized implementation relies on us rejecting/removing other codings
|
|
|
07d5a6 |
+ bool chunked() const { return has(HDR_TRANSFER_ENCODING); }
|
|
|
07d5a6 |
+
|
|
|
07d5a6 |
+ /// whether message used an unsupported and/or invalid Transfer-Encoding
|
|
|
07d5a6 |
+ bool unsupportedTe() const { return teUnsupported_; }
|
|
|
07d5a6 |
|
|
|
07d5a6 |
/* protected, do not use these, use interface functions instead */
|
|
|
07d5a6 |
std::vector<HttpHeaderEntry *> entries; /**< parsed fields in raw format */
|
|
|
07d5a6 |
@@ -282,6 +291,9 @@ protected:
|
|
|
07d5a6 |
private:
|
|
|
07d5a6 |
HttpHeaderEntry *findLastEntry(http_hdr_type id) const;
|
|
|
07d5a6 |
bool conflictingContentLength_; ///< found different Content-Length fields
|
|
|
07d5a6 |
+ /// unsupported encoding, unnecessary syntax characters, and/or
|
|
|
07d5a6 |
+ /// invalid field-value found in Transfer-Encoding header
|
|
|
07d5a6 |
+ bool teUnsupported_ = false;
|
|
|
07d5a6 |
};
|
|
|
07d5a6 |
|
|
|
07d5a6 |
int httpHeaderParseQuotedString(const char *start, const int len, String *val);
|
|
|
07d5a6 |
@@ -293,13 +305,6 @@ int httpHeaderHasByNameListMember(const HttpHeader * hdr, const char *name, cons
|
|
|
07d5a6 |
void httpHeaderUpdate(HttpHeader * old, const HttpHeader * fresh, const HttpHeaderMask * denied_mask);
|
|
|
07d5a6 |
void httpHeaderCalcMask(HttpHeaderMask * mask, http_hdr_type http_hdr_type_enums[], size_t count);
|
|
|
07d5a6 |
|
|
|
07d5a6 |
-inline bool
|
|
|
07d5a6 |
-HttpHeader::chunked() const
|
|
|
07d5a6 |
-{
|
|
|
07d5a6 |
- return has(HDR_TRANSFER_ENCODING) &&
|
|
|
07d5a6 |
- hasListMember(HDR_TRANSFER_ENCODING, "chunked", ',');
|
|
|
07d5a6 |
-}
|
|
|
07d5a6 |
-
|
|
|
07d5a6 |
void httpHeaderInitModule(void);
|
|
|
07d5a6 |
void httpHeaderCleanModule(void);
|
|
|
07d5a6 |
|
|
|
07d5a6 |
diff --git a/src/client_side.cc b/src/client_side.cc
|
|
|
07d5a6 |
index 261abdf..6858eb4 100644
|
|
|
07d5a6 |
--- a/src/client_side.cc
|
|
|
07d5a6 |
+++ b/src/client_side.cc
|
|
|
07d5a6 |
@@ -2581,9 +2581,7 @@ clientProcessRequest(ConnStateData *conn, HttpParser *hp, ClientSocketContext *c
|
|
|
07d5a6 |
ClientHttpRequest *http = context->http;
|
|
|
07d5a6 |
HttpRequest::Pointer request;
|
|
|
07d5a6 |
bool notedUseOfBuffer = false;
|
|
|
07d5a6 |
- bool chunked = false;
|
|
|
07d5a6 |
bool mustReplyToOptions = false;
|
|
|
07d5a6 |
- bool unsupportedTe = false;
|
|
|
07d5a6 |
bool expectBody = false;
|
|
|
07d5a6 |
|
|
|
07d5a6 |
// temporary hack to avoid splitting this huge function with sensitive code
|
|
|
07d5a6 |
@@ -2767,13 +2765,7 @@ clientProcessRequest(ConnStateData *conn, HttpParser *hp, ClientSocketContext *c
|
|
|
07d5a6 |
// TODO: this effectively obsoletes a lot of conn->FOO copying. That needs cleaning up later.
|
|
|
07d5a6 |
request->clientConnectionManager = conn;
|
|
|
07d5a6 |
|
|
|
07d5a6 |
- if (request->header.chunked()) {
|
|
|
07d5a6 |
- chunked = true;
|
|
|
07d5a6 |
- } else if (request->header.has(HDR_TRANSFER_ENCODING)) {
|
|
|
07d5a6 |
- const String te = request->header.getList(HDR_TRANSFER_ENCODING);
|
|
|
07d5a6 |
- // HTTP/1.1 requires chunking to be the last encoding if there is one
|
|
|
07d5a6 |
- unsupportedTe = te.size() && te != "identity";
|
|
|
07d5a6 |
- } // else implied identity coding
|
|
|
07d5a6 |
+ const auto unsupportedTe = request->header.unsupportedTe();
|
|
|
07d5a6 |
|
|
|
07d5a6 |
mustReplyToOptions = (method == Http::METHOD_OPTIONS) &&
|
|
|
07d5a6 |
(request->header.getInt64(HDR_MAX_FORWARDS) == 0);
|
|
|
07d5a6 |
@@ -2791,6 +2783,7 @@ clientProcessRequest(ConnStateData *conn, HttpParser *hp, ClientSocketContext *c
|
|
|
07d5a6 |
return;
|
|
|
07d5a6 |
}
|
|
|
07d5a6 |
|
|
|
07d5a6 |
+ const auto chunked = request->header.chunked();
|
|
|
07d5a6 |
if (!chunked && !clientIsContentLengthValid(request.getRaw())) {
|
|
|
07d5a6 |
clientStreamNode *node = context->getClientReplyContext();
|
|
|
07d5a6 |
clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
|
|
|
07d5a6 |
diff --git a/src/http.cc b/src/http.cc
|
|
|
07d5a6 |
index 08531dc..f0fe648 100644
|
|
|
07d5a6 |
--- a/src/http.cc
|
|
|
07d5a6 |
+++ b/src/http.cc
|
|
|
07d5a6 |
@@ -1296,6 +1296,9 @@ HttpStateData::continueAfterParsingHeader()
|
|
|
07d5a6 |
} else if (vrep->header.conflictingContentLength()) {
|
|
|
07d5a6 |
fwd->dontRetry(true);
|
|
|
07d5a6 |
error = ERR_INVALID_RESP;
|
|
|
07d5a6 |
+ } else if (vrep->header.unsupportedTe()) {
|
|
|
07d5a6 |
+ fwd->dontRetry(true);
|
|
|
07d5a6 |
+ error = ERR_INVALID_RESP;
|
|
|
07d5a6 |
} else {
|
|
|
07d5a6 |
return true; // done parsing, got reply, and no error
|
|
|
07d5a6 |
}
|