]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: mux-h2: Properly handle end of request to expect data from server
authorChristopher Faulet <cfaulet@haproxy.com>
Thu, 4 May 2023 13:49:12 +0000 (15:49 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Thu, 4 May 2023 14:29:27 +0000 (16:29 +0200)
The commit 2722c04b ("MEDIUM: mux-h2: Don't expect data from server as long
as request is unfinished") introduced a regression in the H2 multiplexer.
The end of the request is not systematically handled to state a H2 stream on
client side now expexts data from the server.

Indeed, while the client is uploading its request, the H2 stream warns it
does not expect data from the server. This way, no server timeout is applied
at this stage. When end of the request is detected, the H2 stream must state
it now expects the server response. This enables the server timeout.

However, it was only performed at one place while the end of the request can
be handled at different places. First, during a zero-copy in
h2_rcv_buf(). Then, when the SC is created with the full request. Because of
this bug, it is possible to totally disable the server timeout for H2
streams.

In h2_rcv_buf(), we now rely on h2s flags to detect the end of the request,
but only when the rxbuf was emptied.

It is a 2.8-specific bug. No backport needed.

src/mux_h2.c

index 4b8592a16eddc4711f7d5b811b7ed64f1ecf0b54..44bb51fb3c32cf5c6ab48471c4e8520839a746d5 100644 (file)
@@ -1560,7 +1560,11 @@ static struct h2s *h2c_frt_stream_new(struct h2c *h2c, int id, struct buffer *in
        h2s->sd->se   = h2s;
        h2s->sd->conn = h2c->conn;
        se_fl_set(h2s->sd, SE_FL_T_MUX | SE_FL_ORPHAN | SE_FL_NOT_FIRST);
-       se_expect_no_data(h2s->sd);
+       /* The request is not finished, don't expect data from the opposite side
+        * yet
+        */
+       if (!(h2c->dff & (H2_F_HEADERS_END_STREAM| H2_F_DATA_END_STREAM)))
+               se_expect_no_data(h2s->sd);
 
        /* FIXME wrong analogy between ext-connect and websocket, this need to
         * be refine.
@@ -6430,7 +6434,6 @@ static size_t h2_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, in
                htx_to_buf(h2s_htx, &h2s->rxbuf);
                goto end;
        }
-
        ret = h2s_htx->data;
        buf_htx = htx_from_buf(buf);
 
@@ -6452,13 +6455,6 @@ static size_t h2_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, in
        }
        else if (htx_is_empty(h2s_htx)) {
                buf_htx->flags |= (h2s_htx->flags & HTX_FL_EOM);
-
-               if (!(h2c->flags & H2_CF_IS_BACK) && (buf_htx->flags & HTX_FL_EOM)) {
-                       /* If request EOM is reported to the upper layer, it means the
-                        * H2S now expects data from the opposite side.
-                        */
-                       se_expect_data(h2s->sd);
-               }
        }
 
        buf_htx->extra = (h2s_htx->extra ? (h2s_htx->data + h2s_htx->extra) : 0);
@@ -6470,6 +6466,13 @@ static size_t h2_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, in
        if (b_data(&h2s->rxbuf))
                se_fl_set(h2s->sd, SE_FL_RCV_MORE | SE_FL_WANT_ROOM);
        else {
+               if (!(h2c->flags & H2_CF_IS_BACK) && (h2s->flags & H2_SF_ES_RCVD)) {
+                       /* If request ES is reported to the upper layer, it means the
+                        * H2S now expects data from the opposite side.
+                        */
+                       se_expect_data(h2s->sd);
+               }
+
                se_fl_clr(h2s->sd, SE_FL_RCV_MORE | SE_FL_WANT_ROOM);
                if (h2s->flags & H2_SF_ES_RCVD) {
                        se_fl_set(h2s->sd, SE_FL_EOI);