]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MEDIUM: http-ana: apply tunnel timeout with data filters
authorManu Nicolas <e.nicolas@criteo.com>
Thu, 3 Sep 2026 14:10:54 +0000 (14:10 +0000)
committerChristopher Faulet <cfaulet@haproxy.com>
Tue, 8 Sep 2026 14:29:49 +0000 (16:29 +0200)
The tunnel timeout is installed by process_stream() only once all
analysers are gone. HTTP data filters keep their transfer and end
analysers attached after a protocol upgrade. A stream using, for
example, set-bandwidth-limit therefore remains on its client and server
timeouts even though both HTTP messages are in tunnel mode. A short
client timeout can close a WebSocket before its heartbeat.

Apply the per-stream tunnel timeout when the second HTTP message enters
tunnel mode. Data-filter analysers remain active and continue to process
tunneled data.

It should be backported as far as 2.6.

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

index 3990ddb9a0efe373c7b7f2e30fcfe8f9ba7a200d..6414ebf93999d906f805f81ebe5e6c9708f35c14 100644 (file)
@@ -28,6 +28,7 @@ server s1 {
 
         recv 4
        send "PONG"
+       expect_close
 } -start
 
 # non-conformant server: no websocket key
@@ -111,6 +112,13 @@ haproxy hap -conf {
 
        listen fe1
        bind "fd@${fe1}"
+       timeout client 100ms
+       timeout server 100ms
+       timeout tunnel 1s
+       filter bwlim-in upload default-limit 1m default-period 1s
+       filter bwlim-out download default-limit 1m default-period 1s
+       http-request set-bandwidth-limit upload
+       http-request set-bandwidth-limit download
        server s1 ${s1_addr}:${s1_port}
 
        listen fe2
@@ -141,8 +149,10 @@ client c1 -connect ${hap_fe1_sock} {
        expect resp.http.upgrade == "websocket"
        expect resp.http.sec-websocket-accept == "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
 
+       delay 0.2
        send "PING"
        recv 4
+       expect_close
 } -run
 
 # missing websocket key
index f113529750e11081dee12c3fc7145a46abf2eb16..bcfa4849d8521ccb9ea83e0c21366ea05d350662 100644 (file)
@@ -4588,14 +4588,14 @@ static void http_end_request(struct stream *s)
                 * direction, and sometimes for a close to be effective.
                 */
                if (txn->flags & TX_CON_WANT_TUN) {
-                       /* Tunnel mode will not have any analyser so it needs to
-                        * poll for reads.
-                        */
+                       /* Tunnel mode needs to poll for reads. */
                        channel_auto_read(&s->req);
                        txn->req.msg_state = HTTP_MSG_TUNNEL;
                        s->scb->flags &= ~SC_FL_NOHALF;
                        if (txn->rsp.msg_state != HTTP_MSG_TUNNEL)
                                s->res.flags |= CF_WAKE_ONCE;
+                       else if (s->tunnel_timeout)
+                               s->scf->ioto = s->scb->ioto = s->tunnel_timeout;
                }
                else {
                        /* we're not expecting any new data to come for this
@@ -4708,6 +4708,8 @@ static void http_end_response(struct stream *s)
                        txn->rsp.msg_state = HTTP_MSG_TUNNEL;
                        if (txn->req.msg_state != HTTP_MSG_TUNNEL)
                                s->req.flags |= CF_WAKE_ONCE;
+                       else if (s->tunnel_timeout)
+                               s->scf->ioto = s->scb->ioto = s->tunnel_timeout;
                }
                else {
                        /* we're not expecting any new data to come for this