From a30082eebeaf5c4c16fc55a883e5262c49a8571f Mon Sep 17 00:00:00 2001 From: Liam Warfield Date: Mon, 19 Jan 2026 19:17:23 -0700 Subject: [PATCH] Fix Proxy slow disconnect. When `select!` drops a JoinHandle, it doesn't abort the spawned task - it detaches it. From tokio docs: A JoinHandle detaches the associated task when it is dropped, which means that there is no longer any handle to the task, and no way to join on it. This means the spawn task is still spinning in the background: 1. c2s completes (client disconnected) 2. select! drops the s2c JoinHandle 3. The s2c task continues running in the background, detached 4. That task holds the Mumble server connection open The fix is to hand the async calls directly to select! instead of calling tokio::spawn. --- proxy/src/main.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/proxy/src/main.rs b/proxy/src/main.rs index 90def5e..53f69db 100644 --- a/proxy/src/main.rs +++ b/proxy/src/main.rs @@ -335,19 +335,13 @@ async fn connect_proxy_impl( info!("connected to Mumble server"); - // Spawn tasks to handle transmitting data between the WebTransport client and Mumble TCP Server - let c2s = tokio::spawn( - pass_bytes_loop(incoming, write_server) - .instrument(info_span!("Handler", "Client to server")), - ); - let s2c = tokio::spawn( - pass_bytes_loop(read_server, outgoing) - .instrument(info_span!("Handler", "Server to client")), - ); - + // Handle transmitting data between the WebTransport client and Mumble TCP Server + // When one direction completes/fails, the other is dropped and its streams are closed tokio::select! { - res = c2s => res??, - res = s2c => res??, + res = pass_bytes_loop(incoming, write_server) + .instrument(info_span!("Handler", "Client to server")) => res?, + res = pass_bytes_loop(read_server, outgoing) + .instrument(info_span!("Handler", "Server to client")) => res?, }; Ok(()) }