Hi Jaroslaw,

thanks to the details you sent, I could reproduce the issue. In fact it's
the MSG_MORE which is causing the delay and which should not be emitted at
this point. The issue also existed in http-server-close mode if the server
responded before the client finished to transfer a POST body, though this
case was obviously much less noticeable.

I have fixed it by correctly setting the channels not to delay transfers
when switching to tunnel mode, and now it works fine for me.

If you're running with an 1.5-dev snapshot, you can use the attached patch
that I have committed to the master branch. I'll backport it to 1.4 too.

Best regards,
Willy

>From fc47f91c9cc66e3652d98deab82d6e5fe3a59711 Mon Sep 17 00:00:00 2001
From: Willy Tarreau <[email protected]>
Date: Sat, 20 Oct 2012 10:38:09 +0200
Subject: [PATCH] BUG/MEDIUM: http: set DONTWAIT on data when switching to
 tunnel mode

Jaroslaw Bojar diagnosed an issue when haproxy switches to tunnel mode
after a transfer. The response data are sent with the MSG_MORE flag,
causing them to be needlessly queued in the kernel. In order to fix this,
we set the CF_NEVER_WAIT flag on the channels when switching to tunnel
mode.

One issue remained with client-side keep-alive : if the response is sent
before the end of the request, it suffers the same issue for the same
reason. This is easily addressed by setting the CF_SEND_DONTWAIT flag
on the channel when the response has been parsed and we're waiting for
the other side.

The same issue is present in 1.4 so the fix must be backported.
---
 src/proto_http.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/src/proto_http.c b/src/proto_http.c
index 66dc87d..9cdf3be 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -3986,6 +3986,7 @@ int http_sync_req_state(struct session *s)
                        /* if any side switches to tunnel mode, the other one 
does too */
                        channel_auto_read(chn);
                        txn->req.msg_state = HTTP_MSG_TUNNEL;
+                       chn->flags |= CF_NEVER_WAIT;
                        goto wait_other_side;
                }
 
@@ -4019,6 +4020,7 @@ int http_sync_req_state(struct session *s)
                         */
                        channel_auto_read(chn);
                        txn->req.msg_state = HTTP_MSG_TUNNEL;
+                       chn->flags |= CF_NEVER_WAIT;
                }
 
                if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW)) {
@@ -4105,6 +4107,7 @@ int http_sync_res_state(struct session *s)
                        /* if any side switches to tunnel mode, the other one 
does too */
                        channel_auto_read(chn);
                        txn->rsp.msg_state = HTTP_MSG_TUNNEL;
+                       chn->flags |= CF_NEVER_WAIT;
                        goto wait_other_side;
                }
 
@@ -4142,6 +4145,7 @@ int http_sync_res_state(struct session *s)
                         */
                        channel_auto_read(chn);
                        txn->rsp.msg_state = HTTP_MSG_TUNNEL;
+                       chn->flags |= CF_NEVER_WAIT;
                }
 
                if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW)) {
@@ -4187,6 +4191,11 @@ int http_sync_res_state(struct session *s)
 
  wait_other_side:
        http_silent_debug(__LINE__, s);
+       /* We force the response to leave immediately if we're waiting for the
+        * other side, since there is no pending shutdown to push it out.
+        */
+       if (!channel_is_empty(chn))
+               chn->flags |= CF_SEND_DONTWAIT;
        return txn->rsp.msg_state != old_state || chn->flags != old_flags;
 }
 
-- 
1.7.12.2.21.g234cd45.dirty

Reply via email to