webrtc: report receive progress and ICE's disconnected hint

A peer that is killed, logged out or rebooted sends no reset over UDP, so a
session only learns of it by timing out. Nothing here acts on that; these are
the two observations a caller needs to.

`rx_progress` counts arriving fragments. A message reaches the caller only once
it is whole, so a large one - a clipboard image is the case that occurs - leaves
the receive loop with nothing to observe for as long as the transfer takes, and
a peer sending steadily looks exactly like one that died. A counter, not a
timestamp: `Instant::now()` costs an order of magnitude more per read, and
callers only compare successive samples.

`webrtc_disconnected` exposes the ICE state webrtc-rs already raises ~5s after
it stops hearing from the peer. It is deliberately not a fourth
`WebRTCConnectionState`: that enum gates send and receive readiness and session
cache admissibility, and a transient state must change neither.

TCP and WebSocket answer for neither, on purpose: this change leaves their
existing close, error and timeout behaviour exactly as it was.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019aokqJuhjvB3kijXtAg5Ns
This commit is contained in:
rustdeskandClaude Opus 5 committed 2026-09-08 14:57:09 +08:00
1 parent 55395c6fcb
commit c50c1fef32
4 files changed
+233

No files matched your search

+36
View File
@@ -64,6 +64,42 @@ impl Stream {
}
}
/// An opaque token that changes whenever bytes have arrived from the peer, a fragment of a
/// message that is still incomplete included. Compare successive samples; the value itself
/// means nothing. `None` where the transport reports no such thing.
///
/// A message reaches the caller only once it is whole, so `next()` alone cannot distinguish a
/// peer sending a large one — a clipboard image is the case that occurs — from one that died
/// mid-message. Sampling this alongside the last received message tells them apart.
///
/// TCP and WebSocket answer `None` on purpose: this change leaves their existing close, error
/// and 30s-timeout behaviour exactly as it was, and giving them a value would alter it.
#[inline]
pub fn rx_progress(&self) -> Option<u64> {
match self {
#[cfg(feature = "webrtc")]
Stream::WebRTC(s) => Some(s.rx_progress()),
#[allow(unreachable_patterns)]
_ => None,
}
}
/// Whether ICE has stopped hearing from the peer on a WebRTC transport. Transient and
/// recoverable — it says the peer is worth suspecting, never that the session is over — and
/// always false for transports this change leaves alone.
///
/// Maintained by ICE's own task, so unlike anything derived from the session's read loop it
/// stays accurate while that loop is busy sending.
#[inline]
pub fn webrtc_disconnected(&self) -> bool {
match self {
#[cfg(feature = "webrtc")]
Stream::WebRTC(s) => s.is_disconnected(),
#[allow(unreachable_patterns)]
_ => false,
}
}
#[inline]
pub fn is_secured(&self) -> bool {
match self {