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
co-authored by Claude Opus 4.8
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 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);
}
let result = tokio::select! {
tokio::select! {
accepted = listener.accept() => {
let (tcp, peer) = accepted?;
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");
Ok(())
}
};
}
}
.await;
endpoint.close().await;
result