network: per-peer connection badge (direct/relay, RTT, loss, bitrate)
Answer "am I actually P2P right now?" per peer. A 1 Hz session task snapshots the selected QUIC path of every live audio connection (IrohTransport::connection_stats), core::connstats::derive turns consecutive snapshots into RTT/loss/bitrate (path switches and counter resets invalidate the rate window), and the peer card shows a Direct/Relay badge with a hover tooltip for address, loss, and up/down bitrate. No new dependencies, no wire change. Loopback-integration-tested against real iroh endpoints; not yet field-verified on a 2-machine call (FEATURES.md row marked 🧪). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -689,6 +689,57 @@ impl IrohTransport {
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
/// Snapshot the selected QUIC path of every live audio connection, for the
|
||||
/// UI's per-peer connection badge (direct/relay, RTT, loss, bitrate).
|
||||
/// Cheap and lock-light: the `live_conns` guard is released before touching
|
||||
/// any connection, and `Connection::paths()` reads shared state without I/O.
|
||||
pub fn connection_stats(&self) -> Vec<(EndpointId, crate::network::PathSnapshot)> {
|
||||
// Clone the connections out so the map lock isn't held while we inspect
|
||||
// paths (a supervisor inserts/removes entries as links come and go).
|
||||
let conns: Vec<(EndpointId, Connection)> = self
|
||||
.shared
|
||||
.live_conns
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|(id, conn)| (*id, conn.clone()))
|
||||
.collect();
|
||||
conns
|
||||
.into_iter()
|
||||
.filter_map(|(id, conn)| {
|
||||
let paths = conn.paths();
|
||||
// The selected path is the one carrying application data. In the
|
||||
// brief window where none is flagged (e.g. mid-migration), fall
|
||||
// back to the first open path rather than dropping the badge.
|
||||
let path = paths
|
||||
.iter()
|
||||
.find(|p| p.is_selected())
|
||||
.or_else(|| paths.iter().next())?;
|
||||
let stats = path.stats();
|
||||
// Per-variant display: `TransportAddr`'s own `Display` prefixes
|
||||
// a scheme ("ip:1.2.3.4:5") that's noise next to the badge's
|
||||
// Direct/Relay label.
|
||||
let remote_addr = match path.remote_addr() {
|
||||
iroh::TransportAddr::Ip(sock) => sock.to_string(),
|
||||
iroh::TransportAddr::Relay(url) => url.to_string(),
|
||||
other => other.to_string(),
|
||||
};
|
||||
Some((
|
||||
id,
|
||||
crate::network::PathSnapshot {
|
||||
is_relay: path.remote_addr().is_relay(),
|
||||
remote_addr,
|
||||
rtt: stats.rtt,
|
||||
tx_bytes: stats.udp_tx.bytes,
|
||||
rx_bytes: stats.udp_rx.bytes,
|
||||
tx_datagrams: stats.udp_tx.datagrams,
|
||||
lost_packets: stats.lost_packets,
|
||||
},
|
||||
))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Fetch a chat attachment's bytes from its sender over the file plane.
|
||||
pub async fn fetch_attachment(
|
||||
&self,
|
||||
|
||||
Reference in New Issue
Block a user