BUG/MEDIUM: mux-h1: Fix handling of responses to CONNECT other than 200-ok
authorChristopher Faulet <cfaulet@haproxy.com>
Mon, 22 Feb 2021 07:11:59 +0000 (08:11 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Mon, 22 Feb 2021 09:35:45 +0000 (10:35 +0100)
For a CONNECT request, if the tunnel establishment is refused by the server,
the connection is always closed on the client side. This happen because we
fail to detect the end of the tunnel. Now, when a reponse other than 200-ok
is received, the request is switch back to MSG_DONE state and the end of the
transaction is handled as a classical request/response exchange.

This patch should fix the issue #1140. It must be backported as far as
2.0. There is no upstream commit ID because tunnel management was already
fixed in a non-backportable way in 2.4.

src/mux_h1.c

index 0c1a06a..7b7c83a 100644 (file)
@@ -1502,6 +1502,10 @@ static size_t h1_process_input(struct h1c *h1c, struct buffer *buf, size_t count
 
                        if (!(h1m->flags & H1_MF_RESP) && h1s->status == 101)
                                h1_set_req_tunnel_mode(h1s);
+                       else if ((h1m->flags & H1_MF_RESP) && h1s->req.state == H1_MSG_TUNNEL) {
+                               TRACE_STATE("switch back H1 request from tunnel mode", H1_EV_RX_DATA|H1_EV_H1C_BLK, h1c->conn, h1s);
+                               h1s->req.state = H1_MSG_DONE;
+                       }
                        else if (h1s->req.state < H1_MSG_DONE || h1s->res.state < H1_MSG_DONE) {
                                h1c->flags |= H1C_F_IN_BUSY;
                                TRACE_STATE("switch h1c in busy mode", H1_EV_RX_DATA|H1_EV_H1C_BLK, h1c->conn, h1s);
@@ -1953,7 +1957,13 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
                                        h1_set_req_tunnel_mode(h1s);
                                        TRACE_STATE("switch H1 request in tunnel mode", H1_EV_TX_DATA|H1_EV_TX_HDRS, h1c->conn, h1s);
                                }
-                               else if (h1s->h1c->flags & H1C_F_IN_BUSY) {
+                               else if ((h1m->flags & H1_MF_RESP) && h1s->req.state == H1_MSG_TUNNEL) {
+                                       TRACE_STATE("switch back H1 request from tunnel mode", H1_EV_RX_DATA|H1_EV_H1C_BLK, h1c->conn, h1s);
+                                       h1s->req.state = H1_MSG_DONE;
+                                       h1s->flags |= H1S_F_PARSING_DONE;
+                               }
+
+                               if (h1s->h1c->flags & H1C_F_IN_BUSY) {
                                        h1s->h1c->flags &= ~H1C_F_IN_BUSY;
                                        h1c->conn->xprt->subscribe(h1c->conn, h1c->conn->xprt_ctx, SUB_RETRY_RECV, &h1c->wait_event);
                                        TRACE_STATE("h1c no more busy", H1_EV_TX_DATA|H1_EV_H1C_BLK|H1_EV_H1C_WAKE, h1c->conn, h1s);