fix(client): report one error when both WebRTC race legs fail (#16563)

Both legs of race_transports_prefer_webrtc send their own punch request
to the same rendezvous server for the same peer, so a refusal there
(offline, unknown ID, key mismatch, a server-supplied other_failure)
fails both with the same text. Joining them as "WebRTC failed: X;
fallback failed: X" showed it twice and broke the UI's whole-string
matching: the offline auto-retry needs exactly "Remote desktop is
offline", the message box translates the text as one string, and
check_if_retry hides the retry button for any text containing "failed".

Return the error of the leg that failed last, as select_ok did before
the race existed, and log the other one once.


Claude-Session: https://claude.ai/code/session_01WS1HTjp6UQuG43KEcQPq1R

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
RustDesk
2026-10-15 19:52:19 +08:00
committed by GitHub
co-authored by Claude Opus 5.5
parent f705902ec3
commit c0750fb50c
+28 -2
View File
@@ -267,7 +267,13 @@ async fn race_transports_prefer_webrtc<'a, T: 'a>(
}
}
match others_err.take() {
Some(oe) => bail!("WebRTC failed: {}; fallback failed: {}", e, oe),
// Report one error, as `select_ok` does: the UI matches and translates
// the text whole (e.g. the offline auto-retry), and both legs often
// carry the same rendezvous refusal anyway.
Some(oe) => {
log::warn!("fallback leg also failed: {}", oe);
return Err(e);
}
None if others_fut.is_none() => bail!("WebRTC failed: {}", e),
None => webrtc_err = Some(e),
}
@@ -308,7 +314,10 @@ async fn race_transports_prefer_webrtc<'a, T: 'a>(
// it before failing the connection.
Some(we) => match held.take() {
Some(conn) => return Ok(conn),
None => bail!("WebRTC failed: {}; fallback failed: {}", we, e),
None => {
log::warn!("WebRTC leg also failed: {}", we);
return Err(e);
}
},
None if webrtc_fut.is_none() => match held.take() {
Some(conn) => return Ok(conn),
@@ -5709,6 +5718,23 @@ mod webrtc_race_tests {
assert_eq!(got, "ipv6");
assert!(start.elapsed() < Duration::from_secs(5));
}
// Both legs get the same rendezvous refusal; the UI needs the text verbatim, e.g. the
// offline auto-retry matches it whole. Either leg may fail last.
#[tokio::test]
async fn same_failure_on_both_legs_reported_once() {
for (webrtc_ms, fallback_ms) in [(10, 20), (20, 10)] {
let err = race_transports_prefer_webrtc(
err_after(webrtc_ms, "Remote desktop is offline"),
vec![err_after(fallback_ms, "Remote desktop is offline")],
60_000,
NOT_P2P,
)
.await
.unwrap_err();
assert_eq!(err.to_string(), "Remote desktop is offline");
}
}
}
#[cfg(test)]