MEDIUM: h1: better handle transfer-encoding vs content-length
The transfer-encoding header processing was a bit lenient in this part because it was made to read messages already validated by haproxy. We absolutely need to reinstate the strict processing defined in RFC7230 as is currently being done in proto_http.c. That is, transfer-encoding presence alone is enough to cancel content-length, and must be terminated by the "chunked" token, except in the response where we can fall back to the close mode if it's not last. For this we now use a specific parsing function which updates the flags and we introduce a new flag H1_MF_XFER_ENC indicating that the transfer-encoding header is present. Last, if such a header is found, we delete all content-length header fields found in the message.
This commit is contained in:
parent
2ea6bb5c31
commit
2557f6a3e2
|
@ -145,6 +145,7 @@ enum h1m_state {
|
|||
#define H1_MF_CONN_KAL 0x00000040 // message contains "connection: keep-alive"
|
||||
#define H1_MF_CONN_UPG 0x00000080 // message contains "connection: upgrade"
|
||||
#define H1_MF_XFER_LEN 0x00000100 // message xfer size can be determined
|
||||
#define H1_MF_XFER_ENC 0x00000200 // transfer-encoding is present
|
||||
|
||||
/* Note: for a connection to be persistent, we need this for the request :
|
||||
* - one of CLEN or CHNK
|
||||
|
|
62
src/h1.c
62
src/h1.c
|
@ -660,6 +660,44 @@ void http_msg_analyzer(struct http_msg *msg, struct hdr_idx *idx)
|
|||
}
|
||||
|
||||
|
||||
/* Parse the Transfer-Encoding: header field of an HTTP/1 request, looking for
|
||||
* "chunked" being the last value, and setting H1_MF_CHNK in h1m->flags only in
|
||||
* this case. Any other token found or any empty header field found will reset
|
||||
* this flag, so that it accurately represents the token's presence at the last
|
||||
* position. The H1_MF_XFER_ENC flag is always set. Note that transfer codings
|
||||
* are case-insensitive (cf RFC7230#4).
|
||||
*/
|
||||
void h1_parse_xfer_enc_header(struct h1m *h1m, struct ist value)
|
||||
{
|
||||
char *e, *n;
|
||||
struct ist word;
|
||||
|
||||
h1m->flags |= H1_MF_XFER_ENC;
|
||||
h1m->flags &= ~H1_MF_CHNK;
|
||||
|
||||
word.ptr = value.ptr - 1; // -1 for next loop's pre-increment
|
||||
e = value.ptr + value.len;
|
||||
|
||||
while (++word.ptr < e) {
|
||||
/* skip leading delimitor and blanks */
|
||||
if (HTTP_IS_LWS(*word.ptr))
|
||||
continue;
|
||||
|
||||
n = http_find_hdr_value_end(word.ptr, e); // next comma or end of line
|
||||
word.len = n - word.ptr;
|
||||
|
||||
/* trim trailing blanks */
|
||||
while (word.len && HTTP_IS_LWS(word.ptr[word.len-1]))
|
||||
word.len--;
|
||||
|
||||
h1m->flags &= ~H1_MF_CHNK;
|
||||
if (isteqi(word, ist("chunked")))
|
||||
h1m->flags |= H1_MF_CHNK;
|
||||
|
||||
word.ptr = n;
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse the Connection: header of an HTTP/1 request, looking for "close",
|
||||
* "keep-alive", and "upgrade" values, and updating h1m->flags according to
|
||||
* what was found there. Note that flags are only added, not removed, so the
|
||||
|
@ -1272,8 +1310,7 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
|
|||
}
|
||||
|
||||
if (isteqi(n, ist("transfer-encoding"))) {
|
||||
h1m->flags &= ~H1_MF_CLEN;
|
||||
h1m->flags |= H1_MF_CHNK;
|
||||
h1_parse_xfer_enc_header(h1m, v);
|
||||
}
|
||||
else if (isteqi(n, ist("content-length")) && !(h1m->flags & H1_MF_CHNK)) {
|
||||
h1m->flags |= H1_MF_CLEN;
|
||||
|
@ -1321,10 +1358,23 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
|
|||
if (restarting)
|
||||
goto restart;
|
||||
|
||||
if (h1m->flags & H1_MF_CHNK)
|
||||
state = H1_MSG_CHUNK_SIZE;
|
||||
else
|
||||
state = H1_MSG_DATA;
|
||||
state = H1_MSG_DATA;
|
||||
if (h1m->flags & H1_MF_XFER_ENC) {
|
||||
if (h1m->flags & H1_MF_CLEN) {
|
||||
h1m->flags &= ~H1_MF_CLEN;
|
||||
hdr_count = http_del_hdr(hdr, ist("content-length"));
|
||||
}
|
||||
|
||||
if (h1m->flags & H1_MF_CHNK)
|
||||
state = H1_MSG_CHUNK_SIZE;
|
||||
else if (!(h1m->flags & H1_MF_RESP)) {
|
||||
/* cf RFC7230#3.3.3 : transfer-encoding in
|
||||
* request without chunked encoding is invalid.
|
||||
*/
|
||||
goto http_msg_invalid;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
Loading…
Reference in New Issue