]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: http-ana: Properly switch the request in tunnel mode on upgrade
authorChristopher Faulet <cfaulet@haproxy.com>
Mon, 17 Apr 2023 06:52:10 +0000 (08:52 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Mon, 17 Apr 2023 14:17:35 +0000 (16:17 +0200)
Since the commit f2b02cfd9 ("MAJOR: http-ana: Review error handling during
HTTP payload forwarding"), during the payload forwarding, we are analyzing a
side, we stop to test the opposite side. It means when the HTTP request
forwarding analyzer is called, we no longer check the response side and vice
versa.

Unfortunately, since then, the HTTP tunneling is broken after a protocol
upgrade. On the response is switch in TUNNEL mode. The request remains in
DONE state. As a consequence, data received from the server are forwarded to
the client but not data received from the client.

To fix the bug, when both sides are in DONE state, both are switched in same
time in TUNNEL mode if it was requested. It is performed in the same way in
http_end_request() and http_end_response().

This patch should fix the issue #2125. It is 2.8-specific. No backport
needed.

reg-tests/http-messaging/websocket.vtc
src/http_ana.c

index 5f4b960e8eac044a0b75bb722365aee3964c1f63..aed55fe5153d1742d838a08ab3a30a0c5c78254e 100644 (file)
@@ -27,7 +27,10 @@ server s1 {
          -hdr "connection: upgrade" \
          -hdr "upgrade: websocket" \
          -hdr "sec-websocket-accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
-} -repeat 2 -start
+
+        recv 4
+       send "PONG"
+} -start
 
 # non-conformant server: no websocket key
 server s2 {
@@ -124,6 +127,9 @@ client c1 -connect ${hap_fe1_sock} {
        expect resp.http.connection == "upgrade"
        expect resp.http.upgrade == "websocket"
        expect resp.http.sec-websocket-accept == "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
+
+       send "PING"
+       recv 4
 } -run
 
 # missing websocket key
index 8828e6b12b1365ddabe324fa579fd4a67016df63..9097856ffac15aef6562a23eb8276c4fca9b8673 100644 (file)
@@ -2089,10 +2089,9 @@ int http_response_forward_body(struct stream *s, struct channel *res, int an_bit
                }
        }
 
-       if ((txn->meth == HTTP_METH_CONNECT && txn->status >= 200 && txn->status < 300) || txn->status == 101 ||
-           !(msg->flags & HTTP_MSGF_XFER_LEN)) {
+       if (!(txn->flags & TX_CON_WANT_TUN) && !(msg->flags & HTTP_MSGF_XFER_LEN)) {
+               /* One-side tunnel */
                msg->msg_state = HTTP_MSG_TUNNEL;
-               goto ending;
        }
        else {
                msg->msg_state = HTTP_MSG_DONE;
@@ -4255,8 +4254,10 @@ static void http_end_request(struct stream *s)
                        /* Tunnel mode will not have any analyser so it needs to
                         * poll for reads.
                         */
-                       channel_auto_read(chn);
+                       channel_auto_read(&s->req);
                        txn->req.msg_state = HTTP_MSG_TUNNEL;
+                       channel_auto_read(&s->res);
+                       txn->rsp.msg_state = HTTP_MSG_TUNNEL;
                }
                else {
                        /* we're not expecting any new data to come for this
@@ -4363,7 +4364,9 @@ static void http_end_response(struct stream *s)
                 * direction, and sometimes for a close to be effective.
                 */
                if (txn->flags & TX_CON_WANT_TUN) {
-                       channel_auto_read(chn);
+                       channel_auto_read(&s->req);
+                       txn->req.msg_state = HTTP_MSG_TUNNEL;
+                       channel_auto_read(&s->res);
                        txn->rsp.msg_state = HTTP_MSG_TUNNEL;
                }
                else {