diff --git a/libs/hbb_common b/libs/hbb_common index 6677318bd..73007cb38 160000 --- a/libs/hbb_common +++ b/libs/hbb_common @@ -1 +1 @@ -Subproject commit 6677318bd2d9db9873bbb960a6edb8ec664e5df7 +Subproject commit 73007cb38e1fd688543d14b591ab788e0a6384a3 diff --git a/src/client.rs b/src/client.rs index 024674337..ba5d59904 100644 --- a/src/client.rs +++ b/src/client.rs @@ -202,7 +202,7 @@ impl OffererGuard { impl Drop for OffererGuard { fn drop(&mut self) { if let Some(stream) = self.0.take() { - Client::spawn_close_webrtc(stream); + stream.close_detached(); } } } @@ -581,34 +581,6 @@ impl Client { !session_key.is_empty() && ice.session_key == session_key && !ice.candidate.is_empty() } - /// Tear down an abandoned WebRTC offerer off the connection hot path. - /// - /// The pc must be closed explicitly (a dropped handle leaves the `SESSIONS` clone alive), - /// but `close()` awaits internal ICE/DTLS shutdown and the result is pure cleanup with no - /// downstream dependency, so it must not block session establishment. - fn spawn_close_webrtc(webrtc: WebRTCStream) { - // Use the current runtime handle explicitly: this is also called from OffererGuard::drop, - // which can run during runtime teardown where a bare tokio::spawn would panic. If no - // runtime is available the pc cannot be closed here (it is released at process exit). - // Handle::spawn can also panic when the runtime is shutting down; catch that so Drop - // never panics (prefer a brief leak until process exit over aborting the process). - match tokio::runtime::Handle::try_current() { - Ok(handle) => { - let spawn_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - handle.spawn(async move { - webrtc.close().await; - }); - })); - if spawn_result.is_err() { - log::warn!("failed to spawn WebRTC close (runtime shutting down)"); - } - } - Err(_) => { - log::warn!("no tokio runtime available to close WebRTC peer connection"); - } - } - } - /// Whether to build a WebRTC offerer for this connection. /// /// Off by default against a private rendezvous server, like the UDP/IPv6 punch options: @@ -1180,7 +1152,10 @@ impl Client { let direct = match typ { "IPv6" => true, // WebRTC through a TURN server is relayed traffic; report it as such. - "WebRTC" => !conn.webrtc_relayed().await.unwrap_or(false), + // An unknown answer (no selected pair yet, or the pc closed under a + // concurrent teardown) must not be read as "direct": claiming a P2P + // path needs evidence of one. + "WebRTC" => !conn.webrtc_relayed().await.unwrap_or(true), _ => false, }; // Secured and WebRTC won: disarm so the returned conn keeps the pc alive. @@ -1276,7 +1251,7 @@ impl Client { // Bailing before connect(): an offerer already adopted into webrtc_for_connect was // disarmed out of its guard, so close it (and stop its bridge) explicitly here. if let Some(webrtc) = webrtc_for_connect.take() { - Self::spawn_close_webrtc(webrtc); + webrtc.close_detached(); } if let Some(stop) = webrtc_bridge_stop.take() { let _ = stop.send(()); @@ -1536,8 +1511,9 @@ impl Client { }; if typ == "WebRTC" { // WebRTC through a TURN server (force_relay, or a TURN pair winning under All - // policy) is relayed traffic; report the direct flag accordingly. - if conn.webrtc_relayed().await.unwrap_or(false) { + // policy) is relayed traffic; report the direct flag accordingly. An unknown answer + // counts as relayed — claiming a P2P path needs evidence of one. + if conn.webrtc_relayed().await.unwrap_or(true) { direct = false; } // Secured: disarm so the returned conn keeps the pc alive. diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 83a5656e4..411c3ea9b 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -198,7 +198,7 @@ impl Remote { self.handle_disconnected(round); // Close the WebRTC pc on this decline path too (no-op for TCP/WS); otherwise its // pc lingers in the global session cache until ICE decays on its own. - peer.close_webrtc().await; + peer.close_webrtc(); return; } self.handler.update_direct(Some(direct)); @@ -348,7 +348,7 @@ impl Remote { // Close the WebRTC peer connection (if this session used it) so its pc is not left // lingering in the global session cache after the session ends; dropping `peer` // alone does not release it. No-op for TCP/WebSocket transports. - peer.close_webrtc().await; + peer.close_webrtc(); // Stop client audio server. if let Some(s) = self.stop_voice_call_sender.take() { s.send(()).ok(); diff --git a/src/port_forward.rs b/src/port_forward.rs index f772ccdcd..41e309278 100644 --- a/src/port_forward.rs +++ b/src/port_forward.rs @@ -158,7 +158,7 @@ async fn connect_and_login( *close_port_forward = true; // Close the WebRTC pc on this decline path too (no-op for TCP/WS), matching every // other exit in this function; a bare drop leaks it in the global session cache. - stream.close_webrtc().await; + stream.close_webrtc(); return Ok(None); } } @@ -173,7 +173,7 @@ async fn connect_and_login( tokio::select! { res = timeout(READ_TIMEOUT, stream.next()) => match res { Err(_) => { - stream.close_webrtc().await; + stream.close_webrtc(); bail!("Timeout"); } Ok(Some(Ok(bytes))) => { @@ -184,7 +184,7 @@ async fn connect_and_login( let msg_in = match Message::parse_from_bytes(&bytes) { Ok(msg) => msg, Err(err) => { - stream.close_webrtc().await; + stream.close_webrtc(); return Err(err.into()); } }; @@ -198,7 +198,7 @@ async fn connect_and_login( Some(message::Union::LoginResponse(lr)) => match lr.union { Some(login_response::Union::Error(err)) => { if !interface.handle_login_error(&err) { - stream.close_webrtc().await; + stream.close_webrtc(); return Ok(None); } } @@ -215,11 +215,11 @@ async fn connect_and_login( } } Ok(Some(Err(err))) => { - stream.close_webrtc().await; + stream.close_webrtc(); bail!("Connection closed: {}", err); } _ => { - stream.close_webrtc().await; + stream.close_webrtc(); bail!("Reset by the peer"); } }, @@ -239,7 +239,7 @@ async fn connect_and_login( if let Some(Ok(bytes)) = res { buffer.extend(bytes); } else { - stream.close_webrtc().await; + stream.close_webrtc(); return Ok(None); } }, @@ -344,7 +344,7 @@ async fn run_forward(forward: Framed, stream: Stream) -> }, } } - stream.close_webrtc().await; + stream.close_webrtc(); Ok(()) }