]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: h2: always parse and deduplicate the content-length header
authorWilly Tarreau <w@1wt.eu>
Thu, 24 Jan 2019 10:33:02 +0000 (11:33 +0100)
committerWilly Tarreau <w@1wt.eu>
Thu, 24 Jan 2019 18:07:26 +0000 (19:07 +0100)
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.

include/common/h2.h
src/h2.c
src/mux_h2.c

index 1ef57b55274c15bb7aaf48c1ff5be53b4b1bb0ba..71be8ce78568b09f32bbdf3f3c2dcd0be1d81a50 100644 (file)
@@ -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);
 
 /*
index 72b2dc6138443aebee7f543b057ab3a9fda16b7d..1a103e49e52b864d00590705da0f051725eba844 100644 (file)
--- 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 <list>
  * 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 <list>
  * 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;
 
index 5754f79c29194b148a104fbddf2035a947c43919..c0b62303b8b4eb7769fd67fad51c9c1dd202c9b5 100644 (file)
@@ -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);
        }