Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
feat: add ConnectionInfo
  • Loading branch information
Frando committed Nov 20, 2025
commit abc48c45f0df85edfc2dee5486de399c5bfcdd53
5 changes: 3 additions & 2 deletions iroh/src/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,9 @@ pub use quinn_proto::{

pub use self::connection::{
Accept, Accepting, AlpnError, AuthenticationError, Connecting, ConnectingError, Connection,
ConnectionState, HandshakeCompleted, Incoming, IncomingZeroRtt, IncomingZeroRttConnection,
OutgoingZeroRtt, OutgoingZeroRttConnection, RemoteEndpointIdError, ZeroRttStatus,
ConnectionInfo, ConnectionState, HandshakeCompleted, Incoming, IncomingZeroRtt,
IncomingZeroRttConnection, OutgoingZeroRtt, OutgoingZeroRttConnection, RemoteEndpointIdError,
ZeroRttStatus,
};
pub use crate::magicsock::transports::TransportConfig;

Expand Down
82 changes: 81 additions & 1 deletion iroh/src/endpoint/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use n0_watcher::Watcher;
use pin_project::pin_project;
use quinn::{
AcceptBi, AcceptUni, ConnectionError, ConnectionStats, OpenBi, OpenUni, ReadDatagram,
RetryError, SendDatagramError, ServerConfig, VarInt,
RetryError, SendDatagramError, ServerConfig, Side, VarInt, WeakConnectionHandle,
};
use tracing::warn;

Expand Down Expand Up @@ -977,6 +977,23 @@ impl Connection<HandshakeCompleted> {
pub fn paths(&self) -> impl Watcher<Value = PathInfoList> + Unpin + Send + Sync + 'static {
self.data.paths.clone()
}

/// Returns the side of the connection (client or server).
pub fn side(&self) -> Side {
self.inner.side()
}

/// Returns a connection info struct.
///
/// A [`ConnectionInfo`] is a weak handle to the connection that does not keep the connection alive,
/// but does allow to access some information about the connection and to wait for the connection to be closed.
pub fn to_info(&self) -> ConnectionInfo {
ConnectionInfo {
data: self.data.clone(),
inner: self.inner.weak_handle(),
side: self.side(),
}
}
}

impl Connection<IncomingZeroRtt> {
Expand Down Expand Up @@ -1032,6 +1049,69 @@ impl Connection<OutgoingZeroRtt> {
}
}

/// Information about a connection.
///
/// A [`ConnectionInfo`] is a weak handle to a connection that exposes some information about the connection,
/// but does not keep the connection alive.
#[derive(Debug, Clone)]
pub struct ConnectionInfo {
side: Side,
data: HandshakeCompletedData,
inner: WeakConnectionHandle,
}

#[allow(missing_docs)]
impl ConnectionInfo {
pub fn alpn(&self) -> &[u8] {
&self.data.info.alpn
}

pub fn remote_id(&self) -> &EndpointId {
&self.data.info.endpoint_id
}

pub fn is_alive(&self) -> bool {
self.inner.upgrade().is_some()
}

/// Returns a [`Watcher`] for the network paths of this connection.
///
/// A connection can have several network paths to the remote endpoint, commonly there
/// will be a path via the relay server and a holepunched path.
///
/// The watcher is updated whenever a path is opened or closed, or when the path selected
/// for transmission changes (see [`PathInfo::is_selected`]).
///
/// The [`PathInfoList`] returned from the watcher contains a [`PathInfo`] for each
/// transmission path.
///
/// [`PathInfo::is_selected`]: crate::magicsock::PathInfo::is_selected
/// [`PathInfo`]: crate::magicsock::PathInfo
pub fn paths(&self) -> impl Watcher<Value = PathInfoList> + Unpin + Send + Sync + 'static {
self.data.paths.clone()
}

/// Returns connection statistics.
///
/// Returns `None` if the connection has been dropped.
pub fn stats(&self) -> Option<ConnectionStats> {
self.inner.upgrade().map(|conn| conn.stats())
}

/// Returns the side of the connection (client or server).
pub fn side(&self) -> Side {
self.side
}

/// Waits for the connection to be closed, and returns the close reason and final connection stats.
///
/// Returns `None` if the connection has been dropped already before this call.
pub async fn closed(&self) -> Option<(ConnectionError, ConnectionStats)> {
let fut = self.inner.upgrade()?.on_closed();
Some(fut.await)
}
}

#[cfg(test)]
mod tests {
use std::time::Duration;
Expand Down