]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: mux-h1: Properly handle state transitions of chunked outgoing messages
authorChristopher Faulet <cfaulet@haproxy.com>
Tue, 26 Sep 2023 16:00:49 +0000 (18:00 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Tue, 17 Oct 2023 16:51:13 +0000 (18:51 +0200)
On the sending path, there are 3 states for chunked payload in H1:

  * H1_MSG_CHUNK_SIZE: the chunk size must be emitted
  * H1_MSH_CHUNK_CRLF: The end of the chunk must be emitted
  * H1_MSG_DATA: Chunked data must be emitted

However, some shortcuts were used on the sending path to avoid some
transitions. Especially, outgoing messages were never switched in
H1_MSG_CHUNK_SIZE state.

However, it will be necessary to properly handle all transitions on the payload
to implement mux-to-mux forwarding, to be sure to always known when the chunk
size or the end of the chunk must be emitted.

src/mux_h1.c

index a2261d356d152c68e714efee09b2101fe968ea04..76700870184fc857a12410e756d0bbbdd69b3a47 100644 (file)
@@ -2485,7 +2485,7 @@ static size_t h1_make_eoh(struct h1s *h1s, struct h1m *h1m, struct htx *htx, siz
        else {
                if (!chunk_memcat(&outbuf, "\r\n", 2))
                        goto full;
-               h1m->state = H1_MSG_DATA;
+               h1m->state = ((h1m->flags & H1_MF_CHNK) ? H1_MSG_CHUNK_SIZE: H1_MSG_DATA);
        }
 
        TRACE_PROTO((!(h1m->flags & H1_MF_RESP) ? "H1 request headers xferred" : "H1 response headers xferred"),
@@ -2588,21 +2588,26 @@ static size_t h1_make_data(struct h1s *h1s, struct h1m *h1m, struct buffer *buf,
                         */
 
                        /* If is a new chunk, prepend the chunk size */
-                       if (!h1m->curr_len) {
+                       if (h1m->state == H1_MSG_CHUNK_CRLF || h1m->state == H1_MSG_CHUNK_SIZE) {
+                               if (h1m->curr_len) {
+                                       TRACE_ERROR("too much payload, more than announced",
+                                                   H1_EV_TX_DATA|H1_EV_STRM_ERR|H1_EV_H1C_ERR|H1_EV_H1S_ERR, h1c->conn, h1s);
+                                       goto error;
+                               }
                                h1m->curr_len = count + (htx->extra != HTX_UNKOWN_PAYLOAD_LENGTH ? htx->extra : 0);
                                h1_prepend_chunk_size(&h1c->obuf, h1m->curr_len);
-                       }
-                       h1m->curr_len -= count;
-
-                       /* CRLF of the previous chunk is missing. Prepend it */
-                       if (h1m->state == H1_MSG_CHUNK_CRLF) {
-                               h1_prepend_chunk_crlf(&h1c->obuf);
+                               if (h1m->state == H1_MSG_CHUNK_CRLF)
+                                       h1_prepend_chunk_crlf(&h1c->obuf);
                                h1m->state = H1_MSG_DATA;
                        }
 
+                       h1m->curr_len -= count;
+
                        /* It is the end  of the chunk, append the CRLF */
-                       if (!h1m->curr_len)
+                       if (!h1m->curr_len) {
                                h1_append_chunk_crlf(&h1c->obuf);
+                               h1m->state = H1_MSG_CHUNK_SIZE;
+                       }
 
                        /* It is the end of the message, add the last chunk with the extra CRLF */
                        if (eom) {
@@ -2624,10 +2629,17 @@ static size_t h1_make_data(struct h1s *h1s, struct h1m *h1m, struct buffer *buf,
                b_slow_realign(&h1c->obuf, trash.area, b_data(&h1c->obuf));
        outbuf = b_make(b_tail(&h1c->obuf), b_contig_space(&h1c->obuf), 0, 0);
 
-       if (h1m->state == H1_MSG_CHUNK_CRLF) {
+
+       /* Handle now case of CRLF at the end of a chun. */
+       if ((h1m->flags & H1_MF_CHNK) && h1m->state == H1_MSG_CHUNK_CRLF) {
+               if (h1m->curr_len) {
+                       TRACE_ERROR("too much payload, more than announced",
+                                   H1_EV_TX_DATA|H1_EV_STRM_ERR|H1_EV_H1C_ERR|H1_EV_H1S_ERR, h1c->conn, h1s);
+                       goto error;
+               }
                if (!chunk_memcat(&outbuf, "\r\n", 2))
                        goto full;
-               h1m->state = H1_MSG_DATA;
+               h1m->state = H1_MSG_CHUNK_SIZE;
        }
 
        while (blk && count) {
@@ -2661,12 +2673,13 @@ static size_t h1_make_data(struct h1s *h1s, struct h1m *h1m, struct buffer *buf,
                        chklen = 0;
                        if (h1m->flags & H1_MF_CHNK) {
                                /* If is a new chunk, prepend the chunk size */
-                               if (!h1m->curr_len) {
+                               if (h1m->state == H1_MSG_CHUNK_SIZE) {
                                        h1m->curr_len = (htx->extra && htx->extra != HTX_UNKOWN_PAYLOAD_LENGTH ? htx->data + htx->extra : vlen);
                                        if (!h1_append_chunk_size(&outbuf, h1m->curr_len)) {
                                                h1m->curr_len = 0;
                                                goto full;
                                        }
+                                       h1m->state = H1_MSG_DATA;
                                }
 
                                if (vlen > h1m->curr_len) {
@@ -2696,8 +2709,11 @@ static size_t h1_make_data(struct h1s *h1s, struct h1m *h1m, struct buffer *buf,
                        if (h1m->flags & H1_MF_CHNK) {
                                h1m->curr_len -= vlen;
                                /* Space already reserved, so it must succeed */
-                               if (!h1m->curr_len && !chunk_memcat(&outbuf, "\r\n", 2))
-                                       goto error;
+                               if (!h1m->curr_len) {
+                                       if (!chunk_memcat(&outbuf, "\r\n", 2))
+                                               goto error;
+                                       h1m->state = H1_MSG_CHUNK_SIZE;
+                               }
                                if (last_data && !chunk_memcat(&outbuf, "0\r\n\r\n", 5))
                                        goto error;
                        }
@@ -3014,6 +3030,7 @@ static size_t h1_process_mux(struct h1c *h1c, struct buffer *buf, size_t count)
                                ret = h1_make_eoh(h1s, h1m, htx, count);
                                break;
 
+                       case H1_MSG_CHUNK_SIZE:
                        case H1_MSG_CHUNK_CRLF:
                        case H1_MSG_DATA:
                                ret = h1_make_data(h1s, h1m, buf, count);