]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MEDIUM: mux-h1: don't truncate the response when closing after draining
authorChristopher Faulet <cfaulet@haproxy.com>
Wed, 7 Oct 2026 08:54:53 +0000 (10:54 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Fri, 16 Oct 2026 17:10:26 +0000 (19:10 +0200)
When the response is finished while the request is not, the H1 stream is
detached and the H1 connection switches to the DRAINING state to consume
the end of the request before processing the next one. When the end of
the request is received and the connection must not be kept alive (e.g.
"Connection: close" or HTTP/1.0 request), h1_process() shuts the
connection down for writes and immediately releases it, without checking
whether the end of the response is still pending in the output buffer.
Thus any response data still waiting to be sent are lost and the client
receives a truncated response.

This happens with responses produced while the request payload is still
being uploaded (http-request deny/return/redirect/auth, or a server
responding before reading the request payload) when the client does not
read the response while uploading. For instance, with a 120kB response
returned by "http-request return" (tune.bufsize 131072), a client sending
a 1MB POST request with "Connection: close" and a small receive buffer
gets only the first 12kB of the response once its upload is finished
(the "H1C released with pending output data" debug counter is
incremented). It works in keep-alive mode because h1s_finish_detach() is
used and takes care of the output buffer.

Now, in this case, the H1 connection is switched to the closing state
and the H1 stream is destroyed. Then, the connection is only shut down
and released if there are no more pending output data. Otherwise, the
shutdown is performed by h1_send() once all data are sent, as for the
other paths.

This was introduced by commit 077906da1 ("MAJOR: mux-h1: Drain requests
on client side before shut a stream down") in 3.0. It must be backported
as far as 3.0.

src/mux_h1.c

index 82b29cb480d8644ad42c612507433b35aa8a53cb..86ded66a768f8fb2a67d1c18a03c32dfd7389e1b 100644 (file)
@@ -4375,8 +4375,19 @@ static int h1_process(struct h1c * h1c)
                BUG_ON(!h1c->h1s);
                if (se_fl_test(h1c->h1s->sd, SE_FL_EOI)) {
                        if (h1s_must_shut_conn(h1c->h1s)) {
-                               h1_shutw_conn(conn);
-                               goto release;
+                               /* The H1 stream is no longer needed but the
+                                * end of the response may still be pending
+                                * in the output buffer. In this case, wait
+                                * for it to be sent, h1_send() will perform
+                                * the shutdown.
+                                */
+                               h1_close(h1c);
+                               h1s_destroy(h1c->h1s);
+                               if (!b_data(&h1c->obuf)) {
+                                       h1_shutw_conn(conn);
+                                       goto release;
+                               }
+                               goto end;
                        }
                        if (h1s_finish_detach(h1c->h1s) == -1)
                                goto released;