]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[BUG] http: don't set auto_close if more data are expected
authorWilly Tarreau <w@1wt.eu>
Sat, 28 Aug 2010 16:57:20 +0000 (18:57 +0200)
committerWilly Tarreau <w@1wt.eu>
Sat, 28 Aug 2010 17:06:28 +0000 (19:06 +0200)
Fix 4fe41902789d188ee4c23b14a7cdbf075463b158 was a bit too strong. It
has caused some chunked-encoded responses to be truncated when a recv()
call could return multiple chunks followed by a close. The reason is
that when a chunk is parsed, only its contents are scheduled to be
forwarded. Thus, the reader sees auto_close+shutr and sets shutw_now.
The sender in turn sends the last scheduled data and does shutw().

Another nasty effect is that it has reduced the keep-alive rate. If
a response did not completely fit into the buffer, then the auto_close
bit was left on and the sender would close upon completion.

The fix consists in not making use of auto_close when chunked encoding
is used nor when keep-alive is used, which makes sense. However it is
maintained on error processing.

Thanks to Cyril Bonté for reporting the issue early.

src/proto_http.c

index d737cfe543c126aaa18553ddd73ad649842bd948..865eaa494544e5ea6cd61fe4df0a17b6d342b4f5 100644 (file)
@@ -4248,7 +4248,9 @@ int http_request_forward_body(struct session *s, struct buffer *req, int an_bit)
                else {
                        /* other states, DONE...TUNNEL */
                        /* for keep-alive we don't want to forward closes on DONE */
-                       buffer_dont_close(req);
+                       if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
+                           (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL)
+                               buffer_dont_close(req);
                        if (http_resync_states(s)) {
                                /* some state changes occurred, maybe the analyser
                                 * was disabled too.
@@ -4292,6 +4294,12 @@ int http_request_forward_body(struct session *s, struct buffer *req, int an_bit)
                goto return_bad_req;
        }
 
+       /* When TE: chunked is used, we need to get there again to parse remaining
+        * chunks even if the client has closed, so we don't want to set BF_DONTCLOSE.
+        */
+       if (txn->flags & TX_REQ_TE_CHNK)
+               buffer_dont_close(req);
+
        http_silent_debug(__LINE__, s);
        return 0;
 
@@ -5172,7 +5180,9 @@ int http_response_forward_body(struct session *s, struct buffer *res, int an_bit
                else {
                        /* other states, DONE...TUNNEL */
                        /* for keep-alive we don't want to forward closes on DONE */
-                       buffer_dont_close(res);
+                       if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
+                           (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL)
+                               buffer_dont_close(res);
                        if (http_resync_states(s)) {
                                http_silent_debug(__LINE__, s);
                                /* some state changes occurred, maybe the analyser
@@ -5208,6 +5218,16 @@ int http_response_forward_body(struct session *s, struct buffer *res, int an_bit
                msg->som = msg->sov;
        }
 
+       /* When TE: chunked is used, we need to get there again to parse remaining
+        * chunks even if the server has closed, so we don't want to set BF_DONTCLOSE.
+        * Similarly, with keep-alive on the client side, we don't want to forward a
+        * close.
+        */
+       if ((txn->flags & TX_RES_TE_CHNK) ||
+           (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
+           (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL)
+               buffer_dont_close(res);
+
        /* the session handler will take care of timeouts and errors */
        http_silent_debug(__LINE__, s);
        return 0;