diff --git a/src/viewer/mod.rs b/src/viewer/mod.rs index d6bc081..2bc211d 100644 --- a/src/viewer/mod.rs +++ b/src/viewer/mod.rs @@ -50,34 +50,41 @@ pub async fn run(ticket: EndpointTicket, opts: ViewerOpts) -> Result<()> { } }, }; - let (quic_send, quic_recv) = conn.open_bi().await?; + // 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?; - let port = listener.local_addr()?.port(); - let url = format!("http://127.0.0.1:{port}"); - output::emit(output::Event::Connected { url: &url }); + let listener = TcpListener::bind(("127.0.0.1", opts.port)).await?; + let port = listener.local_addr()?.port(); + let url = format!("http://127.0.0.1:{port}"); + output::emit(output::Event::Connected { url: &url }); - if opts.interactive { - let player = crate::interactive::prompt_player()?; - player - .spawn(&url) - .with_context(|| "failed to launch player")?; - print_viewer_banner_interactive(); - } else { - print_viewer_banner(&url); + if opts.interactive { + let player = crate::interactive::prompt_player()?; + player + .spawn(&url) + .with_context(|| "failed to launch player")?; + print_viewer_banner_interactive(); + } else { + print_viewer_banner(&url); + } + + tokio::select! { + accepted = listener.accept() => { + let (tcp, peer) = accepted?; + tracing::info!(%peer, "local viewer connected"); + crate::common::tunnel::bridge(quic_send, quic_recv, tcp).await + } + _ = cancel.cancelled() => { + tracing::info!("ctrl-c received before local viewer connected"); + Ok(()) + } + } } - - let result = tokio::select! { - accepted = listener.accept() => { - let (tcp, peer) = accepted?; - tracing::info!(%peer, "local viewer connected"); - crate::common::tunnel::bridge(quic_send, quic_recv, tcp).await - } - _ = cancel.cancelled() => { - tracing::info!("ctrl-c received before local viewer connected"); - Ok(()) - } - }; + .await; endpoint.close().await; result