From: Christopher Faulet Date: Wed, 7 Oct 2026 08:54:53 +0000 (+0200) Subject: BUG/MEDIUM: mux-h1: don't truncate the response when closing after draining X-Git-Tag: v3.5-dev10~156 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=4d1088caa936e6a1808ff822735894693c3aed8f;p=haproxy.git BUG/MEDIUM: mux-h1: don't truncate the response when closing after draining 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. --- diff --git a/src/mux_h1.c b/src/mux_h1.c index 82b29cb48..86ded66a7 100644 --- a/src/mux_h1.c +++ b/src/mux_h1.c @@ -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;