From 75292bb6b2bbfca9f358d5a00e85cbff4098f11f Mon Sep 17 00:00:00 2001 From: Max Inden Date: Mon, 30 Aug 2021 18:03:08 +0200 Subject: [PATCH 01/67] *: Implement concurrent dialing --- core/src/connection/error.rs | 12 +- core/src/connection/pool.rs | 17 +- core/src/network.rs | 251 +++++++++++----------------- core/src/network/concurrent_dial.rs | 105 ++++++++++++ core/src/network/event.rs | 24 +-- core/src/network/peer.rs | 44 ++--- core/tests/connection_limits.rs | 8 +- core/tests/network_dial_error.rs | 42 ++--- swarm/src/behaviour.rs | 24 +-- swarm/src/lib.rs | 10 +- swarm/src/test.rs | 13 -- swarm/src/toggle.rs | 11 -- 12 files changed, 282 insertions(+), 279 deletions(-) create mode 100644 core/src/network/concurrent_dial.rs diff --git a/core/src/connection/error.rs b/core/src/connection/error.rs index ec4f7ff6e61..89d863a5b7f 100644 --- a/core/src/connection/error.rs +++ b/core/src/connection/error.rs @@ -69,7 +69,10 @@ where #[derive(Debug)] pub enum PendingConnectionError { /// An error occurred while negotiating the transport protocol(s). - Transport(TransportError), + // + // TODO: Using a Vec here is not ideal. On an incoming listen connection only a single error can + // occur. + Transport(Vec>), /// Pending connection attempt has been aborted. Aborted, @@ -92,7 +95,9 @@ where PendingConnectionError::IO(err) => write!(f, "Pending connection: I/O error: {}", err), PendingConnectionError::Aborted => write!(f, "Pending connection: Aborted."), PendingConnectionError::Transport(err) => { - write!(f, "Pending connection: Transport error: {}", err) + // TODO: Resurect + // write!(f, "Pending connection: Transport error: {}", err) + Ok(()) } PendingConnectionError::InvalidPeerId => { write!(f, "Pending connection: Invalid peer ID.") @@ -108,7 +113,8 @@ where fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { PendingConnectionError::IO(err) => Some(err), - PendingConnectionError::Transport(err) => Some(err), + // TODO: Should we return the first of all transport errors? Or implement std::error::Error for Vec? + PendingConnectionError::Transport(err) => None, PendingConnectionError::InvalidPeerId => None, PendingConnectionError::Aborted => None, } diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index 11861600c21..e82224f1bd5 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -27,7 +27,7 @@ use crate::{ }, muxing::StreamMuxer, network::DialError, - ConnectedPoint, PeerId, + ConnectedPoint, Multiaddr, PeerId, }; use either::Either; use fnv::FnvHashMap; @@ -225,6 +225,9 @@ impl Pool { TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send + 'static, { + // TODO: This is a hack. Fix. + let send_back_addr = info.send_back_addr.clone(); + let future = future.map_ok(|(peer_id, muxer)| (peer_id, send_back_addr, muxer)); self.counters.check_max_pending_incoming()?; let endpoint = info.to_connected_point(); Ok(self.add_pending(future, handler, endpoint, None)) @@ -239,10 +242,10 @@ impl Pool { &mut self, future: TFut, handler: THandler, - info: OutgoingInfo<'_>, + expected_peer_id: Option, ) -> Result> where - TFut: Future>> + TFut: Future>> + Send + 'static, THandler: IntoConnectionHandler + Send + 'static, @@ -255,8 +258,8 @@ impl Pool { if let Err(limit) = self.counters.check_max_pending_outgoing() { return Err(DialError::ConnectionLimit { limit, handler }); }; - let endpoint = info.to_connected_point(); - Ok(self.add_pending(future, handler, endpoint, info.peer_id.cloned())) + let endpoint = ConnectedPoint::Dialer { address: todo!() }; + Ok(self.add_pending(future, handler, endpoint, expected_peer_id)) } /// Adds a pending connection to the pool in the form of a @@ -269,7 +272,7 @@ impl Pool { peer: Option, ) -> ConnectionId where - TFut: Future>> + TFut: Future>> + Send + 'static, THandler: IntoConnectionHandler + Send + 'static, @@ -287,7 +290,7 @@ impl Pool { let endpoint = endpoint.clone(); let expected_peer = peer; let local_id = self.local_id; - move |(peer_id, muxer)| { + move |(peer_id, addr, muxer)| { if let Some(peer) = expected_peer { if peer != peer_id { return future::err(PendingConnectionError::InvalidPeerId); diff --git a/core/src/network.rs b/core/src/network.rs index 831a99c4b01..a0f74525708 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -18,11 +18,12 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +mod concurrent_dial; mod event; pub mod peer; pub use crate::connection::{ConnectionCounters, ConnectionLimits}; -pub use event::{DialAttemptsRemaining, IncomingConnection, NetworkEvent}; +pub use event::{IncomingConnection, NetworkEvent}; pub use peer::Peer; use crate::{ @@ -77,7 +78,7 @@ where /// > the same connection ID. This is ensured by the implementation of /// > `Network` (see `dial_peer_impl` and `on_connection_failed`) /// > together with the implementation of `DialingAttempt::abort`. - dialing: FnvHashMap>, + dialing: FnvHashMap>, } impl fmt::Debug for Network @@ -115,7 +116,7 @@ where impl Network where - TTrans: Transport + Clone, + TTrans: Transport + Clone + 'static, TMuxer: StreamMuxer, THandler: IntoConnectionHandler + Send + 'static, ::OutboundOpenInfo: Send, @@ -218,30 +219,39 @@ where if let Ok(peer) = PeerId::try_from(ma) { return self.dial_peer(DialingOpts { peer, - address: address.clone(), + addresses: vec![address.clone()], handler, - remaining: Vec::new(), }); } } - // The address does not specify an expected peer, so just try to dial it as-is, - // accepting any peer ID that the remote identifies as. - let info = OutgoingInfo { - address, - peer_id: None, - }; - match self.transport().clone().dial(address.clone()) { - Ok(f) => { - let f = - f.map_err(|err| PendingConnectionError::Transport(TransportError::Other(err))); - self.pool.add_outgoing(f, handler, info) - } - Err(err) => { - let f = future::err(PendingConnectionError::Transport(err)); - self.pool.add_outgoing(f, handler, info) - } - } + // TODO: Clone needed? + let fut = concurrent_dial::ConcurrentDial::new( + self.transport().clone(), + None, + vec![address.clone()], + ) + .map_err(|e| { + PendingConnectionError::Transport( + e.into_iter().map(|e| TransportError::Other(e)).collect(), + ) + }); + + self.pool.add_outgoing(fut, handler, None) + + // match self.transport().clone().dial(address.clone()) { + // Ok(f) => { + // let address = address.clone(); + // let f = f + // .map_err(|err| PendingConnectionError::Transport(TransportError::Other(err))) + // .map_ok(|(peer_id, muxer)| (peer_id, address, muxer)); + // self.pool.add_outgoing(f, handler, None) + // } + // Err(err) => { + // let f = future::err(PendingConnectionError::Transport(err)); + // self.pool.add_outgoing(f, handler, None) + // } + // } } /// Returns information about the state of the `Network`. @@ -322,7 +332,7 @@ where { let upgrade = connection .upgrade - .map_err(|err| PendingConnectionError::Transport(TransportError::Other(err))); + .map_err(|err| PendingConnectionError::Transport(vec![TransportError::Other(err)])); let info = IncomingInfo { local_addr: &connection.local_addr, send_back_addr: &connection.send_back_addr, @@ -407,7 +417,7 @@ where num_established, }) => { if let hash_map::Entry::Occupied(mut e) = self.dialing.entry(connection.peer_id()) { - e.get_mut().retain(|s| s.current.0 != connection.id()); + e.get_mut().retain(|s| *s != connection.id()); if e.get().is_empty() { e.remove(); } @@ -427,14 +437,7 @@ where .. }) => { let dialing = &mut self.dialing; - let (next, event) = on_connection_failed(dialing, id, endpoint, error, handler); - if let Some(dial) = next { - let transport = self.listeners.transport().clone(); - if let Err(e) = dial_peer_impl(transport, pool, dialing, dial) { - log::warn!("Dialing aborted: {:?}", e); - } - } - event + on_connection_failed(dialing, id, endpoint, error, handler) } Poll::Ready(PoolEvent::ConnectionClosed { id, @@ -493,15 +496,16 @@ where struct DialingOpts { peer: PeerId, handler: THandler, - address: Multiaddr, - remaining: Vec, + addresses: Vec, } /// Standalone implementation of `Network::dial_peer` for more granular borrowing. +// +// TODO: How about making this a method? fn dial_peer_impl( transport: TTrans, pool: &mut Pool, - dialing: &mut FnvHashMap>, + dialing: &mut FnvHashMap>, opts: DialingOpts, ) -> Result> where @@ -509,52 +513,55 @@ where ::Error: error::Error + Send + 'static, ::OutboundOpenInfo: Send + 'static, THandler::Handler: ConnectionHandler> + Send + 'static, - TTrans: Transport, + TTrans: Transport + Clone + 'static, TTrans::Dial: Send + 'static, TTrans::Error: error::Error + Send + 'static, TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send + 'static, { - // Ensure the address to dial encapsulates the `p2p` protocol for the - // targeted peer, so that the transport has a "fully qualified" address - // to work with. - let addr = match p2p_addr(opts.peer, opts.address) { - Ok(address) => address, - Err(address) => { - return Err(DialError::InvalidAddress { - address, - handler: opts.handler, - }) - } - }; - - let result = match transport.dial(addr.clone()) { - Ok(fut) => { - let fut = fut.map_err(|e| PendingConnectionError::Transport(TransportError::Other(e))); - let info = OutgoingInfo { - address: &addr, - peer_id: Some(&opts.peer), - }; - pool.add_outgoing(fut, opts.handler, info) - } - Err(err) => { - let fut = future::err(PendingConnectionError::Transport(err)); - let info = OutgoingInfo { - address: &addr, - peer_id: Some(&opts.peer), - }; - pool.add_outgoing(fut, opts.handler, info) - } - }; + // // Ensure the address to dial encapsulates the `p2p` protocol for the + // // targeted peer, so that the transport has a "fully qualified" address + // // to work with. + // let addr = match p2p_addr(opts.peer, opts.address) { + // Ok(address) => address, + // Err(address) => { + // return Err(DialError::InvalidAddress { + // address, + // handler: opts.handler, + // }) + // } + // }; + + let fut = concurrent_dial::ConcurrentDial::new(transport, Some(opts.peer), opts.addresses) + .map_err(|e| { + PendingConnectionError::Transport( + e.into_iter().map(|e| TransportError::Other(e)).collect(), + ) + }); + + let result = pool.add_outgoing(fut, opts.handler, Some(opts.peer)); + + // let result = match transport.dial(addr.clone()) { + // Ok(fut) => { + // let fut = fut.map_err(|e| PendingConnectionError::Transport(TransportError::Other(e))); + // let info = OutgoingInfo { + // address: &addr, + // peer_id: Some(&opts.peer), + // }; + // pool.add_outgoing(fut, opts.handler, info) + // } + // Err(err) => { + // let fut = future::err(PendingConnectionError::Transport(err)); + // let info = OutgoingInfo { + // address: &addr, + // peer_id: Some(&opts.peer), + // }; + // pool.add_outgoing(fut, opts.handler, info) + // } + // }; if let Ok(id) = &result { - dialing - .entry(opts.peer) - .or_default() - .push(peer::DialingState { - current: (*id, addr), - remaining: opts.remaining, - }); + dialing.entry(opts.peer).or_default().push(*id); } result @@ -566,83 +573,48 @@ where /// If the failed connection attempt was a dialing attempt and there /// are more addresses to try, new `DialingOpts` are returned. fn on_connection_failed<'a, TTrans, THandler>( - dialing: &mut FnvHashMap>, + dialing: &mut FnvHashMap>, id: ConnectionId, endpoint: ConnectedPoint, error: PendingConnectionError, handler: THandler, -) -> ( - Option>, - NetworkEvent<'a, TTrans, THandlerInEvent, THandlerOutEvent, THandler>, -) +) -> NetworkEvent<'a, TTrans, THandlerInEvent, THandlerOutEvent, THandler> where TTrans: Transport, THandler: IntoConnectionHandler, { // Check if the failed connection is associated with a dialing attempt. let dialing_failed = dialing.iter_mut().find_map(|(peer, attempts)| { - if let Some(pos) = attempts.iter().position(|s| s.current.0 == id) { - let attempt = attempts.remove(pos); - let last = attempts.is_empty(); - Some((*peer, attempt, last)) + if let Some(pos) = attempts.iter().position(|s| *s == id) { + Some(*peer) } else { None } }); - if let Some((peer_id, mut attempt, last)) = dialing_failed { - if last { - dialing.remove(&peer_id); + if let Some(peer_id) = dialing_failed { + NetworkEvent::DialError { + handler, + peer_id, + error, } - - let num_remain = u32::try_from(attempt.remaining.len()).unwrap(); - let failed_addr = attempt.current.1.clone(); - - let (opts, attempts_remaining) = if let Some(num_remain) = NonZeroU32::new(num_remain) { - let next_attempt = attempt.remaining.remove(0); - let opts = DialingOpts { - peer: peer_id, - handler, - address: next_attempt, - remaining: attempt.remaining, - }; - (Some(opts), DialAttemptsRemaining::Some(num_remain)) - } else { - (None, DialAttemptsRemaining::None(handler)) - }; - - ( - opts, - NetworkEvent::DialError { - attempts_remaining, - peer_id, - multiaddr: failed_addr, - error, - }, - ) } else { // A pending incoming connection or outgoing connection to an unknown peer failed. match endpoint { - ConnectedPoint::Dialer { address } => ( - None, - NetworkEvent::UnknownPeerDialError { - multiaddr: address, - error, - handler, - }, - ), + ConnectedPoint::Dialer { address } => NetworkEvent::UnknownPeerDialError { + multiaddr: address, + error, + handler, + }, ConnectedPoint::Listener { local_addr, send_back_addr, - } => ( - None, - NetworkEvent::IncomingConnectionError { - local_addr, - send_back_addr, - error, - handler, - }, - ), + } => NetworkEvent::IncomingConnectionError { + local_addr, + send_back_addr, + error, + handler, + }, } } } @@ -731,27 +703,6 @@ impl NetworkConfig { } } -/// Ensures a given `Multiaddr` is a `/p2p/...` address for the given peer. -/// -/// If the given address is already a `p2p` address for the given peer, -/// i.e. the last encapsulated protocol is `/p2p/`, this is a no-op. -/// -/// If the given address is already a `p2p` address for a different peer -/// than the one given, the given `Multiaddr` is returned as an `Err`. -/// -/// If the given address is not yet a `p2p` address for the given peer, -/// the `/p2p/` protocol is appended to the returned address. -fn p2p_addr(peer: PeerId, addr: Multiaddr) -> Result { - if let Some(multiaddr::Protocol::P2p(hash)) = addr.iter().last() { - if &hash != peer.as_ref() { - return Err(addr); - } - Ok(addr) - } else { - Ok(addr.with(multiaddr::Protocol::P2p(peer.into()))) - } -} - /// Possible (synchronous) errors when dialing a peer. #[derive(Clone)] pub enum DialError { diff --git a/core/src/network/concurrent_dial.rs b/core/src/network/concurrent_dial.rs new file mode 100644 index 00000000000..71e2eafd0b0 --- /dev/null +++ b/core/src/network/concurrent_dial.rs @@ -0,0 +1,105 @@ +pub use crate::connection::{ConnectionCounters, ConnectionLimits}; + +use crate::{ + muxing::StreamMuxer, + transport::{Transport, TransportError}, + ConnectedPoint, Executor, Multiaddr, PeerId, +}; +use fnv::FnvHashMap; +use futures::future::BoxFuture; +use futures::ready; +use futures::stream::FuturesUnordered; +use futures::{future, prelude::*}; +use smallvec::SmallVec; +use std::{ + collections::hash_map, + convert::TryFrom as _, + error, fmt, + num::{NonZeroU32, NonZeroUsize}, + pin::Pin, + task::{Context, Poll}, +}; + +pub(crate) struct ConcurrentDial { + dials: FuturesUnordered>>, + errors: Vec, +} + +impl Unpin for ConcurrentDial {} + +impl ConcurrentDial { + pub(crate) fn new( + transport: TTrans, + peer: Option, + addresses: Vec, + ) -> Self + where + TTrans: Transport + Clone, + TTrans::Dial: Send + 'static, + TError: std::fmt::Debug, + { + Self { + dials: addresses + .into_iter() + .map(|a| p2p_addr(peer, a).unwrap()) + .map(|a| { + let a_clone = a.clone(); + transport + .clone() + // TODO: address could as well be derived from transport. + .map(|(peer_id, muxer), _| (peer_id, a_clone, muxer)) + .dial(a) + .unwrap() + .boxed() + }) + .collect(), + errors: vec![], + } + } +} + +impl Future for ConcurrentDial { + type Output = Result<(PeerId, Multiaddr, TMuxer), Vec>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + loop { + match ready!(self.dials.poll_next_unpin(cx)) { + Some(Ok(output)) => return Poll::Ready(Ok(output)), + Some(Err(e)) => { + self.errors.push(e); + if self.dials.is_empty() { + return Poll::Ready(Err(std::mem::replace(&mut self.errors, vec![]))); + } + } + None => todo!(), + } + } + } +} + +/// Ensures a given `Multiaddr` is a `/p2p/...` address for the given peer. +/// +/// If the given address is already a `p2p` address for the given peer, +/// i.e. the last encapsulated protocol is `/p2p/`, this is a no-op. +/// +/// If the given address is already a `p2p` address for a different peer +/// than the one given, the given `Multiaddr` is returned as an `Err`. +/// +/// If the given address is not yet a `p2p` address for the given peer, +/// the `/p2p/` protocol is appended to the returned address. +fn p2p_addr(peer: Option, addr: Multiaddr) -> Result { + // TODO: Make hack nicer. + let peer = match peer { + Some(p) => p, + None => return Ok(addr), + }; + + if let Some(multiaddr::Protocol::P2p(hash)) = addr.iter().last() { + if &hash != peer.as_ref() { + return Err(addr); + } + Ok(addr) + } else { + Ok(addr.with(multiaddr::Protocol::P2p(peer.into()))) + } +} diff --git a/core/src/network/event.rs b/core/src/network/event.rs index cea5bbddc21..5c0a578ed0f 100644 --- a/core/src/network/event.rs +++ b/core/src/network/event.rs @@ -131,14 +131,11 @@ where /// A dialing attempt to an address of a peer failed. DialError { /// The number of remaining dialing attempts. - attempts_remaining: DialAttemptsRemaining, + handler: THandler, /// Id of the peer we were trying to dial. peer_id: PeerId, - /// The multiaddr we failed to reach. - multiaddr: Multiaddr, - /// The error that happened. error: PendingConnectionError, }, @@ -173,20 +170,6 @@ where }, } -pub enum DialAttemptsRemaining { - Some(NonZeroU32), - None(THandler), -} - -impl DialAttemptsRemaining { - pub fn get_attempts(&self) -> u32 { - match self { - DialAttemptsRemaining::Some(attempts) => (*attempts).into(), - DialAttemptsRemaining::None(_) => 0, - } - } -} - impl fmt::Debug for NetworkEvent<'_, TTrans, TInEvent, TOutEvent, THandler> where @@ -262,15 +245,12 @@ where .field("error", error) .finish(), NetworkEvent::DialError { - attempts_remaining, + handler: _, peer_id, - multiaddr, error, } => f .debug_struct("DialError") - .field("attempts_remaining", &attempts_remaining.get_attempts()) .field("peer_id", peer_id) - .field("multiaddr", multiaddr) .field("error", error) .finish(), NetworkEvent::UnknownPeerDialError { diff --git a/core/src/network/peer.rs b/core/src/network/peer.rs index 1eda5dde9e0..be9b5bd69f7 100644 --- a/core/src/network/peer.rs +++ b/core/src/network/peer.rs @@ -110,7 +110,7 @@ where impl<'a, TTrans, TMuxer, THandler> Peer<'a, TTrans, THandler> where - TTrans: Transport + Clone, + TTrans: Transport + Clone + 'static, TTrans::Error: Send + 'static, TTrans::Dial: Send + 'static, TMuxer: StreamMuxer + Send + Sync + 'static, @@ -160,8 +160,7 @@ where /// attempt to the first address fails. pub fn dial( self, - address: Multiaddr, - remaining: I, + addresses: I, handler: THandler, ) -> Result<(ConnectionId, DialingPeer<'a, TTrans, THandler>), DialError> where @@ -177,8 +176,8 @@ where let id = network.dial_peer(DialingOpts { peer: peer_id, handler, - address, - remaining: remaining.into_iter().collect(), + // TODO: Should network.dial_peer take an iterator as well? + addresses: addresses.into_iter().collect(), })?; Ok((id, DialingPeer { network, peer_id })) @@ -376,12 +375,14 @@ where /// Obtains a dialing attempt to the peer by connection ID of /// the current connection attempt. + // + // TODO: Still needed? pub fn attempt( &mut self, id: ConnectionId, ) -> Option>> { if let hash_map::Entry::Occupied(attempts) = self.network.dialing.entry(self.peer_id) { - if let Some(pos) = attempts.get().iter().position(|s| s.current.0 == id) { + if let Some(pos) = attempts.get().iter().position(|s| *s == id) { if let Some(inner) = self.network.pool.get_outgoing(id) { return Some(DialingAttempt { pos, @@ -469,16 +470,6 @@ where } } -/// The (internal) state of a `DialingAttempt`, tracking the -/// current connection attempt as well as remaining addresses. -#[derive(Debug, Clone)] -pub(super) struct DialingState { - /// The ID and (remote) address of the current connection attempt. - pub(super) current: (ConnectionId, Multiaddr), - /// Multiaddresses to attempt if the current one fails. - pub(super) remaining: Vec, -} - /// A `DialingAttempt` is an ongoing outgoing connection attempt to /// a known / expected remote peer ID and a list of alternative addresses /// to connect to, if the current connection attempt fails. @@ -486,8 +477,8 @@ pub struct DialingAttempt<'a, TInEvent> { /// The underlying pending connection in the `Pool`. inner: PendingConnection<'a, TInEvent>, /// All current dialing attempts of the peer. - attempts: hash_map::OccupiedEntry<'a, PeerId, SmallVec<[DialingState; 10]>>, - /// The position of the current `DialingState` of this connection in the `attempts`. + attempts: hash_map::OccupiedEntry<'a, PeerId, SmallVec<[ConnectionId; 10]>>, + /// The position of the current `ConnectionId` of this connection in the `attempts`. pos: usize, } @@ -522,15 +513,6 @@ impl<'a, TInEvent> DialingAttempt<'a, TInEvent> { } self.inner.abort(); } - - /// Adds an address to the end of the remaining addresses - /// for this dialing attempt. Duplicates are ignored. - pub fn add_address(&mut self, addr: Multiaddr) { - let remaining = &mut self.attempts.get_mut()[self.pos].remaining; - if remaining.iter().all(|a| a != &addr) { - remaining.push(addr); - } - } } /// An iterator over the ongoing dialing attempts to a peer. @@ -544,7 +526,7 @@ pub struct DialingAttemptIter<'a, THandler: IntoConnectionHandler, TTransErr> { /// Ownership of the `OccupiedEntry` for `peer_id` containing all attempts must be /// borrowed to each `DialingAttempt` in order for it to remove the entry if the /// last dialing attempt is aborted. - dialing: &'a mut FnvHashMap>, + dialing: &'a mut FnvHashMap>, /// The current position of the iterator in `dialing[peer_id]`. pos: usize, /// The total number of elements in `dialing[peer_id]` to iterate over. @@ -558,7 +540,7 @@ impl<'a, THandler: IntoConnectionHandler, TTransErr> DialingAttemptIter<'a, THan fn new( peer_id: &'a PeerId, pool: &'a mut Pool, - dialing: &'a mut FnvHashMap>, + dialing: &'a mut FnvHashMap>, ) -> Self { let end = dialing.get(peer_id).map_or(0, |conns| conns.len()); Self { @@ -590,7 +572,7 @@ impl<'a, THandler: IntoConnectionHandler, TTransErr> DialingAttemptIter<'a, THan } if let hash_map::Entry::Occupied(attempts) = self.dialing.entry(*self.peer_id) { - let id = attempts.get()[self.pos].current.0; + let id = attempts.get()[self.pos]; if let Some(inner) = self.pool.get_outgoing(id) { let conn = DialingAttempt { pos: self.pos, @@ -615,7 +597,7 @@ impl<'a, THandler: IntoConnectionHandler, TTransErr> DialingAttemptIter<'a, THan } if let hash_map::Entry::Occupied(attempts) = self.dialing.entry(*self.peer_id) { - let id = attempts.get()[self.pos].current.0; + let id = attempts.get()[self.pos]; if let Some(inner) = self.pool.get_outgoing(id) { return Some(DialingAttempt { pos: self.pos, diff --git a/core/tests/connection_limits.rs b/core/tests/connection_limits.rs index d5156664ffb..481b6019819 100644 --- a/core/tests/connection_limits.rs +++ b/core/tests/connection_limits.rs @@ -21,7 +21,7 @@ mod util; use futures::{future::poll_fn, ready}; -use libp2p_core::multiaddr::{multiaddr, Multiaddr}; +use libp2p_core::multiaddr::{multiaddr, Multiaddr, Protocol}; use libp2p_core::{ connection::ConnectionError, network::{ConnectionLimits, DialError, NetworkConfig, NetworkEvent}, @@ -39,18 +39,20 @@ fn max_outgoing() { let cfg = NetworkConfig::default().with_connection_limits(limits); let mut network = test_network(cfg); + let addr: Multiaddr = "/ip6/::1/tcp/1234".parse().unwrap(); + let target = PeerId::random(); for _ in 0..outgoing_limit { network .peer(target.clone()) - .dial(Multiaddr::empty(), Vec::new(), TestHandler()) + .dial(vec![addr.clone()], TestHandler()) .ok() .expect("Unexpected connection limit."); } match network .peer(target.clone()) - .dial(Multiaddr::empty(), Vec::new(), TestHandler()) + .dial(vec![addr.clone()], TestHandler()) .expect_err("Unexpected dialing success.") { DialError::ConnectionLimit { limit, handler: _ } => { diff --git a/core/tests/network_dial_error.rs b/core/tests/network_dial_error.rs index bed4c06e023..87d84f52eae 100644 --- a/core/tests/network_dial_error.rs +++ b/core/tests/network_dial_error.rs @@ -53,7 +53,7 @@ fn deny_incoming_connec() { swarm2 .peer(swarm1.local_peer_id().clone()) - .dial(address.clone(), Vec::new(), TestHandler()) + .dial(vec![address.clone()], TestHandler()) .unwrap(); async_std::task::block_on(future::poll_fn(|cx| -> Poll> { @@ -65,17 +65,16 @@ fn deny_incoming_connec() { match swarm2.poll(cx) { Poll::Ready(NetworkEvent::DialError { - attempts_remaining, peer_id, - multiaddr, error: PendingConnectionError::Transport(_), + handler: _, }) => { - assert_eq!(0u32, attempts_remaining.get_attempts()); assert_eq!(&peer_id, swarm1.local_peer_id()); - assert_eq!( - multiaddr, - address.clone().with(Protocol::P2p(peer_id.into())) - ); + // TODO: Bring back. + // assert_eq!( + // multiaddr, + // address.clone().with(Protocol::P2p(peer_id.into())) + // ); return Poll::Ready(Ok(())); } Poll::Ready(_) => unreachable!(), @@ -162,6 +161,7 @@ fn dial_self_by_id() { assert!(swarm.peer(peer_id).into_disconnected().is_none()); } +#[ignore] // TODO: Resurect #[test] fn multiple_addresses_err() { // Tries dialing multiple addresses, and makes sure there's one dialing error per address. @@ -179,34 +179,28 @@ fn multiple_addresses_err() { } addresses.shuffle(&mut rand::thread_rng()); - let first = addresses[0].clone(); - let rest = (&addresses[1..]).iter().cloned(); - swarm .peer(target.clone()) - .dial(first, rest, TestHandler()) + .dial(addresses.clone(), TestHandler()) .unwrap(); async_std::task::block_on(future::poll_fn(|cx| -> Poll> { loop { match swarm.poll(cx) { Poll::Ready(NetworkEvent::DialError { - attempts_remaining, peer_id, - multiaddr, + // multiaddr, error: PendingConnectionError::Transport(_), + handler: _, }) => { assert_eq!(peer_id, target); - let expected = addresses - .remove(0) - .with(Protocol::P2p(target.clone().into())); - assert_eq!(multiaddr, expected); - if addresses.is_empty() { - assert_eq!(attempts_remaining.get_attempts(), 0); - return Poll::Ready(Ok(())); - } else { - assert_eq!(attempts_remaining.get_attempts(), addresses.len() as u32); - } + // TODO: Bring back. + // let expected = addresses + // .remove(0) + // .with(Protocol::P2p(target.clone().into())); + // assert_eq!(multiaddr, expected); + + return Poll::Ready(Ok(())); } Poll::Ready(_) => unreachable!(), Poll::Pending => break Poll::Pending, diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index 085439aa1b1..b938ba85730 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -151,17 +151,19 @@ pub trait NetworkBehaviour: Send + 'static { event: <::Handler as ProtocolsHandler>::OutEvent, ); - /// Indicates to the behaviour that we tried to reach an address, but failed. - /// - /// If we were trying to reach a specific node, its ID is passed as parameter. If this is the - /// last address to attempt for the given node, then `inject_dial_failure` is called afterwards. - fn inject_addr_reach_failure( - &mut self, - _peer_id: Option<&PeerId>, - _addr: &Multiaddr, - _error: &dyn error::Error, - ) { - } + // TODO: Remove + // + // /// Indicates to the behaviour that we tried to reach an address, but failed. + // /// + // /// If we were trying to reach a specific node, its ID is passed as parameter. If this is the + // /// last address to attempt for the given node, then `inject_dial_failure` is called afterwards. + // fn inject_addr_reach_failure( + // &mut self, + // _peer_id: Option<&PeerId>, + // _addr: &Multiaddr, + // _error: &dyn error::Error, + // ) { + // } /// Indicates to the behaviour that we tried to dial all the addresses known for a node, but /// failed. diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index dcccd46af79..43a66c1ba2d 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -714,8 +714,9 @@ where error, attempts_remaining, }) => { - this.behaviour - .inject_addr_reach_failure(Some(&peer_id), &multiaddr, &error); + // TODO: Remove + // this.behaviour + // .inject_addr_reach_failure(Some(&peer_id), &multiaddr, &error); let num_remaining: u32; match attempts_remaining { @@ -752,8 +753,9 @@ where multiaddr, error ); - this.behaviour - .inject_addr_reach_failure(None, &multiaddr, &error); + // TODO: Remove + // this.behaviour + // .inject_addr_reach_failure(None, &multiaddr, &error); return Poll::Ready(SwarmEvent::UnknownPeerUnreachableAddr { address: multiaddr, error, diff --git a/swarm/src/test.rs b/swarm/src/test.rs index 457701899a8..c0b4a29257b 100644 --- a/swarm/src/test.rs +++ b/swarm/src/test.rs @@ -108,7 +108,6 @@ where ConnectionId, <::Handler as ProtocolsHandler>::OutEvent, )>, - pub inject_addr_reach_failure: Vec<(Option, Multiaddr)>, pub inject_dial_failure: Vec, pub inject_new_listener: Vec, pub inject_new_listen_addr: Vec<(ListenerId, Multiaddr)>, @@ -133,7 +132,6 @@ where inject_connection_established: Vec::new(), inject_connection_closed: Vec::new(), inject_event: Vec::new(), - inject_addr_reach_failure: Vec::new(), inject_dial_failure: Vec::new(), inject_new_listener: Vec::new(), inject_new_listen_addr: Vec::new(), @@ -153,7 +151,6 @@ where self.inject_connection_established = Vec::new(); self.inject_connection_closed = Vec::new(); self.inject_event = Vec::new(); - self.inject_addr_reach_failure = Vec::new(); self.inject_dial_failure = Vec::new(); self.inject_new_listen_addr = Vec::new(); self.inject_new_external_addr = Vec::new(); @@ -224,16 +221,6 @@ where self.inner.inject_event(p, c, e); } - fn inject_addr_reach_failure( - &mut self, - p: Option<&PeerId>, - a: &Multiaddr, - e: &dyn std::error::Error, - ) { - self.inject_addr_reach_failure.push((p.cloned(), a.clone())); - self.inner.inject_addr_reach_failure(p, a, e); - } - fn inject_dial_failure( &mut self, p: &PeerId, diff --git a/swarm/src/toggle.rs b/swarm/src/toggle.rs index 575d4e46809..815046b5599 100644 --- a/swarm/src/toggle.rs +++ b/swarm/src/toggle.rs @@ -146,17 +146,6 @@ where } } - fn inject_addr_reach_failure( - &mut self, - peer_id: Option<&PeerId>, - addr: &Multiaddr, - error: &dyn error::Error, - ) { - if let Some(inner) = self.inner.as_mut() { - inner.inject_addr_reach_failure(peer_id, addr, error) - } - } - fn inject_dial_failure( &mut self, peer_id: &PeerId, From 10d941bf162a6605dc6863fe573b5e4e31896f24 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Mon, 6 Sep 2021 16:55:42 +0200 Subject: [PATCH 02/67] core/: Track pending connection via Endpoint --- core/src/connection/pool.rs | 134 ++++++++++++++++--------------- core/src/network.rs | 58 ++++++------- core/src/network/event.rs | 24 +++--- core/src/network/peer.rs | 14 ++-- core/tests/network_dial_error.rs | 8 +- 5 files changed, 118 insertions(+), 120 deletions(-) diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index e82224f1bd5..5f0bb2f80ab 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -22,8 +22,8 @@ use crate::{ connection::{ handler::{THandlerError, THandlerInEvent, THandlerOutEvent}, manager::{self, Manager, ManagerConfig}, - Connected, ConnectionError, ConnectionHandler, ConnectionId, ConnectionLimit, IncomingInfo, - IntoConnectionHandler, OutgoingInfo, PendingConnectionError, Substream, + Connected, ConnectionError, ConnectionHandler, ConnectionId, ConnectionLimit, Endpoint, + IncomingInfo, IntoConnectionHandler, OutgoingInfo, PendingConnectionError, Substream, }, muxing::StreamMuxer, network::DialError, @@ -53,7 +53,7 @@ pub struct Pool { established: FnvHashMap>, /// The pending connections that are currently being negotiated. - pending: FnvHashMap)>, + pending: FnvHashMap)>, } impl fmt::Debug for Pool { @@ -104,7 +104,7 @@ pub enum PoolEvent<'a, THandler: IntoConnectionHandler, TTransErr> { /// The ID of the failed connection. id: ConnectionId, /// The local endpoint of the failed connection. - endpoint: ConnectedPoint, + endpoint: Endpoint, /// The error that occurred. error: PendingConnectionError, /// The handler that was supposed to handle the connection, @@ -226,11 +226,11 @@ impl Pool { TMuxer::OutboundSubstream: Send + 'static, { // TODO: This is a hack. Fix. - let send_back_addr = info.send_back_addr.clone(); - let future = future.map_ok(|(peer_id, muxer)| (peer_id, send_back_addr, muxer)); - self.counters.check_max_pending_incoming()?; let endpoint = info.to_connected_point(); - Ok(self.add_pending(future, handler, endpoint, None)) + let future = future.map_ok(|(peer_id, muxer)| (peer_id, endpoint, muxer)); + // TODO: We loose the handler here. + self.counters.check_max_pending_incoming()?; + Ok(self.add_pending(future, handler, Endpoint::Listener, None)) } /// Adds a pending outgoing connection to the pool in the form of a `Future` @@ -245,8 +245,12 @@ impl Pool { expected_peer_id: Option, ) -> Result> where - TFut: Future>> - + Send + TFut: Future< + Output = Result< + (PeerId, ConnectedPoint, TMuxer), + PendingConnectionError, + >, + > + Send + 'static, THandler: IntoConnectionHandler + Send + 'static, THandler::Handler: ConnectionHandler> + Send + 'static, @@ -258,8 +262,7 @@ impl Pool { if let Err(limit) = self.counters.check_max_pending_outgoing() { return Err(DialError::ConnectionLimit { limit, handler }); }; - let endpoint = ConnectedPoint::Dialer { address: todo!() }; - Ok(self.add_pending(future, handler, endpoint, expected_peer_id)) + Ok(self.add_pending(future, handler, Endpoint::Dialer, expected_peer_id)) } /// Adds a pending connection to the pool in the form of a @@ -268,12 +271,16 @@ impl Pool { &mut self, future: TFut, handler: THandler, - endpoint: ConnectedPoint, + endpoint: Endpoint, peer: Option, ) -> ConnectionId where - TFut: Future>> - + Send + TFut: Future< + Output = Result< + (PeerId, ConnectedPoint, TMuxer), + PendingConnectionError, + >, + > + Send + 'static, THandler: IntoConnectionHandler + Send + 'static, THandler::Handler: ConnectionHandler> + Send + 'static, @@ -287,10 +294,9 @@ impl Pool { // by the background task, which happens when this future resolves to an // "established" connection. let future = future.and_then({ - let endpoint = endpoint.clone(); let expected_peer = peer; let local_id = self.local_id; - move |(peer_id, addr, muxer)| { + move |(peer_id, endpoint, muxer)| { if let Some(peer) = expected_peer { if peer != peer_id { return future::err(PendingConnectionError::InvalidPeerId); @@ -307,7 +313,7 @@ impl Pool { }); let id = self.manager.add_pending(future, handler); - self.counters.inc_pending(&endpoint); + self.counters.inc_pending(endpoint); self.pending.insert(id, (endpoint, peer)); id } @@ -351,7 +357,7 @@ impl Pool { id: ConnectionId, ) -> Option>> { match self.pending.get(&id) { - Some((ConnectedPoint::Dialer { .. }, _peer)) => match self.manager.entry(id) { + Some((Endpoint::Dialer, _peer)) => match self.manager.entry(id) { Some(manager::Entry::Pending(entry)) => Some(PendingConnection { entry, pending: &mut self.pending, @@ -419,32 +425,32 @@ impl Pool { EstablishedConnectionIter { pool: self, ids } } - /// Returns an iterator for information on all pending incoming connections. - pub fn iter_pending_incoming(&self) -> impl Iterator> { - self.iter_pending_info() - .filter_map(|(_, ref endpoint, _)| match endpoint { - ConnectedPoint::Listener { - local_addr, - send_back_addr, - } => Some(IncomingInfo { - local_addr, - send_back_addr, - }), - ConnectedPoint::Dialer { .. } => None, - }) - } - - /// Returns an iterator for information on all pending outgoing connections. - pub fn iter_pending_outgoing(&self) -> impl Iterator> { - self.iter_pending_info() - .filter_map(|(_, ref endpoint, ref peer_id)| match endpoint { - ConnectedPoint::Listener { .. } => None, - ConnectedPoint::Dialer { address } => Some(OutgoingInfo { - address, - peer_id: peer_id.as_ref(), - }), - }) - } + // /// Returns an iterator for information on all pending incoming connections. + // pub fn iter_pending_incoming(&self) -> impl Iterator> { + // self.iter_pending_info() + // .filter_map(|(_, ref endpoint, _)| match endpoint { + // ConnectedPoint::Listener { + // local_addr, + // send_back_addr, + // } => Some(IncomingInfo { + // local_addr, + // send_back_addr, + // }), + // ConnectedPoint::Dialer { .. } => None, + // }) + // } + + // /// Returns an iterator for information on all pending outgoing connections. + // pub fn iter_pending_outgoing(&self) -> impl Iterator> { + // self.iter_pending_info() + // .filter_map(|(_, ref endpoint, ref peer_id)| match endpoint { + // ConnectedPoint::Listener { .. } => None, + // ConnectedPoint::Dialer { address } => Some(OutgoingInfo { + // address, + // peer_id: peer_id.as_ref(), + // }), + // }) + // } /// Returns an iterator over all connection IDs and associated endpoints /// of established connections to `peer` known to the pool. @@ -462,10 +468,10 @@ impl Pool { /// with associated endpoints and expected peer IDs in the pool. pub fn iter_pending_info( &self, - ) -> impl Iterator)> + '_ { + ) -> impl Iterator)> + '_ { self.pending .iter() - .map(|(id, (endpoint, info))| (id, endpoint, info)) + .map(|(id, (endpoint, info))| (id, *endpoint, info)) } /// Returns an iterator over all connected peers, i.e. those that have @@ -492,7 +498,7 @@ impl Pool { match item { manager::Event::PendingConnectionError { id, error, handler } => { if let Some((endpoint, peer)) = self.pending.remove(&id) { - self.counters.dec_pending(&endpoint); + self.counters.dec_pending(endpoint); return Poll::Ready(PoolEvent::PendingConnectionError { id, endpoint, @@ -533,10 +539,10 @@ impl Pool { manager::Event::ConnectionEstablished { entry } => { let id = entry.id(); if let Some((endpoint, peer)) = self.pending.remove(&id) { - self.counters.dec_pending(&endpoint); + self.counters.dec_pending(endpoint); // Check general established connection limit. - if let Err(e) = self.counters.check_max_established(&endpoint) { + if let Err(e) = self.counters.check_max_established(endpoint) { entry.start_close(Some(e)); continue; } @@ -567,6 +573,7 @@ impl Pool { let num_established = NonZeroU32::new(u32::try_from(conns.len() + 1).unwrap()) .expect("n + 1 is always non-zero; qed"); + let endpoint = entry.connected().endpoint.clone(); self.counters.inc_established(&endpoint); conns.insert(id, endpoint); match self.get(id) { @@ -631,7 +638,7 @@ pub enum PoolConnection<'a, TInEvent> { /// A pending connection in a pool. pub struct PendingConnection<'a, TInEvent> { entry: manager::PendingEntry<'a, TInEvent>, - pending: &'a mut FnvHashMap)>, + pending: &'a mut FnvHashMap)>, counters: &'a mut ConnectionCounters, } @@ -651,9 +658,8 @@ impl PendingConnection<'_, TInEvent> { } /// Returns information about this endpoint of the connection. - pub fn endpoint(&self) -> &ConnectedPoint { - &self - .pending + pub fn endpoint(&self) -> Endpoint { + self.pending .get(&self.entry.id()) .expect("`entry` is a pending entry") .0 @@ -666,7 +672,7 @@ impl PendingConnection<'_, TInEvent> { .remove(&self.entry.id()) .expect("`entry` is a pending entry") .0; - self.counters.dec_pending(&endpoint); + self.counters.dec_pending(endpoint); self.entry.abort(); } } @@ -861,23 +867,23 @@ impl ConnectionCounters { self.established_outgoing + self.established_incoming } - fn inc_pending(&mut self, endpoint: &ConnectedPoint) { + fn inc_pending(&mut self, endpoint: Endpoint) { match endpoint { - ConnectedPoint::Dialer { .. } => { + Endpoint::Dialer => { self.pending_outgoing += 1; } - ConnectedPoint::Listener { .. } => { + Endpoint::Listener => { self.pending_incoming += 1; } } } - fn dec_pending(&mut self, endpoint: &ConnectedPoint) { + fn dec_pending(&mut self, endpoint: Endpoint) { match endpoint { - ConnectedPoint::Dialer { .. } => { + Endpoint::Dialer => { self.pending_outgoing -= 1; } - ConnectedPoint::Listener { .. } => { + Endpoint::Listener => { self.pending_incoming -= 1; } } @@ -913,16 +919,16 @@ impl ConnectionCounters { Self::check(self.pending_incoming, self.limits.max_pending_incoming) } - fn check_max_established(&self, endpoint: &ConnectedPoint) -> Result<(), ConnectionLimit> { + fn check_max_established(&self, endpoint: Endpoint) -> Result<(), ConnectionLimit> { // Check total connection limit. Self::check(self.num_established(), self.limits.max_established_total)?; // Check incoming/outgoing connection limits match endpoint { - ConnectedPoint::Dialer { .. } => Self::check( + Endpoint::Dialer => Self::check( self.established_outgoing, self.limits.max_established_outgoing, ), - ConnectedPoint::Listener { .. } => Self::check( + Endpoint::Listener => Self::check( self.established_incoming, self.limits.max_established_incoming, ), diff --git a/core/src/network.rs b/core/src/network.rs index a0f74525708..e87a541487c 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -31,9 +31,9 @@ use crate::{ handler::{THandlerInEvent, THandlerOutEvent}, manager::ManagerConfig, pool::{Pool, PoolEvent}, - ConnectionHandler, ConnectionId, ConnectionLimit, IncomingInfo, IntoConnectionHandler, - ListenerId, ListenersEvent, ListenersStream, OutgoingInfo, PendingConnectionError, - Substream, + ConnectionHandler, ConnectionId, ConnectionLimit, Endpoint, IncomingInfo, + IntoConnectionHandler, ListenerId, ListenersEvent, ListenersStream, OutgoingInfo, + PendingConnectionError, Substream, }, muxing::StreamMuxer, transport::{Transport, TransportError}, @@ -235,7 +235,8 @@ where PendingConnectionError::Transport( e.into_iter().map(|e| TransportError::Other(e)).collect(), ) - }); + }) + .map_ok(|(peer_id, address, muxer)| (peer_id, ConnectedPoint::Dialer { address }, muxer)); self.pool.add_outgoing(fut, handler, None) @@ -264,21 +265,21 @@ where } } - /// Returns an iterator for information on all pending incoming connections. - pub fn incoming_info(&self) -> impl Iterator> { - self.pool.iter_pending_incoming() - } + // /// Returns an iterator for information on all pending incoming connections. + // pub fn incoming_info(&self) -> impl Iterator> { + // self.pool.iter_pending_incoming() + // } - /// Returns the list of addresses we're currently dialing without knowing the `PeerId` of. - pub fn unknown_dials(&self) -> impl Iterator { - self.pool.iter_pending_outgoing().filter_map(|info| { - if info.peer_id.is_none() { - Some(info.address) - } else { - None - } - }) - } + // /// Returns the list of addresses we're currently dialing without knowing the `PeerId` of. + // pub fn unknown_dials(&self) -> impl Iterator { + // self.pool.iter_pending_outgoing().filter_map(|info| { + // if info.peer_id.is_none() { + // Some(info.address) + // } else { + // None + // } + // }) + // } /// Returns a list of all connected peers, i.e. peers to whom the `Network` /// has at least one established connection. @@ -537,7 +538,8 @@ where PendingConnectionError::Transport( e.into_iter().map(|e| TransportError::Other(e)).collect(), ) - }); + }) + .map_ok(|(peer_id, address, muxer)| (peer_id, ConnectedPoint::Dialer { address }, muxer)); let result = pool.add_outgoing(fut, opts.handler, Some(opts.peer)); @@ -575,7 +577,7 @@ where fn on_connection_failed<'a, TTrans, THandler>( dialing: &mut FnvHashMap>, id: ConnectionId, - endpoint: ConnectedPoint, + endpoint: Endpoint, error: PendingConnectionError, handler: THandler, ) -> NetworkEvent<'a, TTrans, THandlerInEvent, THandlerOutEvent, THandler> @@ -601,20 +603,8 @@ where } else { // A pending incoming connection or outgoing connection to an unknown peer failed. match endpoint { - ConnectedPoint::Dialer { address } => NetworkEvent::UnknownPeerDialError { - multiaddr: address, - error, - handler, - }, - ConnectedPoint::Listener { - local_addr, - send_back_addr, - } => NetworkEvent::IncomingConnectionError { - local_addr, - send_back_addr, - error, - handler, - }, + Endpoint::Dialer => NetworkEvent::UnknownPeerDialError { error, handler }, + Endpoint::Listener => NetworkEvent::IncomingConnectionError { error, handler }, } } } diff --git a/core/src/network/event.rs b/core/src/network/event.rs index 5c0a578ed0f..188af1d42a6 100644 --- a/core/src/network/event.rs +++ b/core/src/network/event.rs @@ -86,10 +86,10 @@ where /// This can include, for example, an error during the handshake of the encryption layer, or /// the connection unexpectedly closed. IncomingConnectionError { - /// Local connection address. - local_addr: Multiaddr, - /// Address used to send back data to the remote. - send_back_addr: Multiaddr, + // /// Local connection address. + // local_addr: Multiaddr, + // /// Address used to send back data to the remote. + // send_back_addr: Multiaddr, /// The error that happened. error: PendingConnectionError, handler: THandler, @@ -143,7 +143,7 @@ where /// Failed to reach a peer that we were trying to dial. UnknownPeerDialError { /// The multiaddr we failed to reach. - multiaddr: Multiaddr, + // multiaddr: Multiaddr, /// The error that happened. error: PendingConnectionError, @@ -219,14 +219,14 @@ where .field("send_back_addr", &connection.send_back_addr) .finish(), NetworkEvent::IncomingConnectionError { - local_addr, - send_back_addr, + // local_addr, + // send_back_addr, error, handler: _, } => f .debug_struct("IncomingConnectionError") - .field("local_addr", local_addr) - .field("send_back_addr", send_back_addr) + // .field("local_addr", local_addr) + // .field("send_back_addr", send_back_addr) .field("error", error) .finish(), NetworkEvent::ConnectionEstablished { connection, .. } => f @@ -254,10 +254,12 @@ where .field("error", error) .finish(), NetworkEvent::UnknownPeerDialError { - multiaddr, error, .. + // multiaddr, + error, + .. } => f .debug_struct("UnknownPeerDialError") - .field("multiaddr", multiaddr) + // .field("multiaddr", multiaddr) .field("error", error) .finish(), NetworkEvent::ConnectionEvent { connection, event } => f diff --git a/core/src/network/peer.rs b/core/src/network/peer.rs index be9b5bd69f7..ec064df0e4b 100644 --- a/core/src/network/peer.rs +++ b/core/src/network/peer.rs @@ -493,13 +493,13 @@ impl<'a, TInEvent> DialingAttempt<'a, TInEvent> { self.attempts.key() } - /// Returns the remote address of the current connection attempt. - pub fn address(&self) -> &Multiaddr { - match self.inner.endpoint() { - ConnectedPoint::Dialer { address } => address, - ConnectedPoint::Listener { .. } => unreachable!("by definition of a `DialingAttempt`."), - } - } + // /// Returns the remote address of the current connection attempt. + // pub fn address(&self) -> &Multiaddr { + // match self.inner.endpoint() { + // ConnectedPoint::Dialer { address } => address, + // ConnectedPoint::Listener { .. } => unreachable!("by definition of a `DialingAttempt`."), + // } + // } /// Aborts the dialing attempt. /// diff --git a/core/tests/network_dial_error.rs b/core/tests/network_dial_error.rs index 87d84f52eae..9468b8ec047 100644 --- a/core/tests/network_dial_error.rs +++ b/core/tests/network_dial_error.rs @@ -119,20 +119,20 @@ fn dial_self() { loop { match swarm.poll(cx) { Poll::Ready(NetworkEvent::UnknownPeerDialError { - multiaddr, + // multiaddr, error: PendingConnectionError::InvalidPeerId { .. }, .. }) => { assert!(!got_dial_err); - assert_eq!(multiaddr, local_address); + // assert_eq!(multiaddr, local_address); got_dial_err = true; if got_inc_err { return Poll::Ready(Ok(())); } } - Poll::Ready(NetworkEvent::IncomingConnectionError { local_addr, .. }) => { + Poll::Ready(NetworkEvent::IncomingConnectionError { /*local_addr,*/ .. }) => { assert!(!got_inc_err); - assert_eq!(local_addr, local_address); + // assert_eq!(local_addr, local_address); got_inc_err = true; if got_dial_err { return Poll::Ready(Ok(())); From f01b3238d38d5c580ac038a315b405bffe23f5a0 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Mon, 6 Sep 2021 17:29:45 +0200 Subject: [PATCH 03/67] core/: Add PendingPoint --- core/src/connection.rs | 18 ++++++++++++ core/src/connection/pool.rs | 56 +++++++++++++++++++------------------ core/src/network.rs | 16 ++++++++--- core/src/network/event.rs | 16 +++++------ swarm/src/lib.rs | 24 ++++++++-------- 5 files changed, 78 insertions(+), 52 deletions(-) diff --git a/core/src/connection.rs b/core/src/connection.rs index 9e39ae21807..1c9c42561cb 100644 --- a/core/src/connection.rs +++ b/core/src/connection.rs @@ -72,6 +72,17 @@ impl Endpoint { } } +// TODO: Find better name. +pub enum PendingPoint { + Dialer, + Listener { + /// Local connection address. + local_addr: Multiaddr, + /// Stack of protocols used to send back data to the remote. + send_back_addr: Multiaddr, + }, +} + /// The endpoint roles associated with a peer-to-peer connection. #[derive(PartialEq, Eq, Debug, Clone, Hash)] pub enum ConnectedPoint { @@ -294,6 +305,13 @@ pub struct IncomingInfo<'a> { } impl<'a> IncomingInfo<'a> { + /// Builds the `ConnectedPoint` corresponding to the incoming connection. + pub fn to_pending_point(&self) -> PendingPoint { + PendingPoint::Listener { + local_addr: self.local_addr.clone(), + send_back_addr: self.send_back_addr.clone(), + } + } /// Builds the `ConnectedPoint` corresponding to the incoming connection. pub fn to_connected_point(&self) -> ConnectedPoint { ConnectedPoint::Listener { diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index 5f0bb2f80ab..a1d043e42eb 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -23,7 +23,8 @@ use crate::{ handler::{THandlerError, THandlerInEvent, THandlerOutEvent}, manager::{self, Manager, ManagerConfig}, Connected, ConnectionError, ConnectionHandler, ConnectionId, ConnectionLimit, Endpoint, - IncomingInfo, IntoConnectionHandler, OutgoingInfo, PendingConnectionError, Substream, + IncomingInfo, IntoConnectionHandler, OutgoingInfo, PendingConnectionError, PendingPoint, + Substream, }, muxing::StreamMuxer, network::DialError, @@ -53,7 +54,7 @@ pub struct Pool { established: FnvHashMap>, /// The pending connections that are currently being negotiated. - pending: FnvHashMap)>, + pending: FnvHashMap)>, } impl fmt::Debug for Pool { @@ -104,7 +105,7 @@ pub enum PoolEvent<'a, THandler: IntoConnectionHandler, TTransErr> { /// The ID of the failed connection. id: ConnectionId, /// The local endpoint of the failed connection. - endpoint: Endpoint, + endpoint: PendingPoint, /// The error that occurred. error: PendingConnectionError, /// The handler that was supposed to handle the connection, @@ -226,11 +227,11 @@ impl Pool { TMuxer::OutboundSubstream: Send + 'static, { // TODO: This is a hack. Fix. - let endpoint = info.to_connected_point(); + let endpoint = info.clone().to_connected_point(); let future = future.map_ok(|(peer_id, muxer)| (peer_id, endpoint, muxer)); // TODO: We loose the handler here. self.counters.check_max_pending_incoming()?; - Ok(self.add_pending(future, handler, Endpoint::Listener, None)) + Ok(self.add_pending(future, handler, info.to_pending_point(), None)) } /// Adds a pending outgoing connection to the pool in the form of a `Future` @@ -262,7 +263,7 @@ impl Pool { if let Err(limit) = self.counters.check_max_pending_outgoing() { return Err(DialError::ConnectionLimit { limit, handler }); }; - Ok(self.add_pending(future, handler, Endpoint::Dialer, expected_peer_id)) + Ok(self.add_pending(future, handler, PendingPoint::Dialer, expected_peer_id)) } /// Adds a pending connection to the pool in the form of a @@ -271,7 +272,7 @@ impl Pool { &mut self, future: TFut, handler: THandler, - endpoint: Endpoint, + endpoint: PendingPoint, peer: Option, ) -> ConnectionId where @@ -313,7 +314,7 @@ impl Pool { }); let id = self.manager.add_pending(future, handler); - self.counters.inc_pending(endpoint); + self.counters.inc_pending(&endpoint); self.pending.insert(id, (endpoint, peer)); id } @@ -357,7 +358,7 @@ impl Pool { id: ConnectionId, ) -> Option>> { match self.pending.get(&id) { - Some((Endpoint::Dialer, _peer)) => match self.manager.entry(id) { + Some((PendingPoint::Dialer, _peer)) => match self.manager.entry(id) { Some(manager::Entry::Pending(entry)) => Some(PendingConnection { entry, pending: &mut self.pending, @@ -468,10 +469,10 @@ impl Pool { /// with associated endpoints and expected peer IDs in the pool. pub fn iter_pending_info( &self, - ) -> impl Iterator)> + '_ { + ) -> impl Iterator)> + '_ { self.pending .iter() - .map(|(id, (endpoint, info))| (id, *endpoint, info)) + .map(|(id, (endpoint, info))| (id, endpoint, info)) } /// Returns an iterator over all connected peers, i.e. those that have @@ -498,7 +499,7 @@ impl Pool { match item { manager::Event::PendingConnectionError { id, error, handler } => { if let Some((endpoint, peer)) = self.pending.remove(&id) { - self.counters.dec_pending(endpoint); + self.counters.dec_pending(&endpoint); return Poll::Ready(PoolEvent::PendingConnectionError { id, endpoint, @@ -539,10 +540,10 @@ impl Pool { manager::Event::ConnectionEstablished { entry } => { let id = entry.id(); if let Some((endpoint, peer)) = self.pending.remove(&id) { - self.counters.dec_pending(endpoint); + self.counters.dec_pending(&endpoint); // Check general established connection limit. - if let Err(e) = self.counters.check_max_established(endpoint) { + if let Err(e) = self.counters.check_max_established(&endpoint) { entry.start_close(Some(e)); continue; } @@ -638,7 +639,7 @@ pub enum PoolConnection<'a, TInEvent> { /// A pending connection in a pool. pub struct PendingConnection<'a, TInEvent> { entry: manager::PendingEntry<'a, TInEvent>, - pending: &'a mut FnvHashMap)>, + pending: &'a mut FnvHashMap)>, counters: &'a mut ConnectionCounters, } @@ -658,8 +659,9 @@ impl PendingConnection<'_, TInEvent> { } /// Returns information about this endpoint of the connection. - pub fn endpoint(&self) -> Endpoint { - self.pending + pub fn endpoint(&self) -> &PendingPoint { + &self + .pending .get(&self.entry.id()) .expect("`entry` is a pending entry") .0 @@ -672,7 +674,7 @@ impl PendingConnection<'_, TInEvent> { .remove(&self.entry.id()) .expect("`entry` is a pending entry") .0; - self.counters.dec_pending(endpoint); + self.counters.dec_pending(&endpoint); self.entry.abort(); } } @@ -867,23 +869,23 @@ impl ConnectionCounters { self.established_outgoing + self.established_incoming } - fn inc_pending(&mut self, endpoint: Endpoint) { + fn inc_pending(&mut self, endpoint: &PendingPoint) { match endpoint { - Endpoint::Dialer => { + PendingPoint::Dialer => { self.pending_outgoing += 1; } - Endpoint::Listener => { + PendingPoint::Listener { .. } => { self.pending_incoming += 1; } } } - fn dec_pending(&mut self, endpoint: Endpoint) { + fn dec_pending(&mut self, endpoint: &PendingPoint) { match endpoint { - Endpoint::Dialer => { + PendingPoint::Dialer => { self.pending_outgoing -= 1; } - Endpoint::Listener => { + PendingPoint::Listener { .. } => { self.pending_incoming -= 1; } } @@ -919,16 +921,16 @@ impl ConnectionCounters { Self::check(self.pending_incoming, self.limits.max_pending_incoming) } - fn check_max_established(&self, endpoint: Endpoint) -> Result<(), ConnectionLimit> { + fn check_max_established(&self, endpoint: &PendingPoint) -> Result<(), ConnectionLimit> { // Check total connection limit. Self::check(self.num_established(), self.limits.max_established_total)?; // Check incoming/outgoing connection limits match endpoint { - Endpoint::Dialer => Self::check( + PendingPoint::Dialer => Self::check( self.established_outgoing, self.limits.max_established_outgoing, ), - Endpoint::Listener => Self::check( + PendingPoint::Listener { .. } => Self::check( self.established_incoming, self.limits.max_established_incoming, ), diff --git a/core/src/network.rs b/core/src/network.rs index e87a541487c..b1896b30133 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -33,7 +33,7 @@ use crate::{ pool::{Pool, PoolEvent}, ConnectionHandler, ConnectionId, ConnectionLimit, Endpoint, IncomingInfo, IntoConnectionHandler, ListenerId, ListenersEvent, ListenersStream, OutgoingInfo, - PendingConnectionError, Substream, + PendingConnectionError, PendingPoint, Substream, }, muxing::StreamMuxer, transport::{Transport, TransportError}, @@ -577,7 +577,7 @@ where fn on_connection_failed<'a, TTrans, THandler>( dialing: &mut FnvHashMap>, id: ConnectionId, - endpoint: Endpoint, + endpoint: PendingPoint, error: PendingConnectionError, handler: THandler, ) -> NetworkEvent<'a, TTrans, THandlerInEvent, THandlerOutEvent, THandler> @@ -603,8 +603,16 @@ where } else { // A pending incoming connection or outgoing connection to an unknown peer failed. match endpoint { - Endpoint::Dialer => NetworkEvent::UnknownPeerDialError { error, handler }, - Endpoint::Listener => NetworkEvent::IncomingConnectionError { error, handler }, + PendingPoint::Dialer => NetworkEvent::UnknownPeerDialError { error, handler }, + PendingPoint::Listener { + send_back_addr, + local_addr, + } => NetworkEvent::IncomingConnectionError { + error, + handler, + send_back_addr, + local_addr, + }, } } } diff --git a/core/src/network/event.rs b/core/src/network/event.rs index 188af1d42a6..9094b8c2c01 100644 --- a/core/src/network/event.rs +++ b/core/src/network/event.rs @@ -86,10 +86,10 @@ where /// This can include, for example, an error during the handshake of the encryption layer, or /// the connection unexpectedly closed. IncomingConnectionError { - // /// Local connection address. - // local_addr: Multiaddr, - // /// Address used to send back data to the remote. - // send_back_addr: Multiaddr, + /// Local connection address. + local_addr: Multiaddr, + /// Address used to send back data to the remote. + send_back_addr: Multiaddr, /// The error that happened. error: PendingConnectionError, handler: THandler, @@ -219,14 +219,14 @@ where .field("send_back_addr", &connection.send_back_addr) .finish(), NetworkEvent::IncomingConnectionError { - // local_addr, - // send_back_addr, + local_addr, + send_back_addr, error, handler: _, } => f .debug_struct("IncomingConnectionError") - // .field("local_addr", local_addr) - // .field("send_back_addr", send_back_addr) + .field("local_addr", local_addr) + .field("send_back_addr", send_back_addr) .field("error", error) .finish(), NetworkEvent::ConnectionEstablished { connection, .. } => f diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 43a66c1ba2d..509a37012c1 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -82,8 +82,8 @@ use libp2p_core::{ }, muxing::StreamMuxerBox, network::{ - self, peer::ConnectedPeer, ConnectionLimits, DialAttemptsRemaining, Network, NetworkConfig, - NetworkEvent, NetworkInfo, + self, peer::ConnectedPeer, ConnectionLimits, Network, NetworkConfig, NetworkEvent, + NetworkInfo, }, transport::{self, TransportError}, upgrade::ProtocolName, @@ -372,22 +372,20 @@ where .behaviour .addresses_of_peer(peer_id) .into_iter() - .filter(|a| !self_listening.contains(a)); - - let first = match addrs.next() { - Some(first) => first, - None => { - let error = DialError::NoAddresses; - self.behaviour - .inject_dial_failure(peer_id, handler, error.clone()); - return Err(error); - } + .filter(|a| !self_listening.contains(a)) + .peekable(); + + if addrs.peek().is_none() { + let error = DialError::NoAddresses; + self.behaviour + .inject_dial_failure(peer_id, handler, error.clone()); + return Err(error); }; let handler = handler .into_node_handler_builder() .with_substream_upgrade_protocol_override(self.substream_upgrade_protocol_override); - match self.network.peer(*peer_id).dial(first, addrs, handler) { + match self.network.peer(*peer_id).dial(addrs, handler) { Ok(_connection_id) => Ok(()), Err(error) => { let (error, handler) = DialError::from_network_dial_error(error); From d6da03993ef33a7656cf4c59dd86337b72cfe34f Mon Sep 17 00:00:00 2001 From: Max Inden Date: Tue, 7 Sep 2021 20:11:01 +0200 Subject: [PATCH 04/67] swarm/src/lib: Adjust --- core/src/connection/error.rs | 1 + core/src/network/concurrent_dial.rs | 1 + swarm/src/lib.rs | 97 +++++++++++++---------------- 3 files changed, 44 insertions(+), 55 deletions(-) diff --git a/core/src/connection/error.rs b/core/src/connection/error.rs index 89d863a5b7f..db94123ff8b 100644 --- a/core/src/connection/error.rs +++ b/core/src/connection/error.rs @@ -72,6 +72,7 @@ pub enum PendingConnectionError { // // TODO: Using a Vec here is not ideal. On an incoming listen connection only a single error can // occur. + // How about splitting into PendingIncomingConnectionError and PendingOutgoingConnectionError? Transport(Vec>), /// Pending connection attempt has been aborted. diff --git a/core/src/network/concurrent_dial.rs b/core/src/network/concurrent_dial.rs index 71e2eafd0b0..9e5608666ab 100644 --- a/core/src/network/concurrent_dial.rs +++ b/core/src/network/concurrent_dial.rs @@ -64,6 +64,7 @@ impl Future for ConcurrentDial { fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { loop { match ready!(self.dials.poll_next_unpin(cx)) { + // TODO: What about self.errors? Sure we should loose these? Some(Ok(output)) => return Poll::Ready(Ok(output)), Some(Err(e)) => { self.errors.push(e); diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 509a37012c1..ab32d290e26 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -206,7 +206,9 @@ pub enum SwarmEvent { /// to reach. UnknownPeerUnreachableAddr { /// Address that we failed to reach. - address: Multiaddr, + // + // TODO: Are the addresses that failed contained in the error now? + // address: Multiaddr, /// Error that has been encountered. error: PendingConnectionError, }, @@ -708,56 +710,39 @@ where } Poll::Ready(NetworkEvent::DialError { peer_id, - multiaddr, error, - attempts_remaining, + handler, }) => { // TODO: Remove // this.behaviour // .inject_addr_reach_failure(Some(&peer_id), &multiaddr, &error); - let num_remaining: u32; - match attempts_remaining { - DialAttemptsRemaining::Some(n) => { - num_remaining = n.into(); - } - DialAttemptsRemaining::None(handler) => { - num_remaining = 0; - this.behaviour.inject_dial_failure( - &peer_id, - handler.into_protocols_handler(), - DialError::UnreachableAddr(multiaddr.clone()), - ); - } - } - - log::debug!( - "Connection attempt to {:?} via {:?} failed with {:?}. Attempts remaining: {}.", - peer_id, multiaddr, error, num_remaining, + this.behaviour.inject_dial_failure( + &peer_id, + handler.into_protocols_handler(), + DialError::UnreachableAddr(todo!()), ); - return Poll::Ready(SwarmEvent::UnreachableAddr { + log::debug!( + "Connection attempt to {:?} failed with {:?}.", peer_id, - address: multiaddr, error, - attempts_remaining: num_remaining, - }); - } - Poll::Ready(NetworkEvent::UnknownPeerDialError { - multiaddr, error, .. - }) => { - log::debug!( - "Connection attempt to address {:?} of unknown peer failed with {:?}", - multiaddr, - error ); + + todo!() + // return Poll::Ready(SwarmEvent::UnreachableAddr { + // peer_id, + // address: multiaddr, + // error, + // attempts_remaining: num_remaining, + // }); + } + Poll::Ready(NetworkEvent::UnknownPeerDialError { error, .. }) => { + log::debug!("Connection attempt to unknown peer failed with {:?}", error); // TODO: Remove // this.behaviour // .inject_addr_reach_failure(None, &multiaddr, &error); - return Poll::Ready(SwarmEvent::UnknownPeerUnreachableAddr { - address: multiaddr, - error, - }); + return Poll::Ready(SwarmEvent::UnknownPeerUnreachableAddr { error }); } } @@ -826,24 +811,26 @@ where return Poll::Ready(SwarmEvent::Dialing(peer_id)); } } else { - // Even if the condition for a _new_ dialing attempt is not met, - // we always add any potentially new addresses of the peer to an - // ongoing dialing attempt, if there is one. - log::trace!( - "Condition for new dialing attempt to {:?} not met: {:?}", - peer_id, - condition - ); - let self_listening = &this.listened_addrs; - if let Some(mut peer) = this.network.peer(peer_id).into_dialing() { - let addrs = this.behaviour.addresses_of_peer(peer.id()); - let mut attempt = peer.some_attempt(); - for a in addrs { - if !self_listening.contains(&a) { - attempt.add_address(a); - } - } - } + // TODO: Make sure this behaviour is removed from all other doc comments as well. + // + // // Even if the condition for a _new_ dialing attempt is not met, + // // we always add any potentially new addresses of the peer to an + // // ongoing dialing attempt, if there is one. + // log::trace!( + // "Condition for new dialing attempt to {:?} not met: {:?}", + // peer_id, + // condition + // ); + // let self_listening = &this.listened_addrs; + // if let Some(mut peer) = this.network.peer(peer_id).into_dialing() { + // let addrs = this.behaviour.addresses_of_peer(peer.id()); + // let mut attempt = peer.some_attempt(); + // for a in addrs { + // if !self_listening.contains(&a) { + // attempt.add_address(a); + // } + // } + // } this.behaviour.inject_dial_failure( &peer_id, From 5887d695adc15e3dae29bc556516800b21dcc59d Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 8 Sep 2021 16:20:48 +0200 Subject: [PATCH 05/67] *: Associate Multiaddr with transport error --- core/src/connection/error.rs | 33 ++++++--- core/src/network.rs | 43 +++++------ core/src/network/concurrent_dial.rs | 40 +++++----- core/src/network/event.rs | 1 + core/tests/network_dial_error.rs | 4 +- examples/file-sharing.rs | 9 +-- misc/metrics/src/swarm.rs | 32 ++++---- protocols/kad/src/behaviour.rs | 90 ++++++++++++----------- protocols/relay/src/behaviour.rs | 9 --- swarm-derive/src/lib.rs | 21 +----- swarm/src/behaviour.rs | 6 +- swarm/src/lib.rs | 110 +++++++++++++++------------- swarm/src/test.rs | 2 +- swarm/src/toggle.rs | 2 +- 14 files changed, 190 insertions(+), 212 deletions(-) diff --git a/core/src/connection/error.rs b/core/src/connection/error.rs index db94123ff8b..5488a357f03 100644 --- a/core/src/connection/error.rs +++ b/core/src/connection/error.rs @@ -20,6 +20,7 @@ use crate::connection::ConnectionLimit; use crate::transport::TransportError; +use crate::Multiaddr; use std::{fmt, io}; /// Errors that can occur in the context of an established `Connection`. @@ -68,12 +69,13 @@ where /// Errors that can occur in the context of a pending `Connection`. #[derive(Debug)] pub enum PendingConnectionError { + // TODO: Not ideal that a dial could potentially emit a TransportListen error + // TODO: Could as well rename this to TransportConcurrent + // TODO: In some cases the multiaddr is included twice now. E.g. MultiaddrUnsupported. + TransportDial(Vec<(Multiaddr, TransportError)>), + /// An error occurred while negotiating the transport protocol(s). - // - // TODO: Using a Vec here is not ideal. On an incoming listen connection only a single error can - // occur. - // How about splitting into PendingIncomingConnectionError and PendingOutgoingConnectionError? - Transport(Vec>), + TransportListen(TransportError), /// Pending connection attempt has been aborted. Aborted, @@ -95,10 +97,20 @@ where match self { PendingConnectionError::IO(err) => write!(f, "Pending connection: I/O error: {}", err), PendingConnectionError::Aborted => write!(f, "Pending connection: Aborted."), - PendingConnectionError::Transport(err) => { - // TODO: Resurect - // write!(f, "Pending connection: Transport error: {}", err) - Ok(()) + PendingConnectionError::TransportListen(err) => { + write!( + f, + "Pending connection: Transport error on listening connection: {}", + err + ) + } + PendingConnectionError::TransportDial(err) => { + // write!( + // f, + // "Pending connection: Transport error on dialing connection: {}", + // err + // ) + todo!() } PendingConnectionError::InvalidPeerId => { write!(f, "Pending connection: Invalid peer ID.") @@ -115,7 +127,8 @@ where match self { PendingConnectionError::IO(err) => Some(err), // TODO: Should we return the first of all transport errors? Or implement std::error::Error for Vec? - PendingConnectionError::Transport(err) => None, + PendingConnectionError::TransportListen(err) => None, + PendingConnectionError::TransportDial(err) => None, PendingConnectionError::InvalidPeerId => None, PendingConnectionError::Aborted => None, } diff --git a/core/src/network.rs b/core/src/network.rs index b1896b30133..287eda8675d 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -231,11 +231,7 @@ where None, vec![address.clone()], ) - .map_err(|e| { - PendingConnectionError::Transport( - e.into_iter().map(|e| TransportError::Other(e)).collect(), - ) - }) + .map_err(|e| PendingConnectionError::TransportDial(e)) .map_ok(|(peer_id, address, muxer)| (peer_id, ConnectedPoint::Dialer { address }, muxer)); self.pool.add_outgoing(fut, handler, None) @@ -333,7 +329,7 @@ where { let upgrade = connection .upgrade - .map_err(|err| PendingConnectionError::Transport(vec![TransportError::Other(err)])); + .map_err(|err| PendingConnectionError::TransportListen(TransportError::Other(err))); let info = IncomingInfo { local_addr: &connection.local_addr, send_back_addr: &connection.send_back_addr, @@ -534,11 +530,7 @@ where // }; let fut = concurrent_dial::ConcurrentDial::new(transport, Some(opts.peer), opts.addresses) - .map_err(|e| { - PendingConnectionError::Transport( - e.into_iter().map(|e| TransportError::Other(e)).collect(), - ) - }) + .map_err(|e| PendingConnectionError::TransportDial(e)) .map_ok(|(peer_id, address, muxer)| (peer_id, ConnectedPoint::Dialer { address }, muxer)); let result = pool.add_outgoing(fut, opts.handler, Some(opts.peer)); @@ -702,6 +694,8 @@ impl NetworkConfig { } /// Possible (synchronous) errors when dialing a peer. +// +// TODO: How about ImidiateDialError? #[derive(Clone)] pub enum DialError { /// The dialing attempt is rejected because of a connection limit. @@ -709,12 +703,13 @@ pub enum DialError { limit: ConnectionLimit, handler: THandler, }, - /// The address being dialed is invalid, e.g. if it refers to a different - /// remote peer than the one being dialed. - InvalidAddress { - address: Multiaddr, - handler: THandler, - }, + // /// The address being dialed is invalid, e.g. if it refers to a different + // /// remote peer than the one being dialed. + // // TODO: Still needed? + // InvalidAddress { + // address: Multiaddr, + // handler: THandler, + // }, /// The dialing attempt is rejected because the peer being dialed is the local peer. LocalPeerId { handler: THandler }, } @@ -726,13 +721,13 @@ impl fmt::Debug for DialError { .debug_struct("DialError::ConnectionLimit") .field("limit", limit) .finish(), - DialError::InvalidAddress { - address, - handler: _, - } => f - .debug_struct("DialError::InvalidAddress") - .field("address", address) - .finish(), + // DialError::InvalidAddress { + // address, + // handler: _, + // } => f + // .debug_struct("DialError::InvalidAddress") + // .field("address", address) + // .finish(), DialError::LocalPeerId { handler: _ } => { f.debug_struct("DialError::LocalPeerId").finish() } diff --git a/core/src/network/concurrent_dial.rs b/core/src/network/concurrent_dial.rs index 9e5608666ab..199e47c913b 100644 --- a/core/src/network/concurrent_dial.rs +++ b/core/src/network/concurrent_dial.rs @@ -21,8 +21,8 @@ use std::{ }; pub(crate) struct ConcurrentDial { - dials: FuturesUnordered>>, - errors: Vec, + dials: FuturesUnordered)>>, + errors: Vec<(Multiaddr, TransportError)>, } impl Unpin for ConcurrentDial {} @@ -38,36 +38,32 @@ impl ConcurrentDial { TTrans::Dial: Send + 'static, TError: std::fmt::Debug, { - Self { - dials: addresses - .into_iter() - .map(|a| p2p_addr(peer, a).unwrap()) - .map(|a| { - let a_clone = a.clone(); - transport - .clone() - // TODO: address could as well be derived from transport. - .map(|(peer_id, muxer), _| (peer_id, a_clone, muxer)) - .dial(a) - .unwrap() - .boxed() - }) - .collect(), - errors: vec![], + let dials = FuturesUnordered::default(); + let mut errors = vec![]; + + for address in addresses.into_iter().map(|a| p2p_addr(peer, a).unwrap()) { + match transport.clone().dial(address.clone()) { + Ok(fut) => dials.push(fut.map(|r| (address, r)).boxed()), + Err(err) => errors.push((address, err)), + } } + + Self { dials, errors } } } impl Future for ConcurrentDial { - type Output = Result<(PeerId, Multiaddr, TMuxer), Vec>; + type Output = Result<(PeerId, Multiaddr, TMuxer), Vec<(Multiaddr, TransportError)>>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { loop { match ready!(self.dials.poll_next_unpin(cx)) { // TODO: What about self.errors? Sure we should loose these? - Some(Ok(output)) => return Poll::Ready(Ok(output)), - Some(Err(e)) => { - self.errors.push(e); + Some((addr, Ok((peer_id, muxer)))) => { + return Poll::Ready(Ok((peer_id, addr, muxer))) + } + Some((addr, Err(e))) => { + self.errors.push((addr, TransportError::Other(e))); if self.dials.is_empty() { return Poll::Ready(Err(std::mem::replace(&mut self.errors, vec![]))); } diff --git a/core/src/network/event.rs b/core/src/network/event.rs index 9094b8c2c01..025b64e8457 100644 --- a/core/src/network/event.rs +++ b/core/src/network/event.rs @@ -140,6 +140,7 @@ where error: PendingConnectionError, }, + // TODO: How about merging this into [`DialError`]? /// Failed to reach a peer that we were trying to dial. UnknownPeerDialError { /// The multiaddr we failed to reach. diff --git a/core/tests/network_dial_error.rs b/core/tests/network_dial_error.rs index 9468b8ec047..e2696d50df4 100644 --- a/core/tests/network_dial_error.rs +++ b/core/tests/network_dial_error.rs @@ -66,7 +66,7 @@ fn deny_incoming_connec() { match swarm2.poll(cx) { Poll::Ready(NetworkEvent::DialError { peer_id, - error: PendingConnectionError::Transport(_), + error: PendingConnectionError::TransportDial(_), handler: _, }) => { assert_eq!(&peer_id, swarm1.local_peer_id()); @@ -190,7 +190,7 @@ fn multiple_addresses_err() { Poll::Ready(NetworkEvent::DialError { peer_id, // multiaddr, - error: PendingConnectionError::Transport(_), + error: PendingConnectionError::TransportDial(_), handler: _, }) => { assert_eq!(peer_id, target); diff --git a/examples/file-sharing.rs b/examples/file-sharing.rs index 2bcdbe8719f..d6e5aff928f 100644 --- a/examples/file-sharing.rs +++ b/examples/file-sharing.rs @@ -492,13 +492,8 @@ mod network { } } SwarmEvent::ConnectionClosed { .. } => {} - SwarmEvent::UnreachableAddr { - peer_id, - attempts_remaining, - error, - .. - } => { - if attempts_remaining == 0 { + SwarmEvent::OutgoingConnectionError { peer_id, error, .. } => { + if let Some(peer_id) = peer_id { if let Some(sender) = self.pending_dial.remove(&peer_id) { let _ = sender.send(Err(Box::new(error))); } diff --git a/misc/metrics/src/swarm.rs b/misc/metrics/src/swarm.rs index 630479a2f9c..2e9d9977ec2 100644 --- a/misc/metrics/src/swarm.rs +++ b/misc/metrics/src/swarm.rs @@ -166,21 +166,16 @@ impl super::Recorder { + todo!() + // self.swarm + // .dial_unreachable_addr + // .get_or_create(&vec![("peer".into(), "known".into())]) + // .inc(); + } libp2p_swarm::SwarmEvent::BannedPeer { .. } => { self.swarm.connected_to_banned_peer.inc(); } - libp2p_swarm::SwarmEvent::UnreachableAddr { .. } => { - self.swarm - .dial_unreachable_addr - .get_or_create(&vec![("peer".into(), "known".into())]) - .inc(); - } - libp2p_swarm::SwarmEvent::UnknownPeerUnreachableAddr { .. } => { - self.swarm - .dial_unreachable_addr - .get_or_create(&vec![("peer".into(), "unknown".into())]) - .inc(); - } libp2p_swarm::SwarmEvent::NewListenAddr { .. } => { self.swarm.new_listen_addr.inc(); } @@ -242,16 +237,17 @@ impl From<&libp2p_core::connection::PendingConnectionError libp2p_core::connection::PendingConnectionError::InvalidPeerId => { PendingConnectionError::InvalidPeerId } - libp2p_core::connection::PendingConnectionError::Transport( - libp2p_core::transport::TransportError::MultiaddrNotSupported(_), - ) => PendingConnectionError::TransportErrorMultiaddrNotSupported, - libp2p_core::connection::PendingConnectionError::Transport( - libp2p_core::transport::TransportError::Other(_), - ) => PendingConnectionError::TransportErrorOther, + // libp2p_core::connection::PendingConnectionError::Transport( + // libp2p_core::transport::TransportError::MultiaddrNotSupported(_), + // ) => PendingConnectionError::TransportErrorMultiaddrNotSupported, + // libp2p_core::connection::PendingConnectionError::Transport( + // libp2p_core::transport::TransportError::Other(_), + // ) => PendingConnectionError::TransportErrorOther, libp2p_core::connection::PendingConnectionError::Aborted => { PendingConnectionError::Aborted } libp2p_core::connection::PendingConnectionError::IO(_) => PendingConnectionError::Io, + _ => todo!(), } } } diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 74b5616ff91..5e6a7a39d88 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -1820,50 +1820,52 @@ where } } - fn inject_addr_reach_failure( - &mut self, - peer_id: Option<&PeerId>, - addr: &Multiaddr, - err: &dyn error::Error, - ) { - if let Some(peer_id) = peer_id { - let key = kbucket::Key::from(*peer_id); - - if let Some(addrs) = self.kbuckets.entry(&key).value() { - // TODO: Ideally, the address should only be removed if the error can - // be classified as "permanent" but since `err` is currently a borrowed - // trait object without a `'static` bound, even downcasting for inspection - // of the error is not possible (and also not truly desirable or ergonomic). - // The error passed in should rather be a dedicated enum. - if addrs.remove(addr).is_ok() { - debug!( - "Address '{}' removed from peer '{}' due to error: {}.", - addr, peer_id, err - ); - } else { - // Despite apparently having no reachable address (any longer), - // the peer is kept in the routing table with the last address to avoid - // (temporary) loss of network connectivity to "flush" the routing - // table. Once in, a peer is only removed from the routing table - // if it is the least recently connected peer, currently disconnected - // and is unreachable in the context of another peer pending insertion - // into the same bucket. This is handled transparently by the - // `KBucketsTable` and takes effect through `KBucketsTable::take_applied_pending` - // within `Kademlia::poll`. - debug!( - "Last remaining address '{}' of peer '{}' is unreachable: {}.", - addr, peer_id, err - ) - } - } - - for query in self.queries.iter_mut() { - if let Some(addrs) = query.inner.addresses.get_mut(peer_id) { - addrs.retain(|a| a != addr); - } - } - } - } + // TODO: Trigger the logic within inject_dial_failure + // + // fn inject_addr_reach_failure( + // &mut self, + // peer_id: Option<&PeerId>, + // addr: &Multiaddr, + // err: &dyn error::Error, + // ) { + // if let Some(peer_id) = peer_id { + // let key = kbucket::Key::from(*peer_id); + + // if let Some(addrs) = self.kbuckets.entry(&key).value() { + // // TODO: Ideally, the address should only be removed if the error can + // // be classified as "permanent" but since `err` is currently a borrowed + // // trait object without a `'static` bound, even downcasting for inspection + // // of the error is not possible (and also not truly desirable or ergonomic). + // // The error passed in should rather be a dedicated enum. + // if addrs.remove(addr).is_ok() { + // debug!( + // "Address '{}' removed from peer '{}' due to error: {}.", + // addr, peer_id, err + // ); + // } else { + // // Despite apparently having no reachable address (any longer), + // // the peer is kept in the routing table with the last address to avoid + // // (temporary) loss of network connectivity to "flush" the routing + // // table. Once in, a peer is only removed from the routing table + // // if it is the least recently connected peer, currently disconnected + // // and is unreachable in the context of another peer pending insertion + // // into the same bucket. This is handled transparently by the + // // `KBucketsTable` and takes effect through `KBucketsTable::take_applied_pending` + // // within `Kademlia::poll`. + // debug!( + // "Last remaining address '{}' of peer '{}' is unreachable: {}.", + // addr, peer_id, err + // ) + // } + // } + + // for query in self.queries.iter_mut() { + // if let Some(addrs) = query.inner.addresses.get_mut(peer_id) { + // addrs.retain(|a| a != addr); + // } + // } + // } + // } fn inject_dial_failure( &mut self, diff --git a/protocols/relay/src/behaviour.rs b/protocols/relay/src/behaviour.rs index 35f97647efc..9365fe9736d 100644 --- a/protocols/relay/src/behaviour.rs +++ b/protocols/relay/src/behaviour.rs @@ -411,15 +411,6 @@ impl NetworkBehaviour for Relay { } } - fn inject_addr_reach_failure( - &mut self, - _peer_id: Option<&PeerId>, - _addr: &Multiaddr, - _error: &dyn std::error::Error, - ) { - // Handled in `inject_dial_failure`. - } - fn inject_listener_error(&mut self, _id: ListenerId, _err: &(dyn std::error::Error + 'static)) { } diff --git a/swarm-derive/src/lib.rs b/swarm-derive/src/lib.rs index eef08d15c07..f715a8cbd22 100644 --- a/swarm-derive/src/lib.rs +++ b/swarm-derive/src/lib.rs @@ -253,21 +253,6 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream { }) }; - // Build the list of statements to put in the body of `inject_addr_reach_failure()`. - let inject_addr_reach_failure_stmts = - { - data_struct.fields.iter().enumerate().filter_map(move |(field_n, field)| { - if is_ignored(&field) { - return None; - } - - Some(match field.ident { - Some(ref i) => quote!{ self.#i.inject_addr_reach_failure(peer_id, addr, error); }, - None => quote!{ self.#field_n.inject_addr_reach_failure(peer_id, addr, error); }, - }) - }) - }; - // Build the list of statements to put in the body of `inject_dial_failure()`. let inject_dial_failure_stmts = { data_struct @@ -686,11 +671,7 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream { #(#inject_connection_closed_stmts);* } - fn inject_addr_reach_failure(&mut self, peer_id: Option<&#peer_id>, addr: &#multiaddr, error: &dyn std::error::Error) { - #(#inject_addr_reach_failure_stmts);* - } - - fn inject_dial_failure(&mut self, peer_id: &#peer_id, handlers: Self::ProtocolsHandler, error: #dial_error) { + fn inject_dial_failure(&mut self, peer_id: &#peer_id, handlers: Self::ProtocolsHandler, error: &#dial_error) { #(#inject_dial_failure_stmts);* } diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index b938ba85730..59fadb5d2f8 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -174,7 +174,7 @@ pub trait NetworkBehaviour: Send + 'static { &mut self, _peer_id: &PeerId, _handler: Self::ProtocolsHandler, - _error: DialError, + _error: &DialError, ) { } @@ -188,6 +188,8 @@ pub trait NetworkBehaviour: Send + 'static { _local_addr: &Multiaddr, _send_back_addr: &Multiaddr, _handler: Self::ProtocolsHandler, + // TODO: Should we forward the PendingConnectionError to the inject_listen_failure call as + // well? ) { } @@ -403,7 +405,7 @@ pub enum NetworkBehaviourAction< /// &mut self, /// _: &PeerId, /// handler: Self::ProtocolsHandler, - /// _: DialError, + /// _: &DialError, /// ) { /// // As expected, sending the message failed. But lucky us, we got the handler back, thus /// // the precious message is not lost and we can return it back to the user. diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index ab32d290e26..91c53b06858 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -66,6 +66,7 @@ pub use behaviour::{ CloseConnection, DialPeerCondition, NetworkBehaviour, NetworkBehaviourAction, NetworkBehaviourEventProcess, NotifyHandler, PollParameters, }; +use log::Level::Error; pub use protocols_handler::{ IntoProtocolsHandler, IntoProtocolsHandlerSelect, KeepAlive, OneShotHandler, OneShotHandlerConfig, ProtocolsHandler, ProtocolsHandlerEvent, ProtocolsHandlerSelect, @@ -183,6 +184,13 @@ pub enum SwarmEvent { /// The error that happened. error: PendingConnectionError, }, + /// Tried to dial an address but it ended up being unreachaable. + OutgoingConnectionError { + /// If known, [`PeerId`] of the peer we tried to reach. + peer_id: Option, + /// Error that has been encountered. + error: DialError, + }, /// We connected to a peer, but we immediately closed the connection because that peer is banned. BannedPeer { /// Identity of the banned peer. @@ -190,28 +198,6 @@ pub enum SwarmEvent { /// Endpoint of the connection that has been closed. endpoint: ConnectedPoint, }, - /// Tried to dial an address but it ended up being unreachaable. - UnreachableAddr { - /// `PeerId` that we were trying to reach. - peer_id: PeerId, - /// Address that we failed to reach. - address: Multiaddr, - /// Error that has been encountered. - error: PendingConnectionError, - /// Number of remaining connection attempts that are being tried for this peer. - attempts_remaining: u32, - }, - /// Tried to dial an address but it ended up being unreachaable. - /// Contrary to `UnreachableAddr`, we don't know the identity of the peer that we were trying - /// to reach. - UnknownPeerUnreachableAddr { - /// Address that we failed to reach. - // - // TODO: Are the addresses that failed contained in the error now? - // address: Multiaddr, - /// Error that has been encountered. - error: PendingConnectionError, - }, /// One of our listeners has reported a new local listening address. NewListenAddr { /// The listener that is listening on the new address. @@ -364,8 +350,7 @@ where ) -> Result<(), DialError> { if self.banned_peers.contains(peer_id) { let error = DialError::Banned; - self.behaviour - .inject_dial_failure(peer_id, handler, error.clone()); + self.behaviour.inject_dial_failure(peer_id, handler, &error); return Err(error); } @@ -379,8 +364,7 @@ where if addrs.peek().is_none() { let error = DialError::NoAddresses; - self.behaviour - .inject_dial_failure(peer_id, handler, error.clone()); + self.behaviour.inject_dial_failure(peer_id, handler, &error); return Err(error); }; @@ -394,7 +378,7 @@ where self.behaviour.inject_dial_failure( &peer_id, handler.into_protocols_handler(), - error.clone(), + &error, ); Err(error) } @@ -717,10 +701,12 @@ where // this.behaviour // .inject_addr_reach_failure(Some(&peer_id), &multiaddr, &error); + let error = error.into(); + this.behaviour.inject_dial_failure( &peer_id, handler.into_protocols_handler(), - DialError::UnreachableAddr(todo!()), + &error, ); log::debug!( @@ -729,20 +715,21 @@ where error, ); - todo!() - // return Poll::Ready(SwarmEvent::UnreachableAddr { - // peer_id, - // address: multiaddr, - // error, - // attempts_remaining: num_remaining, - // }); + return Poll::Ready(SwarmEvent::OutgoingConnectionError { + peer_id: Some(peer_id), + error, + }); } - Poll::Ready(NetworkEvent::UnknownPeerDialError { error, .. }) => { + Poll::Ready(NetworkEvent::UnknownPeerDialError { error, handler }) => { log::debug!("Connection attempt to unknown peer failed with {:?}", error); - // TODO: Remove + let error = error.into(); + // TODO: Make sure to give the handler back to the behaviour. // this.behaviour - // .inject_addr_reach_failure(None, &multiaddr, &error); - return Poll::Ready(SwarmEvent::UnknownPeerUnreachableAddr { error }); + // .inject_dial_failure(None, &multiaddr, &error); + return Poll::Ready(SwarmEvent::OutgoingConnectionError { + peer_id: None, + error: error, + }); } } @@ -835,8 +822,10 @@ where this.behaviour.inject_dial_failure( &peer_id, handler, - DialError::DialPeerConditionFalse(condition), + &DialError::DialPeerConditionFalse(condition), ); + + // TODO: Should we not emit a swarm event? } } Poll::Ready(NetworkBehaviourAction::NotifyHandler { @@ -1187,17 +1176,14 @@ where } /// The possible failures of dialing. -#[derive(Debug, Clone)] +// TODO: Should we have separate errors for synchronous and asynchronous dial errors? +#[derive(Debug)] pub enum DialError { /// The peer is currently banned. Banned, /// The configured limit for simultaneous outgoing connections /// has been reached. ConnectionLimit(ConnectionLimit), - /// The address given for dialing is invalid. - InvalidAddress(Multiaddr), - /// Tried to dial an address but it ended up being unreachaable. - UnreachableAddr(Multiaddr), /// The peer being dialed is the local peer and thus the dial was aborted. LocalPeerId, /// [`NetworkBehaviour::addresses_of_peer`] returned no addresses @@ -1205,6 +1191,12 @@ pub enum DialError { NoAddresses, /// The provided [`DialPeerCondition`] evaluated to false and thus the dial was aborted. DialPeerConditionFalse(DialPeerCondition), + + // TODO: Document + Aborted, + InvalidPeerId, + ConnectionIo(io::Error), + Transport(Vec<(Multiaddr, TransportError)>), } impl DialError { @@ -1213,22 +1205,34 @@ impl DialError { network::DialError::ConnectionLimit { limit, handler } => { (DialError::ConnectionLimit(limit), handler) } - network::DialError::InvalidAddress { address, handler } => { - (DialError::InvalidAddress(address), handler) - } + // network::DialError::InvalidAddress { address, handler } => { + // (DialError::InvalidAddress(address), handler) + // } network::DialError::LocalPeerId { handler } => (DialError::LocalPeerId, handler), } } } +impl From> for DialError { + fn from(error: PendingConnectionError) -> Self { + match error { + PendingConnectionError::Aborted => DialError::Aborted, + PendingConnectionError::InvalidPeerId => DialError::InvalidPeerId, + PendingConnectionError::IO(e) => DialError::ConnectionIo(e), + PendingConnectionError::TransportDial(e) => DialError::Transport(e), + PendingConnectionError::TransportListen(e) => todo!(), + } + } +} + impl fmt::Display for DialError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { DialError::ConnectionLimit(err) => write!(f, "Dial error: {}", err), DialError::NoAddresses => write!(f, "Dial error: no addresses for peer."), DialError::LocalPeerId => write!(f, "Dial error: tried to dial local peer id."), - DialError::InvalidAddress(a) => write!(f, "Dial error: invalid address: {}", a), - DialError::UnreachableAddr(a) => write!(f, "Dial error: unreachable address: {}", a), + // DialError::InvalidAddress(a) => write!(f, "Dial error: invalid address: {}", a), + // DialError::UnreachableAddr(a) => write!(f, "Dial error: unreachable address: {}", a), DialError::Banned => write!(f, "Dial error: peer is banned."), DialError::DialPeerConditionFalse(c) => { write!( @@ -1237,6 +1241,7 @@ impl fmt::Display for DialError { c ) } + _ => todo!(), } } } @@ -1245,12 +1250,13 @@ impl error::Error for DialError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { DialError::ConnectionLimit(err) => Some(err), - DialError::InvalidAddress(_) => None, - DialError::UnreachableAddr(_) => None, + // DialError::InvalidAddress(_) => None, + // TODO: Can we do better? DialError::LocalPeerId => None, DialError::NoAddresses => None, DialError::Banned => None, DialError::DialPeerConditionFalse(_) => None, + _ => todo!(), } } } diff --git a/swarm/src/test.rs b/swarm/src/test.rs index c0b4a29257b..5c97f756a78 100644 --- a/swarm/src/test.rs +++ b/swarm/src/test.rs @@ -225,7 +225,7 @@ where &mut self, p: &PeerId, handler: Self::ProtocolsHandler, - error: DialError, + error: &DialError, ) { self.inject_dial_failure.push(p.clone()); self.inner.inject_dial_failure(p, handler, error); diff --git a/swarm/src/toggle.rs b/swarm/src/toggle.rs index 815046b5599..181e2abd259 100644 --- a/swarm/src/toggle.rs +++ b/swarm/src/toggle.rs @@ -150,7 +150,7 @@ where &mut self, peer_id: &PeerId, handler: Self::ProtocolsHandler, - error: DialError, + error: &DialError, ) { if let Some(inner) = self.inner.as_mut() { if let Some(handler) = handler.inner { From 08c196f86565874f35ba9aa99f8be70ef32fc221 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 8 Sep 2021 16:28:48 +0200 Subject: [PATCH 06/67] protocols: Update --- core/src/connection/.#manager.rs | 1 + core/src/network/concurrent_dial.rs | 13 +-- protocols/identify/src/identify.rs | 2 +- protocols/kad/Cargo.toml | 1 + protocols/kad/src/behaviour.rs | 95 ++++++++++----------- protocols/kad/src/behaviour/test.rs | 1 + protocols/relay/src/behaviour.rs | 2 +- protocols/relay/tests/lib.rs | 43 ++++++---- protocols/request-response/src/lib.rs | 2 +- protocols/request-response/src/throttled.rs | 2 +- 10 files changed, 85 insertions(+), 77 deletions(-) create mode 120000 core/src/connection/.#manager.rs diff --git a/core/src/connection/.#manager.rs b/core/src/connection/.#manager.rs new file mode 120000 index 00000000000..faf0e01aaf2 --- /dev/null +++ b/core/src/connection/.#manager.rs @@ -0,0 +1 @@ +mxinden@debian.3649:1631102366 \ No newline at end of file diff --git a/core/src/network/concurrent_dial.rs b/core/src/network/concurrent_dial.rs index 199e47c913b..fd92a2e5ef2 100644 --- a/core/src/network/concurrent_dial.rs +++ b/core/src/network/concurrent_dial.rs @@ -38,6 +38,7 @@ impl ConcurrentDial { TTrans::Dial: Send + 'static, TError: std::fmt::Debug, { + println!("Length of addresses: {}", addresses.len()); let dials = FuturesUnordered::default(); let mut errors = vec![]; @@ -56,19 +57,21 @@ impl Future for ConcurrentDial { type Output = Result<(PeerId, Multiaddr, TMuxer), Vec<(Multiaddr, TransportError)>>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + println!("Lenght of dials: {}", self.dials.len()); loop { match ready!(self.dials.poll_next_unpin(cx)) { // TODO: What about self.errors? Sure we should loose these? Some((addr, Ok((peer_id, muxer)))) => { - return Poll::Ready(Ok((peer_id, addr, muxer))) + println!("Got a connection"); + return Poll::Ready(Ok((peer_id, addr, muxer))); } Some((addr, Err(e))) => { + println!("Got an error"); self.errors.push((addr, TransportError::Other(e))); - if self.dials.is_empty() { - return Poll::Ready(Err(std::mem::replace(&mut self.errors, vec![]))); - } } - None => todo!(), + None => { + return Poll::Ready(Err(std::mem::replace(&mut self.errors, vec![]))); + } } } } diff --git a/protocols/identify/src/identify.rs b/protocols/identify/src/identify.rs index d75dfb72054..ac9ce9ab5ae 100644 --- a/protocols/identify/src/identify.rs +++ b/protocols/identify/src/identify.rs @@ -223,7 +223,7 @@ impl NetworkBehaviour for Identify { } } - fn inject_dial_failure(&mut self, peer_id: &PeerId, _: Self::ProtocolsHandler, _: DialError) { + fn inject_dial_failure(&mut self, peer_id: &PeerId, _: Self::ProtocolsHandler, _: &DialError) { if !self.connected.contains_key(peer_id) { self.pending_push.remove(peer_id); } diff --git a/protocols/kad/Cargo.toml b/protocols/kad/Cargo.toml index 20fe6fe2c75..272ab9473d3 100644 --- a/protocols/kad/Cargo.toml +++ b/protocols/kad/Cargo.toml @@ -29,6 +29,7 @@ unsigned-varint = { version = "0.7", features = ["asynchronous_codec"] } void = "1.0" [dev-dependencies] +env_logger = "0.9.0" futures-timer = "3.0" libp2p-noise = { path = "../../transports/noise" } libp2p-yamux = { path = "../../muxers/yamux" } diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 5e6a7a39d88..3838a4f67ae 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -1820,66 +1820,61 @@ where } } - // TODO: Trigger the logic within inject_dial_failure - // - // fn inject_addr_reach_failure( - // &mut self, - // peer_id: Option<&PeerId>, - // addr: &Multiaddr, - // err: &dyn error::Error, - // ) { - // if let Some(peer_id) = peer_id { - // let key = kbucket::Key::from(*peer_id); - - // if let Some(addrs) = self.kbuckets.entry(&key).value() { - // // TODO: Ideally, the address should only be removed if the error can - // // be classified as "permanent" but since `err` is currently a borrowed - // // trait object without a `'static` bound, even downcasting for inspection - // // of the error is not possible (and also not truly desirable or ergonomic). - // // The error passed in should rather be a dedicated enum. - // if addrs.remove(addr).is_ok() { - // debug!( - // "Address '{}' removed from peer '{}' due to error: {}.", - // addr, peer_id, err - // ); - // } else { - // // Despite apparently having no reachable address (any longer), - // // the peer is kept in the routing table with the last address to avoid - // // (temporary) loss of network connectivity to "flush" the routing - // // table. Once in, a peer is only removed from the routing table - // // if it is the least recently connected peer, currently disconnected - // // and is unreachable in the context of another peer pending insertion - // // into the same bucket. This is handled transparently by the - // // `KBucketsTable` and takes effect through `KBucketsTable::take_applied_pending` - // // within `Kademlia::poll`. - // debug!( - // "Last remaining address '{}' of peer '{}' is unreachable: {}.", - // addr, peer_id, err - // ) - // } - // } - - // for query in self.queries.iter_mut() { - // if let Some(addrs) = query.inner.addresses.get_mut(peer_id) { - // addrs.retain(|a| a != addr); - // } - // } - // } - // } - fn inject_dial_failure( &mut self, peer_id: &PeerId, _: Self::ProtocolsHandler, - error: DialError, + error: &DialError, ) { match error { DialError::Banned | DialError::ConnectionLimit(_) - | DialError::InvalidAddress(_) - | DialError::UnreachableAddr(_) | DialError::LocalPeerId + | DialError::InvalidPeerId + | DialError::Aborted + | DialError::ConnectionIo(_) + | DialError::Transport(_) | DialError::NoAddresses => { + if let DialError::Transport(addresses) = error { + for (addr, err) in addresses { + let key = kbucket::Key::from(*peer_id); + + if let Some(addrs) = self.kbuckets.entry(&key).value() { + // TODO: Ideally, the address should only be removed if the error can + // be classified as "permanent" but since `err` is currently a borrowed + // trait object without a `'static` bound, even downcasting for inspection + // of the error is not possible (and also not truly desirable or ergonomic). + // The error passed in should rather be a dedicated enum. + if addrs.remove(addr).is_ok() { + debug!( + "Address '{}' removed from peer '{}' due to error: {}.", + addr, peer_id, err + ); + } else { + // Despite apparently having no reachable address (any longer), + // the peer is kept in the routing table with the last address to avoid + // (temporary) loss of network connectivity to "flush" the routing + // table. Once in, a peer is only removed from the routing table + // if it is the least recently connected peer, currently disconnected + // and is unreachable in the context of another peer pending insertion + // into the same bucket. This is handled transparently by the + // `KBucketsTable` and takes effect through `KBucketsTable::take_applied_pending` + // within `Kademlia::poll`. + debug!( + "Last remaining address '{}' of peer '{}' is unreachable: {}.", + addr, peer_id, err + ) + } + } + + for query in self.queries.iter_mut() { + if let Some(addrs) = query.inner.addresses.get_mut(peer_id) { + addrs.retain(|a| a != addr); + } + } + } + } + for query in self.queries.iter_mut() { query.on_failure(peer_id); } diff --git a/protocols/kad/src/behaviour/test.rs b/protocols/kad/src/behaviour/test.rs index fc855b049ea..4c72079ec90 100644 --- a/protocols/kad/src/behaviour/test.rs +++ b/protocols/kad/src/behaviour/test.rs @@ -313,6 +313,7 @@ fn query_iter() { #[test] fn unresponsive_not_returned_direct() { + let _ = env_logger::try_init(); // Build one node. It contains fake addresses to non-existing nodes. We ask it to find a // random peer. We make sure that no fake address is returned. diff --git a/protocols/relay/src/behaviour.rs b/protocols/relay/src/behaviour.rs index 9365fe9736d..754dee51924 100644 --- a/protocols/relay/src/behaviour.rs +++ b/protocols/relay/src/behaviour.rs @@ -306,7 +306,7 @@ impl NetworkBehaviour for Relay { &mut self, peer_id: &PeerId, _: Self::ProtocolsHandler, - error: DialError, + error: &DialError, ) { if let DialError::DialPeerConditionFalse( DialPeerCondition::Disconnected | DialPeerCondition::NotDialing, diff --git a/protocols/relay/tests/lib.rs b/protocols/relay/tests/lib.rs index 29a0cfd35ed..d73aa7184f0 100644 --- a/protocols/relay/tests/lib.rs +++ b/protocols/relay/tests/lib.rs @@ -36,8 +36,8 @@ use libp2p_plaintext::PlainText2Config; use libp2p_relay::{Relay, RelayConfig}; use libp2p_swarm::protocols_handler::KeepAlive; use libp2p_swarm::{ - DummyBehaviour, NetworkBehaviour, NetworkBehaviourAction, NetworkBehaviourEventProcess, - PollParameters, Swarm, SwarmEvent, + DialError, DummyBehaviour, NetworkBehaviour, NetworkBehaviourAction, + NetworkBehaviourEventProcess, PollParameters, Swarm, SwarmEvent, }; use std::task::{Context, Poll}; use std::time::Duration; @@ -391,10 +391,11 @@ fn src_try_connect_to_offline_dst() { loop { match src_swarm.select_next_some().await { - SwarmEvent::UnreachableAddr { - address, peer_id, .. - } if address == dst_addr_via_relay => { - assert_eq!(peer_id, dst_peer_id); + SwarmEvent::OutgoingConnectionError { + error: DialError::Transport(addresses), + peer_id, + } if *addresses.iter().map(|(a, _)| a).next().unwrap() == dst_addr_via_relay => { + assert_eq!(peer_id, Some(dst_peer_id)); break; } SwarmEvent::Behaviour(CombinedEvent::Ping(_)) => {} @@ -448,10 +449,11 @@ fn src_try_connect_to_unsupported_dst() { loop { match src_swarm.select_next_some().await { - SwarmEvent::UnreachableAddr { - address, peer_id, .. - } if address == dst_addr_via_relay => { - assert_eq!(peer_id, dst_peer_id); + SwarmEvent::OutgoingConnectionError { + error: DialError::Transport(addresses), + peer_id, + } if *addresses.iter().map(|(a, _)| a).next().unwrap() == dst_addr_via_relay => { + assert_eq!(peer_id, Some(dst_peer_id)); break; } SwarmEvent::ConnectionClosed { peer_id, .. } if peer_id == relay_peer_id => {} @@ -492,16 +494,18 @@ fn src_try_connect_to_offline_dst_via_offline_relay() { // Source Node fail to reach Relay. match src_swarm.select_next_some().await { - SwarmEvent::UnreachableAddr { peer_id, .. } if peer_id == relay_peer_id => {} + SwarmEvent::OutgoingConnectionError { peer_id, .. } + if peer_id == Some(relay_peer_id) => {} e => panic!("{:?}", e), } // Source Node fail to reach Destination Node due to failure reaching Relay. match src_swarm.select_next_some().await { - SwarmEvent::UnreachableAddr { - address, peer_id, .. - } if address == dst_addr_via_relay => { - assert_eq!(peer_id, dst_peer_id); + SwarmEvent::OutgoingConnectionError { + error: DialError::Transport(addresses), + peer_id, + } if *addresses.iter().map(|(a, _)| a).next().unwrap() == dst_addr_via_relay => { + assert_eq!(peer_id, Some(dst_peer_id)); } e => panic!("{:?}", e), } @@ -1063,10 +1067,13 @@ fn yield_incoming_connection_through_correct_listener() { e => panic!("{:?}", e), }, event = src_3_swarm.select_next_some() => match event { - SwarmEvent::UnreachableAddr { address, peer_id, .. } - if address == dst_addr_via_relay_3 => + SwarmEvent::OutgoingConnectionError { + error: DialError::Transport(addresses), + peer_id, + } if *addresses.iter().map(|(a, _)| a).next().unwrap() + == dst_addr_via_relay_3 => { - assert_eq!(peer_id, dst_peer_id); + assert_eq!(peer_id, Some(dst_peer_id)); break; } SwarmEvent::Dialing { .. } => {} diff --git a/protocols/request-response/src/lib.rs b/protocols/request-response/src/lib.rs index ef3913c1efb..48abd11d07d 100644 --- a/protocols/request-response/src/lib.rs +++ b/protocols/request-response/src/lib.rs @@ -686,7 +686,7 @@ where self.connected.remove(peer); } - fn inject_dial_failure(&mut self, peer: &PeerId, _: Self::ProtocolsHandler, _: DialError) { + fn inject_dial_failure(&mut self, peer: &PeerId, _: Self::ProtocolsHandler, _: &DialError) { // If there are pending outgoing requests when a dial failure occurs, // it is implied that we are not connected to the peer, since pending // outgoing requests are drained when a connection is established and diff --git a/protocols/request-response/src/throttled.rs b/protocols/request-response/src/throttled.rs index 2b8693bc437..a9a0c7ded84 100644 --- a/protocols/request-response/src/throttled.rs +++ b/protocols/request-response/src/throttled.rs @@ -497,7 +497,7 @@ where &mut self, p: &PeerId, handler: Self::ProtocolsHandler, - error: DialError, + error: &DialError, ) { self.behaviour.inject_dial_failure(p, handler, error) } From 6c1ce85f56327355f7b3b79194a7fac4d948b899 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 9 Sep 2021 13:06:27 +0200 Subject: [PATCH 07/67] core/src/: Remove printlns --- core/src/network/concurrent_dial.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/core/src/network/concurrent_dial.rs b/core/src/network/concurrent_dial.rs index fd92a2e5ef2..1e5fc3c7027 100644 --- a/core/src/network/concurrent_dial.rs +++ b/core/src/network/concurrent_dial.rs @@ -1,3 +1,23 @@ +// Copyright 2021 Protocol Labs. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + pub use crate::connection::{ConnectionCounters, ConnectionLimits}; use crate::{ @@ -38,7 +58,6 @@ impl ConcurrentDial { TTrans::Dial: Send + 'static, TError: std::fmt::Debug, { - println!("Length of addresses: {}", addresses.len()); let dials = FuturesUnordered::default(); let mut errors = vec![]; @@ -57,7 +76,6 @@ impl Future for ConcurrentDial { type Output = Result<(PeerId, Multiaddr, TMuxer), Vec<(Multiaddr, TransportError)>>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { - println!("Lenght of dials: {}", self.dials.len()); loop { match ready!(self.dials.poll_next_unpin(cx)) { // TODO: What about self.errors? Sure we should loose these? From 9070bd2ca65e0a2a8beb47093be3ce9c39884365 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 9 Sep 2021 13:34:04 +0200 Subject: [PATCH 08/67] misc/metrics: Update --- core/src/network/concurrent_dial.rs | 2 + misc/metrics/src/swarm.rs | 114 +++++++++++++++++++++++----- 2 files changed, 95 insertions(+), 21 deletions(-) diff --git a/core/src/network/concurrent_dial.rs b/core/src/network/concurrent_dial.rs index 1e5fc3c7027..13cffc76074 100644 --- a/core/src/network/concurrent_dial.rs +++ b/core/src/network/concurrent_dial.rs @@ -40,6 +40,8 @@ use std::{ task::{Context, Poll}, }; +// TODO: Have a concurrency limit. + pub(crate) struct ConcurrentDial { dials: FuturesUnordered)>>, errors: Vec<(Multiaddr, TransportError)>, diff --git a/misc/metrics/src/swarm.rs b/misc/metrics/src/swarm.rs index 2e9d9977ec2..ddc7c5fcbba 100644 --- a/misc/metrics/src/swarm.rs +++ b/misc/metrics/src/swarm.rs @@ -37,7 +37,7 @@ pub struct Metrics { listener_error: Counter, dial_attempt: Counter, - dial_unreachable_addr: Family, Counter>, + outgoing_connection_error: Family, connected_to_banned_peer: Counter, } @@ -94,11 +94,11 @@ impl Metrics { Box::new(dial_attempt.clone()), ); - let dial_unreachable_addr = Family::default(); + let outgoing_connection_error = Family::default(); sub_registry.register( - "dial_unreachable_addr", - "Number of unreachable addresses dialed", - Box::new(dial_unreachable_addr.clone()), + "outgoing_connection_error", + "Number outgoing connection errors", + Box::new(outgoing_connection_error.clone()), ); let connected_to_banned_peer = Counter::default(); @@ -132,7 +132,7 @@ impl Metrics { listener_closed, listener_error, dial_attempt, - dial_unreachable_addr, + outgoing_connection_error, connected_to_banned_peer, } } @@ -166,12 +166,58 @@ impl super::Recorder { - todo!() - // self.swarm - // .dial_unreachable_addr - // .get_or_create(&vec![("peer".into(), "known".into())]) - // .inc(); + libp2p_swarm::SwarmEvent::OutgoingConnectionError { error, peer_id } => { + let peer = match peer_id { + Some(_) => PeerStatus::Known, + None => PeerStatus::Unknown, + }; + + let record = |error| { + self.swarm + .outgoing_connection_error + .get_or_create(&OutgoingConnectionErrorLabels { peer, error }) + .inc(); + }; + + let error = match error { + libp2p_swarm::DialError::Transport(errors) => { + for (_multiaddr, error) in errors { + let error = match error { + libp2p_core::transport::TransportError::MultiaddrNotSupported( + _, + ) => record( + OutgoingConnectionErrorError::TransportMultiaddrNotSupported, + ), + libp2p_core::transport::TransportError::Other(_) => { + record(OutgoingConnectionErrorError::TransportOther) + } + }; + } + } + + libp2p_swarm::DialError::Banned => record(OutgoingConnectionErrorError::Banned), + libp2p_swarm::DialError::ConnectionLimit(_) => { + record(OutgoingConnectionErrorError::ConnectionLimit) + } + libp2p_swarm::DialError::LocalPeerId => { + record(OutgoingConnectionErrorError::LocalPeerId) + } + libp2p_swarm::DialError::NoAddresses => { + record(OutgoingConnectionErrorError::NoAddresses) + } + libp2p_swarm::DialError::DialPeerConditionFalse(_) => { + record(OutgoingConnectionErrorError::DialPeerConditionFalse) + } + libp2p_swarm::DialError::Aborted => { + record(OutgoingConnectionErrorError::Aborted) + } + libp2p_swarm::DialError::InvalidPeerId => { + record(OutgoingConnectionErrorError::InvalidPeerId) + } + libp2p_swarm::DialError::ConnectionIo(_) => { + record(OutgoingConnectionErrorError::ConnectionIo) + } + }; } libp2p_swarm::SwarmEvent::BannedPeer { .. } => { self.swarm.connected_to_banned_peer.inc(); @@ -215,6 +261,32 @@ impl From<&libp2p_core::ConnectedPoint> for Role { } } +#[derive(Encode, Hash, Clone, Eq, PartialEq)] +struct OutgoingConnectionErrorLabels { + peer: PeerStatus, + error: OutgoingConnectionErrorError, +} + +#[derive(Encode, Hash, Clone, Eq, PartialEq, Copy)] +enum PeerStatus { + Known, + Unknown, +} + +#[derive(Encode, Hash, Clone, Eq, PartialEq)] +enum OutgoingConnectionErrorError { + Banned, + ConnectionLimit, + LocalPeerId, + NoAddresses, + DialPeerConditionFalse, + Aborted, + InvalidPeerId, + ConnectionIo, + TransportMultiaddrNotSupported, + TransportOther, +} + #[derive(Encode, Hash, Clone, Eq, PartialEq)] struct IncomingConnectionErrorLabels { error: PendingConnectionError, @@ -232,22 +304,22 @@ enum PendingConnectionError { impl From<&libp2p_core::connection::PendingConnectionError> for PendingConnectionError { - fn from(point: &libp2p_core::connection::PendingConnectionError) -> Self { - match point { + fn from(error: &libp2p_core::connection::PendingConnectionError) -> Self { + match error { libp2p_core::connection::PendingConnectionError::InvalidPeerId => { PendingConnectionError::InvalidPeerId } - // libp2p_core::connection::PendingConnectionError::Transport( - // libp2p_core::transport::TransportError::MultiaddrNotSupported(_), - // ) => PendingConnectionError::TransportErrorMultiaddrNotSupported, - // libp2p_core::connection::PendingConnectionError::Transport( - // libp2p_core::transport::TransportError::Other(_), - // ) => PendingConnectionError::TransportErrorOther, + libp2p_core::connection::PendingConnectionError::TransportListen( + libp2p_core::transport::TransportError::MultiaddrNotSupported(_), + ) => PendingConnectionError::TransportErrorMultiaddrNotSupported, + libp2p_core::connection::PendingConnectionError::TransportListen( + libp2p_core::transport::TransportError::Other(_), + ) => PendingConnectionError::TransportErrorOther, + libp2p_core::connection::PendingConnectionError::TransportDial(_) => unreachable!(), libp2p_core::connection::PendingConnectionError::Aborted => { PendingConnectionError::Aborted } libp2p_core::connection::PendingConnectionError::IO(_) => PendingConnectionError::Io, - _ => todo!(), } } } From 40439708356e80b8a67045c91200415d55b4caf9 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 9 Sep 2021 13:35:22 +0200 Subject: [PATCH 09/67] core/: Remove printlns --- core/src/network/concurrent_dial.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/network/concurrent_dial.rs b/core/src/network/concurrent_dial.rs index 13cffc76074..eebad6acaab 100644 --- a/core/src/network/concurrent_dial.rs +++ b/core/src/network/concurrent_dial.rs @@ -82,11 +82,9 @@ impl Future for ConcurrentDial { match ready!(self.dials.poll_next_unpin(cx)) { // TODO: What about self.errors? Sure we should loose these? Some((addr, Ok((peer_id, muxer)))) => { - println!("Got a connection"); return Poll::Ready(Ok((peer_id, addr, muxer))); } Some((addr, Err(e))) => { - println!("Got an error"); self.errors.push((addr, TransportError::Other(e))); } None => { From d59420885e77142347265b79b2f710f4c2fbbb86 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 9 Sep 2021 14:19:54 +0200 Subject: [PATCH 10/67] core/src/network/concurrent: Catch address error --- core/src/network/concurrent_dial.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/core/src/network/concurrent_dial.rs b/core/src/network/concurrent_dial.rs index eebad6acaab..aa3c9c20afc 100644 --- a/core/src/network/concurrent_dial.rs +++ b/core/src/network/concurrent_dial.rs @@ -63,10 +63,17 @@ impl ConcurrentDial { let dials = FuturesUnordered::default(); let mut errors = vec![]; - for address in addresses.into_iter().map(|a| p2p_addr(peer, a).unwrap()) { - match transport.clone().dial(address.clone()) { - Ok(fut) => dials.push(fut.map(|r| (address, r)).boxed()), - Err(err) => errors.push((address, err)), + for address in addresses.into_iter().map(|a| p2p_addr(peer, a)) { + match address { + Ok(address) => match transport.clone().dial(address.clone()) { + Ok(fut) => dials.push(fut.map(|r| (address, r)).boxed()), + Err(err) => errors.push((address, err)), + }, + Err(address) => errors.push(( + address.clone(), + // TODO: It is not really the Multiaddr that is not supported. + TransportError::MultiaddrNotSupported(address), + )), } } From a705bb085ce74a03e0c4d4a56852d37a6a95d12d Mon Sep 17 00:00:00 2001 From: Max Inden Date: Sun, 26 Sep 2021 12:28:52 +0200 Subject: [PATCH 11/67] core/src/connection: Remove meta file --- core/src/connection/.#manager.rs | 1 - 1 file changed, 1 deletion(-) delete mode 120000 core/src/connection/.#manager.rs diff --git a/core/src/connection/.#manager.rs b/core/src/connection/.#manager.rs deleted file mode 120000 index faf0e01aaf2..00000000000 --- a/core/src/connection/.#manager.rs +++ /dev/null @@ -1 +0,0 @@ -mxinden@debian.3649:1631102366 \ No newline at end of file From 63c4a322aacffe31a58ffc663f9e19792bc9347d Mon Sep 17 00:00:00 2001 From: Max Inden Date: Tue, 28 Sep 2021 22:34:28 +0200 Subject: [PATCH 12/67] core/: Stage connection task in pending and established --- core/src/connection.rs | 39 +- core/src/connection/error.rs | 16 +- core/src/connection/manager.rs | 375 ++++++++++++-- core/src/connection/manager/task.rs | 82 ++-- core/src/connection/pool.rs | 541 ++++++++++++++------- core/src/connection/substream.rs | 3 +- core/src/network.rs | 48 +- core/src/network/concurrent_dial.rs | 22 +- core/src/network/peer.rs | 118 +++-- core/tests/connection_limits.rs | 6 +- core/tests/util.rs | 2 +- misc/metrics/src/swarm.rs | 4 + misc/multistream-select/tests/transport.rs | 2 +- protocols/rendezvous/examples/discover.rs | 11 - protocols/rendezvous/tests/harness/mod.rs | 3 - swarm/src/lib.rs | 5 +- 16 files changed, 915 insertions(+), 362 deletions(-) diff --git a/core/src/connection.rs b/core/src/connection.rs index 1c9c42561cb..92611516a7d 100644 --- a/core/src/connection.rs +++ b/core/src/connection.rs @@ -36,6 +36,7 @@ pub use substream::{Close, Substream, SubstreamEndpoint}; use crate::muxing::StreamMuxer; use crate::{Multiaddr, PeerId}; +use futures::stream::Stream; use std::hash::Hash; use std::{error::Error, fmt, pin::Pin, task::Context, task::Poll}; use substream::{Muxing, SubstreamEvent}; @@ -83,6 +84,21 @@ pub enum PendingPoint { }, } +impl From for PendingPoint { + fn from(endpoint: ConnectedPoint) -> Self { + match endpoint { + ConnectedPoint::Dialer { .. } => PendingPoint::Dialer, + ConnectedPoint::Listener { + local_addr, + send_back_addr, + } => PendingPoint::Listener { + local_addr, + send_back_addr, + }, + } + } +} + /// The endpoint roles associated with a peer-to-peer connection. #[derive(PartialEq, Eq, Debug, Clone, Hash)] pub enum ConnectedPoint { @@ -245,13 +261,18 @@ where pub fn close(self) -> (THandler, Close) { (self.handler, self.muxing.close().0) } +} + +impl Stream for Connection +where + TMuxer: StreamMuxer, + THandler: ConnectionHandler>, +{ + type Item = Result, ConnectionError>; /// Polls the connection for events produced by the associated handler /// as a result of I/O activity on the substream multiplexer. - pub fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll, ConnectionError>> { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { loop { let mut io_pending = false; @@ -271,9 +292,9 @@ where } Poll::Ready(Ok(SubstreamEvent::AddressChange(address))) => { self.handler.inject_address_change(&address); - return Poll::Ready(Ok(Event::AddressChange(address))); + return Poll::Ready(Some(Ok(Event::AddressChange(address)))); } - Poll::Ready(Err(err)) => return Poll::Ready(Err(ConnectionError::IO(err))), + Poll::Ready(Err(err)) => return Poll::Ready(Some(Err(ConnectionError::IO(err)))), } // Poll the handler for new events. @@ -287,9 +308,11 @@ where self.muxing.open_substream(user_data); } Poll::Ready(Ok(ConnectionHandlerEvent::Custom(event))) => { - return Poll::Ready(Ok(Event::Handler(event))); + return Poll::Ready(Some(Ok(Event::Handler(event)))); + } + Poll::Ready(Err(err)) => { + return Poll::Ready(Some(Err(ConnectionError::Handler(err)))) } - Poll::Ready(Err(err)) => return Poll::Ready(Err(ConnectionError::Handler(err))), } } } diff --git a/core/src/connection/error.rs b/core/src/connection/error.rs index 5488a357f03..0efc439ec91 100644 --- a/core/src/connection/error.rs +++ b/core/src/connection/error.rs @@ -30,10 +30,6 @@ pub enum ConnectionError { // TODO: Eventually this should also be a custom error? IO(io::Error), - /// The connection was dropped because the connection limit - /// for a peer has been reached. - ConnectionLimit(ConnectionLimit), - /// The connection handler produced an error. Handler(THandlerErr), } @@ -46,9 +42,6 @@ where match self { ConnectionError::IO(err) => write!(f, "Connection error: I/O error: {}", err), ConnectionError::Handler(err) => write!(f, "Connection error: Handler error: {}", err), - ConnectionError::ConnectionLimit(l) => { - write!(f, "Connection error: Connection limit: {}.", l) - } } } } @@ -61,7 +54,6 @@ where match self { ConnectionError::IO(err) => Some(err), ConnectionError::Handler(err) => Some(err), - ConnectionError::ConnectionLimit(..) => None, } } } @@ -77,6 +69,10 @@ pub enum PendingConnectionError { /// An error occurred while negotiating the transport protocol(s). TransportListen(TransportError), + /// The connection was dropped because the connection limit + /// for a peer has been reached. + ConnectionLimit(ConnectionLimit), + /// Pending connection attempt has been aborted. Aborted, @@ -112,6 +108,9 @@ where // ) todo!() } + PendingConnectionError::ConnectionLimit(l) => { + write!(f, "Connection error: Connection limit: {}.", l) + } PendingConnectionError::InvalidPeerId => { write!(f, "Pending connection: Invalid peer ID.") } @@ -131,6 +130,7 @@ where PendingConnectionError::TransportDial(err) => None, PendingConnectionError::InvalidPeerId => None, PendingConnectionError::Aborted => None, + PendingConnectionError::ConnectionLimit(..) => None, } } } diff --git a/core/src/connection/manager.rs b/core/src/connection/manager.rs index 51c08024202..8ea46f4e325 100644 --- a/core/src/connection/manager.rs +++ b/core/src/connection/manager.rs @@ -23,13 +23,20 @@ use super::{ Connected, ConnectedPoint, ConnectionError, ConnectionHandler, ConnectionLimit, IntoConnectionHandler, PendingConnectionError, Substream, }; -use crate::{muxing::StreamMuxer, Executor}; +use crate::{muxing::StreamMuxer, transport::TransportError, Executor, Multiaddr, PeerId}; use fnv::FnvHashMap; -use futures::{channel::mpsc, prelude::*, stream::FuturesUnordered}; +use futures::{ + channel::{mpsc, oneshot}, + future::{poll_fn, BoxFuture, Either}, + prelude::*, + ready, + stream::FuturesUnordered, +}; use std::{ - collections::hash_map, + collections::{hash_map, HashMap}, error, fmt, mem, pin::Pin, + sync::Arc, task::{Context, Poll}, }; use task::{Task, TaskId}; @@ -74,7 +81,10 @@ impl ConnectionId { } /// A connection `Manager` orchestrates the I/O of a set of connections. -pub struct Manager { +pub struct Manager { + pending_outgoing_tasks: HashMap>, + pending_incoming_tasks: HashMap>, + /// The tasks of the managed connections. /// /// Each managed connection is associated with a (background) task @@ -107,7 +117,7 @@ pub struct Manager { events_rx: mpsc::Receiver>, } -impl fmt::Debug for Manager { +impl fmt::Debug for Manager { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_map() .entries(self.tasks.iter().map(|(id, task)| (id, &task.state))) @@ -164,7 +174,7 @@ enum TaskState { /// Events produced by the [`Manager`]. #[derive(Debug)] -pub enum Event<'a, H: IntoConnectionHandler, TE> { +pub enum Event<'a, H: IntoConnectionHandler, TMuxer, TE> { /// A connection attempt has failed. PendingConnectionError { /// The connection ID. @@ -175,8 +185,6 @@ pub enum Event<'a, H: IntoConnectionHandler, TE> { id: ConnectionId, /// What happened. error: PendingConnectionError, - /// The handler that was supposed to handle the failed connection. - handler: H, }, /// An established connection has been closed. @@ -196,9 +204,19 @@ pub enum Event<'a, H: IntoConnectionHandler, TE> { }, /// A connection has been established. - ConnectionEstablished { - /// The entry associated with the new connection. - entry: EstablishedEntry<'a, THandlerInEvent>, + OutgoingConnectionEstablished { + id: ConnectionId, + peer_id: PeerId, + address: Multiaddr, + muxer: TMuxer, + errors: Vec<(Multiaddr, TransportError)>, + }, + + /// A connection has been established. + IncomingConnectionEstablished { + id: ConnectionId, + peer_id: PeerId, + muxer: TMuxer, }, /// A connection handler has produced an event. @@ -220,11 +238,13 @@ pub enum Event<'a, H: IntoConnectionHandler, TE> { }, } -impl Manager { +impl Manager { /// Creates a new connection manager. pub fn new(config: ManagerConfig) -> Self { let (tx, rx) = mpsc::channel(config.task_event_buffer_size); Self { + pending_outgoing_tasks: Default::default(), + pending_incoming_tasks: Default::default(), tasks: FnvHashMap::default(), next_task_id: TaskId(0), task_command_buffer_size: config.task_command_buffer_size, @@ -235,22 +255,208 @@ impl Manager { } } + fn next_task_id(&mut self) -> TaskId { + let task_id = self.next_task_id; + self.next_task_id.0 += 1; + + task_id + } + + fn spawn(&mut self, task: BoxFuture<'static, ()>) { + if let Some(executor) = &mut self.executor { + executor.exec(task); + } else { + self.local_spawns.push(task); + } + } + + pub fn add_closing(&mut self, muxer: TMuxer) + where + TMuxer: StreamMuxer + Send + Sync + 'static, + { + self.spawn( + poll_fn(move |cx| { + ready!(muxer.close(cx)); + // TODO: Send the result back to the manager. + Poll::Ready(()) + }) + .boxed(), + ); + } + + pub fn add_pending_incoming(&mut self, dial: TFut) -> ConnectionId + where + TFut: + Future>> + Send + 'static, + { + let task_id = self.next_task_id(); + + let (mut sender, receiver) = oneshot::channel(); + + self.pending_incoming_tasks + .insert(task_id, PendingIncomingTaskInfo { receiver }); + + self.spawn( + async move { + match futures::future::select( + poll_fn(|cx| sender.poll_canceled(cx)), + Box::pin(dial), + ) + .await + { + Either::Left((_, _)) => todo!(), + Either::Right((res, _)) => { + sender.send(res); + } + } + } + .boxed(), + ); + + ConnectionId(task_id) + } + + pub fn add_pending_outgoing( + &mut self, + dial: crate::network::concurrent_dial::ConcurrentDial, + ) -> ConnectionId { + let task_id = self.next_task_id(); + + let (mut sender, receiver) = oneshot::channel(); + + self.pending_outgoing_tasks + .insert(task_id, PendingOutgoingTaskInfo { receiver }); + + self.spawn( + async move { + match futures::future::select( + poll_fn(|cx| sender.poll_canceled(cx)), + Box::pin(dial), + ) + .await + { + Either::Left((_, _)) => todo!(), + Either::Right((res, _)) => { + sender.send(res); + } + } + } + .boxed(), + ); + + ConnectionId(task_id) + } + + pub fn add_established( + &mut self, + task_id: ConnectionId, + mut connection: crate::connection::Connection, + connected: Connected, + ) where + TMuxer: StreamMuxer + Send + Sync + 'static, + TMuxer::OutboundSubstream: Send + 'static, + H: IntoConnectionHandler + Send + 'static, + H::Handler: ConnectionHandler> + Send + 'static, + ::OutboundOpenInfo: Send + 'static, + { + let (command_sender, mut command_receiver) = mpsc::channel(self.task_command_buffer_size); + + self.tasks.insert( + task_id.0, + TaskInfo { + sender: command_sender, + state: TaskState::Established(connected), + }, + ); + + let mut events = self.events_tx.clone(); + + let task = async move { + loop { + match futures::future::select(command_receiver.next(), connection.next()).await { + Either::Left((Some(command), _)) => { + match command { + task::Command::NotifyHandler(event) => connection.inject_event(event), + task::Command::Close(error) => { + // Don't accept any further commands. + command_receiver.close(); + // Discard the event, if any, and start a graceful close. + let (handler, closing_muxer) = connection.close(); + todo!() + // this.state = State::Closing { + // handler, + // closing_muxer, + // error, + // }; + // continue 'poll; + } + } + } + + // The manager has disappeared; abort. + Either::Left((None, _)) => return, + + Either::Right((Some(event), _)) => { + match event { + Ok(super::Event::Handler(event)) => { + events + .send(task::Event::Notify { + id: task_id.0, + event, + }) + .await; + } + Ok(super::Event::AddressChange(new_address)) => { + events + .send(task::Event::AddressChange { + id: task_id.0, + new_address, + }) + .await; + } + Err(error) => { + // Don't accept any further commands. + command_receiver.close(); + let (handler, _closing_muxer) = connection.close(); + // Terminate the task with the error, dropping the connection. + events + .send(task::Event::Closed { + id: task_id.0, + error: Some(error), + handler, + }) + .await; + return; + } + } + } + Either::Right((None, _)) => { + // TODO: Double check this is correct. + unreachable!("Connection is an infinite stream"); + } + } + } + } + .boxed(); + + self.spawn(task); + } + /// Adds to the manager a future that tries to reach a node. /// /// This method spawns a task dedicated to resolving this future and /// processing the node's events. - pub fn add_pending(&mut self, future: F, handler: H) -> ConnectionId + pub fn add_pending(&mut self, future: F, handler: H) -> ConnectionId where TE: error::Error + Send + 'static, - M: StreamMuxer + Send + Sync + 'static, - M::OutboundSubstream: Send + 'static, - F: Future> + Send + 'static, + F: Future> + Send + 'static, + TMuxer: StreamMuxer + Send + Sync + 'static, + TMuxer::OutboundSubstream: Send + 'static, H: IntoConnectionHandler + Send + 'static, - H::Handler: ConnectionHandler> + Send + 'static, + H::Handler: ConnectionHandler> + Send + 'static, ::OutboundOpenInfo: Send + 'static, { - let task_id = self.next_task_id; - self.next_task_id.0 += 1; + let task_id = self.next_task_id(); let (tx, rx) = mpsc::channel(self.task_command_buffer_size); self.tasks.insert( @@ -268,21 +474,32 @@ impl Manager { future, handler, )); - if let Some(executor) = &mut self.executor { - executor.exec(task); - } else { - self.local_spawns.push(task); - } + + self.spawn(task); ConnectionId(task_id) } /// Gets an entry for a managed connection, if it exists. - pub fn entry(&mut self, id: ConnectionId) -> Option>> { + // TODO: Rework this. + pub fn entry(&mut self, id: ConnectionId) -> Option, TMuxer, TE>> { if let hash_map::Entry::Occupied(task) = self.tasks.entry(id.0) { Some(Entry::new(task)) } else { - None + if let hash_map::Entry::Occupied(entry) = self.pending_incoming_tasks.entry(id.0) { + Some(Entry::Pending(PendingEntry { + connection_id: id, + task: Either::Left(entry), + })) + } else if let hash_map::Entry::Occupied(entry) = self.pending_outgoing_tasks.entry(id.0) + { + Some(Entry::Pending(PendingEntry { + connection_id: id, + task: Either::Right(entry), + })) + } else { + None + } } } @@ -298,10 +515,53 @@ impl Manager { } /// Polls the manager for events relating to the managed connections. - pub fn poll<'a>(&'a mut self, cx: &mut Context<'_>) -> Poll> { + pub fn poll<'a>(&'a mut self, cx: &mut Context<'_>) -> Poll> { // Advance the content of `local_spawns`. while let Poll::Ready(Some(_)) = self.local_spawns.poll_next_unpin(cx) {} + // TODO: Iterating a hashmap each time is not ideal. Can we do better? Does it matter? + for (task_id, PendingOutgoingTaskInfo { receiver }) in + self.pending_outgoing_tasks.iter_mut() + { + match receiver.poll_unpin(cx) { + Poll::Ready(Ok(Ok((peer_id, address, muxer, errors)))) => { + return Poll::Ready(Event::OutgoingConnectionEstablished { + id: ConnectionId(*task_id), + peer_id, + address, + muxer, + errors, + }) + } + Poll::Ready(Ok(Err(errors))) => { + return Poll::Ready(Event::PendingConnectionError { + id: ConnectionId(*task_id), + error: PendingConnectionError::TransportDial(errors), + }) + } + Poll::Ready(Err(e)) => println!("{:?}", e), + Poll::Pending => {} + } + } + + // TODO: Iterating a hashmap each time is not ideal. Can we do better? Does it matter? + for (task_id, PendingIncomingTaskInfo { receiver }) in + self.pending_incoming_tasks.iter_mut() + { + match receiver.poll_unpin(cx) { + Poll::Ready(Ok(Ok((peer_id, muxer)))) => { + return Poll::Ready(Event::IncomingConnectionEstablished { + id: ConnectionId(*task_id), + peer_id, + muxer, + }) + } + Poll::Ready(Ok(Err(errors))) => todo!(), + Poll::Ready(Err(e)) => println!("{:?}", e), + Poll::Pending => {} + } + } + // Poll for the first event for which the manager still has a registered task, if any. let event = loop { match self.events_rx.poll_next_unpin(cx) { @@ -324,15 +584,17 @@ impl Manager { }, task::Event::Established { id: _, info } => { // (2) - task.get_mut().state = TaskState::Established(info); // (3) - Event::ConnectionEstablished { - entry: EstablishedEntry { task }, - } + // task.get_mut().state = TaskState::Established(info); // (3) + // Event::ConnectionEstablished { + // entry: EstablishedEntry { task }, + // } + + todo!() } task::Event::Failed { id, error, handler } => { let id = ConnectionId(id); let _ = task.remove(); - Event::PendingConnectionError { id, error, handler } + Event::PendingConnectionError { id, error } } task::Event::AddressChange { id: _, new_address } => { let (new, old) = if let TaskState::Established(c) = &mut task.get_mut().state { @@ -375,15 +637,15 @@ impl Manager { /// An entry for a connection in the manager. #[derive(Debug)] -pub enum Entry<'a, I> { - Pending(PendingEntry<'a, I>), +pub enum Entry<'a, I, TMuxer, TError> { + Pending(PendingEntry<'a, TMuxer, TError>), Established(EstablishedEntry<'a, I>), } -impl<'a, I> Entry<'a, I> { +impl<'a, I, TMuxer, TError> Entry<'a, I, TMuxer, TError> { fn new(task: hash_map::OccupiedEntry<'a, TaskId, TaskInfo>) -> Self { match &task.get().state { - TaskState::Pending => Entry::Pending(PendingEntry { task }), + TaskState::Pending => todo!(), TaskState::Established(_) => Entry::Established(EstablishedEntry { task }), } } @@ -468,21 +730,52 @@ impl<'a, I> EstablishedEntry<'a, I> { } } +#[derive(Debug)] +struct PendingOutgoingTaskInfo { + receiver: oneshot::Receiver< + Result< + ( + PeerId, + Multiaddr, + TMuxer, + Vec<(Multiaddr, TransportError)>, + ), + Vec<(Multiaddr, TransportError)>, + >, + >, +} + +#[derive(Debug)] +struct PendingIncomingTaskInfo { + receiver: oneshot::Receiver>>, +} + /// An entry for a managed connection that is currently being established /// (i.e. pending). #[derive(Debug)] -pub struct PendingEntry<'a, I> { - task: hash_map::OccupiedEntry<'a, TaskId, TaskInfo>, +pub struct PendingEntry<'a, TMuxer, TError> { + connection_id: ConnectionId, + task: Either< + hash_map::OccupiedEntry<'a, TaskId, PendingIncomingTaskInfo>, + hash_map::OccupiedEntry<'a, TaskId, PendingOutgoingTaskInfo>, + >, } -impl<'a, I> PendingEntry<'a, I> { +impl<'a, TMuxer, TError> PendingEntry<'a, TMuxer, TError> { /// Returns the connection id. pub fn id(&self) -> ConnectionId { - ConnectionId(*self.task.key()) + self.connection_id } /// Aborts the pending connection attempt. pub fn abort(self) { - self.task.remove(); + match self.task { + Either::Left(entry) => { + entry.remove(); + } + Either::Right(entry) => { + entry.remove(); + } + } } } diff --git a/core/src/connection/manager/task.rs b/core/src/connection/manager/task.rs index 5860bcc6c9d..027021497ee 100644 --- a/core/src/connection/manager/task.rs +++ b/core/src/connection/manager/task.rs @@ -301,40 +301,41 @@ where } } } else { - // Poll the connection for new events. - match Connection::poll(Pin::new(&mut connection), cx) { - Poll::Pending => { - this.state = State::Established { - connection, - event: None, - }; - return Poll::Pending; - } - Poll::Ready(Ok(connection::Event::Handler(event))) => { - this.state = State::Established { - connection, - event: Some(Event::Notify { id, event }), - }; - } - Poll::Ready(Ok(connection::Event::AddressChange(new_address))) => { - this.state = State::Established { - connection, - event: Some(Event::AddressChange { id, new_address }), - }; - } - Poll::Ready(Err(error)) => { - // Don't accept any further commands. - this.commands.get_mut().close(); - let (handler, _closing_muxer) = connection.close(); - // Terminate the task with the error, dropping the connection. - let event = Event::Closed { - id, - error: Some(error), - handler, - }; - this.state = State::Terminating(event); - } - } + todo!("can be removed") + // // Poll the connection for new events. + // match Connection::poll(Pin::new(&mut connection), cx) { + // Poll::Pending => { + // this.state = State::Established { + // connection, + // event: None, + // }; + // return Poll::Pending; + // } + // Poll::Ready(Ok(connection::Event::Handler(event))) => { + // this.state = State::Established { + // connection, + // event: Some(Event::Notify { id, event }), + // }; + // } + // Poll::Ready(Ok(connection::Event::AddressChange(new_address))) => { + // this.state = State::Established { + // connection, + // event: Some(Event::AddressChange { id, new_address }), + // }; + // } + // Poll::Ready(Err(error)) => { + // // Don't accept any further commands. + // this.commands.get_mut().close(); + // let (handler, _closing_muxer) = connection.close(); + // // Terminate the task with the error, dropping the connection. + // let event = Event::Closed { + // id, + // error: Some(error), + // handler, + // }; + // this.state = State::Terminating(event); + // } + // } } } @@ -346,12 +347,13 @@ where // Try to gracefully close the connection. match closing_muxer.poll_unpin(cx) { Poll::Ready(Ok(())) => { - let event = Event::Closed { - id: this.id, - error: error.map(ConnectionError::ConnectionLimit), - handler, - }; - this.state = State::Terminating(event); + todo!() + // let event = Event::Closed { + // id: this.id, + // error: error.map(ConnectionError::ConnectionLimit), + // handler, + // }; + // this.state = State::Terminating(event); } Poll::Ready(Err(e)) => { let event = Event::Closed { diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index 65218749da8..86fd64784e4 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -28,16 +28,20 @@ use crate::{ }, muxing::StreamMuxer, network::DialError, + transport::TransportError, ConnectedPoint, Multiaddr, PeerId, }; use either::Either; use fnv::FnvHashMap; use futures::prelude::*; use smallvec::SmallVec; -use std::{convert::TryFrom as _, error, fmt, num::NonZeroU32, task::Context, task::Poll}; +use std::{ + collections::HashMap, convert::TryFrom as _, error, fmt, num::NonZeroU32, task::Context, + task::Poll, +}; /// A connection `Pool` manages a set of connections for each peer. -pub struct Pool { +pub struct Pool { local_id: PeerId, /// The connection counter(s). @@ -47,17 +51,31 @@ pub struct Pool { /// established and pending connections. /// /// For every established connection there is a corresponding entry in `established`. - manager: Manager, + manager: Manager, /// The managed connections of each peer that are currently considered - /// established, as witnessed by the associated `ConnectedPoint`. + /// established, as witnessed the associated `ConnectedPoint`. established: FnvHashMap>, /// The pending connections that are currently being negotiated. - pending: FnvHashMap)>, + pending_outgoing_connection: HashMap>, + pending_incoming_connection: HashMap>, +} + +struct PendingOutgoingConnectionInfo { + peer_id: Option, + handler: THandler, +} + +struct PendingIncomingConnectionInfo { + peer_id: Option, + handler: THandler, + endpoint: ConnectedPoint, } -impl fmt::Debug for Pool { +impl fmt::Debug + for Pool +{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.debug_struct("Pool") .field("counters", &self.counters) @@ -65,10 +83,13 @@ impl fmt::Debug for Pool Unpin for Pool {} +impl Unpin + for Pool +{ +} /// Event that can happen on the `Pool`. -pub enum PoolEvent<'a, THandler: IntoConnectionHandler, TTransErr> { +pub enum PoolEvent<'a, THandler: IntoConnectionHandler, TMuxer, TTransErr> { /// A new connection has been established. ConnectionEstablished { connection: EstablishedConnection<'a, THandlerInEvent>, @@ -94,7 +115,7 @@ pub enum PoolEvent<'a, THandler: IntoConnectionHandler, TTransErr> { /// was closed by the local peer. error: Option>>, /// A reference to the pool that used to manage the connection. - pool: &'a mut Pool, + pool: &'a mut Pool, /// The remaining number of established connections to the same peer. num_established: u32, handler: THandler::Handler, @@ -114,7 +135,7 @@ pub enum PoolEvent<'a, THandler: IntoConnectionHandler, TTransErr> { /// The (expected) peer of the failed connection. peer: Option, /// A reference to the pool that managed the connection. - pool: &'a mut Pool, + pool: &'a mut Pool, }, /// A node has produced an event. @@ -136,8 +157,8 @@ pub enum PoolEvent<'a, THandler: IntoConnectionHandler, TTransErr> { }, } -impl<'a, THandler: IntoConnectionHandler, TTransErr> fmt::Debug - for PoolEvent<'a, THandler, TTransErr> +impl<'a, THandler: IntoConnectionHandler, TMuxer, TTransErr> fmt::Debug + for PoolEvent<'a, THandler, TMuxer, TTransErr> where TTransErr: fmt::Debug, { @@ -187,7 +208,9 @@ where } } -impl Pool { +impl + Pool +{ /// Creates a new empty `Pool`. pub fn new(local_id: PeerId, manager_config: ManagerConfig, limits: ConnectionLimits) -> Self { Pool { @@ -195,7 +218,8 @@ impl Pool { counters: ConnectionCounters::new(limits), manager: Manager::new(manager_config), established: Default::default(), - pending: Default::default(), + pending_outgoing_connection: Default::default(), + pending_incoming_connection: Default::default(), } } @@ -209,7 +233,7 @@ impl Pool { /// /// Returns an error if the limit of pending incoming connections /// has been reached. - pub fn add_incoming( + pub fn add_incoming( &mut self, future: TFut, handler: THandler, @@ -219,19 +243,31 @@ impl Pool { TFut: Future>> + Send + 'static, + // TODO: Bounds still needed here? THandler: IntoConnectionHandler + Send + 'static, THandler::Handler: ConnectionHandler> + Send + 'static, ::OutboundOpenInfo: Send + 'static, TTransErr: error::Error + Send + 'static, + // TODO: Still needed? TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send + 'static, { // TODO: This is a hack. Fix. let endpoint = info.clone().to_connected_point(); - let future = future.map_ok(|(peer_id, muxer)| (peer_id, endpoint, muxer)); // TODO: We loose the handler here. self.counters.check_max_pending_incoming()?; - Ok(self.add_pending(future, handler, info.to_pending_point(), None)) + + let id = self.manager.add_pending_incoming(future); + self.counters.inc_pending_incoming(); + self.pending_incoming_connection.insert( + id, + PendingIncomingConnectionInfo { + peer_id: None, + handler, + endpoint, + }, + ); + Ok(id) } /// Adds a pending outgoing connection to the pool in the form of a `Future` @@ -239,20 +275,13 @@ impl Pool { /// /// Returns an error if the limit of pending outgoing connections /// has been reached. - pub fn add_outgoing( + pub fn add_outgoing( &mut self, - future: TFut, + dial: crate::network::concurrent_dial::ConcurrentDial, handler: THandler, expected_peer_id: Option, ) -> Result> where - TFut: Future< - Output = Result< - (PeerId, ConnectedPoint, TMuxer), - PendingConnectionError, - >, - > + Send - + 'static, THandler: IntoConnectionHandler + Send + 'static, THandler::Handler: ConnectionHandler> + Send + 'static, ::OutboundOpenInfo: Send + 'static, @@ -263,60 +292,40 @@ impl Pool { if let Err(limit) = self.counters.check_max_pending_outgoing() { return Err(DialError::ConnectionLimit { limit, handler }); }; - Ok(self.add_pending(future, handler, PendingPoint::Dialer, expected_peer_id)) - } - /// Adds a pending connection to the pool in the form of a - /// `Future` that establishes and negotiates the connection. - fn add_pending( - &mut self, - future: TFut, - handler: THandler, - endpoint: PendingPoint, - peer: Option, - ) -> ConnectionId - where - TFut: Future< - Output = Result< - (PeerId, ConnectedPoint, TMuxer), - PendingConnectionError, - >, - > + Send - + 'static, - THandler: IntoConnectionHandler + Send + 'static, - THandler::Handler: ConnectionHandler> + Send + 'static, - ::OutboundOpenInfo: Send + 'static, - TTransErr: error::Error + Send + 'static, - TMuxer: StreamMuxer + Send + Sync + 'static, - TMuxer::OutboundSubstream: Send + 'static, - { - // Validate the received peer ID as the last step of the pending connection - // future, so that these errors can be raised before the `handler` is consumed - // by the background task, which happens when this future resolves to an - // "established" connection. - let future = future.and_then({ - let expected_peer = peer; - let local_id = self.local_id; - move |(peer_id, endpoint, muxer)| { - if let Some(peer) = expected_peer { - if peer != peer_id { - return future::err(PendingConnectionError::InvalidPeerId); - } - } - - if local_id == peer_id { - return future::err(PendingConnectionError::InvalidPeerId); - } - - let connected = Connected { peer_id, endpoint }; - future::ready(Ok((connected, muxer))) - } - }); - - let id = self.manager.add_pending(future, handler); - self.counters.inc_pending(&endpoint); - self.pending.insert(id, (endpoint, peer)); - id + // // Validate the received peer ID as the last step of the pending connection + // // future, so that these errors can be raised before the `handler` is consumed + // // by the background task, which happens when this future resolves to an + // // "established" connection. + // let future = future.and_then({ + // let expected_peer = peer; + // let local_id = self.local_id; + // move |(peer_id, endpoint, muxer)| { + // if let Some(peer) = expected_peer { + // if peer != peer_id { + // return future::err(PendingConnectionError::InvalidPeerId); + // } + // } + // + // if local_id == peer_id { + // return future::err(PendingConnectionError::InvalidPeerId); + // } + // + // let connected = Connected { peer_id, endpoint }; + // future::ready(Ok((connected, muxer))) + // } + // }); + + let id = self.manager.add_pending_outgoing(dial); + self.counters.inc_pending(&PendingPoint::Dialer); + self.pending_outgoing_connection.insert( + id, + PendingOutgoingConnectionInfo { + peer_id: expected_peer_id, + handler, + }, + ); + Ok(id) } /// Gets an entry representing a connection in the pool. @@ -325,7 +334,7 @@ impl Pool { pub fn get( &mut self, id: ConnectionId, - ) -> Option>> { + ) -> Option, THandler, TMuxer, TTransErr>> { match self.manager.entry(id) { Some(manager::Entry::Established(entry)) => { Some(PoolConnection::Established(EstablishedConnection { entry })) @@ -333,7 +342,11 @@ impl Pool { Some(manager::Entry::Pending(entry)) => { Some(PoolConnection::Pending(PendingConnection { entry, - pending: &mut self.pending, + // pending: &mut self.pending, + pending_incoming_connection: &mut self.pending_incoming_connection, + pending_outgoing_connection: &mut self.pending_outgoing_connection, + peer_id: todo!(), + endpoint: todo!(), counters: &mut self.counters, })) } @@ -356,14 +369,21 @@ impl Pool { pub fn get_outgoing( &mut self, id: ConnectionId, - ) -> Option>> { - match self.pending.get(&id) { - Some((PendingPoint::Dialer, _peer)) => match self.manager.entry(id) { - Some(manager::Entry::Pending(entry)) => Some(PendingConnection { - entry, - pending: &mut self.pending, - counters: &mut self.counters, - }), + ) -> Option> { + match self.pending_outgoing_connection.get(&id) { + Some(PendingOutgoingConnectionInfo { peer_id, .. }) => match self.manager.entry(id) { + Some(manager::Entry::Pending(entry)) => { + let peer_id = *peer_id; + Some(PendingConnection { + entry, + // pending: &mut self.pending, + pending_incoming_connection: &mut self.pending_incoming_connection, + pending_outgoing_connection: &mut self.pending_outgoing_connection, + peer_id, + endpoint: PendingPoint::Dialer, + counters: &mut self.counters, + }) + } _ => unreachable!("by consistency of `self.pending` with `self.manager`"), }, _ => None, @@ -397,17 +417,18 @@ impl Pool { } } - for (&id, (_endpoint, peer2)) in &self.pending { - if Some(peer) == peer2.as_ref() { - if let Some(manager::Entry::Pending(e)) = self.manager.entry(id) { - e.abort(); - } - } - } + todo!() + // for (&id, (_endpoint, peer2)) in &self.pending { + // if Some(peer) == peer2.as_ref() { + // if let Some(manager::Entry::Pending(e)) = self.manager.entry(id) { + // e.abort(); + // } + // } + // } } /// Counts the number of established connections to the given peer. - pub fn num_peer_established(&self, peer: &PeerId) -> u32 { + pub fn num_peer_established(&self, peer: PeerId) -> u32 { num_peer_established(&self.established, peer) } @@ -415,8 +436,13 @@ impl Pool { pub fn iter_peer_established<'a>( &'a mut self, peer: &PeerId, - ) -> EstablishedConnectionIter<'a, impl Iterator, THandler, TTransErr> - { + ) -> EstablishedConnectionIter< + 'a, + impl Iterator, + THandler, + TMuxer, + TTransErr, + > { let ids = self .iter_peer_established_info(peer) .map(|(id, _endpoint)| *id) @@ -465,15 +491,15 @@ impl Pool { } } - /// Returns an iterator over all pending connection IDs together - /// with associated endpoints and expected peer IDs in the pool. - pub fn iter_pending_info( - &self, - ) -> impl Iterator)> + '_ { - self.pending - .iter() - .map(|(id, (endpoint, info))| (id, endpoint, info)) - } + // /// Returns an iterator over all pending connection IDs together + // /// with associated endpoints and expected peer IDs in the pool. + // pub fn iter_pending_info( + // &self, + // ) -> impl Iterator)> + '_ { + // self.pending + // .iter() + // .map(|(id, (endpoint, info))| (id, endpoint, info)) + // } /// Returns an iterator over all connected peers, i.e. those that have /// at least one established connection in the pool. @@ -488,7 +514,14 @@ impl Pool { pub fn poll<'a>( &'a mut self, cx: &mut Context<'_>, - ) -> Poll> { + ) -> Poll> + where + TMuxer: StreamMuxer + Send + Sync + 'static, + THandler: IntoConnectionHandler + Send + 'static, + THandler::Handler: ConnectionHandler> + Send + 'static, + ::OutboundOpenInfo: Send + 'static, + TMuxer::OutboundSubstream: Send + 'static, + { // Poll the connection `Manager`. loop { let item = match self.manager.poll(cx) { @@ -497,17 +530,36 @@ impl Pool { }; match item { - manager::Event::PendingConnectionError { id, error, handler } => { - if let Some((endpoint, peer)) = self.pending.remove(&id) { + manager::Event::PendingConnectionError { id, error } => { + if let Some(PendingIncomingConnectionInfo { + peer_id, + handler, + endpoint, + }) = self.pending_incoming_connection.remove(&id) + { self.counters.dec_pending(&endpoint); return Poll::Ready(PoolEvent::PendingConnectionError { id, - endpoint, + endpoint: endpoint.into(), + error, + handler, + peer: peer_id, + pool: self, + }); + } else if let Some(PendingOutgoingConnectionInfo { peer_id, handler }) = + self.pending_outgoing_connection.remove(&id) + { + self.counters.dec_pending_outgoing(); + return Poll::Ready(PoolEvent::PendingConnectionError { + id, + endpoint: PendingPoint::Dialer, error, handler, - peer, + peer: peer_id, pool: self, }); + } else { + todo!() } } manager::Event::ConnectionClosed { @@ -537,46 +589,182 @@ impl Pool { handler, }); } - manager::Event::ConnectionEstablished { entry } => { - let id = entry.id(); - if let Some((endpoint, peer)) = self.pending.remove(&id) { + manager::Event::OutgoingConnectionEstablished { + id, + peer_id, + address, + muxer, + // TODO: Bubble these errors up. + errors, + } => { + if let Some(PendingOutgoingConnectionInfo { + peer_id: expected_peer_id, + handler, + }) = self.pending_outgoing_connection.remove(&id) + { + let endpoint = ConnectedPoint::Dialer { address }; self.counters.dec_pending(&endpoint); // Check general established connection limit. if let Err(e) = self.counters.check_max_established(&endpoint) { - entry.start_close(Some(e)); - continue; + self.manager.add_closing(muxer); + return Poll::Ready(PoolEvent::PendingConnectionError { + id, + endpoint: endpoint.into(), + error: PendingConnectionError::ConnectionLimit(e), + handler, + peer: Some(peer_id), + pool: self, + }); } // Check per-peer established connection limit. - let current = - num_peer_established(&self.established, &entry.connected().peer_id); + let current = num_peer_established(&self.established, peer_id); if let Err(e) = self.counters.check_max_established_per_peer(current) { - entry.start_close(Some(e)); - continue; + self.manager.add_closing(muxer); + return Poll::Ready(PoolEvent::PendingConnectionError { + id, + endpoint: endpoint.into(), + error: PendingConnectionError::ConnectionLimit(e), + handler, + peer: Some(peer_id), + pool: self, + }); } - // Peer ID checks must already have happened. See `add_pending`. - if cfg!(debug_assertions) { - if self.local_id == entry.connected().peer_id { - panic!("Unexpected local peer ID for remote."); + if let Some(peer) = expected_peer_id { + if peer != peer_id { + self.manager.add_closing(muxer); + return Poll::Ready(PoolEvent::PendingConnectionError { + id, + endpoint: endpoint.into(), + error: PendingConnectionError::InvalidPeerId, + handler, + peer: Some(peer_id), + pool: self, + }); } - if let Some(peer) = peer { - if peer != entry.connected().peer_id { - panic!("Unexpected peer ID mismatch."); - } + } + + if self.local_id == peer_id { + self.manager.add_closing(muxer); + return Poll::Ready(PoolEvent::PendingConnectionError { + id, + endpoint: endpoint.into(), + error: PendingConnectionError::InvalidPeerId, + handler, + peer: Some(peer_id), + pool: self, + }); + } + + // Add the connection to the pool. + let conns = self.established.entry(peer_id).or_default(); + let num_established = + NonZeroU32::new(u32::try_from(conns.len() + 1).unwrap()) + .expect("n + 1 is always non-zero; qed"); + self.counters.inc_established(&endpoint); + conns.insert(id, endpoint.clone()); + + let connected = Connected { + endpoint: endpoint.clone(), + peer_id, + }; + + let connection = + super::Connection::new(muxer, handler.into_handler(&connected)); + self.manager.add_established(id, connection, connected); + + match self.get(id) { + Some(PoolConnection::Established(connection)) => { + return Poll::Ready(PoolEvent::ConnectionEstablished { + connection, + num_established, + }) } + _ => unreachable!("since `entry` is an `EstablishedEntry`."), + } + } + } + manager::Event::IncomingConnectionEstablished { id, peer_id, muxer } => { + if let Some(PendingIncomingConnectionInfo { + peer_id: expected_peer_id, + handler, + endpoint, + }) = self.pending_incoming_connection.remove(&id) + { + self.counters.dec_pending(&endpoint); + + // Check general established connection limit. + if let Err(e) = self.counters.check_max_established(&endpoint) { + self.manager.add_closing(muxer); + return Poll::Ready(PoolEvent::PendingConnectionError { + id, + endpoint: endpoint.into(), + error: PendingConnectionError::ConnectionLimit(e), + handler, + peer: Some(peer_id), + pool: self, + }); + } + + // Check per-peer established connection limit. + let current = num_peer_established(&self.established, peer_id); + if let Err(e) = self.counters.check_max_established_per_peer(current) { + self.manager.add_closing(muxer); + return Poll::Ready(PoolEvent::PendingConnectionError { + id, + endpoint: endpoint.into(), + error: PendingConnectionError::ConnectionLimit(e), + handler, + peer: Some(peer_id), + pool: self, + }); + } + + if let Some(peer) = expected_peer_id { + if peer != peer_id { + self.manager.add_closing(muxer); + return Poll::Ready(PoolEvent::PendingConnectionError { + id, + endpoint: endpoint.into(), + error: PendingConnectionError::InvalidPeerId, + handler, + peer: Some(peer_id), + pool: self, + }); + } + } + + if self.local_id == peer_id { + self.manager.add_closing(muxer); + return Poll::Ready(PoolEvent::PendingConnectionError { + id, + endpoint: endpoint.into(), + error: PendingConnectionError::InvalidPeerId, + handler, + peer: Some(peer_id), + pool: self, + }); } // Add the connection to the pool. - let peer = entry.connected().peer_id; - let conns = self.established.entry(peer).or_default(); + let conns = self.established.entry(peer_id).or_default(); let num_established = NonZeroU32::new(u32::try_from(conns.len() + 1).unwrap()) .expect("n + 1 is always non-zero; qed"); - let endpoint = entry.connected().endpoint.clone(); self.counters.inc_established(&endpoint); - conns.insert(id, endpoint); + conns.insert(id, endpoint.clone()); + + let connected = Connected { + endpoint: endpoint.clone(), + peer_id, + }; + + let connection = + super::Connection::new(muxer, handler.into_handler(&connected)); + self.manager.add_established(id, connection, connected); + match self.get(id) { Some(PoolConnection::Established(connection)) => { return Poll::Ready(PoolEvent::ConnectionEstablished { @@ -631,50 +819,49 @@ impl Pool { } /// A connection in a [`Pool`]. -pub enum PoolConnection<'a, TInEvent> { - Pending(PendingConnection<'a, TInEvent>), +pub enum PoolConnection<'a, TInEvent, THandler, TMuxer, TError> { + Pending(PendingConnection<'a, THandler, TMuxer, TError>), Established(EstablishedConnection<'a, TInEvent>), } /// A pending connection in a pool. -pub struct PendingConnection<'a, TInEvent> { - entry: manager::PendingEntry<'a, TInEvent>, - pending: &'a mut FnvHashMap)>, +pub struct PendingConnection<'a, THandler, TMuxer, TError> { + entry: manager::PendingEntry<'a, TMuxer, TError>, + pending_outgoing_connection: + &'a mut HashMap>, + pending_incoming_connection: + &'a mut HashMap>, + peer_id: Option, + endpoint: PendingPoint, counters: &'a mut ConnectionCounters, } -impl PendingConnection<'_, TInEvent> { +impl PendingConnection<'_, THandler, TMuxer, TError> { /// Returns the local connection ID. pub fn id(&self) -> ConnectionId { - self.entry.id() + todo!() + // self.entry.id() } /// Returns the (expected) identity of the remote peer, if known. pub fn peer_id(&self) -> &Option { - &self - .pending - .get(&self.entry.id()) - .expect("`entry` is a pending entry") - .1 + &self.peer_id } /// Returns information about this endpoint of the connection. pub fn endpoint(&self) -> &PendingPoint { - &self - .pending - .get(&self.entry.id()) - .expect("`entry` is a pending entry") - .0 + &self.endpoint } /// Aborts the connection attempt, closing the connection. pub fn abort(self) { - let endpoint = self - .pending - .remove(&self.entry.id()) - .expect("`entry` is a pending entry") - .0; - self.counters.dec_pending(&endpoint); + if let Some(_) = self.pending_outgoing_connection.remove(&self.entry.id()) { + self.counters.dec_pending_outgoing(); + } else if let Some(_) = self.pending_incoming_connection.remove(&self.entry.id()) { + self.counters.dec_pending_incoming(); + } else { + todo!() + } self.entry.abort(); } } @@ -748,18 +935,20 @@ impl EstablishedConnection<'_, TInEvent> { } /// An iterator over established connections in a pool. -pub struct EstablishedConnectionIter<'a, I, THandler: IntoConnectionHandler, TTransErr> { - pool: &'a mut Pool, +pub struct EstablishedConnectionIter<'a, I, THandler: IntoConnectionHandler, TMuxer, TTransErr> { + pool: &'a mut Pool, ids: I, } // Note: Ideally this would be an implementation of `Iterator`, but that // requires GATs (cf. https://github.com/rust-lang/rust/issues/44265) and // a different definition of `Iterator`. -impl<'a, I, THandler: IntoConnectionHandler, TTransErr> - EstablishedConnectionIter<'a, I, THandler, TTransErr> +impl<'a, I, THandler: IntoConnectionHandler, TMuxer, TTransErr> + EstablishedConnectionIter<'a, I, THandler, TMuxer, TTransErr> where I: Iterator, + TTransErr: Send + 'static, + TMuxer: Send + 'static, { /// Obtains the next connection, if any. #[allow(clippy::should_implement_trait)] @@ -880,17 +1069,31 @@ impl ConnectionCounters { } } - fn dec_pending(&mut self, endpoint: &PendingPoint) { + fn inc_pending_incoming(&mut self) { + self.pending_incoming += 1; + } + + fn dec_pending(&mut self, endpoint: &ConnectedPoint) { match endpoint { - PendingPoint::Dialer => { + ConnectedPoint::Dialer { .. } => { self.pending_outgoing -= 1; } - PendingPoint::Listener { .. } => { + ConnectedPoint::Listener { .. } => { self.pending_incoming -= 1; } } } + // TODO: Still needed? + fn dec_pending_incoming(&mut self) { + self.pending_incoming -= 1; + } + + // TODO: Still needed? + fn dec_pending_outgoing(&mut self) { + self.pending_outgoing -= 1; + } + fn inc_established(&mut self, endpoint: &ConnectedPoint) { match endpoint { ConnectedPoint::Dialer { .. } => { @@ -921,16 +1124,16 @@ impl ConnectionCounters { Self::check(self.pending_incoming, self.limits.max_pending_incoming) } - fn check_max_established(&self, endpoint: &PendingPoint) -> Result<(), ConnectionLimit> { + fn check_max_established(&self, endpoint: &ConnectedPoint) -> Result<(), ConnectionLimit> { // Check total connection limit. Self::check(self.num_established(), self.limits.max_established_total)?; // Check incoming/outgoing connection limits match endpoint { - PendingPoint::Dialer => Self::check( + ConnectedPoint::Dialer { .. } => Self::check( self.established_outgoing, self.limits.max_established_outgoing, ), - PendingPoint::Listener { .. } => Self::check( + ConnectedPoint::Listener { .. } => Self::check( self.established_incoming, self.limits.max_established_incoming, ), @@ -954,9 +1157,9 @@ impl ConnectionCounters { /// Counts the number of established connections to the given peer. fn num_peer_established( established: &FnvHashMap>, - peer: &PeerId, + peer: PeerId, ) -> u32 { - established.get(peer).map_or(0, |conns| { + established.get(&peer).map_or(0, |conns| { u32::try_from(conns.len()).expect("Unexpectedly large number of connections for a peer.") }) } diff --git a/core/src/connection/substream.rs b/core/src/connection/substream.rs index 399b09b9f0a..1244d56230a 100644 --- a/core/src/connection/substream.rs +++ b/core/src/connection/substream.rs @@ -70,7 +70,8 @@ where /// Future that signals the remote that we have closed the connection. pub struct Close { /// Muxer to close. - muxer: Arc, + // TODO: pub needed? + pub muxer: Arc, } /// A successfully opened substream. diff --git a/core/src/network.rs b/core/src/network.rs index 287eda8675d..6c4952edb5c 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -18,7 +18,8 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -mod concurrent_dial; +// TODO: pub needed? +pub mod concurrent_dial; mod event; pub mod peer; @@ -52,7 +53,7 @@ use std::{ }; /// Implementation of `Stream` that handles the nodes. -pub struct Network +pub struct Network where TTrans: Transport, THandler: IntoConnectionHandler, @@ -64,7 +65,7 @@ where listeners: ListenersStream, /// The nodes currently active. - pool: Pool, + pool: Pool, /// The ongoing dialing attempts. /// @@ -81,7 +82,7 @@ where dialing: FnvHashMap>, } -impl fmt::Debug for Network +impl fmt::Debug for Network where TTrans: fmt::Debug + Transport, THandler: fmt::Debug + ConnectionHandler, @@ -96,17 +97,19 @@ where } } -impl Unpin for Network +impl Unpin for Network where TTrans: Transport, THandler: IntoConnectionHandler, { } -impl Network +impl Network where TTrans: Transport, + ::Error: Send + 'static, THandler: IntoConnectionHandler, + TMuxer: Send + 'static, { fn disconnect(&mut self, peer: &PeerId) { self.pool.disconnect(peer); @@ -114,10 +117,11 @@ where } } -impl Network +impl Network where TTrans: Transport + Clone + 'static, - TMuxer: StreamMuxer, + ::Error: Send + 'static, + TMuxer: StreamMuxer + Send + 'static, THandler: IntoConnectionHandler + Send + 'static, ::OutboundOpenInfo: Send, ::Error: error::Error + Send, @@ -226,15 +230,15 @@ where } // TODO: Clone needed? - let fut = concurrent_dial::ConcurrentDial::new( - self.transport().clone(), + self.pool.add_outgoing( + concurrent_dial::ConcurrentDial::new( + self.transport().clone(), + None, + vec![address.clone()], + ), + handler, None, - vec![address.clone()], ) - .map_err(|e| PendingConnectionError::TransportDial(e)) - .map_ok(|(peer_id, address, muxer)| (peer_id, ConnectedPoint::Dialer { address }, muxer)); - - self.pool.add_outgoing(fut, handler, None) // match self.transport().clone().dial(address.clone()) { // Ok(f) => { @@ -306,7 +310,7 @@ where } /// Obtains a view of a [`Peer`] with the given ID in the network. - pub fn peer(&mut self, peer_id: PeerId) -> Peer<'_, TTrans, THandler> { + pub fn peer(&mut self, peer_id: PeerId) -> Peer<'_, TTrans, THandler, TMuxer> { Peer::new(self, peer_id) } @@ -501,7 +505,7 @@ struct DialingOpts { // TODO: How about making this a method? fn dial_peer_impl( transport: TTrans, - pool: &mut Pool, + pool: &mut Pool, dialing: &mut FnvHashMap>, opts: DialingOpts, ) -> Result> @@ -529,11 +533,11 @@ where // } // }; - let fut = concurrent_dial::ConcurrentDial::new(transport, Some(opts.peer), opts.addresses) - .map_err(|e| PendingConnectionError::TransportDial(e)) - .map_ok(|(peer_id, address, muxer)| (peer_id, ConnectedPoint::Dialer { address }, muxer)); - - let result = pool.add_outgoing(fut, opts.handler, Some(opts.peer)); + let result = pool.add_outgoing( + concurrent_dial::ConcurrentDial::new(transport, Some(opts.peer), opts.addresses), + opts.handler, + Some(opts.peer), + ); // let result = match transport.dial(addr.clone()) { // Ok(fut) => { diff --git a/core/src/network/concurrent_dial.rs b/core/src/network/concurrent_dial.rs index aa3c9c20afc..ec831018e3f 100644 --- a/core/src/network/concurrent_dial.rs +++ b/core/src/network/concurrent_dial.rs @@ -42,7 +42,9 @@ use std::{ // TODO: Have a concurrency limit. -pub(crate) struct ConcurrentDial { +// TODO: pub needed? +pub struct ConcurrentDial { + // TODO: We could as well spawn each of these on a separate task. dials: FuturesUnordered)>>, errors: Vec<(Multiaddr, TransportError)>, } @@ -82,14 +84,26 @@ impl ConcurrentDial { } impl Future for ConcurrentDial { - type Output = Result<(PeerId, Multiaddr, TMuxer), Vec<(Multiaddr, TransportError)>>; + type Output = Result< + ( + PeerId, + Multiaddr, + TMuxer, + Vec<(Multiaddr, TransportError)>, + ), + Vec<(Multiaddr, TransportError)>, + >; fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { loop { match ready!(self.dials.poll_next_unpin(cx)) { - // TODO: What about self.errors? Sure we should loose these? Some((addr, Ok((peer_id, muxer)))) => { - return Poll::Ready(Ok((peer_id, addr, muxer))); + return Poll::Ready(Ok(( + peer_id, + addr, + muxer, + std::mem::replace(&mut self.errors, vec![]), + ))); } Some((addr, Err(e))) => { self.errors.push((addr, TransportError::Other(e))); diff --git a/core/src/network/peer.rs b/core/src/network/peer.rs index ec064df0e4b..78df243ec78 100644 --- a/core/src/network/peer.rs +++ b/core/src/network/peer.rs @@ -37,32 +37,34 @@ use std::{collections::hash_map, error, fmt}; /// > **Note**: In any state there may always be a pending incoming /// > connection attempt from the peer, however, the remote identity /// > of a peer is only known once a connection is fully established. -pub enum Peer<'a, TTrans, THandler> +pub enum Peer<'a, TTrans, THandler, TMuxer> where TTrans: Transport, THandler: IntoConnectionHandler, { /// At least one established connection exists to the peer. - Connected(ConnectedPeer<'a, TTrans, THandler>), + Connected(ConnectedPeer<'a, TTrans, THandler, TMuxer>), /// There is an ongoing dialing (i.e. outgoing connection) attempt /// to the peer. There may already be other established connections /// to the peer. - Dialing(DialingPeer<'a, TTrans, THandler>), + Dialing(DialingPeer<'a, TTrans, THandler, TMuxer>), /// There exists no established connection to the peer and there is /// currently no ongoing dialing (i.e. outgoing connection) attempt /// in progress. - Disconnected(DisconnectedPeer<'a, TTrans, THandler>), + Disconnected(DisconnectedPeer<'a, TTrans, THandler, TMuxer>), /// The peer represents the local node. Local, } -impl<'a, TTrans, THandler> fmt::Debug for Peer<'a, TTrans, THandler> +impl<'a, TTrans, THandler, TMuxer> fmt::Debug for Peer<'a, TTrans, THandler, TMuxer> where TTrans: Transport, + TTrans::Error: Send + 'static, THandler: IntoConnectionHandler, + TMuxer: Send + 'static, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match self { @@ -74,12 +76,14 @@ where } } -impl<'a, TTrans, THandler> Peer<'a, TTrans, THandler> +impl<'a, TTrans, THandler, TMuxer> Peer<'a, TTrans, THandler, TMuxer> where TTrans: Transport, + TTrans::Error: Send + 'static, THandler: IntoConnectionHandler, + TMuxer: Send + 'static, { - pub(super) fn new(network: &'a mut Network, peer_id: PeerId) -> Self { + pub(super) fn new(network: &'a mut Network, peer_id: PeerId) -> Self { if peer_id == network.local_peer_id { return Peer::Local; } @@ -95,20 +99,20 @@ where Self::disconnected(network, peer_id) } - fn disconnected(network: &'a mut Network, peer_id: PeerId) -> Self { + fn disconnected(network: &'a mut Network, peer_id: PeerId) -> Self { Peer::Disconnected(DisconnectedPeer { network, peer_id }) } - fn connected(network: &'a mut Network, peer_id: PeerId) -> Self { + fn connected(network: &'a mut Network, peer_id: PeerId) -> Self { Peer::Connected(ConnectedPeer { network, peer_id }) } - fn dialing(network: &'a mut Network, peer_id: PeerId) -> Self { + fn dialing(network: &'a mut Network, peer_id: PeerId) -> Self { Peer::Dialing(DialingPeer { network, peer_id }) } } -impl<'a, TTrans, TMuxer, THandler> Peer<'a, TTrans, THandler> +impl<'a, TTrans, TMuxer, THandler> Peer<'a, TTrans, THandler, TMuxer> where TTrans: Transport + Clone + 'static, TTrans::Error: Send + 'static, @@ -162,7 +166,7 @@ where self, addresses: I, handler: THandler, - ) -> Result<(ConnectionId, DialingPeer<'a, TTrans, THandler>), DialError> + ) -> Result<(ConnectionId, DialingPeer<'a, TTrans, THandler, TMuxer>), DialError> where I: IntoIterator, { @@ -186,7 +190,7 @@ where /// Converts the peer into a `ConnectedPeer`, if an established connection exists. /// /// Succeeds if the there is at least one established connection to the peer. - pub fn into_connected(self) -> Option> { + pub fn into_connected(self) -> Option> { match self { Peer::Connected(peer) => Some(peer), Peer::Dialing(peer) => peer.into_connected(), @@ -198,7 +202,7 @@ where /// Converts the peer into a `DialingPeer`, if a dialing attempt exists. /// /// Succeeds if the there is at least one pending outgoing connection to the peer. - pub fn into_dialing(self) -> Option> { + pub fn into_dialing(self) -> Option> { match self { Peer::Dialing(peer) => Some(peer), Peer::Connected(peer) => peer.into_dialing(), @@ -209,7 +213,7 @@ where /// Converts the peer into a `DisconnectedPeer`, if neither an established connection /// nor a dialing attempt exists. - pub fn into_disconnected(self) -> Option> { + pub fn into_disconnected(self) -> Option> { match self { Peer::Disconnected(peer) => Some(peer), _ => None, @@ -220,26 +224,28 @@ where /// The representation of a peer in a [`Network`] to whom at least /// one established connection exists. There may also be additional ongoing /// dialing attempts to the peer. -pub struct ConnectedPeer<'a, TTrans, THandler> +pub struct ConnectedPeer<'a, TTrans, THandler, TMuxer> where TTrans: Transport, THandler: IntoConnectionHandler, { - network: &'a mut Network, + network: &'a mut Network, peer_id: PeerId, } -impl<'a, TTrans, THandler> ConnectedPeer<'a, TTrans, THandler> +impl<'a, TTrans, THandler, TMuxer> ConnectedPeer<'a, TTrans, THandler, TMuxer> where TTrans: Transport, + ::Error: Send + 'static, THandler: IntoConnectionHandler, + TMuxer: Send + 'static, { pub fn id(&self) -> &PeerId { &self.peer_id } /// Returns the `ConnectedPeer` into a `Peer`. - pub fn into_peer(self) -> Peer<'a, TTrans, THandler> { + pub fn into_peer(self) -> Peer<'a, TTrans, THandler, TMuxer> { Peer::Connected(self) } @@ -253,7 +259,7 @@ where /// The number of established connections to the peer. pub fn num_connections(&self) -> u32 { - self.network.pool.num_peer_established(&self.peer_id) + self.network.pool.num_peer_established(self.peer_id) } /// Checks whether there is an ongoing dialing attempt to the peer. @@ -265,7 +271,7 @@ where /// Converts this peer into a [`DialingPeer`], if there is an ongoing /// dialing attempt, `None` otherwise. - pub fn into_dialing(self) -> Option> { + pub fn into_dialing(self) -> Option> { if self.network.dialing.contains_key(&self.peer_id) { Some(DialingPeer { network: self.network, @@ -279,8 +285,12 @@ where /// Gets an iterator over all established connections to the peer. pub fn connections( &mut self, - ) -> EstablishedConnectionIter, THandler, TTrans::Error> - { + ) -> EstablishedConnectionIter< + impl Iterator, + THandler, + TMuxer, + TTrans::Error, + > { self.network.pool.iter_peer_established(&self.peer_id) } @@ -292,7 +302,7 @@ where } /// Disconnects from the peer, closing all connections. - pub fn disconnect(self) -> DisconnectedPeer<'a, TTrans, THandler> { + pub fn disconnect(self) -> DisconnectedPeer<'a, TTrans, THandler, TMuxer> { self.network.disconnect(&self.peer_id); DisconnectedPeer { network: self.network, @@ -301,10 +311,12 @@ where } } -impl<'a, TTrans, THandler> fmt::Debug for ConnectedPeer<'a, TTrans, THandler> +impl<'a, TTrans, THandler, TMuxer> fmt::Debug for ConnectedPeer<'a, TTrans, THandler, TMuxer> where TTrans: Transport, + TTrans::Error: Send + 'static, THandler: IntoConnectionHandler, + TMuxer: Send + 'static, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.debug_struct("ConnectedPeer") @@ -321,32 +333,34 @@ where /// The representation of a peer in a [`Network`] to whom a dialing /// attempt is ongoing. There may already exist other established /// connections to this peer. -pub struct DialingPeer<'a, TTrans, THandler> +pub struct DialingPeer<'a, TTrans, THandler, TMuxer> where TTrans: Transport, THandler: IntoConnectionHandler, { - network: &'a mut Network, + network: &'a mut Network, peer_id: PeerId, } -impl<'a, TTrans, THandler> DialingPeer<'a, TTrans, THandler> +impl<'a, TTrans, THandler, TMuxer> DialingPeer<'a, TTrans, THandler, TMuxer> where TTrans: Transport, + TTrans::Error: Send + 'static, THandler: IntoConnectionHandler, + TMuxer: Send + 'static, { pub fn id(&self) -> &PeerId { &self.peer_id } /// Returns the `DialingPeer` into a `Peer`. - pub fn into_peer(self) -> Peer<'a, TTrans, THandler> { + pub fn into_peer(self) -> Peer<'a, TTrans, THandler, TMuxer> { Peer::Dialing(self) } /// Disconnects from this peer, closing all established connections and /// aborting all dialing attempts. - pub fn disconnect(self) -> DisconnectedPeer<'a, TTrans, THandler> { + pub fn disconnect(self) -> DisconnectedPeer<'a, TTrans, THandler, TMuxer> { self.network.disconnect(&self.peer_id); DisconnectedPeer { network: self.network, @@ -362,7 +376,7 @@ where } /// Converts the peer into a `ConnectedPeer`, if an established connection exists. - pub fn into_connected(self) -> Option> { + pub fn into_connected(self) -> Option> { if self.is_connected() { Some(ConnectedPeer { peer_id: self.peer_id, @@ -380,7 +394,7 @@ where pub fn attempt( &mut self, id: ConnectionId, - ) -> Option>> { + ) -> Option> { if let hash_map::Entry::Occupied(attempts) = self.network.dialing.entry(self.peer_id) { if let Some(pos) = attempts.get().iter().position(|s| *s == id) { if let Some(inner) = self.network.pool.get_outgoing(id) { @@ -396,7 +410,7 @@ where } /// Gets an iterator over all dialing (i.e. pending outgoing) connections to the peer. - pub fn attempts(&mut self) -> DialingAttemptIter<'_, THandler, TTrans::Error> { + pub fn attempts(&mut self) -> DialingAttemptIter<'_, THandler, TMuxer, TTrans::Error> { DialingAttemptIter::new( &self.peer_id, &mut self.network.pool, @@ -407,17 +421,19 @@ where /// Obtains some dialing connection to the peer. /// /// At least one dialing connection is guaranteed to exist on a `DialingPeer`. - pub fn some_attempt(&mut self) -> DialingAttempt<'_, THandlerInEvent> { + pub fn some_attempt(&mut self) -> DialingAttempt<'_, THandler, TMuxer, TTrans::Error> { self.attempts() .into_first() .expect("By `Peer::new` and the definition of `DialingPeer`.") } } -impl<'a, TTrans, THandler> fmt::Debug for DialingPeer<'a, TTrans, THandler> +impl<'a, TTrans, THandler, TMuxer> fmt::Debug for DialingPeer<'a, TTrans, THandler, TMuxer> where TTrans: Transport, + TTrans::Error: Send + 'static, THandler: IntoConnectionHandler, + TMuxer: Send + 'static, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.debug_struct("DialingPeer") @@ -434,16 +450,16 @@ where /// The representation of a peer to whom the `Network` has currently /// neither an established connection, nor an ongoing dialing attempt /// initiated by the local peer. -pub struct DisconnectedPeer<'a, TTrans, THandler> +pub struct DisconnectedPeer<'a, TTrans, THandler, TMuxer> where TTrans: Transport, THandler: IntoConnectionHandler, { peer_id: PeerId, - network: &'a mut Network, + network: &'a mut Network, } -impl<'a, TTrans, THandler> fmt::Debug for DisconnectedPeer<'a, TTrans, THandler> +impl<'a, TTrans, THandler, TMuxer> fmt::Debug for DisconnectedPeer<'a, TTrans, THandler, TMuxer> where TTrans: Transport, THandler: IntoConnectionHandler, @@ -455,7 +471,7 @@ where } } -impl<'a, TTrans, THandler> DisconnectedPeer<'a, TTrans, THandler> +impl<'a, TTrans, THandler, TMuxer> DisconnectedPeer<'a, TTrans, THandler, TMuxer> where TTrans: Transport, THandler: IntoConnectionHandler, @@ -465,7 +481,7 @@ where } /// Returns the `DisconnectedPeer` into a `Peer`. - pub fn into_peer(self) -> Peer<'a, TTrans, THandler> { + pub fn into_peer(self) -> Peer<'a, TTrans, THandler, TMuxer> { Peer::Disconnected(self) } } @@ -473,16 +489,16 @@ where /// A `DialingAttempt` is an ongoing outgoing connection attempt to /// a known / expected remote peer ID and a list of alternative addresses /// to connect to, if the current connection attempt fails. -pub struct DialingAttempt<'a, TInEvent> { +pub struct DialingAttempt<'a, THandler, TMuxer, TError> { /// The underlying pending connection in the `Pool`. - inner: PendingConnection<'a, TInEvent>, + inner: PendingConnection<'a, THandler, TMuxer, TError>, /// All current dialing attempts of the peer. attempts: hash_map::OccupiedEntry<'a, PeerId, SmallVec<[ConnectionId; 10]>>, /// The position of the current `ConnectionId` of this connection in the `attempts`. pos: usize, } -impl<'a, TInEvent> DialingAttempt<'a, TInEvent> { +impl<'a, THandler, TMuxer, TError> DialingAttempt<'a, THandler, TMuxer, TError> { /// Returns the ID of the current connection attempt. pub fn id(&self) -> ConnectionId { self.inner.id() @@ -516,11 +532,11 @@ impl<'a, TInEvent> DialingAttempt<'a, TInEvent> { } /// An iterator over the ongoing dialing attempts to a peer. -pub struct DialingAttemptIter<'a, THandler: IntoConnectionHandler, TTransErr> { +pub struct DialingAttemptIter<'a, THandler: IntoConnectionHandler, TMuxer, TTransErr> { /// The peer whose dialing attempts are being iterated. peer_id: &'a PeerId, /// The underlying connection `Pool` of the `Network`. - pool: &'a mut Pool, + pool: &'a mut Pool, /// The state of all current dialing attempts known to the `Network`. /// /// Ownership of the `OccupiedEntry` for `peer_id` containing all attempts must be @@ -536,10 +552,14 @@ pub struct DialingAttemptIter<'a, THandler: IntoConnectionHandler, TTransErr> { // Note: Ideally this would be an implementation of `Iterator`, but that // requires GATs (cf. https://github.com/rust-lang/rust/issues/44265) and // a different definition of `Iterator`. -impl<'a, THandler: IntoConnectionHandler, TTransErr> DialingAttemptIter<'a, THandler, TTransErr> { +impl<'a, THandler: IntoConnectionHandler, TMuxer, TTransErr: Send + 'static> + DialingAttemptIter<'a, THandler, TMuxer, TTransErr> +where + TMuxer: Send + 'static, +{ fn new( peer_id: &'a PeerId, - pool: &'a mut Pool, + pool: &'a mut Pool, dialing: &'a mut FnvHashMap>, ) -> Self { let end = dialing.get(peer_id).map_or(0, |conns| conns.len()); @@ -554,7 +574,7 @@ impl<'a, THandler: IntoConnectionHandler, TTransErr> DialingAttemptIter<'a, THan /// Obtains the next dialing connection, if any. #[allow(clippy::should_implement_trait)] - pub fn next(&mut self) -> Option>> { + pub fn next(&mut self) -> Option> { // If the number of elements reduced, the current `DialingAttempt` has been // aborted and iteration needs to continue from the previous position to // account for the removed element. @@ -588,7 +608,7 @@ impl<'a, THandler: IntoConnectionHandler, TTransErr> DialingAttemptIter<'a, THan } /// Returns the first connection, if any, consuming the iterator. - pub fn into_first<'b>(self) -> Option>> + pub fn into_first<'b>(self) -> Option> where 'a: 'b, { diff --git a/core/tests/connection_limits.rs b/core/tests/connection_limits.rs index 481b6019819..92dadb52a31 100644 --- a/core/tests/connection_limits.rs +++ b/core/tests/connection_limits.rs @@ -23,7 +23,7 @@ mod util; use futures::{future::poll_fn, ready}; use libp2p_core::multiaddr::{multiaddr, Multiaddr, Protocol}; use libp2p_core::{ - connection::ConnectionError, + connection::{ConnectionError, PendingConnectionError}, network::{ConnectionLimits, DialError, NetworkConfig, NetworkEvent}, PeerId, }; @@ -113,8 +113,8 @@ fn max_established_incoming() { network1.accept(connection, TestHandler()).unwrap(); } NetworkEvent::ConnectionEstablished { .. } => {} - NetworkEvent::ConnectionClosed { - error: Some(ConnectionError::ConnectionLimit(err)), + NetworkEvent::IncomingConnectionError { + error: PendingConnectionError::ConnectionLimit(err), .. } => { assert_eq!(err.limit, limit); diff --git a/core/tests/util.rs b/core/tests/util.rs index 0c175448336..3cf15aa2e02 100644 --- a/core/tests/util.rs +++ b/core/tests/util.rs @@ -13,7 +13,7 @@ use libp2p_noise as noise; use libp2p_tcp as tcp; use std::{io, pin::Pin, task::Context, task::Poll}; -type TestNetwork = Network; +type TestNetwork = Network; type TestTransport = transport::Boxed<(PeerId, StreamMuxerBox)>; /// Creates a new `TestNetwork` with a TCP transport. diff --git a/misc/metrics/src/swarm.rs b/misc/metrics/src/swarm.rs index 570f3485e1a..851ee06162b 100644 --- a/misc/metrics/src/swarm.rs +++ b/misc/metrics/src/swarm.rs @@ -309,6 +309,7 @@ enum PendingConnectionError { TransportErrorOther, Aborted, Io, + ConnectionLimit, } impl From<&libp2p_core::connection::PendingConnectionError> @@ -319,6 +320,9 @@ impl From<&libp2p_core::connection::PendingConnectionError libp2p_core::connection::PendingConnectionError::InvalidPeerId => { PendingConnectionError::InvalidPeerId } + libp2p_core::connection::PendingConnectionError::ConnectionLimit(_) => { + PendingConnectionError::ConnectionLimit + } libp2p_core::connection::PendingConnectionError::TransportListen( libp2p_core::transport::TransportError::MultiaddrNotSupported(_), ) => PendingConnectionError::TransportErrorMultiaddrNotSupported, diff --git a/misc/multistream-select/tests/transport.rs b/misc/multistream-select/tests/transport.rs index f42797f13ca..e76926767cd 100644 --- a/misc/multistream-select/tests/transport.rs +++ b/misc/multistream-select/tests/transport.rs @@ -37,7 +37,7 @@ use std::{ }; type TestTransport = transport::Boxed<(PeerId, StreamMuxerBox)>; -type TestNetwork = Network; +type TestNetwork = Network; fn mk_transport(up: upgrade::Version) -> (PeerId, TestTransport) { let keys = identity::Keypair::generate_ed25519(); diff --git a/protocols/rendezvous/examples/discover.rs b/protocols/rendezvous/examples/discover.rs index 9e60378b261..4466e1caaa0 100644 --- a/protocols/rendezvous/examples/discover.rs +++ b/protocols/rendezvous/examples/discover.rs @@ -76,17 +76,6 @@ async fn main() { rendezvous_point, ); } - SwarmEvent::UnreachableAddr { error, address, .. } - | SwarmEvent::UnknownPeerUnreachableAddr { error, address, .. } - if address == rendezvous_point_address => - { - log::error!( - "Failed to connect to rendezvous point at {}: {}", - address, - error - ); - return; - } SwarmEvent::Behaviour(MyEvent::Rendezvous(rendezvous::client::Event::Discovered { registrations, cookie: new_cookie, diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index 5747f7d19a6..1dce86210bf 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -155,9 +155,6 @@ where SwarmEvent::ConnectionEstablished { .. } => { dialer_done = true; } - SwarmEvent::UnknownPeerUnreachableAddr { address, error } if address == addr_to_dial => { - panic!("Failed to dial address {}: {}", addr_to_dial, error) - } other => { log::debug!("Ignoring {:?}", other); } diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index e38a7c67088..8aad4dc4b92 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -251,6 +251,7 @@ where network: Network< transport::Boxed<(PeerId, StreamMuxerBox)>, NodeHandlerWrapperBuilder>, + StreamMuxerBox, >, /// Handles which nodes to connect to and how to handle the events sent back by the protocol @@ -931,12 +932,13 @@ fn notify_one<'a, THandlerInEvent>( /// was successfully sent to a handler, in either case the event is consumed. fn notify_any<'a, TTrans, THandler, TBehaviour>( ids: SmallVec<[ConnectionId; 10]>, - peer: &mut ConnectedPeer<'a, TTrans, THandler>, + peer: &mut ConnectedPeer<'a, TTrans, THandler, StreamMuxerBox>, event: THandlerInEvent, cx: &mut Context<'_>, ) -> Option<(THandlerInEvent, SmallVec<[ConnectionId; 10]>)> where TTrans: Transport, + TTrans::Error: Send + 'static, TBehaviour: NetworkBehaviour, THandler: IntoConnectionHandler, THandler::Handler: ConnectionHandler< @@ -1216,6 +1218,7 @@ impl DialError { impl From> for DialError { fn from(error: PendingConnectionError) -> Self { match error { + PendingConnectionError::ConnectionLimit(limit) => DialError::ConnectionLimit(limit), PendingConnectionError::Aborted => DialError::Aborted, PendingConnectionError::InvalidPeerId => DialError::InvalidPeerId, PendingConnectionError::IO(e) => DialError::ConnectionIo(e), From eb86b41ced3cc97af420ced14e57d5bae182b2bc Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 30 Sep 2021 09:31:54 +0200 Subject: [PATCH 13/67] core/src/connection: Send pending result through events channel --- core/src/connection/manager.rs | 210 +++++++++++----------------- core/src/connection/manager/task.rs | 38 +++-- core/src/connection/pool.rs | 22 ++- core/src/network/peer.rs | 17 +-- 4 files changed, 126 insertions(+), 161 deletions(-) diff --git a/core/src/connection/manager.rs b/core/src/connection/manager.rs index 8ea46f4e325..b5e63bab037 100644 --- a/core/src/connection/manager.rs +++ b/core/src/connection/manager.rs @@ -26,17 +26,16 @@ use super::{ use crate::{muxing::StreamMuxer, transport::TransportError, Executor, Multiaddr, PeerId}; use fnv::FnvHashMap; use futures::{ - channel::{mpsc, oneshot}, + channel::mpsc, future::{poll_fn, BoxFuture, Either}, prelude::*, ready, stream::FuturesUnordered, }; use std::{ - collections::{hash_map, HashMap}, + collections::hash_map, error, fmt, mem, pin::Pin, - sync::Arc, task::{Context, Poll}, }; use task::{Task, TaskId}; @@ -82,9 +81,6 @@ impl ConnectionId { /// A connection `Manager` orchestrates the I/O of a set of connections. pub struct Manager { - pending_outgoing_tasks: HashMap>, - pending_incoming_tasks: HashMap>, - /// The tasks of the managed connections. /// /// Each managed connection is associated with a (background) task @@ -111,10 +107,10 @@ pub struct Manager { /// Sender distributed to managed tasks for reporting events back /// to the manager. - events_tx: mpsc::Sender>, + events_tx: mpsc::Sender>, /// Receiver for events reported from managed tasks. - events_rx: mpsc::Receiver>, + events_rx: mpsc::Receiver>, } impl fmt::Debug for Manager { @@ -243,8 +239,6 @@ impl Manag pub fn new(config: ManagerConfig) -> Self { let (tx, rx) = mpsc::channel(config.task_event_buffer_size); Self { - pending_outgoing_tasks: Default::default(), - pending_incoming_tasks: Default::default(), tasks: FnvHashMap::default(), next_task_id: TaskId(0), task_command_buffer_size: config.task_command_buffer_size, @@ -288,25 +282,35 @@ impl Manag where TFut: Future>> + Send + 'static, + + H: IntoConnectionHandler + Send + 'static, + H::Handler: ConnectionHandler + Send + 'static, { let task_id = self.next_task_id(); - let (mut sender, receiver) = oneshot::channel(); + let (sender, mut receiver) = mpsc::channel(self.task_command_buffer_size); + self.tasks.insert( + task_id, + TaskInfo { + sender, + state: TaskState::Pending, + }, + ); - self.pending_incoming_tasks - .insert(task_id, PendingIncomingTaskInfo { receiver }); + let mut events = self.events_tx.clone(); self.spawn( async move { - match futures::future::select( - poll_fn(|cx| sender.poll_canceled(cx)), - Box::pin(dial), - ) - .await - { + match futures::future::select(receiver.next(), Box::pin(dial)).await { + // TODO: The receiver has been dropped. We should never get a command here. Either::Left((_, _)) => todo!(), Either::Right((res, _)) => { - sender.send(res); + events + .send(task::Event::IncomingEstablished { + id: task_id, + result: res, + }) + .await; } } } @@ -319,25 +323,36 @@ impl Manag pub fn add_pending_outgoing( &mut self, dial: crate::network::concurrent_dial::ConcurrentDial, - ) -> ConnectionId { + ) -> ConnectionId + where + H: IntoConnectionHandler + Send + 'static, + H::Handler: ConnectionHandler + Send + 'static, + { let task_id = self.next_task_id(); - let (mut sender, receiver) = oneshot::channel(); + let (sender, mut receiver) = mpsc::channel(self.task_command_buffer_size); + + self.tasks.insert( + task_id, + TaskInfo { + sender, + state: TaskState::Pending, + }, + ); - self.pending_outgoing_tasks - .insert(task_id, PendingOutgoingTaskInfo { receiver }); + let mut events = self.events_tx.clone(); self.spawn( async move { - match futures::future::select( - poll_fn(|cx| sender.poll_canceled(cx)), - Box::pin(dial), - ) - .await - { + match futures::future::select(receiver.next(), Box::pin(dial)).await { Either::Left((_, _)) => todo!(), Either::Right((res, _)) => { - sender.send(res); + events + .send(task::Event::OutgoingEstablished { + id: task_id, + result: res, + }) + .await; } } } @@ -482,24 +497,11 @@ impl Manag /// Gets an entry for a managed connection, if it exists. // TODO: Rework this. - pub fn entry(&mut self, id: ConnectionId) -> Option, TMuxer, TE>> { + pub fn entry(&mut self, id: ConnectionId) -> Option>> { if let hash_map::Entry::Occupied(task) = self.tasks.entry(id.0) { Some(Entry::new(task)) } else { - if let hash_map::Entry::Occupied(entry) = self.pending_incoming_tasks.entry(id.0) { - Some(Entry::Pending(PendingEntry { - connection_id: id, - task: Either::Left(entry), - })) - } else if let hash_map::Entry::Occupied(entry) = self.pending_outgoing_tasks.entry(id.0) - { - Some(Entry::Pending(PendingEntry { - connection_id: id, - task: Either::Right(entry), - })) - } else { - None - } + None } } @@ -519,49 +521,6 @@ impl Manag // Advance the content of `local_spawns`. while let Poll::Ready(Some(_)) = self.local_spawns.poll_next_unpin(cx) {} - // TODO: Iterating a hashmap each time is not ideal. Can we do better? Does it matter? - for (task_id, PendingOutgoingTaskInfo { receiver }) in - self.pending_outgoing_tasks.iter_mut() - { - match receiver.poll_unpin(cx) { - Poll::Ready(Ok(Ok((peer_id, address, muxer, errors)))) => { - return Poll::Ready(Event::OutgoingConnectionEstablished { - id: ConnectionId(*task_id), - peer_id, - address, - muxer, - errors, - }) - } - Poll::Ready(Ok(Err(errors))) => { - return Poll::Ready(Event::PendingConnectionError { - id: ConnectionId(*task_id), - error: PendingConnectionError::TransportDial(errors), - }) - } - Poll::Ready(Err(e)) => println!("{:?}", e), - Poll::Pending => {} - } - } - - // TODO: Iterating a hashmap each time is not ideal. Can we do better? Does it matter? - for (task_id, PendingIncomingTaskInfo { receiver }) in - self.pending_incoming_tasks.iter_mut() - { - match receiver.poll_unpin(cx) { - Poll::Ready(Ok(Ok((peer_id, muxer)))) => { - return Poll::Ready(Event::IncomingConnectionEstablished { - id: ConnectionId(*task_id), - peer_id, - muxer, - }) - } - Poll::Ready(Ok(Err(errors))) => todo!(), - Poll::Ready(Err(e)) => println!("{:?}", e), - Poll::Pending => {} - } - } - // Poll for the first event for which the manager still has a registered task, if any. let event = loop { match self.events_rx.poll_next_unpin(cx) { @@ -578,6 +537,30 @@ impl Manag if let hash_map::Entry::Occupied(mut task) = self.tasks.entry(*event.id()) { Poll::Ready(match event { + // TODO: Make sure to remove the task. + task::Event::IncomingEstablished { id, result } => match result { + Ok((peer_id, muxer)) => Event::IncomingConnectionEstablished { + id: ConnectionId(id), + peer_id, + muxer, + }, + _ => todo!(), + }, + // TODO: Make sure to remove the task. + task::Event::OutgoingEstablished { id, result } => match result { + Ok((peer_id, address, muxer, errors)) => Event::OutgoingConnectionEstablished { + id: ConnectionId(id), + peer_id, + address, + muxer, + errors, + }, + Err(errors) => Event::PendingConnectionError { + id: ConnectionId(id), + error: PendingConnectionError::TransportDial(errors), + }, + _ => todo!(), + }, task::Event::Notify { id: _, event } => Event::ConnectionEvent { entry: EstablishedEntry { task }, event, @@ -637,15 +620,15 @@ impl Manag /// An entry for a connection in the manager. #[derive(Debug)] -pub enum Entry<'a, I, TMuxer, TError> { - Pending(PendingEntry<'a, TMuxer, TError>), +pub enum Entry<'a, I> { + Pending(PendingEntry<'a, I>), Established(EstablishedEntry<'a, I>), } -impl<'a, I, TMuxer, TError> Entry<'a, I, TMuxer, TError> { +impl<'a, I> Entry<'a, I> { fn new(task: hash_map::OccupiedEntry<'a, TaskId, TaskInfo>) -> Self { match &task.get().state { - TaskState::Pending => todo!(), + TaskState::Pending => Entry::Pending(PendingEntry { task }), TaskState::Established(_) => Entry::Established(EstablishedEntry { task }), } } @@ -730,52 +713,21 @@ impl<'a, I> EstablishedEntry<'a, I> { } } -#[derive(Debug)] -struct PendingOutgoingTaskInfo { - receiver: oneshot::Receiver< - Result< - ( - PeerId, - Multiaddr, - TMuxer, - Vec<(Multiaddr, TransportError)>, - ), - Vec<(Multiaddr, TransportError)>, - >, - >, -} - -#[derive(Debug)] -struct PendingIncomingTaskInfo { - receiver: oneshot::Receiver>>, -} - /// An entry for a managed connection that is currently being established /// (i.e. pending). #[derive(Debug)] -pub struct PendingEntry<'a, TMuxer, TError> { - connection_id: ConnectionId, - task: Either< - hash_map::OccupiedEntry<'a, TaskId, PendingIncomingTaskInfo>, - hash_map::OccupiedEntry<'a, TaskId, PendingOutgoingTaskInfo>, - >, +pub struct PendingEntry<'a, I> { + task: hash_map::OccupiedEntry<'a, TaskId, TaskInfo>, } -impl<'a, TMuxer, TError> PendingEntry<'a, TMuxer, TError> { +impl<'a, I> PendingEntry<'a, I> { /// Returns the connection id. pub fn id(&self) -> ConnectionId { - self.connection_id + ConnectionId(*self.task.key()) } /// Aborts the pending connection attempt. pub fn abort(self) { - match self.task { - Either::Left(entry) => { - entry.remove(); - } - Either::Right(entry) => { - entry.remove(); - } - } + self.task.remove(); } } diff --git a/core/src/connection/manager/task.rs b/core/src/connection/manager/task.rs index 027021497ee..d8377713312 100644 --- a/core/src/connection/manager/task.rs +++ b/core/src/connection/manager/task.rs @@ -27,7 +27,8 @@ use crate::{ IntoConnectionHandler, PendingConnectionError, Substream, }, muxing::StreamMuxer, - Multiaddr, + transport::TransportError, + Multiaddr, PeerId, }; use futures::{channel::mpsc, prelude::*, stream}; use std::{pin::Pin, task::Context, task::Poll}; @@ -48,13 +49,32 @@ pub enum Command { /// Events that a task can emit to its manager. #[derive(Debug)] -pub enum Event { +pub enum Event { + // TODO: Remove most of these /// A connection to a node has succeeded. Established { id: TaskId, info: Connected }, + IncomingEstablished { + id: TaskId, + // A result in a success message? + result: Result<(PeerId, TMuxer), PendingConnectionError>, + }, + OutgoingEstablished { + id: TaskId, + // A result in a success message? + result: Result< + ( + PeerId, + Multiaddr, + TMuxer, + Vec<(Multiaddr, TransportError)>, + ), + Vec<(Multiaddr, TransportError)>, + >, + }, /// A pending connection failed. Failed { id: TaskId, - error: PendingConnectionError, + error: PendingConnectionError, handler: H, }, /// A node we are connected to has changed its address. @@ -75,10 +95,12 @@ pub enum Event { }, } -impl Event { +impl Event { pub fn id(&self) -> &TaskId { match self { Event::Established { id, .. } => id, + Event::IncomingEstablished { id, .. } => id, + Event::OutgoingEstablished { id, .. } => id, Event::Failed { id, .. } => id, Event::AddressChange { id, .. } => id, Event::Notify { id, .. } => id, @@ -98,7 +120,7 @@ where id: TaskId, /// Sender to emit events to the manager of this task. - events: mpsc::Sender>, + events: mpsc::Sender>, /// Receiver for commands sent by the manager of this task. commands: stream::Fuse>>>, @@ -116,7 +138,7 @@ where /// Create a new task to connect and handle some node. pub fn pending( id: TaskId, - events: mpsc::Sender>, + events: mpsc::Sender>, commands: mpsc::Receiver>>, future: F, handler: H, @@ -156,7 +178,7 @@ where /// is polled for new events in this state, otherwise the event /// must be sent to the `Manager` before the connection can be /// polled again. - event: Option>, + event: Option>, }, /// The connection is closing (active close). @@ -167,7 +189,7 @@ where }, /// The task is terminating with a final event for the `Manager`. - Terminating(Event), + Terminating(Event), /// The task has finished. Done, diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index 86fd64784e4..c7146e6829b 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -331,10 +331,7 @@ impl Option, THandler, TMuxer, TTransErr>> { + pub fn get(&mut self, id: ConnectionId) -> Option> { match self.manager.entry(id) { Some(manager::Entry::Established(entry)) => { Some(PoolConnection::Established(EstablishedConnection { entry })) @@ -366,10 +363,7 @@ impl Option> { + pub fn get_outgoing(&mut self, id: ConnectionId) -> Option> { match self.pending_outgoing_connection.get(&id) { Some(PendingOutgoingConnectionInfo { peer_id, .. }) => match self.manager.entry(id) { Some(manager::Entry::Pending(entry)) => { @@ -819,14 +813,14 @@ impl { - Pending(PendingConnection<'a, THandler, TMuxer, TError>), - Established(EstablishedConnection<'a, TInEvent>), +pub enum PoolConnection<'a, THandler: IntoConnectionHandler> { + Pending(PendingConnection<'a, THandler>), + Established(EstablishedConnection<'a, THandlerInEvent>), } /// A pending connection in a pool. -pub struct PendingConnection<'a, THandler, TMuxer, TError> { - entry: manager::PendingEntry<'a, TMuxer, TError>, +pub struct PendingConnection<'a, THandler: IntoConnectionHandler> { + entry: manager::PendingEntry<'a, THandlerInEvent>, pending_outgoing_connection: &'a mut HashMap>, pending_incoming_connection: @@ -836,7 +830,7 @@ pub struct PendingConnection<'a, THandler, TMuxer, TError> { counters: &'a mut ConnectionCounters, } -impl PendingConnection<'_, THandler, TMuxer, TError> { +impl PendingConnection<'_, THandler> { /// Returns the local connection ID. pub fn id(&self) -> ConnectionId { todo!() diff --git a/core/src/network/peer.rs b/core/src/network/peer.rs index 78df243ec78..53b50cc3812 100644 --- a/core/src/network/peer.rs +++ b/core/src/network/peer.rs @@ -391,10 +391,7 @@ where /// the current connection attempt. // // TODO: Still needed? - pub fn attempt( - &mut self, - id: ConnectionId, - ) -> Option> { + pub fn attempt(&mut self, id: ConnectionId) -> Option> { if let hash_map::Entry::Occupied(attempts) = self.network.dialing.entry(self.peer_id) { if let Some(pos) = attempts.get().iter().position(|s| *s == id) { if let Some(inner) = self.network.pool.get_outgoing(id) { @@ -421,7 +418,7 @@ where /// Obtains some dialing connection to the peer. /// /// At least one dialing connection is guaranteed to exist on a `DialingPeer`. - pub fn some_attempt(&mut self) -> DialingAttempt<'_, THandler, TMuxer, TTrans::Error> { + pub fn some_attempt(&mut self) -> DialingAttempt<'_, THandler> { self.attempts() .into_first() .expect("By `Peer::new` and the definition of `DialingPeer`.") @@ -489,16 +486,16 @@ where /// A `DialingAttempt` is an ongoing outgoing connection attempt to /// a known / expected remote peer ID and a list of alternative addresses /// to connect to, if the current connection attempt fails. -pub struct DialingAttempt<'a, THandler, TMuxer, TError> { +pub struct DialingAttempt<'a, THandler: IntoConnectionHandler> { /// The underlying pending connection in the `Pool`. - inner: PendingConnection<'a, THandler, TMuxer, TError>, + inner: PendingConnection<'a, THandler>, /// All current dialing attempts of the peer. attempts: hash_map::OccupiedEntry<'a, PeerId, SmallVec<[ConnectionId; 10]>>, /// The position of the current `ConnectionId` of this connection in the `attempts`. pos: usize, } -impl<'a, THandler, TMuxer, TError> DialingAttempt<'a, THandler, TMuxer, TError> { +impl<'a, THandler: IntoConnectionHandler> DialingAttempt<'a, THandler> { /// Returns the ID of the current connection attempt. pub fn id(&self) -> ConnectionId { self.inner.id() @@ -574,7 +571,7 @@ where /// Obtains the next dialing connection, if any. #[allow(clippy::should_implement_trait)] - pub fn next(&mut self) -> Option> { + pub fn next(&mut self) -> Option> { // If the number of elements reduced, the current `DialingAttempt` has been // aborted and iteration needs to continue from the previous position to // account for the removed element. @@ -608,7 +605,7 @@ where } /// Returns the first connection, if any, consuming the iterator. - pub fn into_first<'b>(self) -> Option> + pub fn into_first<'b>(self) -> Option> where 'a: 'b, { From ae21f9e7fb7e37668a00074ee65e28ce9176599c Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 30 Sep 2021 19:09:51 +0200 Subject: [PATCH 14/67] core/src/connection: Handle connection error --- core/src/connection.rs | 1 + core/src/connection/manager.rs | 129 ++++++++++++++++++---------- core/src/connection/manager/task.rs | 78 ++++++++--------- core/src/connection/pool.rs | 4 +- core/src/network.rs | 1 + 5 files changed, 123 insertions(+), 90 deletions(-) diff --git a/core/src/connection.rs b/core/src/connection.rs index 92611516a7d..4224b8d5557 100644 --- a/core/src/connection.rs +++ b/core/src/connection.rs @@ -177,6 +177,7 @@ impl ConnectedPoint { } } +// TODO: Still needed? /// Information about a successfully established connection. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Connected { diff --git a/core/src/connection/manager.rs b/core/src/connection/manager.rs index b5e63bab037..a37a79e723b 100644 --- a/core/src/connection/manager.rs +++ b/core/src/connection/manager.rs @@ -271,7 +271,7 @@ impl Manag self.spawn( poll_fn(move |cx| { ready!(muxer.close(cx)); - // TODO: Send the result back to the manager. + // TODO: Should we send the result back to the manager? Poll::Ready(()) }) .boxed(), @@ -302,13 +302,33 @@ impl Manag self.spawn( async move { match futures::future::select(receiver.next(), Box::pin(dial)).await { - // TODO: The receiver has been dropped. We should never get a command here. - Either::Left((_, _)) => todo!(), - Either::Right((res, _)) => { + Either::Left((None, _)) => { + receiver.close(); + events + .send(task::Event::PendingFailed { + id: task_id, + error: PendingConnectionError::Aborted, + }) + .await; + } + Either::Left((Some(_), _)) => { + // TODO: We should never get a command here. + // TODO: Create a channel with Void. + } + Either::Right((Ok((peer_id, muxer)), _)) => { events .send(task::Event::IncomingEstablished { id: task_id, - result: res, + peer_id, + muxer, + }) + .await; + } + Either::Right((Err(e), _)) => { + events + .send(task::Event::PendingFailed { + id: task_id, + error: e, }) .await; } @@ -345,12 +365,36 @@ impl Manag self.spawn( async move { match futures::future::select(receiver.next(), Box::pin(dial)).await { - Either::Left((_, _)) => todo!(), - Either::Right((res, _)) => { + Either::Left((None, _)) => { + // TODO: Needed? + receiver.close(); + events + .send(task::Event::PendingFailed { + id: task_id, + error: PendingConnectionError::Aborted, + }) + .await; + } + Either::Left((Some(_), _)) => { + // TODO: We should never get a command here. + // TODO: Create a channel with Void. + } + Either::Right((Ok((peer_id, address, muxer, errors)), _)) => { events .send(task::Event::OutgoingEstablished { id: task_id, - result: res, + peer_id, + muxer, + address, + errors, + }) + .await; + } + Either::Right((Err(e), _)) => { + events + .send(task::Event::PendingFailed { + id: task_id, + error: PendingConnectionError::TransportDial(e), }) .await; } @@ -392,18 +436,22 @@ impl Manag Either::Left((Some(command), _)) => { match command { task::Command::NotifyHandler(event) => connection.inject_event(event), - task::Command::Close(error) => { + task::Command::Close => { // Don't accept any further commands. command_receiver.close(); // Discard the event, if any, and start a graceful close. let (handler, closing_muxer) = connection.close(); - todo!() - // this.state = State::Closing { - // handler, - // closing_muxer, - // error, - // }; - // continue 'poll; + + let error = closing_muxer.await; + events + .send(task::Event::Closed { + // TODO: Safe task id instead of connection id once. + id: task_id.0, + error: error.err().map(ConnectionError::IO), + handler, + }) + .await; + return; } } } @@ -538,43 +586,32 @@ impl Manag if let hash_map::Entry::Occupied(mut task) = self.tasks.entry(*event.id()) { Poll::Ready(match event { // TODO: Make sure to remove the task. - task::Event::IncomingEstablished { id, result } => match result { - Ok((peer_id, muxer)) => Event::IncomingConnectionEstablished { + task::Event::IncomingEstablished { id, peer_id, muxer } => { + Event::IncomingConnectionEstablished { id: ConnectionId(id), peer_id, muxer, - }, - _ => todo!(), - }, + } + } // TODO: Make sure to remove the task. - task::Event::OutgoingEstablished { id, result } => match result { - Ok((peer_id, address, muxer, errors)) => Event::OutgoingConnectionEstablished { - id: ConnectionId(id), - peer_id, - address, - muxer, - errors, - }, - Err(errors) => Event::PendingConnectionError { - id: ConnectionId(id), - error: PendingConnectionError::TransportDial(errors), - }, - _ => todo!(), + task::Event::OutgoingEstablished { + id, + peer_id, + address, + muxer, + errors, + } => Event::OutgoingConnectionEstablished { + id: ConnectionId(id), + peer_id, + address, + muxer, + errors, }, task::Event::Notify { id: _, event } => Event::ConnectionEvent { entry: EstablishedEntry { task }, event, }, - task::Event::Established { id: _, info } => { - // (2) - // task.get_mut().state = TaskState::Established(info); // (3) - // Event::ConnectionEstablished { - // entry: EstablishedEntry { task }, - // } - - todo!() - } - task::Event::Failed { id, error, handler } => { + task::Event::PendingFailed { id, error } => { let id = ConnectionId(id); let _ = task.remove(); Event::PendingConnectionError { id, error } @@ -684,7 +721,7 @@ impl<'a, I> EstablishedEntry<'a, I> { /// /// When the connection is ultimately closed, [`Event::ConnectionClosed`] /// is emitted by [`Manager::poll`]. - pub fn start_close(mut self, error: Option) { + pub fn start_close(mut self) { // Clone the sender so that we are guaranteed to have // capacity for the close command (every sender gets a slot). match self @@ -692,7 +729,7 @@ impl<'a, I> EstablishedEntry<'a, I> { .get_mut() .sender .clone() - .try_send(task::Command::Close(error)) + .try_send(task::Command::Close) { Ok(()) => {} Err(e) => assert!(e.is_disconnected(), "No capacity for close command."), diff --git a/core/src/connection/manager/task.rs b/core/src/connection/manager/task.rs index d8377713312..bd9f4338872 100644 --- a/core/src/connection/manager/task.rs +++ b/core/src/connection/manager/task.rs @@ -30,7 +30,7 @@ use crate::{ transport::TransportError, Multiaddr, PeerId, }; -use futures::{channel::mpsc, prelude::*, stream}; +use futures::{channel::mpsc, future::Either, prelude::*, stream}; use std::{pin::Pin, task::Context, task::Poll}; /// Identifier of a [`Task`] in a [`Manager`](super::Manager). @@ -44,38 +44,30 @@ pub enum Command { NotifyHandler(T), /// Gracefully close the connection (active close) before /// terminating the task. - Close(Option), + Close, } /// Events that a task can emit to its manager. #[derive(Debug)] pub enum Event { // TODO: Remove most of these - /// A connection to a node has succeeded. - Established { id: TaskId, info: Connected }, IncomingEstablished { id: TaskId, - // A result in a success message? - result: Result<(PeerId, TMuxer), PendingConnectionError>, + peer_id: PeerId, + muxer: TMuxer, }, OutgoingEstablished { id: TaskId, - // A result in a success message? - result: Result< - ( - PeerId, - Multiaddr, - TMuxer, - Vec<(Multiaddr, TransportError)>, - ), - Vec<(Multiaddr, TransportError)>, - >, + peer_id: PeerId, + muxer: TMuxer, + address: Multiaddr, + // TODO: Document errors in a success message. + errors: Vec<(Multiaddr, TransportError)>, }, /// A pending connection failed. - Failed { + PendingFailed { id: TaskId, error: PendingConnectionError, - handler: H, }, /// A node we are connected to has changed its address. AddressChange { id: TaskId, new_address: Multiaddr }, @@ -98,10 +90,9 @@ pub enum Event { impl Event { pub fn id(&self) -> &TaskId { match self { - Event::Established { id, .. } => id, Event::IncomingEstablished { id, .. } => id, Event::OutgoingEstablished { id, .. } => id, - Event::Failed { id, .. } => id, + Event::PendingFailed { id, .. } => id, Event::AddressChange { id, .. } => id, Event::Notify { id, .. } => id, Event::Closed { id, .. } => id, @@ -230,17 +221,18 @@ where match this.commands.poll_next_unpin(cx) { Poll::Pending => {} Poll::Ready(None) => { - // The manager has dropped the task; abort. - // Don't accept any further commands and terminate the - // task with a final event. - this.commands.get_mut().close(); - let event = Event::Failed { - id, - handler, - error: PendingConnectionError::Aborted, - }; - this.state = State::Terminating(event); - continue 'poll; + todo!() + // // The manager has dropped the task; abort. + // // Don't accept any further commands and terminate the + // // task with a final event. + // this.commands.get_mut().close(); + // let event = Event::PendingFailed { + // id, + // handler, + // error: PendingConnectionError::Aborted, + // }; + // this.state = State::Terminating(event); + // continue 'poll; } Poll::Ready(Some(_)) => { panic!("Task received command while the connection is pending.") @@ -249,21 +241,23 @@ where // Check if the connection succeeded. match future.poll_unpin(cx) { Poll::Ready(Ok((info, muxer))) => { - this.state = State::Established { - connection: Connection::new(muxer, handler.into_handler(&info)), - event: Some(Event::Established { id, info }), - } + todo!() + // this.state = State::Established { + // connection: Connection::new(muxer, handler.into_handler(&info)), + // event: Some(Event::Established { id, info }), + // } } Poll::Pending => { this.state = State::Pending { future, handler }; return Poll::Pending; } Poll::Ready(Err(error)) => { - // Don't accept any further commands and terminate the - // task with a final event. - this.commands.get_mut().close(); - let event = Event::Failed { id, handler, error }; - this.state = State::Terminating(event) + todo!() + // // Don't accept any further commands and terminate the + // // task with a final event. + // this.commands.get_mut().close(); + // let event = Event::Failed { id, handler, error }; + // this.state = State::Terminating(event) } } } @@ -279,7 +273,7 @@ where Poll::Ready(Some(Command::NotifyHandler(event))) => { connection.inject_event(event) } - Poll::Ready(Some(Command::Close(error))) => { + Poll::Ready(Some(Command::Close)) => { // Don't accept any further commands. this.commands.get_mut().close(); // Discard the event, if any, and start a graceful close. @@ -287,7 +281,7 @@ where this.state = State::Closing { handler, closing_muxer, - error, + error: None, }; continue 'poll; } diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index c7146e6829b..bb84daba1a8 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -406,7 +406,7 @@ impl EstablishedConnection<'_, TInEvent> { /// /// Has no effect if the connection is already closing. pub fn start_close(self) { - self.entry.start_close(None) + self.entry.start_close() } } diff --git a/core/src/network.rs b/core/src/network.rs index 6c4952edb5c..af7698f9dfd 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -333,6 +333,7 @@ where { let upgrade = connection .upgrade + // TODO: Why this map? .map_err(|err| PendingConnectionError::TransportListen(TransportError::Other(err))); let info = IncomingInfo { local_addr: &connection.local_addr, From 7d3114b0d7694dc4e89ace265c173cb42b4148f1 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 30 Sep 2021 19:27:49 +0200 Subject: [PATCH 15/67] core/src/connection: Use oneshot Void for pending connection --- core/src/connection/manager.rs | 206 ++++++++++++--------------------- 1 file changed, 71 insertions(+), 135 deletions(-) diff --git a/core/src/connection/manager.rs b/core/src/connection/manager.rs index a37a79e723b..c0452bd583e 100644 --- a/core/src/connection/manager.rs +++ b/core/src/connection/manager.rs @@ -20,13 +20,13 @@ use super::{ handler::{THandlerError, THandlerInEvent, THandlerOutEvent}, - Connected, ConnectedPoint, ConnectionError, ConnectionHandler, ConnectionLimit, - IntoConnectionHandler, PendingConnectionError, Substream, + Connected, ConnectedPoint, ConnectionError, ConnectionHandler, IntoConnectionHandler, + PendingConnectionError, Substream, }; use crate::{muxing::StreamMuxer, transport::TransportError, Executor, Multiaddr, PeerId}; use fnv::FnvHashMap; use futures::{ - channel::mpsc, + channel::{mpsc, oneshot}, future::{poll_fn, BoxFuture, Either}, prelude::*, ready, @@ -39,6 +39,7 @@ use std::{ task::{Context, Poll}, }; use task::{Task, TaskId}; +use void::Void; mod task; @@ -115,9 +116,7 @@ pub struct Manager { impl fmt::Debug for Manager { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_map() - .entries(self.tasks.iter().map(|(id, task)| (id, &task.state))) - .finish() + f.debug_map().entries(self.tasks.iter()).finish() } } @@ -152,20 +151,16 @@ impl Default for ManagerConfig { /// Contains the sender to deliver event messages to the task, and /// the associated user data. #[derive(Debug)] -struct TaskInfo { - /// Channel endpoint to send messages to the task. - sender: mpsc::Sender>, - /// The state of the task as seen by the `Manager`. - state: TaskState, -} - -/// Internal state of a running task as seen by the `Manager`. -#[derive(Debug, Clone, PartialEq, Eq)] -enum TaskState { - /// The connection is being established. - Pending, - /// The connection is established. - Established(Connected), +enum TaskInfo { + Pending { + // TODO: Document + drop_notifier: oneshot::Sender, + }, + Established { + connected: Connected, + /// Channel endpoint to send messages to the task. + sender: mpsc::Sender>, + }, } /// Events produced by the [`Manager`]. @@ -288,22 +283,16 @@ impl Manag { let task_id = self.next_task_id(); - let (sender, mut receiver) = mpsc::channel(self.task_command_buffer_size); - self.tasks.insert( - task_id, - TaskInfo { - sender, - state: TaskState::Pending, - }, - ); + let (drop_notifier, mut drop_receiver) = oneshot::channel(); + self.tasks + .insert(task_id, TaskInfo::Pending { drop_notifier }); let mut events = self.events_tx.clone(); self.spawn( async move { - match futures::future::select(receiver.next(), Box::pin(dial)).await { - Either::Left((None, _)) => { - receiver.close(); + match futures::future::select(drop_receiver, Box::pin(dial)).await { + Either::Left((Err(oneshot::Canceled), _)) => { events .send(task::Event::PendingFailed { id: task_id, @@ -311,10 +300,7 @@ impl Manag }) .await; } - Either::Left((Some(_), _)) => { - // TODO: We should never get a command here. - // TODO: Create a channel with Void. - } + Either::Left((Ok(v), _)) => void::unreachable(v), Either::Right((Ok((peer_id, muxer)), _)) => { events .send(task::Event::IncomingEstablished { @@ -350,24 +336,17 @@ impl Manag { let task_id = self.next_task_id(); - let (sender, mut receiver) = mpsc::channel(self.task_command_buffer_size); + let (drop_notifier, mut drop_receiver) = oneshot::channel(); - self.tasks.insert( - task_id, - TaskInfo { - sender, - state: TaskState::Pending, - }, - ); + self.tasks + .insert(task_id, TaskInfo::Pending { drop_notifier }); let mut events = self.events_tx.clone(); self.spawn( async move { - match futures::future::select(receiver.next(), Box::pin(dial)).await { - Either::Left((None, _)) => { - // TODO: Needed? - receiver.close(); + match futures::future::select(drop_receiver, Box::pin(dial)).await { + Either::Left((Err(oneshot::Canceled), _)) => { events .send(task::Event::PendingFailed { id: task_id, @@ -375,10 +354,7 @@ impl Manag }) .await; } - Either::Left((Some(_), _)) => { - // TODO: We should never get a command here. - // TODO: Create a channel with Void. - } + Either::Left((Ok(v), _)) => void::unreachable(v), Either::Right((Ok((peer_id, address, muxer, errors)), _)) => { events .send(task::Event::OutgoingEstablished { @@ -422,9 +398,9 @@ impl Manag self.tasks.insert( task_id.0, - TaskInfo { + TaskInfo::Established { sender: command_sender, - state: TaskState::Established(connected), + connected, }, ); @@ -505,46 +481,7 @@ impl Manag self.spawn(task); } - /// Adds to the manager a future that tries to reach a node. - /// - /// This method spawns a task dedicated to resolving this future and - /// processing the node's events. - pub fn add_pending(&mut self, future: F, handler: H) -> ConnectionId - where - TE: error::Error + Send + 'static, - F: Future> + Send + 'static, - TMuxer: StreamMuxer + Send + Sync + 'static, - TMuxer::OutboundSubstream: Send + 'static, - H: IntoConnectionHandler + Send + 'static, - H::Handler: ConnectionHandler> + Send + 'static, - ::OutboundOpenInfo: Send + 'static, - { - let task_id = self.next_task_id(); - - let (tx, rx) = mpsc::channel(self.task_command_buffer_size); - self.tasks.insert( - task_id, - TaskInfo { - sender: tx, - state: TaskState::Pending, - }, - ); - - let task = Box::pin(Task::pending( - task_id, - self.events_tx.clone(), - rx, - future, - handler, - )); - - self.spawn(task); - - ConnectionId(task_id) - } - /// Gets an entry for a managed connection, if it exists. - // TODO: Rework this. pub fn entry(&mut self, id: ConnectionId) -> Option>> { if let hash_map::Entry::Occupied(task) = self.tasks.entry(id.0) { Some(Entry::new(task)) @@ -555,13 +492,7 @@ impl Manag /// Checks whether an established connection with the given ID is currently managed. pub fn is_established(&self, id: &ConnectionId) -> bool { - matches!( - self.tasks.get(&id.0), - Some(TaskInfo { - state: TaskState::Established(..), - .. - }) - ) + matches!(self.tasks.get(&id.0), Some(TaskInfo::Established { .. })) } /// Polls the manager for events relating to the managed connections. @@ -617,16 +548,18 @@ impl Manag Event::PendingConnectionError { id, error } } task::Event::AddressChange { id: _, new_address } => { - let (new, old) = if let TaskState::Established(c) = &mut task.get_mut().state { - let mut new_endpoint = c.endpoint.clone(); - new_endpoint.set_remote_address(new_address); - let old_endpoint = mem::replace(&mut c.endpoint, new_endpoint.clone()); - (new_endpoint, old_endpoint) - } else { - unreachable!( + let (new, old) = + if let TaskInfo::Established { connected, .. } = &mut task.get_mut() { + let mut new_endpoint = connected.endpoint.clone(); + new_endpoint.set_remote_address(new_address); + let old_endpoint = + mem::replace(&mut connected.endpoint, new_endpoint.clone()); + (new_endpoint, old_endpoint) + } else { + unreachable!( "`Event::AddressChange` implies (2) occurred on that task and thus (3)." ) - }; + }; Event::AddressChange { entry: EstablishedEntry { task }, old_endpoint: old, @@ -635,15 +568,15 @@ impl Manag } task::Event::Closed { id, error, handler } => { let id = ConnectionId(id); - let task = task.remove(); - match task.state { - TaskState::Established(connected) => Event::ConnectionClosed { + match task.remove() { + TaskInfo::Established { sender, connected } => Event::ConnectionClosed { id, connected, error, handler, }, - TaskState::Pending => unreachable!( + TaskInfo::Pending { .. } => unreachable!( + // TODO: Reconsider these numbers. "`Event::Closed` implies (2) occurred on that task and thus (3)." ), } @@ -664,9 +597,9 @@ pub enum Entry<'a, I> { impl<'a, I> Entry<'a, I> { fn new(task: hash_map::OccupiedEntry<'a, TaskId, TaskInfo>) -> Self { - match &task.get().state { - TaskState::Pending => Entry::Pending(PendingEntry { task }), - TaskState::Established(_) => Entry::Established(EstablishedEntry { task }), + match &task.get() { + TaskInfo::Pending { .. } => Entry::Pending(PendingEntry { task }), + TaskInfo::Established { .. } => Entry::Established(EstablishedEntry { task }), } } } @@ -694,14 +627,15 @@ impl<'a, I> EstablishedEntry<'a, I> { /// > the connection handler not being ready at this time. pub fn notify_handler(&mut self, event: I) -> Result<(), I> { let cmd = task::Command::NotifyHandler(event); // (*) - self.task - .get_mut() - .sender - .try_send(cmd) - .map_err(|e| match e.into_inner() { - task::Command::NotifyHandler(event) => event, - _ => panic!("Unexpected command. Expected `NotifyHandler`"), // see (*) - }) + match self.task.get_mut() { + TaskInfo::Established { sender, .. } => { + sender.try_send(cmd).map_err(|e| match e.into_inner() { + task::Command::NotifyHandler(event) => event, + _ => panic!("Unexpected command. Expected `NotifyHandler`"), // see (*) + }) + } + TaskInfo::Pending { .. } => unreachable!("TODO"), + } } /// Checks if `notify_handler` is ready to accept an event. @@ -711,7 +645,10 @@ impl<'a, I> EstablishedEntry<'a, I> { /// Returns `Err(())` if the background task associated with the connection /// is terminating and the connection is about to close. pub fn poll_ready_notify_handler(&mut self, cx: &mut Context<'_>) -> Poll> { - self.task.get_mut().sender.poll_ready(cx).map_err(|_| ()) + match self.task.get_mut() { + TaskInfo::Established { sender, .. } => sender.poll_ready(cx).map_err(|_| ()), + TaskInfo::Pending { .. } => unreachable!("TODO"), + } } /// Sends a close command to the associated background task, @@ -724,23 +661,22 @@ impl<'a, I> EstablishedEntry<'a, I> { pub fn start_close(mut self) { // Clone the sender so that we are guaranteed to have // capacity for the close command (every sender gets a slot). - match self - .task - .get_mut() - .sender - .clone() - .try_send(task::Command::Close) - { - Ok(()) => {} - Err(e) => assert!(e.is_disconnected(), "No capacity for close command."), + match self.task.get_mut() { + TaskInfo::Established { sender, .. } => { + match sender.clone().try_send(task::Command::Close) { + Ok(()) => {} + Err(e) => assert!(e.is_disconnected(), "No capacity for close command."), + } + } + TaskInfo::Pending { .. } => unreachable!("TODO"), } } /// Obtains information about the established connection. pub fn connected(&self) -> &Connected { - match &self.task.get().state { - TaskState::Established(c) => c, - TaskState::Pending => unreachable!("By Entry::new()"), + match &self.task.get() { + TaskInfo::Established { connected, .. } => connected, + TaskInfo::Pending { .. } => unreachable!("By Entry::new()"), } } From c43496d6cc740bffc8d53e58dff39b323bb17cde Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 30 Sep 2021 19:39:22 +0200 Subject: [PATCH 16/67] core/src/connection: Remove task --- core/src/connection/manager.rs | 98 +++++++++++++++++----------------- 1 file changed, 48 insertions(+), 50 deletions(-) diff --git a/core/src/connection/manager.rs b/core/src/connection/manager.rs index c0452bd583e..9e87d33b8fe 100644 --- a/core/src/connection/manager.rs +++ b/core/src/connection/manager.rs @@ -153,7 +153,7 @@ impl Default for ManagerConfig { #[derive(Debug)] enum TaskInfo { Pending { - // TODO: Document + /// When dropped, notifies the task which can then terminate. drop_notifier: oneshot::Sender, }, Established { @@ -384,7 +384,7 @@ impl Manag pub fn add_established( &mut self, - task_id: ConnectionId, + connection_id: ConnectionId, mut connection: crate::connection::Connection, connected: Connected, ) where @@ -394,10 +394,11 @@ impl Manag H::Handler: ConnectionHandler> + Send + 'static, ::OutboundOpenInfo: Send + 'static, { + let task_id = connection_id.0; let (command_sender, mut command_receiver) = mpsc::channel(self.task_command_buffer_size); self.tasks.insert( - task_id.0, + task_id, TaskInfo::Established { sender: command_sender, connected, @@ -409,28 +410,23 @@ impl Manag let task = async move { loop { match futures::future::select(command_receiver.next(), connection.next()).await { - Either::Left((Some(command), _)) => { - match command { - task::Command::NotifyHandler(event) => connection.inject_event(event), - task::Command::Close => { - // Don't accept any further commands. - command_receiver.close(); - // Discard the event, if any, and start a graceful close. - let (handler, closing_muxer) = connection.close(); - - let error = closing_muxer.await; - events - .send(task::Event::Closed { - // TODO: Safe task id instead of connection id once. - id: task_id.0, - error: error.err().map(ConnectionError::IO), - handler, - }) - .await; - return; - } + Either::Left((Some(command), _)) => match command { + task::Command::NotifyHandler(event) => connection.inject_event(event), + task::Command::Close => { + command_receiver.close(); + let (handler, closing_muxer) = connection.close(); + + let error = closing_muxer.await.err().map(ConnectionError::IO); + events + .send(task::Event::Closed { + id: task_id, + error, + handler, + }) + .await; + return; } - } + }, // The manager has disappeared; abort. Either::Left((None, _)) => return, @@ -439,28 +435,24 @@ impl Manag match event { Ok(super::Event::Handler(event)) => { events - .send(task::Event::Notify { - id: task_id.0, - event, - }) + .send(task::Event::Notify { id: task_id, event }) .await; } Ok(super::Event::AddressChange(new_address)) => { events .send(task::Event::AddressChange { - id: task_id.0, + id: task_id, new_address, }) .await; } Err(error) => { - // Don't accept any further commands. command_receiver.close(); let (handler, _closing_muxer) = connection.close(); // Terminate the task with the error, dropping the connection. events .send(task::Event::Closed { - id: task_id.0, + id: task_id, error: Some(error), handler, }) @@ -470,7 +462,6 @@ impl Manag } } Either::Right((None, _)) => { - // TODO: Double check this is correct. unreachable!("Connection is an infinite stream"); } } @@ -516,28 +507,30 @@ impl Manag if let hash_map::Entry::Occupied(mut task) = self.tasks.entry(*event.id()) { Poll::Ready(match event { - // TODO: Make sure to remove the task. task::Event::IncomingEstablished { id, peer_id, muxer } => { + task.remove(); Event::IncomingConnectionEstablished { id: ConnectionId(id), peer_id, muxer, } } - // TODO: Make sure to remove the task. task::Event::OutgoingEstablished { id, peer_id, address, muxer, errors, - } => Event::OutgoingConnectionEstablished { - id: ConnectionId(id), - peer_id, - address, - muxer, - errors, - }, + } => { + task.remove(); + Event::OutgoingConnectionEstablished { + id: ConnectionId(id), + peer_id, + address, + muxer, + errors, + } + } task::Event::Notify { id: _, event } => Event::ConnectionEvent { entry: EstablishedEntry { task }, event, @@ -575,10 +568,9 @@ impl Manag error, handler, }, - TaskInfo::Pending { .. } => unreachable!( - // TODO: Reconsider these numbers. - "`Event::Closed` implies (2) occurred on that task and thus (3)." - ), + TaskInfo::Pending { .. } => { + unreachable!("`Event::Closed` is never emitted for pending connection.") + } } } }) @@ -626,15 +618,17 @@ impl<'a, I> EstablishedEntry<'a, I> { /// > task _may not be notified_ if sending the event fails due to /// > the connection handler not being ready at this time. pub fn notify_handler(&mut self, event: I) -> Result<(), I> { - let cmd = task::Command::NotifyHandler(event); // (*) + let cmd = task::Command::NotifyHandler(event); match self.task.get_mut() { TaskInfo::Established { sender, .. } => { sender.try_send(cmd).map_err(|e| match e.into_inner() { task::Command::NotifyHandler(event) => event, - _ => panic!("Unexpected command. Expected `NotifyHandler`"), // see (*) + _ => unreachable!("Expect `EstablishedEntry` to point to established task."), }) } - TaskInfo::Pending { .. } => unreachable!("TODO"), + TaskInfo::Pending { .. } => { + unreachable!("Expect `EstablishedEntry` to point to established task.") + } } } @@ -647,7 +641,9 @@ impl<'a, I> EstablishedEntry<'a, I> { pub fn poll_ready_notify_handler(&mut self, cx: &mut Context<'_>) -> Poll> { match self.task.get_mut() { TaskInfo::Established { sender, .. } => sender.poll_ready(cx).map_err(|_| ()), - TaskInfo::Pending { .. } => unreachable!("TODO"), + TaskInfo::Pending { .. } => { + unreachable!("Expect `EstablishedEntry` to point to established task.") + } } } @@ -668,7 +664,9 @@ impl<'a, I> EstablishedEntry<'a, I> { Err(e) => assert!(e.is_disconnected(), "No capacity for close command."), } } - TaskInfo::Pending { .. } => unreachable!("TODO"), + TaskInfo::Pending { .. } => { + unreachable!("Expect `EstablishedEntry` to point to established task.") + } } } From e06131c99119f35087f1bc3559809ce38fcc95da Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 30 Sep 2021 20:55:45 +0200 Subject: [PATCH 17/67] core/: Bubble dial errors on success up --- core/src/connection.rs | 1 + core/src/connection/pool.rs | 462 ++++++++++++++++---------------- core/src/network.rs | 13 +- core/src/network/event.rs | 26 +- core/tests/connection_limits.rs | 4 +- 5 files changed, 262 insertions(+), 244 deletions(-) diff --git a/core/src/connection.rs b/core/src/connection.rs index 4224b8d5557..cc28dd08f1b 100644 --- a/core/src/connection.rs +++ b/core/src/connection.rs @@ -74,6 +74,7 @@ impl Endpoint { } // TODO: Find better name. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum PendingPoint { Dialer, Listener { diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index bb84daba1a8..41f92d2e4c1 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -58,19 +58,13 @@ pub struct Pool { established: FnvHashMap>, /// The pending connections that are currently being negotiated. - pending_outgoing_connection: HashMap>, - pending_incoming_connection: HashMap>, + pending: HashMap>, } -struct PendingOutgoingConnectionInfo { +struct PendingConnectionInfo { peer_id: Option, handler: THandler, -} - -struct PendingIncomingConnectionInfo { - peer_id: Option, - handler: THandler, - endpoint: ConnectedPoint, + endpoint: PendingPoint, } impl fmt::Debug @@ -90,8 +84,15 @@ impl Unpin /// Event that can happen on the `Pool`. pub enum PoolEvent<'a, THandler: IntoConnectionHandler, TMuxer, TTransErr> { - /// A new connection has been established. - ConnectionEstablished { + /// A new outgoing connection has been established. + OutgoingConnectionEstablished { + connection: EstablishedConnection<'a, THandlerInEvent>, + num_established: NonZeroU32, + errors: Vec<(Multiaddr, TransportError)>, + }, + + /// A new incoming connection has been established. + IncomingConnectionEstablished { connection: EstablishedConnection<'a, THandlerInEvent>, num_established: NonZeroU32, }, @@ -164,8 +165,12 @@ where { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match *self { - PoolEvent::ConnectionEstablished { ref connection, .. } => f - .debug_tuple("PoolEvent::ConnectionEstablished") + PoolEvent::OutgoingConnectionEstablished { ref connection, .. } => f + .debug_tuple("PoolEvent::OutgoingConnectionEstablished") + .field(connection) + .finish(), + PoolEvent::IncomingConnectionEstablished { ref connection, .. } => f + .debug_tuple("PoolEvent::IncomingConnectionEstablished") .field(connection) .finish(), PoolEvent::ConnectionClosed { @@ -218,8 +223,7 @@ impl { Some(PoolConnection::Pending(PendingConnection { entry, - // pending: &mut self.pending, - pending_incoming_connection: &mut self.pending_incoming_connection, - pending_outgoing_connection: &mut self.pending_outgoing_connection, - peer_id: todo!(), - endpoint: todo!(), + pending: &mut self.pending, counters: &mut self.counters, })) } @@ -364,17 +365,17 @@ impl Option> { - match self.pending_outgoing_connection.get(&id) { - Some(PendingOutgoingConnectionInfo { peer_id, .. }) => match self.manager.entry(id) { + match self.pending.get(&id) { + Some(PendingConnectionInfo { + peer_id, + endpoint: PendingPoint::Dialer, + handler: _, + }) => match self.manager.entry(id) { Some(manager::Entry::Pending(entry)) => { let peer_id = *peer_id; Some(PendingConnection { entry, - // pending: &mut self.pending, - pending_incoming_connection: &mut self.pending_incoming_connection, - pending_outgoing_connection: &mut self.pending_outgoing_connection, - peer_id, - endpoint: PendingPoint::Dialer, + pending: &mut self.pending, counters: &mut self.counters, }) } @@ -525,36 +526,24 @@ impl { - if let Some(PendingIncomingConnectionInfo { + let PendingConnectionInfo { peer_id, handler, endpoint, - }) = self.pending_incoming_connection.remove(&id) - { - self.counters.dec_pending(&endpoint); - return Poll::Ready(PoolEvent::PendingConnectionError { - id, - endpoint: endpoint.into(), - error, - handler, - peer: peer_id, - pool: self, - }); - } else if let Some(PendingOutgoingConnectionInfo { peer_id, handler }) = - self.pending_outgoing_connection.remove(&id) - { - self.counters.dec_pending_outgoing(); - return Poll::Ready(PoolEvent::PendingConnectionError { - id, - endpoint: PendingPoint::Dialer, - error, - handler, - peer: peer_id, - pool: self, - }); - } else { - todo!() - } + } = self + .pending + .remove(&id) + .expect("Entry in `self.pending` for previously pending connection."); + + self.counters.dec_pending(&endpoint); + return Poll::Ready(PoolEvent::PendingConnectionError { + id, + endpoint, + error, + handler, + peer: peer_id, + pool: self, + }); } manager::Event::ConnectionClosed { id, @@ -591,183 +580,183 @@ impl { - if let Some(PendingOutgoingConnectionInfo { + let PendingConnectionInfo { peer_id: expected_peer_id, handler, - }) = self.pending_outgoing_connection.remove(&id) - { - let endpoint = ConnectedPoint::Dialer { address }; - self.counters.dec_pending(&endpoint); + endpoint, + } = self + .pending + .remove(&id) + .expect("Entry in `self.pending` for previously pending connection."); - // Check general established connection limit. - if let Err(e) = self.counters.check_max_established(&endpoint) { - self.manager.add_closing(muxer); - return Poll::Ready(PoolEvent::PendingConnectionError { - id, - endpoint: endpoint.into(), - error: PendingConnectionError::ConnectionLimit(e), - handler, - peer: Some(peer_id), - pool: self, - }); - } + assert_eq!(endpoint, PendingPoint::Dialer); + self.counters.dec_pending(&endpoint); - // Check per-peer established connection limit. - let current = num_peer_established(&self.established, peer_id); - if let Err(e) = self.counters.check_max_established_per_peer(current) { - self.manager.add_closing(muxer); - return Poll::Ready(PoolEvent::PendingConnectionError { - id, - endpoint: endpoint.into(), - error: PendingConnectionError::ConnectionLimit(e), - handler, - peer: Some(peer_id), - pool: self, - }); - } + let endpoint = ConnectedPoint::Dialer { address }; - if let Some(peer) = expected_peer_id { - if peer != peer_id { - self.manager.add_closing(muxer); - return Poll::Ready(PoolEvent::PendingConnectionError { - id, - endpoint: endpoint.into(), - error: PendingConnectionError::InvalidPeerId, - handler, - peer: Some(peer_id), - pool: self, - }); + let error = self + .counters + // Check general established connection limit. + .check_max_established(&endpoint) + .map_err(PendingConnectionError::ConnectionLimit) + // Check per-peer established connection limit. + .and_then(|()| { + self.counters + .check_max_established_per_peer(num_peer_established( + &self.established, + peer_id, + )) + .map_err(PendingConnectionError::ConnectionLimit) + }) + // Check expected peer id matches. + .and_then(|()| { + if let Some(peer) = expected_peer_id { + if peer != peer_id { + return Err(PendingConnectionError::InvalidPeerId); + } } - } + Ok(()) + }) + // Check peer is not local peer. + .and_then(|()| { + if self.local_id == peer_id { + Err(PendingConnectionError::InvalidPeerId) + } else { + Ok(()) + } + }); - if self.local_id == peer_id { - self.manager.add_closing(muxer); - return Poll::Ready(PoolEvent::PendingConnectionError { - id, - endpoint: endpoint.into(), - error: PendingConnectionError::InvalidPeerId, - handler, - peer: Some(peer_id), - pool: self, - }); - } + if let Err(error) = error { + self.manager.add_closing(muxer); + return Poll::Ready(PoolEvent::PendingConnectionError { + id, + endpoint: endpoint.into(), + error, + handler, + peer: Some(peer_id), + pool: self, + }); + } - // Add the connection to the pool. - let conns = self.established.entry(peer_id).or_default(); - let num_established = - NonZeroU32::new(u32::try_from(conns.len() + 1).unwrap()) - .expect("n + 1 is always non-zero; qed"); - self.counters.inc_established(&endpoint); - conns.insert(id, endpoint.clone()); - - let connected = Connected { - endpoint: endpoint.clone(), - peer_id, - }; + // Add the connection to the pool. + let conns = self.established.entry(peer_id).or_default(); + let num_established = NonZeroU32::new(u32::try_from(conns.len() + 1).unwrap()) + .expect("n + 1 is always non-zero; qed"); + self.counters.inc_established(&endpoint); + conns.insert(id, endpoint.clone()); - let connection = - super::Connection::new(muxer, handler.into_handler(&connected)); - self.manager.add_established(id, connection, connected); + let connected = Connected { + endpoint: endpoint.clone(), + peer_id, + }; - match self.get(id) { - Some(PoolConnection::Established(connection)) => { - return Poll::Ready(PoolEvent::ConnectionEstablished { - connection, - num_established, - }) - } - _ => unreachable!("since `entry` is an `EstablishedEntry`."), + let connection = + super::Connection::new(muxer, handler.into_handler(&connected)); + self.manager.add_established(id, connection, connected); + + match self.get(id) { + Some(PoolConnection::Established(connection)) => { + return Poll::Ready(PoolEvent::OutgoingConnectionEstablished { + connection, + num_established, + errors, + }) } + _ => unreachable!("since `entry` is an `EstablishedEntry`."), } } manager::Event::IncomingConnectionEstablished { id, peer_id, muxer } => { - if let Some(PendingIncomingConnectionInfo { + let PendingConnectionInfo { peer_id: expected_peer_id, handler, endpoint, - }) = self.pending_incoming_connection.remove(&id) - { - self.counters.dec_pending(&endpoint); + } = self + .pending + .remove(&id) + .expect("Entry in `self.pending` for previously pending connection."); + self.counters.dec_pending(&endpoint); + + let endpoint = match endpoint { + PendingPoint::Listener { + local_addr, + send_back_addr, + } => ConnectedPoint::Listener { + local_addr, + send_back_addr, + }, + PendingPoint::Dialer => unreachable!( + "Established incoming connection on pending outgoing connection." + ), + }; + let error = self + .counters // Check general established connection limit. - if let Err(e) = self.counters.check_max_established(&endpoint) { - self.manager.add_closing(muxer); - return Poll::Ready(PoolEvent::PendingConnectionError { - id, - endpoint: endpoint.into(), - error: PendingConnectionError::ConnectionLimit(e), - handler, - peer: Some(peer_id), - pool: self, - }); - } - + .check_max_established(&endpoint) + .map_err(PendingConnectionError::ConnectionLimit) // Check per-peer established connection limit. - let current = num_peer_established(&self.established, peer_id); - if let Err(e) = self.counters.check_max_established_per_peer(current) { - self.manager.add_closing(muxer); - return Poll::Ready(PoolEvent::PendingConnectionError { - id, - endpoint: endpoint.into(), - error: PendingConnectionError::ConnectionLimit(e), - handler, - peer: Some(peer_id), - pool: self, - }); - } - - if let Some(peer) = expected_peer_id { - if peer != peer_id { - self.manager.add_closing(muxer); - return Poll::Ready(PoolEvent::PendingConnectionError { - id, - endpoint: endpoint.into(), - error: PendingConnectionError::InvalidPeerId, - handler, - peer: Some(peer_id), - pool: self, - }); + .and_then(|()| { + self.counters + .check_max_established_per_peer(num_peer_established( + &self.established, + peer_id, + )) + .map_err(PendingConnectionError::ConnectionLimit) + }) + // Check expected peer id matches. + .and_then(|()| { + if let Some(peer) = expected_peer_id { + if peer != peer_id { + return Err(PendingConnectionError::InvalidPeerId); + } } - } + Ok(()) + }) + // Check peer is not local peer. + .and_then(|()| { + if self.local_id == peer_id { + Err(PendingConnectionError::InvalidPeerId) + } else { + Ok(()) + } + }); - if self.local_id == peer_id { - self.manager.add_closing(muxer); - return Poll::Ready(PoolEvent::PendingConnectionError { - id, - endpoint: endpoint.into(), - error: PendingConnectionError::InvalidPeerId, - handler, - peer: Some(peer_id), - pool: self, - }); - } + if let Err(error) = error { + self.manager.add_closing(muxer); + return Poll::Ready(PoolEvent::PendingConnectionError { + id, + endpoint: endpoint.into(), + error, + handler, + peer: Some(peer_id), + pool: self, + }); + } - // Add the connection to the pool. - let conns = self.established.entry(peer_id).or_default(); - let num_established = - NonZeroU32::new(u32::try_from(conns.len() + 1).unwrap()) - .expect("n + 1 is always non-zero; qed"); - self.counters.inc_established(&endpoint); - conns.insert(id, endpoint.clone()); - - let connected = Connected { - endpoint: endpoint.clone(), - peer_id, - }; + // Add the connection to the pool. + let conns = self.established.entry(peer_id).or_default(); + let num_established = NonZeroU32::new(u32::try_from(conns.len() + 1).unwrap()) + .expect("n + 1 is always non-zero; qed"); + self.counters.inc_established(&endpoint); + conns.insert(id, endpoint.clone()); - let connection = - super::Connection::new(muxer, handler.into_handler(&connected)); - self.manager.add_established(id, connection, connected); + let connected = Connected { + endpoint: endpoint.clone(), + peer_id, + }; - match self.get(id) { - Some(PoolConnection::Established(connection)) => { - return Poll::Ready(PoolEvent::ConnectionEstablished { - connection, - num_established, - }) - } - _ => unreachable!("since `entry` is an `EstablishedEntry`."), + let connection = + super::Connection::new(muxer, handler.into_handler(&connected)); + self.manager.add_established(id, connection, connected); + + match self.get(id) { + Some(PoolConnection::Established(connection)) => { + return Poll::Ready(PoolEvent::IncomingConnectionEstablished { + connection, + num_established, + }) } + _ => unreachable!("since `entry` is an `EstablishedEntry`."), } } manager::Event::ConnectionEvent { entry, event } => { @@ -821,41 +810,42 @@ pub enum PoolConnection<'a, THandler: IntoConnectionHandler> { /// A pending connection in a pool. pub struct PendingConnection<'a, THandler: IntoConnectionHandler> { entry: manager::PendingEntry<'a, THandlerInEvent>, - pending_outgoing_connection: - &'a mut HashMap>, - pending_incoming_connection: - &'a mut HashMap>, - peer_id: Option, - endpoint: PendingPoint, + pending: &'a mut HashMap>, counters: &'a mut ConnectionCounters, } impl PendingConnection<'_, THandler> { /// Returns the local connection ID. pub fn id(&self) -> ConnectionId { - todo!() - // self.entry.id() + self.entry.id() } /// Returns the (expected) identity of the remote peer, if known. pub fn peer_id(&self) -> &Option { - &self.peer_id + &self + .pending + .get(&self.entry.id()) + .expect("`entry` is a pending entry") + .peer_id } /// Returns information about this endpoint of the connection. pub fn endpoint(&self) -> &PendingPoint { - &self.endpoint + &self + .pending + .get(&self.entry.id()) + .expect("`entry` is a pending entry") + .endpoint } /// Aborts the connection attempt, closing the connection. pub fn abort(self) { - if let Some(_) = self.pending_outgoing_connection.remove(&self.entry.id()) { - self.counters.dec_pending_outgoing(); - } else if let Some(_) = self.pending_incoming_connection.remove(&self.entry.id()) { - self.counters.dec_pending_incoming(); - } else { - todo!() - } + let endpoint = self + .pending + .remove(&self.entry.id()) + .expect("`entry` is a pending entry") + .endpoint; + self.counters.dec_pending(&endpoint); self.entry.abort(); } } @@ -1067,26 +1057,26 @@ impl ConnectionCounters { self.pending_incoming += 1; } - fn dec_pending(&mut self, endpoint: &ConnectedPoint) { + fn dec_pending(&mut self, endpoint: &PendingPoint) { match endpoint { - ConnectedPoint::Dialer { .. } => { + PendingPoint::Dialer => { self.pending_outgoing -= 1; } - ConnectedPoint::Listener { .. } => { + PendingPoint::Listener { .. } => { self.pending_incoming -= 1; } } } // TODO: Still needed? - fn dec_pending_incoming(&mut self) { - self.pending_incoming -= 1; - } + // fn dec_pending_incoming(&mut self) { + // self.pending_incoming -= 1; + // } // TODO: Still needed? - fn dec_pending_outgoing(&mut self) { - self.pending_outgoing -= 1; - } + // fn dec_pending_outgoing(&mut self) { + // self.pending_outgoing -= 1; + // } fn inc_established(&mut self, endpoint: &ConnectedPoint) { match endpoint { diff --git a/core/src/network.rs b/core/src/network.rs index af7698f9dfd..09cd658c6d2 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -414,9 +414,10 @@ where // Poll the known peers. let event = match self.pool.poll(cx) { Poll::Pending => return Poll::Pending, - Poll::Ready(PoolEvent::ConnectionEstablished { + Poll::Ready(PoolEvent::OutgoingConnectionEstablished { connection, num_established, + errors, }) => { if let hash_map::Entry::Occupied(mut e) = self.dialing.entry(connection.peer_id()) { e.get_mut().retain(|s| *s != connection.id()); @@ -425,11 +426,19 @@ where } } - NetworkEvent::ConnectionEstablished { + NetworkEvent::OutgoingConnectionEstablished { connection, num_established, + errors, } } + Poll::Ready(PoolEvent::IncomingConnectionEstablished { + connection, + num_established, + }) => NetworkEvent::IncomingConnectionEstablished { + connection, + num_established, + }, Poll::Ready(PoolEvent::PendingConnectionError { id, endpoint, diff --git a/core/src/network/event.rs b/core/src/network/event.rs index 025b64e8457..fd586a5cfc1 100644 --- a/core/src/network/event.rs +++ b/core/src/network/event.rs @@ -25,7 +25,7 @@ use crate::{ Connected, ConnectedPoint, ConnectionError, ConnectionHandler, ConnectionId, EstablishedConnection, IntoConnectionHandler, ListenerId, PendingConnectionError, }, - transport::Transport, + transport::{Transport, TransportError}, Multiaddr, PeerId, }; use std::{fmt, num::NonZeroU32}; @@ -96,7 +96,7 @@ where }, /// A new connection to a peer has been established. - ConnectionEstablished { + IncomingConnectionEstablished { /// The newly established connection. connection: EstablishedConnection<'a, TInEvent>, /// The total number of established connections to the same peer, @@ -104,6 +104,17 @@ where num_established: NonZeroU32, }, + /// A new connection to a peer has been established. + OutgoingConnectionEstablished { + /// The newly established connection. + connection: EstablishedConnection<'a, TInEvent>, + /// The total number of established connections to the same peer, + /// including the one that has just been opened. + num_established: NonZeroU32, + // TODO: Document + errors: Vec<(Multiaddr, TransportError)>, + }, + /// An established connection to a peer has been closed. /// /// A connection may close if @@ -230,8 +241,15 @@ where .field("send_back_addr", send_back_addr) .field("error", error) .finish(), - NetworkEvent::ConnectionEstablished { connection, .. } => f - .debug_struct("ConnectionEstablished") + NetworkEvent::OutgoingConnectionEstablished { + connection, errors, .. + } => f + .debug_struct("OutgoingConnectionEstablished") + .field("connection", connection) + .field("errors", errors) + .finish(), + NetworkEvent::IncomingConnectionEstablished { connection, .. } => f + .debug_struct("IncomingConnectionEstablished") .field("connection", connection) .finish(), NetworkEvent::ConnectionClosed { diff --git a/core/tests/connection_limits.rs b/core/tests/connection_limits.rs index 92dadb52a31..63e6f78c407 100644 --- a/core/tests/connection_limits.rs +++ b/core/tests/connection_limits.rs @@ -112,7 +112,7 @@ fn max_established_incoming() { NetworkEvent::IncomingConnection { connection, .. } => { network1.accept(connection, TestHandler()).unwrap(); } - NetworkEvent::ConnectionEstablished { .. } => {} + NetworkEvent::IncomingConnectionEstablished { .. } => {} NetworkEvent::IncomingConnectionError { error: PendingConnectionError::ConnectionLimit(err), .. @@ -138,7 +138,7 @@ fn max_established_incoming() { poll_fn(|cx| { loop { match ready!(network2.poll(cx)) { - NetworkEvent::ConnectionEstablished { connection, .. } => { + NetworkEvent::OutgoingConnectionEstablished { connection, .. } => { n += 1; if n <= limit { // Dial again until the limit is exceeded. From 7c6a0fe9e3dd49f563e4bc191052dcac658a9a1a Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 30 Sep 2021 21:31:38 +0200 Subject: [PATCH 18/67] *: Pass dial success errors to behaviours --- core/src/connection/manager.rs | 20 +-- core/src/connection/pool.rs | 147 +++++---------------- core/src/network.rs | 15 +-- core/src/network/event.rs | 25 +--- core/tests/connection_limits.rs | 4 +- protocols/gossipsub/src/behaviour.rs | 1 + protocols/gossipsub/src/behaviour/tests.rs | 5 + protocols/identify/src/identify.rs | 1 + protocols/kad/src/behaviour.rs | 49 ++++++- protocols/kad/src/behaviour/test.rs | 2 +- protocols/relay/src/behaviour.rs | 1 + protocols/request-response/src/lib.rs | 1 + swarm-derive/src/lib.rs | 7 +- swarm/src/behaviour.rs | 10 +- swarm/src/lib.rs | 4 + swarm/src/test.rs | 10 +- swarm/src/toggle.rs | 3 +- 17 files changed, 138 insertions(+), 167 deletions(-) diff --git a/core/src/connection/manager.rs b/core/src/connection/manager.rs index 9e87d33b8fe..da397217c8b 100644 --- a/core/src/connection/manager.rs +++ b/core/src/connection/manager.rs @@ -195,19 +195,11 @@ pub enum Event<'a, H: IntoConnectionHandler, TMuxer, TE> { }, /// A connection has been established. - OutgoingConnectionEstablished { - id: ConnectionId, - peer_id: PeerId, - address: Multiaddr, - muxer: TMuxer, - errors: Vec<(Multiaddr, TransportError)>, - }, - - /// A connection has been established. - IncomingConnectionEstablished { + ConnectionEstablished { id: ConnectionId, peer_id: PeerId, muxer: TMuxer, + outgoing: Option<(Multiaddr, Vec<(Multiaddr, TransportError)>)>, }, /// A connection handler has produced an event. @@ -509,10 +501,11 @@ impl Manag Poll::Ready(match event { task::Event::IncomingEstablished { id, peer_id, muxer } => { task.remove(); - Event::IncomingConnectionEstablished { + Event::ConnectionEstablished { id: ConnectionId(id), peer_id, muxer, + outgoing: None, } } task::Event::OutgoingEstablished { @@ -523,12 +516,11 @@ impl Manag errors, } => { task.remove(); - Event::OutgoingConnectionEstablished { + Event::ConnectionEstablished { id: ConnectionId(id), peer_id, - address, muxer, - errors, + outgoing: Some((address, errors)), } } task::Event::Notify { id: _, event } => Event::ConnectionEvent { diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index 41f92d2e4c1..e5531eed95b 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -84,17 +84,11 @@ impl Unpin /// Event that can happen on the `Pool`. pub enum PoolEvent<'a, THandler: IntoConnectionHandler, TMuxer, TTransErr> { - /// A new outgoing connection has been established. - OutgoingConnectionEstablished { - connection: EstablishedConnection<'a, THandlerInEvent>, - num_established: NonZeroU32, - errors: Vec<(Multiaddr, TransportError)>, - }, - - /// A new incoming connection has been established. - IncomingConnectionEstablished { + /// A new connection has been established. + ConnectionEstablished { connection: EstablishedConnection<'a, THandlerInEvent>, num_established: NonZeroU32, + outgoing: Option)>>, }, /// An established connection was closed. @@ -165,13 +159,14 @@ where { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match *self { - PoolEvent::OutgoingConnectionEstablished { ref connection, .. } => f + PoolEvent::ConnectionEstablished { + ref connection, + ref outgoing, + .. + } => f .debug_tuple("PoolEvent::OutgoingConnectionEstablished") .field(connection) - .finish(), - PoolEvent::IncomingConnectionEstablished { ref connection, .. } => f - .debug_tuple("PoolEvent::IncomingConnectionEstablished") - .field(connection) + .field(outgoing) .finish(), PoolEvent::ConnectionClosed { ref id, @@ -572,13 +567,11 @@ impl { let PendingConnectionInfo { peer_id: expected_peer_id, @@ -589,103 +582,30 @@ impl { - return Poll::Ready(PoolEvent::OutgoingConnectionEstablished { - connection, - num_established, - errors, - }) + let (endpoint, dialing_errors) = match (endpoint, outgoing) { + (PendingPoint::Dialer, Some((address, errors))) => { + (ConnectedPoint::Dialer { address }, Some(errors)) } - _ => unreachable!("since `entry` is an `EstablishedEntry`."), - } - } - manager::Event::IncomingConnectionEstablished { id, peer_id, muxer } => { - let PendingConnectionInfo { - peer_id: expected_peer_id, - handler, - endpoint, - } = self - .pending - .remove(&id) - .expect("Entry in `self.pending` for previously pending connection."); - self.counters.dec_pending(&endpoint); - - let endpoint = match endpoint { - PendingPoint::Listener { - local_addr, - send_back_addr, - } => ConnectedPoint::Listener { - local_addr, - send_back_addr, - }, - PendingPoint::Dialer => unreachable!( - "Established incoming connection on pending outgoing connection." + ( + PendingPoint::Listener { + local_addr, + send_back_addr, + }, + None, + ) => ( + ConnectedPoint::Listener { + local_addr, + send_back_addr, + }, + None, + ), + (PendingPoint::Dialer, None) => unreachable!( + "Established incoming connection via pending outgoing connection." + ), + (PendingPoint::Listener { .. }, Some(_)) => unreachable!( + "Established outgoing connection via pending incoming connection." ), }; @@ -751,9 +671,10 @@ impl { - return Poll::Ready(PoolEvent::IncomingConnectionEstablished { + return Poll::Ready(PoolEvent::ConnectionEstablished { connection, num_established, + outgoing: dialing_errors, }) } _ => unreachable!("since `entry` is an `EstablishedEntry`."), diff --git a/core/src/network.rs b/core/src/network.rs index 09cd658c6d2..75ce6e39628 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -414,10 +414,10 @@ where // Poll the known peers. let event = match self.pool.poll(cx) { Poll::Pending => return Poll::Pending, - Poll::Ready(PoolEvent::OutgoingConnectionEstablished { + Poll::Ready(PoolEvent::ConnectionEstablished { connection, num_established, - errors, + outgoing, }) => { if let hash_map::Entry::Occupied(mut e) = self.dialing.entry(connection.peer_id()) { e.get_mut().retain(|s| *s != connection.id()); @@ -426,19 +426,12 @@ where } } - NetworkEvent::OutgoingConnectionEstablished { + NetworkEvent::ConnectionEstablished { connection, num_established, - errors, + outgoing, } } - Poll::Ready(PoolEvent::IncomingConnectionEstablished { - connection, - num_established, - }) => NetworkEvent::IncomingConnectionEstablished { - connection, - num_established, - }, Poll::Ready(PoolEvent::PendingConnectionError { id, endpoint, diff --git a/core/src/network/event.rs b/core/src/network/event.rs index fd586a5cfc1..69609238ad6 100644 --- a/core/src/network/event.rs +++ b/core/src/network/event.rs @@ -96,23 +96,14 @@ where }, /// A new connection to a peer has been established. - IncomingConnectionEstablished { - /// The newly established connection. - connection: EstablishedConnection<'a, TInEvent>, - /// The total number of established connections to the same peer, - /// including the one that has just been opened. - num_established: NonZeroU32, - }, - - /// A new connection to a peer has been established. - OutgoingConnectionEstablished { + ConnectionEstablished { /// The newly established connection. connection: EstablishedConnection<'a, TInEvent>, /// The total number of established connections to the same peer, /// including the one that has just been opened. num_established: NonZeroU32, // TODO: Document - errors: Vec<(Multiaddr, TransportError)>, + outgoing: Option)>>, }, /// An established connection to a peer has been closed. @@ -241,16 +232,14 @@ where .field("send_back_addr", send_back_addr) .field("error", error) .finish(), - NetworkEvent::OutgoingConnectionEstablished { - connection, errors, .. + NetworkEvent::ConnectionEstablished { + connection, + outgoing, + .. } => f .debug_struct("OutgoingConnectionEstablished") .field("connection", connection) - .field("errors", errors) - .finish(), - NetworkEvent::IncomingConnectionEstablished { connection, .. } => f - .debug_struct("IncomingConnectionEstablished") - .field("connection", connection) + .field("outgoing", outgoing) .finish(), NetworkEvent::ConnectionClosed { id, diff --git a/core/tests/connection_limits.rs b/core/tests/connection_limits.rs index 63e6f78c407..92dadb52a31 100644 --- a/core/tests/connection_limits.rs +++ b/core/tests/connection_limits.rs @@ -112,7 +112,7 @@ fn max_established_incoming() { NetworkEvent::IncomingConnection { connection, .. } => { network1.accept(connection, TestHandler()).unwrap(); } - NetworkEvent::IncomingConnectionEstablished { .. } => {} + NetworkEvent::ConnectionEstablished { .. } => {} NetworkEvent::IncomingConnectionError { error: PendingConnectionError::ConnectionLimit(err), .. @@ -138,7 +138,7 @@ fn max_established_incoming() { poll_fn(|cx| { loop { match ready!(network2.poll(cx)) { - NetworkEvent::OutgoingConnectionEstablished { connection, .. } => { + NetworkEvent::ConnectionEstablished { connection, .. } => { n += 1; if n <= limit { // Dial again until the limit is exceeded. diff --git a/protocols/gossipsub/src/behaviour.rs b/protocols/gossipsub/src/behaviour.rs index 65c551a2f3c..9c2feb77765 100644 --- a/protocols/gossipsub/src/behaviour.rs +++ b/protocols/gossipsub/src/behaviour.rs @@ -2927,6 +2927,7 @@ where peer_id: &PeerId, connection_id: &ConnectionId, endpoint: &ConnectedPoint, + _: Option<&Vec>, ) { // Check if the peer is an outbound peer if let ConnectedPoint::Dialer { .. } = endpoint { diff --git a/protocols/gossipsub/src/behaviour/tests.rs b/protocols/gossipsub/src/behaviour/tests.rs index 5794f2e0054..d22ce8a3e6d 100644 --- a/protocols/gossipsub/src/behaviour/tests.rs +++ b/protocols/gossipsub/src/behaviour/tests.rs @@ -195,6 +195,7 @@ mod tests { send_back_addr: address, } }, + None, ); as NetworkBehaviour>::inject_connected(gs, &peer); if let Some(kind) = kind { @@ -533,6 +534,7 @@ mod tests { &ConnectedPoint::Dialer { address: "/ip4/127.0.0.1".parse::().unwrap(), }, + None, ); gs.inject_connected(&random_peer); @@ -4085,6 +4087,7 @@ mod tests { &ConnectedPoint::Dialer { address: addr.clone(), }, + None, ); } @@ -4103,6 +4106,7 @@ mod tests { &ConnectedPoint::Dialer { address: addr2.clone(), }, + None, ); } @@ -4130,6 +4134,7 @@ mod tests { &ConnectedPoint::Dialer { address: addr.clone(), }, + None, ); //nothing changed diff --git a/protocols/identify/src/identify.rs b/protocols/identify/src/identify.rs index ac9ce9ab5ae..964c474b469 100644 --- a/protocols/identify/src/identify.rs +++ b/protocols/identify/src/identify.rs @@ -199,6 +199,7 @@ impl NetworkBehaviour for Identify { peer_id: &PeerId, conn: &ConnectionId, endpoint: &ConnectedPoint, + _: Option<&Vec>, ) { let addr = match endpoint { ConnectedPoint::Dialer { address } => address.clone(), diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index acadc2f037c..4b0cd364bcf 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -1743,7 +1743,54 @@ where peer_addrs } - fn inject_connection_established(&mut self, _: &PeerId, _: &ConnectionId, _: &ConnectedPoint) { + fn inject_connection_established( + &mut self, + peer_id: &PeerId, + _: &ConnectionId, + _: &ConnectedPoint, + errors: Option<&Vec>, + ) { + for addr in errors.map(|a| a.into_iter()).into_iter().flatten() { + // TODO: Should this be deduplicated with the logic triggered in inject_dial_failure? + let key = kbucket::Key::from(*peer_id); + + if let Some(addrs) = self.kbuckets.entry(&key).value() { + // TODO: Ideally, the address should only be removed if the error can + // be classified as "permanent" but since `err` is currently a borrowed + // trait object without a `'static` bound, even downcasting for inspection + // of the error is not possible (and also not truly desirable or ergonomic). + // The error passed in should rather be a dedicated enum. + if addrs.remove(addr).is_ok() { + debug!( + // "Address '{}' removed from peer '{}' due to error: {}.", + "Address '{}' removed from peer '{}'.", + addr, peer_id + ); + } else { + // Despite apparently having no reachable address (any longer), + // the peer is kept in the routing table with the last address to avoid + // (temporary) loss of network connectivity to "flush" the routing + // table. Once in, a peer is only removed from the routing table + // if it is the least recently connected peer, currently disconnected + // and is unreachable in the context of another peer pending insertion + // into the same bucket. This is handled transparently by the + // `KBucketsTable` and takes effect through `KBucketsTable::take_applied_pending` + // within `Kademlia::poll`. + debug!( + // "Last remaining address '{}' of peer '{}' is unreachable: {}.", + "Last remaining address '{}' of peer '{}' is unreachable.", + addr, peer_id + ) + } + } + + for query in self.queries.iter_mut() { + if let Some(addrs) = query.inner.addresses.get_mut(peer_id) { + addrs.retain(|a| a != addr); + } + } + } + // When a connection is established, we don't know yet whether the // remote supports the configured protocol name. Only once a connection // handler reports [`KademliaHandlerEvent::ProtocolConfirmed`] do we diff --git a/protocols/kad/src/behaviour/test.rs b/protocols/kad/src/behaviour/test.rs index 2cbd0dee4e7..463ffed7710 100644 --- a/protocols/kad/src/behaviour/test.rs +++ b/protocols/kad/src/behaviour/test.rs @@ -1281,7 +1281,7 @@ fn network_behaviour_inject_address_change() { }; // Mimick a connection being established. - kademlia.inject_connection_established(&remote_peer_id, &connection_id, &endpoint); + kademlia.inject_connection_established(&remote_peer_id, &connection_id, &endpoint, None); kademlia.inject_connected(&remote_peer_id); // At this point the remote is not yet known to support the diff --git a/protocols/relay/src/behaviour.rs b/protocols/relay/src/behaviour.rs index b9477eba4c4..99d5471b685 100644 --- a/protocols/relay/src/behaviour.rs +++ b/protocols/relay/src/behaviour.rs @@ -202,6 +202,7 @@ impl NetworkBehaviour for Relay { peer: &PeerId, connection_id: &ConnectionId, _: &ConnectedPoint, + _: Option<&Vec>, ) { let is_first = self .connected_peers diff --git a/protocols/request-response/src/lib.rs b/protocols/request-response/src/lib.rs index e0a89966b41..4007be14b5a 100644 --- a/protocols/request-response/src/lib.rs +++ b/protocols/request-response/src/lib.rs @@ -606,6 +606,7 @@ where peer: &PeerId, conn: &ConnectionId, endpoint: &ConnectedPoint, + _errors: Option<&Vec>, ) { let address = match endpoint { ConnectedPoint::Dialer { address } => Some(address.clone()), diff --git a/swarm-derive/src/lib.rs b/swarm-derive/src/lib.rs index 6ecac040dcd..b9e4ea05a30 100644 --- a/swarm-derive/src/lib.rs +++ b/swarm-derive/src/lib.rs @@ -55,6 +55,7 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream { let into_proto_select_ident = quote! {::libp2p::swarm::IntoProtocolsHandlerSelect}; let peer_id = quote! {::libp2p::core::PeerId}; let connection_id = quote! {::libp2p::core::connection::ConnectionId}; + let dial_errors = quote! {Option<&Vec<::libp2p::core::Multiaddr>>}; let connected_point = quote! {::libp2p::core::ConnectedPoint}; let listener_id = quote! {::libp2p::core::connection::ListenerId}; let dial_error = quote! {::libp2p::swarm::DialError}; @@ -203,8 +204,8 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream { return None; } Some(match field.ident { - Some(ref i) => quote!{ self.#i.inject_connection_established(peer_id, connection_id, endpoint); }, - None => quote!{ self.#field_n.inject_connection_established(peer_id, connection_id, endpoint); }, + Some(ref i) => quote!{ self.#i.inject_connection_established(peer_id, connection_id, endpoint, errors); }, + None => quote!{ self.#field_n.inject_connection_established(peer_id, connection_id, endpoint, errors); }, }) }) }; @@ -659,7 +660,7 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream { #(#inject_disconnected_stmts);* } - fn inject_connection_established(&mut self, peer_id: &#peer_id, connection_id: &#connection_id, endpoint: &#connected_point) { + fn inject_connection_established(&mut self, peer_id: &#peer_id, connection_id: &#connection_id, endpoint: &#connected_point, errors: #dial_errors) { #(#inject_connection_established_stmts);* } diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index 57239ad0fb8..4d476db8c1c 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -113,7 +113,15 @@ pub trait NetworkBehaviour: Send + 'static { fn inject_disconnected(&mut self, _: &PeerId) {} /// Informs the behaviour about a newly established connection to a peer. - fn inject_connection_established(&mut self, _: &PeerId, _: &ConnectionId, _: &ConnectedPoint) {} + // TODO: Should we pass more than just the Multiaddr? The behaviour can't do anything with it anyways. + fn inject_connection_established( + &mut self, + _: &PeerId, + _: &ConnectionId, + _: &ConnectedPoint, + _: Option<&Vec>, + ) { + } /// Informs the behaviour about a closed connection to a peer. /// diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 8aad4dc4b92..4b5271364ae 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -537,6 +537,7 @@ where Poll::Ready(NetworkEvent::ConnectionEstablished { connection, num_established, + outgoing, }) => { let peer_id = connection.peer_id(); let endpoint = connection.endpoint().clone(); @@ -558,6 +559,9 @@ where &peer_id, &connection.id(), &endpoint, + outgoing + .map(|es| es.into_iter().map(|(a, e)| a).collect()) + .as_ref(), ); if num_established.get() == 1 { this.behaviour.inject_connected(&peer_id); diff --git a/swarm/src/test.rs b/swarm/src/test.rs index 5c97f756a78..c34f64494c1 100644 --- a/swarm/src/test.rs +++ b/swarm/src/test.rs @@ -188,10 +188,16 @@ where self.inner.inject_connected(peer); } - fn inject_connection_established(&mut self, p: &PeerId, c: &ConnectionId, e: &ConnectedPoint) { + fn inject_connection_established( + &mut self, + p: &PeerId, + c: &ConnectionId, + e: &ConnectedPoint, + errors: Option<&Vec>, + ) { self.inject_connection_established .push((p.clone(), c.clone(), e.clone())); - self.inner.inject_connection_established(p, c, e); + self.inner.inject_connection_established(p, c, e, errors); } fn inject_disconnected(&mut self, peer: &PeerId) { diff --git a/swarm/src/toggle.rs b/swarm/src/toggle.rs index 181e2abd259..f9f10891dff 100644 --- a/swarm/src/toggle.rs +++ b/swarm/src/toggle.rs @@ -103,9 +103,10 @@ where peer_id: &PeerId, connection: &ConnectionId, endpoint: &ConnectedPoint, + errors: Option<&Vec>, ) { if let Some(inner) = self.inner.as_mut() { - inner.inject_connection_established(peer_id, connection, endpoint) + inner.inject_connection_established(peer_id, connection, endpoint, errors) } } From 752bb7089a2b3a1381dc0f7e5447aad7dc138a58 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Fri, 1 Oct 2021 11:20:19 +0200 Subject: [PATCH 19/67] core/src/network/concurrent_dial: Limit concurrency factor --- core/src/network/concurrent_dial.rs | 42 ++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/core/src/network/concurrent_dial.rs b/core/src/network/concurrent_dial.rs index ec831018e3f..38bfb48a28f 100644 --- a/core/src/network/concurrent_dial.rs +++ b/core/src/network/concurrent_dial.rs @@ -32,20 +32,23 @@ use futures::stream::FuturesUnordered; use futures::{future, prelude::*}; use smallvec::SmallVec; use std::{ - collections::hash_map, + collections::{hash_map, VecDeque}, convert::TryFrom as _, error, fmt, num::{NonZeroU32, NonZeroUsize}, pin::Pin, task::{Context, Poll}, + time::Instant, }; // TODO: Have a concurrency limit. // TODO: pub needed? pub struct ConcurrentDial { + start: Instant, // TODO: We could as well spawn each of these on a separate task. dials: FuturesUnordered)>>, + pending_dials: VecDeque)>>, errors: Vec<(Multiaddr, TransportError)>, } @@ -63,12 +66,23 @@ impl ConcurrentDial { TError: std::fmt::Debug, { let dials = FuturesUnordered::default(); + let mut pending_dials = VecDeque::default(); let mut errors = vec![]; - for address in addresses.into_iter().map(|a| p2p_addr(peer, a)) { - match address { + let addresses = addresses.into_iter(); + + for addr in addresses.into_iter().map(|a| p2p_addr(peer, a)) { + match addr { Ok(address) => match transport.clone().dial(address.clone()) { - Ok(fut) => dials.push(fut.map(|r| (address, r)).boxed()), + Ok(fut) => { + let fut = fut.map(|r| (address, r)).boxed(); + // TODO: Move to constant + if dials.len() <= 5 { + dials.push(fut) + } else { + pending_dials.push_back(fut) + } + } Err(err) => errors.push((address, err)), }, Err(address) => errors.push(( @@ -79,11 +93,16 @@ impl ConcurrentDial { } } - Self { dials, errors } + Self { + start: Instant::now(), + dials, + errors, + pending_dials, + } } } -impl Future for ConcurrentDial { +impl Future for ConcurrentDial { type Output = Result< ( PeerId, @@ -98,15 +117,14 @@ impl Future for ConcurrentDial { loop { match ready!(self.dials.poll_next_unpin(cx)) { Some((addr, Ok((peer_id, muxer)))) => { - return Poll::Ready(Ok(( - peer_id, - addr, - muxer, - std::mem::replace(&mut self.errors, vec![]), - ))); + let errors = std::mem::replace(&mut self.errors, vec![]); + return Poll::Ready(Ok((peer_id, addr, muxer, errors))); } Some((addr, Err(e))) => { self.errors.push((addr, TransportError::Other(e))); + if let Some(dial) = self.pending_dials.pop_front() { + self.dials.push(dial) + } } None => { return Poll::Ready(Err(std::mem::replace(&mut self.errors, vec![]))); From 0dcd1cf4aaf3fafd61d52de5e23c225bc92337cb Mon Sep 17 00:00:00 2001 From: Max Inden Date: Fri, 1 Oct 2021 11:29:08 +0200 Subject: [PATCH 20/67] core/src/network/concurrent_dial: Remove bound on Debug --- core/src/network/concurrent_dial.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/core/src/network/concurrent_dial.rs b/core/src/network/concurrent_dial.rs index 38bfb48a28f..d9884a6fe55 100644 --- a/core/src/network/concurrent_dial.rs +++ b/core/src/network/concurrent_dial.rs @@ -38,14 +38,10 @@ use std::{ num::{NonZeroU32, NonZeroUsize}, pin::Pin, task::{Context, Poll}, - time::Instant, }; -// TODO: Have a concurrency limit. - // TODO: pub needed? pub struct ConcurrentDial { - start: Instant, // TODO: We could as well spawn each of these on a separate task. dials: FuturesUnordered)>>, pending_dials: VecDeque)>>, @@ -63,7 +59,6 @@ impl ConcurrentDial { where TTrans: Transport + Clone, TTrans::Dial: Send + 'static, - TError: std::fmt::Debug, { let dials = FuturesUnordered::default(); let mut pending_dials = VecDeque::default(); @@ -94,7 +89,6 @@ impl ConcurrentDial { } Self { - start: Instant::now(), dials, errors, pending_dials, @@ -102,7 +96,7 @@ impl ConcurrentDial { } } -impl Future for ConcurrentDial { +impl Future for ConcurrentDial { type Output = Result< ( PeerId, From 2392249d2c098eef43b176921d97938233ed49dd Mon Sep 17 00:00:00 2001 From: Max Inden Date: Mon, 4 Oct 2021 21:26:30 +0200 Subject: [PATCH 21/67] core/src/connection: Remove task module --- core/src/connection/manager.rs | 128 ++++++--- core/src/connection/manager/task.rs | 413 ---------------------------- 2 files changed, 95 insertions(+), 446 deletions(-) delete mode 100644 core/src/connection/manager/task.rs diff --git a/core/src/connection/manager.rs b/core/src/connection/manager.rs index da397217c8b..ab146625357 100644 --- a/core/src/connection/manager.rs +++ b/core/src/connection/manager.rs @@ -38,11 +38,77 @@ use std::{ pin::Pin, task::{Context, Poll}, }; -use task::{Task, TaskId}; use void::Void; -mod task; +/// Identifier of a [`Task`] in a [`Manager`](super::Manager). +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct TaskId(pub(super) usize); + +/// Commands that can be sent to a task. +#[derive(Debug)] +pub enum Command { + /// Notify the connection handler of an event. + NotifyHandler(T), + /// Gracefully close the connection (active close) before + /// terminating the task. + Close, +} +/// Events that a task can emit to its manager. +#[derive(Debug)] +pub enum TaskEvent { + // TODO: Remove most of these + IncomingEstablished { + id: TaskId, + peer_id: PeerId, + muxer: TMuxer, + }, + OutgoingEstablished { + id: TaskId, + peer_id: PeerId, + muxer: TMuxer, + address: Multiaddr, + // TODO: Document errors in a success message. + errors: Vec<(Multiaddr, TransportError)>, + }, + /// A pending connection failed. + PendingFailed { + id: TaskId, + error: PendingConnectionError, + }, + /// A node we are connected to has changed its address. + AddressChange { id: TaskId, new_address: Multiaddr }, + /// Notify the manager of an event from the connection. + Notify { + id: TaskId, + event: THandlerOutEvent, + }, + /// A connection closed, possibly due to an error. + /// + /// If `error` is `None`, the connection has completed + /// an active orderly close. + Closed { + id: TaskId, + error: Option>>, + handler: H::Handler, + }, +} + +impl TaskEvent { + pub fn id(&self) -> &TaskId { + match self { + TaskEvent::IncomingEstablished { id, .. } => id, + TaskEvent::OutgoingEstablished { id, .. } => id, + TaskEvent::PendingFailed { id, .. } => id, + TaskEvent::AddressChange { id, .. } => id, + TaskEvent::Notify { id, .. } => id, + TaskEvent::Closed { id, .. } => id, + } + } +} + +// TODO: Still up to date? +// // Implementation Notes // ==================== // @@ -108,10 +174,10 @@ pub struct Manager { /// Sender distributed to managed tasks for reporting events back /// to the manager. - events_tx: mpsc::Sender>, + events_tx: mpsc::Sender>, /// Receiver for events reported from managed tasks. - events_rx: mpsc::Receiver>, + events_rx: mpsc::Receiver>, } impl fmt::Debug for Manager { @@ -159,7 +225,7 @@ enum TaskInfo { Established { connected: Connected, /// Channel endpoint to send messages to the task. - sender: mpsc::Sender>, + sender: mpsc::Sender>, }, } @@ -286,7 +352,7 @@ impl Manag match futures::future::select(drop_receiver, Box::pin(dial)).await { Either::Left((Err(oneshot::Canceled), _)) => { events - .send(task::Event::PendingFailed { + .send(TaskEvent::PendingFailed { id: task_id, error: PendingConnectionError::Aborted, }) @@ -295,7 +361,7 @@ impl Manag Either::Left((Ok(v), _)) => void::unreachable(v), Either::Right((Ok((peer_id, muxer)), _)) => { events - .send(task::Event::IncomingEstablished { + .send(TaskEvent::IncomingEstablished { id: task_id, peer_id, muxer, @@ -304,7 +370,7 @@ impl Manag } Either::Right((Err(e), _)) => { events - .send(task::Event::PendingFailed { + .send(TaskEvent::PendingFailed { id: task_id, error: e, }) @@ -340,7 +406,7 @@ impl Manag match futures::future::select(drop_receiver, Box::pin(dial)).await { Either::Left((Err(oneshot::Canceled), _)) => { events - .send(task::Event::PendingFailed { + .send(TaskEvent::PendingFailed { id: task_id, error: PendingConnectionError::Aborted, }) @@ -349,7 +415,7 @@ impl Manag Either::Left((Ok(v), _)) => void::unreachable(v), Either::Right((Ok((peer_id, address, muxer, errors)), _)) => { events - .send(task::Event::OutgoingEstablished { + .send(TaskEvent::OutgoingEstablished { id: task_id, peer_id, muxer, @@ -360,7 +426,7 @@ impl Manag } Either::Right((Err(e), _)) => { events - .send(task::Event::PendingFailed { + .send(TaskEvent::PendingFailed { id: task_id, error: PendingConnectionError::TransportDial(e), }) @@ -403,14 +469,14 @@ impl Manag loop { match futures::future::select(command_receiver.next(), connection.next()).await { Either::Left((Some(command), _)) => match command { - task::Command::NotifyHandler(event) => connection.inject_event(event), - task::Command::Close => { + Command::NotifyHandler(event) => connection.inject_event(event), + Command::Close => { command_receiver.close(); let (handler, closing_muxer) = connection.close(); let error = closing_muxer.await.err().map(ConnectionError::IO); events - .send(task::Event::Closed { + .send(TaskEvent::Closed { id: task_id, error, handler, @@ -426,13 +492,11 @@ impl Manag Either::Right((Some(event), _)) => { match event { Ok(super::Event::Handler(event)) => { - events - .send(task::Event::Notify { id: task_id, event }) - .await; + events.send(TaskEvent::Notify { id: task_id, event }).await; } Ok(super::Event::AddressChange(new_address)) => { events - .send(task::Event::AddressChange { + .send(TaskEvent::AddressChange { id: task_id, new_address, }) @@ -443,7 +507,7 @@ impl Manag let (handler, _closing_muxer) = connection.close(); // Terminate the task with the error, dropping the connection. events - .send(task::Event::Closed { + .send(TaskEvent::Closed { id: task_id, error: Some(error), handler, @@ -499,7 +563,7 @@ impl Manag if let hash_map::Entry::Occupied(mut task) = self.tasks.entry(*event.id()) { Poll::Ready(match event { - task::Event::IncomingEstablished { id, peer_id, muxer } => { + TaskEvent::IncomingEstablished { id, peer_id, muxer } => { task.remove(); Event::ConnectionEstablished { id: ConnectionId(id), @@ -508,7 +572,7 @@ impl Manag outgoing: None, } } - task::Event::OutgoingEstablished { + TaskEvent::OutgoingEstablished { id, peer_id, address, @@ -523,16 +587,16 @@ impl Manag outgoing: Some((address, errors)), } } - task::Event::Notify { id: _, event } => Event::ConnectionEvent { + TaskEvent::Notify { id: _, event } => Event::ConnectionEvent { entry: EstablishedEntry { task }, event, }, - task::Event::PendingFailed { id, error } => { + TaskEvent::PendingFailed { id, error } => { let id = ConnectionId(id); let _ = task.remove(); Event::PendingConnectionError { id, error } } - task::Event::AddressChange { id: _, new_address } => { + TaskEvent::AddressChange { id: _, new_address } => { let (new, old) = if let TaskInfo::Established { connected, .. } = &mut task.get_mut() { let mut new_endpoint = connected.endpoint.clone(); @@ -551,7 +615,7 @@ impl Manag new_endpoint: new, } } - task::Event::Closed { id, error, handler } => { + TaskEvent::Closed { id, error, handler } => { let id = ConnectionId(id); match task.remove() { TaskInfo::Established { sender, connected } => Event::ConnectionClosed { @@ -610,11 +674,11 @@ impl<'a, I> EstablishedEntry<'a, I> { /// > task _may not be notified_ if sending the event fails due to /// > the connection handler not being ready at this time. pub fn notify_handler(&mut self, event: I) -> Result<(), I> { - let cmd = task::Command::NotifyHandler(event); + let cmd = Command::NotifyHandler(event); match self.task.get_mut() { TaskInfo::Established { sender, .. } => { sender.try_send(cmd).map_err(|e| match e.into_inner() { - task::Command::NotifyHandler(event) => event, + Command::NotifyHandler(event) => event, _ => unreachable!("Expect `EstablishedEntry` to point to established task."), }) } @@ -650,12 +714,10 @@ impl<'a, I> EstablishedEntry<'a, I> { // Clone the sender so that we are guaranteed to have // capacity for the close command (every sender gets a slot). match self.task.get_mut() { - TaskInfo::Established { sender, .. } => { - match sender.clone().try_send(task::Command::Close) { - Ok(()) => {} - Err(e) => assert!(e.is_disconnected(), "No capacity for close command."), - } - } + TaskInfo::Established { sender, .. } => match sender.clone().try_send(Command::Close) { + Ok(()) => {} + Err(e) => assert!(e.is_disconnected(), "No capacity for close command."), + }, TaskInfo::Pending { .. } => { unreachable!("Expect `EstablishedEntry` to point to established task.") } diff --git a/core/src/connection/manager/task.rs b/core/src/connection/manager/task.rs deleted file mode 100644 index bd9f4338872..00000000000 --- a/core/src/connection/manager/task.rs +++ /dev/null @@ -1,413 +0,0 @@ -// Copyright 2019 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -use super::ConnectResult; -use crate::{ - connection::{ - self, - handler::{THandlerError, THandlerInEvent, THandlerOutEvent}, - Close, Connected, Connection, ConnectionError, ConnectionHandler, ConnectionLimit, - IntoConnectionHandler, PendingConnectionError, Substream, - }, - muxing::StreamMuxer, - transport::TransportError, - Multiaddr, PeerId, -}; -use futures::{channel::mpsc, future::Either, prelude::*, stream}; -use std::{pin::Pin, task::Context, task::Poll}; - -/// Identifier of a [`Task`] in a [`Manager`](super::Manager). -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct TaskId(pub(super) usize); - -/// Commands that can be sent to a [`Task`]. -#[derive(Debug)] -pub enum Command { - /// Notify the connection handler of an event. - NotifyHandler(T), - /// Gracefully close the connection (active close) before - /// terminating the task. - Close, -} - -/// Events that a task can emit to its manager. -#[derive(Debug)] -pub enum Event { - // TODO: Remove most of these - IncomingEstablished { - id: TaskId, - peer_id: PeerId, - muxer: TMuxer, - }, - OutgoingEstablished { - id: TaskId, - peer_id: PeerId, - muxer: TMuxer, - address: Multiaddr, - // TODO: Document errors in a success message. - errors: Vec<(Multiaddr, TransportError)>, - }, - /// A pending connection failed. - PendingFailed { - id: TaskId, - error: PendingConnectionError, - }, - /// A node we are connected to has changed its address. - AddressChange { id: TaskId, new_address: Multiaddr }, - /// Notify the manager of an event from the connection. - Notify { - id: TaskId, - event: THandlerOutEvent, - }, - /// A connection closed, possibly due to an error. - /// - /// If `error` is `None`, the connection has completed - /// an active orderly close. - Closed { - id: TaskId, - error: Option>>, - handler: H::Handler, - }, -} - -impl Event { - pub fn id(&self) -> &TaskId { - match self { - Event::IncomingEstablished { id, .. } => id, - Event::OutgoingEstablished { id, .. } => id, - Event::PendingFailed { id, .. } => id, - Event::AddressChange { id, .. } => id, - Event::Notify { id, .. } => id, - Event::Closed { id, .. } => id, - } - } -} - -/// A `Task` is a [`Future`] that handles a single connection. -pub struct Task -where - M: StreamMuxer, - H: IntoConnectionHandler, - H::Handler: ConnectionHandler>, -{ - /// The ID of this task. - id: TaskId, - - /// Sender to emit events to the manager of this task. - events: mpsc::Sender>, - - /// Receiver for commands sent by the manager of this task. - commands: stream::Fuse>>>, - - /// Inner state of this `Task`. - state: State, -} - -impl Task -where - M: StreamMuxer, - H: IntoConnectionHandler, - H::Handler: ConnectionHandler>, -{ - /// Create a new task to connect and handle some node. - pub fn pending( - id: TaskId, - events: mpsc::Sender>, - commands: mpsc::Receiver>>, - future: F, - handler: H, - ) -> Self { - Task { - id, - events, - commands: commands.fuse(), - state: State::Pending { - future: Box::pin(future), - handler, - }, - } - } -} - -/// The state associated with the `Task` of a connection. -enum State -where - M: StreamMuxer, - H: IntoConnectionHandler, - H::Handler: ConnectionHandler>, -{ - /// The connection is being negotiated. - Pending { - /// The future that will attempt to reach the node. - // TODO: don't pin this Future; this requires deeper changes though - future: Pin>, - /// The intended handler for the established connection. - handler: H, - }, - - /// The connection is established. - Established { - connection: Connection, - /// An event to send to the `Manager`. If `None`, the `connection` - /// is polled for new events in this state, otherwise the event - /// must be sent to the `Manager` before the connection can be - /// polled again. - event: Option>, - }, - - /// The connection is closing (active close). - Closing { - closing_muxer: Close, - handler: H::Handler, - error: Option, - }, - - /// The task is terminating with a final event for the `Manager`. - Terminating(Event), - - /// The task has finished. - Done, -} - -impl Unpin for Task -where - M: StreamMuxer, - H: IntoConnectionHandler, - H::Handler: ConnectionHandler>, -{ -} - -impl Future for Task -where - M: StreamMuxer, - F: Future>, - H: IntoConnectionHandler, - H::Handler: ConnectionHandler> + Send + 'static, -{ - type Output = (); - - // NOTE: It is imperative to always consume all incoming commands from - // the manager first, in order to not prevent it from making progress because - // it is blocked on the channel capacity. - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { - let this = &mut *self; - let id = this.id; - - 'poll: loop { - match std::mem::replace(&mut this.state, State::Done) { - State::Pending { - mut future, - handler, - } => { - // Check whether the task is still registered with a `Manager` - // by polling the commands channel. - match this.commands.poll_next_unpin(cx) { - Poll::Pending => {} - Poll::Ready(None) => { - todo!() - // // The manager has dropped the task; abort. - // // Don't accept any further commands and terminate the - // // task with a final event. - // this.commands.get_mut().close(); - // let event = Event::PendingFailed { - // id, - // handler, - // error: PendingConnectionError::Aborted, - // }; - // this.state = State::Terminating(event); - // continue 'poll; - } - Poll::Ready(Some(_)) => { - panic!("Task received command while the connection is pending.") - } - } - // Check if the connection succeeded. - match future.poll_unpin(cx) { - Poll::Ready(Ok((info, muxer))) => { - todo!() - // this.state = State::Established { - // connection: Connection::new(muxer, handler.into_handler(&info)), - // event: Some(Event::Established { id, info }), - // } - } - Poll::Pending => { - this.state = State::Pending { future, handler }; - return Poll::Pending; - } - Poll::Ready(Err(error)) => { - todo!() - // // Don't accept any further commands and terminate the - // // task with a final event. - // this.commands.get_mut().close(); - // let event = Event::Failed { id, handler, error }; - // this.state = State::Terminating(event) - } - } - } - - State::Established { - mut connection, - event, - } => { - // Check for commands from the `Manager`. - loop { - match this.commands.poll_next_unpin(cx) { - Poll::Pending => break, - Poll::Ready(Some(Command::NotifyHandler(event))) => { - connection.inject_event(event) - } - Poll::Ready(Some(Command::Close)) => { - // Don't accept any further commands. - this.commands.get_mut().close(); - // Discard the event, if any, and start a graceful close. - let (handler, closing_muxer) = connection.close(); - this.state = State::Closing { - handler, - closing_muxer, - error: None, - }; - continue 'poll; - } - Poll::Ready(None) => { - // The manager has disappeared; abort. - return Poll::Ready(()); - } - } - } - - if let Some(event) = event { - // Send the event to the manager. - match this.events.poll_ready(cx) { - Poll::Pending => { - this.state = State::Established { - connection, - event: Some(event), - }; - return Poll::Pending; - } - Poll::Ready(result) => { - if result.is_ok() { - if let Ok(()) = this.events.start_send(event) { - this.state = State::Established { - connection, - event: None, - }; - continue 'poll; - } - } - // The manager is no longer reachable; abort. - return Poll::Ready(()); - } - } - } else { - todo!("can be removed") - // // Poll the connection for new events. - // match Connection::poll(Pin::new(&mut connection), cx) { - // Poll::Pending => { - // this.state = State::Established { - // connection, - // event: None, - // }; - // return Poll::Pending; - // } - // Poll::Ready(Ok(connection::Event::Handler(event))) => { - // this.state = State::Established { - // connection, - // event: Some(Event::Notify { id, event }), - // }; - // } - // Poll::Ready(Ok(connection::Event::AddressChange(new_address))) => { - // this.state = State::Established { - // connection, - // event: Some(Event::AddressChange { id, new_address }), - // }; - // } - // Poll::Ready(Err(error)) => { - // // Don't accept any further commands. - // this.commands.get_mut().close(); - // let (handler, _closing_muxer) = connection.close(); - // // Terminate the task with the error, dropping the connection. - // let event = Event::Closed { - // id, - // error: Some(error), - // handler, - // }; - // this.state = State::Terminating(event); - // } - // } - } - } - - State::Closing { - handler, - error, - mut closing_muxer, - } => { - // Try to gracefully close the connection. - match closing_muxer.poll_unpin(cx) { - Poll::Ready(Ok(())) => { - todo!() - // let event = Event::Closed { - // id: this.id, - // error: error.map(ConnectionError::ConnectionLimit), - // handler, - // }; - // this.state = State::Terminating(event); - } - Poll::Ready(Err(e)) => { - let event = Event::Closed { - id: this.id, - error: Some(ConnectionError::IO(e)), - handler, - }; - this.state = State::Terminating(event); - } - Poll::Pending => { - this.state = State::Closing { - handler, - error, - closing_muxer, - }; - return Poll::Pending; - } - } - } - - State::Terminating(event) => { - // Try to deliver the final event. - match this.events.poll_ready(cx) { - Poll::Pending => { - self.state = State::Terminating(event); - return Poll::Pending; - } - Poll::Ready(result) => { - if result.is_ok() { - let _ = this.events.start_send(event); - } - return Poll::Ready(()); - } - } - } - - State::Done => panic!("`Task::poll()` called after completion."), - } - } - } -} From b27c73539da95de2f5a5da6b0b442142a0b419e4 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Mon, 4 Oct 2021 21:30:33 +0200 Subject: [PATCH 22/67] core/src/connection: Revive Pool::disconnect --- core/src/connection/pool.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index e5531eed95b..a1458fcc06c 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -407,14 +407,13 @@ impl Date: Tue, 5 Oct 2021 09:34:41 +0200 Subject: [PATCH 23/67] core/src/connection: Fold manager into pool --- core/src/connection.rs | 4 +- core/src/connection/manager.rs | 758 ------------------------------- core/src/connection/pool.rs | 722 +++++++++++++++++------------ core/src/connection/pool/task.rs | 267 +++++++++++ core/src/network.rs | 3 +- core/src/network/peer.rs | 20 +- 6 files changed, 721 insertions(+), 1053 deletions(-) delete mode 100644 core/src/connection/manager.rs create mode 100644 core/src/connection/pool/task.rs diff --git a/core/src/connection.rs b/core/src/connection.rs index cc28dd08f1b..81df440bce9 100644 --- a/core/src/connection.rs +++ b/core/src/connection.rs @@ -23,14 +23,12 @@ pub(crate) mod handler; mod listeners; mod substream; -pub(crate) mod manager; pub(crate) mod pool; pub use error::{ConnectionError, PendingConnectionError}; pub use handler::{ConnectionHandler, ConnectionHandlerEvent, IntoConnectionHandler}; pub use listeners::{ListenerId, ListenersEvent, ListenersStream}; -pub use manager::ConnectionId; -pub use pool::{ConnectionCounters, ConnectionLimits}; +pub use pool::{ConnectionCounters, ConnectionId, ConnectionLimits}; pub use pool::{EstablishedConnection, EstablishedConnectionIter, PendingConnection}; pub use substream::{Close, Substream, SubstreamEndpoint}; diff --git a/core/src/connection/manager.rs b/core/src/connection/manager.rs deleted file mode 100644 index ab146625357..00000000000 --- a/core/src/connection/manager.rs +++ /dev/null @@ -1,758 +0,0 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -use super::{ - handler::{THandlerError, THandlerInEvent, THandlerOutEvent}, - Connected, ConnectedPoint, ConnectionError, ConnectionHandler, IntoConnectionHandler, - PendingConnectionError, Substream, -}; -use crate::{muxing::StreamMuxer, transport::TransportError, Executor, Multiaddr, PeerId}; -use fnv::FnvHashMap; -use futures::{ - channel::{mpsc, oneshot}, - future::{poll_fn, BoxFuture, Either}, - prelude::*, - ready, - stream::FuturesUnordered, -}; -use std::{ - collections::hash_map, - error, fmt, mem, - pin::Pin, - task::{Context, Poll}, -}; -use void::Void; - -/// Identifier of a [`Task`] in a [`Manager`](super::Manager). -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct TaskId(pub(super) usize); - -/// Commands that can be sent to a task. -#[derive(Debug)] -pub enum Command { - /// Notify the connection handler of an event. - NotifyHandler(T), - /// Gracefully close the connection (active close) before - /// terminating the task. - Close, -} - -/// Events that a task can emit to its manager. -#[derive(Debug)] -pub enum TaskEvent { - // TODO: Remove most of these - IncomingEstablished { - id: TaskId, - peer_id: PeerId, - muxer: TMuxer, - }, - OutgoingEstablished { - id: TaskId, - peer_id: PeerId, - muxer: TMuxer, - address: Multiaddr, - // TODO: Document errors in a success message. - errors: Vec<(Multiaddr, TransportError)>, - }, - /// A pending connection failed. - PendingFailed { - id: TaskId, - error: PendingConnectionError, - }, - /// A node we are connected to has changed its address. - AddressChange { id: TaskId, new_address: Multiaddr }, - /// Notify the manager of an event from the connection. - Notify { - id: TaskId, - event: THandlerOutEvent, - }, - /// A connection closed, possibly due to an error. - /// - /// If `error` is `None`, the connection has completed - /// an active orderly close. - Closed { - id: TaskId, - error: Option>>, - handler: H::Handler, - }, -} - -impl TaskEvent { - pub fn id(&self) -> &TaskId { - match self { - TaskEvent::IncomingEstablished { id, .. } => id, - TaskEvent::OutgoingEstablished { id, .. } => id, - TaskEvent::PendingFailed { id, .. } => id, - TaskEvent::AddressChange { id, .. } => id, - TaskEvent::Notify { id, .. } => id, - TaskEvent::Closed { id, .. } => id, - } - } -} - -// TODO: Still up to date? -// -// Implementation Notes -// ==================== -// -// A `Manager` is decoupled from the background tasks through channels. -// The state of a `Manager` therefore "lags behind" the progress of -// the tasks -- it is only made aware of progress in the background tasks -// when it is `poll()`ed. -// -// A `Manager` is ignorant of substreams and does not emit any events -// related to specific substreams. -// -// A `Manager` is unaware of any association between connections and peers -// / peer identities (i.e. the type parameter `C` is completely opaque). -// -// There is a 1-1 correspondence between (internal) task IDs and (public) -// connection IDs, i.e. the task IDs are "re-exported" as connection IDs -// by the manager. The notion of a (background) task is internal to the -// manager. - -/// The result of a pending connection attempt. -type ConnectResult = Result<(Connected, M), PendingConnectionError>; - -/// Connection identifier. -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct ConnectionId(TaskId); - -impl ConnectionId { - /// Creates a `ConnectionId` from a non-negative integer. - /// - /// This is primarily useful for creating connection IDs - /// in test environments. There is in general no guarantee - /// that all connection IDs are based on non-negative integers. - pub fn new(id: usize) -> Self { - ConnectionId(TaskId(id)) - } -} - -/// A connection `Manager` orchestrates the I/O of a set of connections. -pub struct Manager { - /// The tasks of the managed connections. - /// - /// Each managed connection is associated with a (background) task - /// spawned onto an executor. Each `TaskInfo` in `tasks` is linked to such a - /// background task via a channel. Closing that channel (i.e. dropping - /// the sender in the associated `TaskInfo`) stops the background task, - /// which will attempt to gracefully close the connection. - tasks: FnvHashMap>>, - - /// Next available identifier for a new connection / task. - next_task_id: TaskId, - - /// Size of the task command buffer (per task). - task_command_buffer_size: usize, - - /// The executor to use for running the background tasks. If `None`, - /// the tasks are kept in `local_spawns` instead and polled on the - /// current thread when the manager is polled for new events. - executor: Option>, - - /// If no `executor` is configured, tasks are kept in this set and - /// polled on the current thread when the manager is polled for new events. - local_spawns: FuturesUnordered + Send>>>, - - /// Sender distributed to managed tasks for reporting events back - /// to the manager. - events_tx: mpsc::Sender>, - - /// Receiver for events reported from managed tasks. - events_rx: mpsc::Receiver>, -} - -impl fmt::Debug for Manager { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_map().entries(self.tasks.iter()).finish() - } -} - -/// Configuration options when creating a [`Manager`]. -/// -/// The default configuration specifies no dedicated task executor, a -/// task event buffer size of 32, and a task command buffer size of 7. -#[non_exhaustive] -pub struct ManagerConfig { - /// Executor to use to spawn tasks. - pub executor: Option>, - - /// Size of the task command buffer (per task). - pub task_command_buffer_size: usize, - - /// Size of the task event buffer (for all tasks). - pub task_event_buffer_size: usize, -} - -impl Default for ManagerConfig { - fn default() -> Self { - ManagerConfig { - executor: None, - task_event_buffer_size: 32, - task_command_buffer_size: 7, - } - } -} - -/// Internal information about a running task. -/// -/// Contains the sender to deliver event messages to the task, and -/// the associated user data. -#[derive(Debug)] -enum TaskInfo { - Pending { - /// When dropped, notifies the task which can then terminate. - drop_notifier: oneshot::Sender, - }, - Established { - connected: Connected, - /// Channel endpoint to send messages to the task. - sender: mpsc::Sender>, - }, -} - -/// Events produced by the [`Manager`]. -#[derive(Debug)] -pub enum Event<'a, H: IntoConnectionHandler, TMuxer, TE> { - /// A connection attempt has failed. - PendingConnectionError { - /// The connection ID. - /// - /// As a result of the error, the pending connection has been removed - /// from the `Manager` and is being closed. Hence this ID will - /// no longer resolve to a valid entry in the manager. - id: ConnectionId, - /// What happened. - error: PendingConnectionError, - }, - - /// An established connection has been closed. - ConnectionClosed { - /// The connection ID. - /// - /// > **Note**: Closed connections are removed from the `Manager`. - /// > Hence this ID will no longer resolve to a valid entry in - /// > the manager. - id: ConnectionId, - /// Information about the closed connection. - connected: Connected, - /// The error that occurred, if any. If `None`, the connection - /// has been actively closed. - error: Option>>, - handler: H::Handler, - }, - - /// A connection has been established. - ConnectionEstablished { - id: ConnectionId, - peer_id: PeerId, - muxer: TMuxer, - outgoing: Option<(Multiaddr, Vec<(Multiaddr, TransportError)>)>, - }, - - /// A connection handler has produced an event. - ConnectionEvent { - /// The entry associated with the connection that produced the event. - entry: EstablishedEntry<'a, THandlerInEvent>, - /// The produced event. - event: THandlerOutEvent, - }, - - /// A connection to a node has changed its address. - AddressChange { - /// The entry associated with the connection that changed address. - entry: EstablishedEntry<'a, THandlerInEvent>, - /// The former [`ConnectedPoint`]. - old_endpoint: ConnectedPoint, - /// The new [`ConnectedPoint`]. - new_endpoint: ConnectedPoint, - }, -} - -impl Manager { - /// Creates a new connection manager. - pub fn new(config: ManagerConfig) -> Self { - let (tx, rx) = mpsc::channel(config.task_event_buffer_size); - Self { - tasks: FnvHashMap::default(), - next_task_id: TaskId(0), - task_command_buffer_size: config.task_command_buffer_size, - executor: config.executor, - local_spawns: FuturesUnordered::new(), - events_tx: tx, - events_rx: rx, - } - } - - fn next_task_id(&mut self) -> TaskId { - let task_id = self.next_task_id; - self.next_task_id.0 += 1; - - task_id - } - - fn spawn(&mut self, task: BoxFuture<'static, ()>) { - if let Some(executor) = &mut self.executor { - executor.exec(task); - } else { - self.local_spawns.push(task); - } - } - - pub fn add_closing(&mut self, muxer: TMuxer) - where - TMuxer: StreamMuxer + Send + Sync + 'static, - { - self.spawn( - poll_fn(move |cx| { - ready!(muxer.close(cx)); - // TODO: Should we send the result back to the manager? - Poll::Ready(()) - }) - .boxed(), - ); - } - - pub fn add_pending_incoming(&mut self, dial: TFut) -> ConnectionId - where - TFut: - Future>> + Send + 'static, - - H: IntoConnectionHandler + Send + 'static, - H::Handler: ConnectionHandler + Send + 'static, - { - let task_id = self.next_task_id(); - - let (drop_notifier, mut drop_receiver) = oneshot::channel(); - self.tasks - .insert(task_id, TaskInfo::Pending { drop_notifier }); - - let mut events = self.events_tx.clone(); - - self.spawn( - async move { - match futures::future::select(drop_receiver, Box::pin(dial)).await { - Either::Left((Err(oneshot::Canceled), _)) => { - events - .send(TaskEvent::PendingFailed { - id: task_id, - error: PendingConnectionError::Aborted, - }) - .await; - } - Either::Left((Ok(v), _)) => void::unreachable(v), - Either::Right((Ok((peer_id, muxer)), _)) => { - events - .send(TaskEvent::IncomingEstablished { - id: task_id, - peer_id, - muxer, - }) - .await; - } - Either::Right((Err(e), _)) => { - events - .send(TaskEvent::PendingFailed { - id: task_id, - error: e, - }) - .await; - } - } - } - .boxed(), - ); - - ConnectionId(task_id) - } - - pub fn add_pending_outgoing( - &mut self, - dial: crate::network::concurrent_dial::ConcurrentDial, - ) -> ConnectionId - where - H: IntoConnectionHandler + Send + 'static, - H::Handler: ConnectionHandler + Send + 'static, - { - let task_id = self.next_task_id(); - - let (drop_notifier, mut drop_receiver) = oneshot::channel(); - - self.tasks - .insert(task_id, TaskInfo::Pending { drop_notifier }); - - let mut events = self.events_tx.clone(); - - self.spawn( - async move { - match futures::future::select(drop_receiver, Box::pin(dial)).await { - Either::Left((Err(oneshot::Canceled), _)) => { - events - .send(TaskEvent::PendingFailed { - id: task_id, - error: PendingConnectionError::Aborted, - }) - .await; - } - Either::Left((Ok(v), _)) => void::unreachable(v), - Either::Right((Ok((peer_id, address, muxer, errors)), _)) => { - events - .send(TaskEvent::OutgoingEstablished { - id: task_id, - peer_id, - muxer, - address, - errors, - }) - .await; - } - Either::Right((Err(e), _)) => { - events - .send(TaskEvent::PendingFailed { - id: task_id, - error: PendingConnectionError::TransportDial(e), - }) - .await; - } - } - } - .boxed(), - ); - - ConnectionId(task_id) - } - - pub fn add_established( - &mut self, - connection_id: ConnectionId, - mut connection: crate::connection::Connection, - connected: Connected, - ) where - TMuxer: StreamMuxer + Send + Sync + 'static, - TMuxer::OutboundSubstream: Send + 'static, - H: IntoConnectionHandler + Send + 'static, - H::Handler: ConnectionHandler> + Send + 'static, - ::OutboundOpenInfo: Send + 'static, - { - let task_id = connection_id.0; - let (command_sender, mut command_receiver) = mpsc::channel(self.task_command_buffer_size); - - self.tasks.insert( - task_id, - TaskInfo::Established { - sender: command_sender, - connected, - }, - ); - - let mut events = self.events_tx.clone(); - - let task = async move { - loop { - match futures::future::select(command_receiver.next(), connection.next()).await { - Either::Left((Some(command), _)) => match command { - Command::NotifyHandler(event) => connection.inject_event(event), - Command::Close => { - command_receiver.close(); - let (handler, closing_muxer) = connection.close(); - - let error = closing_muxer.await.err().map(ConnectionError::IO); - events - .send(TaskEvent::Closed { - id: task_id, - error, - handler, - }) - .await; - return; - } - }, - - // The manager has disappeared; abort. - Either::Left((None, _)) => return, - - Either::Right((Some(event), _)) => { - match event { - Ok(super::Event::Handler(event)) => { - events.send(TaskEvent::Notify { id: task_id, event }).await; - } - Ok(super::Event::AddressChange(new_address)) => { - events - .send(TaskEvent::AddressChange { - id: task_id, - new_address, - }) - .await; - } - Err(error) => { - command_receiver.close(); - let (handler, _closing_muxer) = connection.close(); - // Terminate the task with the error, dropping the connection. - events - .send(TaskEvent::Closed { - id: task_id, - error: Some(error), - handler, - }) - .await; - return; - } - } - } - Either::Right((None, _)) => { - unreachable!("Connection is an infinite stream"); - } - } - } - } - .boxed(); - - self.spawn(task); - } - - /// Gets an entry for a managed connection, if it exists. - pub fn entry(&mut self, id: ConnectionId) -> Option>> { - if let hash_map::Entry::Occupied(task) = self.tasks.entry(id.0) { - Some(Entry::new(task)) - } else { - None - } - } - - /// Checks whether an established connection with the given ID is currently managed. - pub fn is_established(&self, id: &ConnectionId) -> bool { - matches!(self.tasks.get(&id.0), Some(TaskInfo::Established { .. })) - } - - /// Polls the manager for events relating to the managed connections. - pub fn poll<'a>(&'a mut self, cx: &mut Context<'_>) -> Poll> { - // Advance the content of `local_spawns`. - while let Poll::Ready(Some(_)) = self.local_spawns.poll_next_unpin(cx) {} - - // Poll for the first event for which the manager still has a registered task, if any. - let event = loop { - match self.events_rx.poll_next_unpin(cx) { - Poll::Ready(Some(event)) => { - if self.tasks.contains_key(event.id()) { - // (1) - break event; - } - } - Poll::Pending => return Poll::Pending, - Poll::Ready(None) => unreachable!("Manager holds both sender and receiver."), - } - }; - - if let hash_map::Entry::Occupied(mut task) = self.tasks.entry(*event.id()) { - Poll::Ready(match event { - TaskEvent::IncomingEstablished { id, peer_id, muxer } => { - task.remove(); - Event::ConnectionEstablished { - id: ConnectionId(id), - peer_id, - muxer, - outgoing: None, - } - } - TaskEvent::OutgoingEstablished { - id, - peer_id, - address, - muxer, - errors, - } => { - task.remove(); - Event::ConnectionEstablished { - id: ConnectionId(id), - peer_id, - muxer, - outgoing: Some((address, errors)), - } - } - TaskEvent::Notify { id: _, event } => Event::ConnectionEvent { - entry: EstablishedEntry { task }, - event, - }, - TaskEvent::PendingFailed { id, error } => { - let id = ConnectionId(id); - let _ = task.remove(); - Event::PendingConnectionError { id, error } - } - TaskEvent::AddressChange { id: _, new_address } => { - let (new, old) = - if let TaskInfo::Established { connected, .. } = &mut task.get_mut() { - let mut new_endpoint = connected.endpoint.clone(); - new_endpoint.set_remote_address(new_address); - let old_endpoint = - mem::replace(&mut connected.endpoint, new_endpoint.clone()); - (new_endpoint, old_endpoint) - } else { - unreachable!( - "`Event::AddressChange` implies (2) occurred on that task and thus (3)." - ) - }; - Event::AddressChange { - entry: EstablishedEntry { task }, - old_endpoint: old, - new_endpoint: new, - } - } - TaskEvent::Closed { id, error, handler } => { - let id = ConnectionId(id); - match task.remove() { - TaskInfo::Established { sender, connected } => Event::ConnectionClosed { - id, - connected, - error, - handler, - }, - TaskInfo::Pending { .. } => { - unreachable!("`Event::Closed` is never emitted for pending connection.") - } - } - } - }) - } else { - unreachable!("By (1)") - } - } -} - -/// An entry for a connection in the manager. -#[derive(Debug)] -pub enum Entry<'a, I> { - Pending(PendingEntry<'a, I>), - Established(EstablishedEntry<'a, I>), -} - -impl<'a, I> Entry<'a, I> { - fn new(task: hash_map::OccupiedEntry<'a, TaskId, TaskInfo>) -> Self { - match &task.get() { - TaskInfo::Pending { .. } => Entry::Pending(PendingEntry { task }), - TaskInfo::Established { .. } => Entry::Established(EstablishedEntry { task }), - } - } -} - -/// An entry for a managed connection that is considered established. -#[derive(Debug)] -pub struct EstablishedEntry<'a, I> { - task: hash_map::OccupiedEntry<'a, TaskId, TaskInfo>, -} - -impl<'a, I> EstablishedEntry<'a, I> { - /// (Asynchronously) sends an event to the connection handler. - /// - /// If the handler is not ready to receive the event, either because - /// it is busy or the connection is about to close, the given event - /// is returned with an `Err`. - /// - /// If execution of this method is preceded by successful execution of - /// `poll_ready_notify_handler` without another intervening execution - /// of `notify_handler`, it only fails if the connection is now about - /// to close. - /// - /// > **Note**: As this method does not take a `Context`, the current - /// > task _may not be notified_ if sending the event fails due to - /// > the connection handler not being ready at this time. - pub fn notify_handler(&mut self, event: I) -> Result<(), I> { - let cmd = Command::NotifyHandler(event); - match self.task.get_mut() { - TaskInfo::Established { sender, .. } => { - sender.try_send(cmd).map_err(|e| match e.into_inner() { - Command::NotifyHandler(event) => event, - _ => unreachable!("Expect `EstablishedEntry` to point to established task."), - }) - } - TaskInfo::Pending { .. } => { - unreachable!("Expect `EstablishedEntry` to point to established task.") - } - } - } - - /// Checks if `notify_handler` is ready to accept an event. - /// - /// Returns `Ok(())` if the handler is ready to receive an event via `notify_handler`. - /// - /// Returns `Err(())` if the background task associated with the connection - /// is terminating and the connection is about to close. - pub fn poll_ready_notify_handler(&mut self, cx: &mut Context<'_>) -> Poll> { - match self.task.get_mut() { - TaskInfo::Established { sender, .. } => sender.poll_ready(cx).map_err(|_| ()), - TaskInfo::Pending { .. } => { - unreachable!("Expect `EstablishedEntry` to point to established task.") - } - } - } - - /// Sends a close command to the associated background task, - /// thus initiating a graceful active close of the connection. - /// - /// Has no effect if the connection is already closing. - /// - /// When the connection is ultimately closed, [`Event::ConnectionClosed`] - /// is emitted by [`Manager::poll`]. - pub fn start_close(mut self) { - // Clone the sender so that we are guaranteed to have - // capacity for the close command (every sender gets a slot). - match self.task.get_mut() { - TaskInfo::Established { sender, .. } => match sender.clone().try_send(Command::Close) { - Ok(()) => {} - Err(e) => assert!(e.is_disconnected(), "No capacity for close command."), - }, - TaskInfo::Pending { .. } => { - unreachable!("Expect `EstablishedEntry` to point to established task.") - } - } - } - - /// Obtains information about the established connection. - pub fn connected(&self) -> &Connected { - match &self.task.get() { - TaskInfo::Established { connected, .. } => connected, - TaskInfo::Pending { .. } => unreachable!("By Entry::new()"), - } - } - - /// Returns the connection ID. - pub fn id(&self) -> ConnectionId { - ConnectionId(*self.task.key()) - } -} - -/// An entry for a managed connection that is currently being established -/// (i.e. pending). -#[derive(Debug)] -pub struct PendingEntry<'a, I> { - task: hash_map::OccupiedEntry<'a, TaskId, TaskInfo>, -} - -impl<'a, I> PendingEntry<'a, I> { - /// Returns the connection id. - pub fn id(&self) -> ConnectionId { - ConnectionId(*self.task.key()) - } - - /// Aborts the pending connection attempt. - pub fn abort(self) { - self.task.remove(); - } -} diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index a1458fcc06c..4d17bf5009e 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -1,3 +1,4 @@ +// Copyright 2021 Protocol Labs. // Copyright 2018 Parity Technologies (UK) Ltd. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -21,24 +22,37 @@ use crate::{ connection::{ handler::{THandlerError, THandlerInEvent, THandlerOutEvent}, - manager::{self, Manager, ManagerConfig}, - Connected, ConnectionError, ConnectionHandler, ConnectionId, ConnectionLimit, Endpoint, - IncomingInfo, IntoConnectionHandler, OutgoingInfo, PendingConnectionError, PendingPoint, - Substream, + Connected, ConnectionError, ConnectionHandler, ConnectionLimit, Endpoint, IncomingInfo, + IntoConnectionHandler, OutgoingInfo, PendingConnectionError, PendingPoint, Substream, }, muxing::StreamMuxer, network::DialError, transport::TransportError, - ConnectedPoint, Multiaddr, PeerId, + ConnectedPoint, Executor, Multiaddr, PeerId, }; -use either::Either; use fnv::FnvHashMap; use futures::prelude::*; +use futures::{ + channel::{mpsc, oneshot}, + future::Either, + future::{poll_fn, BoxFuture}, + prelude::*, + ready, + stream::FuturesUnordered, +}; use smallvec::SmallVec; use std::{ - collections::HashMap, convert::TryFrom as _, error, fmt, num::NonZeroU32, task::Context, + collections::{hash_map, HashMap}, + convert::TryFrom as _, + error, fmt, + num::NonZeroU32, + pin::Pin, + task::Context, task::Poll, }; +use void::Void; + +mod task; /// A connection `Pool` manages a set of connections for each peer. pub struct Pool { @@ -47,24 +61,57 @@ pub struct Pool { /// The connection counter(s). counters: ConnectionCounters, - /// The connection manager that handles the connection I/O for both - /// established and pending connections. - /// - /// For every established connection there is a corresponding entry in `established`. - manager: Manager, - + // TODO: Update comment? + // TODO: Quite often need a mapping from connectionid -> EstablishedConnectionInfo. /// The managed connections of each peer that are currently considered /// established, as witnessed the associated `ConnectedPoint`. - established: FnvHashMap>, + established: FnvHashMap< + PeerId, + FnvHashMap>>, + >, /// The pending connections that are currently being negotiated. pending: HashMap>, + + // TODO Ex manager + /// Next available identifier for a new connection / task. + next_connection_id: ConnectionId, + + /// Size of the task command buffer (per task). + task_command_buffer_size: usize, + + /// The executor to use for running the background tasks. If `None`, + /// the tasks are kept in `local_spawns` instead and polled on the + /// current thread when the manager is polled for new events. + executor: Option>, + + /// If no `executor` is configured, tasks are kept in this set and + /// polled on the current thread when the manager is polled for new events. + local_spawns: FuturesUnordered + Send>>>, + + // TODO: How about a channel for pending and one for established? Would remove the handler bound in many cases. + /// Sender distributed to managed tasks for reporting events back + /// to the manager. + events_tx: mpsc::Sender>, + + /// Receiver for events reported from managed tasks. + events_rx: mpsc::Receiver>, +} + +#[derive(Debug)] +struct EstablishedConnectionInfo { + peer_id: PeerId, + endpoint: ConnectedPoint, + /// Channel endpoint to send messages to the task. + sender: mpsc::Sender>, } struct PendingConnectionInfo { peer_id: Option, handler: THandler, endpoint: PendingPoint, + /// When dropped, notifies the task which can then terminate. + drop_notifier: oneshot::Sender, } impl fmt::Debug @@ -73,6 +120,7 @@ impl fmt::Debug fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.debug_struct("Pool") .field("counters", &self.counters) + // TODO: Should we add more fields? .finish() } } @@ -212,13 +260,21 @@ impl { /// Creates a new empty `Pool`. - pub fn new(local_id: PeerId, manager_config: ManagerConfig, limits: ConnectionLimits) -> Self { + pub fn new(local_id: PeerId, config: ManagerConfig, limits: ConnectionLimits) -> Self { + let (tx, rx) = mpsc::channel(config.task_event_buffer_size); Pool { local_id, counters: ConnectionCounters::new(limits), - manager: Manager::new(manager_config), established: Default::default(), pending: Default::default(), + + // TODO Ex manager + next_connection_id: ConnectionId(0), + task_command_buffer_size: config.task_command_buffer_size, + executor: config.executor, + local_spawns: FuturesUnordered::new(), + events_tx: tx, + events_rx: rx, } } @@ -227,123 +283,134 @@ impl( + pub fn add_outgoing( &mut self, - future: TFut, + dial: crate::network::concurrent_dial::ConcurrentDial, handler: THandler, - info: IncomingInfo<'_>, - ) -> Result + expected_peer_id: Option, + ) -> Result> where - TFut: Future>> - + Send - + 'static, - // TODO: Bounds still needed here? THandler: IntoConnectionHandler + Send + 'static, THandler::Handler: ConnectionHandler> + Send + 'static, ::OutboundOpenInfo: Send + 'static, TTransErr: error::Error + Send + 'static, - // TODO: Still needed? TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send + 'static, { - // TODO: This is a hack. Fix. - let endpoint = info.clone().to_connected_point(); - // TODO: We loose the handler here. - self.counters.check_max_pending_incoming()?; + if let Err(limit) = self.counters.check_max_pending_outgoing() { + return Err(DialError::ConnectionLimit { limit, handler }); + }; - let id = self.manager.add_pending_incoming(future); - self.counters.inc_pending_incoming(); + let connection_id = self.next_connection_id(); + + let (drop_notifier, mut drop_receiver) = oneshot::channel(); + + let mut events = self.events_tx.clone(); + + self.spawn( + task::new_for_pending_outgoing_connection( + connection_id, + dial, + drop_receiver, + self.events_tx.clone(), + ) + .boxed(), + ); + + self.counters.inc_pending(&PendingPoint::Dialer); self.pending.insert( - id, + connection_id, PendingConnectionInfo { - peer_id: None, + peer_id: expected_peer_id, handler, - endpoint: endpoint.into(), + endpoint: PendingPoint::Dialer, + drop_notifier, }, ); - Ok(id) + Ok(connection_id) } - /// Adds a pending outgoing connection to the pool in the form of a `Future` - /// that establishes and negotiates the connection. + /// Adds a pending incoming connection to the pool in the form of a + /// `Future` that establishes and negotiates the connection. /// - /// Returns an error if the limit of pending outgoing connections + /// Returns an error if the limit of pending incoming connections /// has been reached. - pub fn add_outgoing( + pub fn add_incoming( &mut self, - dial: crate::network::concurrent_dial::ConcurrentDial, + future: TFut, handler: THandler, - expected_peer_id: Option, - ) -> Result> + info: IncomingInfo<'_>, + ) -> Result where + TFut: Future>> + + Send + + 'static, + // TODO: Bounds still needed here? THandler: IntoConnectionHandler + Send + 'static, THandler::Handler: ConnectionHandler> + Send + 'static, ::OutboundOpenInfo: Send + 'static, TTransErr: error::Error + Send + 'static, + // TODO: Still needed? TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send + 'static, { - if let Err(limit) = self.counters.check_max_pending_outgoing() { - return Err(DialError::ConnectionLimit { limit, handler }); - }; + // TODO: This is a hack. Fix. + let endpoint = info.clone().to_connected_point(); + // TODO: We loose the handler here. + self.counters.check_max_pending_incoming()?; - // // Validate the received peer ID as the last step of the pending connection - // // future, so that these errors can be raised before the `handler` is consumed - // // by the background task, which happens when this future resolves to an - // // "established" connection. - // let future = future.and_then({ - // let expected_peer = peer; - // let local_id = self.local_id; - // move |(peer_id, endpoint, muxer)| { - // if let Some(peer) = expected_peer { - // if peer != peer_id { - // return future::err(PendingConnectionError::InvalidPeerId); - // } - // } - // - // if local_id == peer_id { - // return future::err(PendingConnectionError::InvalidPeerId); - // } - // - // let connected = Connected { peer_id, endpoint }; - // future::ready(Ok((connected, muxer))) - // } - // }); - - let id = self.manager.add_pending_outgoing(dial); - self.counters.inc_pending(&PendingPoint::Dialer); + let connection_id = self.next_connection_id(); + + let (drop_notifier, mut drop_receiver) = oneshot::channel(); + + let mut events = self.events_tx.clone(); + + self.spawn( + task::new_for_pending_incoming_connection( + connection_id, + future, + drop_receiver, + self.events_tx.clone(), + ) + .boxed(), + ); + + self.counters.inc_pending_incoming(); self.pending.insert( - id, + connection_id, PendingConnectionInfo { - peer_id: expected_peer_id, + peer_id: None, handler, - endpoint: PendingPoint::Dialer, + endpoint: endpoint.into(), + drop_notifier, }, ); - Ok(id) + Ok(connection_id) } /// Gets an entry representing a connection in the pool. /// /// Returns `None` if the pool has no connection with the given ID. pub fn get(&mut self, id: ConnectionId) -> Option> { - match self.manager.entry(id) { - Some(manager::Entry::Established(entry)) => { - Some(PoolConnection::Established(EstablishedConnection { entry })) - } - Some(manager::Entry::Pending(entry)) => { - Some(PoolConnection::Pending(PendingConnection { - entry, - pending: &mut self.pending, - counters: &mut self.counters, - })) - } - None => None, + if let hash_map::Entry::Occupied(entry) = self.pending.entry(id) { + Some(PoolConnection::Pending(PendingConnection { + entry, + counters: &mut self.counters, + })) + } else { + self.established + .iter_mut() + .find_map(|(_, cs)| match cs.entry(id) { + hash_map::Entry::Occupied(entry) => { + Some(PoolConnection::Established(EstablishedConnection { entry })) + } + hash_map::Entry::Vacant(_) => None, + }) } } @@ -360,23 +427,12 @@ impl Option> { - match self.pending.get(&id) { - Some(PendingConnectionInfo { - peer_id, - endpoint: PendingPoint::Dialer, - handler: _, - }) => match self.manager.entry(id) { - Some(manager::Entry::Pending(entry)) => { - let peer_id = *peer_id; - Some(PendingConnection { - entry, - pending: &mut self.pending, - counters: &mut self.counters, - }) - } - _ => unreachable!("by consistency of `self.pending` with `self.manager`"), - }, - _ => None, + match self.pending.entry(id) { + hash_map::Entry::Occupied(entry) => Some(PendingConnection { + entry, + counters: &mut self.counters, + }), + hash_map::Entry::Vacant(_) => None, } } @@ -399,20 +455,42 @@ impl>(); + + for id in connection_ids.into_iter() { + let established_connection = match conns.entry(id) { + hash_map::Entry::Occupied(entry) => EstablishedConnection { entry }, + hash_map::Entry::Vacant(_) => { + unreachable!("Iterating established connections.") + } + }; + + established_connection.start_close(); } } - for (&id, connection_info) in &self.pending { - if Some(peer) == connection_info.peer_id.as_ref() { - if let Some(manager::Entry::Pending(e)) = self.manager.entry(id) { - e.abort(); - } - } + let pending_connections = self + .pending + .iter() + .filter(|(connection_id, PendingConnectionInfo { peer_id, .. })| { + peer_id.as_ref() == Some(peer) + }) + .map(|(id, _)| *id) + .collect::>(); + + for pending_connection in pending_connections.into_iter() { + let pending_connection = match self.pending.entry(pending_connection) { + hash_map::Entry::Occupied(entry) => PendingConnection { + entry, + counters: &mut self.counters, + }, + hash_map::Entry::Vacant(_) => unreachable!("Iterating pending connections"), + }; + + pending_connection.abort(); } } @@ -425,20 +503,18 @@ impl( &'a mut self, peer: &PeerId, - ) -> EstablishedConnectionIter< - 'a, - impl Iterator, - THandler, - TMuxer, - TTransErr, - > { + ) -> EstablishedConnectionIter<'a, impl Iterator, THandlerInEvent> + { let ids = self .iter_peer_established_info(peer) .map(|(id, _endpoint)| *id) .collect::>() .into_iter(); - EstablishedConnectionIter { pool: self, ids } + EstablishedConnectionIter { + connections: self.established.get_mut(peer), + ids, + } } // /// Returns an iterator for information on all pending incoming connections. @@ -473,10 +549,14 @@ impl impl Iterator + fmt::Debug + '_ { + ) -> impl Iterator { match self.established.get(peer) { - Some(conns) => Either::Left(conns.iter()), - None => Either::Right(std::iter::empty()), + Some(conns) => either::Either::Left( + conns + .iter() + .map(|(id, EstablishedConnectionInfo { endpoint, .. })| (id, endpoint)), + ), + None => either::Either::Right(std::iter::empty()), } } @@ -496,6 +576,35 @@ impl ConnectionId { + let connection_id = self.next_connection_id; + self.next_connection_id.0 += 1; + + connection_id + } + + fn spawn(&mut self, task: BoxFuture<'static, ()>) { + if let Some(executor) = &mut self.executor { + executor.exec(task); + } else { + self.local_spawns.push(task); + } + } + + pub fn add_established( + &mut self, + connection_id: ConnectionId, + mut connection: crate::connection::Connection, + connected: Connected, + ) where + TMuxer: StreamMuxer + Send + Sync + 'static, + TMuxer::OutboundSubstream: Send + 'static, + THandler: IntoConnectionHandler + Send + 'static, + THandler::Handler: ConnectionHandler> + Send + 'static, + ::OutboundOpenInfo: Send + 'static, + { + } + /// Polls the connection pool for events. /// /// > **Note**: We use a regular `poll` method instead of implementing `Stream`, @@ -511,71 +620,29 @@ impl::OutboundOpenInfo: Send + 'static, TMuxer::OutboundSubstream: Send + 'static, { - // Poll the connection `Manager`. + // Advance the content of `local_spawns`. + while let Poll::Ready(Some(_)) = self.local_spawns.poll_next_unpin(cx) {} + + // Poll for the first event for which the manager still has a registered task, if any. loop { - let item = match self.manager.poll(cx) { - Poll::Ready(item) => item, + let event = match self.events_rx.poll_next_unpin(cx) { + Poll::Ready(Some(event)) => event, Poll::Pending => return Poll::Pending, + Poll::Ready(None) => unreachable!("Pool holds both sender and receiver."), }; - match item { - manager::Event::PendingConnectionError { id, error } => { - let PendingConnectionInfo { - peer_id, - handler, - endpoint, - } = self - .pending - .remove(&id) - .expect("Entry in `self.pending` for previously pending connection."); - - self.counters.dec_pending(&endpoint); - return Poll::Ready(PoolEvent::PendingConnectionError { - id, - endpoint, - error, - handler, - peer: peer_id, - pool: self, - }); - } - manager::Event::ConnectionClosed { - id, - connected, - error, - handler, - } => { - let num_established = - if let Some(conns) = self.established.get_mut(&connected.peer_id) { - if let Some(endpoint) = conns.remove(&id) { - self.counters.dec_established(&endpoint); - } - u32::try_from(conns.len()).unwrap() - } else { - 0 - }; - if num_established == 0 { - self.established.remove(&connected.peer_id); - } - return Poll::Ready(PoolEvent::ConnectionClosed { - id, - connected, - error, - num_established, - pool: self, - handler, - }); - } - manager::Event::ConnectionEstablished { + match event { + task::Event::ConnectionEstablished { id, peer_id, - outgoing, muxer, + outgoing, } => { let PendingConnectionInfo { peer_id: expected_peer_id, handler, endpoint, + drop_notifier, } = self .pending .remove(&id) @@ -641,7 +708,15 @@ impl { @@ -679,31 +769,39 @@ impl unreachable!("since `entry` is an `EstablishedEntry`."), } } - manager::Event::ConnectionEvent { entry, event } => { - let id = entry.id(); - match self.get(id) { - Some(PoolConnection::Established(connection)) => { - return Poll::Ready(PoolEvent::ConnectionEvent { connection, event }) - } - _ => unreachable!("since `entry` is an `EstablishedEntry`."), + task::Event::Notify { id, event } => match self.get(id) { + Some(PoolConnection::Established(connection)) => { + return Poll::Ready(PoolEvent::ConnectionEvent { connection, event }) + } + _ => unreachable!("since `entry` is an `EstablishedEntry`."), + }, + task::Event::PendingFailed { id, error } => { + if let Some(PendingConnectionInfo { + peer_id, + handler, + endpoint, + drop_notifier, + }) = self.pending.remove(&id) + { + self.counters.dec_pending(&endpoint); + return Poll::Ready(PoolEvent::PendingConnectionError { + id, + endpoint, + error, + handler, + peer: peer_id, + pool: self, + }); } } - manager::Event::AddressChange { - entry, - new_endpoint, - old_endpoint, - } => { - let id = entry.id(); - - match self.established.get_mut(&entry.connected().peer_id) { - Some(list) => { - *list.get_mut(&id).expect( - "state inconsistency: entry is `EstablishedEntry` but absent \ - from `established`", - ) = new_endpoint.clone() - } - None => unreachable!("since `entry` is an `EstablishedEntry`."), - }; + task::Event::AddressChange { id, new_address } => { + let mut connection = self + .get_established(id) + .expect("Receive `AddressChange` event from established connection."); + let mut new_endpoint = connection.endpoint().clone(); + new_endpoint.set_remote_address(new_address); + let old_endpoint = + std::mem::replace(connection.endpoint_mut(), new_endpoint.clone()); match self.get(id) { Some(PoolConnection::Established(connection)) => { @@ -716,6 +814,33 @@ impl unreachable!("since `entry` is an `EstablishedEntry`."), } } + task::Event::Closed { id, error, handler } => { + // TODO: Just to get peerID :/ + let peer_id = self + .get_established(id) + .expect("Receive `Closed` event from established connection.") + .peer_id(); + + let connections = self + .established + .get_mut(&peer_id) + .expect("`Closed` event for established connection"); + let EstablishedConnectionInfo { endpoint, .. } = + connections.remove(&id).expect("Connection to be present"); + self.counters.dec_established(&endpoint); + let num_established = u32::try_from(connections.len()).unwrap(); + if num_established == 0 { + self.established.remove(&peer_id); + } + return Poll::Ready(PoolEvent::ConnectionClosed { + id, + connected: Connected { endpoint, peer_id }, + error, + num_established, + pool: self, + handler, + }); + } } } } @@ -729,50 +854,36 @@ pub enum PoolConnection<'a, THandler: IntoConnectionHandler> { /// A pending connection in a pool. pub struct PendingConnection<'a, THandler: IntoConnectionHandler> { - entry: manager::PendingEntry<'a, THandlerInEvent>, - pending: &'a mut HashMap>, + entry: hash_map::OccupiedEntry<'a, ConnectionId, PendingConnectionInfo>, counters: &'a mut ConnectionCounters, } impl PendingConnection<'_, THandler> { /// Returns the local connection ID. pub fn id(&self) -> ConnectionId { - self.entry.id() + *self.entry.key() } /// Returns the (expected) identity of the remote peer, if known. pub fn peer_id(&self) -> &Option { - &self - .pending - .get(&self.entry.id()) - .expect("`entry` is a pending entry") - .peer_id + &self.entry.get().peer_id } /// Returns information about this endpoint of the connection. pub fn endpoint(&self) -> &PendingPoint { - &self - .pending - .get(&self.entry.id()) - .expect("`entry` is a pending entry") - .endpoint + &self.entry.get().endpoint } /// Aborts the connection attempt, closing the connection. pub fn abort(self) { - let endpoint = self - .pending - .remove(&self.entry.id()) - .expect("`entry` is a pending entry") - .endpoint; - self.counters.dec_pending(&endpoint); - self.entry.abort(); + self.counters.dec_pending(&self.entry.get().endpoint); + self.entry.remove(); } } /// An established connection in a pool. pub struct EstablishedConnection<'a, TInEvent> { - entry: manager::EstablishedEntry<'a, TInEvent>, + entry: hash_map::OccupiedEntry<'a, ConnectionId, EstablishedConnectionInfo>, } impl fmt::Debug for EstablishedConnection<'_, TInEvent> @@ -787,23 +898,28 @@ where } impl EstablishedConnection<'_, TInEvent> { - pub fn connected(&self) -> &Connected { - self.entry.connected() - } + // TODO: Needed? + // pub fn connected(&self) -> &Connected { + // self.entry.connected() + // } /// Returns information about the connected endpoint. pub fn endpoint(&self) -> &ConnectedPoint { - &self.entry.connected().endpoint + &self.entry.get().endpoint + } + + fn endpoint_mut(&mut self) -> &mut ConnectedPoint { + &mut self.entry.get_mut().endpoint } /// Returns the identity of the connected peer. pub fn peer_id(&self) -> PeerId { - self.entry.connected().peer_id + self.entry.get().peer_id } /// Returns the local connection ID. pub fn id(&self) -> ConnectionId { - self.entry.id() + *self.entry.key() } /// (Asynchronously) sends an event to the connection handler. @@ -817,7 +933,15 @@ impl EstablishedConnection<'_, TInEvent> { /// of `notify_handler`, it only fails if the connection is now about /// to close. pub fn notify_handler(&mut self, event: TInEvent) -> Result<(), TInEvent> { - self.entry.notify_handler(event) + let cmd = task::Command::NotifyHandler(event); + self.entry + .get_mut() + .sender + .try_send(cmd) + .map_err(|e| match e.into_inner() { + task::Command::NotifyHandler(event) => event, + _ => unreachable!("Expect failed send to return initial event."), + }) } /// Checks if `notify_handler` is ready to accept an event. @@ -827,48 +951,52 @@ impl EstablishedConnection<'_, TInEvent> { /// Returns `Err(())` if the background task associated with the connection /// is terminating and the connection is about to close. pub fn poll_ready_notify_handler(&mut self, cx: &mut Context<'_>) -> Poll> { - self.entry.poll_ready_notify_handler(cx) + self.entry.get_mut().sender.poll_ready(cx).map_err(|_| ()) } /// Initiates a graceful close of the connection. /// /// Has no effect if the connection is already closing. - pub fn start_close(self) { - self.entry.start_close() + pub fn start_close(mut self) { + // Clone the sender so that we are guaranteed to have + // capacity for the close command (every sender gets a slot). + match self + .entry + .get_mut() + .sender + .clone() + .try_send(task::Command::Close) + { + Ok(()) => {} + Err(e) => assert!(e.is_disconnected(), "No capacity for close command."), + }; } } /// An iterator over established connections in a pool. -pub struct EstablishedConnectionIter<'a, I, THandler: IntoConnectionHandler, TMuxer, TTransErr> { - pool: &'a mut Pool, +pub struct EstablishedConnectionIter<'a, I, TInEvent> { + connections: Option<&'a mut FnvHashMap>>, ids: I, } // Note: Ideally this would be an implementation of `Iterator`, but that // requires GATs (cf. https://github.com/rust-lang/rust/issues/44265) and // a different definition of `Iterator`. -impl<'a, I, THandler: IntoConnectionHandler, TMuxer, TTransErr> - EstablishedConnectionIter<'a, I, THandler, TMuxer, TTransErr> +impl<'a, I, TInEvent> EstablishedConnectionIter<'a, I, TInEvent> where I: Iterator, - TTransErr: Send + 'static, - TMuxer: Send + 'static, { /// Obtains the next connection, if any. #[allow(clippy::should_implement_trait)] - pub fn next(&mut self) -> Option>> { - while let Some(id) = self.ids.next() { - if self.pool.manager.is_established(&id) { - // (*) - match self.pool.manager.entry(id) { - Some(manager::Entry::Established(entry)) => { - return Some(EstablishedConnection { entry }) - } - _ => panic!("Established entry not found in manager."), // see (*) - } + pub fn next(&mut self) -> Option> { + if let (Some(id), Some(connections)) = (self.ids.next(), self.connections.as_mut()) { + match connections.entry(id) { + hash_map::Entry::Occupied(entry) => Some(EstablishedConnection { entry }), + hash_map::Entry::Vacant(_) => unreachable!("Established entry not found in pool."), } + } else { + None } - None } /// Turns the iterator into an iterator over just the connection IDs. @@ -877,22 +1005,18 @@ where } /// Returns the first connection, if any, consuming the iterator. - pub fn into_first<'b>(mut self) -> Option>> + pub fn into_first<'b>(mut self) -> Option> where 'a: 'b, { - while let Some(id) = self.ids.next() { - if self.pool.manager.is_established(&id) { - // (*) - match self.pool.manager.entry(id) { - Some(manager::Entry::Established(entry)) => { - return Some(EstablishedConnection { entry }) - } - _ => panic!("Established entry not found in manager."), // see (*) - } + if let (Some(id), Some(connections)) = (self.ids.next(), self.connections) { + match connections.entry(id) { + hash_map::Entry::Occupied(entry) => Some(EstablishedConnection { entry }), + hash_map::Entry::Vacant(_) => unreachable!("Established entry not found in pool."), } + } else { + None } - None } } @@ -988,16 +1112,6 @@ impl ConnectionCounters { } } - // TODO: Still needed? - // fn dec_pending_incoming(&mut self) { - // self.pending_incoming -= 1; - // } - - // TODO: Still needed? - // fn dec_pending_outgoing(&mut self) { - // self.pending_outgoing -= 1; - // } - fn inc_established(&mut self, endpoint: &ConnectedPoint) { match endpoint { ConnectedPoint::Dialer { .. } => { @@ -1059,8 +1173,8 @@ impl ConnectionCounters { } /// Counts the number of established connections to the given peer. -fn num_peer_established( - established: &FnvHashMap>, +fn num_peer_established( + established: &FnvHashMap>>, peer: PeerId, ) -> u32 { established.get(&peer).map_or(0, |conns| { @@ -1124,3 +1238,47 @@ impl ConnectionLimits { self } } + +/// Connection identifier. +// TODO: Should this live in connection.rs? +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct ConnectionId(usize); + +impl ConnectionId { + /// Creates a `ConnectionId` from a non-negative integer. + /// + /// This is primarily useful for creating connection IDs + /// in test environments. There is in general no guarantee + /// that all connection IDs are based on non-negative integers. + pub fn new(id: usize) -> Self { + ConnectionId(id) + } +} + +/// Configuration options when creating a [`Manager`]. +/// +/// The default configuration specifies no dedicated task executor, a +/// task event buffer size of 32, and a task command buffer size of 7. +// TODO: Rename +// TODO: Remove exhaustive. +#[non_exhaustive] +pub struct ManagerConfig { + /// Executor to use to spawn tasks. + pub executor: Option>, + + /// Size of the task command buffer (per task). + pub task_command_buffer_size: usize, + + /// Size of the task event buffer (for all tasks). + pub task_event_buffer_size: usize, +} + +impl Default for ManagerConfig { + fn default() -> Self { + ManagerConfig { + executor: None, + task_event_buffer_size: 32, + task_command_buffer_size: 7, + } + } +} diff --git a/core/src/connection/pool/task.rs b/core/src/connection/pool/task.rs new file mode 100644 index 00000000000..b7ccad0b390 --- /dev/null +++ b/core/src/connection/pool/task.rs @@ -0,0 +1,267 @@ +// Copyright 2021 Protocol Labs. +// Copyright 2018 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//! Async functions driving pending and established connections in the form of a task. + +use super::ConnectionId; +use crate::{ + connection::{ + self, + handler::{THandlerError, THandlerInEvent, THandlerOutEvent}, + Connected, ConnectionError, ConnectionHandler, ConnectionLimit, Endpoint, IncomingInfo, + IntoConnectionHandler, OutgoingInfo, PendingConnectionError, PendingPoint, Substream, + }, + muxing::StreamMuxer, + network::DialError, + transport::TransportError, + ConnectedPoint, Executor, Multiaddr, PeerId, +}; +use fnv::FnvHashMap; +use futures::prelude::*; +use futures::{ + channel::{mpsc, oneshot}, + future::Either, + future::{poll_fn, BoxFuture}, + prelude::*, + ready, + stream::FuturesUnordered, +}; +use smallvec::SmallVec; +use std::{ + collections::HashMap, convert::TryFrom as _, error, fmt, num::NonZeroU32, pin::Pin, + task::Context, task::Poll, +}; +use void::Void; + +/// Commands that can be sent to a task. +#[derive(Debug)] +pub enum Command { + /// Notify the connection handler of an event. + NotifyHandler(T), + /// Gracefully close the connection (active close) before + /// terminating the task. + Close, +} + +/// Events that a task can emit to its manager. +#[derive(Debug)] +pub enum Event { + // TODO: Remove most of these + ConnectionEstablished { + id: ConnectionId, + peer_id: PeerId, + muxer: TMuxer, + // TODO: Document errors in a success message. + outgoing: Option<(Multiaddr, Vec<(Multiaddr, TransportError)>)>, + }, + /// A pending connection failed. + PendingFailed { + id: ConnectionId, + error: PendingConnectionError, + }, + /// A node we are connected to has changed its address. + AddressChange { + id: ConnectionId, + new_address: Multiaddr, + }, + /// Notify the manager of an event from the connection. + Notify { + id: ConnectionId, + event: THandlerOutEvent, + }, + /// A connection closed, possibly due to an error. + /// + /// If `error` is `None`, the connection has completed + /// an active orderly close. + Closed { + id: ConnectionId, + error: Option>>, + handler: THandler::Handler, + }, +} + +// TODO: Still needed? +impl Event { + pub fn id(&self) -> &ConnectionId { + match self { + Event::ConnectionEstablished { id, .. } => id, + Event::PendingFailed { id, .. } => id, + Event::AddressChange { id, .. } => id, + Event::Notify { id, .. } => id, + Event::Closed { id, .. } => id, + } + } +} +pub async fn new_for_pending_outgoing_connection( + connection_id: ConnectionId, + dial: crate::network::concurrent_dial::ConcurrentDial, + drop_receiver: oneshot::Receiver, + mut events: mpsc::Sender>, +) where + THandler: IntoConnectionHandler + Send + 'static, +{ + match futures::future::select(drop_receiver, Box::pin(dial)).await { + Either::Left((Err(oneshot::Canceled), _)) => { + events + .send(Event::PendingFailed { + id: connection_id, + error: PendingConnectionError::Aborted, + }) + .await; + } + Either::Left((Ok(v), _)) => void::unreachable(v), + Either::Right((Ok((peer_id, address, muxer, errors)), _)) => { + events + .send(Event::ConnectionEstablished { + id: connection_id, + peer_id, + muxer, + outgoing: Some((address, errors)), + }) + .await; + } + Either::Right((Err(e), _)) => { + events + .send(Event::PendingFailed { + id: connection_id, + error: PendingConnectionError::TransportDial(e), + }) + .await; + } + } +} + +pub async fn new_for_pending_incoming_connection( + connection_id: ConnectionId, + future: TFut, + drop_receiver: oneshot::Receiver, + mut events: mpsc::Sender>, +) where + TFut: Future>> + + Send + + 'static, + THandler: IntoConnectionHandler + Send + 'static, +{ + match futures::future::select(drop_receiver, Box::pin(future)).await { + Either::Left((Err(oneshot::Canceled), _)) => { + events + .send(Event::PendingFailed { + id: connection_id, + error: PendingConnectionError::Aborted, + }) + .await; + } + Either::Left((Ok(v), _)) => void::unreachable(v), + Either::Right((Ok((peer_id, muxer)), _)) => { + events + .send(Event::ConnectionEstablished { + id: connection_id, + peer_id, + muxer, + outgoing: None, + }) + .await; + } + Either::Right((Err(e), _)) => { + events + .send(Event::PendingFailed { + id: connection_id, + error: e, + }) + .await; + } + } +} + +pub async fn new_for_established_connection( + connection_id: ConnectionId, + mut connection: crate::connection::Connection, + mut command_receiver: mpsc::Receiver>>, + mut events: mpsc::Sender>, +) where + TMuxer: StreamMuxer + Send + Sync + 'static, + TMuxer::OutboundSubstream: Send + 'static, + THandler: IntoConnectionHandler, + THandler::Handler: ConnectionHandler> + Send + 'static, + ::OutboundOpenInfo: Send + 'static, +{ + loop { + match futures::future::select(command_receiver.next(), connection.next()).await { + Either::Left((Some(command), _)) => match command { + Command::NotifyHandler(event) => connection.inject_event(event), + Command::Close => { + command_receiver.close(); + let (handler, closing_muxer) = connection.close(); + + let error = closing_muxer.await.err().map(ConnectionError::IO); + events + .send(Event::Closed { + id: connection_id, + error, + handler, + }) + .await; + return; + } + }, + + // The manager has disappeared; abort. + Either::Left((None, _)) => return, + + Either::Right((Some(event), _)) => { + match event { + Ok(connection::Event::Handler(event)) => { + events + .send(Event::Notify { + id: connection_id, + event, + }) + .await; + } + Ok(connection::Event::AddressChange(new_address)) => { + events + .send(Event::AddressChange { + id: connection_id, + new_address, + }) + .await; + } + Err(error) => { + command_receiver.close(); + let (handler, _closing_muxer) = connection.close(); + // Terminate the task with the error, dropping the connection. + events + .send(Event::Closed { + id: connection_id, + error: Some(error), + handler, + }) + .await; + return; + } + } + } + Either::Right((None, _)) => { + unreachable!("Connection is an infinite stream"); + } + } + } +} diff --git a/core/src/network.rs b/core/src/network.rs index 75ce6e39628..861c8826893 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -30,8 +30,7 @@ pub use peer::Peer; use crate::{ connection::{ handler::{THandlerInEvent, THandlerOutEvent}, - manager::ManagerConfig, - pool::{Pool, PoolEvent}, + pool::{ManagerConfig, Pool, PoolEvent}, ConnectionHandler, ConnectionId, ConnectionLimit, Endpoint, IncomingInfo, IntoConnectionHandler, ListenerId, ListenersEvent, ListenersStream, OutgoingInfo, PendingConnectionError, PendingPoint, Substream, diff --git a/core/src/network/peer.rs b/core/src/network/peer.rs index 53b50cc3812..8ea30e13487 100644 --- a/core/src/network/peer.rs +++ b/core/src/network/peer.rs @@ -285,12 +285,8 @@ where /// Gets an iterator over all established connections to the peer. pub fn connections( &mut self, - ) -> EstablishedConnectionIter< - impl Iterator, - THandler, - TMuxer, - TTrans::Error, - > { + ) -> EstablishedConnectionIter, THandlerInEvent> + { self.network.pool.iter_peer_established(&self.peer_id) } @@ -323,7 +319,11 @@ where .field("peer_id", &self.peer_id) .field( "established", - &self.network.pool.iter_peer_established_info(&self.peer_id), + &self + .network + .pool + .iter_peer_established_info(&self.peer_id) + .collect::>(), ) .field("attempts", &self.network.dialing.get(&self.peer_id)) .finish() @@ -437,7 +437,11 @@ where .field("peer_id", &self.peer_id) .field( "established", - &self.network.pool.iter_peer_established_info(&self.peer_id), + &self + .network + .pool + .iter_peer_established_info(&self.peer_id) + .collect::>(), ) .field("attempts", &self.network.dialing.get(&self.peer_id)) .finish() From 794ca73ea8f84e010bfaa7a0b3ed1ea58cef360f Mon Sep 17 00:00:00 2001 From: Max Inden Date: Tue, 5 Oct 2021 17:37:04 +0200 Subject: [PATCH 24/67] core/src/connection: Use different channel for pending and established --- core/src/connection.rs | 16 -- core/src/connection/pool.rs | 322 +++++++++++++++---------------- core/src/connection/pool/task.rs | 93 ++++----- core/src/network.rs | 75 ++----- 4 files changed, 222 insertions(+), 284 deletions(-) diff --git a/core/src/connection.rs b/core/src/connection.rs index 81df440bce9..c70bc3ae494 100644 --- a/core/src/connection.rs +++ b/core/src/connection.rs @@ -344,22 +344,6 @@ impl<'a> IncomingInfo<'a> { } } -/// Borrowed information about an outgoing connection currently being negotiated. -#[derive(Debug, Copy, Clone)] -pub struct OutgoingInfo<'a> { - pub address: &'a Multiaddr, - pub peer_id: Option<&'a PeerId>, -} - -impl<'a> OutgoingInfo<'a> { - /// Builds a `ConnectedPoint` corresponding to the outgoing connection. - pub fn to_connected_point(&self) -> ConnectedPoint { - ConnectedPoint::Dialer { - address: self.address.clone(), - } - } -} - /// Information about a connection limit. #[derive(Debug, Clone)] pub struct ConnectionLimit { diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index 4d17bf5009e..ae4abdb87fa 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -22,8 +22,8 @@ use crate::{ connection::{ handler::{THandlerError, THandlerInEvent, THandlerOutEvent}, - Connected, ConnectionError, ConnectionHandler, ConnectionLimit, Endpoint, IncomingInfo, - IntoConnectionHandler, OutgoingInfo, PendingConnectionError, PendingPoint, Substream, + Connected, ConnectionError, ConnectionHandler, ConnectionLimit, IncomingInfo, + IntoConnectionHandler, PendingConnectionError, PendingPoint, Substream, }, muxing::StreamMuxer, network::DialError, @@ -34,9 +34,7 @@ use fnv::FnvHashMap; use futures::prelude::*; use futures::{ channel::{mpsc, oneshot}, - future::Either, future::{poll_fn, BoxFuture}, - prelude::*, ready, stream::FuturesUnordered, }; @@ -61,10 +59,7 @@ pub struct Pool { /// The connection counter(s). counters: ConnectionCounters, - // TODO: Update comment? - // TODO: Quite often need a mapping from connectionid -> EstablishedConnectionInfo. - /// The managed connections of each peer that are currently considered - /// established, as witnessed the associated `ConnectedPoint`. + /// The managed connections of each peer that are currently considered established. established: FnvHashMap< PeerId, FnvHashMap>>, @@ -73,7 +68,6 @@ pub struct Pool { /// The pending connections that are currently being negotiated. pending: HashMap>, - // TODO Ex manager /// Next available identifier for a new connection / task. next_connection_id: ConnectionId, @@ -82,20 +76,26 @@ pub struct Pool { /// The executor to use for running the background tasks. If `None`, /// the tasks are kept in `local_spawns` instead and polled on the - /// current thread when the manager is polled for new events. + /// current thread when the [`Pool`] is polled for new events. executor: Option>, /// If no `executor` is configured, tasks are kept in this set and - /// polled on the current thread when the manager is polled for new events. + /// polled on the current thread when the [`Pool`] is polled for new events. local_spawns: FuturesUnordered + Send>>>, - // TODO: How about a channel for pending and one for established? Would remove the handler bound in many cases. - /// Sender distributed to managed tasks for reporting events back - /// to the manager. - events_tx: mpsc::Sender>, + /// Sender distributed to pending tasks for reporting events back + /// to the pool. + pending_connection_events_tx: mpsc::Sender>, - /// Receiver for events reported from managed tasks. - events_rx: mpsc::Receiver>, + /// Receiver for events reported from pending tasks. + pending_connection_events_rx: mpsc::Receiver>, + + /// Sender distributed to established tasks for reporting events back + /// to the pool. + established_connection_events_tx: mpsc::Sender>, + + /// Receiver for events reported from established tasks. + established_connection_events_rx: mpsc::Receiver>, } #[derive(Debug)] @@ -260,21 +260,24 @@ impl { /// Creates a new empty `Pool`. - pub fn new(local_id: PeerId, config: ManagerConfig, limits: ConnectionLimits) -> Self { - let (tx, rx) = mpsc::channel(config.task_event_buffer_size); + pub fn new(local_id: PeerId, config: PoolConfig, limits: ConnectionLimits) -> Self { + let (pending_connection_events_tx, pending_connection_events_rx) = + mpsc::channel(config.task_event_buffer_size); + let (established_connection_events_tx, established_connection_events_rx) = + mpsc::channel(config.task_event_buffer_size); Pool { local_id, counters: ConnectionCounters::new(limits), established: Default::default(), pending: Default::default(), - - // TODO Ex manager next_connection_id: ConnectionId(0), task_command_buffer_size: config.task_command_buffer_size, executor: config.executor, local_spawns: FuturesUnordered::new(), - events_tx: tx, - events_rx: rx, + pending_connection_events_tx, + pending_connection_events_rx, + established_connection_events_tx, + established_connection_events_rx, } } @@ -308,16 +311,14 @@ impl>(); @@ -517,32 +514,20 @@ impl impl Iterator> { - // self.iter_pending_info() - // .filter_map(|(_, ref endpoint, _)| match endpoint { - // ConnectedPoint::Listener { - // local_addr, - // send_back_addr, - // } => Some(IncomingInfo { - // local_addr, - // send_back_addr, - // }), - // ConnectedPoint::Dialer { .. } => None, - // }) - // } - - // /// Returns an iterator for information on all pending outgoing connections. - // pub fn iter_pending_outgoing(&self) -> impl Iterator> { - // self.iter_pending_info() - // .filter_map(|(_, ref endpoint, peer_id)| match endpoint { - // ConnectedPoint::Listener { .. } => None, - // ConnectedPoint::Dialer { address } => Some(OutgoingInfo { - // address, - // peer_id: peer_id.as_ref(), - // }), - // }) - // } + /// Returns an iterator for information on all pending incoming connections. + pub fn iter_pending_incoming(&self) -> impl Iterator> { + self.iter_pending_info() + .filter_map(|(_, ref endpoint, _)| match endpoint { + PendingPoint::Listener { + local_addr, + send_back_addr, + } => Some(IncomingInfo { + local_addr, + send_back_addr, + }), + PendingPoint::Dialer => None, + }) + } /// Returns an iterator over all connection IDs and associated endpoints /// of established connections to `peer` known to the pool. @@ -560,15 +545,20 @@ impl impl Iterator)> + '_ { - // self.pending - // .iter() - // .map(|(id, (endpoint, info))| (id, endpoint, info)) - // } + /// Returns an iterator over all pending connection IDs together + /// with associated endpoints and expected peer IDs in the pool. + pub fn iter_pending_info( + &self, + ) -> impl Iterator)> + '_ { + self.pending.iter().map( + |( + id, + PendingConnectionInfo { + peer_id, endpoint, .. + }, + )| (id, endpoint, peer_id), + ) + } /// Returns an iterator over all connected peers, i.e. those that have /// at least one established connection in the pool. @@ -591,20 +581,6 @@ impl, - connected: Connected, - ) where - TMuxer: StreamMuxer + Send + Sync + 'static, - TMuxer::OutboundSubstream: Send + 'static, - THandler: IntoConnectionHandler + Send + 'static, - THandler::Handler: ConnectionHandler> + Send + 'static, - ::OutboundOpenInfo: Send + 'static, - { - } - /// Polls the connection pool for events. /// /// > **Note**: We use a regular `poll` method instead of implementing `Stream`, @@ -620,19 +596,100 @@ impl::OutboundOpenInfo: Send + 'static, TMuxer::OutboundSubstream: Send + 'static, { - // Advance the content of `local_spawns`. + // Advance the tasks in `local_spawns`. while let Poll::Ready(Some(_)) = self.local_spawns.poll_next_unpin(cx) {} - // Poll for the first event for which the manager still has a registered task, if any. + // Poll for events of established connections. + // + // Note that established connections are polled before pending connections, thus + // prioritizing established connections over pending connections. + match self.established_connection_events_rx.poll_next_unpin(cx) { + Poll::Pending => {} + Poll::Ready(None) => unreachable!("Pool holds both sender and receiver."), + + Poll::Ready(Some(task::EstablishedConnectionEvent::Notify { id, peer_id, event })) => { + match self + .established + .get_mut(&peer_id) + .expect("Receive `Notify` event for established peer.") + .entry(id) + { + hash_map::Entry::Occupied(entry) => { + return Poll::Ready(PoolEvent::ConnectionEvent { + connection: EstablishedConnection { entry }, + event, + }) + } + hash_map::Entry::Vacant(_) => { + unreachable!("Receive `Notify` event from established connection") + } + } + } + Poll::Ready(Some(task::EstablishedConnectionEvent::AddressChange { + id, + peer_id, + new_address, + })) => { + let connection = self + .established + .get_mut(&peer_id) + .expect("Receive `AddressChange` event for established peer.") + .get_mut(&id) + .expect("Receive `AddressChange` event from established connection"); + let mut new_endpoint = connection.endpoint.clone(); + new_endpoint.set_remote_address(new_address); + let old_endpoint = + std::mem::replace(&mut connection.endpoint, new_endpoint.clone()); + + match self.get(id) { + Some(PoolConnection::Established(connection)) => { + return Poll::Ready(PoolEvent::AddressChange { + connection, + new_endpoint, + old_endpoint, + }) + } + _ => unreachable!("since `entry` is an `EstablishedEntry`."), + } + } + Poll::Ready(Some(task::EstablishedConnectionEvent::Closed { + id, + peer_id, + error, + handler, + })) => { + let connections = self + .established + .get_mut(&peer_id) + .expect("`Closed` event for established connection"); + let EstablishedConnectionInfo { endpoint, .. } = + connections.remove(&id).expect("Connection to be present"); + self.counters.dec_established(&endpoint); + let num_established = u32::try_from(connections.len()).unwrap(); + if num_established == 0 { + self.established.remove(&peer_id); + } + return Poll::Ready(PoolEvent::ConnectionClosed { + id, + connected: Connected { endpoint, peer_id }, + error, + num_established, + pool: self, + handler, + }); + } + } + + // Poll for events of pending connections. loop { - let event = match self.events_rx.poll_next_unpin(cx) { + let event = match self.pending_connection_events_rx.poll_next_unpin(cx) { Poll::Ready(Some(event)) => event, - Poll::Pending => return Poll::Pending, + Poll::Pending => break, Poll::Ready(None) => unreachable!("Pool holds both sender and receiver."), }; match event { - task::Event::ConnectionEstablished { + task::PendingConnectionEvent::ConnectionEstablished { id, peer_id, muxer, @@ -642,7 +699,7 @@ impl unreachable!("since `entry` is an `EstablishedEntry`."), } } - task::Event::Notify { id, event } => match self.get(id) { - Some(PoolConnection::Established(connection)) => { - return Poll::Ready(PoolEvent::ConnectionEvent { connection, event }) - } - _ => unreachable!("since `entry` is an `EstablishedEntry`."), - }, - task::Event::PendingFailed { id, error } => { + task::PendingConnectionEvent::PendingFailed { id, error } => { if let Some(PendingConnectionInfo { peer_id, handler, endpoint, - drop_notifier, + drop_notifier: _, }) = self.pending.remove(&id) { self.counters.dec_pending(&endpoint); @@ -794,55 +846,10 @@ impl { - let mut connection = self - .get_established(id) - .expect("Receive `AddressChange` event from established connection."); - let mut new_endpoint = connection.endpoint().clone(); - new_endpoint.set_remote_address(new_address); - let old_endpoint = - std::mem::replace(connection.endpoint_mut(), new_endpoint.clone()); - - match self.get(id) { - Some(PoolConnection::Established(connection)) => { - return Poll::Ready(PoolEvent::AddressChange { - connection, - new_endpoint, - old_endpoint, - }) - } - _ => unreachable!("since `entry` is an `EstablishedEntry`."), - } - } - task::Event::Closed { id, error, handler } => { - // TODO: Just to get peerID :/ - let peer_id = self - .get_established(id) - .expect("Receive `Closed` event from established connection.") - .peer_id(); - - let connections = self - .established - .get_mut(&peer_id) - .expect("`Closed` event for established connection"); - let EstablishedConnectionInfo { endpoint, .. } = - connections.remove(&id).expect("Connection to be present"); - self.counters.dec_established(&endpoint); - let num_established = u32::try_from(connections.len()).unwrap(); - if num_established == 0 { - self.established.remove(&peer_id); - } - return Poll::Ready(PoolEvent::ConnectionClosed { - id, - connected: Connected { endpoint, peer_id }, - error, - num_established, - pool: self, - handler, - }); - } } } + + Poll::Pending } } @@ -898,20 +905,11 @@ where } impl EstablishedConnection<'_, TInEvent> { - // TODO: Needed? - // pub fn connected(&self) -> &Connected { - // self.entry.connected() - // } - /// Returns information about the connected endpoint. pub fn endpoint(&self) -> &ConnectedPoint { &self.entry.get().endpoint } - fn endpoint_mut(&mut self) -> &mut ConnectedPoint { - &mut self.entry.get_mut().endpoint - } - /// Returns the identity of the connected peer. pub fn peer_id(&self) -> PeerId { self.entry.get().peer_id @@ -1255,27 +1253,25 @@ impl ConnectionId { } } -/// Configuration options when creating a [`Manager`]. +/// Configuration options when creating a [`Pool`]. /// /// The default configuration specifies no dedicated task executor, a /// task event buffer size of 32, and a task command buffer size of 7. -// TODO: Rename -// TODO: Remove exhaustive. -#[non_exhaustive] -pub struct ManagerConfig { +pub struct PoolConfig { /// Executor to use to spawn tasks. pub executor: Option>, /// Size of the task command buffer (per task). pub task_command_buffer_size: usize, - /// Size of the task event buffer (for all tasks). + /// Size of the pending connection task event buffer and the established connection task event + /// buffer. pub task_event_buffer_size: usize, } -impl Default for ManagerConfig { +impl Default for PoolConfig { fn default() -> Self { - ManagerConfig { + PoolConfig { executor: None, task_event_buffer_size: 32, task_command_buffer_size: 7, diff --git a/core/src/connection/pool/task.rs b/core/src/connection/pool/task.rs index b7ccad0b390..16283207aaa 100644 --- a/core/src/connection/pool/task.rs +++ b/core/src/connection/pool/task.rs @@ -26,28 +26,17 @@ use crate::{ connection::{ self, handler::{THandlerError, THandlerInEvent, THandlerOutEvent}, - Connected, ConnectionError, ConnectionHandler, ConnectionLimit, Endpoint, IncomingInfo, - IntoConnectionHandler, OutgoingInfo, PendingConnectionError, PendingPoint, Substream, + ConnectionError, ConnectionHandler, IntoConnectionHandler, PendingConnectionError, + Substream, }, muxing::StreamMuxer, - network::DialError, transport::TransportError, - ConnectedPoint, Executor, Multiaddr, PeerId, + Multiaddr, PeerId, }; -use fnv::FnvHashMap; -use futures::prelude::*; use futures::{ channel::{mpsc, oneshot}, - future::Either, - future::{poll_fn, BoxFuture}, - prelude::*, - ready, - stream::FuturesUnordered, -}; -use smallvec::SmallVec; -use std::{ - collections::HashMap, convert::TryFrom as _, error, fmt, num::NonZeroU32, pin::Pin, - task::Context, task::Poll, + future::{Either, Future}, + SinkExt, StreamExt, }; use void::Void; @@ -61,9 +50,8 @@ pub enum Command { Close, } -/// Events that a task can emit to its manager. #[derive(Debug)] -pub enum Event { +pub enum PendingConnectionEvent { // TODO: Remove most of these ConnectionEstablished { id: ConnectionId, @@ -77,14 +65,20 @@ pub enum Event { id: ConnectionId, error: PendingConnectionError, }, +} + +#[derive(Debug)] +pub enum EstablishedConnectionEvent { /// A node we are connected to has changed its address. AddressChange { id: ConnectionId, + peer_id: PeerId, new_address: Multiaddr, }, /// Notify the manager of an event from the connection. Notify { id: ConnectionId, + peer_id: PeerId, event: THandlerOutEvent, }, /// A connection closed, possibly due to an error. @@ -93,35 +87,42 @@ pub enum Event { /// an active orderly close. Closed { id: ConnectionId, + peer_id: PeerId, error: Option>>, handler: THandler::Handler, }, } // TODO: Still needed? -impl Event { +impl PendingConnectionEvent { + pub fn id(&self) -> &ConnectionId { + match self { + PendingConnectionEvent::ConnectionEstablished { id, .. } => id, + PendingConnectionEvent::PendingFailed { id, .. } => id, + } + } +} + +impl EstablishedConnectionEvent { pub fn id(&self) -> &ConnectionId { match self { - Event::ConnectionEstablished { id, .. } => id, - Event::PendingFailed { id, .. } => id, - Event::AddressChange { id, .. } => id, - Event::Notify { id, .. } => id, - Event::Closed { id, .. } => id, + EstablishedConnectionEvent::AddressChange { id, .. } => id, + EstablishedConnectionEvent::Notify { id, .. } => id, + EstablishedConnectionEvent::Closed { id, .. } => id, } } } -pub async fn new_for_pending_outgoing_connection( + +pub async fn new_for_pending_outgoing_connection( connection_id: ConnectionId, dial: crate::network::concurrent_dial::ConcurrentDial, drop_receiver: oneshot::Receiver, - mut events: mpsc::Sender>, -) where - THandler: IntoConnectionHandler + Send + 'static, -{ + mut events: mpsc::Sender>, +) { match futures::future::select(drop_receiver, Box::pin(dial)).await { Either::Left((Err(oneshot::Canceled), _)) => { events - .send(Event::PendingFailed { + .send(PendingConnectionEvent::PendingFailed { id: connection_id, error: PendingConnectionError::Aborted, }) @@ -130,7 +131,7 @@ pub async fn new_for_pending_outgoing_connection( Either::Left((Ok(v), _)) => void::unreachable(v), Either::Right((Ok((peer_id, address, muxer, errors)), _)) => { events - .send(Event::ConnectionEstablished { + .send(PendingConnectionEvent::ConnectionEstablished { id: connection_id, peer_id, muxer, @@ -140,7 +141,7 @@ pub async fn new_for_pending_outgoing_connection( } Either::Right((Err(e), _)) => { events - .send(Event::PendingFailed { + .send(PendingConnectionEvent::PendingFailed { id: connection_id, error: PendingConnectionError::TransportDial(e), }) @@ -149,21 +150,20 @@ pub async fn new_for_pending_outgoing_connection( } } -pub async fn new_for_pending_incoming_connection( +pub async fn new_for_pending_incoming_connection( connection_id: ConnectionId, future: TFut, drop_receiver: oneshot::Receiver, - mut events: mpsc::Sender>, + mut events: mpsc::Sender>, ) where TFut: Future>> + Send + 'static, - THandler: IntoConnectionHandler + Send + 'static, { match futures::future::select(drop_receiver, Box::pin(future)).await { Either::Left((Err(oneshot::Canceled), _)) => { events - .send(Event::PendingFailed { + .send(PendingConnectionEvent::PendingFailed { id: connection_id, error: PendingConnectionError::Aborted, }) @@ -172,7 +172,7 @@ pub async fn new_for_pending_incoming_connection void::unreachable(v), Either::Right((Ok((peer_id, muxer)), _)) => { events - .send(Event::ConnectionEstablished { + .send(PendingConnectionEvent::ConnectionEstablished { id: connection_id, peer_id, muxer, @@ -182,7 +182,7 @@ pub async fn new_for_pending_incoming_connection { events - .send(Event::PendingFailed { + .send(PendingConnectionEvent::PendingFailed { id: connection_id, error: e, }) @@ -191,11 +191,12 @@ pub async fn new_for_pending_incoming_connection( +pub async fn new_for_established_connection( connection_id: ConnectionId, + peer_id: PeerId, mut connection: crate::connection::Connection, mut command_receiver: mpsc::Receiver>>, - mut events: mpsc::Sender>, + mut events: mpsc::Sender>, ) where TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send + 'static, @@ -213,8 +214,9 @@ pub async fn new_for_established_connection( let error = closing_muxer.await.err().map(ConnectionError::IO); events - .send(Event::Closed { + .send(EstablishedConnectionEvent::Closed { id: connection_id, + peer_id, error, handler, }) @@ -230,16 +232,18 @@ pub async fn new_for_established_connection( match event { Ok(connection::Event::Handler(event)) => { events - .send(Event::Notify { + .send(EstablishedConnectionEvent::Notify { id: connection_id, + peer_id, event, }) .await; } Ok(connection::Event::AddressChange(new_address)) => { events - .send(Event::AddressChange { + .send(EstablishedConnectionEvent::AddressChange { id: connection_id, + peer_id, new_address, }) .await; @@ -249,8 +253,9 @@ pub async fn new_for_established_connection( let (handler, _closing_muxer) = connection.close(); // Terminate the task with the error, dropping the connection. events - .send(Event::Closed { + .send(EstablishedConnectionEvent::Closed { id: connection_id, + peer_id, error: Some(error), handler, }) diff --git a/core/src/network.rs b/core/src/network.rs index 861c8826893..3d3c26da6ae 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -30,10 +30,10 @@ pub use peer::Peer; use crate::{ connection::{ handler::{THandlerInEvent, THandlerOutEvent}, - pool::{ManagerConfig, Pool, PoolEvent}, + pool::{Pool, PoolConfig, PoolEvent}, ConnectionHandler, ConnectionId, ConnectionLimit, Endpoint, IncomingInfo, - IntoConnectionHandler, ListenerId, ListenersEvent, ListenersStream, OutgoingInfo, - PendingConnectionError, PendingPoint, Substream, + IntoConnectionHandler, ListenerId, ListenersEvent, ListenersStream, PendingConnectionError, + PendingPoint, Substream, }, muxing::StreamMuxer, transport::{Transport, TransportError}, @@ -131,7 +131,7 @@ where Network { local_peer_id, listeners: ListenersStream::new(transport), - pool: Pool::new(local_peer_id, config.manager_config, config.limits), + pool: Pool::new(local_peer_id, config.pool_config, config.limits), dialing: Default::default(), } } @@ -228,7 +228,6 @@ where } } - // TODO: Clone needed? self.pool.add_outgoing( concurrent_dial::ConcurrentDial::new( self.transport().clone(), @@ -238,20 +237,6 @@ where handler, None, ) - - // match self.transport().clone().dial(address.clone()) { - // Ok(f) => { - // let address = address.clone(); - // let f = f - // .map_err(|err| PendingConnectionError::Transport(TransportError::Other(err))) - // .map_ok(|(peer_id, muxer)| (peer_id, address, muxer)); - // self.pool.add_outgoing(f, handler, None) - // } - // Err(err) => { - // let f = future::err(PendingConnectionError::Transport(err)); - // self.pool.add_outgoing(f, handler, None) - // } - // } } /// Returns information about the state of the `Network`. @@ -264,21 +249,10 @@ where } } - // /// Returns an iterator for information on all pending incoming connections. - // pub fn incoming_info(&self) -> impl Iterator> { - // self.pool.iter_pending_incoming() - // } - - // /// Returns the list of addresses we're currently dialing without knowing the `PeerId` of. - // pub fn unknown_dials(&self) -> impl Iterator { - // self.pool.iter_pending_outgoing().filter_map(|info| { - // if info.peer_id.is_none() { - // Some(info.address) - // } else { - // None - // } - // }) - // } + /// Returns an iterator for information on all pending incoming connections. + pub fn incoming_info(&self) -> impl Iterator> { + self.pool.iter_pending_incoming() + } /// Returns a list of all connected peers, i.e. peers to whom the `Network` /// has at least one established connection. @@ -541,25 +515,6 @@ where Some(opts.peer), ); - // let result = match transport.dial(addr.clone()) { - // Ok(fut) => { - // let fut = fut.map_err(|e| PendingConnectionError::Transport(TransportError::Other(e))); - // let info = OutgoingInfo { - // address: &addr, - // peer_id: Some(&opts.peer), - // }; - // pool.add_outgoing(fut, opts.handler, info) - // } - // Err(err) => { - // let fut = future::err(PendingConnectionError::Transport(err)); - // let info = OutgoingInfo { - // address: &addr, - // peer_id: Some(&opts.peer), - // }; - // pool.add_outgoing(fut, opts.handler, info) - // } - // }; - if let Ok(id) = &result { dialing.entry(opts.peer).or_default().push(*id); } @@ -644,10 +599,8 @@ impl NetworkInfo { /// `notify_handler` buffer size of 8. #[derive(Default)] pub struct NetworkConfig { - /// Note that the `ManagerConfig`s task command buffer always provides - /// one "free" slot per task. Thus the given total `notify_handler_buffer_size` - /// exposed for configuration on the `Network` is reduced by one. - manager_config: ManagerConfig, + /// Connection [`Pool`] configuration. + pool_config: PoolConfig, /// The effective connection limits. limits: ConnectionLimits, } @@ -655,7 +608,7 @@ pub struct NetworkConfig { impl NetworkConfig { /// Configures the executor to use for spawning connection background tasks. pub fn with_executor(mut self, e: Box) -> Self { - self.manager_config.executor = Some(e); + self.pool_config.executor = Some(e); self } @@ -665,7 +618,7 @@ impl NetworkConfig { where F: FnOnce() -> Option>, { - self.manager_config.executor = self.manager_config.executor.or_else(f); + self.pool_config.executor = self.pool_config.executor.or_else(f); self } @@ -677,7 +630,7 @@ impl NetworkConfig { /// longer be able to deliver events to the associated `ConnectionHandler`, /// thus exerting back-pressure on the connection and peer API. pub fn with_notify_handler_buffer_size(mut self, n: NonZeroUsize) -> Self { - self.manager_config.task_command_buffer_size = n.get() - 1; + self.pool_config.task_command_buffer_size = n.get() - 1; self } @@ -688,7 +641,7 @@ impl NetworkConfig { /// In this way, the consumers of network events exert back-pressure on /// the network connection I/O. pub fn with_connection_event_buffer_size(mut self, n: usize) -> Self { - self.manager_config.task_event_buffer_size = n; + self.pool_config.task_event_buffer_size = n; self } From 036c4330aa087fc0d2de644f19407d5610f0781b Mon Sep 17 00:00:00 2001 From: Max Inden Date: Tue, 5 Oct 2021 17:50:44 +0200 Subject: [PATCH 25/67] core/: Fix lints --- core/src/connection/error.rs | 18 ++++++------- core/src/connection/pool.rs | 18 +++++-------- core/src/connection/pool/task.rs | 40 ++++++++--------------------- core/src/network.rs | 28 +++++--------------- core/src/network/concurrent_dial.rs | 19 +++++--------- core/src/network/peer.rs | 2 +- core/tests/connection_limits.rs | 4 +-- core/tests/network_dial_error.rs | 1 - 8 files changed, 42 insertions(+), 88 deletions(-) diff --git a/core/src/connection/error.rs b/core/src/connection/error.rs index 0efc439ec91..61ba8e7231f 100644 --- a/core/src/connection/error.rs +++ b/core/src/connection/error.rs @@ -87,7 +87,7 @@ pub enum PendingConnectionError { impl fmt::Display for PendingConnectionError where - TTransErr: fmt::Display, + TTransErr: fmt::Display + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -101,12 +101,11 @@ where ) } PendingConnectionError::TransportDial(err) => { - // write!( - // f, - // "Pending connection: Transport error on dialing connection: {}", - // err - // ) - todo!() + write!( + f, + "Pending connection: Transport error on dialing connection: {:?}", + err + ) } PendingConnectionError::ConnectionLimit(l) => { write!(f, "Connection error: Connection limit: {}.", l) @@ -125,9 +124,8 @@ where fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { PendingConnectionError::IO(err) => Some(err), - // TODO: Should we return the first of all transport errors? Or implement std::error::Error for Vec? - PendingConnectionError::TransportListen(err) => None, - PendingConnectionError::TransportDial(err) => None, + PendingConnectionError::TransportListen(err) => Some(err), + PendingConnectionError::TransportDial(_) => None, PendingConnectionError::InvalidPeerId => None, PendingConnectionError::Aborted => None, PendingConnectionError::ConnectionLimit(..) => None, diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index ae4abdb87fa..4d56ac8c7bd 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -111,7 +111,7 @@ struct PendingConnectionInfo { handler: THandler, endpoint: PendingPoint, /// When dropped, notifies the task which can then terminate. - drop_notifier: oneshot::Sender, + _drop_notifier: oneshot::Sender, } impl fmt::Debug @@ -177,8 +177,6 @@ pub enum PoolEvent<'a, THandler: IntoConnectionHandler, TMuxer, TTransErr> { handler: THandler, /// The (expected) peer of the failed connection. peer: Option, - /// A reference to the pool that managed the connection. - pool: &'a mut Pool, }, /// A node has produced an event. @@ -330,7 +328,7 @@ impl { }, } -// TODO: Still needed? -impl PendingConnectionEvent { - pub fn id(&self) -> &ConnectionId { - match self { - PendingConnectionEvent::ConnectionEstablished { id, .. } => id, - PendingConnectionEvent::PendingFailed { id, .. } => id, - } - } -} - -impl EstablishedConnectionEvent { - pub fn id(&self) -> &ConnectionId { - match self { - EstablishedConnectionEvent::AddressChange { id, .. } => id, - EstablishedConnectionEvent::Notify { id, .. } => id, - EstablishedConnectionEvent::Closed { id, .. } => id, - } - } -} - pub async fn new_for_pending_outgoing_connection( connection_id: ConnectionId, dial: crate::network::concurrent_dial::ConcurrentDial, @@ -121,7 +101,7 @@ pub async fn new_for_pending_outgoing_connection( ) { match futures::future::select(drop_receiver, Box::pin(dial)).await { Either::Left((Err(oneshot::Canceled), _)) => { - events + let _ = events .send(PendingConnectionEvent::PendingFailed { id: connection_id, error: PendingConnectionError::Aborted, @@ -130,7 +110,7 @@ pub async fn new_for_pending_outgoing_connection( } Either::Left((Ok(v), _)) => void::unreachable(v), Either::Right((Ok((peer_id, address, muxer, errors)), _)) => { - events + let _ = events .send(PendingConnectionEvent::ConnectionEstablished { id: connection_id, peer_id, @@ -140,7 +120,7 @@ pub async fn new_for_pending_outgoing_connection( .await; } Either::Right((Err(e), _)) => { - events + let _ = events .send(PendingConnectionEvent::PendingFailed { id: connection_id, error: PendingConnectionError::TransportDial(e), @@ -162,7 +142,7 @@ pub async fn new_for_pending_incoming_connection( { match futures::future::select(drop_receiver, Box::pin(future)).await { Either::Left((Err(oneshot::Canceled), _)) => { - events + let _ = events .send(PendingConnectionEvent::PendingFailed { id: connection_id, error: PendingConnectionError::Aborted, @@ -171,7 +151,7 @@ pub async fn new_for_pending_incoming_connection( } Either::Left((Ok(v), _)) => void::unreachable(v), Either::Right((Ok((peer_id, muxer)), _)) => { - events + let _ = events .send(PendingConnectionEvent::ConnectionEstablished { id: connection_id, peer_id, @@ -181,7 +161,7 @@ pub async fn new_for_pending_incoming_connection( .await; } Either::Right((Err(e), _)) => { - events + let _ = events .send(PendingConnectionEvent::PendingFailed { id: connection_id, error: e, @@ -213,7 +193,7 @@ pub async fn new_for_established_connection( let (handler, closing_muxer) = connection.close(); let error = closing_muxer.await.err().map(ConnectionError::IO); - events + let _ = events .send(EstablishedConnectionEvent::Closed { id: connection_id, peer_id, @@ -231,7 +211,7 @@ pub async fn new_for_established_connection( Either::Right((Some(event), _)) => { match event { Ok(connection::Event::Handler(event)) => { - events + let _ = events .send(EstablishedConnectionEvent::Notify { id: connection_id, peer_id, @@ -240,7 +220,7 @@ pub async fn new_for_established_connection( .await; } Ok(connection::Event::AddressChange(new_address)) => { - events + let _ = events .send(EstablishedConnectionEvent::AddressChange { id: connection_id, peer_id, @@ -252,7 +232,7 @@ pub async fn new_for_established_connection( command_receiver.close(); let (handler, _closing_muxer) = connection.close(); // Terminate the task with the error, dropping the connection. - events + let _ = events .send(EstablishedConnectionEvent::Closed { id: connection_id, peer_id, diff --git a/core/src/network.rs b/core/src/network.rs index 3d3c26da6ae..96f756e1f0b 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -31,22 +31,22 @@ use crate::{ connection::{ handler::{THandlerInEvent, THandlerOutEvent}, pool::{Pool, PoolConfig, PoolEvent}, - ConnectionHandler, ConnectionId, ConnectionLimit, Endpoint, IncomingInfo, - IntoConnectionHandler, ListenerId, ListenersEvent, ListenersStream, PendingConnectionError, - PendingPoint, Substream, + ConnectionHandler, ConnectionId, ConnectionLimit, IncomingInfo, IntoConnectionHandler, + ListenerId, ListenersEvent, ListenersStream, PendingConnectionError, PendingPoint, + Substream, }, muxing::StreamMuxer, transport::{Transport, TransportError}, - ConnectedPoint, Executor, Multiaddr, PeerId, + Executor, Multiaddr, PeerId, }; use fnv::FnvHashMap; -use futures::{future, prelude::*}; +use futures::prelude::*; use smallvec::SmallVec; use std::{ collections::hash_map, convert::TryFrom as _, error, fmt, - num::{NonZeroU32, NonZeroUsize}, + num::NonZeroUsize, pin::Pin, task::{Context, Poll}, }; @@ -410,7 +410,6 @@ where endpoint, error, handler, - pool, .. }) => { let dialing = &mut self.dialing; @@ -496,19 +495,6 @@ where TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send + 'static, { - // // Ensure the address to dial encapsulates the `p2p` protocol for the - // // targeted peer, so that the transport has a "fully qualified" address - // // to work with. - // let addr = match p2p_addr(opts.peer, opts.address) { - // Ok(address) => address, - // Err(address) => { - // return Err(DialError::InvalidAddress { - // address, - // handler: opts.handler, - // }) - // } - // }; - let result = pool.add_outgoing( concurrent_dial::ConcurrentDial::new(transport, Some(opts.peer), opts.addresses), opts.handler, @@ -540,7 +526,7 @@ where { // Check if the failed connection is associated with a dialing attempt. let dialing_failed = dialing.iter_mut().find_map(|(peer, attempts)| { - if let Some(pos) = attempts.iter().position(|s| *s == id) { + if attempts.iter().any(|s| *s == id) { Some(*peer) } else { None diff --git a/core/src/network/concurrent_dial.rs b/core/src/network/concurrent_dial.rs index d9884a6fe55..5cd031e0f37 100644 --- a/core/src/network/concurrent_dial.rs +++ b/core/src/network/concurrent_dial.rs @@ -21,21 +21,16 @@ pub use crate::connection::{ConnectionCounters, ConnectionLimits}; use crate::{ - muxing::StreamMuxer, transport::{Transport, TransportError}, - ConnectedPoint, Executor, Multiaddr, PeerId, + Multiaddr, PeerId, +}; +use futures::{ + future::{BoxFuture, Future, FutureExt}, + ready, + stream::{FuturesUnordered, StreamExt}, }; -use fnv::FnvHashMap; -use futures::future::BoxFuture; -use futures::ready; -use futures::stream::FuturesUnordered; -use futures::{future, prelude::*}; -use smallvec::SmallVec; use std::{ - collections::{hash_map, VecDeque}, - convert::TryFrom as _, - error, fmt, - num::{NonZeroU32, NonZeroUsize}, + collections::VecDeque, pin::Pin, task::{Context, Poll}, }; diff --git a/core/src/network/peer.rs b/core/src/network/peer.rs index 8ea30e13487..1a52d81a566 100644 --- a/core/src/network/peer.rs +++ b/core/src/network/peer.rs @@ -21,7 +21,7 @@ use super::{DialError, DialingOpts, Network}; use crate::{ connection::{ - handler::THandlerInEvent, pool::Pool, ConnectedPoint, ConnectionHandler, ConnectionId, + handler::THandlerInEvent, pool::Pool, ConnectionHandler, ConnectionId, EstablishedConnection, EstablishedConnectionIter, IntoConnectionHandler, PendingConnection, Substream, }, diff --git a/core/tests/connection_limits.rs b/core/tests/connection_limits.rs index 92dadb52a31..8c732624b8b 100644 --- a/core/tests/connection_limits.rs +++ b/core/tests/connection_limits.rs @@ -21,9 +21,9 @@ mod util; use futures::{future::poll_fn, ready}; -use libp2p_core::multiaddr::{multiaddr, Multiaddr, Protocol}; +use libp2p_core::multiaddr::{multiaddr, Multiaddr}; use libp2p_core::{ - connection::{ConnectionError, PendingConnectionError}, + connection::PendingConnectionError, network::{ConnectionLimits, DialError, NetworkConfig, NetworkEvent}, PeerId, }; diff --git a/core/tests/network_dial_error.rs b/core/tests/network_dial_error.rs index e2696d50df4..325382c236e 100644 --- a/core/tests/network_dial_error.rs +++ b/core/tests/network_dial_error.rs @@ -24,7 +24,6 @@ use futures::prelude::*; use libp2p_core::multiaddr::multiaddr; use libp2p_core::{ connection::PendingConnectionError, - multiaddr::Protocol, network::{NetworkConfig, NetworkEvent}, PeerId, }; From 6eda209dfec49f931128b50e9e62ec00c8fb5fc2 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Tue, 5 Oct 2021 18:03:11 +0200 Subject: [PATCH 26/67] *: Adjust to changes in core --- core/tests/network_dial_error.rs | 20 +++++++++++++------- misc/metrics/src/swarm.rs | 4 ++-- protocols/kad/src/behaviour.rs | 2 +- swarm/src/behaviour.rs | 2 +- swarm/src/lib.rs | 6 +++--- swarm/src/toggle.rs | 2 +- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/core/tests/network_dial_error.rs b/core/tests/network_dial_error.rs index 325382c236e..ea985d444e6 100644 --- a/core/tests/network_dial_error.rs +++ b/core/tests/network_dial_error.rs @@ -24,6 +24,7 @@ use futures::prelude::*; use libp2p_core::multiaddr::multiaddr; use libp2p_core::{ connection::PendingConnectionError, + multiaddr::Protocol, network::{NetworkConfig, NetworkEvent}, PeerId, }; @@ -160,7 +161,6 @@ fn dial_self_by_id() { assert!(swarm.peer(peer_id).into_disconnected().is_none()); } -#[ignore] // TODO: Resurect #[test] fn multiple_addresses_err() { // Tries dialing multiple addresses, and makes sure there's one dialing error per address. @@ -189,15 +189,21 @@ fn multiple_addresses_err() { Poll::Ready(NetworkEvent::DialError { peer_id, // multiaddr, - error: PendingConnectionError::TransportDial(_), + error: PendingConnectionError::TransportDial(errors), handler: _, }) => { assert_eq!(peer_id, target); - // TODO: Bring back. - // let expected = addresses - // .remove(0) - // .with(Protocol::P2p(target.clone().into())); - // assert_eq!(multiaddr, expected); + + let failed_addresses = + errors.into_iter().map(|(addr, _)| addr).collect::>(); + assert_eq!( + failed_addresses, + addresses + .clone() + .into_iter() + .map(|addr| addr.with(Protocol::P2p(target.into()))) + .collect::>() + ); return Poll::Ready(Ok(())); } diff --git a/misc/metrics/src/swarm.rs b/misc/metrics/src/swarm.rs index 851ee06162b..fb8cc25641b 100644 --- a/misc/metrics/src/swarm.rs +++ b/misc/metrics/src/swarm.rs @@ -184,10 +184,10 @@ impl super::Recorder { for (_multiaddr, error) in errors { - let error = match error { + match error { libp2p_core::transport::TransportError::MultiaddrNotSupported( _, ) => record( diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 4b0cd364bcf..1fe35492f2c 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -53,7 +53,7 @@ use std::fmt; use std::num::NonZeroUsize; use std::task::{Context, Poll}; use std::vec; -use std::{borrow::Cow, error, time::Duration}; +use std::{borrow::Cow, time::Duration}; use wasm_timer::Instant; pub use crate::query::QueryStats; diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index 4d476db8c1c..8446107cbd6 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -24,7 +24,7 @@ use libp2p_core::{ connection::{ConnectionId, ListenerId}, ConnectedPoint, Multiaddr, PeerId, }; -use std::{error, task::Context, task::Poll}; +use std::{task::Context, task::Poll}; /// Custom event that can be received by the [`ProtocolsHandler`]. type THandlerInEvent = diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 4b5271364ae..c8891b83140 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -66,7 +66,6 @@ pub use behaviour::{ CloseConnection, DialPeerCondition, NetworkBehaviour, NetworkBehaviourAction, NetworkBehaviourEventProcess, NotifyHandler, PollParameters, }; -use log::Level::Error; pub use protocols_handler::{ IntoProtocolsHandler, IntoProtocolsHandlerSelect, KeepAlive, OneShotHandler, OneShotHandlerConfig, ProtocolsHandler, ProtocolsHandlerEvent, ProtocolsHandlerSelect, @@ -550,8 +549,9 @@ where return Poll::Ready(SwarmEvent::BannedPeer { peer_id, endpoint }); } else { log::debug!( - "Connection established: {:?}; Total (peer): {}.", - connection.connected(), + "Connection established: {:?} {:?}; Total (peer): {}.", + connection.peer_id(), + connection.endpoint(), num_established ); let endpoint = connection.endpoint().clone(); diff --git a/swarm/src/toggle.rs b/swarm/src/toggle.rs index f9f10891dff..362d1be4231 100644 --- a/swarm/src/toggle.rs +++ b/swarm/src/toggle.rs @@ -34,7 +34,7 @@ use libp2p_core::{ upgrade::{DeniedUpgrade, EitherUpgrade}, ConnectedPoint, Multiaddr, PeerId, }; -use std::{error, task::Context, task::Poll}; +use std::{task::Context, task::Poll}; /// Implementation of `NetworkBehaviour` that can be either in the disabled or enabled state. /// From 999b4814f9da7a911c6338b8ee395707a16be77c Mon Sep 17 00:00:00 2001 From: Max Inden Date: Tue, 5 Oct 2021 22:50:18 +0200 Subject: [PATCH 27/67] core/src/connection/pool: Take care of events before triggering more --- core/src/connection/pool.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index 4d56ac8c7bd..39b2072ab18 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -594,9 +594,6 @@ impl::OutboundOpenInfo: Send + 'static, TMuxer::OutboundSubstream: Send + 'static, { - // Advance the tasks in `local_spawns`. - while let Poll::Ready(Some(_)) = self.local_spawns.poll_next_unpin(cx) {} - // Poll for events of established connections. // // Note that established connections are polled before pending connections, thus @@ -845,6 +842,9 @@ impl Date: Wed, 6 Oct 2021 20:22:51 +0200 Subject: [PATCH 28/67] core/src/connection: Document PendingPoint --- core/src/connection.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core/src/connection.rs b/core/src/connection.rs index c70bc3ae494..d95d6e72592 100644 --- a/core/src/connection.rs +++ b/core/src/connection.rs @@ -71,10 +71,16 @@ impl Endpoint { } } -// TODO: Find better name. +/// The endpoint roles associated with a pending peer-to-peer connection. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum PendingPoint { + /// The socket comes from a dialer. + /// + /// There is no single address associated with the Dialer of a pending + /// connection. Addresses are dialed in parallel. Only once the first dial + /// is successful is the address of the connection known. Dialer, + /// The socket comes from a listener. Listener { /// Local connection address. local_addr: Multiaddr, @@ -98,7 +104,7 @@ impl From for PendingPoint { } } -/// The endpoint roles associated with a peer-to-peer connection. +/// The endpoint roles associated with an established peer-to-peer connection. #[derive(PartialEq, Eq, Debug, Clone, Hash)] pub enum ConnectedPoint { /// We dialed the node. From 8eb278d202b3e9337368af0548f1ef52981ad376 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 6 Oct 2021 20:36:25 +0200 Subject: [PATCH 29/67] core/src/connection: Wrap listener error in task.rs not network.rs --- core/src/connection.rs | 1 - core/src/connection/error.rs | 8 ++++---- core/src/connection/pool.rs | 20 +++++++++----------- core/src/connection/pool/task.rs | 6 ++---- core/src/network.rs | 24 +++++++++++++----------- core/src/network/event.rs | 4 +++- 6 files changed, 31 insertions(+), 32 deletions(-) diff --git a/core/src/connection.rs b/core/src/connection.rs index d95d6e72592..8ca492b11ee 100644 --- a/core/src/connection.rs +++ b/core/src/connection.rs @@ -182,7 +182,6 @@ impl ConnectedPoint { } } -// TODO: Still needed? /// Information about a successfully established connection. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Connected { diff --git a/core/src/connection/error.rs b/core/src/connection/error.rs index 61ba8e7231f..5249eaca947 100644 --- a/core/src/connection/error.rs +++ b/core/src/connection/error.rs @@ -61,12 +61,12 @@ where /// Errors that can occur in the context of a pending `Connection`. #[derive(Debug)] pub enum PendingConnectionError { - // TODO: Not ideal that a dial could potentially emit a TransportListen error - // TODO: Could as well rename this to TransportConcurrent - // TODO: In some cases the multiaddr is included twice now. E.g. MultiaddrUnsupported. + /// An error occurred while negotiating the transport protocol(s) on a + /// dialing connection. TransportDial(Vec<(Multiaddr, TransportError)>), - /// An error occurred while negotiating the transport protocol(s). + /// An error occurred while negotiating the transport protocol(s) on a + /// listening connection. TransportListen(TransportError), /// The connection was dropped because the connection limit diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index 39b2072ab18..53f8f63e585 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -100,17 +100,20 @@ pub struct Pool { #[derive(Debug)] struct EstablishedConnectionInfo { + /// [`PeerId`] of the remote peer. peer_id: PeerId, endpoint: ConnectedPoint, - /// Channel endpoint to send messages to the task. + /// Channel endpoint to send commands to the task. sender: mpsc::Sender>, } struct PendingConnectionInfo { + /// [`PeerId`] of the remote peer. peer_id: Option, + /// Handler to handle connection once no longer pending but established. handler: THandler, endpoint: PendingPoint, - /// When dropped, notifies the task which can then terminate. + /// When dropped, notifies the task which then knows to terminate. _drop_notifier: oneshot::Sender, } @@ -120,22 +123,19 @@ impl fmt::Debug fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.debug_struct("Pool") .field("counters", &self.counters) - // TODO: Should we add more fields? .finish() } } -impl Unpin - for Pool -{ -} - /// Event that can happen on the `Pool`. pub enum PoolEvent<'a, THandler: IntoConnectionHandler, TMuxer, TTransErr> { /// A new connection has been established. ConnectionEstablished { connection: EstablishedConnection<'a, THandlerInEvent>, num_established: NonZeroU32, + /// [`Some`] when the new connection is an outgoing connection. + /// Addresses are dialed in parallel. Contains the addresses and errors + /// of dial attempts that failed before the one successful dial. outgoing: Option)>>, }, @@ -346,9 +346,7 @@ impl, ) -> Result where - TFut: Future>> - + Send - + 'static, + TFut: Future> + Send + 'static, // TODO: Bounds still needed here? THandler: IntoConnectionHandler + Send + 'static, THandler::Handler: ConnectionHandler> + Send + 'static, diff --git a/core/src/connection/pool/task.rs b/core/src/connection/pool/task.rs index 38d0c7060ea..6e9d49ceb1c 100644 --- a/core/src/connection/pool/task.rs +++ b/core/src/connection/pool/task.rs @@ -136,9 +136,7 @@ pub async fn new_for_pending_incoming_connection( drop_receiver: oneshot::Receiver, mut events: mpsc::Sender>, ) where - TFut: Future>> - + Send - + 'static, + TFut: Future> + Send + 'static, { match futures::future::select(drop_receiver, Box::pin(future)).await { Either::Left((Err(oneshot::Canceled), _)) => { @@ -164,7 +162,7 @@ pub async fn new_for_pending_incoming_connection( let _ = events .send(PendingConnectionEvent::PendingFailed { id: connection_id, - error: e, + error: PendingConnectionError::TransportListen(TransportError::Other(e)), }) .await; } diff --git a/core/src/network.rs b/core/src/network.rs index 96f756e1f0b..0b0102684f8 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -40,7 +40,6 @@ use crate::{ Executor, Multiaddr, PeerId, }; use fnv::FnvHashMap; -use futures::prelude::*; use smallvec::SmallVec; use std::{ collections::hash_map, @@ -294,7 +293,11 @@ where /// completed, the connection is associated with the provided `handler`. pub fn accept( &mut self, - connection: IncomingConnection, + IncomingConnection { + upgrade, + local_addr, + send_back_addr, + }: IncomingConnection, handler: THandler, ) -> Result where @@ -304,15 +307,14 @@ where TTrans::Error: Send + 'static, TTrans::ListenerUpgrade: Send + 'static, { - let upgrade = connection - .upgrade - // TODO: Why this map? - .map_err(|err| PendingConnectionError::TransportListen(TransportError::Other(err))); - let info = IncomingInfo { - local_addr: &connection.local_addr, - send_back_addr: &connection.send_back_addr, - }; - self.pool.add_incoming(upgrade, handler, info) + self.pool.add_incoming( + upgrade, + handler, + IncomingInfo { + local_addr: &local_addr, + send_back_addr: &send_back_addr, + }, + ) } /// Provides an API similar to `Stream`, except that it cannot error. diff --git a/core/src/network/event.rs b/core/src/network/event.rs index 69609238ad6..b5f8d715ccd 100644 --- a/core/src/network/event.rs +++ b/core/src/network/event.rs @@ -102,7 +102,9 @@ where /// The total number of established connections to the same peer, /// including the one that has just been opened. num_established: NonZeroU32, - // TODO: Document + /// [`Some`] when the new connection is an outgoing connection. + /// Addresses are dialed in parallel. Contains the addresses and errors + /// of dial attempts that failed before the one successful dial. outgoing: Option)>>, }, From 73ffc76e9ba35f613ace0d4934460c054fa639ac Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 6 Oct 2021 20:49:28 +0200 Subject: [PATCH 30/67] core/src/connection/pool: Clean up trait bounds --- core/src/connection/pool.rs | 31 +++++++------------------------ core/src/connection/pool/task.rs | 6 ++---- core/src/network.rs | 1 + 3 files changed, 10 insertions(+), 28 deletions(-) diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index 53f8f63e585..43f896dd9cd 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -42,7 +42,7 @@ use smallvec::SmallVec; use std::{ collections::{hash_map, HashMap}, convert::TryFrom as _, - error, fmt, + fmt, num::NonZeroU32, pin::Pin, task::Context, @@ -294,15 +294,7 @@ impl, handler: THandler, expected_peer_id: Option, - ) -> Result> - where - THandler: IntoConnectionHandler + Send + 'static, - THandler::Handler: ConnectionHandler> + Send + 'static, - ::OutboundOpenInfo: Send + 'static, - TTransErr: error::Error + Send + 'static, - TMuxer: StreamMuxer + Send + Sync + 'static, - TMuxer::OutboundSubstream: Send + 'static, - { + ) -> Result> { if let Err(limit) = self.counters.check_max_pending_outgoing() { return Err(DialError::ConnectionLimit { limit, handler }); }; @@ -347,17 +339,8 @@ impl Result where TFut: Future> + Send + 'static, - // TODO: Bounds still needed here? - THandler: IntoConnectionHandler + Send + 'static, - THandler::Handler: ConnectionHandler> + Send + 'static, - ::OutboundOpenInfo: Send + 'static, - TTransErr: error::Error + Send + 'static, - // TODO: Still needed? - TMuxer: StreamMuxer + Send + Sync + 'static, - TMuxer::OutboundSubstream: Send + 'static, { - // TODO: This is a hack. Fix. - let endpoint = info.clone().to_connected_point(); + let endpoint = info.to_connected_point(); // TODO: We loose the handler here. self.counters.check_max_pending_incoming()?; @@ -587,10 +570,10 @@ impl Poll> where TMuxer: StreamMuxer + Send + Sync + 'static, - THandler: IntoConnectionHandler + Send + 'static, - THandler::Handler: ConnectionHandler> + Send + 'static, - ::OutboundOpenInfo: Send + 'static, - TMuxer::OutboundSubstream: Send + 'static, + THandler: IntoConnectionHandler + 'static, + THandler::Handler: ConnectionHandler> + Send, + ::OutboundOpenInfo: Send, + TMuxer::OutboundSubstream: Send, { // Poll for events of established connections. // diff --git a/core/src/connection/pool/task.rs b/core/src/connection/pool/task.rs index 6e9d49ceb1c..0d4f83a42d0 100644 --- a/core/src/connection/pool/task.rs +++ b/core/src/connection/pool/task.rs @@ -176,11 +176,9 @@ pub async fn new_for_established_connection( mut command_receiver: mpsc::Receiver>>, mut events: mpsc::Sender>, ) where - TMuxer: StreamMuxer + Send + Sync + 'static, - TMuxer::OutboundSubstream: Send + 'static, + TMuxer: StreamMuxer, THandler: IntoConnectionHandler, - THandler::Handler: ConnectionHandler> + Send + 'static, - ::OutboundOpenInfo: Send + 'static, + THandler::Handler: ConnectionHandler>, { loop { match futures::future::select(command_receiver.next(), connection.next()).await { diff --git a/core/src/network.rs b/core/src/network.rs index 0b0102684f8..e5f76a85ff9 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -685,6 +685,7 @@ impl fmt::Debug for DialError { #[cfg(test)] mod tests { use super::*; + use futures::future::Future; struct Dummy; From 1a97d9b4d61b85791c46970ab5439af1c2ae9657 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 6 Oct 2021 20:56:55 +0200 Subject: [PATCH 31/67] core/src/connection: Move ConnectionId to connection.rs --- core/src/connection.rs | 17 ++++++++++++++++- core/src/connection/pool.rs | 18 +----------------- core/src/connection/pool/task.rs | 5 ++--- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/core/src/connection.rs b/core/src/connection.rs index 8ca492b11ee..6b8f37d436c 100644 --- a/core/src/connection.rs +++ b/core/src/connection.rs @@ -28,7 +28,7 @@ pub(crate) mod pool; pub use error::{ConnectionError, PendingConnectionError}; pub use handler::{ConnectionHandler, ConnectionHandlerEvent, IntoConnectionHandler}; pub use listeners::{ListenerId, ListenersEvent, ListenersStream}; -pub use pool::{ConnectionCounters, ConnectionId, ConnectionLimits}; +pub use pool::{ConnectionCounters, ConnectionLimits}; pub use pool::{EstablishedConnection, EstablishedConnectionIter, PendingConnection}; pub use substream::{Close, Substream, SubstreamEndpoint}; @@ -39,6 +39,21 @@ use std::hash::Hash; use std::{error::Error, fmt, pin::Pin, task::Context, task::Poll}; use substream::{Muxing, SubstreamEvent}; +/// Connection identifier. +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct ConnectionId(usize); + +impl ConnectionId { + /// Creates a `ConnectionId` from a non-negative integer. + /// + /// This is primarily useful for creating connection IDs + /// in test environments. There is in general no guarantee + /// that all connection IDs are based on non-negative integers. + pub fn new(id: usize) -> Self { + ConnectionId(id) + } +} + /// The endpoint roles associated with a peer-to-peer communication channel. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum Endpoint { diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index 43f896dd9cd..8d1851b6064 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -22,7 +22,7 @@ use crate::{ connection::{ handler::{THandlerError, THandlerInEvent, THandlerOutEvent}, - Connected, ConnectionError, ConnectionHandler, ConnectionLimit, IncomingInfo, + Connected, ConnectionError, ConnectionHandler, ConnectionId, ConnectionLimit, IncomingInfo, IntoConnectionHandler, PendingConnectionError, PendingPoint, Substream, }, muxing::StreamMuxer, @@ -1214,22 +1214,6 @@ impl ConnectionLimits { } } -/// Connection identifier. -// TODO: Should this live in connection.rs? -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct ConnectionId(usize); - -impl ConnectionId { - /// Creates a `ConnectionId` from a non-negative integer. - /// - /// This is primarily useful for creating connection IDs - /// in test environments. There is in general no guarantee - /// that all connection IDs are based on non-negative integers. - pub fn new(id: usize) -> Self { - ConnectionId(id) - } -} - /// Configuration options when creating a [`Pool`]. /// /// The default configuration specifies no dedicated task executor, a diff --git a/core/src/connection/pool/task.rs b/core/src/connection/pool/task.rs index 0d4f83a42d0..d323f68ae01 100644 --- a/core/src/connection/pool/task.rs +++ b/core/src/connection/pool/task.rs @@ -21,13 +21,12 @@ //! Async functions driving pending and established connections in the form of a task. -use super::ConnectionId; use crate::{ connection::{ self, handler::{THandlerError, THandlerInEvent, THandlerOutEvent}, - ConnectionError, ConnectionHandler, IntoConnectionHandler, PendingConnectionError, - Substream, + ConnectionError, ConnectionHandler, ConnectionId, IntoConnectionHandler, + PendingConnectionError, Substream, }, muxing::StreamMuxer, transport::TransportError, From f5d0e915b60090f3f5e819450a1f8c66164936f7 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 6 Oct 2021 21:40:23 +0200 Subject: [PATCH 32/67] protocols/rendezvous/tests: Await events sequentially to prevent race --- protocols/rendezvous/tests/rendezvous.rs | 36 +++++++++++++++++------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index e6ba5fcc39a..c258dd01d92 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -173,11 +173,19 @@ async fn discover_allows_for_dial_by_peer_id() { bob.behaviour_mut() .discover(Some(namespace.clone()), None, None, roberts_peer_id); - assert_behaviour_events! { - alice: rendezvous::client::Event::Registered { .. }, - bob: rendezvous::client::Event::Discovered { .. }, - || { } - }; + // TODO: Clean up + match alice.select_next_some().await { + SwarmEvent::Behaviour(rendezvous::client::Event::Registered { .. }) => {} + _ => todo!(), + } + + // TODO: Clean up + match bob.select_next_some().await { + SwarmEvent::Behaviour(rendezvous::client::Event::Discovered { registrations, .. }) => { + assert!(!registrations.is_empty()); + } + _ => todo!(), + } let alices_peer_id = *alice.local_peer_id(); let bobs_peer_id = *bob.local_peer_id(); @@ -278,11 +286,19 @@ async fn registration_on_clients_expire() { bob.behaviour_mut() .discover(Some(namespace), None, None, roberts_peer_id); - assert_behaviour_events! { - alice: rendezvous::client::Event::Registered { .. }, - bob: rendezvous::client::Event::Discovered { .. }, - || { } - }; + // TODO: Clean up + match alice.select_next_some().await { + SwarmEvent::Behaviour(rendezvous::client::Event::Registered { .. }) => {} + _ => todo!(), + } + + // TODO: Clean up + match bob.select_next_some().await { + SwarmEvent::Behaviour(rendezvous::client::Event::Discovered { registrations, .. }) => { + assert!(!registrations.is_empty()); + } + _ => todo!(), + } tokio::time::sleep(Duration::from_secs(registration_ttl + 5)).await; From 62ed126f027427ad97e62f03187f1039c2e9ab08 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 6 Oct 2021 21:54:49 +0200 Subject: [PATCH 33/67] swarm/src/lib: Return dial errors in SwarmEvent::EstablishedConnection --- swarm/src/lib.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index c8891b83140..3a2c797570c 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -141,6 +141,10 @@ pub enum SwarmEvent { /// Number of established connections to this peer, including the one that has just been /// opened. num_established: NonZeroU32, + /// [`Some`] when the new connection is an outgoing connection. + /// Addresses are dialed in parallel. Contains the addresses and errors + /// of dial attempts that failed before the one successful dial. + outgoing: Option)>>, }, /// A connection with the given peer has been closed, /// possibly as a result of an error. @@ -555,13 +559,14 @@ where num_established ); let endpoint = connection.endpoint().clone(); + let failed_addresses = outgoing + .as_ref() + .map(|es| es.iter().map(|(a, _)| a).cloned().collect()); this.behaviour.inject_connection_established( &peer_id, &connection.id(), &endpoint, - outgoing - .map(|es| es.into_iter().map(|(a, e)| a).collect()) - .as_ref(), + failed_addresses.as_ref(), ); if num_established.get() == 1 { this.behaviour.inject_connected(&peer_id); @@ -570,6 +575,7 @@ where peer_id, num_established, endpoint, + outgoing, }); } } From 42ef7f5998d1c76b9c25fccfdd0ece2b0052ea62 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 6 Oct 2021 22:42:28 +0200 Subject: [PATCH 34/67] core/src/network: Lazily call Transport dial on concurrent dialing --- core/src/network.rs | 8 ++-- core/src/network/concurrent_dial.rs | 72 +++++++++++++++-------------- core/src/network/peer.rs | 2 +- 3 files changed, 42 insertions(+), 40 deletions(-) diff --git a/core/src/network.rs b/core/src/network.rs index e5f76a85ff9..17e68e3f5c9 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -19,7 +19,7 @@ // DEALINGS IN THE SOFTWARE. // TODO: pub needed? -pub mod concurrent_dial; +pub(crate) mod concurrent_dial; mod event; pub mod peer; @@ -207,7 +207,7 @@ where handler: THandler, ) -> Result> where - TTrans: Transport, + TTrans: Transport + Send, TTrans::Error: Send + 'static, TTrans::Dial: Send + 'static, TMuxer: Send + Sync + 'static, @@ -454,7 +454,7 @@ where opts: DialingOpts, ) -> Result> where - TTrans: Transport, + TTrans: Transport + Send, TTrans::Dial: Send + 'static, TTrans::Error: Send + 'static, TMuxer: Send + Sync + 'static, @@ -491,7 +491,7 @@ where ::Error: error::Error + Send + 'static, ::OutboundOpenInfo: Send + 'static, THandler::Handler: ConnectionHandler> + Send + 'static, - TTrans: Transport + Clone + 'static, + TTrans: Transport + Clone + Send + 'static, TTrans::Dial: Send + 'static, TTrans::Error: error::Error + Send + 'static, TMuxer: StreamMuxer + Send + Sync + 'static, diff --git a/core/src/network/concurrent_dial.rs b/core/src/network/concurrent_dial.rs index 5cd031e0f37..336282a02df 100644 --- a/core/src/network/concurrent_dial.rs +++ b/core/src/network/concurrent_dial.rs @@ -30,75 +30,77 @@ use futures::{ stream::{FuturesUnordered, StreamExt}, }; use std::{ - collections::VecDeque, pin::Pin, task::{Context, Poll}, }; -// TODO: pub needed? +type Dial = + BoxFuture<'static, (Multiaddr, Result<(PeerId, TMuxer), TransportError>)>; + pub struct ConcurrentDial { - // TODO: We could as well spawn each of these on a separate task. - dials: FuturesUnordered)>>, - pending_dials: VecDeque)>>, + dials: FuturesUnordered>, + pending_dials: Box> + Send>, errors: Vec<(Multiaddr, TransportError)>, } impl Unpin for ConcurrentDial {} -impl ConcurrentDial { - pub(crate) fn new( +impl ConcurrentDial { + pub(crate) fn new( transport: TTrans, peer: Option, addresses: Vec, ) -> Self where - TTrans: Transport + Clone, + TTrans: Transport + Clone + 'static, TTrans::Dial: Send + 'static, { - let dials = FuturesUnordered::default(); - let mut pending_dials = VecDeque::default(); - let mut errors = vec![]; - - let addresses = addresses.into_iter(); - - for addr in addresses.into_iter().map(|a| p2p_addr(peer, a)) { - match addr { + let mut pending_dials = addresses.into_iter().map(move |address| { + match p2p_addr(peer, address) { Ok(address) => match transport.clone().dial(address.clone()) { - Ok(fut) => { - let fut = fut.map(|r| (address, r)).boxed(); - // TODO: Move to constant - if dials.len() <= 5 { - dials.push(fut) - } else { - pending_dials.push_back(fut) - } - } - Err(err) => errors.push((address, err)), + Ok(fut) => fut + .map(|r| (address, r.map_err(|e| TransportError::Other(e)))) + .boxed(), + Err(err) => futures::future::ready((address.clone(), Err(err))).boxed(), }, - Err(address) => errors.push(( - address.clone(), - // TODO: It is not really the Multiaddr that is not supported. - TransportError::MultiaddrNotSupported(address), - )), + Err(address) => { + futures::future::ready(( + address.clone(), + // TODO: It is not really the Multiaddr that is not supported. + Err(TransportError::MultiaddrNotSupported(address)), + )) + .boxed() + } + } + }); + + let dials = FuturesUnordered::new(); + while let Some(dial) = pending_dials.next() { + dials.push(dial); + if dials.len() == 5 { + break; } } Self { dials, - errors, - pending_dials, + errors: Default::default(), + pending_dials: Box::new(pending_dials), } } } impl Future for ConcurrentDial { type Output = Result< + // Either one dial succeeded, returning the negotiated [`PeerId`], the address, the + // muxer and the addresses and errors of the dials that failed before. ( PeerId, Multiaddr, TMuxer, Vec<(Multiaddr, TransportError)>, ), + // Or all dials failed, thus returning the address and error for each dial. Vec<(Multiaddr, TransportError)>, >; @@ -110,8 +112,8 @@ impl Future for ConcurrentDial { return Poll::Ready(Ok((peer_id, addr, muxer, errors))); } Some((addr, Err(e))) => { - self.errors.push((addr, TransportError::Other(e))); - if let Some(dial) = self.pending_dials.pop_front() { + self.errors.push((addr, e)); + if let Some(dial) = self.pending_dials.next() { self.dials.push(dial) } } diff --git a/core/src/network/peer.rs b/core/src/network/peer.rs index 1a52d81a566..b75588e2456 100644 --- a/core/src/network/peer.rs +++ b/core/src/network/peer.rs @@ -114,7 +114,7 @@ where impl<'a, TTrans, TMuxer, THandler> Peer<'a, TTrans, THandler, TMuxer> where - TTrans: Transport + Clone + 'static, + TTrans: Transport + Clone + Send + 'static, TTrans::Error: Send + 'static, TTrans::Dial: Send + 'static, TMuxer: StreamMuxer + Send + Sync + 'static, From 520c4ecab9995893d4498b22cff8801a855c7990 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 6 Oct 2021 22:56:43 +0200 Subject: [PATCH 35/67] core/src: Move ConcurrentDial into pool/ --- core/src/connection/pool.rs | 18 ++++++++++++++---- .../pool}/concurrent_dial.rs | 4 ++-- core/src/connection/pool/task.rs | 3 ++- core/src/network.rs | 14 ++++++-------- 4 files changed, 24 insertions(+), 15 deletions(-) rename core/src/{network => connection/pool}/concurrent_dial.rs (97%) diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index 8d1851b6064..198eb1b047e 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -27,9 +27,10 @@ use crate::{ }, muxing::StreamMuxer, network::DialError, - transport::TransportError, + transport::{Transport, TransportError}, ConnectedPoint, Executor, Multiaddr, PeerId, }; +use concurrent_dial::ConcurrentDial; use fnv::FnvHashMap; use futures::prelude::*; use futures::{ @@ -50,6 +51,7 @@ use std::{ }; use void::Void; +mod concurrent_dial; mod task; /// A connection `Pool` manages a set of connections for each peer. @@ -289,16 +291,24 @@ impl( &mut self, - dial: crate::network::concurrent_dial::ConcurrentDial, + transport: TTrans, + addresses: impl Iterator + Send + 'static, + peer: Option, handler: THandler, expected_peer_id: Option, - ) -> Result> { + ) -> Result> + where + TTrans: Transport + Clone + Send + 'static, + TTrans::Dial: Send + 'static, + { if let Err(limit) = self.counters.check_max_pending_outgoing() { return Err(DialError::ConnectionLimit { limit, handler }); }; + let dial = ConcurrentDial::new(transport, peer, addresses); + let connection_id = self.next_connection_id(); let (drop_notifier, drop_receiver) = oneshot::channel(); diff --git a/core/src/network/concurrent_dial.rs b/core/src/connection/pool/concurrent_dial.rs similarity index 97% rename from core/src/network/concurrent_dial.rs rename to core/src/connection/pool/concurrent_dial.rs index 336282a02df..53998d58e32 100644 --- a/core/src/network/concurrent_dial.rs +++ b/core/src/connection/pool/concurrent_dial.rs @@ -49,13 +49,13 @@ impl ConcurrentDial( transport: TTrans, peer: Option, - addresses: Vec, + addresses: impl Iterator + Send + 'static, ) -> Self where TTrans: Transport + Clone + 'static, TTrans::Dial: Send + 'static, { - let mut pending_dials = addresses.into_iter().map(move |address| { + let mut pending_dials = addresses.map(move |address| { match p2p_addr(peer, address) { Ok(address) => match transport.clone().dial(address.clone()) { Ok(fut) => fut diff --git a/core/src/connection/pool/task.rs b/core/src/connection/pool/task.rs index d323f68ae01..01fcc41f378 100644 --- a/core/src/connection/pool/task.rs +++ b/core/src/connection/pool/task.rs @@ -21,6 +21,7 @@ //! Async functions driving pending and established connections in the form of a task. +use super::concurrent_dial::ConcurrentDial; use crate::{ connection::{ self, @@ -94,7 +95,7 @@ pub enum EstablishedConnectionEvent { pub async fn new_for_pending_outgoing_connection( connection_id: ConnectionId, - dial: crate::network::concurrent_dial::ConcurrentDial, + dial: ConcurrentDial, drop_receiver: oneshot::Receiver, mut events: mpsc::Sender>, ) { diff --git a/core/src/network.rs b/core/src/network.rs index 17e68e3f5c9..8cd971a82c2 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -18,8 +18,6 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -// TODO: pub needed? -pub(crate) mod concurrent_dial; mod event; pub mod peer; @@ -228,11 +226,9 @@ where } self.pool.add_outgoing( - concurrent_dial::ConcurrentDial::new( - self.transport().clone(), - None, - vec![address.clone()], - ), + self.transport().clone(), + std::iter::once(address.clone()), + None, handler, None, ) @@ -498,7 +494,9 @@ where TMuxer::OutboundSubstream: Send + 'static, { let result = pool.add_outgoing( - concurrent_dial::ConcurrentDial::new(transport, Some(opts.peer), opts.addresses), + transport, + opts.addresses.into_iter(), + Some(opts.peer), opts.handler, Some(opts.peer), ); From f76cf2b1d440e35338caf1eadff8436830dac41c Mon Sep 17 00:00:00 2001 From: Max Inden Date: Fri, 8 Oct 2021 16:53:28 +0200 Subject: [PATCH 36/67] core/src/connection: Only bound poll to TMuxer --- core/src/connection/pool.rs | 240 ++++++++++---------- core/src/connection/pool/concurrent_dial.rs | 51 +++-- core/src/connection/pool/task.rs | 41 ++-- core/src/network.rs | 117 ++++------ core/src/network/peer.rs | 95 ++++---- 5 files changed, 261 insertions(+), 283 deletions(-) diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index 198eb1b047e..40f7b2a2ff1 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -55,7 +55,10 @@ mod concurrent_dial; mod task; /// A connection `Pool` manages a set of connections for each peer. -pub struct Pool { +pub struct Pool +where + TTrans: Transport, +{ local_id: PeerId, /// The connection counter(s). @@ -87,10 +90,10 @@ pub struct Pool { /// Sender distributed to pending tasks for reporting events back /// to the pool. - pending_connection_events_tx: mpsc::Sender>, + pending_connection_events_tx: mpsc::Sender>, /// Receiver for events reported from pending tasks. - pending_connection_events_rx: mpsc::Receiver>, + pending_connection_events_rx: mpsc::Receiver>, /// Sender distributed to established tasks for reporting events back /// to the pool. @@ -119,9 +122,7 @@ struct PendingConnectionInfo { _drop_notifier: oneshot::Sender, } -impl fmt::Debug - for Pool -{ +impl fmt::Debug for Pool { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.debug_struct("Pool") .field("counters", &self.counters) @@ -130,7 +131,10 @@ impl fmt::Debug } /// Event that can happen on the `Pool`. -pub enum PoolEvent<'a, THandler: IntoConnectionHandler, TMuxer, TTransErr> { +pub enum PoolEvent<'a, THandler: IntoConnectionHandler, TTrans> +where + TTrans: Transport, +{ /// A new connection has been established. ConnectionEstablished { connection: EstablishedConnection<'a, THandlerInEvent>, @@ -138,7 +142,7 @@ pub enum PoolEvent<'a, THandler: IntoConnectionHandler, TMuxer, TTransErr> { /// [`Some`] when the new connection is an outgoing connection. /// Addresses are dialed in parallel. Contains the addresses and errors /// of dial attempts that failed before the one successful dial. - outgoing: Option)>>, + outgoing: Option)>>, }, /// An established connection was closed. @@ -160,7 +164,7 @@ pub enum PoolEvent<'a, THandler: IntoConnectionHandler, TMuxer, TTransErr> { /// was closed by the local peer. error: Option>>, /// A reference to the pool that used to manage the connection. - pool: &'a mut Pool, + pool: &'a mut Pool, /// The remaining number of established connections to the same peer. num_established: u32, handler: THandler::Handler, @@ -173,7 +177,7 @@ pub enum PoolEvent<'a, THandler: IntoConnectionHandler, TMuxer, TTransErr> { /// The local endpoint of the failed connection. endpoint: PendingPoint, /// The error that occurred. - error: PendingConnectionError, + error: PendingConnectionError, /// The handler that was supposed to handle the connection, /// if the connection failed before the handler was consumed. handler: THandler, @@ -200,10 +204,10 @@ pub enum PoolEvent<'a, THandler: IntoConnectionHandler, TMuxer, TTransErr> { }, } -impl<'a, THandler: IntoConnectionHandler, TMuxer, TTransErr> fmt::Debug - for PoolEvent<'a, THandler, TMuxer, TTransErr> +impl<'a, THandler: IntoConnectionHandler, TTrans> fmt::Debug for PoolEvent<'a, THandler, TTrans> where - TTransErr: fmt::Debug, + TTrans: Transport, + TTrans::Error: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match *self { @@ -256,8 +260,10 @@ where } } -impl - Pool +impl Pool +where + THandler: IntoConnectionHandler, + TTrans: Transport, { /// Creates a new empty `Pool`. pub fn new(local_id: PeerId, config: PoolConfig, limits: ConnectionLimits) -> Self { @@ -286,101 +292,6 @@ impl( - &mut self, - transport: TTrans, - addresses: impl Iterator + Send + 'static, - peer: Option, - handler: THandler, - expected_peer_id: Option, - ) -> Result> - where - TTrans: Transport + Clone + Send + 'static, - TTrans::Dial: Send + 'static, - { - if let Err(limit) = self.counters.check_max_pending_outgoing() { - return Err(DialError::ConnectionLimit { limit, handler }); - }; - - let dial = ConcurrentDial::new(transport, peer, addresses); - - let connection_id = self.next_connection_id(); - - let (drop_notifier, drop_receiver) = oneshot::channel(); - - self.spawn( - task::new_for_pending_outgoing_connection( - connection_id, - dial, - drop_receiver, - self.pending_connection_events_tx.clone(), - ) - .boxed(), - ); - - self.counters.inc_pending(&PendingPoint::Dialer); - self.pending.insert( - connection_id, - PendingConnectionInfo { - peer_id: expected_peer_id, - handler, - endpoint: PendingPoint::Dialer, - _drop_notifier: drop_notifier, - }, - ); - Ok(connection_id) - } - - /// Adds a pending incoming connection to the pool in the form of a - /// `Future` that establishes and negotiates the connection. - /// - /// Returns an error if the limit of pending incoming connections - /// has been reached. - pub fn add_incoming( - &mut self, - future: TFut, - handler: THandler, - info: IncomingInfo<'_>, - ) -> Result - where - TFut: Future> + Send + 'static, - { - let endpoint = info.to_connected_point(); - // TODO: We loose the handler here. - self.counters.check_max_pending_incoming()?; - - let connection_id = self.next_connection_id(); - - let (drop_notifier, drop_receiver) = oneshot::channel(); - - self.spawn( - task::new_for_pending_incoming_connection( - connection_id, - future, - drop_receiver, - self.pending_connection_events_tx.clone(), - ) - .boxed(), - ); - - self.counters.inc_pending_incoming(); - self.pending.insert( - connection_id, - PendingConnectionInfo { - peer_id: None, - handler, - endpoint: endpoint.into(), - _drop_notifier: drop_notifier, - }, - ); - Ok(connection_id) - } - /// Gets an entry representing a connection in the pool. /// /// Returns `None` if the pool has no connection with the given ID. @@ -569,16 +480,118 @@ impl Pool +where + THandler: IntoConnectionHandler, + TTrans: Transport + 'static, + TTrans::Output: Send + 'static, + TTrans::Error: Send + 'static, +{ + /// Adds a pending outgoing connection to the pool in the form of a `Future` + /// that establishes and negotiates the connection. + /// + /// Returns an error if the limit of pending outgoing connections + /// has been reached. + pub fn add_outgoing( + &mut self, + transport: TTrans, + addresses: impl Iterator + Send + 'static, + peer: Option, + handler: THandler, + ) -> Result> + where + TTrans: Clone + Send, + TTrans::Dial: Send + 'static, + { + if let Err(limit) = self.counters.check_max_pending_outgoing() { + return Err(DialError::ConnectionLimit { limit, handler }); + }; + + let dial = ConcurrentDial::new(transport, peer, addresses); + + let connection_id = self.next_connection_id(); + + let (drop_notifier, drop_receiver) = oneshot::channel(); + + self.spawn( + task::new_for_pending_outgoing_connection( + connection_id, + dial, + drop_receiver, + self.pending_connection_events_tx.clone(), + ) + .boxed(), + ); + + self.counters.inc_pending(&PendingPoint::Dialer); + self.pending.insert( + connection_id, + PendingConnectionInfo { + peer_id: peer, + handler, + endpoint: PendingPoint::Dialer, + _drop_notifier: drop_notifier, + }, + ); + Ok(connection_id) + } + + /// Adds a pending incoming connection to the pool in the form of a + /// `Future` that establishes and negotiates the connection. + /// + /// Returns an error if the limit of pending incoming connections + /// has been reached. + pub fn add_incoming( + &mut self, + future: TFut, + handler: THandler, + info: IncomingInfo<'_>, + ) -> Result + where + TFut: Future> + Send + 'static, + { + let endpoint = info.to_connected_point(); + // TODO: We loose the handler here. + self.counters.check_max_pending_incoming()?; + let connection_id = self.next_connection_id(); + + let (drop_notifier, drop_receiver) = oneshot::channel(); + + self.spawn( + task::new_for_pending_incoming_connection( + connection_id, + future, + drop_receiver, + self.pending_connection_events_tx.clone(), + ) + .boxed(), + ); + + self.counters.inc_pending_incoming(); + self.pending.insert( + connection_id, + PendingConnectionInfo { + peer_id: None, + handler, + endpoint: endpoint.into(), + _drop_notifier: drop_notifier, + }, + ); + Ok(connection_id) + } /// Polls the connection pool for events. /// /// > **Note**: We use a regular `poll` method instead of implementing `Stream`, /// > because we want the `Pool` to stay borrowed if necessary. - pub fn poll<'a>( + pub fn poll<'a, TMuxer>( &'a mut self, cx: &mut Context<'_>, - ) -> Poll> + ) -> Poll> where + TTrans: Transport, TMuxer: StreamMuxer + Send + Sync + 'static, THandler: IntoConnectionHandler + 'static, THandler::Handler: ConnectionHandler> + Send, @@ -677,8 +690,7 @@ impl { let PendingConnectionInfo { diff --git a/core/src/connection/pool/concurrent_dial.rs b/core/src/connection/pool/concurrent_dial.rs index 53998d58e32..59438351ec5 100644 --- a/core/src/connection/pool/concurrent_dial.rs +++ b/core/src/connection/pool/concurrent_dial.rs @@ -34,27 +34,34 @@ use std::{ task::{Context, Poll}, }; -type Dial = - BoxFuture<'static, (Multiaddr, Result<(PeerId, TMuxer), TransportError>)>; +type Dial = BoxFuture< + 'static, + ( + Multiaddr, + Result>, + ), +>; -pub struct ConcurrentDial { - dials: FuturesUnordered>, - pending_dials: Box> + Send>, - errors: Vec<(Multiaddr, TransportError)>, +pub struct ConcurrentDial { + dials: FuturesUnordered>, + pending_dials: Box> + Send>, + errors: Vec<(Multiaddr, TransportError)>, } -impl Unpin for ConcurrentDial {} +impl Unpin for ConcurrentDial {} -impl ConcurrentDial { - pub(crate) fn new( +impl ConcurrentDial +where + TTrans: Transport + Clone + Send + 'static, + TTrans::Output: Send, + TTrans::Error: Send, + TTrans::Dial: Send + 'static, +{ + pub(crate) fn new( transport: TTrans, peer: Option, addresses: impl Iterator + Send + 'static, - ) -> Self - where - TTrans: Transport + Clone + 'static, - TTrans::Dial: Send + 'static, - { + ) -> Self { let mut pending_dials = addresses.map(move |address| { match p2p_addr(peer, address) { Ok(address) => match transport.clone().dial(address.clone()) { @@ -90,26 +97,28 @@ impl ConcurrentDial Future for ConcurrentDial { +impl Future for ConcurrentDial +where + TTrans: Transport, +{ type Output = Result< // Either one dial succeeded, returning the negotiated [`PeerId`], the address, the // muxer and the addresses and errors of the dials that failed before. ( - PeerId, Multiaddr, - TMuxer, - Vec<(Multiaddr, TransportError)>, + TTrans::Output, + Vec<(Multiaddr, TransportError)>, ), // Or all dials failed, thus returning the address and error for each dial. - Vec<(Multiaddr, TransportError)>, + Vec<(Multiaddr, TransportError)>, >; fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { loop { match ready!(self.dials.poll_next_unpin(cx)) { - Some((addr, Ok((peer_id, muxer)))) => { + Some((addr, Ok(output))) => { let errors = std::mem::replace(&mut self.errors, vec![]); - return Poll::Ready(Ok((peer_id, addr, muxer, errors))); + return Poll::Ready(Ok((addr, output, errors))); } Some((addr, Err(e))) => { self.errors.push((addr, e)); diff --git a/core/src/connection/pool/task.rs b/core/src/connection/pool/task.rs index 01fcc41f378..b04a6e1b8c4 100644 --- a/core/src/connection/pool/task.rs +++ b/core/src/connection/pool/task.rs @@ -30,7 +30,7 @@ use crate::{ PendingConnectionError, Substream, }, muxing::StreamMuxer, - transport::TransportError, + transport::{Transport, TransportError}, Multiaddr, PeerId, }; use futures::{ @@ -51,19 +51,21 @@ pub enum Command { } #[derive(Debug)] -pub enum PendingConnectionEvent { +pub enum PendingConnectionEvent +where + TTrans: Transport, +{ // TODO: Remove most of these ConnectionEstablished { id: ConnectionId, - peer_id: PeerId, - muxer: TMuxer, + output: TTrans::Output, // TODO: Document errors in a success message. - outgoing: Option<(Multiaddr, Vec<(Multiaddr, TransportError)>)>, + outgoing: Option<(Multiaddr, Vec<(Multiaddr, TransportError)>)>, }, /// A pending connection failed. PendingFailed { id: ConnectionId, - error: PendingConnectionError, + error: PendingConnectionError, }, } @@ -93,12 +95,14 @@ pub enum EstablishedConnectionEvent { }, } -pub async fn new_for_pending_outgoing_connection( +pub async fn new_for_pending_outgoing_connection( connection_id: ConnectionId, - dial: ConcurrentDial, + dial: ConcurrentDial, drop_receiver: oneshot::Receiver, - mut events: mpsc::Sender>, -) { + mut events: mpsc::Sender>, +) where + TTrans: Transport, +{ match futures::future::select(drop_receiver, Box::pin(dial)).await { Either::Left((Err(oneshot::Canceled), _)) => { let _ = events @@ -109,12 +113,11 @@ pub async fn new_for_pending_outgoing_connection( .await; } Either::Left((Ok(v), _)) => void::unreachable(v), - Either::Right((Ok((peer_id, address, muxer, errors)), _)) => { + Either::Right((Ok((address, output, errors)), _)) => { let _ = events .send(PendingConnectionEvent::ConnectionEstablished { id: connection_id, - peer_id, - muxer, + output, outgoing: Some((address, errors)), }) .await; @@ -130,13 +133,14 @@ pub async fn new_for_pending_outgoing_connection( } } -pub async fn new_for_pending_incoming_connection( +pub async fn new_for_pending_incoming_connection( connection_id: ConnectionId, future: TFut, drop_receiver: oneshot::Receiver, - mut events: mpsc::Sender>, + mut events: mpsc::Sender>, ) where - TFut: Future> + Send + 'static, + TTrans: Transport, + TFut: Future> + Send + 'static, { match futures::future::select(drop_receiver, Box::pin(future)).await { Either::Left((Err(oneshot::Canceled), _)) => { @@ -148,12 +152,11 @@ pub async fn new_for_pending_incoming_connection( .await; } Either::Left((Ok(v), _)) => void::unreachable(v), - Either::Right((Ok((peer_id, muxer)), _)) => { + Either::Right((Ok(output), _)) => { let _ = events .send(PendingConnectionEvent::ConnectionEstablished { id: connection_id, - peer_id, - muxer, + output, outgoing: None, }) .await; diff --git a/core/src/network.rs b/core/src/network.rs index 8cd971a82c2..fb622244eac 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -49,7 +49,7 @@ use std::{ }; /// Implementation of `Stream` that handles the nodes. -pub struct Network +pub struct Network where TTrans: Transport, THandler: IntoConnectionHandler, @@ -61,7 +61,7 @@ where listeners: ListenersStream, /// The nodes currently active. - pool: Pool, + pool: Pool, /// The ongoing dialing attempts. /// @@ -78,7 +78,7 @@ where dialing: FnvHashMap>, } -impl fmt::Debug for Network +impl fmt::Debug for Network where TTrans: fmt::Debug + Transport, THandler: fmt::Debug + ConnectionHandler, @@ -93,19 +93,18 @@ where } } -impl Unpin for Network +impl Unpin for Network where TTrans: Transport, THandler: IntoConnectionHandler, { } -impl Network +impl Network where TTrans: Transport, ::Error: Send + 'static, THandler: IntoConnectionHandler, - TMuxer: Send + 'static, { fn disconnect(&mut self, peer: &PeerId) { self.pool.disconnect(peer); @@ -113,15 +112,11 @@ where } } -impl Network +impl Network where TTrans: Transport + Clone + 'static, ::Error: Send + 'static, - TMuxer: StreamMuxer + Send + 'static, THandler: IntoConnectionHandler + Send + 'static, - ::OutboundOpenInfo: Send, - ::Error: error::Error + Send, - THandler::Handler: ConnectionHandler> + Send, { /// Creates a new node events stream. pub fn new(transport: TTrans, local_peer_id: PeerId, config: NetworkConfig) -> Self { @@ -172,7 +167,6 @@ where /// The translation is transport-specific. See [`Transport::address_translation`]. pub fn address_translation<'a>(&'a self, observed_addr: &'a Multiaddr) -> Vec where - TMuxer: 'a, THandler: 'a, { let transport = self.listeners.transport(); @@ -205,11 +199,11 @@ where handler: THandler, ) -> Result> where - TTrans: Transport + Send, + TTrans: Transport + Send, + TTrans::Output: Send + 'static, + TTrans::Dial: Send + 'static, TTrans::Error: Send + 'static, TTrans::Dial: Send + 'static, - TMuxer: Send + Sync + 'static, - TMuxer::OutboundSubstream: Send, { // If the address ultimately encapsulates an expected peer ID, dial that peer // such that any mismatch is detected. We do not "pop off" the `P2p` protocol @@ -230,10 +224,32 @@ where std::iter::once(address.clone()), None, handler, - None, ) } + /// Initiates a connection attempt to a known peer. + fn dial_peer( + &mut self, + opts: DialingOpts, + ) -> Result> + where + TTrans: Transport + Send, + TTrans::Output: Send + 'static, + TTrans::Dial: Send + 'static, + TTrans::Error: Send + 'static, + { + let id = self.pool.add_outgoing( + self.transport().clone(), + opts.addresses.into_iter(), + Some(opts.peer), + opts.handler, + )?; + + self.dialing.entry(opts.peer).or_default().push(id); + + Ok(id) + } + /// Returns information about the state of the `Network`. pub fn info(&self) -> NetworkInfo { let num_peers = self.pool.num_peers(); @@ -278,7 +294,7 @@ where } /// Obtains a view of a [`Peer`] with the given ID in the network. - pub fn peer(&mut self, peer_id: PeerId) -> Peer<'_, TTrans, THandler, TMuxer> { + pub fn peer(&mut self, peer_id: PeerId) -> Peer<'_, TTrans, THandler> { Peer::new(self, peer_id) } @@ -297,9 +313,8 @@ where handler: THandler, ) -> Result where - TMuxer: StreamMuxer + Send + Sync + 'static, - TMuxer::OutboundSubstream: Send, - TTrans: Transport, + TTrans: Transport, + TTrans::Output: Send + 'static, TTrans::Error: Send + 'static, TTrans::ListenerUpgrade: Send + 'static, { @@ -314,7 +329,7 @@ where } /// Provides an API similar to `Stream`, except that it cannot error. - pub fn poll<'a>( + pub fn poll<'a, TMuxer>( &'a mut self, cx: &mut Context<'_>, ) -> Poll< @@ -325,10 +340,13 @@ where TTrans::Error: Send + 'static, TTrans::Dial: Send + 'static, TTrans::ListenerUpgrade: Send + 'static, - TMuxer: Send + Sync + 'static, + TMuxer: StreamMuxer + Send + Sync + 'static, TMuxer::OutboundSubstream: Send, THandler: IntoConnectionHandler + Send + 'static, ::Error: error::Error + Send + 'static, + ::OutboundOpenInfo: Send, + ::Error: error::Error + Send, + THandler::Handler: ConnectionHandler> + Send, { // Poll the listener(s) for new connections. match ListenersStream::poll(Pin::new(&mut self.listeners), cx) { @@ -443,26 +461,6 @@ where Poll::Ready(event) } - - /// Initiates a connection attempt to a known peer. - fn dial_peer( - &mut self, - opts: DialingOpts, - ) -> Result> - where - TTrans: Transport + Send, - TTrans::Dial: Send + 'static, - TTrans::Error: Send + 'static, - TMuxer: Send + Sync + 'static, - TMuxer::OutboundSubstream: Send, - { - dial_peer_impl( - self.transport().clone(), - &mut self.pool, - &mut self.dialing, - opts, - ) - } } /// Options for a dialing attempt (i.e. repeated connection attempt @@ -473,41 +471,6 @@ struct DialingOpts { addresses: Vec, } -/// Standalone implementation of `Network::dial_peer` for more granular borrowing. -// -// TODO: How about making this a method? -fn dial_peer_impl( - transport: TTrans, - pool: &mut Pool, - dialing: &mut FnvHashMap>, - opts: DialingOpts, -) -> Result> -where - THandler: IntoConnectionHandler + Send + 'static, - ::Error: error::Error + Send + 'static, - ::OutboundOpenInfo: Send + 'static, - THandler::Handler: ConnectionHandler> + Send + 'static, - TTrans: Transport + Clone + Send + 'static, - TTrans::Dial: Send + 'static, - TTrans::Error: error::Error + Send + 'static, - TMuxer: StreamMuxer + Send + Sync + 'static, - TMuxer::OutboundSubstream: Send + 'static, -{ - let result = pool.add_outgoing( - transport, - opts.addresses.into_iter(), - Some(opts.peer), - opts.handler, - Some(opts.peer), - ); - - if let Ok(id) = &result { - dialing.entry(opts.peer).or_default().push(*id); - } - - result -} - /// Callback for handling a failed connection attempt, returning an /// event to emit from the `Network`. /// diff --git a/core/src/network/peer.rs b/core/src/network/peer.rs index b75588e2456..7ca95974b5a 100644 --- a/core/src/network/peer.rs +++ b/core/src/network/peer.rs @@ -37,34 +37,33 @@ use std::{collections::hash_map, error, fmt}; /// > **Note**: In any state there may always be a pending incoming /// > connection attempt from the peer, however, the remote identity /// > of a peer is only known once a connection is fully established. -pub enum Peer<'a, TTrans, THandler, TMuxer> +pub enum Peer<'a, TTrans, THandler> where TTrans: Transport, THandler: IntoConnectionHandler, { /// At least one established connection exists to the peer. - Connected(ConnectedPeer<'a, TTrans, THandler, TMuxer>), + Connected(ConnectedPeer<'a, TTrans, THandler>), /// There is an ongoing dialing (i.e. outgoing connection) attempt /// to the peer. There may already be other established connections /// to the peer. - Dialing(DialingPeer<'a, TTrans, THandler, TMuxer>), + Dialing(DialingPeer<'a, TTrans, THandler>), /// There exists no established connection to the peer and there is /// currently no ongoing dialing (i.e. outgoing connection) attempt /// in progress. - Disconnected(DisconnectedPeer<'a, TTrans, THandler, TMuxer>), + Disconnected(DisconnectedPeer<'a, TTrans, THandler>), /// The peer represents the local node. Local, } -impl<'a, TTrans, THandler, TMuxer> fmt::Debug for Peer<'a, TTrans, THandler, TMuxer> +impl<'a, TTrans, THandler> fmt::Debug for Peer<'a, TTrans, THandler> where TTrans: Transport, TTrans::Error: Send + 'static, THandler: IntoConnectionHandler, - TMuxer: Send + 'static, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match self { @@ -76,14 +75,13 @@ where } } -impl<'a, TTrans, THandler, TMuxer> Peer<'a, TTrans, THandler, TMuxer> +impl<'a, TTrans, THandler> Peer<'a, TTrans, THandler> where TTrans: Transport, TTrans::Error: Send + 'static, THandler: IntoConnectionHandler, - TMuxer: Send + 'static, { - pub(super) fn new(network: &'a mut Network, peer_id: PeerId) -> Self { + pub(super) fn new(network: &'a mut Network, peer_id: PeerId) -> Self { if peer_id == network.local_peer_id { return Peer::Local; } @@ -99,28 +97,27 @@ where Self::disconnected(network, peer_id) } - fn disconnected(network: &'a mut Network, peer_id: PeerId) -> Self { + fn disconnected(network: &'a mut Network, peer_id: PeerId) -> Self { Peer::Disconnected(DisconnectedPeer { network, peer_id }) } - fn connected(network: &'a mut Network, peer_id: PeerId) -> Self { + fn connected(network: &'a mut Network, peer_id: PeerId) -> Self { Peer::Connected(ConnectedPeer { network, peer_id }) } - fn dialing(network: &'a mut Network, peer_id: PeerId) -> Self { + fn dialing(network: &'a mut Network, peer_id: PeerId) -> Self { Peer::Dialing(DialingPeer { network, peer_id }) } } -impl<'a, TTrans, TMuxer, THandler> Peer<'a, TTrans, THandler, TMuxer> +impl<'a, TTrans, THandler> Peer<'a, TTrans, THandler> where - TTrans: Transport + Clone + Send + 'static, + TTrans: Transport + Clone + Send + 'static, + TTrans::Output: Send + 'static, TTrans::Error: Send + 'static, TTrans::Dial: Send + 'static, - TMuxer: StreamMuxer + Send + Sync + 'static, - TMuxer::OutboundSubstream: Send, THandler: IntoConnectionHandler + Send + 'static, - THandler::Handler: ConnectionHandler> + Send, + THandler::Handler: ConnectionHandler + Send, ::OutboundOpenInfo: Send, ::Error: error::Error + Send + 'static, { @@ -166,7 +163,7 @@ where self, addresses: I, handler: THandler, - ) -> Result<(ConnectionId, DialingPeer<'a, TTrans, THandler, TMuxer>), DialError> + ) -> Result<(ConnectionId, DialingPeer<'a, TTrans, THandler>), DialError> where I: IntoIterator, { @@ -190,7 +187,7 @@ where /// Converts the peer into a `ConnectedPeer`, if an established connection exists. /// /// Succeeds if the there is at least one established connection to the peer. - pub fn into_connected(self) -> Option> { + pub fn into_connected(self) -> Option> { match self { Peer::Connected(peer) => Some(peer), Peer::Dialing(peer) => peer.into_connected(), @@ -202,7 +199,7 @@ where /// Converts the peer into a `DialingPeer`, if a dialing attempt exists. /// /// Succeeds if the there is at least one pending outgoing connection to the peer. - pub fn into_dialing(self) -> Option> { + pub fn into_dialing(self) -> Option> { match self { Peer::Dialing(peer) => Some(peer), Peer::Connected(peer) => peer.into_dialing(), @@ -213,7 +210,7 @@ where /// Converts the peer into a `DisconnectedPeer`, if neither an established connection /// nor a dialing attempt exists. - pub fn into_disconnected(self) -> Option> { + pub fn into_disconnected(self) -> Option> { match self { Peer::Disconnected(peer) => Some(peer), _ => None, @@ -224,28 +221,27 @@ where /// The representation of a peer in a [`Network`] to whom at least /// one established connection exists. There may also be additional ongoing /// dialing attempts to the peer. -pub struct ConnectedPeer<'a, TTrans, THandler, TMuxer> +pub struct ConnectedPeer<'a, TTrans, THandler> where TTrans: Transport, THandler: IntoConnectionHandler, { - network: &'a mut Network, + network: &'a mut Network, peer_id: PeerId, } -impl<'a, TTrans, THandler, TMuxer> ConnectedPeer<'a, TTrans, THandler, TMuxer> +impl<'a, TTrans, THandler> ConnectedPeer<'a, TTrans, THandler> where TTrans: Transport, ::Error: Send + 'static, THandler: IntoConnectionHandler, - TMuxer: Send + 'static, { pub fn id(&self) -> &PeerId { &self.peer_id } /// Returns the `ConnectedPeer` into a `Peer`. - pub fn into_peer(self) -> Peer<'a, TTrans, THandler, TMuxer> { + pub fn into_peer(self) -> Peer<'a, TTrans, THandler> { Peer::Connected(self) } @@ -271,7 +267,7 @@ where /// Converts this peer into a [`DialingPeer`], if there is an ongoing /// dialing attempt, `None` otherwise. - pub fn into_dialing(self) -> Option> { + pub fn into_dialing(self) -> Option> { if self.network.dialing.contains_key(&self.peer_id) { Some(DialingPeer { network: self.network, @@ -298,7 +294,7 @@ where } /// Disconnects from the peer, closing all connections. - pub fn disconnect(self) -> DisconnectedPeer<'a, TTrans, THandler, TMuxer> { + pub fn disconnect(self) -> DisconnectedPeer<'a, TTrans, THandler> { self.network.disconnect(&self.peer_id); DisconnectedPeer { network: self.network, @@ -307,12 +303,11 @@ where } } -impl<'a, TTrans, THandler, TMuxer> fmt::Debug for ConnectedPeer<'a, TTrans, THandler, TMuxer> +impl<'a, TTrans, THandler> fmt::Debug for ConnectedPeer<'a, TTrans, THandler> where TTrans: Transport, TTrans::Error: Send + 'static, THandler: IntoConnectionHandler, - TMuxer: Send + 'static, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.debug_struct("ConnectedPeer") @@ -333,34 +328,33 @@ where /// The representation of a peer in a [`Network`] to whom a dialing /// attempt is ongoing. There may already exist other established /// connections to this peer. -pub struct DialingPeer<'a, TTrans, THandler, TMuxer> +pub struct DialingPeer<'a, TTrans, THandler> where TTrans: Transport, THandler: IntoConnectionHandler, { - network: &'a mut Network, + network: &'a mut Network, peer_id: PeerId, } -impl<'a, TTrans, THandler, TMuxer> DialingPeer<'a, TTrans, THandler, TMuxer> +impl<'a, TTrans, THandler> DialingPeer<'a, TTrans, THandler> where TTrans: Transport, TTrans::Error: Send + 'static, THandler: IntoConnectionHandler, - TMuxer: Send + 'static, { pub fn id(&self) -> &PeerId { &self.peer_id } /// Returns the `DialingPeer` into a `Peer`. - pub fn into_peer(self) -> Peer<'a, TTrans, THandler, TMuxer> { + pub fn into_peer(self) -> Peer<'a, TTrans, THandler> { Peer::Dialing(self) } /// Disconnects from this peer, closing all established connections and /// aborting all dialing attempts. - pub fn disconnect(self) -> DisconnectedPeer<'a, TTrans, THandler, TMuxer> { + pub fn disconnect(self) -> DisconnectedPeer<'a, TTrans, THandler> { self.network.disconnect(&self.peer_id); DisconnectedPeer { network: self.network, @@ -376,7 +370,7 @@ where } /// Converts the peer into a `ConnectedPeer`, if an established connection exists. - pub fn into_connected(self) -> Option> { + pub fn into_connected(self) -> Option> { if self.is_connected() { Some(ConnectedPeer { peer_id: self.peer_id, @@ -407,7 +401,7 @@ where } /// Gets an iterator over all dialing (i.e. pending outgoing) connections to the peer. - pub fn attempts(&mut self) -> DialingAttemptIter<'_, THandler, TMuxer, TTrans::Error> { + pub fn attempts(&mut self) -> DialingAttemptIter<'_, THandler, TTrans> { DialingAttemptIter::new( &self.peer_id, &mut self.network.pool, @@ -425,12 +419,11 @@ where } } -impl<'a, TTrans, THandler, TMuxer> fmt::Debug for DialingPeer<'a, TTrans, THandler, TMuxer> +impl<'a, TTrans, THandler> fmt::Debug for DialingPeer<'a, TTrans, THandler> where TTrans: Transport, TTrans::Error: Send + 'static, THandler: IntoConnectionHandler, - TMuxer: Send + 'static, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.debug_struct("DialingPeer") @@ -451,16 +444,16 @@ where /// The representation of a peer to whom the `Network` has currently /// neither an established connection, nor an ongoing dialing attempt /// initiated by the local peer. -pub struct DisconnectedPeer<'a, TTrans, THandler, TMuxer> +pub struct DisconnectedPeer<'a, TTrans, THandler> where TTrans: Transport, THandler: IntoConnectionHandler, { peer_id: PeerId, - network: &'a mut Network, + network: &'a mut Network, } -impl<'a, TTrans, THandler, TMuxer> fmt::Debug for DisconnectedPeer<'a, TTrans, THandler, TMuxer> +impl<'a, TTrans, THandler> fmt::Debug for DisconnectedPeer<'a, TTrans, THandler> where TTrans: Transport, THandler: IntoConnectionHandler, @@ -472,7 +465,7 @@ where } } -impl<'a, TTrans, THandler, TMuxer> DisconnectedPeer<'a, TTrans, THandler, TMuxer> +impl<'a, TTrans, THandler> DisconnectedPeer<'a, TTrans, THandler> where TTrans: Transport, THandler: IntoConnectionHandler, @@ -482,7 +475,7 @@ where } /// Returns the `DisconnectedPeer` into a `Peer`. - pub fn into_peer(self) -> Peer<'a, TTrans, THandler, TMuxer> { + pub fn into_peer(self) -> Peer<'a, TTrans, THandler> { Peer::Disconnected(self) } } @@ -533,11 +526,11 @@ impl<'a, THandler: IntoConnectionHandler> DialingAttempt<'a, THandler> { } /// An iterator over the ongoing dialing attempts to a peer. -pub struct DialingAttemptIter<'a, THandler: IntoConnectionHandler, TMuxer, TTransErr> { +pub struct DialingAttemptIter<'a, THandler: IntoConnectionHandler, TTrans: Transport> { /// The peer whose dialing attempts are being iterated. peer_id: &'a PeerId, /// The underlying connection `Pool` of the `Network`. - pool: &'a mut Pool, + pool: &'a mut Pool, /// The state of all current dialing attempts known to the `Network`. /// /// Ownership of the `OccupiedEntry` for `peer_id` containing all attempts must be @@ -553,14 +546,12 @@ pub struct DialingAttemptIter<'a, THandler: IntoConnectionHandler, TMuxer, TTran // Note: Ideally this would be an implementation of `Iterator`, but that // requires GATs (cf. https://github.com/rust-lang/rust/issues/44265) and // a different definition of `Iterator`. -impl<'a, THandler: IntoConnectionHandler, TMuxer, TTransErr: Send + 'static> - DialingAttemptIter<'a, THandler, TMuxer, TTransErr> -where - TMuxer: Send + 'static, +impl<'a, THandler: IntoConnectionHandler, TTrans: Transport> + DialingAttemptIter<'a, THandler, TTrans> { fn new( peer_id: &'a PeerId, - pool: &'a mut Pool, + pool: &'a mut Pool, dialing: &'a mut FnvHashMap>, ) -> Self { let end = dialing.get(peer_id).map_or(0, |conns| conns.len()); From 0ba84ab56a6d3de4b76bb5f6cda409b861fe0bf8 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Fri, 8 Oct 2021 19:03:46 +0200 Subject: [PATCH 37/67] core/src/connection: Split PendingConnectionError --- core/src/connection.rs | 5 +- core/src/connection/error.rs | 45 +++++---- core/src/connection/pool.rs | 164 +++++++++++++++++++++++-------- core/src/connection/pool/task.rs | 17 ++-- 4 files changed, 161 insertions(+), 70 deletions(-) diff --git a/core/src/connection.rs b/core/src/connection.rs index 6b8f37d436c..64b1883812c 100644 --- a/core/src/connection.rs +++ b/core/src/connection.rs @@ -25,7 +25,10 @@ mod substream; pub(crate) mod pool; -pub use error::{ConnectionError, PendingConnectionError}; +pub use error::{ + ConnectionError, PendingConnectionError, PendingInboundConnectionError, + PendingOutboundConnectionError, +}; pub use handler::{ConnectionHandler, ConnectionHandlerEvent, IntoConnectionHandler}; pub use listeners::{ListenerId, ListenersEvent, ListenersStream}; pub use pool::{ConnectionCounters, ConnectionLimits}; diff --git a/core/src/connection/error.rs b/core/src/connection/error.rs index 5249eaca947..c8b778c7bde 100644 --- a/core/src/connection/error.rs +++ b/core/src/connection/error.rs @@ -58,16 +58,23 @@ where } } +/// Errors that can occur in the context of a pending outgoing `Connection`. +/// +/// Note: Addresses for an outbound connection are dialed in parallel. Thus, compared to +/// [`PendingInboundConnectionError`], one or more [`TransportError`]s can occur for a single +/// connection. +pub type PendingOutboundConnectionError = + PendingConnectionError)>>; + +/// Errors that can occur in the context of a pending incoming `Connection`. +pub type PendingInboundConnectionError = + PendingConnectionError>; + /// Errors that can occur in the context of a pending `Connection`. #[derive(Debug)] -pub enum PendingConnectionError { - /// An error occurred while negotiating the transport protocol(s) on a - /// dialing connection. - TransportDial(Vec<(Multiaddr, TransportError)>), - - /// An error occurred while negotiating the transport protocol(s) on a - /// listening connection. - TransportListen(TransportError), +pub enum PendingConnectionError { + /// An error occurred while negotiating the transport protocol(s) on a connection. + Transport(TransportError), /// The connection was dropped because the connection limit /// for a peer has been reached. @@ -85,25 +92,18 @@ pub enum PendingConnectionError { IO(io::Error), } -impl fmt::Display for PendingConnectionError +impl fmt::Display for PendingConnectionError where - TTransErr: fmt::Display + fmt::Debug, + TransportError: fmt::Display + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { PendingConnectionError::IO(err) => write!(f, "Pending connection: I/O error: {}", err), PendingConnectionError::Aborted => write!(f, "Pending connection: Aborted."), - PendingConnectionError::TransportListen(err) => { - write!( - f, - "Pending connection: Transport error on listening connection: {}", - err - ) - } - PendingConnectionError::TransportDial(err) => { + PendingConnectionError::Transport(err) => { write!( f, - "Pending connection: Transport error on dialing connection: {:?}", + "Pending connection: Transport error on connection: {}", err ) } @@ -117,15 +117,14 @@ where } } -impl std::error::Error for PendingConnectionError +impl std::error::Error for PendingConnectionError where - TTransErr: std::error::Error + 'static, + TransportError: std::error::Error + 'static, { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { PendingConnectionError::IO(err) => Some(err), - PendingConnectionError::TransportListen(err) => Some(err), - PendingConnectionError::TransportDial(_) => None, + PendingConnectionError::Transport(_) => None, PendingConnectionError::InvalidPeerId => None, PendingConnectionError::Aborted => None, PendingConnectionError::ConnectionLimit(..) => None, diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index 40f7b2a2ff1..508b0f424a0 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -23,7 +23,8 @@ use crate::{ connection::{ handler::{THandlerError, THandlerInEvent, THandlerOutEvent}, Connected, ConnectionError, ConnectionHandler, ConnectionId, ConnectionLimit, IncomingInfo, - IntoConnectionHandler, PendingConnectionError, PendingPoint, Substream, + IntoConnectionHandler, PendingConnectionError, PendingInboundConnectionError, + PendingOutboundConnectionError, PendingPoint, Substream, }, muxing::StreamMuxer, network::DialError, @@ -35,7 +36,7 @@ use fnv::FnvHashMap; use futures::prelude::*; use futures::{ channel::{mpsc, oneshot}, - future::{poll_fn, BoxFuture}, + future::{poll_fn, BoxFuture, Either}, ready, stream::FuturesUnordered, }; @@ -170,14 +171,12 @@ where handler: THandler::Handler, }, - /// A connection attempt failed. - PendingConnectionError { + /// An outbound connection attempt failed. + PendingOutboundConnectionError { /// The ID of the failed connection. id: ConnectionId, - /// The local endpoint of the failed connection. - endpoint: PendingPoint, /// The error that occurred. - error: PendingConnectionError, + error: PendingOutboundConnectionError, /// The handler that was supposed to handle the connection, /// if the connection failed before the handler was consumed. handler: THandler, @@ -185,6 +184,21 @@ where peer: Option, }, + /// An outbound connection attempt failed. + PendingInboundConnectionError { + /// The ID of the failed connection. + id: ConnectionId, + // TODO: Document. + send_back_addr: Multiaddr, + // TODO: Document. + local_addr: Multiaddr, + /// The error that occurred. + error: PendingInboundConnectionError, + /// The handler that was supposed to handle the connection, + /// if the connection failed before the handler was consumed. + handler: THandler, + }, + /// A node has produced an event. ConnectionEvent { /// The connection that has generated the event. @@ -210,10 +224,10 @@ where TTrans::Error: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match *self { + match self { PoolEvent::ConnectionEstablished { - ref connection, - ref outgoing, + connection, + outgoing, .. } => f .debug_tuple("PoolEvent::OutgoingConnectionEstablished") @@ -221,9 +235,9 @@ where .field(outgoing) .finish(), PoolEvent::ConnectionClosed { - ref id, - ref connected, - ref error, + id, + connected, + error, .. } => f .debug_struct("PoolEvent::ConnectionClosed") @@ -231,25 +245,36 @@ where .field("connected", connected) .field("error", error) .finish(), - PoolEvent::PendingConnectionError { - ref id, ref error, .. + PoolEvent::PendingOutboundConnectionError { + id, error, peer, .. } => f - .debug_struct("PoolEvent::PendingConnectionError") + .debug_struct("PoolEvent::PendingOutboundConnectionError") .field("id", id) .field("error", error) + .field("peer", peer) .finish(), - PoolEvent::ConnectionEvent { - ref connection, - ref event, + PoolEvent::PendingInboundConnectionError { + id, + error, + send_back_addr, + local_addr, + .. } => f + .debug_struct("PoolEvent::PendingInboundConnectionError") + .field("id", id) + .field("error", error) + .field("send_back_addr", send_back_addr) + .field("local_addr", local_addr) + .finish(), + PoolEvent::ConnectionEvent { connection, event } => f .debug_struct("PoolEvent::ConnectionEvent") .field("peer", &connection.peer_id()) .field("event", event) .finish(), PoolEvent::AddressChange { - ref connection, - ref new_endpoint, - ref old_endpoint, + connection, + new_endpoint, + old_endpoint, } => f .debug_struct("PoolEvent::AddressChange") .field("peer", &connection.peer_id()) @@ -730,11 +755,27 @@ where ), }; + enum Error { + ConnectionLimit(ConnectionLimit), + InvalidPeerId, + } + + impl From for PendingConnectionError { + fn from(error: Error) -> Self { + match error { + Error::ConnectionLimit(limit) => { + PendingConnectionError::ConnectionLimit(limit) + } + Error::InvalidPeerId => PendingConnectionError::InvalidPeerId, + } + } + } + let error = self .counters // Check general established connection limit. .check_max_established(&endpoint) - .map_err(PendingConnectionError::ConnectionLimit) + .map_err(Error::ConnectionLimit) // Check per-peer established connection limit. .and_then(|()| { self.counters @@ -742,13 +783,13 @@ where &self.established, peer_id, )) - .map_err(PendingConnectionError::ConnectionLimit) + .map_err(Error::ConnectionLimit) }) // Check expected peer id matches. .and_then(|()| { if let Some(peer) = expected_peer_id { if peer != peer_id { - return Err(PendingConnectionError::InvalidPeerId); + return Err(Error::InvalidPeerId); } } Ok(()) @@ -756,7 +797,7 @@ where // Check peer is not local peer. .and_then(|()| { if self.local_id == peer_id { - Err(PendingConnectionError::InvalidPeerId) + Err(Error::InvalidPeerId) } else { Ok(()) } @@ -772,13 +813,28 @@ where .boxed(), ); - return Poll::Ready(PoolEvent::PendingConnectionError { - id, - endpoint: endpoint.into(), - error, - handler, - peer: Some(peer_id), - }); + match endpoint { + ConnectedPoint::Dialer { .. } => { + return Poll::Ready(PoolEvent::PendingOutboundConnectionError { + id, + error: error.into(), + handler, + peer: Some(peer_id), + }) + } + ConnectedPoint::Listener { + send_back_addr, + local_addr, + } => { + return Poll::Ready(PoolEvent::PendingInboundConnectionError { + id, + error: error.into(), + handler, + send_back_addr, + local_addr, + }) + } + }; } // Add the connection to the pool. @@ -825,6 +881,9 @@ where } } task::PendingConnectionEvent::PendingFailed { id, error } => { + // TODO: How about not removing from pending on abortion and instead remove it + // here. Thus not requiring a loop in poll. + if let Some(PendingConnectionInfo { peer_id, handler, @@ -833,13 +892,38 @@ where }) = self.pending.remove(&id) { self.counters.dec_pending(&endpoint); - return Poll::Ready(PoolEvent::PendingConnectionError { - id, - endpoint, - error, - handler, - peer: peer_id, - }); + + match (endpoint, error) { + (PendingPoint::Dialer, Either::Left(error)) => { + return Poll::Ready(PoolEvent::PendingOutboundConnectionError { + id, + error, + handler, + peer: peer_id, + }); + } + ( + PendingPoint::Listener { + send_back_addr, + local_addr, + }, + Either::Right(error), + ) => { + return Poll::Ready(PoolEvent::PendingInboundConnectionError { + id, + error, + handler, + send_back_addr, + local_addr, + }); + } + (PendingPoint::Dialer, Either::Right(_)) => { + unreachable!("Inbound error for outbound connection.") + } + (PendingPoint::Listener { .. }, Either::Left(_)) => { + unreachable!("Outbound error for inbound connection.") + } + } } } } diff --git a/core/src/connection/pool/task.rs b/core/src/connection/pool/task.rs index b04a6e1b8c4..0cbef328bd5 100644 --- a/core/src/connection/pool/task.rs +++ b/core/src/connection/pool/task.rs @@ -27,7 +27,7 @@ use crate::{ self, handler::{THandlerError, THandlerInEvent, THandlerOutEvent}, ConnectionError, ConnectionHandler, ConnectionId, IntoConnectionHandler, - PendingConnectionError, Substream, + PendingInboundConnectionError, PendingOutboundConnectionError, Substream, }, muxing::StreamMuxer, transport::{Transport, TransportError}, @@ -65,7 +65,10 @@ where /// A pending connection failed. PendingFailed { id: ConnectionId, - error: PendingConnectionError, + error: Either< + PendingOutboundConnectionError, + PendingInboundConnectionError, + >, }, } @@ -108,7 +111,7 @@ pub async fn new_for_pending_outgoing_connection( let _ = events .send(PendingConnectionEvent::PendingFailed { id: connection_id, - error: PendingConnectionError::Aborted, + error: Either::Left(PendingOutboundConnectionError::Aborted), }) .await; } @@ -126,7 +129,7 @@ pub async fn new_for_pending_outgoing_connection( let _ = events .send(PendingConnectionEvent::PendingFailed { id: connection_id, - error: PendingConnectionError::TransportDial(e), + error: Either::Left(PendingOutboundConnectionError::Transport(e)), }) .await; } @@ -147,7 +150,7 @@ pub async fn new_for_pending_incoming_connection( let _ = events .send(PendingConnectionEvent::PendingFailed { id: connection_id, - error: PendingConnectionError::Aborted, + error: Either::Right(PendingInboundConnectionError::Aborted), }) .await; } @@ -165,7 +168,9 @@ pub async fn new_for_pending_incoming_connection( let _ = events .send(PendingConnectionEvent::PendingFailed { id: connection_id, - error: PendingConnectionError::TransportListen(TransportError::Other(e)), + error: Either::Right(PendingInboundConnectionError::Transport( + TransportError::Other(e), + )), }) .await; } From 30401cbaa112c5f6c1badc6dce4ff80551bef9a8 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Fri, 8 Oct 2021 19:04:06 +0200 Subject: [PATCH 38/67] core/src/network: Remove Network::dialing --- core/src/connection/pool/concurrent_dial.rs | 4 +- core/src/network.rs | 153 +++++++------------- core/src/network/event.rs | 9 +- core/src/network/peer.rs | 133 +++++------------ 4 files changed, 91 insertions(+), 208 deletions(-) diff --git a/core/src/connection/pool/concurrent_dial.rs b/core/src/connection/pool/concurrent_dial.rs index 59438351ec5..c7ce1ae2e52 100644 --- a/core/src/connection/pool/concurrent_dial.rs +++ b/core/src/connection/pool/concurrent_dial.rs @@ -34,11 +34,11 @@ use std::{ task::{Context, Poll}, }; -type Dial = BoxFuture< +type Dial = BoxFuture< 'static, ( Multiaddr, - Result>, + Result<::Output, TransportError<::Error>>, ), >; diff --git a/core/src/network.rs b/core/src/network.rs index fb622244eac..18d3b85f549 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -30,17 +30,13 @@ use crate::{ handler::{THandlerInEvent, THandlerOutEvent}, pool::{Pool, PoolConfig, PoolEvent}, ConnectionHandler, ConnectionId, ConnectionLimit, IncomingInfo, IntoConnectionHandler, - ListenerId, ListenersEvent, ListenersStream, PendingConnectionError, PendingPoint, - Substream, + ListenerId, ListenersEvent, ListenersStream, PendingPoint, Substream, }, muxing::StreamMuxer, transport::{Transport, TransportError}, Executor, Multiaddr, PeerId, }; -use fnv::FnvHashMap; -use smallvec::SmallVec; use std::{ - collections::hash_map, convert::TryFrom as _, error, fmt, num::NonZeroUsize, @@ -62,20 +58,6 @@ where /// The nodes currently active. pool: Pool, - - /// The ongoing dialing attempts. - /// - /// There may be multiple ongoing dialing attempts to the same peer. - /// Each dialing attempt is associated with a new connection and hence - /// a new connection ID. - /// - /// > **Note**: `dialing` must be consistent with the pending outgoing - /// > connections in `pool`. That is, for every entry in `dialing` - /// > there must exist a pending outgoing connection in `pool` with - /// > the same connection ID. This is ensured by the implementation of - /// > `Network` (see `dial_peer_impl` and `on_connection_failed`) - /// > together with the implementation of `DialingAttempt::abort`. - dialing: FnvHashMap>, } impl fmt::Debug for Network @@ -88,7 +70,6 @@ where .field("local_peer_id", &self.local_peer_id) .field("listeners", &self.listeners) .field("peers", &self.pool) - .field("dialing", &self.dialing) .finish() } } @@ -103,12 +84,29 @@ where impl Network where TTrans: Transport, - ::Error: Send + 'static, THandler: IntoConnectionHandler, { + /// Checks whether the network has an established connection to a peer. + pub fn is_connected(&self, peer: &PeerId) -> bool { + self.pool.is_connected(peer) + } + + fn dialing_attempts(&self, peer: PeerId) -> impl Iterator { + self.pool + .iter_pending_info() + .filter(move |(_, endpoint, peer_id)| { + matches!(endpoint, PendingPoint::Dialer) && peer_id.as_ref() == Some(&peer) + }) + .map(|(connection_id, _, _)| connection_id) + } + + /// Checks whether the network has an ongoing dialing attempt to a peer. + pub fn is_dialing(&self, peer: &PeerId) -> bool { + self.dialing_attempts(*peer).next().is_some() + } + fn disconnect(&mut self, peer: &PeerId) { self.pool.disconnect(peer); - self.dialing.remove(peer); } } @@ -124,7 +122,6 @@ where local_peer_id, listeners: ListenersStream::new(transport), pool: Pool::new(local_peer_id, config.pool_config, config.limits), - dialing: Default::default(), } } @@ -245,8 +242,6 @@ where opts.handler, )?; - self.dialing.entry(opts.peer).or_default().push(id); - Ok(id) } @@ -271,16 +266,6 @@ where self.pool.iter_connected() } - /// Checks whether the network has an established connection to a peer. - pub fn is_connected(&self, peer: &PeerId) -> bool { - self.pool.is_connected(peer) - } - - /// Checks whether the network has an ongoing dialing attempt to a peer. - pub fn is_dialing(&self, peer: &PeerId) -> bool { - self.dialing.contains_key(peer) - } - /// Checks whether the network has neither an ongoing dialing attempt, /// nor an established connection to a peer. pub fn is_disconnected(&self, peer: &PeerId) -> bool { @@ -290,7 +275,10 @@ where /// Returns a list of all the peers to whom a new outgoing connection /// is currently being established. pub fn dialing_peers(&self) -> impl Iterator { - self.dialing.keys() + self.pool + .iter_pending_info() + .filter(|(_, endpoint, _)| matches!(endpoint, PendingPoint::Dialer)) + .filter_map(|(_, _, peer)| peer.as_ref()) } /// Obtains a view of a [`Peer`] with the given ID in the network. @@ -407,30 +395,39 @@ where connection, num_established, outgoing, + }) => NetworkEvent::ConnectionEstablished { + connection, + num_established, + outgoing, + }, + Poll::Ready(PoolEvent::PendingOutboundConnectionError { + id: _, + error, + handler, + peer, }) => { - if let hash_map::Entry::Occupied(mut e) = self.dialing.entry(connection.peer_id()) { - e.get_mut().retain(|s| *s != connection.id()); - if e.get().is_empty() { - e.remove(); + if let Some(peer) = peer { + NetworkEvent::DialError { + handler, + peer_id: peer, + error, } - } - - NetworkEvent::ConnectionEstablished { - connection, - num_established, - outgoing, + } else { + NetworkEvent::UnknownPeerDialError { error, handler } } } - Poll::Ready(PoolEvent::PendingConnectionError { - id, - endpoint, + Poll::Ready(PoolEvent::PendingInboundConnectionError { + id: _, + send_back_addr, + local_addr, error, handler, - .. - }) => { - let dialing = &mut self.dialing; - on_connection_failed(dialing, id, endpoint, error, handler) - } + }) => NetworkEvent::IncomingConnectionError { + error, + handler, + send_back_addr, + local_addr, + }, Poll::Ready(PoolEvent::ConnectionClosed { id, connected, @@ -471,54 +468,6 @@ struct DialingOpts { addresses: Vec, } -/// Callback for handling a failed connection attempt, returning an -/// event to emit from the `Network`. -/// -/// If the failed connection attempt was a dialing attempt and there -/// are more addresses to try, new `DialingOpts` are returned. -fn on_connection_failed<'a, TTrans, THandler>( - dialing: &mut FnvHashMap>, - id: ConnectionId, - endpoint: PendingPoint, - error: PendingConnectionError, - handler: THandler, -) -> NetworkEvent<'a, TTrans, THandlerInEvent, THandlerOutEvent, THandler> -where - TTrans: Transport, - THandler: IntoConnectionHandler, -{ - // Check if the failed connection is associated with a dialing attempt. - let dialing_failed = dialing.iter_mut().find_map(|(peer, attempts)| { - if attempts.iter().any(|s| *s == id) { - Some(*peer) - } else { - None - } - }); - - if let Some(peer_id) = dialing_failed { - NetworkEvent::DialError { - handler, - peer_id, - error, - } - } else { - // A pending incoming connection or outgoing connection to an unknown peer failed. - match endpoint { - PendingPoint::Dialer => NetworkEvent::UnknownPeerDialError { error, handler }, - PendingPoint::Listener { - send_back_addr, - local_addr, - } => NetworkEvent::IncomingConnectionError { - error, - handler, - send_back_addr, - local_addr, - }, - } - } -} - /// Information about the network obtained by [`Network::info()`]. #[derive(Clone, Debug)] pub struct NetworkInfo { diff --git a/core/src/network/event.rs b/core/src/network/event.rs index b5f8d715ccd..fa4a4e80a80 100644 --- a/core/src/network/event.rs +++ b/core/src/network/event.rs @@ -23,7 +23,8 @@ use crate::{ connection::{ Connected, ConnectedPoint, ConnectionError, ConnectionHandler, ConnectionId, - EstablishedConnection, IntoConnectionHandler, ListenerId, PendingConnectionError, + EstablishedConnection, IntoConnectionHandler, ListenerId, PendingInboundConnectionError, + PendingOutboundConnectionError, }, transport::{Transport, TransportError}, Multiaddr, PeerId, @@ -91,7 +92,7 @@ where /// Address used to send back data to the remote. send_back_addr: Multiaddr, /// The error that happened. - error: PendingConnectionError, + error: PendingInboundConnectionError, handler: THandler, }, @@ -141,7 +142,7 @@ where peer_id: PeerId, /// The error that happened. - error: PendingConnectionError, + error: PendingOutboundConnectionError, }, // TODO: How about merging this into [`DialError`]? @@ -151,7 +152,7 @@ where // multiaddr: Multiaddr, /// The error that happened. - error: PendingConnectionError, + error: PendingOutboundConnectionError, handler: THandler, }, diff --git a/core/src/network/peer.rs b/core/src/network/peer.rs index 7ca95974b5a..77a82b408b5 100644 --- a/core/src/network/peer.rs +++ b/core/src/network/peer.rs @@ -23,13 +23,10 @@ use crate::{ connection::{ handler::THandlerInEvent, pool::Pool, ConnectionHandler, ConnectionId, EstablishedConnection, EstablishedConnectionIter, IntoConnectionHandler, PendingConnection, - Substream, }, - Multiaddr, PeerId, StreamMuxer, Transport, + Multiaddr, PeerId, Transport, }; -use fnv::FnvHashMap; -use smallvec::SmallVec; -use std::{collections::hash_map, error, fmt}; +use std::{collections::VecDeque, error, fmt}; /// The possible representations of a peer in a [`Network`], as /// seen by the local node. @@ -90,7 +87,7 @@ where return Self::connected(network, peer_id); } - if network.dialing.get_mut(&peer_id).is_some() { + if network.is_dialing(&peer_id) { return Self::dialing(network, peer_id); } @@ -262,13 +259,13 @@ where /// /// Returns `true` iff [`ConnectedPeer::into_dialing`] returns `Some`. pub fn is_dialing(&self) -> bool { - self.network.dialing.contains_key(&self.peer_id) + self.network.is_dialing(&self.peer_id) } /// Converts this peer into a [`DialingPeer`], if there is an ongoing /// dialing attempt, `None` otherwise. pub fn into_dialing(self) -> Option> { - if self.network.dialing.contains_key(&self.peer_id) { + if self.network.is_dialing(&self.peer_id) { Some(DialingPeer { network: self.network, peer_id: self.peer_id, @@ -320,7 +317,7 @@ where .iter_peer_established_info(&self.peer_id) .collect::>(), ) - .field("attempts", &self.network.dialing.get(&self.peer_id)) + .field("attempts", &self.network.is_dialing(&self.peer_id)) .finish() } } @@ -386,27 +383,15 @@ where // // TODO: Still needed? pub fn attempt(&mut self, id: ConnectionId) -> Option> { - if let hash_map::Entry::Occupied(attempts) = self.network.dialing.entry(self.peer_id) { - if let Some(pos) = attempts.get().iter().position(|s| *s == id) { - if let Some(inner) = self.network.pool.get_outgoing(id) { - return Some(DialingAttempt { - pos, - inner, - attempts, - }); - } - } - } - None + Some(DialingAttempt { + peer_id: self.peer_id, + inner: self.network.pool.get_outgoing(id)?, + }) } /// Gets an iterator over all dialing (i.e. pending outgoing) connections to the peer. pub fn attempts(&mut self) -> DialingAttemptIter<'_, THandler, TTrans> { - DialingAttemptIter::new( - &self.peer_id, - &mut self.network.pool, - &mut self.network.dialing, - ) + DialingAttemptIter::new(&self.peer_id, &mut self.network) } /// Obtains some dialing connection to the peer. @@ -436,7 +421,6 @@ where .iter_peer_established_info(&self.peer_id) .collect::>(), ) - .field("attempts", &self.network.dialing.get(&self.peer_id)) .finish() } } @@ -484,12 +468,9 @@ where /// a known / expected remote peer ID and a list of alternative addresses /// to connect to, if the current connection attempt fails. pub struct DialingAttempt<'a, THandler: IntoConnectionHandler> { + peer_id: PeerId, /// The underlying pending connection in the `Pool`. inner: PendingConnection<'a, THandler>, - /// All current dialing attempts of the peer. - attempts: hash_map::OccupiedEntry<'a, PeerId, SmallVec<[ConnectionId; 10]>>, - /// The position of the current `ConnectionId` of this connection in the `attempts`. - pos: usize, } impl<'a, THandler: IntoConnectionHandler> DialingAttempt<'a, THandler> { @@ -500,7 +481,7 @@ impl<'a, THandler: IntoConnectionHandler> DialingAttempt<'a, THandler> { /// Returns the (expected) peer ID of the dialing attempt. pub fn peer_id(&self) -> &PeerId { - self.attempts.key() + &self.peer_id } // /// Returns the remote address of the current connection attempt. @@ -516,11 +497,7 @@ impl<'a, THandler: IntoConnectionHandler> DialingAttempt<'a, THandler> { /// Aborting a dialing attempt involves aborting the current connection /// attempt and dropping any remaining addresses given to [`Peer::dial()`] /// that have not yet been tried. - pub fn abort(mut self) { - self.attempts.get_mut().remove(self.pos); - if self.attempts.get().is_empty() { - self.attempts.remove(); - } + pub fn abort(self) { self.inner.abort(); } } @@ -531,16 +508,8 @@ pub struct DialingAttemptIter<'a, THandler: IntoConnectionHandler, TTrans: Trans peer_id: &'a PeerId, /// The underlying connection `Pool` of the `Network`. pool: &'a mut Pool, - /// The state of all current dialing attempts known to the `Network`. - /// - /// Ownership of the `OccupiedEntry` for `peer_id` containing all attempts must be - /// borrowed to each `DialingAttempt` in order for it to remove the entry if the - /// last dialing attempt is aborted. - dialing: &'a mut FnvHashMap>, - /// The current position of the iterator in `dialing[peer_id]`. - pos: usize, - /// The total number of elements in `dialing[peer_id]` to iterate over. - end: usize, + /// [`ConnectionId`]s of the dialing attempts of the peer. + connections: VecDeque, } // Note: Ideally this would be an implementation of `Iterator`, but that @@ -549,76 +518,40 @@ pub struct DialingAttemptIter<'a, THandler: IntoConnectionHandler, TTrans: Trans impl<'a, THandler: IntoConnectionHandler, TTrans: Transport> DialingAttemptIter<'a, THandler, TTrans> { - fn new( - peer_id: &'a PeerId, - pool: &'a mut Pool, - dialing: &'a mut FnvHashMap>, - ) -> Self { - let end = dialing.get(peer_id).map_or(0, |conns| conns.len()); + fn new(peer_id: &'a PeerId, network: &'a mut Network) -> Self { + let connections = network.dialing_attempts(*peer_id).map(|id| *id).collect(); Self { - pos: 0, - end, - pool, - dialing, + pool: &mut network.pool, peer_id, + connections, } } /// Obtains the next dialing connection, if any. #[allow(clippy::should_implement_trait)] pub fn next(&mut self) -> Option> { - // If the number of elements reduced, the current `DialingAttempt` has been - // aborted and iteration needs to continue from the previous position to - // account for the removed element. - let end = self - .dialing - .get(self.peer_id) - .map_or(0, |conns| conns.len()); - if self.end > end { - self.end = end; - self.pos -= 1; - } - - if self.pos == self.end { - return None; - } + let connection_id = self.connections.pop_front()?; - if let hash_map::Entry::Occupied(attempts) = self.dialing.entry(*self.peer_id) { - let id = attempts.get()[self.pos]; - if let Some(inner) = self.pool.get_outgoing(id) { - let conn = DialingAttempt { - pos: self.pos, - inner, - attempts, - }; - self.pos += 1; - return Some(conn); - } - } + let inner = self.pool.get_outgoing(connection_id)?; - None + Some(DialingAttempt { + peer_id: *self.peer_id, + inner, + }) } /// Returns the first connection, if any, consuming the iterator. - pub fn into_first<'b>(self) -> Option> + pub fn into_first<'b>(mut self) -> Option> where 'a: 'b, { - if self.pos == self.end { - return None; - } + let connection_id = self.connections.pop_front()?; - if let hash_map::Entry::Occupied(attempts) = self.dialing.entry(*self.peer_id) { - let id = attempts.get()[self.pos]; - if let Some(inner) = self.pool.get_outgoing(id) { - return Some(DialingAttempt { - pos: self.pos, - inner, - attempts, - }); - } - } + let inner = self.pool.get_outgoing(connection_id)?; - None + Some(DialingAttempt { + peer_id: *self.peer_id, + inner, + }) } } From 2147b07a7f7e326d294369956259e273494605f2 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Fri, 8 Oct 2021 19:09:04 +0200 Subject: [PATCH 39/67] core/src/network/peer: Remove DialingAttempt::address There is no single address for a dialing attempt no more. --- core/src/network/peer.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/core/src/network/peer.rs b/core/src/network/peer.rs index 77a82b408b5..61ed58d837d 100644 --- a/core/src/network/peer.rs +++ b/core/src/network/peer.rs @@ -484,14 +484,6 @@ impl<'a, THandler: IntoConnectionHandler> DialingAttempt<'a, THandler> { &self.peer_id } - // /// Returns the remote address of the current connection attempt. - // pub fn address(&self) -> &Multiaddr { - // match self.inner.endpoint() { - // ConnectedPoint::Dialer { address } => address, - // ConnectedPoint::Listener { .. } => unreachable!("by definition of a `DialingAttempt`."), - // } - // } - /// Aborts the dialing attempt. /// /// Aborting a dialing attempt involves aborting the current connection From c1491aa6bb13ac112800acc5a19e3f5a7ca03be3 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Fri, 8 Oct 2021 19:21:50 +0200 Subject: [PATCH 40/67] *: Adjust to changes in libp2p-core --- core/tests/network_dial_error.rs | 4 +-- core/tests/util.rs | 2 +- misc/metrics/src/swarm.rs | 35 +++++++++++----------- misc/multistream-select/tests/transport.rs | 2 +- swarm/src/lib.rs | 14 ++++----- 5 files changed, 28 insertions(+), 29 deletions(-) diff --git a/core/tests/network_dial_error.rs b/core/tests/network_dial_error.rs index ea985d444e6..8c1930b7438 100644 --- a/core/tests/network_dial_error.rs +++ b/core/tests/network_dial_error.rs @@ -66,7 +66,7 @@ fn deny_incoming_connec() { match swarm2.poll(cx) { Poll::Ready(NetworkEvent::DialError { peer_id, - error: PendingConnectionError::TransportDial(_), + error: PendingConnectionError::Transport(_), handler: _, }) => { assert_eq!(&peer_id, swarm1.local_peer_id()); @@ -189,7 +189,7 @@ fn multiple_addresses_err() { Poll::Ready(NetworkEvent::DialError { peer_id, // multiaddr, - error: PendingConnectionError::TransportDial(errors), + error: PendingConnectionError::Transport(errors), handler: _, }) => { assert_eq!(peer_id, target); diff --git a/core/tests/util.rs b/core/tests/util.rs index 3cf15aa2e02..0c175448336 100644 --- a/core/tests/util.rs +++ b/core/tests/util.rs @@ -13,7 +13,7 @@ use libp2p_noise as noise; use libp2p_tcp as tcp; use std::{io, pin::Pin, task::Context, task::Poll}; -type TestNetwork = Network; +type TestNetwork = Network; type TestTransport = transport::Boxed<(PeerId, StreamMuxerBox)>; /// Creates a new `TestNetwork` with a TCP transport. diff --git a/misc/metrics/src/swarm.rs b/misc/metrics/src/swarm.rs index fb8cc25641b..ea6f3da20b8 100644 --- a/misc/metrics/src/swarm.rs +++ b/misc/metrics/src/swarm.rs @@ -299,11 +299,11 @@ enum OutgoingConnectionErrorError { #[derive(Encode, Hash, Clone, Eq, PartialEq)] struct IncomingConnectionErrorLabels { - error: PendingConnectionError, + error: PendingInboundConnectionError, } #[derive(Encode, Hash, Clone, Eq, PartialEq)] -enum PendingConnectionError { +enum PendingInboundConnectionError { InvalidPeerId, TransportErrorMultiaddrNotSupported, TransportErrorOther, @@ -312,28 +312,29 @@ enum PendingConnectionError { ConnectionLimit, } -impl From<&libp2p_core::connection::PendingConnectionError> - for PendingConnectionError +impl From<&libp2p_core::connection::PendingInboundConnectionError> + for PendingInboundConnectionError { - fn from(error: &libp2p_core::connection::PendingConnectionError) -> Self { + fn from(error: &libp2p_core::connection::PendingInboundConnectionError) -> Self { match error { - libp2p_core::connection::PendingConnectionError::InvalidPeerId => { - PendingConnectionError::InvalidPeerId + libp2p_core::connection::PendingInboundConnectionError::InvalidPeerId => { + PendingInboundConnectionError::InvalidPeerId } - libp2p_core::connection::PendingConnectionError::ConnectionLimit(_) => { - PendingConnectionError::ConnectionLimit + libp2p_core::connection::PendingInboundConnectionError::ConnectionLimit(_) => { + PendingInboundConnectionError::ConnectionLimit } - libp2p_core::connection::PendingConnectionError::TransportListen( + libp2p_core::connection::PendingInboundConnectionError::Transport( libp2p_core::transport::TransportError::MultiaddrNotSupported(_), - ) => PendingConnectionError::TransportErrorMultiaddrNotSupported, - libp2p_core::connection::PendingConnectionError::TransportListen( + ) => PendingInboundConnectionError::TransportErrorMultiaddrNotSupported, + libp2p_core::connection::PendingInboundConnectionError::Transport( libp2p_core::transport::TransportError::Other(_), - ) => PendingConnectionError::TransportErrorOther, - libp2p_core::connection::PendingConnectionError::TransportDial(_) => unreachable!(), - libp2p_core::connection::PendingConnectionError::Aborted => { - PendingConnectionError::Aborted + ) => PendingInboundConnectionError::TransportErrorOther, + libp2p_core::connection::PendingInboundConnectionError::Aborted => { + PendingInboundConnectionError::Aborted + } + libp2p_core::connection::PendingInboundConnectionError::IO(_) => { + PendingInboundConnectionError::Io } - libp2p_core::connection::PendingConnectionError::IO(_) => PendingConnectionError::Io, } } } diff --git a/misc/multistream-select/tests/transport.rs b/misc/multistream-select/tests/transport.rs index e76926767cd..f42797f13ca 100644 --- a/misc/multistream-select/tests/transport.rs +++ b/misc/multistream-select/tests/transport.rs @@ -37,7 +37,7 @@ use std::{ }; type TestTransport = transport::Boxed<(PeerId, StreamMuxerBox)>; -type TestNetwork = Network; +type TestNetwork = Network; fn mk_transport(up: upgrade::Version) -> (PeerId, TestTransport) { let keys = identity::Keypair::generate_ed25519(); diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 3a2c797570c..0a48036cbaa 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -78,7 +78,7 @@ use libp2p_core::{ connection::{ ConnectedPoint, ConnectionError, ConnectionHandler, ConnectionId, ConnectionLimit, EstablishedConnection, IntoConnectionHandler, ListenerId, PendingConnectionError, - Substream, + PendingInboundConnectionError, PendingOutboundConnectionError, Substream, }, muxing::StreamMuxerBox, network::{ @@ -185,7 +185,7 @@ pub enum SwarmEvent { /// Address used to send back data to the remote. send_back_addr: Multiaddr, /// The error that happened. - error: PendingConnectionError, + error: PendingInboundConnectionError, }, /// Tried to dial an address but it ended up being unreachaable. OutgoingConnectionError { @@ -254,7 +254,6 @@ where network: Network< transport::Boxed<(PeerId, StreamMuxerBox)>, NodeHandlerWrapperBuilder>, - StreamMuxerBox, >, /// Handles which nodes to connect to and how to handle the events sent back by the protocol @@ -942,7 +941,7 @@ fn notify_one<'a, THandlerInEvent>( /// was successfully sent to a handler, in either case the event is consumed. fn notify_any<'a, TTrans, THandler, TBehaviour>( ids: SmallVec<[ConnectionId; 10]>, - peer: &mut ConnectedPeer<'a, TTrans, THandler, StreamMuxerBox>, + peer: &mut ConnectedPeer<'a, TTrans, THandler>, event: THandlerInEvent, cx: &mut Context<'_>, ) -> Option<(THandlerInEvent, SmallVec<[ConnectionId; 10]>)> @@ -1225,15 +1224,14 @@ impl DialError { } } -impl From> for DialError { - fn from(error: PendingConnectionError) -> Self { +impl From> for DialError { + fn from(error: PendingOutboundConnectionError) -> Self { match error { PendingConnectionError::ConnectionLimit(limit) => DialError::ConnectionLimit(limit), PendingConnectionError::Aborted => DialError::Aborted, PendingConnectionError::InvalidPeerId => DialError::InvalidPeerId, PendingConnectionError::IO(e) => DialError::ConnectionIo(e), - PendingConnectionError::TransportDial(e) => DialError::Transport(e), - PendingConnectionError::TransportListen(e) => todo!(), + PendingConnectionError::Transport(e) => DialError::Transport(e), } } } From 77e36e9cdbff037baf35fc473b4903262d7225fc Mon Sep 17 00:00:00 2001 From: Max Inden Date: Fri, 8 Oct 2021 19:59:23 +0200 Subject: [PATCH 41/67] core/src/connection: Log muxer closing error --- core/src/connection/pool.rs | 24 ++++++++++++++---------- core/src/network.rs | 1 + 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index 508b0f424a0..04dbe4f2ef4 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -188,9 +188,9 @@ where PendingInboundConnectionError { /// The ID of the failed connection. id: ConnectionId, - // TODO: Document. + /// Stack of protocols used to send back data to the remote. send_back_addr: Multiaddr, - // TODO: Document. + /// Local connection address. local_addr: Multiaddr, /// The error that occurred. error: PendingInboundConnectionError, @@ -380,8 +380,8 @@ where /// by the pool effective immediately. pub fn disconnect(&mut self, peer: &PeerId) { if let Some(conns) = self.established.get_mut(peer) { - // TODO: Detour via EstablishedConnection not ideal, but at least only one code path to - // start closing a connection. + // Detour via `EstablishedConnection` is not ideal, but at least only one code path in + // order to start closing a connection exists. let connection_ids = conns.iter().map(|(id, _)| *id).collect::>(); for id in connection_ids.into_iter() { @@ -618,10 +618,11 @@ where where TTrans: Transport, TMuxer: StreamMuxer + Send + Sync + 'static, + TMuxer::Error: std::fmt::Debug, + TMuxer::OutboundSubstream: Send, THandler: IntoConnectionHandler + 'static, THandler::Handler: ConnectionHandler> + Send, ::OutboundOpenInfo: Send, - TMuxer::OutboundSubstream: Send, { // Poll for events of established connections. // @@ -806,8 +807,14 @@ where if let Err(error) = error { self.spawn( poll_fn(move |cx| { - // TODO: Should we send the result back to the Pool? - let _ = ready!(muxer.close(cx)); + if let Err(e) = ready!(muxer.close(cx)) { + log::debug!( + "Failed to close connection {:?} to peer {}: {:?}", + id, + peer_id, + e + ); + } Poll::Ready(()) }) .boxed(), @@ -881,9 +888,6 @@ where } } task::PendingConnectionEvent::PendingFailed { id, error } => { - // TODO: How about not removing from pending on abortion and instead remove it - // here. Thus not requiring a loop in poll. - if let Some(PendingConnectionInfo { peer_id, handler, diff --git a/core/src/network.rs b/core/src/network.rs index 18d3b85f549..1c8d970a5db 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -329,6 +329,7 @@ where TTrans::Dial: Send + 'static, TTrans::ListenerUpgrade: Send + 'static, TMuxer: StreamMuxer + Send + Sync + 'static, + TMuxer::Error: std::fmt::Debug, TMuxer::OutboundSubstream: Send, THandler: IntoConnectionHandler + Send + 'static, ::Error: error::Error + Send + 'static, From a80fe6c814cbbd07c01490b6d51c0adce8550fea Mon Sep 17 00:00:00 2001 From: Max Inden Date: Fri, 8 Oct 2021 20:12:07 +0200 Subject: [PATCH 42/67] core/src/connection: Return handler on incoming connection limit --- core/src/connection/pool.rs | 8 ++++--- core/src/network.rs | 2 +- core/tests/util.rs | 1 + misc/multistream-select/tests/transport.rs | 1 + swarm/src/lib.rs | 25 +++++++++++++--------- 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index 04dbe4f2ef4..4b56e21137d 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -573,13 +573,15 @@ where future: TFut, handler: THandler, info: IncomingInfo<'_>, - ) -> Result + ) -> Result where TFut: Future> + Send + 'static, { let endpoint = info.to_connected_point(); - // TODO: We loose the handler here. - self.counters.check_max_pending_incoming()?; + + if let Err(limit) = self.counters.check_max_pending_incoming() { + return Err((limit, handler)); + } let connection_id = self.next_connection_id(); diff --git a/core/src/network.rs b/core/src/network.rs index 1c8d970a5db..c74b77a9f70 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -299,7 +299,7 @@ where send_back_addr, }: IncomingConnection, handler: THandler, - ) -> Result + ) -> Result where TTrans: Transport, TTrans::Output: Send + 'static, diff --git a/core/tests/util.rs b/core/tests/util.rs index 0c175448336..755b9b171a1 100644 --- a/core/tests/util.rs +++ b/core/tests/util.rs @@ -32,6 +32,7 @@ pub fn test_network(cfg: NetworkConfig) -> TestNetwork { TestNetwork::new(transport, local_public_key.into(), cfg) } +#[derive(Debug)] pub struct TestHandler(); impl ConnectionHandler for TestHandler { diff --git a/misc/multistream-select/tests/transport.rs b/misc/multistream-select/tests/transport.rs index f42797f13ca..1c48af37715 100644 --- a/misc/multistream-select/tests/transport.rs +++ b/misc/multistream-select/tests/transport.rs @@ -106,6 +106,7 @@ fn transport_upgrade() { run(upgrade::Version::V1Lazy); } +#[derive(Debug)] struct TestHandler(); impl ConnectionHandler for TestHandler { diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 0a48036cbaa..86bc78b7817 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -618,13 +618,22 @@ where ); let local_addr = connection.local_addr.clone(); let send_back_addr = connection.send_back_addr.clone(); - if let Err(e) = this.network.accept(connection, handler) { - log::warn!("Incoming connection rejected: {:?}", e); + match this.network.accept(connection, handler) { + Ok(_connection_id) => { + return Poll::Ready(SwarmEvent::IncomingConnection { + local_addr, + send_back_addr, + }); + } + Err((connection_limit, handler)) => { + this.behaviour.inject_listen_failure( + &local_addr, + &send_back_addr, + handler.into_protocols_handler(), + ); + log::warn!("Incoming connection rejected: {:?}", connection_limit); + } } - return Poll::Ready(SwarmEvent::IncomingConnection { - local_addr, - send_back_addr, - }); } Poll::Ready(NetworkEvent::NewListenerAddress { listener_id, @@ -707,10 +716,6 @@ where error, handler, }) => { - // TODO: Remove - // this.behaviour - // .inject_addr_reach_failure(Some(&peer_id), &multiaddr, &error); - let error = error.into(); this.behaviour.inject_dial_failure( From 095e08134854ce3e1d6d7d742430d33d7004343b Mon Sep 17 00:00:00 2001 From: Max Inden Date: Fri, 8 Oct 2021 20:39:10 +0200 Subject: [PATCH 43/67] swarm/: Return handler on dial failure to unknown peer --- protocols/identify/src/identify.rs | 13 ++- protocols/kad/src/behaviour.rs | 125 ++++++++++---------------- protocols/relay/src/behaviour.rs | 54 +++++------ protocols/request-response/src/lib.rs | 41 +++++---- swarm-derive/src/lib.rs | 2 +- swarm/src/behaviour.rs | 30 +------ swarm/src/lib.rs | 47 ++++------ swarm/src/test.rs | 6 +- swarm/src/toggle.rs | 2 +- 9 files changed, 135 insertions(+), 185 deletions(-) diff --git a/protocols/identify/src/identify.rs b/protocols/identify/src/identify.rs index 964c474b469..939e8e68911 100644 --- a/protocols/identify/src/identify.rs +++ b/protocols/identify/src/identify.rs @@ -224,9 +224,16 @@ impl NetworkBehaviour for Identify { } } - fn inject_dial_failure(&mut self, peer_id: &PeerId, _: Self::ProtocolsHandler, _: &DialError) { - if !self.connected.contains_key(peer_id) { - self.pending_push.remove(peer_id); + fn inject_dial_failure( + &mut self, + peer_id: Option, + _: Self::ProtocolsHandler, + _: &DialError, + ) { + if let Some(peer_id) = peer_id { + if !self.connected.contains_key(&peer_id) { + self.pending_push.remove(&peer_id); + } } } diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 1fe35492f2c..eb62b54a4c1 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -1697,6 +1697,44 @@ where } } } + + fn address_failed(&mut self, peer_id: PeerId, address: &Multiaddr) { + let key = kbucket::Key::from(peer_id); + + if let Some(addrs) = self.kbuckets.entry(&key).value() { + // TODO: Ideally, the address should only be removed if the error can + // be classified as "permanent" but since `err` is currently a borrowed + // trait object without a `'static` bound, even downcasting for inspection + // of the error is not possible (and also not truly desirable or ergonomic). + // The error passed in should rather be a dedicated enum. + if addrs.remove(address).is_ok() { + debug!( + "Address '{}' removed from peer '{}' due to error.", + address, peer_id + ); + } else { + // Despite apparently having no reachable address (any longer), + // the peer is kept in the routing table with the last address to avoid + // (temporary) loss of network connectivity to "flush" the routing + // table. Once in, a peer is only removed from the routing table + // if it is the least recently connected peer, currently disconnected + // and is unreachable in the context of another peer pending insertion + // into the same bucket. This is handled transparently by the + // `KBucketsTable` and takes effect through `KBucketsTable::take_applied_pending` + // within `Kademlia::poll`. + debug!( + "Last remaining address '{}' of peer '{}' is unreachable.", + address, peer_id, + ) + } + } + + for query in self.queries.iter_mut() { + if let Some(addrs) = query.inner.addresses.get_mut(&peer_id) { + addrs.retain(|a| a != address); + } + } + } } /// Exponentially decrease the given duration (base 2). @@ -1751,44 +1789,7 @@ where errors: Option<&Vec>, ) { for addr in errors.map(|a| a.into_iter()).into_iter().flatten() { - // TODO: Should this be deduplicated with the logic triggered in inject_dial_failure? - let key = kbucket::Key::from(*peer_id); - - if let Some(addrs) = self.kbuckets.entry(&key).value() { - // TODO: Ideally, the address should only be removed if the error can - // be classified as "permanent" but since `err` is currently a borrowed - // trait object without a `'static` bound, even downcasting for inspection - // of the error is not possible (and also not truly desirable or ergonomic). - // The error passed in should rather be a dedicated enum. - if addrs.remove(addr).is_ok() { - debug!( - // "Address '{}' removed from peer '{}' due to error: {}.", - "Address '{}' removed from peer '{}'.", - addr, peer_id - ); - } else { - // Despite apparently having no reachable address (any longer), - // the peer is kept in the routing table with the last address to avoid - // (temporary) loss of network connectivity to "flush" the routing - // table. Once in, a peer is only removed from the routing table - // if it is the least recently connected peer, currently disconnected - // and is unreachable in the context of another peer pending insertion - // into the same bucket. This is handled transparently by the - // `KBucketsTable` and takes effect through `KBucketsTable::take_applied_pending` - // within `Kademlia::poll`. - debug!( - // "Last remaining address '{}' of peer '{}' is unreachable: {}.", - "Last remaining address '{}' of peer '{}' is unreachable.", - addr, peer_id - ) - } - } - - for query in self.queries.iter_mut() { - if let Some(addrs) = query.inner.addresses.get_mut(peer_id) { - addrs.retain(|a| a != addr); - } - } + self.address_failed(*peer_id, addr); } // When a connection is established, we don't know yet whether the @@ -1876,10 +1877,16 @@ where fn inject_dial_failure( &mut self, - peer_id: &PeerId, + peer_id: Option, _: Self::ProtocolsHandler, error: &DialError, ) { + let peer_id = match peer_id { + Some(id) => id, + // Not interested in dial failures to unknown peers. + None => return, + }; + match error { DialError::Banned | DialError::ConnectionLimit(_) @@ -1890,47 +1897,13 @@ where | DialError::Transport(_) | DialError::NoAddresses => { if let DialError::Transport(addresses) = error { - for (addr, err) in addresses { - let key = kbucket::Key::from(*peer_id); - - if let Some(addrs) = self.kbuckets.entry(&key).value() { - // TODO: Ideally, the address should only be removed if the error can - // be classified as "permanent" but since `err` is currently a borrowed - // trait object without a `'static` bound, even downcasting for inspection - // of the error is not possible (and also not truly desirable or ergonomic). - // The error passed in should rather be a dedicated enum. - if addrs.remove(addr).is_ok() { - debug!( - "Address '{}' removed from peer '{}' due to error: {}.", - addr, peer_id, err - ); - } else { - // Despite apparently having no reachable address (any longer), - // the peer is kept in the routing table with the last address to avoid - // (temporary) loss of network connectivity to "flush" the routing - // table. Once in, a peer is only removed from the routing table - // if it is the least recently connected peer, currently disconnected - // and is unreachable in the context of another peer pending insertion - // into the same bucket. This is handled transparently by the - // `KBucketsTable` and takes effect through `KBucketsTable::take_applied_pending` - // within `Kademlia::poll`. - debug!( - "Last remaining address '{}' of peer '{}' is unreachable: {}.", - addr, peer_id, err - ) - } - } - - for query in self.queries.iter_mut() { - if let Some(addrs) = query.inner.addresses.get_mut(peer_id) { - addrs.retain(|a| a != addr); - } - } + for (addr, _) in addresses { + self.address_failed(peer_id, addr) } } for query in self.queries.iter_mut() { - query.on_failure(peer_id); + query.on_failure(&peer_id); } } DialError::DialPeerConditionFalse( diff --git a/protocols/relay/src/behaviour.rs b/protocols/relay/src/behaviour.rs index 99d5471b685..d69de981590 100644 --- a/protocols/relay/src/behaviour.rs +++ b/protocols/relay/src/behaviour.rs @@ -305,7 +305,7 @@ impl NetworkBehaviour for Relay { fn inject_dial_failure( &mut self, - peer_id: &PeerId, + peer_id: Option, _: Self::ProtocolsHandler, error: &DialError, ) { @@ -317,35 +317,37 @@ impl NetworkBehaviour for Relay { return; } - if let Entry::Occupied(o) = self.listeners.entry(*peer_id) { - if matches!(o.get(), RelayListener::Connecting { .. }) { - // By removing the entry, the channel to the listener is dropped and thus the - // listener is notified that dialing the relay failed. - o.remove_entry(); + if let Some(peer_id) = peer_id { + if let Entry::Occupied(o) = self.listeners.entry(peer_id) { + if matches!(o.get(), RelayListener::Connecting { .. }) { + // By removing the entry, the channel to the listener is dropped and thus the + // listener is notified that dialing the relay failed. + o.remove_entry(); + } } - } - if let Some(reqs) = self.outgoing_relay_reqs.dialing.remove(peer_id) { - for req in reqs { - let _ = req.send_back.send(Err(OutgoingRelayReqError::DialingRelay)); + if let Some(reqs) = self.outgoing_relay_reqs.dialing.remove(&peer_id) { + for req in reqs { + let _ = req.send_back.send(Err(OutgoingRelayReqError::DialingRelay)); + } } - } - if let Some(reqs) = self.incoming_relay_reqs.remove(peer_id) { - for req in reqs { - let IncomingRelayReq::DialingDst { - src_peer_id, - incoming_relay_req, - .. - } = req; - self.outbox_to_swarm - .push_back(NetworkBehaviourAction::NotifyHandler { - peer_id: src_peer_id, - handler: NotifyHandler::Any, - event: RelayHandlerIn::DenyIncomingRelayReq( - incoming_relay_req.deny(circuit_relay::Status::HopCantDialDst), - ), - }) + if let Some(reqs) = self.incoming_relay_reqs.remove(&peer_id) { + for req in reqs { + let IncomingRelayReq::DialingDst { + src_peer_id, + incoming_relay_req, + .. + } = req; + self.outbox_to_swarm + .push_back(NetworkBehaviourAction::NotifyHandler { + peer_id: src_peer_id, + handler: NotifyHandler::Any, + event: RelayHandlerIn::DenyIncomingRelayReq( + incoming_relay_req.deny(circuit_relay::Status::HopCantDialDst), + ), + }) + } } } } diff --git a/protocols/request-response/src/lib.rs b/protocols/request-response/src/lib.rs index 4007be14b5a..c04f229b64e 100644 --- a/protocols/request-response/src/lib.rs +++ b/protocols/request-response/src/lib.rs @@ -667,23 +667,30 @@ where self.connected.remove(peer); } - fn inject_dial_failure(&mut self, peer: &PeerId, _: Self::ProtocolsHandler, _: &DialError) { - // If there are pending outgoing requests when a dial failure occurs, - // it is implied that we are not connected to the peer, since pending - // outgoing requests are drained when a connection is established and - // only created when a peer is not connected when a request is made. - // Thus these requests must be considered failed, even if there is - // another, concurrent dialing attempt ongoing. - if let Some(pending) = self.pending_outbound_requests.remove(peer) { - for request in pending { - self.pending_events - .push_back(NetworkBehaviourAction::GenerateEvent( - RequestResponseEvent::OutboundFailure { - peer: *peer, - request_id: request.request_id, - error: OutboundFailure::DialFailure, - }, - )); + fn inject_dial_failure( + &mut self, + peer: Option, + _: Self::ProtocolsHandler, + _: &DialError, + ) { + if let Some(peer) = peer { + // If there are pending outgoing requests when a dial failure occurs, + // it is implied that we are not connected to the peer, since pending + // outgoing requests are drained when a connection is established and + // only created when a peer is not connected when a request is made. + // Thus these requests must be considered failed, even if there is + // another, concurrent dialing attempt ongoing. + if let Some(pending) = self.pending_outbound_requests.remove(&peer) { + for request in pending { + self.pending_events + .push_back(NetworkBehaviourAction::GenerateEvent( + RequestResponseEvent::OutboundFailure { + peer: peer, + request_id: request.request_id, + error: OutboundFailure::DialFailure, + }, + )); + } } } } diff --git a/swarm-derive/src/lib.rs b/swarm-derive/src/lib.rs index b9e4ea05a30..512e0923670 100644 --- a/swarm-derive/src/lib.rs +++ b/swarm-derive/src/lib.rs @@ -672,7 +672,7 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> TokenStream { #(#inject_connection_closed_stmts);* } - fn inject_dial_failure(&mut self, peer_id: &#peer_id, handlers: Self::ProtocolsHandler, error: &#dial_error) { + fn inject_dial_failure(&mut self, peer_id: Option<#peer_id>, handlers: Self::ProtocolsHandler, error: &#dial_error) { #(#inject_dial_failure_stmts);* } diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index 8446107cbd6..a6a22f43a7a 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -159,28 +159,10 @@ pub trait NetworkBehaviour: Send + 'static { event: <::Handler as ProtocolsHandler>::OutEvent, ); - // TODO: Remove - // - // /// Indicates to the behaviour that we tried to reach an address, but failed. - // /// - // /// If we were trying to reach a specific node, its ID is passed as parameter. If this is the - // /// last address to attempt for the given node, then `inject_dial_failure` is called afterwards. - // fn inject_addr_reach_failure( - // &mut self, - // _peer_id: Option<&PeerId>, - // _addr: &Multiaddr, - // _error: &dyn error::Error, - // ) { - // } - - /// Indicates to the behaviour that we tried to dial all the addresses known for a node, but - /// failed. - /// - /// The `peer_id` is guaranteed to be in a disconnected state. In other words, - /// `inject_connected` has not been called, or `inject_disconnected` has been called since then. + /// Indicates to the behaviour that the dial to a known or unknown node failed. fn inject_dial_failure( &mut self, - _peer_id: &PeerId, + _peer_id: Option, _handler: Self::ProtocolsHandler, _error: &DialError, ) { @@ -719,18 +701,10 @@ pub enum DialPeerCondition { /// A new dialing attempt is initiated _only if_ the peer is currently /// considered disconnected, i.e. there is no established connection /// and no ongoing dialing attempt. - /// - /// If there is an ongoing dialing attempt, the addresses reported by - /// [`NetworkBehaviour::addresses_of_peer`] are added to the ongoing - /// dialing attempt, ignoring duplicates. Disconnected, /// A new dialing attempt is initiated _only if_ there is currently /// no ongoing dialing attempt, i.e. the peer is either considered /// disconnected or connected but without an ongoing dialing attempt. - /// - /// If there is an ongoing dialing attempt, the addresses reported by - /// [`NetworkBehaviour::addresses_of_peer`] are added to the ongoing - /// dialing attempt, ignoring duplicates. NotDialing, /// A new dialing attempt is always initiated, only subject to the /// configured connection limits. diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 86bc78b7817..047a31967a7 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -353,7 +353,8 @@ where ) -> Result<(), DialError> { if self.banned_peers.contains(peer_id) { let error = DialError::Banned; - self.behaviour.inject_dial_failure(peer_id, handler, &error); + self.behaviour + .inject_dial_failure(Some(*peer_id), handler, &error); return Err(error); } @@ -367,7 +368,8 @@ where if addrs.peek().is_none() { let error = DialError::NoAddresses; - self.behaviour.inject_dial_failure(peer_id, handler, &error); + self.behaviour + .inject_dial_failure(Some(*peer_id), handler, &error); return Err(error); }; @@ -379,7 +381,7 @@ where Err(error) => { let (error, handler) = DialError::from_network_dial_error(error); self.behaviour.inject_dial_failure( - peer_id, + Some(*peer_id), handler.into_protocols_handler(), &error, ); @@ -719,7 +721,7 @@ where let error = error.into(); this.behaviour.inject_dial_failure( - &peer_id, + Some(peer_id), handler.into_protocols_handler(), &error, ); @@ -738,9 +740,11 @@ where Poll::Ready(NetworkEvent::UnknownPeerDialError { error, handler }) => { log::debug!("Connection attempt to unknown peer failed with {:?}", error); let error = error.into(); - // TODO: Make sure to give the handler back to the behaviour. - // this.behaviour - // .inject_dial_failure(None, &multiaddr, &error); + this.behaviour.inject_dial_failure( + None, + handler.into_protocols_handler(), + &error, + ); return Poll::Ready(SwarmEvent::OutgoingConnectionError { peer_id: None, error: error, @@ -813,34 +817,17 @@ where return Poll::Ready(SwarmEvent::Dialing(peer_id)); } } else { - // TODO: Make sure this behaviour is removed from all other doc comments as well. - // - // // Even if the condition for a _new_ dialing attempt is not met, - // // we always add any potentially new addresses of the peer to an - // // ongoing dialing attempt, if there is one. - // log::trace!( - // "Condition for new dialing attempt to {:?} not met: {:?}", - // peer_id, - // condition - // ); - // let self_listening = &this.listened_addrs; - // if let Some(mut peer) = this.network.peer(peer_id).into_dialing() { - // let addrs = this.behaviour.addresses_of_peer(peer.id()); - // let mut attempt = peer.some_attempt(); - // for a in addrs { - // if !self_listening.contains(&a) { - // attempt.add_address(a); - // } - // } - // } + log::trace!( + "Condition for new dialing attempt to {:?} not met: {:?}", + peer_id, + condition + ); this.behaviour.inject_dial_failure( - &peer_id, + Some(peer_id), handler, &DialError::DialPeerConditionFalse(condition), ); - - // TODO: Should we not emit a swarm event? } } Poll::Ready(NetworkBehaviourAction::NotifyHandler { diff --git a/swarm/src/test.rs b/swarm/src/test.rs index c34f64494c1..54a806ca6d2 100644 --- a/swarm/src/test.rs +++ b/swarm/src/test.rs @@ -108,7 +108,7 @@ where ConnectionId, <::Handler as ProtocolsHandler>::OutEvent, )>, - pub inject_dial_failure: Vec, + pub inject_dial_failure: Vec>, pub inject_new_listener: Vec, pub inject_new_listen_addr: Vec<(ListenerId, Multiaddr)>, pub inject_new_external_addr: Vec, @@ -229,11 +229,11 @@ where fn inject_dial_failure( &mut self, - p: &PeerId, + p: Option, handler: Self::ProtocolsHandler, error: &DialError, ) { - self.inject_dial_failure.push(p.clone()); + self.inject_dial_failure.push(p); self.inner.inject_dial_failure(p, handler, error); } diff --git a/swarm/src/toggle.rs b/swarm/src/toggle.rs index 362d1be4231..b6af0e38237 100644 --- a/swarm/src/toggle.rs +++ b/swarm/src/toggle.rs @@ -149,7 +149,7 @@ where fn inject_dial_failure( &mut self, - peer_id: &PeerId, + peer_id: Option, handler: Self::ProtocolsHandler, error: &DialError, ) { From 2c245424b45e39da86982c5f3c7f5d7868f6d5db Mon Sep 17 00:00:00 2001 From: Max Inden Date: Fri, 8 Oct 2021 21:14:35 +0200 Subject: [PATCH 44/67] *: Address minor TODOs --- core/src/connection/pool/concurrent_dial.rs | 30 +++++++----------- core/src/connection/pool/task.rs | 5 +-- core/src/connection/substream.rs | 3 +- core/src/network.rs | 29 +++++------------ core/src/network/event.rs | 4 --- core/src/network/peer.rs | 6 ++-- core/tests/network_dial_error.rs | 21 ++++++------- swarm/src/behaviour.rs | 11 +++---- swarm/src/lib.rs | 35 ++++++++++++--------- 9 files changed, 60 insertions(+), 84 deletions(-) diff --git a/core/src/connection/pool/concurrent_dial.rs b/core/src/connection/pool/concurrent_dial.rs index c7ce1ae2e52..0b3e3d3d6e8 100644 --- a/core/src/connection/pool/concurrent_dial.rs +++ b/core/src/connection/pool/concurrent_dial.rs @@ -62,23 +62,18 @@ where peer: Option, addresses: impl Iterator + Send + 'static, ) -> Self { - let mut pending_dials = addresses.map(move |address| { - match p2p_addr(peer, address) { - Ok(address) => match transport.clone().dial(address.clone()) { - Ok(fut) => fut - .map(|r| (address, r.map_err(|e| TransportError::Other(e)))) - .boxed(), - Err(err) => futures::future::ready((address.clone(), Err(err))).boxed(), - }, - Err(address) => { - futures::future::ready(( - address.clone(), - // TODO: It is not really the Multiaddr that is not supported. - Err(TransportError::MultiaddrNotSupported(address)), - )) - .boxed() - } - } + let mut pending_dials = addresses.map(move |address| match p2p_addr(peer, address) { + Ok(address) => match transport.clone().dial(address.clone()) { + Ok(fut) => fut + .map(|r| (address, r.map_err(|e| TransportError::Other(e)))) + .boxed(), + Err(err) => futures::future::ready((address.clone(), Err(err))).boxed(), + }, + Err(address) => futures::future::ready(( + address.clone(), + Err(TransportError::MultiaddrNotSupported(address)), + )) + .boxed(), }); let dials = FuturesUnordered::new(); @@ -145,7 +140,6 @@ where /// If the given address is not yet a `p2p` address for the given peer, /// the `/p2p/` protocol is appended to the returned address. fn p2p_addr(peer: Option, addr: Multiaddr) -> Result { - // TODO: Make hack nicer. let peer = match peer { Some(p) => p, None => return Ok(addr), diff --git a/core/src/connection/pool/task.rs b/core/src/connection/pool/task.rs index 0cbef328bd5..d2f6a23bb42 100644 --- a/core/src/connection/pool/task.rs +++ b/core/src/connection/pool/task.rs @@ -55,11 +55,12 @@ pub enum PendingConnectionEvent where TTrans: Transport, { - // TODO: Remove most of these ConnectionEstablished { id: ConnectionId, output: TTrans::Output, - // TODO: Document errors in a success message. + /// [`Some`] when the new connection is an outgoing connection. + /// Addresses are dialed in parallel. Contains the addresses and errors + /// of dial attempts that failed before the one successful dial. outgoing: Option<(Multiaddr, Vec<(Multiaddr, TransportError)>)>, }, /// A pending connection failed. diff --git a/core/src/connection/substream.rs b/core/src/connection/substream.rs index 1244d56230a..399b09b9f0a 100644 --- a/core/src/connection/substream.rs +++ b/core/src/connection/substream.rs @@ -70,8 +70,7 @@ where /// Future that signals the remote that we have closed the connection. pub struct Close { /// Muxer to close. - // TODO: pub needed? - pub muxer: Arc, + muxer: Arc, } /// A successfully opened substream. diff --git a/core/src/network.rs b/core/src/network.rs index c74b77a9f70..a3defeb1c87 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -210,7 +210,7 @@ where if let Ok(peer) = PeerId::try_from(ma) { return self.dial_peer(DialingOpts { peer, - addresses: vec![address.clone()], + addresses: std::iter::once(address.clone()), handler, }); } @@ -225,11 +225,12 @@ where } /// Initiates a connection attempt to a known peer. - fn dial_peer( + fn dial_peer( &mut self, - opts: DialingOpts, + opts: DialingOpts, ) -> Result> where + I: Iterator + Send + 'static, TTrans: Transport + Send, TTrans::Output: Send + 'static, TTrans::Dial: Send + 'static, @@ -237,7 +238,7 @@ where { let id = self.pool.add_outgoing( self.transport().clone(), - opts.addresses.into_iter(), + opts.addresses, Some(opts.peer), opts.handler, )?; @@ -463,10 +464,10 @@ where /// Options for a dialing attempt (i.e. repeated connection attempt /// via a list of address) to a peer. -struct DialingOpts { +struct DialingOpts> { peer: PeerId, handler: THandler, - addresses: Vec, + addresses: I, } /// Information about the network obtained by [`Network::info()`]. @@ -552,8 +553,6 @@ impl NetworkConfig { } /// Possible (synchronous) errors when dialing a peer. -// -// TODO: How about ImidiateDialError? #[derive(Clone)] pub enum DialError { /// The dialing attempt is rejected because of a connection limit. @@ -561,13 +560,6 @@ pub enum DialError { limit: ConnectionLimit, handler: THandler, }, - // /// The address being dialed is invalid, e.g. if it refers to a different - // /// remote peer than the one being dialed. - // // TODO: Still needed? - // InvalidAddress { - // address: Multiaddr, - // handler: THandler, - // }, /// The dialing attempt is rejected because the peer being dialed is the local peer. LocalPeerId { handler: THandler }, } @@ -579,13 +571,6 @@ impl fmt::Debug for DialError { .debug_struct("DialError::ConnectionLimit") .field("limit", limit) .finish(), - // DialError::InvalidAddress { - // address, - // handler: _, - // } => f - // .debug_struct("DialError::InvalidAddress") - // .field("address", address) - // .finish(), DialError::LocalPeerId { handler: _ } => { f.debug_struct("DialError::LocalPeerId").finish() } diff --git a/core/src/network/event.rs b/core/src/network/event.rs index fa4a4e80a80..c4f4c41579d 100644 --- a/core/src/network/event.rs +++ b/core/src/network/event.rs @@ -145,12 +145,8 @@ where error: PendingOutboundConnectionError, }, - // TODO: How about merging this into [`DialError`]? /// Failed to reach a peer that we were trying to dial. UnknownPeerDialError { - /// The multiaddr we failed to reach. - // multiaddr: Multiaddr, - /// The error that happened. error: PendingOutboundConnectionError, diff --git a/core/src/network/peer.rs b/core/src/network/peer.rs index 61ed58d837d..813467cb8ea 100644 --- a/core/src/network/peer.rs +++ b/core/src/network/peer.rs @@ -163,6 +163,7 @@ where ) -> Result<(ConnectionId, DialingPeer<'a, TTrans, THandler>), DialError> where I: IntoIterator, + I::IntoIter: Send + 'static, { let (peer_id, network) = match self { Peer::Connected(p) => (p.peer_id, p.network), @@ -174,8 +175,7 @@ where let id = network.dial_peer(DialingOpts { peer: peer_id, handler, - // TODO: Should network.dial_peer take an iterator as well? - addresses: addresses.into_iter().collect(), + addresses: addresses.into_iter(), })?; Ok((id, DialingPeer { network, peer_id })) @@ -380,8 +380,6 @@ where /// Obtains a dialing attempt to the peer by connection ID of /// the current connection attempt. - // - // TODO: Still needed? pub fn attempt(&mut self, id: ConnectionId) -> Option> { Some(DialingAttempt { peer_id: self.peer_id, diff --git a/core/tests/network_dial_error.rs b/core/tests/network_dial_error.rs index 8c1930b7438..3e29aaaabaa 100644 --- a/core/tests/network_dial_error.rs +++ b/core/tests/network_dial_error.rs @@ -66,15 +66,14 @@ fn deny_incoming_connec() { match swarm2.poll(cx) { Poll::Ready(NetworkEvent::DialError { peer_id, - error: PendingConnectionError::Transport(_), + error: PendingConnectionError::Transport(errors), handler: _, }) => { assert_eq!(&peer_id, swarm1.local_peer_id()); - // TODO: Bring back. - // assert_eq!( - // multiaddr, - // address.clone().with(Protocol::P2p(peer_id.into())) - // ); + assert_eq!( + errors.get(0).expect("One error.").0, + address.clone().with(Protocol::P2p(peer_id.into())) + ); return Poll::Ready(Ok(())); } Poll::Ready(_) => unreachable!(), @@ -118,21 +117,21 @@ fn dial_self() { async_std::task::block_on(future::poll_fn(|cx| -> Poll> { loop { match swarm.poll(cx) { - Poll::Ready(NetworkEvent::UnknownPeerDialError { - // multiaddr, + Poll::Ready(NetworkEvent::DialError { + peer_id, error: PendingConnectionError::InvalidPeerId { .. }, .. }) => { + assert_eq!(&peer_id, swarm.local_peer_id()); assert!(!got_dial_err); - // assert_eq!(multiaddr, local_address); got_dial_err = true; if got_inc_err { return Poll::Ready(Ok(())); } } - Poll::Ready(NetworkEvent::IncomingConnectionError { /*local_addr,*/ .. }) => { + Poll::Ready(NetworkEvent::IncomingConnectionError { local_addr, .. }) => { assert!(!got_inc_err); - // assert_eq!(local_addr, local_address); + assert_eq!(local_addr, local_address); got_inc_err = true; if got_dial_err { return Poll::Ready(Ok(())); diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index a6a22f43a7a..f095370d80a 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -113,13 +113,12 @@ pub trait NetworkBehaviour: Send + 'static { fn inject_disconnected(&mut self, _: &PeerId) {} /// Informs the behaviour about a newly established connection to a peer. - // TODO: Should we pass more than just the Multiaddr? The behaviour can't do anything with it anyways. fn inject_connection_established( &mut self, - _: &PeerId, - _: &ConnectionId, - _: &ConnectedPoint, - _: Option<&Vec>, + _peer_id: &PeerId, + _connection_id: &ConnectionId, + _endpoint: &ConnectedPoint, + _failed_addresses: Option<&Vec>, ) { } @@ -178,8 +177,6 @@ pub trait NetworkBehaviour: Send + 'static { _local_addr: &Multiaddr, _send_back_addr: &Multiaddr, _handler: Self::ProtocolsHandler, - // TODO: Should we forward the PendingConnectionError to the inject_listen_failure call as - // well? ) { } diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 047a31967a7..8f22f3b6264 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -358,12 +358,12 @@ where return Err(error); } - let self_listening = &self.listened_addrs; + let self_listening = self.listened_addrs.clone(); let mut addrs = self .behaviour .addresses_of_peer(peer_id) .into_iter() - .filter(|a| !self_listening.contains(a)) + .filter(move |a| !self_listening.contains(a)) .peekable(); if addrs.peek().is_none() { @@ -1179,7 +1179,6 @@ where } /// The possible failures of dialing. -// TODO: Should we have separate errors for synchronous and asynchronous dial errors? #[derive(Debug)] pub enum DialError { /// The peer is currently banned. @@ -1194,11 +1193,14 @@ pub enum DialError { NoAddresses, /// The provided [`DialPeerCondition`] evaluated to false and thus the dial was aborted. DialPeerConditionFalse(DialPeerCondition), - - // TODO: Document + /// Pending connection attempt has been aborted. Aborted, + /// The peer identity obtained on the connection did not + /// match the one that was expected or is otherwise invalid. InvalidPeerId, + /// An I/O error occurred on the connection. ConnectionIo(io::Error), + /// An error occurred while negotiating the transport protocol(s) on a connection. Transport(Vec<(Multiaddr, TransportError)>), } @@ -1208,9 +1210,6 @@ impl DialError { network::DialError::ConnectionLimit { limit, handler } => { (DialError::ConnectionLimit(limit), handler) } - // network::DialError::InvalidAddress { address, handler } => { - // (DialError::InvalidAddress(address), handler) - // } network::DialError::LocalPeerId { handler } => (DialError::LocalPeerId, handler), } } @@ -1234,8 +1233,6 @@ impl fmt::Display for DialError { DialError::ConnectionLimit(err) => write!(f, "Dial error: {}", err), DialError::NoAddresses => write!(f, "Dial error: no addresses for peer."), DialError::LocalPeerId => write!(f, "Dial error: tried to dial local peer id."), - // DialError::InvalidAddress(a) => write!(f, "Dial error: invalid address: {}", a), - // DialError::UnreachableAddr(a) => write!(f, "Dial error: unreachable address: {}", a), DialError::Banned => write!(f, "Dial error: peer is banned."), DialError::DialPeerConditionFalse(c) => { write!( @@ -1244,7 +1241,16 @@ impl fmt::Display for DialError { c ) } - _ => todo!(), + DialError::Aborted => write!( + f, + "Dial error: Pending connection attempt has been aborted." + ), + DialError::InvalidPeerId => write!(f, "Dial error: Invalid peer ID."), + DialError::ConnectionIo(e) => write!( + f, + "Dial error: An I/O error occurred on the connection: {:?}.", e + ), + DialError::Transport(e) => write!(f, "An error occurred while negotiating the transport protocol(s) on a connection: {:?}.", e), } } } @@ -1253,13 +1259,14 @@ impl error::Error for DialError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { DialError::ConnectionLimit(err) => Some(err), - // DialError::InvalidAddress(_) => None, - // TODO: Can we do better? DialError::LocalPeerId => None, DialError::NoAddresses => None, DialError::Banned => None, DialError::DialPeerConditionFalse(_) => None, - _ => todo!(), + DialError::Aborted => None, + DialError::InvalidPeerId => None, + DialError::ConnectionIo(_) => None, + DialError::Transport(_) => None, } } } From 07ed8880429bd41950f880a17b8f3fc20e889204 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Sun, 10 Oct 2021 14:01:21 +0200 Subject: [PATCH 45/67] swarm/src/behaviour: Fix doc example --- swarm/src/behaviour.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index f095370d80a..a8217eda066 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -390,7 +390,7 @@ pub enum NetworkBehaviourAction< /// # /// fn inject_dial_failure( /// &mut self, - /// _: &PeerId, + /// _: Option, /// handler: Self::ProtocolsHandler, /// _: &DialError, /// ) { From dd49cfc5eec2ea2e70c5a0fe9dc3bb80e2ee5374 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Sun, 10 Oct 2021 14:16:25 +0200 Subject: [PATCH 46/67] core/src/connection: Address clippy suggestions --- core/src/connection/pool.rs | 4 +++- core/src/connection/pool/concurrent_dial.rs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index 4b56e21137d..00b1f22a4df 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -382,6 +382,7 @@ where if let Some(conns) = self.established.get_mut(peer) { // Detour via `EstablishedConnection` is not ideal, but at least only one code path in // order to start closing a connection exists. + #[allow(clippy::needless_collect)] let connection_ids = conns.iter().map(|(id, _)| *id).collect::>(); for id in connection_ids.into_iter() { @@ -396,6 +397,7 @@ where } } + #[allow(clippy::needless_collect)] let pending_connections = self .pending .iter() @@ -403,7 +405,7 @@ where .map(|(id, _)| *id) .collect::>(); - for pending_connection in pending_connections.into_iter() { + for pending_connection in pending_connections { let pending_connection = match self.pending.entry(pending_connection) { hash_map::Entry::Occupied(entry) => PendingConnection { entry, diff --git a/core/src/connection/pool/concurrent_dial.rs b/core/src/connection/pool/concurrent_dial.rs index 0b3e3d3d6e8..a3780d8a5b1 100644 --- a/core/src/connection/pool/concurrent_dial.rs +++ b/core/src/connection/pool/concurrent_dial.rs @@ -67,7 +67,7 @@ where Ok(fut) => fut .map(|r| (address, r.map_err(|e| TransportError::Other(e)))) .boxed(), - Err(err) => futures::future::ready((address.clone(), Err(err))).boxed(), + Err(err) => futures::future::ready((address, Err(err))).boxed(), }, Err(address) => futures::future::ready(( address.clone(), From a725fbf082641d0d95d45423422e7dd98db3dc83 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Sun, 10 Oct 2021 14:21:28 +0200 Subject: [PATCH 47/67] swarm/src/lib: Fix doc comment --- swarm/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 8f22f3b6264..42b9206c7f0 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -236,10 +236,10 @@ pub enum SwarmEvent { }, /// A new dialing attempt has been initiated. /// - /// A [`ConnectionEstablished`](SwarmEvent::ConnectionEstablished) - /// event is reported if the dialing attempt succeeds, otherwise a - /// [`UnreachableAddr`](SwarmEvent::UnreachableAddr) event is reported - /// with `attempts_remaining` equal to 0. + /// A [`ConnectionEstablished`](SwarmEvent::ConnectionEstablished) event is + /// reported if the dialing attempt succeeds, otherwise a + /// [`OutgoingConnectionError`](SwarmEvent::OutgoingConnectionError) event + /// is reported with `attempts_remaining` equal to 0. Dialing(PeerId), } From 66cb2be8bd941033d76031cb5d53cff90cdaa734 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Sun, 10 Oct 2021 14:40:07 +0200 Subject: [PATCH 48/67] *: Minor rewording on doc comments --- core/src/connection/pool.rs | 12 +++++------- swarm/src/lib.rs | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index 00b1f22a4df..81735956cf1 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -177,14 +177,13 @@ where id: ConnectionId, /// The error that occurred. error: PendingOutboundConnectionError, - /// The handler that was supposed to handle the connection, - /// if the connection failed before the handler was consumed. + /// The handler that was supposed to handle the connection. handler: THandler, /// The (expected) peer of the failed connection. peer: Option, }, - /// An outbound connection attempt failed. + /// An inbound connection attempt failed. PendingInboundConnectionError { /// The ID of the failed connection. id: ConnectionId, @@ -194,8 +193,7 @@ where local_addr: Multiaddr, /// The error that occurred. error: PendingInboundConnectionError, - /// The handler that was supposed to handle the connection, - /// if the connection failed before the handler was consumed. + /// The handler that was supposed to handle the connection. handler: THandler, }, @@ -230,7 +228,7 @@ where outgoing, .. } => f - .debug_tuple("PoolEvent::OutgoingConnectionEstablished") + .debug_tuple("PoolEvent::ConnectionEstablished") .field(connection) .field(outgoing) .finish(), @@ -938,7 +936,7 @@ where } // Advance the tasks in `local_spawns`. - while let Poll::Ready(Some(_)) = self.local_spawns.poll_next_unpin(cx) {} + while let Poll::Ready(Some(())) = self.local_spawns.poll_next_unpin(cx) {} Poll::Pending } diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 42b9206c7f0..4340e4d5e76 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -187,7 +187,7 @@ pub enum SwarmEvent { /// The error that happened. error: PendingInboundConnectionError, }, - /// Tried to dial an address but it ended up being unreachaable. + /// Outgoing connection attempt failed. OutgoingConnectionError { /// If known, [`PeerId`] of the peer we tried to reach. peer_id: Option, From 4dba4c887cee1a8d4c9c3fcd37e78b914abfb507 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Sun, 10 Oct 2021 15:42:36 +0200 Subject: [PATCH 49/67] core/tests: Use MemoryTransport --- core/tests/connection_limits.rs | 4 ++-- core/tests/network_dial_error.rs | 8 ++------ core/tests/util.rs | 6 +++--- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/core/tests/connection_limits.rs b/core/tests/connection_limits.rs index 8c732624b8b..5070e90ad99 100644 --- a/core/tests/connection_limits.rs +++ b/core/tests/connection_limits.rs @@ -39,7 +39,7 @@ fn max_outgoing() { let cfg = NetworkConfig::default().with_connection_limits(limits); let mut network = test_network(cfg); - let addr: Multiaddr = "/ip6/::1/tcp/1234".parse().unwrap(); + let addr: Multiaddr = "/memory/1234".parse().unwrap(); let target = PeerId::random(); for _ in 0..outgoing_limit { @@ -98,7 +98,7 @@ fn max_established_incoming() { let mut network1 = test_network(config(limit)); let mut network2 = test_network(config(limit)); - let listen_addr = multiaddr![Ip4(std::net::Ipv4Addr::new(127, 0, 0, 1)), Tcp(0u16)]; + let listen_addr = multiaddr![Memory(0u64)]; let _ = network1.listen_on(listen_addr.clone()).unwrap(); let (addr_sender, addr_receiver) = futures::channel::oneshot::channel(); let mut addr_sender = Some(addr_sender); diff --git a/core/tests/network_dial_error.rs b/core/tests/network_dial_error.rs index 3e29aaaabaa..827db92e444 100644 --- a/core/tests/network_dial_error.rs +++ b/core/tests/network_dial_error.rs @@ -39,9 +39,7 @@ fn deny_incoming_connec() { let mut swarm1 = test_network(NetworkConfig::default()); let mut swarm2 = test_network(NetworkConfig::default()); - swarm1 - .listen_on("/ip4/127.0.0.1/tcp/0".parse().unwrap()) - .unwrap(); + swarm1.listen_on("/memory/0".parse().unwrap()).unwrap(); let address = async_std::task::block_on(future::poll_fn(|cx| match swarm1.poll(cx) { Poll::Ready(NetworkEvent::NewListenerAddress { listen_addr, .. }) => { @@ -98,9 +96,7 @@ fn dial_self() { // The last two can happen in any order. let mut swarm = test_network(NetworkConfig::default()); - swarm - .listen_on("/ip4/127.0.0.1/tcp/0".parse().unwrap()) - .unwrap(); + swarm.listen_on("/memory/0".parse().unwrap()).unwrap(); let local_address = async_std::task::block_on(future::poll_fn(|cx| match swarm.poll(cx) { Poll::Ready(NetworkEvent::NewListenerAddress { listen_addr, .. }) => { diff --git a/core/tests/util.rs b/core/tests/util.rs index 755b9b171a1..9592daca9eb 100644 --- a/core/tests/util.rs +++ b/core/tests/util.rs @@ -6,11 +6,11 @@ use libp2p_core::{ identity, muxing::{StreamMuxer, StreamMuxerBox}, network::{Network, NetworkConfig}, - transport, upgrade, Multiaddr, PeerId, Transport, + transport::{self, memory::MemoryTransport}, + upgrade, Multiaddr, PeerId, Transport, }; use libp2p_mplex as mplex; use libp2p_noise as noise; -use libp2p_tcp as tcp; use std::{io, pin::Pin, task::Context, task::Poll}; type TestNetwork = Network; @@ -23,7 +23,7 @@ pub fn test_network(cfg: NetworkConfig) -> TestNetwork { let noise_keys = noise::Keypair::::new() .into_authentic(&local_key) .unwrap(); - let transport: TestTransport = tcp::TcpConfig::new() + let transport: TestTransport = MemoryTransport::default() .upgrade(upgrade::Version::V1) .authenticate(noise::NoiseConfig::xx(noise_keys).into_authenticated()) .multiplex(mplex::MplexConfig::new()) From dc3c46db06ac55474c9b9eeb3e540fb37b76391c Mon Sep 17 00:00:00 2001 From: Max Inden Date: Sun, 10 Oct 2021 16:06:57 +0200 Subject: [PATCH 50/67] protocols/rendezvous: Implement assert_behaviour_events for single swarm --- protocols/rendezvous/tests/harness/mod.rs | 36 +++++++++++++++---- protocols/rendezvous/tests/rendezvous.rs | 43 ++++++++++------------- 2 files changed, 47 insertions(+), 32 deletions(-) diff --git a/protocols/rendezvous/tests/harness/mod.rs b/protocols/rendezvous/tests/harness/mod.rs index 1dce86210bf..7b37fb740ca 100644 --- a/protocols/rendezvous/tests/harness/mod.rs +++ b/protocols/rendezvous/tests/harness/mod.rs @@ -76,13 +76,29 @@ fn get_rand_memory_address() -> Multiaddr { addr } -pub async fn await_events_or_timeout( - swarm_1: &mut (impl Stream> + FusedStream + Unpin), - swarm_2: &mut (impl Stream> + FusedStream + Unpin), -) -> (SwarmEvent, SwarmEvent) +pub async fn await_event_or_timeout( + swarm: &mut (impl Stream> + FusedStream + Unpin), +) -> SwarmEvent where - SwarmEvent: Debug, - SwarmEvent: Debug, + SwarmEvent: Debug, +{ + tokio::time::timeout( + Duration::from_secs(30), + swarm + .inspect(|event| log::debug!("Swarm emitted {:?}", event)) + .select_next_some(), + ) + .await + .expect("network behaviour to emit an event within 30 seconds") +} + +pub async fn await_events_or_timeout( + swarm_1: &mut (impl Stream> + FusedStream + Unpin), + swarm_2: &mut (impl Stream> + FusedStream + Unpin), +) -> (SwarmEvent, SwarmEvent) +where + SwarmEvent: Debug, + SwarmEvent: Debug, { tokio::time::timeout( Duration::from_secs(30), @@ -96,11 +112,17 @@ where ), ) .await - .expect("network behaviours to emit an event within 10 seconds") + .expect("network behaviours to emit an event within 30 seconds") } #[macro_export] macro_rules! assert_behaviour_events { + ($swarm: ident: $pat: pat, || $body: block) => { + match await_event_or_timeout(&mut $swarm).await { + libp2p::swarm::SwarmEvent::Behaviour($pat) => $body, + _ => panic!("Unexpected combination of events emitted, check logs for details"), + } + }; ($swarm1: ident: $pat1: pat, $swarm2: ident: $pat2: pat, || $body: block) => { match await_events_or_timeout(&mut $swarm1, &mut $swarm2).await { ( diff --git a/protocols/rendezvous/tests/rendezvous.rs b/protocols/rendezvous/tests/rendezvous.rs index c258dd01d92..cdec79a4ce4 100644 --- a/protocols/rendezvous/tests/rendezvous.rs +++ b/protocols/rendezvous/tests/rendezvous.rs @@ -21,7 +21,7 @@ #[macro_use] pub mod harness; -use crate::harness::{await_events_or_timeout, new_swarm, SwarmExt}; +use crate::harness::{await_event_or_timeout, await_events_or_timeout, new_swarm, SwarmExt}; use futures::stream::FuturesUnordered; use futures::StreamExt; use libp2p_core::identity; @@ -170,22 +170,19 @@ async fn discover_allows_for_dial_by_peer_id() { alice .behaviour_mut() .register(namespace.clone(), roberts_peer_id, None); + assert_behaviour_events! { + alice: rendezvous::client::Event::Registered { .. }, + || { } + }; + bob.behaviour_mut() .discover(Some(namespace.clone()), None, None, roberts_peer_id); - - // TODO: Clean up - match alice.select_next_some().await { - SwarmEvent::Behaviour(rendezvous::client::Event::Registered { .. }) => {} - _ => todo!(), - } - - // TODO: Clean up - match bob.select_next_some().await { - SwarmEvent::Behaviour(rendezvous::client::Event::Discovered { registrations, .. }) => { + assert_behaviour_events! { + bob: rendezvous::client::Event::Discovered { registrations,.. }, + || { assert!(!registrations.is_empty()); } - _ => todo!(), - } + }; let alices_peer_id = *alice.local_peer_id(); let bobs_peer_id = *bob.local_peer_id(); @@ -283,22 +280,18 @@ async fn registration_on_clients_expire() { alice .behaviour_mut() .register(namespace.clone(), roberts_peer_id, Some(registration_ttl)); + assert_behaviour_events! { + alice: rendezvous::client::Event::Registered { .. }, + || { } + }; bob.behaviour_mut() .discover(Some(namespace), None, None, roberts_peer_id); - - // TODO: Clean up - match alice.select_next_some().await { - SwarmEvent::Behaviour(rendezvous::client::Event::Registered { .. }) => {} - _ => todo!(), - } - - // TODO: Clean up - match bob.select_next_some().await { - SwarmEvent::Behaviour(rendezvous::client::Event::Discovered { registrations, .. }) => { + assert_behaviour_events! { + bob: rendezvous::client::Event::Discovered { registrations,.. }, + || { assert!(!registrations.is_empty()); } - _ => todo!(), - } + }; tokio::time::sleep(Duration::from_secs(registration_ttl + 5)).await; From 559bb31a82015c58e338f435853f6ebe237d4057 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Sun, 10 Oct 2021 16:19:07 +0200 Subject: [PATCH 51/67] core/src/connection: Move dial concurrency factor to constant --- core/src/connection/pool/concurrent_dial.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/connection/pool/concurrent_dial.rs b/core/src/connection/pool/concurrent_dial.rs index a3780d8a5b1..7f598530789 100644 --- a/core/src/connection/pool/concurrent_dial.rs +++ b/core/src/connection/pool/concurrent_dial.rs @@ -34,6 +34,9 @@ use std::{ task::{Context, Poll}, }; +/// Maximum number of address candidates concurrently dialed in a dial attempt. +const CONCURRENCY_FACTOR: usize = 5; + type Dial = BoxFuture< 'static, ( @@ -79,7 +82,7 @@ where let dials = FuturesUnordered::new(); while let Some(dial) = pending_dials.next() { dials.push(dial); - if dials.len() == 5 { + if dials.len() == CONCURRENCY_FACTOR { break; } } From 8547941a5e1255f8e342e6474ed76323603c695c Mon Sep 17 00:00:00 2001 From: Max Inden Date: Sun, 10 Oct 2021 16:40:01 +0200 Subject: [PATCH 52/67] *: Add changelog entries --- core/CHANGELOG.md | 8 ++++++++ swarm/CHANGELOG.md | 17 +++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 1c4ace6835f..16e6e8af40d 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -36,6 +36,13 @@ - Add `SignedEnvelope` and `PeerRecord` according to [RFC0002] and [RFC0003] (see [PR 2107]). +- Concurrently dial up to 5 address candidates within a single dial attempt (see [PR 2248]). + + - On success of a single address, provide errors of the thus far failed dials via + `NetworkEvent::ConnectionEstablished::outgoing`. + + - On failure of all addresses, provide the errors via `NetworkEvent::DialError`. + [PR 2145]: https://github.com/libp2p/rust-libp2p/pull/2145 [PR 2213]: https://github.com/libp2p/rust-libp2p/pull/2213 [PR 2142]: https://github.com/libp2p/rust-libp2p/pull/2142 @@ -44,6 +51,7 @@ [PR 2191]: https://github.com/libp2p/rust-libp2p/pull/2191 [PR 2195]: https://github.com/libp2p/rust-libp2p/pull/2195 [PR 2107]: https://github.com/libp2p/rust-libp2p/pull/2107 +[PR 2248]: https://github.com/libp2p/rust-libp2p/pull/2248 [RFC0002]: https://github.com/libp2p/specs/blob/master/RFC/0002-signed-envelopes.md [RFC0003]: https://github.com/libp2p/specs/blob/master/RFC/0003-routing-records.md diff --git a/swarm/CHANGELOG.md b/swarm/CHANGELOG.md index 5894591c3bd..7fb77c7e607 100644 --- a/swarm/CHANGELOG.md +++ b/swarm/CHANGELOG.md @@ -42,11 +42,28 @@ parameters to `NetworkBehaviourAction`. See [PR 2191]. +- Concurrently dial up to 5 address candidates within a single dial attempt (see [PR 2248]). + + - On success of a single address, report errors of the thus far failed dials via + `SwarmEvent::ConnectionEstablished::outgoing`. + + - On failure of all addresses, report errors via the new `SwarmEvent::OutgoingConnectionError`. + + - Remove `SwarmEvent::UnreachableAddr` and `SwarmEvent::UnknownPeerUnreachableAddr` event. + + - In `NetworkBehaviour::inject_connection_established` provide errors of all thus far failed addresses. + + - On unknown peer dial failures, call `NetworkBehaviour::inject_dial_failure` with a peer ID of `None`. + + - Remove `NetworkBehaviour::inject_addr_reach_failure`. Information is now provided via + `NetworkBehaviour::inject_connection_established` and `NetworkBehaviour::inject_dial_failure`. + [PR 2150]: https://github.com/libp2p/rust-libp2p/pull/2150 [PR 2182]: https://github.com/libp2p/rust-libp2p/pull/2182 [PR 2183]: https://github.com/libp2p/rust-libp2p/pull/2183 [PR 2192]: https://github.com/libp2p/rust-libp2p/pull/2192 [PR 2191]: https://github.com/libp2p/rust-libp2p/pull/2191 +[PR 2248]: https://github.com/libp2p/rust-libp2p/pull/2248 # 0.30.0 [2021-07-12] From 3aca8d0d16cf01101de1eacaa95f89caa653994d Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 13 Oct 2021 14:43:15 +0200 Subject: [PATCH 53/67] {core,swarm}/: Make concurrent dialing configurable --- core/src/connection/pool.rs | 13 +++++++++++-- core/src/connection/pool/concurrent_dial.rs | 7 +++---- core/src/network.rs | 8 +++++++- swarm/src/lib.rs | 8 +++++++- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index 81735956cf1..7efb50c4b86 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -45,7 +45,7 @@ use std::{ collections::{hash_map, HashMap}, convert::TryFrom as _, fmt, - num::NonZeroU32, + num::{NonZeroU32, NonZeroU8}, pin::Pin, task::Context, task::Poll, @@ -80,6 +80,9 @@ where /// Size of the task command buffer (per task). task_command_buffer_size: usize, + /// Number of addresses concurrently dialed for a single outbound connection attempt. + dial_concurrency_factor: NonZeroU8, + /// The executor to use for running the background tasks. If `None`, /// the tasks are kept in `local_spawns` instead and polled on the /// current thread when the [`Pool`] is polled for new events. @@ -301,6 +304,7 @@ where pending: Default::default(), next_connection_id: ConnectionId(0), task_command_buffer_size: config.task_command_buffer_size, + dial_concurrency_factor: config.dial_concurrency_factor, executor: config.executor, local_spawns: FuturesUnordered::new(), pending_connection_events_tx, @@ -534,7 +538,7 @@ where return Err(DialError::ConnectionLimit { limit, handler }); }; - let dial = ConcurrentDial::new(transport, peer, addresses); + let dial = ConcurrentDial::new(transport, peer, addresses, self.dial_concurrency_factor); let connection_id = self.next_connection_id(); @@ -1340,6 +1344,9 @@ pub struct PoolConfig { /// Size of the pending connection task event buffer and the established connection task event /// buffer. pub task_event_buffer_size: usize, + + /// Number of addresses concurrently dialed for a single outbound connection attempt. + pub dial_concurrency_factor: NonZeroU8, } impl Default for PoolConfig { @@ -1348,6 +1355,8 @@ impl Default for PoolConfig { executor: None, task_event_buffer_size: 32, task_command_buffer_size: 7, + // By default, addresses of a single connection attempt are dialed in sequence. + dial_concurrency_factor: NonZeroU8::new(1).expect("1 > 0"), } } } diff --git a/core/src/connection/pool/concurrent_dial.rs b/core/src/connection/pool/concurrent_dial.rs index 7f598530789..a7434a23fe9 100644 --- a/core/src/connection/pool/concurrent_dial.rs +++ b/core/src/connection/pool/concurrent_dial.rs @@ -30,13 +30,11 @@ use futures::{ stream::{FuturesUnordered, StreamExt}, }; use std::{ + num::NonZeroU8, pin::Pin, task::{Context, Poll}, }; -/// Maximum number of address candidates concurrently dialed in a dial attempt. -const CONCURRENCY_FACTOR: usize = 5; - type Dial = BoxFuture< 'static, ( @@ -64,6 +62,7 @@ where transport: TTrans, peer: Option, addresses: impl Iterator + Send + 'static, + concurrency_factor: NonZeroU8, ) -> Self { let mut pending_dials = addresses.map(move |address| match p2p_addr(peer, address) { Ok(address) => match transport.clone().dial(address.clone()) { @@ -82,7 +81,7 @@ where let dials = FuturesUnordered::new(); while let Some(dial) = pending_dials.next() { dials.push(dial); - if dials.len() == CONCURRENCY_FACTOR { + if dials.len() == concurrency_factor.get().into() { break; } } diff --git a/core/src/network.rs b/core/src/network.rs index a3defeb1c87..197c1d08360 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -39,7 +39,7 @@ use crate::{ use std::{ convert::TryFrom as _, error, fmt, - num::NonZeroUsize, + num::{NonZeroU8, NonZeroUsize}, pin::Pin, task::{Context, Poll}, }; @@ -545,6 +545,12 @@ impl NetworkConfig { self } + /// Number of addresses concurrently dialed for a single outbound connection attempt. + pub fn with_dial_concurrency_factor(mut self, factor: NonZeroU8) -> Self { + self.pool_config.dial_concurrency_factor = factor; + self + } + /// Sets the connection limits to enforce. pub fn with_connection_limits(mut self, limits: ConnectionLimits) -> Self { self.limits = limits; diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 4340e4d5e76..db6649209ae 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -93,7 +93,7 @@ use protocols_handler::{NodeHandlerWrapperBuilder, NodeHandlerWrapperError}; use registry::{AddressIntoIter, Addresses}; use smallvec::SmallVec; use std::collections::HashSet; -use std::num::{NonZeroU32, NonZeroUsize}; +use std::num::{NonZeroU32, NonZeroU8, NonZeroUsize}; use std::{ error, fmt, io, pin::Pin, @@ -1116,6 +1116,12 @@ where self } + /// Number of addresses concurrently dialed for a single outbound connection attempt. + pub fn dial_concurrency_factor(mut self, factor: NonZeroU8) -> Self { + self.network_config = self.network_config.with_dial_concurrency_factor(factor); + self + } + /// Configures the connection limits. pub fn connection_limits(mut self, limits: ConnectionLimits) -> Self { self.network_config = self.network_config.with_connection_limits(limits); From ef36c5e70dc06c3d504db46182c638e3306cf3d2 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 13 Oct 2021 17:13:14 +0200 Subject: [PATCH 54/67] core/tests/: Test concurrent dialing --- core/src/network.rs | 2 +- core/tests/concurrent_dialing.rs | 167 +++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 core/tests/concurrent_dialing.rs diff --git a/core/src/network.rs b/core/src/network.rs index 197c1d08360..6b94ebf8b64 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -317,7 +317,7 @@ where ) } - /// Provides an API similar to `Stream`, except that it cannot error. + /// Provides an API similar to `Stream`, except that it does not terminate. pub fn poll<'a, TMuxer>( &'a mut self, cx: &mut Context<'_>, diff --git a/core/tests/concurrent_dialing.rs b/core/tests/concurrent_dialing.rs new file mode 100644 index 00000000000..33902382dfd --- /dev/null +++ b/core/tests/concurrent_dialing.rs @@ -0,0 +1,167 @@ +// Copyright 2021 Protocol Labs. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +mod util; + +use futures::executor::block_on; +use futures::future::poll_fn; +use futures::ready; +use libp2p_core::{ + multiaddr::Protocol, + network::{NetworkConfig, NetworkEvent}, + ConnectedPoint, +}; +use quickcheck::*; +use rand::Rng; +use std::num::NonZeroU8; +use std::task::Poll; +use util::{test_network, TestHandler}; + +#[test] +fn concurrent_dialing() { + #[derive(Clone, Debug)] + struct DialConcurrencyFactor(NonZeroU8); + + impl Arbitrary for DialConcurrencyFactor { + fn arbitrary(g: &mut G) -> Self { + Self(NonZeroU8::new(g.gen_range(1, 11)).unwrap()) + } + } + + fn prop(concurrency_factor: DialConcurrencyFactor) { + block_on(async { + let mut network_1 = test_network(NetworkConfig::default()); + let mut network_2 = test_network( + NetworkConfig::default().with_dial_concurrency_factor(concurrency_factor.0), + ); + + // Listen on `concurrency_factor + 1` addresses. + // + // `+ 1` to ensure a subset of addresses is dialed by network_2. + let num_listen_addrs = concurrency_factor.0.get() + 2; + let mut network_1_listen_addresses = Vec::new(); + for _ in 0..num_listen_addrs { + network_1.listen_on("/memory/0".parse().unwrap()).unwrap(); + + poll_fn(|cx| match ready!(network_1.poll(cx)) { + NetworkEvent::NewListenerAddress { listen_addr, .. } => { + network_1_listen_addresses.push(listen_addr); + return Poll::Ready(()); + } + _ => panic!("Expected `NewListenerAddress` event."), + }) + .await; + } + + // Have network 2 dial network 1 and wait for network 1 to receive the incoming + // connections. + network_2 + .peer(*network_1.local_peer_id()) + .dial(network_1_listen_addresses.clone(), TestHandler()) + .unwrap(); + let mut network_1_incoming_connections = Vec::new(); + for i in 0..concurrency_factor.0.get() { + poll_fn(|cx| { + match network_2.poll(cx) { + Poll::Ready(e) => panic!("Unexpected event: {:?}", e), + Poll::Pending => {} + } + + match ready!(network_1.poll(cx)) { + NetworkEvent::IncomingConnection { connection, .. } => { + assert_eq!( + connection.local_addr, network_1_listen_addresses[i as usize], + "Expect network 2 to prioritize by address order." + ); + + network_1_incoming_connections.push(connection); + return Poll::Ready(()); + } + _ => panic!("Expected `NewListenerAddress` event."), + } + }) + .await; + } + + // Have network 1 accept the incoming connection and wait for network 1 and network 2 to + // report a shared established connection. + let accepted_addr = network_1_incoming_connections[0].local_addr.clone(); + network_1 + .accept(network_1_incoming_connections.remove(0), TestHandler()) + .unwrap(); + let mut network_1_connection_established = false; + let mut network_2_connection_established = false; + poll_fn(|cx| { + match network_2.poll(cx) { + Poll::Ready(NetworkEvent::ConnectionEstablished { + connection, + outgoing, + .. + }) => { + match connection.endpoint() { + ConnectedPoint::Dialer { address } => { + assert_eq!( + *address, + accepted_addr + .clone() + .with(Protocol::P2p((*network_1.local_peer_id()).into())) + ) + } + ConnectedPoint::Listener { .. } => panic!("Expected dialer."), + } + assert!(outgoing.unwrap().is_empty()); + network_2_connection_established = true; + if network_1_connection_established { + return Poll::Ready(()); + } + } + Poll::Ready(e) => panic!("Expected `ConnectionEstablished` event: {:?}.", e), + Poll::Pending => {} + } + + match ready!(network_1.poll(cx)) { + NetworkEvent::ConnectionEstablished { + connection, + outgoing, + .. + } => { + match connection.endpoint() { + ConnectedPoint::Listener { local_addr, .. } => { + assert_eq!(*local_addr, accepted_addr) + } + ConnectedPoint::Dialer { .. } => panic!("Expected listener."), + } + assert!(outgoing.is_none()); + network_1_connection_established = true; + if network_2_connection_established { + return Poll::Ready(()); + } + } + e => panic!("Expected `ConnectionEstablished` event: {:?}.", e), + } + + Poll::Pending + }) + .await; + }) + } + + QuickCheck::new().quickcheck(prop as fn(_) -> _); +} From 0fea620e963c3d30d56473c5782962eedf6b26a4 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 13 Oct 2021 17:24:23 +0200 Subject: [PATCH 55/67] core/src/connection: Revert Stream impl for Connection --- core/src/connection.rs | 24 ++++++++---------------- core/src/connection/pool/task.rs | 15 +++++++++------ 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/core/src/connection.rs b/core/src/connection.rs index 64b1883812c..203789797dc 100644 --- a/core/src/connection.rs +++ b/core/src/connection.rs @@ -37,7 +37,6 @@ pub use substream::{Close, Substream, SubstreamEndpoint}; use crate::muxing::StreamMuxer; use crate::{Multiaddr, PeerId}; -use futures::stream::Stream; use std::hash::Hash; use std::{error::Error, fmt, pin::Pin, task::Context, task::Poll}; use substream::{Muxing, SubstreamEvent}; @@ -284,18 +283,13 @@ where pub fn close(self) -> (THandler, Close) { (self.handler, self.muxing.close().0) } -} - -impl Stream for Connection -where - TMuxer: StreamMuxer, - THandler: ConnectionHandler>, -{ - type Item = Result, ConnectionError>; /// Polls the connection for events produced by the associated handler /// as a result of I/O activity on the substream multiplexer. - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + pub fn poll( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll, ConnectionError>> { loop { let mut io_pending = false; @@ -315,9 +309,9 @@ where } Poll::Ready(Ok(SubstreamEvent::AddressChange(address))) => { self.handler.inject_address_change(&address); - return Poll::Ready(Some(Ok(Event::AddressChange(address)))); + return Poll::Ready(Ok(Event::AddressChange(address))); } - Poll::Ready(Err(err)) => return Poll::Ready(Some(Err(ConnectionError::IO(err)))), + Poll::Ready(Err(err)) => return Poll::Ready(Err(ConnectionError::IO(err))), } // Poll the handler for new events. @@ -331,11 +325,9 @@ where self.muxing.open_substream(user_data); } Poll::Ready(Ok(ConnectionHandlerEvent::Custom(event))) => { - return Poll::Ready(Some(Ok(Event::Handler(event)))); - } - Poll::Ready(Err(err)) => { - return Poll::Ready(Some(Err(ConnectionError::Handler(err)))) + return Poll::Ready(Ok(Event::Handler(event))); } + Poll::Ready(Err(err)) => return Poll::Ready(Err(ConnectionError::Handler(err))), } } } diff --git a/core/src/connection/pool/task.rs b/core/src/connection/pool/task.rs index d2f6a23bb42..9062583fd79 100644 --- a/core/src/connection/pool/task.rs +++ b/core/src/connection/pool/task.rs @@ -35,9 +35,10 @@ use crate::{ }; use futures::{ channel::{mpsc, oneshot}, - future::{Either, Future}, + future::{poll_fn, Either, Future}, SinkExt, StreamExt, }; +use std::pin::Pin; use void::Void; /// Commands that can be sent to a task. @@ -190,7 +191,12 @@ pub async fn new_for_established_connection( THandler::Handler: ConnectionHandler>, { loop { - match futures::future::select(command_receiver.next(), connection.next()).await { + match futures::future::select( + command_receiver.next(), + poll_fn(|cx| Pin::new(&mut connection).poll(cx)), + ) + .await + { Either::Left((Some(command), _)) => match command { Command::NotifyHandler(event) => connection.inject_event(event), Command::Close => { @@ -213,7 +219,7 @@ pub async fn new_for_established_connection( // The manager has disappeared; abort. Either::Left((None, _)) => return, - Either::Right((Some(event), _)) => { + Either::Right((event, _)) => { match event { Ok(connection::Event::Handler(event)) => { let _ = events @@ -249,9 +255,6 @@ pub async fn new_for_established_connection( } } } - Either::Right((None, _)) => { - unreachable!("Connection is an infinite stream"); - } } } } From d25c95beb994909e3cd7df976e4ac2104a468eec Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 13 Oct 2021 17:28:18 +0200 Subject: [PATCH 56/67] core/: Replace "Stack of protocols" with "Address" --- core/src/connection.rs | 6 +++--- core/src/connection/pool.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/connection.rs b/core/src/connection.rs index 203789797dc..bbcfed59e1f 100644 --- a/core/src/connection.rs +++ b/core/src/connection.rs @@ -101,7 +101,7 @@ pub enum PendingPoint { Listener { /// Local connection address. local_addr: Multiaddr, - /// Stack of protocols used to send back data to the remote. + /// Address used to send back data to the remote. send_back_addr: Multiaddr, }, } @@ -133,7 +133,7 @@ pub enum ConnectedPoint { Listener { /// Local connection address. local_addr: Multiaddr, - /// Stack of protocols used to send back data to the remote. + /// Address used to send back data to the remote. send_back_addr: Multiaddr, }, } @@ -338,7 +338,7 @@ where pub struct IncomingInfo<'a> { /// Local connection address. pub local_addr: &'a Multiaddr, - /// Stack of protocols used to send back data to the remote. + /// Address used to send back data to the remote. pub send_back_addr: &'a Multiaddr, } diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index 7efb50c4b86..bea39904971 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -190,7 +190,7 @@ where PendingInboundConnectionError { /// The ID of the failed connection. id: ConnectionId, - /// Stack of protocols used to send back data to the remote. + /// Address used to send back data to the remote. send_back_addr: Multiaddr, /// Local connection address. local_addr: Multiaddr, From ecdb812b9e154745b94397aaa7addf50857f7997 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 13 Oct 2021 17:55:11 +0200 Subject: [PATCH 57/67] core/src/connection: Fix doc comment on to_pending_point --- core/src/connection.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/connection.rs b/core/src/connection.rs index bbcfed59e1f..961eb4abc0e 100644 --- a/core/src/connection.rs +++ b/core/src/connection.rs @@ -343,14 +343,14 @@ pub struct IncomingInfo<'a> { } impl<'a> IncomingInfo<'a> { - /// Builds the `ConnectedPoint` corresponding to the incoming connection. + /// Builds the [`PendingPoint`] corresponding to the incoming connection. pub fn to_pending_point(&self) -> PendingPoint { PendingPoint::Listener { local_addr: self.local_addr.clone(), send_back_addr: self.send_back_addr.clone(), } } - /// Builds the `ConnectedPoint` corresponding to the incoming connection. + /// Builds the [`ConnectedPoint`] corresponding to the incoming connection. pub fn to_connected_point(&self) -> ConnectedPoint { ConnectedPoint::Listener { local_addr: self.local_addr.clone(), From 2a6dc5485edd35b14af2f0f6aa90295f8287b37a Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 13 Oct 2021 17:57:45 +0200 Subject: [PATCH 58/67] core/src/connection: Rename trait bound TransportError to TTransErr --- core/src/connection/error.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/connection/error.rs b/core/src/connection/error.rs index c8b778c7bde..e127db7574e 100644 --- a/core/src/connection/error.rs +++ b/core/src/connection/error.rs @@ -72,9 +72,9 @@ pub type PendingInboundConnectionError = /// Errors that can occur in the context of a pending `Connection`. #[derive(Debug)] -pub enum PendingConnectionError { +pub enum PendingConnectionError { /// An error occurred while negotiating the transport protocol(s) on a connection. - Transport(TransportError), + Transport(TTransErr), /// The connection was dropped because the connection limit /// for a peer has been reached. @@ -92,9 +92,9 @@ pub enum PendingConnectionError { IO(io::Error), } -impl fmt::Display for PendingConnectionError +impl fmt::Display for PendingConnectionError where - TransportError: fmt::Display + fmt::Debug, + TTransErr: fmt::Display + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -117,9 +117,9 @@ where } } -impl std::error::Error for PendingConnectionError +impl std::error::Error for PendingConnectionError where - TransportError: std::error::Error + 'static, + TTransErr: std::error::Error + 'static, { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { From e7beec40c53320832765dc8ae750df718227cb98 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 13 Oct 2021 18:06:43 +0200 Subject: [PATCH 59/67] core/src/connection/pool: Impl start_close on EstablishedConnInfo --- core/src/connection/pool.rs | 43 +++++++++++++++---------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index bea39904971..ca1fdcd8898 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -116,6 +116,20 @@ struct EstablishedConnectionInfo { sender: mpsc::Sender>, } +impl EstablishedConnectionInfo { + /// Initiates a graceful close of the connection. + /// + /// Has no effect if the connection is already closing. + pub fn start_close(&mut self) { + // Clone the sender so that we are guaranteed to have + // capacity for the close command (every sender gets a slot). + match self.sender.clone().try_send(task::Command::Close) { + Ok(()) => {} + Err(e) => assert!(e.is_disconnected(), "No capacity for close command."), + }; + } +} + struct PendingConnectionInfo { /// [`PeerId`] of the remote peer. peer_id: Option, @@ -382,20 +396,8 @@ where /// by the pool effective immediately. pub fn disconnect(&mut self, peer: &PeerId) { if let Some(conns) = self.established.get_mut(peer) { - // Detour via `EstablishedConnection` is not ideal, but at least only one code path in - // order to start closing a connection exists. - #[allow(clippy::needless_collect)] - let connection_ids = conns.iter().map(|(id, _)| *id).collect::>(); - - for id in connection_ids.into_iter() { - let established_connection = match conns.entry(id) { - hash_map::Entry::Occupied(entry) => EstablishedConnection { entry }, - hash_map::Entry::Vacant(_) => { - unreachable!("Iterating established connections.") - } - }; - - established_connection.start_close(); + for (_, conn) in conns.iter_mut() { + conn.start_close(); } } @@ -1049,18 +1051,7 @@ impl EstablishedConnection<'_, TInEvent> { /// /// Has no effect if the connection is already closing. pub fn start_close(mut self) { - // Clone the sender so that we are guaranteed to have - // capacity for the close command (every sender gets a slot). - match self - .entry - .get_mut() - .sender - .clone() - .try_send(task::Command::Close) - { - Ok(()) => {} - Err(e) => assert!(e.is_disconnected(), "No capacity for close command."), - }; + self.entry.get_mut().start_close() } } From 34daf905677ce29b1678eb675e923864f27c67aa Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 13 Oct 2021 18:18:03 +0200 Subject: [PATCH 60/67] core/src/network: Fix trait bounds on DialingOpts --- core/src/network.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/network.rs b/core/src/network.rs index 6b94ebf8b64..19eefa577a3 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -227,7 +227,7 @@ where /// Initiates a connection attempt to a known peer. fn dial_peer( &mut self, - opts: DialingOpts, + opts: DialingOpts, ) -> Result> where I: Iterator + Send + 'static, @@ -464,7 +464,7 @@ where /// Options for a dialing attempt (i.e. repeated connection attempt /// via a list of address) to a peer. -struct DialingOpts> { +struct DialingOpts { peer: PeerId, handler: THandler, addresses: I, From d09910627a8ed4b07cbd484833fb7ce91b4dbcb2 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 13 Oct 2021 18:20:42 +0200 Subject: [PATCH 61/67] core/src/network/peer: Fix doc comment on DialingAttempt --- core/src/network/peer.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/src/network/peer.rs b/core/src/network/peer.rs index 813467cb8ea..7fb4004b062 100644 --- a/core/src/network/peer.rs +++ b/core/src/network/peer.rs @@ -462,9 +462,8 @@ where } } -/// A `DialingAttempt` is an ongoing outgoing connection attempt to -/// a known / expected remote peer ID and a list of alternative addresses -/// to connect to, if the current connection attempt fails. +/// A [`DialingAttempt`] is a pending outgoing connection attempt to a known / +/// expected remote peer ID. pub struct DialingAttempt<'a, THandler: IntoConnectionHandler> { peer_id: PeerId, /// The underlying pending connection in the `Pool`. From d5e06b6d2b2abfc690cb6ae8402db36b00348b73 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 13 Oct 2021 18:22:08 +0200 Subject: [PATCH 62/67] core/src/network/peer: Remove outdated comment on DialingAttempt::abort --- core/src/network/peer.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/src/network/peer.rs b/core/src/network/peer.rs index 7fb4004b062..3a286c56c90 100644 --- a/core/src/network/peer.rs +++ b/core/src/network/peer.rs @@ -482,10 +482,6 @@ impl<'a, THandler: IntoConnectionHandler> DialingAttempt<'a, THandler> { } /// Aborts the dialing attempt. - /// - /// Aborting a dialing attempt involves aborting the current connection - /// attempt and dropping any remaining addresses given to [`Peer::dial()`] - /// that have not yet been tried. pub fn abort(self) { self.inner.abort(); } From 7331d90d318c36df6dfd241c8fc6e0832ad383e2 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 13 Oct 2021 18:35:14 +0200 Subject: [PATCH 63/67] {core,swarm}/: Rename outgoing to concurrent_dial_errors --- core/src/connection/pool.rs | 10 +++++----- core/src/network.rs | 4 ++-- core/src/network/event.rs | 6 +++--- core/tests/concurrent_dialing.rs | 8 ++++---- swarm/src/lib.rs | 10 +++++----- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index ca1fdcd8898..cc1aef8cbda 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -160,7 +160,7 @@ where /// [`Some`] when the new connection is an outgoing connection. /// Addresses are dialed in parallel. Contains the addresses and errors /// of dial attempts that failed before the one successful dial. - outgoing: Option)>>, + concurrent_dial_errors: Option)>>, }, /// An established connection was closed. @@ -242,12 +242,12 @@ where match self { PoolEvent::ConnectionEstablished { connection, - outgoing, + concurrent_dial_errors, .. } => f .debug_tuple("PoolEvent::ConnectionEstablished") .field(connection) - .field(outgoing) + .field(concurrent_dial_errors) .finish(), PoolEvent::ConnectionClosed { id, @@ -739,7 +739,7 @@ where self.counters.dec_pending(&endpoint); - let (endpoint, dialing_errors) = match (endpoint, outgoing) { + let (endpoint, concurrent_dial_errors) = match (endpoint, outgoing) { (PendingPoint::Dialer, Some((address, errors))) => { (ConnectedPoint::Dialer { address }, Some(errors)) } @@ -889,7 +889,7 @@ where return Poll::Ready(PoolEvent::ConnectionEstablished { connection, num_established, - outgoing: dialing_errors, + concurrent_dial_errors, }) } _ => unreachable!("since `entry` is an `EstablishedEntry`."), diff --git a/core/src/network.rs b/core/src/network.rs index 19eefa577a3..ed8f6d9b3ce 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -396,11 +396,11 @@ where Poll::Ready(PoolEvent::ConnectionEstablished { connection, num_established, - outgoing, + concurrent_dial_errors, }) => NetworkEvent::ConnectionEstablished { connection, num_established, - outgoing, + concurrent_dial_errors, }, Poll::Ready(PoolEvent::PendingOutboundConnectionError { id: _, diff --git a/core/src/network/event.rs b/core/src/network/event.rs index c4f4c41579d..6e016e82159 100644 --- a/core/src/network/event.rs +++ b/core/src/network/event.rs @@ -106,7 +106,7 @@ where /// [`Some`] when the new connection is an outgoing connection. /// Addresses are dialed in parallel. Contains the addresses and errors /// of dial attempts that failed before the one successful dial. - outgoing: Option)>>, + concurrent_dial_errors: Option)>>, }, /// An established connection to a peer has been closed. @@ -233,12 +233,12 @@ where .finish(), NetworkEvent::ConnectionEstablished { connection, - outgoing, + concurrent_dial_errors, .. } => f .debug_struct("OutgoingConnectionEstablished") .field("connection", connection) - .field("outgoing", outgoing) + .field("concurrent_dial_errors", concurrent_dial_errors) .finish(), NetworkEvent::ConnectionClosed { id, diff --git a/core/tests/concurrent_dialing.rs b/core/tests/concurrent_dialing.rs index 33902382dfd..1948b201509 100644 --- a/core/tests/concurrent_dialing.rs +++ b/core/tests/concurrent_dialing.rs @@ -112,7 +112,7 @@ fn concurrent_dialing() { match network_2.poll(cx) { Poll::Ready(NetworkEvent::ConnectionEstablished { connection, - outgoing, + concurrent_dial_errors, .. }) => { match connection.endpoint() { @@ -126,7 +126,7 @@ fn concurrent_dialing() { } ConnectedPoint::Listener { .. } => panic!("Expected dialer."), } - assert!(outgoing.unwrap().is_empty()); + assert!(concurrent_dial_errors.unwrap().is_empty()); network_2_connection_established = true; if network_1_connection_established { return Poll::Ready(()); @@ -139,7 +139,7 @@ fn concurrent_dialing() { match ready!(network_1.poll(cx)) { NetworkEvent::ConnectionEstablished { connection, - outgoing, + concurrent_dial_errors, .. } => { match connection.endpoint() { @@ -148,7 +148,7 @@ fn concurrent_dialing() { } ConnectedPoint::Dialer { .. } => panic!("Expected listener."), } - assert!(outgoing.is_none()); + assert!(concurrent_dial_errors.is_none()); network_1_connection_established = true; if network_2_connection_established { return Poll::Ready(()); diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index db6649209ae..113b4bc0876 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -142,9 +142,9 @@ pub enum SwarmEvent { /// opened. num_established: NonZeroU32, /// [`Some`] when the new connection is an outgoing connection. - /// Addresses are dialed in parallel. Contains the addresses and errors + /// Addresses are dialed concurrently. Contains the addresses and errors /// of dial attempts that failed before the one successful dial. - outgoing: Option)>>, + concurrent_dial_errors: Option)>>, }, /// A connection with the given peer has been closed, /// possibly as a result of an error. @@ -541,7 +541,7 @@ where Poll::Ready(NetworkEvent::ConnectionEstablished { connection, num_established, - outgoing, + concurrent_dial_errors, }) => { let peer_id = connection.peer_id(); let endpoint = connection.endpoint().clone(); @@ -560,7 +560,7 @@ where num_established ); let endpoint = connection.endpoint().clone(); - let failed_addresses = outgoing + let failed_addresses = concurrent_dial_errors .as_ref() .map(|es| es.iter().map(|(a, _)| a).cloned().collect()); this.behaviour.inject_connection_established( @@ -576,7 +576,7 @@ where peer_id, num_established, endpoint, - outgoing, + concurrent_dial_errors, }); } } From 9fc587053364c588bc437868aab794d6c1403469 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 13 Oct 2021 18:57:00 +0200 Subject: [PATCH 64/67] core/src/connection/pool: Implement expect_occupied for hash_map::Entry --- core/src/connection/pool.rs | 66 +++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/core/src/connection/pool.rs b/core/src/connection/pool.rs index cc1aef8cbda..163da2178f6 100644 --- a/core/src/connection/pool.rs +++ b/core/src/connection/pool.rs @@ -410,15 +410,16 @@ where .collect::>(); for pending_connection in pending_connections { - let pending_connection = match self.pending.entry(pending_connection) { - hash_map::Entry::Occupied(entry) => PendingConnection { - entry, - counters: &mut self.counters, - }, - hash_map::Entry::Vacant(_) => unreachable!("Iterating pending connections"), - }; + let entry = self + .pending + .entry(pending_connection) + .expect_occupied("Iterating pending connections"); - pending_connection.abort(); + PendingConnection { + entry, + counters: &mut self.counters, + } + .abort(); } } @@ -641,22 +642,16 @@ where Poll::Ready(None) => unreachable!("Pool holds both sender and receiver."), Poll::Ready(Some(task::EstablishedConnectionEvent::Notify { id, peer_id, event })) => { - match self + let entry = self .established .get_mut(&peer_id) .expect("Receive `Notify` event for established peer.") .entry(id) - { - hash_map::Entry::Occupied(entry) => { - return Poll::Ready(PoolEvent::ConnectionEvent { - connection: EstablishedConnection { entry }, - event, - }) - } - hash_map::Entry::Vacant(_) => { - unreachable!("Receive `Notify` event from established connection") - } - } + .expect_occupied("Receive `Notify` event from established connection"); + return Poll::Ready(PoolEvent::ConnectionEvent { + connection: EstablishedConnection { entry }, + event, + }); } Poll::Ready(Some(task::EstablishedConnectionEvent::AddressChange { id, @@ -1072,10 +1067,11 @@ where #[allow(clippy::should_implement_trait)] pub fn next(&mut self) -> Option> { if let (Some(id), Some(connections)) = (self.ids.next(), self.connections.as_mut()) { - match connections.entry(id) { - hash_map::Entry::Occupied(entry) => Some(EstablishedConnection { entry }), - hash_map::Entry::Vacant(_) => unreachable!("Established entry not found in pool."), - } + Some(EstablishedConnection { + entry: connections + .entry(id) + .expect_occupied("Established entry not found in pool."), + }) } else { None } @@ -1092,10 +1088,11 @@ where 'a: 'b, { if let (Some(id), Some(connections)) = (self.ids.next(), self.connections) { - match connections.entry(id) { - hash_map::Entry::Occupied(entry) => Some(EstablishedConnection { entry }), - hash_map::Entry::Vacant(_) => unreachable!("Established entry not found in pool."), - } + Some(EstablishedConnection { + entry: connections + .entry(id) + .expect_occupied("Established entry not found in pool."), + }) } else { None } @@ -1351,3 +1348,16 @@ impl Default for PoolConfig { } } } + +trait EntryExt<'a, K, V> { + fn expect_occupied(self, msg: &'static str) -> hash_map::OccupiedEntry<'a, K, V>; +} + +impl<'a, K: 'a, V: 'a> EntryExt<'a, K, V> for hash_map::Entry<'a, K, V> { + fn expect_occupied(self, msg: &'static str) -> hash_map::OccupiedEntry<'a, K, V> { + match self { + hash_map::Entry::Occupied(entry) => entry, + hash_map::Entry::Vacant(_) => panic!("{}", msg), + } + } +} From cb553cf326093ebd949bb5a7007f2be1165eba2f Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 14 Oct 2021 15:09:00 +0200 Subject: [PATCH 65/67] {core,swarm}/CHANGELOG: Update now that concurrency is configurable --- core/CHANGELOG.md | 3 ++- swarm/CHANGELOG.md | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 0e5a66f3a85..fafd53de677 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -39,7 +39,8 @@ - Report `ListenersEvent::Closed` when dropping a listener in `ListenersStream::remove_listener`, return `bool` instead of `Result<(), ()>` (see [PR 2261]). -- Concurrently dial up to 5 address candidates within a single dial attempt (see [PR 2248]). +- Concurrently dial up address candidates within a single dial attempt (see [PR 2248]) configured + via `Network::with_dial_concurrency_factor`. - On success of a single address, provide errors of the thus far failed dials via `NetworkEvent::ConnectionEstablished::outgoing`. diff --git a/swarm/CHANGELOG.md b/swarm/CHANGELOG.md index 3db7fe1fceb..51264235026 100644 --- a/swarm/CHANGELOG.md +++ b/swarm/CHANGELOG.md @@ -45,7 +45,8 @@ - Return `bool` instead of `Result<(), ()>` for `Swarm::remove_listener`(see [PR 2261]). -- Concurrently dial up to 5 address candidates within a single dial attempt (see [PR 2248]). +- Concurrently dial address candidates within a single dial attempt (see [PR 2248]) configured via + `Swarm::dial_concurrency_factor`. - On success of a single address, report errors of the thus far failed dials via `SwarmEvent::ConnectionEstablished::outgoing`. From 04c694faa5dae5abbde3a6b23e03d8a14859e9e8 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 14 Oct 2021 15:19:27 +0200 Subject: [PATCH 66/67] protocols/src/identify: Update to changed address failure reporting --- protocols/identify/src/identify.rs | 35 +++++++++++++++++------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/protocols/identify/src/identify.rs b/protocols/identify/src/identify.rs index 08a69235a7c..8a5bfc7613e 100644 --- a/protocols/identify/src/identify.rs +++ b/protocols/identify/src/identify.rs @@ -220,7 +220,7 @@ impl NetworkBehaviour for Identify { peer_id: &PeerId, conn: &ConnectionId, endpoint: &ConnectedPoint, - _: Option<&Vec>, + failed_addresses: Option<&Vec>, ) { let addr = match endpoint { ConnectedPoint::Dialer { address } => address.clone(), @@ -231,6 +231,16 @@ impl NetworkBehaviour for Identify { .entry(*peer_id) .or_default() .insert(*conn, addr); + + if let Some(entry) = self.discovered_peers.get_mut(peer_id) { + for addr in failed_addresses + .into_iter() + .map(|addresses| addresses.into_iter()) + .flatten() + { + entry.remove(addr); + } + } } fn inject_connection_closed( @@ -249,13 +259,21 @@ impl NetworkBehaviour for Identify { &mut self, peer_id: Option, _: Self::ProtocolsHandler, - _: &DialError, + error: &DialError, ) { if let Some(peer_id) = peer_id { if !self.connected.contains_key(&peer_id) { self.pending_push.remove(&peer_id); } } + + if let Some(entry) = peer_id.and_then(|id| self.discovered_peers.get_mut(&id)) { + if let DialError::Transport(errors) = error { + for (addr, _error) in errors { + entry.remove(addr); + } + } + } } fn inject_disconnected(&mut self, peer_id: &PeerId) { @@ -429,19 +447,6 @@ impl NetworkBehaviour for Identify { .map(|addr| Vec::from_iter(addr)) .unwrap_or_default() } - - fn inject_addr_reach_failure( - &mut self, - peer_id: Option<&PeerId>, - addr: &Multiaddr, - _: &dyn std::error::Error, - ) { - if let Some(peer) = peer_id { - if let Some(entry) = self.discovered_peers.get_mut(peer) { - entry.remove(addr); - } - } - } } /// Event emitted by the `Identify` behaviour. From 7aafc5247a638831e1a832ec8a06c88582f236fa Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 14 Oct 2021 17:16:16 +0200 Subject: [PATCH 67/67] core/CHANGELOG.md: Fix typo --- core/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index fafd53de677..4d9f4bda667 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -39,7 +39,7 @@ - Report `ListenersEvent::Closed` when dropping a listener in `ListenersStream::remove_listener`, return `bool` instead of `Result<(), ()>` (see [PR 2261]). -- Concurrently dial up address candidates within a single dial attempt (see [PR 2248]) configured +- Concurrently dial address candidates within a single dial attempt (see [PR 2248]) configured via `Network::with_dial_concurrency_factor`. - On success of a single address, provide errors of the thus far failed dials via