From: "Michael Köppl" <m.koeppl@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH http-server 1/1] apiserver: send TLS close notify at the end of streamed responses
Date: Tue, 3 Nov 2026 16:52:23 +0100 [thread overview]
Message-ID: <20261103155223.1960754-1-m.koeppl@proxmox.com> (raw)
A streamed response (e.g. for the single file restore download), has no
Content-Length nor chunked encoding, so the client can only tell where
the body ends by the connection being closed. The response accounced
'Connection: Keep-Alive' and, at the end of the stream, push_shutdown()
shut down the socket directly, bypassing the TLS layer. The client then
receives a TCP FIN without a TLS close notify [0].
Without a close notify, a client cannot tell a complete body from a
truncated one. OpenSSL 3 reports this as an unexpected EOF, which makes
curl fail with error 56 even though all data arrived. Even though
browsers usually accept this, this affects users who might use this for
scripted downloads from endpoints such as the file-restore one.
Do not keep streamed response alive. finish_response() then disconnects
the client once all data is written and, since commit 07e56cc ("fix
unexpected EOF for client when closing TLS session"),
client_do_disconnect() sends a close notify before shutting down the
socket. Drop the push_shutdown() call, as it would shut down the socket
before that if the write buffer is already empty.
[0] https://www.rfc-editor.org/info/rfc9846/#name-closure-alerts
Fixes: 389ad88 ("support streaming data form fh to client")
Signed-off-by: Michael Köppl <m.koeppl@proxmox.com>
---
Ran into this while writing an E2E test that downloads a single file
from a PBS backup through the file-restore API endpoint with curl,
which failed with exit code 56 even though the file was complete.
I tested this manually with curl, as well as the GUI file restore. On a
3-node cluster, downloaded a single file from a backup on node 2
proxied through node 1, without and with this patch applied. Node 1
receives the stream completely and without error in both cases.
src/PVE/APIServer/AnyEvent.pm | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/PVE/APIServer/AnyEvent.pm b/src/PVE/APIServer/AnyEvent.pm
index 449b1c0..9258b6e 100644
--- a/src/PVE/APIServer/AnyEvent.pm
+++ b/src/PVE/APIServer/AnyEvent.pm
@@ -274,7 +274,6 @@ sub response_stream {
# write out any remaining data
$reqhdl->push_write($hdl->{rbuf}) if length($hdl->{rbuf}) > 0;
$hdl->{rbuf} = "";
- $reqhdl->push_shutdown();
$self->finish_response($reqstate);
}
};
@@ -366,6 +365,8 @@ sub response {
$resp->remove_header("Content-Length");
}
+ $reqstate->{keep_alive} = 0 if $stream_fh;
+
if ($reqstate->{keep_alive} > 0) {
$resp->push_header('Connection' => 'Keep-Alive');
} else {
--
2.47.3
next reply other threads:[~2026-11-03 15:52 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-03 15:52 Michael Köppl [this message]
2026-11-03 16:19 ` [PATCH http-server 1/1] apiserver: send TLS close notify at the end of streamed responses Michael Köppl
2026-11-05 17:50 ` applied: " Thomas Lamprecht
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261103155223.1960754-1-m.koeppl@proxmox.com \
--to=m.koeppl@proxmox.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.