feat(client): report relay RTT diagnostics

Expose the active QUIC connection RTT in shared client diagnostics and in the
Windows client status line. This gives operators a live relay-path latency
signal without pretending to measure end-to-end gateway or LAN latency.

The new diagnostics field defaults to unknown when older JSON snapshots omit it,
so consumers can read pre-change snapshots without special migration code. The
user-facing diagnostics now print Relay RTT only when the client has an active
QUIC measurement.

Test Plan:
- cargo fmt --check
- git diff --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings

Refs: PLAN.md logging and diagnostics section
This commit is contained in:
2026-05-21 23:00:18 +02:00
parent 77025e6564
commit e69d41691a
4 changed files with 70 additions and 15 deletions
+13 -1
View File
@@ -265,8 +265,9 @@ impl ClientSession {
}
#[must_use]
pub const fn quic_diagnostics(&self) -> QuicDiagnostics {
pub fn quic_diagnostics(&self) -> QuicDiagnostics {
QuicDiagnostics::new(true, Some(self.quic_max_datagram_size))
.with_relay_rtt_ms(Some(duration_millis_saturating(self.connection.rtt())))
}
#[must_use]
@@ -568,6 +569,10 @@ fn client_bind_addr(relay_addr: SocketAddr) -> SocketAddr {
}
}
fn duration_millis_saturating(duration: Duration) -> u64 {
u64::try_from(duration.as_millis()).unwrap_or(u64::MAX)
}
async fn request_control_message(
connection: &quinn::Connection,
message: ControlMessage,
@@ -844,6 +849,7 @@ mod tests {
client.quic_diagnostics().max_datagram_size(),
Some(client.quic_max_datagram_size())
);
assert!(client.quic_diagnostics().relay_rtt_ms().is_some());
let relay_io = client.relay_io();
assert_eq!(relay_io.welcome().peer_id(), 2);
assert_eq!(
@@ -933,6 +939,12 @@ mod tests {
assert_eq!(snapshot.malformed_frames(), 1);
}
#[test]
fn converts_relay_rtt_to_milliseconds() {
assert_eq!(duration_millis_saturating(Duration::from_micros(999)), 0);
assert_eq!(duration_millis_saturating(Duration::from_millis(23)), 23);
}
fn test_server_config() -> (ServerConfig, CertificateDer<'static>) {
let certified_key =
rcgen::generate_simple_self_signed(vec!["lanparty-relay.local".into()]).unwrap();