Permit concurrent dialing attempts per peer. (#1506)

* Permit concurrent dialing attempts per peer.

This is a follow-up to https://github.com/libp2p/rust-libp2p/pull/1440
and relates to https://github.com/libp2p/rust-libp2p/issues/925.
This change permits multiple dialing attempts per peer.
Note though that `libp2p-swarm` does not yet make use of this ability,
retaining the current behaviour. The essence of the changes are that the
`Peer` API now provides `Peer::dial()`, i.e. regardless of the state in
which the peer is. A dialing attempt is always made up of one or more
addresses tried sequentially, as before, but now there can be multiple
dialing attempts per peer. A configurable per-peer limit for outgoing
connections and thus concurrent dialing attempts is also included.

* Introduce `DialError` in `libp2p-swarm`.

For a cleaner API and to treat the case of no addresses
for a peer as an error, such that a `NetworkBehaviourAction::DialPeer`
request is always matched up with either `inject_connection_established`
or `inject_dial_error`.

* Fix rustdoc link.

* Add `DialPeerCondition::Always`.

* Adapt to master.

* Update changelog.
This commit is contained in:
Roman Borschel
2020-05-12 13:10:18 +02:00
committed by GitHub
parent 44c0c76981
commit 5ba7c4831b
7 changed files with 473 additions and 337 deletions

View File

@ -291,12 +291,10 @@ pub enum DialPeerCondition {
/// If there is an ongoing dialing attempt, the addresses reported by
/// [`NetworkBehaviour::addresses_of_peer`] are added to the ongoing
/// dialing attempt, ignoring duplicates.
///
/// This condition implies [`DialPeerCondition::Disconnected`].
NotDialing,
// TODO: Once multiple dialing attempts per peer are permitted.
// See https://github.com/libp2p/rust-libp2p/pull/1506.
// Always,
/// A new dialing attempt is always initiated, only subject to the
/// configured connection limits.
Always,
}
impl Default for DialPeerCondition {

View File

@ -115,7 +115,6 @@ use libp2p_core::{
NetworkInfo,
NetworkEvent,
NetworkConfig,
Peer,
peer::ConnectedPeer,
},
upgrade::ProtocolName,
@ -379,70 +378,31 @@ where TBehaviour: NetworkBehaviour<ProtocolsHandler = THandler>,
///
/// If a new dialing attempt has been initiated, `Ok(true)` is returned.
///
/// If there is an ongoing dialing attempt, the current addresses of the
/// peer, as reported by [`NetworkBehaviour::addresses_of_peer`] are added
/// to the ongoing dialing attempt, ignoring duplicates. In this case no
/// new dialing attempt is initiated.
///
/// If no new dialing attempt has been initiated, meaning there is an ongoing
/// dialing attempt or `addresses_of_peer` reports no addresses, `Ok(false)`
/// is returned.
pub fn dial(me: &mut Self, peer_id: &PeerId) -> Result<bool, ConnectionLimit> {
pub fn dial(me: &mut Self, peer_id: &PeerId) -> Result<(), DialError> {
let mut addrs = me.behaviour.addresses_of_peer(peer_id).into_iter();
match me.network.peer(peer_id.clone()) {
Peer::Disconnected(peer) => {
if let Some(first) = addrs.next() {
let handler = me.behaviour.new_handler().into_node_handler_builder();
match peer.connect(first, addrs, handler) {
Ok(_) => return Ok(true),
Err(error) => {
log::debug!(
"New dialing attempt to disconnected peer {:?} failed: {:?}.",
peer_id, error);
me.behaviour.inject_dial_failure(&peer_id);
return Err(error)
}
}
} else {
log::debug!(
"New dialing attempt to disconnected peer {:?} failed: no address.",
peer_id
);
me.behaviour.inject_dial_failure(&peer_id);
}
Ok(false)
},
Peer::Connected(peer) => {
if let Some(first) = addrs.next() {
let handler = me.behaviour.new_handler().into_node_handler_builder();
match peer.connect(first, addrs, handler) {
Ok(_) => return Ok(true),
Err(error) => {
log::debug!(
"New dialing attempt to connected peer {:?} failed: {:?}.",
peer_id, error);
me.behaviour.inject_dial_failure(&peer_id);
return Err(error)
}
}
} else {
log::debug!(
"New dialing attempt to disconnected peer {:?} failed: no address.",
peer_id
);
me.behaviour.inject_dial_failure(&peer_id);
}
Ok(false)
}
Peer::Dialing(mut peer) => {
peer.connection().add_addresses(addrs);
Ok(false)
},
Peer::Local => {
me.behaviour.inject_dial_failure(&peer_id);
Err(ConnectionLimit { current: 0, limit: 0 })
}
let peer = me.network.peer(peer_id.clone());
let result =
if let Some(first) = addrs.next() {
let handler = me.behaviour.new_handler().into_node_handler_builder();
peer.dial(first, addrs, handler)
.map(|_| ())
.map_err(DialError::ConnectionLimit)
} else {
Err(DialError::NoAddresses)
};
if let Err(error) = &result {
log::debug!(
"New dialing attempt to peer {:?} failed: {:?}.",
peer_id, error);
me.behaviour.inject_dial_failure(&peer_id);
}
result
}
/// Returns an iterator that produces the list of addresses we're listening on.
@ -721,18 +681,22 @@ where TBehaviour: NetworkBehaviour<ProtocolsHandler = THandler>,
if !this.network.is_dialing(&peer_id) => true,
_ => false
};
if condition_matched {
if let Ok(true) = ExpandedSwarm::dial(this, &peer_id) {
return Poll::Ready(SwarmEvent::Dialing(peer_id));
if ExpandedSwarm::dial(this, &peer_id).is_ok() {
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);
if let Some(mut peer) = this.network.peer(peer_id.clone()).into_dialing() {
let addrs = this.behaviour.addresses_of_peer(peer.id());
peer.connection().add_addresses(addrs);
let mut attempt = peer.some_attempt();
for addr in addrs {
attempt.add_address(addr);
}
}
}
}
@ -1104,6 +1068,35 @@ where TBehaviour: NetworkBehaviour,
}
}
/// The possible failures of [`ExpandedSwarm::dial`].
#[derive(Debug)]
pub enum DialError {
/// The configured limit for simultaneous outgoing connections
/// has been reached.
ConnectionLimit(ConnectionLimit),
/// [`NetworkBehaviour::addresses_of_peer`] returned no addresses
/// for the peer to dial.
NoAddresses
}
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.")
}
}
}
impl error::Error for DialError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
DialError::ConnectionLimit(err) => Some(err),
DialError::NoAddresses => None
}
}
}
/// Dummy implementation of [`NetworkBehaviour`] that doesn't do anything.
#[derive(Clone, Default)]
pub struct DummyBehaviour {