]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: mux-h2: Block client data on server side waiting tunnel establishment
authorChristopher Faulet <cfaulet@haproxy.com>
Fri, 22 Jan 2021 10:59:07 +0000 (11:59 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Thu, 28 Jan 2021 15:37:14 +0000 (16:37 +0100)
On the server side, when a tunnel is not fully established, we must block
tunneled data, waiting for the server response. It is mandatory because the
server may refuse the tunnel. This happens when a DATA htx block is
processed in tunnel mode (H2_SF_BODY_TUNNEL flag set) but before the
response HEADERS frame is received (H2_SF_HEADERS_RCVD flag no set). In this
case, the H2_SF_BLK_MBUSY flag is set to mark the stream as busy. This flag
is removed when the tunnel is fully established or aborted.

This patch contributes to fix the tunnel mode between the H1 and the H2
muxes.

src/mux_h2.c

index f367cd172e9ae5176192c8d86d2e72f0fbc5cc43..38c53fef795cc6cef0a3f0fcab29684107b24534 100644 (file)
@@ -2796,6 +2796,16 @@ static struct h2s *h2c_bck_handle_headers(struct h2c *h2c, struct h2s *h2s)
                        h2s_close(h2s);
        }
 
+       /* Unblock busy server h2s waiting for the response headers to validate
+        * the tunnel establishment or the end of the response of an oborted
+        * tunnel
+        */
+       if ((h2s->flags & (H2_SF_BODY_TUNNEL|H2_SF_BLK_MBUSY)) == (H2_SF_BODY_TUNNEL|H2_SF_BLK_MBUSY) ||
+           (h2s->flags & (H2_SF_TUNNEL_ABRT|H2_SF_ES_RCVD|H2_SF_BLK_MBUSY)) == (H2_SF_TUNNEL_ABRT|H2_SF_ES_RCVD|H2_SF_BLK_MBUSY)) {
+               TRACE_STATE("Unblock h2s blocked on tunnel establishment/abort", H2_EV_RX_FRAME|H2_EV_RX_DATA, h2c->conn, h2s);
+               h2s->flags &= ~H2_SF_BLK_MBUSY;
+       }
+
        TRACE_USER("rcvd H2 response", H2_EV_RX_FRAME|H2_EV_RX_HDR, h2c->conn, 0, &h2s->rxbuf);
        TRACE_LEAVE(H2_EV_RX_FRAME|H2_EV_RX_HDR, h2c->conn, h2s);
        return h2s;
@@ -2897,6 +2907,15 @@ static int h2c_handle_data(struct h2c *h2c, struct h2s *h2s)
                }
        }
 
+       /* Unblock busy server h2s waiting for the end of the response for an
+        * aborted tunnel
+        */
+       if ((h2c->flags & H2_CF_IS_BACK) &&
+           (h2s->flags & (H2_SF_TUNNEL_ABRT|H2_SF_ES_RCVD|H2_SF_BLK_MBUSY)) == (H2_SF_TUNNEL_ABRT|H2_SF_ES_RCVD|H2_SF_BLK_MBUSY)) {
+               TRACE_STATE("Unblock h2s blocked on tunnel abort", H2_EV_RX_FRAME|H2_EV_RX_DATA, h2c->conn, h2s);
+               h2s->flags &= ~H2_SF_BLK_MBUSY;
+       }
+
        TRACE_LEAVE(H2_EV_RX_FRAME|H2_EV_RX_DATA, h2c->conn, h2s);
        return 1;
 
@@ -5508,6 +5527,16 @@ static size_t h2s_make_data(struct h2s *h2s, struct buffer *buf, size_t count)
        }
        else if (type != HTX_BLK_DATA)
                goto end;
+       else if ((h2c->flags & H2_CF_IS_BACK) &&
+                (h2s->flags & (H2_SF_HEADERS_RCVD|H2_SF_BODY_TUNNEL)) == H2_SF_BODY_TUNNEL) {
+               /* The response HEADERS frame not received yet. Thus the tunnel
+                * is not fully established yet. In this situation, we block
+                * data sending.
+                */
+               h2s->flags |= H2_SF_BLK_MBUSY;
+               TRACE_STATE("Request DATA frame blocked waiting for tunnel establishment", H2_EV_TX_FRAME|H2_EV_TX_DATA, h2c->conn, h2s);
+               goto end;
+       }
 
        mbuf = br_tail(h2c->mbuf);
  retry: