From: Willy Tarreau Date: Thu, 24 Jan 2019 10:33:02 +0000 (+0100) Subject: MEDIUM: h2: always parse and deduplicate the content-length header X-Git-Tag: v2.0-dev1~155 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4790f7c90743ce52021373c5567d78c32c42d972;p=thirdparty%2Fhaproxy.git MEDIUM: h2: always parse and deduplicate the content-length header The header used to be parsed only in HTX but not in legacy. And even in HTX mode, the value was dropped. Let's always parse it and report the parsed value back so that we'll be able to store it in the streams. --- diff --git a/include/common/h2.h b/include/common/h2.h index 1ef57b5527..71be8ce785 100644 --- a/include/common/h2.h +++ b/include/common/h2.h @@ -181,11 +181,11 @@ enum h2_err { /* various protocol processing functions */ -int h2_make_h1_request(struct http_hdr *list, char *out, int osize, unsigned int *msgf); +int h2_make_h1_request(struct http_hdr *list, char *out, int osize, unsigned int *msgf, unsigned long long *body_len); int h2_make_h1_trailers(struct http_hdr *list, char *out, int osize); int h2_parse_cont_len_header(unsigned int *msgf, struct ist *value, unsigned long long *body_len); -int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *msgf); -int h2_make_htx_response(struct http_hdr *list, struct htx *htx, unsigned int *msgf); +int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *msgf, unsigned long long *body_len); +int h2_make_htx_response(struct http_hdr *list, struct htx *htx, unsigned int *msgf, unsigned long long *body_len); int h2_make_htx_trailers(struct http_hdr *list, struct htx *htx); /* diff --git a/src/h2.c b/src/h2.c index 72b2dc6138..1a103e49e5 100644 --- a/src/h2.c +++ b/src/h2.c @@ -130,7 +130,7 @@ static int h2_prepare_h1_reqline(uint32_t fields, struct ist *phdr, char **ptr, * The Cookie header will be reassembled at the end, and for this, the * will be used to create a linked list, so its contents may be destroyed. */ -int h2_make_h1_request(struct http_hdr *list, char *out, int osize, unsigned int *msgf) +int h2_make_h1_request(struct http_hdr *list, char *out, int osize, unsigned int *msgf, unsigned long long *body_len) { struct ist phdr_val[H2_PHDR_NUM_ENTRIES]; char *out_end = out + osize; @@ -191,9 +191,14 @@ int h2_make_h1_request(struct http_hdr *list, char *out, int osize, unsigned int if (isteq(list[idx].n, ist("host"))) fields |= H2_PHDR_FND_HOST; - if ((*msgf & (H2_MSGF_BODY|H2_MSGF_BODY_TUNNEL|H2_MSGF_BODY_CL)) == H2_MSGF_BODY && - isteq(list[idx].n, ist("content-length"))) - *msgf |= H2_MSGF_BODY_CL; + if (isteq(list[idx].n, ist("content-length"))) { + ret = h2_parse_cont_len_header(msgf, &list[idx].v, body_len); + if (ret < 0) + goto fail; + + if (ret == 0) + continue; // skip this duplicate + } /* these ones are forbidden in requests (RFC7540#8.1.2.2) */ if (isteq(list[idx].n, ist("connection")) || @@ -556,7 +561,7 @@ static struct htx_sl *h2_prepare_htx_reqline(uint32_t fields, struct ist *phdr, * The Cookie header will be reassembled at the end, and for this, the * will be used to create a linked list, so its contents may be destroyed. */ -int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *msgf) +int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *msgf, unsigned long long *body_len) { struct ist phdr_val[H2_PHDR_NUM_ENTRIES]; uint32_t fields; /* bit mask of H2_PHDR_FND_* */ @@ -567,7 +572,6 @@ int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *ms int i; struct htx_sl *sl = NULL; unsigned int sl_flags = 0; - unsigned long long body_len; lck = ck = -1; // no cookie for now fields = 0; @@ -620,7 +624,7 @@ int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *ms fields |= H2_PHDR_FND_HOST; if (isteq(list[idx].n, ist("content-length"))) { - ret = h2_parse_cont_len_header(msgf, &list[idx].v, &body_len); + ret = h2_parse_cont_len_header(msgf, &list[idx].v, body_len); if (ret < 0) goto fail; @@ -784,7 +788,7 @@ static struct htx_sl *h2_prepare_htx_stsline(uint32_t fields, struct ist *phdr, * - in all cases except the end of list, v.name and v.len must designate a * valid value. */ -int h2_make_htx_response(struct http_hdr *list, struct htx *htx, unsigned int *msgf) +int h2_make_htx_response(struct http_hdr *list, struct htx *htx, unsigned int *msgf, unsigned long long *body_len) { struct ist phdr_val[H2_PHDR_NUM_ENTRIES]; uint32_t fields; /* bit mask of H2_PHDR_FND_* */ @@ -794,7 +798,6 @@ int h2_make_htx_response(struct http_hdr *list, struct htx *htx, unsigned int *m int i; struct htx_sl *sl = NULL; unsigned int sl_flags = 0; - unsigned long long body_len; fields = 0; for (idx = 0; list[idx].n.len != 0; idx++) { @@ -843,7 +846,7 @@ int h2_make_htx_response(struct http_hdr *list, struct htx *htx, unsigned int *m } if (isteq(list[idx].n, ist("content-length"))) { - ret = h2_parse_cont_len_header(msgf, &list[idx].v, &body_len); + ret = h2_parse_cont_len_header(msgf, &list[idx].v, body_len); if (ret < 0) goto fail; diff --git a/src/mux_h2.c b/src/mux_h2.c index 5754f79c29..c0b62303b8 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -270,7 +270,7 @@ static int h2_recv(struct h2c *h2c); static int h2_process(struct h2c *h2c); static struct task *h2_io_cb(struct task *t, void *ctx, unsigned short state); static inline struct h2s *h2c_st_by_id(struct h2c *h2c, int id); -static int h2c_decode_headers(struct h2c *h2c, struct buffer *rxbuf, uint32_t *flags); +static int h2c_decode_headers(struct h2c *h2c, struct buffer *rxbuf, uint32_t *flags, unsigned long long *body_len); static int h2_frt_transfer_data(struct h2s *h2s); static struct task *h2_deferred_shut(struct task *t, void *ctx, unsigned short state); static struct h2s *h2c_bck_stream_new(struct h2c *h2c, struct conn_stream *cs, struct session *sess); @@ -1893,6 +1893,7 @@ static int h2c_handle_rst_stream(struct h2c *h2c, struct h2s *h2s) static struct h2s *h2c_frt_handle_headers(struct h2c *h2c, struct h2s *h2s) { struct buffer rxbuf = BUF_NULL; + unsigned long long body_len = 0; uint32_t flags = 0; int error; @@ -1913,7 +1914,7 @@ static struct h2s *h2c_frt_handle_headers(struct h2c *h2c, struct h2s *h2s) if (h2s->st != H2_SS_IDLE) { /* The stream exists/existed, this must be a trailers frame */ if (h2s->st != H2_SS_CLOSED) { - if (h2c_decode_headers(h2c, &h2s->rxbuf, &h2s->flags) <= 0) + if (h2c_decode_headers(h2c, &h2s->rxbuf, &h2s->flags, &body_len) <= 0) goto out; goto done; } @@ -1930,7 +1931,7 @@ static struct h2s *h2c_frt_handle_headers(struct h2c *h2c, struct h2s *h2s) else if (h2c->flags & H2_CF_DEM_TOOMANY) goto out; // IDLE but too many cs still present - error = h2c_decode_headers(h2c, &rxbuf, &flags); + error = h2c_decode_headers(h2c, &rxbuf, &flags, &body_len); /* unrecoverable error ? */ if (h2c->st0 >= H2_CS_ERROR) @@ -2001,6 +2002,7 @@ static struct h2s *h2c_frt_handle_headers(struct h2c *h2c, struct h2s *h2s) */ static struct h2s *h2c_bck_handle_headers(struct h2c *h2c, struct h2s *h2s) { + unsigned long long body_len = 0; int error; if (!h2c->dfl) { @@ -2016,7 +2018,7 @@ static struct h2s *h2c_bck_handle_headers(struct h2c *h2c, struct h2s *h2s) if (b_data(&h2c->dbuf) < h2c->dfl && !b_full(&h2c->dbuf)) return NULL; // incomplete frame - error = h2c_decode_headers(h2c, &h2s->rxbuf, &h2s->flags); + error = h2c_decode_headers(h2c, &h2s->rxbuf, &h2s->flags, &body_len); /* unrecoverable error ? */ if (h2c->st0 >= H2_CS_ERROR) @@ -3271,7 +3273,7 @@ static void h2_shutw(struct conn_stream *cs, enum cs_shw_mode mode) * decoding, in order to detect if we're dealing with a headers or a trailers * block (the trailers block appears after H2_SF_HEADERS_RCVD was seen). */ -static int h2c_decode_headers(struct h2c *h2c, struct buffer *rxbuf, uint32_t *flags) +static int h2c_decode_headers(struct h2c *h2c, struct buffer *rxbuf, uint32_t *flags, unsigned long long *body_len) { const uint8_t *hdrs = (uint8_t *)b_head(&h2c->dbuf); struct buffer *tmp = get_trash_chunk(); @@ -3431,12 +3433,12 @@ next_frame: if (htx) { /* HTX mode */ if (h2c->flags & H2_CF_IS_BACK) - outlen = h2_make_htx_response(list, htx, &msgf); + outlen = h2_make_htx_response(list, htx, &msgf, body_len); else - outlen = h2_make_htx_request(list, htx, &msgf); + outlen = h2_make_htx_request(list, htx, &msgf, body_len); } else { /* HTTP/1 mode */ - outlen = h2_make_h1_request(list, b_tail(rxbuf), try, &msgf); + outlen = h2_make_h1_request(list, b_tail(rxbuf), try, &msgf, body_len); if (outlen > 0) b_add(rxbuf, outlen); }