diff --git a/include/common/h2.h b/include/common/h2.h index 1ef57b552..71be8ce78 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 72b2dc613..1a103e49e 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 5754f79c2..c0b62303b 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); }