-
Notifications
You must be signed in to change notification settings - Fork 314
feat(multipath): add RemoteInfo and ConnectionInfo #3614
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feat-multipath
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,7 +35,7 @@ use n0_watcher::{Watchable, 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 quinn_proto::PathId; | ||
| use tracing::warn; | ||
|
|
@@ -241,15 +241,16 @@ fn conn_from_quinn_conn( | |
| let alpn = alpn_from_quinn_conn(&conn).ok_or_else(|| e!(AuthenticationError::NoAlpn))?; | ||
| let paths_info_watchable = init_paths_info_watcher(&conn, ep); | ||
| let paths_info = paths_info_watchable.watch(); | ||
| // Register this connection with the magicsock. | ||
| ep.msock | ||
| .register_connection(remote_id, &conn, paths_info_watchable.clone()); | ||
| Ok(Connection { | ||
| let conn = Connection { | ||
| remote_id, | ||
| alpn, | ||
| inner: conn, | ||
| paths_info, | ||
| }) | ||
| }; | ||
| let info = conn.to_info(); | ||
| // Register this connection with the magicsock. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can remove this comment now, so we look a bit less like an AI 😅 |
||
| ep.msock.register_connection(info, paths_info_watchable); | ||
| Ok(conn) | ||
| } | ||
|
|
||
| fn init_paths_info_watcher(conn: &quinn::Connection, ep: &Endpoint) -> Watchable<PathsInfo> { | ||
|
|
@@ -1530,6 +1531,83 @@ impl Connection { | |
| pub fn set_max_concurrent_bi_streams(&self, count: VarInt) { | ||
| self.inner.set_max_concurrent_bi_streams(count) | ||
| } | ||
|
|
||
| /// Returns the side of the connection (client or server). | ||
| pub fn side(&self) -> Side { | ||
| self.inner.side() | ||
| } | ||
|
Comment on lines
+1535
to
+1538
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, thanks for adding this! I'd write a lot more docs, like explaining why on the QUIC-level this is a thing and why you might want to know for a protocol despite that iroh always allows you to be both. Basically assume you get a user landing from hn or reddit and exploring iroh for the first time without having a clue. |
||
|
|
||
| /// Returns a [`ConnectionInfo`], which 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 allows to wait for the connection to be closed. | ||
|
Comment on lines
+1540
to
+1542
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am a stickler for following the rust doc style guide strictly :) So re-phrasing this so you have a 1-line summary followed by a blank line and longer explanation will focus you on writing meaningful words in that one line, because that's the line you'll see on rustdoc summary pages and will most often read from ide help. (I'll only leave this comment once)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fully. Haven't done docs properly at all yet, wanna settle the approach first. |
||
| pub fn to_info(&self) -> ConnectionInfo { | ||
| ConnectionInfo { | ||
| alpn: self.alpn.clone(), | ||
| remote_id: self.remote_id, | ||
| inner: self.inner.weak_handle(), | ||
| paths_info: self.paths_info.clone(), | ||
| side: self.side(), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// 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 { | ||
| pub(crate) side: Side, | ||
| pub(crate) alpn: Vec<u8>, | ||
| pub(crate) remote_id: EndpointId, | ||
| pub(crate) inner: WeakConnectionHandle, | ||
| pub(crate) paths_info: n0_watcher::Direct<PathsInfo>, | ||
| } | ||
|
|
||
| #[allow(missing_docs)] | ||
| impl ConnectionInfo { | ||
| pub fn alpn(&self) -> &[u8] { | ||
| &self.alpn | ||
| } | ||
|
|
||
| pub fn remote_id(&self) -> &EndpointId { | ||
| &self.remote_id | ||
| } | ||
|
|
||
| pub fn is_alive(&self) -> bool { | ||
| self.inner.upgrade().is_some() | ||
| } | ||
|
|
||
| /// Returns information about the network paths in use by 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. This returns all the | ||
| /// paths in use by this connection. | ||
| pub fn paths_info(&self) -> &impl Watcher<Value = PathsInfo> { | ||
| &self.paths_info | ||
| } | ||
|
|
||
| // We could add such util methods here, not sure. | ||
| pub fn has_direct_path(&mut self) -> bool { | ||
| self.paths_info.get().keys().any(|addr| addr.is_ip()) | ||
| } | ||
|
Comment on lines
+1588
to
+1591
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think |
||
|
|
||
| /// Current best estimate of this connection's latency (round-trip-time) | ||
| /// | ||
| /// Returns `None` if the connection has been dropped. | ||
| pub fn rtt(&self) -> Option<Duration> { | ||
| self.inner.upgrade().map(|conn| conn.rtt()) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where you are getting this value from is problematic currently. That's a known issue on the multipath branch that this is kinda bogus. Probably should take the rtt of the selected path. If getting that isn't too expensive here. It would be nice to expose probably. |
||
| } | ||
|
|
||
| /// Returns connection statistics. | ||
| /// | ||
| /// Returns `None` if the connection has been dropped. | ||
| pub fn stats(&self) -> Option<ConnectionStats> { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One issue I have with |
||
| self.inner.upgrade().map(|conn| conn.stats()) | ||
| } | ||
|
|
||
| /// Returns the side of the connection (client or server). | ||
| pub fn side(&self) -> Side { | ||
| self.side | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,15 +17,15 @@ use super::{ | |
| mapped_addrs::{AddrMap, EndpointIdMappedAddr, MultipathMappedAddr, RelayMappedAddr}, | ||
| transports::{self, OwnedTransmit, TransportsSender}, | ||
| }; | ||
| use crate::disco::{self}; | ||
| use crate::disco; | ||
| // #[cfg(any(test, feature = "test-utils"))] | ||
| // use crate::endpoint::PathSelection; | ||
|
|
||
| mod endpoint_state; | ||
| mod path_state; | ||
|
|
||
| pub(super) use endpoint_state::EndpointStateMessage; | ||
| pub use endpoint_state::{ConnectionType, PathInfo, PathsInfo}; | ||
| pub use endpoint_state::{ConnectionType, PathInfo, PathsInfo, RemoteInfo}; | ||
| use endpoint_state::{EndpointStateActor, EndpointStateHandle}; | ||
|
|
||
| // TODO: use this | ||
|
|
@@ -122,17 +122,23 @@ impl EndpointMap { | |
| } | ||
| } | ||
|
|
||
| /// Returns a [`n0_watcher::Direct`] for given endpoint's [`ConnectionType`]. | ||
| /// | ||
| /// # Errors | ||
| /// | ||
| /// Will return `None` if there is not an entry in the [`EndpointMap`] for | ||
| /// the `endpoint_id` | ||
| pub(super) fn conn_type( | ||
| &self, | ||
| _endpoint_id: EndpointId, | ||
| ) -> Option<n0_watcher::Direct<ConnectionType>> { | ||
| todo!(); | ||
| pub(crate) fn remote_info(&self, eid: EndpointId) -> Option<RemoteInfo> { | ||
| self.actor_handles | ||
| .lock() | ||
| .expect("poisoned") | ||
| .get(&eid) | ||
| .map(|handle| RemoteInfo::new(eid, handle.sender.clone(), handle.selected_path.watch())) | ||
| } | ||
|
|
||
| pub(crate) fn remotes(&self) -> Vec<RemoteInfo> { | ||
| self.actor_handles | ||
| .lock() | ||
| .expect("poisoned") | ||
| .iter() | ||
| .map(|(eid, handle)| { | ||
| RemoteInfo::new(*eid, handle.sender.clone(), handle.selected_path.watch()) | ||
| }) | ||
| .collect() | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah right. It's the lock inside again that forces you to collect. This is kind of bad because of the many connections things. I can think of wrapping the mutex in an Arc and that would allow you to clone it out and pass it to a custom iterator struct which could then take the lock on each |
||
| } | ||
|
|
||
| /// Returns the sender for the [`EndpointStateActor`]. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd consider making this
impl Iterator, it means we don't have to allocate a vec (even if we currently do internally). E.g. if you have a busy server this could be thousands really.