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.

This has been broken since stream-level bandwidth limitation was added
by commit 2b6777021d55 ("MEDIUM: bwlim: Add support of
bandwith limitation at the stream level"). It should be backported as
far as 2.7.
---
 reg-tests/http-messaging/websocket.vtc | 10 ++++++++++
 src/http_ana.c                         |  8 +++++---
 2 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/reg-tests/http-messaging/websocket.vtc 
b/reg-tests/http-messaging/websocket.vtc
index 3990ddb9a..6414ebf93 100644
--- a/reg-tests/http-messaging/websocket.vtc
+++ b/reg-tests/http-messaging/websocket.vtc
@@ -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
diff --git a/src/http_ana.c b/src/http_ana.c
index f11352975..bcfa4849d 100644
--- a/src/http_ana.c
+++ b/src/http_ana.c
@@ -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
-- 
2.55.0

Reply via email to