tls: send a close_notify on close for established TLS sessions instead of just dropping the socket, so clients see a clean shutdown not a truncation error

This commit is contained in:
Jean Chevronnet 2026-08-12 15:53:10 +00:00
parent 59e18b0205
commit d8d57511ab
2 changed files with 14 additions and 0 deletions

View file

@ -154,6 +154,13 @@ impl Sock {
Sock::Tls(t) => t.write(buf),
}
}
/// Best-effort graceful close. TLS sends a close_notify alert; plaintext relies on
/// the socket's own FIN when the stream drops.
fn shutdown(&mut self) {
if let Sock::Tls(t) = self {
t.shutdown();
}
}
}
struct Conn {
@ -816,6 +823,10 @@ fn flush_conn(poll: &mut Poll, conns: &mut HashMap<usize, Conn>, t: usize, core:
fn close_conn(poll: &mut Poll, conns: &mut HashMap<usize, Conn>, t: usize, core: &Sender<Event>) {
if let Some(mut c) = conns.remove(&t) {
let _ = poll.registry().deregister(c.sock.source());
// send a TLS close_notify for an established session (not a half-done handshake)
if !c.handshaking {
c.sock.shutdown();
}
let uid = c.uid;
// a conn whose Connect was never emitted — a still-pending PROXY header or an
// unfinished TLS handshake — must not send the core a Disconnect for a uid it

View file

@ -143,6 +143,9 @@ impl TlsSession for OpensslSession {
Some(digest.iter().map(|b| format!("{b:02x}")).collect())
}
fn shutdown(&mut self) {
// best-effort TLS close_notify, then close the socket. Non-blocking, so a
// WouldBlock just means the alert is queued — we don't wait for the peer's.
let _ = self.0.shutdown();
let _ = self.0.get_ref().shutdown(Shutdown::Both);
}
}