fix(viewer): close the endpoint on all post-connect failure paths

open_bi/bind/local_addr/accept all `?`-propagated straight out of run(),
skipping the endpoint.close().await at the end and leaking the iroh
Endpoint on any post-connect error. Wrap the post-connect body in one
block whose result is captured, then close unconditionally — matching
the explicit-close idiom of the connect-phase select arms.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-05-28 15:24:47 -04:00
parent a740376ea9
commit 14245cbf08
+9 -2
View File
@@ -50,6 +50,11 @@ pub async fn run(ticket: EndpointTicket, opts: ViewerOpts) -> Result<()> {
} }
}, },
}; };
// Everything past the established connection runs in one block so any error
// (open_bi, bind, local_addr, accept) is captured rather than `?`-propagated
// straight out of the function — that would skip the close below and leak the
// endpoint. The connect-phase arms above close explicitly for the same reason.
let result = async {
let (quic_send, quic_recv) = conn.open_bi().await?; let (quic_send, quic_recv) = conn.open_bi().await?;
let listener = TcpListener::bind(("127.0.0.1", opts.port)).await?; let listener = TcpListener::bind(("127.0.0.1", opts.port)).await?;
@@ -67,7 +72,7 @@ pub async fn run(ticket: EndpointTicket, opts: ViewerOpts) -> Result<()> {
print_viewer_banner(&url); print_viewer_banner(&url);
} }
let result = tokio::select! { tokio::select! {
accepted = listener.accept() => { accepted = listener.accept() => {
let (tcp, peer) = accepted?; let (tcp, peer) = accepted?;
tracing::info!(%peer, "local viewer connected"); tracing::info!(%peer, "local viewer connected");
@@ -77,7 +82,9 @@ pub async fn run(ticket: EndpointTicket, opts: ViewerOpts) -> Result<()> {
tracing::info!("ctrl-c received before local viewer connected"); tracing::info!("ctrl-c received before local viewer connected");
Ok(()) Ok(())
} }
}; }
}
.await;
endpoint.close().await; endpoint.close().await;
result result