From: Christopher Faulet Date: Fri, 22 Jan 2021 10:59:07 +0000 (+0100) Subject: MEDIUM: mux-h2: Block client data on server side waiting tunnel establishment X-Git-Tag: v2.4-dev7~102 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f95f87650f5154382fe9c23b80d74175459e7726;p=thirdparty%2Fhaproxy.git MEDIUM: mux-h2: Block client data on server side waiting tunnel establishment 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. --- diff --git a/src/mux_h2.c b/src/mux_h2.c index f367cd172e..38c53fef79 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -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: