diff --git a/src/socketengine.rs b/src/socketengine.rs index 4791258..71f1d91 100644 --- a/src/socketengine.rs +++ b/src/socketengine.rs @@ -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, t: usize, core: fn close_conn(poll: &mut Poll, conns: &mut HashMap, t: usize, core: &Sender) { 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 diff --git a/src/tls.rs b/src/tls.rs index cba8529..de05cf3 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -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); } }