mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-29 17:51:35 +00:00
refactor(swarm)!: remove handler
from NetworkBehaviourAction::Dial
(#3328)
We create the `ConnectionId` for the new connection as part of `DialOpts`. This allows `NetworkBehaviour`s to accurately track state regarding their own dial attempts. This patch is the main enabler of https://github.com/libp2p/rust-libp2p/pull/3254. Removing the `handler` field will allow us to deprecate the `NetworkBehaviour::new_handler` function in favor of four new ones that give more control over the connection lifecycle.
This commit is contained in:
@ -29,12 +29,13 @@ use libp2p_swarm::behaviour::{ConnectionClosed, ConnectionEstablished, DialFailu
|
||||
use libp2p_swarm::dial_opts::{self, DialOpts};
|
||||
use libp2p_swarm::{
|
||||
ConnectionHandlerUpgrErr, ExternalAddresses, NetworkBehaviour, NetworkBehaviourAction,
|
||||
NotifyHandler, PollParameters,
|
||||
NotifyHandler, PollParameters, THandlerInEvent,
|
||||
};
|
||||
use libp2p_swarm::{ConnectionId, THandlerOutEvent};
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
use std::task::{Context, Poll};
|
||||
use thiserror::Error;
|
||||
use void::Void;
|
||||
|
||||
const MAX_NUMBER_OF_UPGRADE_ATTEMPTS: u8 = 3;
|
||||
|
||||
@ -68,7 +69,7 @@ pub enum Error {
|
||||
|
||||
pub struct Behaviour {
|
||||
/// Queue of actions to return when polled.
|
||||
queued_events: VecDeque<NetworkBehaviourAction<Event, handler::Prototype>>,
|
||||
queued_events: VecDeque<NetworkBehaviourAction<Event, Either<handler::relayed::Command, Void>>>,
|
||||
|
||||
/// All direct (non-relayed) connections.
|
||||
direct_connections: HashMap<PeerId, HashSet<ConnectionId>>,
|
||||
@ -76,6 +77,12 @@ pub struct Behaviour {
|
||||
external_addresses: ExternalAddresses,
|
||||
|
||||
local_peer_id: PeerId,
|
||||
|
||||
direct_to_relayed_connections: HashMap<ConnectionId, ConnectionId>,
|
||||
|
||||
/// Indexed by the [`ConnectionId`] of the relayed connection and
|
||||
/// the [`PeerId`] we are trying to establish a direct connection to.
|
||||
outgoing_direct_connection_attempts: HashMap<(ConnectionId, PeerId), u8>,
|
||||
}
|
||||
|
||||
impl Behaviour {
|
||||
@ -85,6 +92,8 @@ impl Behaviour {
|
||||
direct_connections: Default::default(),
|
||||
external_addresses: Default::default(),
|
||||
local_peer_id,
|
||||
direct_to_relayed_connections: Default::default(),
|
||||
outgoing_direct_connection_attempts: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,40 +157,57 @@ impl Behaviour {
|
||||
fn on_dial_failure(
|
||||
&mut self,
|
||||
DialFailure {
|
||||
peer_id, handler, ..
|
||||
}: DialFailure<<Self as NetworkBehaviour>::ConnectionHandler>,
|
||||
peer_id,
|
||||
connection_id: failed_direct_connection,
|
||||
..
|
||||
}: DialFailure,
|
||||
) {
|
||||
if let handler::Prototype::DirectConnection {
|
||||
relayed_connection_id,
|
||||
role: handler::Role::Initiator { attempt },
|
||||
} = handler
|
||||
let peer_id = if let Some(peer_id) = peer_id {
|
||||
peer_id
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
let relayed_connection_id = if let Some(relayed_connection_id) = self
|
||||
.direct_to_relayed_connections
|
||||
.get(&failed_direct_connection)
|
||||
{
|
||||
let peer_id = peer_id.expect("Peer of `Prototype::DirectConnection` is always known.");
|
||||
if attempt < MAX_NUMBER_OF_UPGRADE_ATTEMPTS {
|
||||
self.queued_events
|
||||
.push_back(NetworkBehaviourAction::NotifyHandler {
|
||||
handler: NotifyHandler::One(relayed_connection_id),
|
||||
peer_id,
|
||||
event: Either::Left(handler::relayed::Command::Connect {
|
||||
attempt: attempt + 1,
|
||||
obs_addrs: self.observed_addreses(),
|
||||
}),
|
||||
})
|
||||
} else {
|
||||
self.queued_events.extend([
|
||||
NetworkBehaviourAction::NotifyHandler {
|
||||
peer_id,
|
||||
handler: NotifyHandler::One(relayed_connection_id),
|
||||
event: Either::Left(
|
||||
handler::relayed::Command::UpgradeFinishedDontKeepAlive,
|
||||
),
|
||||
},
|
||||
NetworkBehaviourAction::GenerateEvent(Event::DirectConnectionUpgradeFailed {
|
||||
remote_peer_id: peer_id,
|
||||
error: Error::Dial,
|
||||
*relayed_connection_id
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
let attempt = if let Some(attempt) = self
|
||||
.outgoing_direct_connection_attempts
|
||||
.get(&(relayed_connection_id, peer_id))
|
||||
{
|
||||
*attempt
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
if attempt < MAX_NUMBER_OF_UPGRADE_ATTEMPTS {
|
||||
self.queued_events
|
||||
.push_back(NetworkBehaviourAction::NotifyHandler {
|
||||
handler: NotifyHandler::One(relayed_connection_id),
|
||||
peer_id,
|
||||
event: Either::Left(handler::relayed::Command::Connect {
|
||||
attempt: attempt + 1,
|
||||
obs_addrs: self.observed_addreses(),
|
||||
}),
|
||||
]);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
self.queued_events.extend([
|
||||
NetworkBehaviourAction::NotifyHandler {
|
||||
peer_id,
|
||||
handler: NotifyHandler::One(relayed_connection_id),
|
||||
event: Either::Left(handler::relayed::Command::UpgradeFinishedDontKeepAlive),
|
||||
},
|
||||
NetworkBehaviourAction::GenerateEvent(Event::DirectConnectionUpgradeFailed {
|
||||
remote_peer_id: peer_id,
|
||||
error: Error::Dial,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -215,15 +241,26 @@ impl NetworkBehaviour for Behaviour {
|
||||
type OutEvent = Event;
|
||||
|
||||
fn new_handler(&mut self) -> Self::ConnectionHandler {
|
||||
handler::Prototype::UnknownConnection
|
||||
handler::Prototype
|
||||
}
|
||||
|
||||
fn on_connection_handler_event(
|
||||
&mut self,
|
||||
event_source: PeerId,
|
||||
connection: ConnectionId,
|
||||
connection_id: ConnectionId,
|
||||
handler_event: THandlerOutEvent<Self>,
|
||||
) {
|
||||
let relayed_connection_id = match handler_event.as_ref() {
|
||||
Either::Left(_) => connection_id,
|
||||
Either::Right(_) => match self.direct_to_relayed_connections.get(&connection_id) {
|
||||
None => {
|
||||
// If the connection ID is unknown to us, it means we didn't create it so ignore any event coming from it.
|
||||
return;
|
||||
}
|
||||
Some(relayed_connection_id) => *relayed_connection_id,
|
||||
},
|
||||
};
|
||||
|
||||
match handler_event {
|
||||
Either::Left(handler::relayed::Event::InboundConnectRequest {
|
||||
inbound_connect,
|
||||
@ -231,7 +268,7 @@ impl NetworkBehaviour for Behaviour {
|
||||
}) => {
|
||||
self.queued_events.extend([
|
||||
NetworkBehaviourAction::NotifyHandler {
|
||||
handler: NotifyHandler::One(connection),
|
||||
handler: NotifyHandler::One(relayed_connection_id),
|
||||
peer_id: event_source,
|
||||
event: Either::Left(handler::relayed::Command::AcceptInboundConnect {
|
||||
inbound_connect,
|
||||
@ -256,16 +293,17 @@ impl NetworkBehaviour for Behaviour {
|
||||
));
|
||||
}
|
||||
Either::Left(handler::relayed::Event::InboundConnectNegotiated(remote_addrs)) => {
|
||||
self.queued_events.push_back(NetworkBehaviourAction::Dial {
|
||||
opts: DialOpts::peer_id(event_source)
|
||||
.addresses(remote_addrs)
|
||||
.condition(dial_opts::PeerCondition::Always)
|
||||
.build(),
|
||||
handler: handler::Prototype::DirectConnection {
|
||||
relayed_connection_id: connection,
|
||||
role: handler::Role::Listener,
|
||||
},
|
||||
});
|
||||
let opts = DialOpts::peer_id(event_source)
|
||||
.addresses(remote_addrs)
|
||||
.condition(dial_opts::PeerCondition::Always)
|
||||
.build();
|
||||
|
||||
let maybe_direct_connection_id = opts.connection_id();
|
||||
|
||||
self.direct_to_relayed_connections
|
||||
.insert(maybe_direct_connection_id, relayed_connection_id);
|
||||
self.queued_events
|
||||
.push_back(NetworkBehaviourAction::Dial { opts });
|
||||
}
|
||||
Either::Left(handler::relayed::Event::OutboundNegotiationFailed { error }) => {
|
||||
self.queued_events
|
||||
@ -276,27 +314,25 @@ impl NetworkBehaviour for Behaviour {
|
||||
},
|
||||
));
|
||||
}
|
||||
Either::Left(handler::relayed::Event::OutboundConnectNegotiated {
|
||||
remote_addrs,
|
||||
attempt,
|
||||
}) => {
|
||||
self.queued_events.push_back(NetworkBehaviourAction::Dial {
|
||||
opts: DialOpts::peer_id(event_source)
|
||||
.condition(dial_opts::PeerCondition::Always)
|
||||
.addresses(remote_addrs)
|
||||
.override_role()
|
||||
.build(),
|
||||
handler: handler::Prototype::DirectConnection {
|
||||
relayed_connection_id: connection,
|
||||
role: handler::Role::Initiator { attempt },
|
||||
},
|
||||
});
|
||||
Either::Left(handler::relayed::Event::OutboundConnectNegotiated { remote_addrs }) => {
|
||||
let opts = DialOpts::peer_id(event_source)
|
||||
.condition(dial_opts::PeerCondition::Always)
|
||||
.addresses(remote_addrs)
|
||||
.override_role()
|
||||
.build();
|
||||
|
||||
let maybe_direct_connection_id = opts.connection_id();
|
||||
|
||||
self.direct_to_relayed_connections
|
||||
.insert(maybe_direct_connection_id, relayed_connection_id);
|
||||
*self
|
||||
.outgoing_direct_connection_attempts
|
||||
.entry((relayed_connection_id, event_source))
|
||||
.or_default() += 1;
|
||||
self.queued_events
|
||||
.push_back(NetworkBehaviourAction::Dial { opts });
|
||||
}
|
||||
Either::Right(Either::Left(
|
||||
handler::direct::Event::DirectConnectionUpgradeSucceeded {
|
||||
relayed_connection_id,
|
||||
},
|
||||
)) => {
|
||||
Either::Right(handler::direct::Event::DirectConnectionEstablished) => {
|
||||
self.queued_events.extend([
|
||||
NetworkBehaviourAction::NotifyHandler {
|
||||
peer_id: event_source,
|
||||
@ -312,7 +348,6 @@ impl NetworkBehaviour for Behaviour {
|
||||
),
|
||||
]);
|
||||
}
|
||||
Either::Right(Either::Right(event)) => void::unreachable(event),
|
||||
};
|
||||
}
|
||||
|
||||
@ -320,7 +355,7 @@ impl NetworkBehaviour for Behaviour {
|
||||
&mut self,
|
||||
_cx: &mut Context<'_>,
|
||||
_: &mut impl PollParameters,
|
||||
) -> Poll<NetworkBehaviourAction<Self::OutEvent, Self::ConnectionHandler>> {
|
||||
) -> Poll<NetworkBehaviourAction<Self::OutEvent, THandlerInEvent<Self>>> {
|
||||
if let Some(event) = self.queued_events.pop_front() {
|
||||
return Poll::Ready(event);
|
||||
}
|
||||
|
@ -20,61 +20,27 @@
|
||||
|
||||
use crate::protocol;
|
||||
use either::Either;
|
||||
use libp2p_core::upgrade::DeniedUpgrade;
|
||||
use libp2p_core::{ConnectedPoint, PeerId};
|
||||
use libp2p_swarm::dummy;
|
||||
use libp2p_swarm::handler::SendWrapper;
|
||||
use libp2p_swarm::{ConnectionHandler, ConnectionId, IntoConnectionHandler};
|
||||
use libp2p_swarm::{ConnectionHandler, IntoConnectionHandler};
|
||||
|
||||
pub mod direct;
|
||||
pub mod relayed;
|
||||
|
||||
pub enum Prototype {
|
||||
DirectConnection {
|
||||
role: Role,
|
||||
relayed_connection_id: ConnectionId,
|
||||
},
|
||||
UnknownConnection,
|
||||
}
|
||||
|
||||
pub enum Role {
|
||||
Initiator { attempt: u8 },
|
||||
Listener,
|
||||
}
|
||||
pub struct Prototype;
|
||||
|
||||
impl IntoConnectionHandler for Prototype {
|
||||
type Handler = Either<relayed::Handler, Either<direct::Handler, dummy::ConnectionHandler>>;
|
||||
type Handler = Either<relayed::Handler, direct::Handler>;
|
||||
|
||||
fn into_handler(self, _remote_peer_id: &PeerId, endpoint: &ConnectedPoint) -> Self::Handler {
|
||||
match self {
|
||||
Self::UnknownConnection => {
|
||||
if endpoint.is_relayed() {
|
||||
Either::Left(relayed::Handler::new(endpoint.clone()))
|
||||
} else {
|
||||
Either::Right(Either::Right(dummy::ConnectionHandler))
|
||||
}
|
||||
}
|
||||
Self::DirectConnection {
|
||||
relayed_connection_id,
|
||||
..
|
||||
} => {
|
||||
assert!(
|
||||
!endpoint.is_relayed(),
|
||||
"`Prototype::DirectConnection` is never created for relayed connection."
|
||||
);
|
||||
Either::Right(Either::Left(direct::Handler::new(relayed_connection_id)))
|
||||
}
|
||||
if endpoint.is_relayed() {
|
||||
Either::Left(relayed::Handler::new(endpoint.clone()))
|
||||
} else {
|
||||
Either::Right(direct::Handler::default()) // This is a direct connection. What we don't know is whether it is the one we created or another one that happened accidentally.
|
||||
}
|
||||
}
|
||||
|
||||
fn inbound_protocol(&self) -> <Self::Handler as ConnectionHandler>::InboundProtocol {
|
||||
match self {
|
||||
Prototype::UnknownConnection => {
|
||||
Either::Left(SendWrapper(Either::Left(protocol::inbound::Upgrade {})))
|
||||
}
|
||||
Prototype::DirectConnection { .. } => {
|
||||
Either::Left(SendWrapper(Either::Right(DeniedUpgrade)))
|
||||
}
|
||||
}
|
||||
Either::Left(SendWrapper(Either::Left(protocol::inbound::Upgrade {})))
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@
|
||||
use libp2p_core::upgrade::DeniedUpgrade;
|
||||
use libp2p_swarm::handler::ConnectionEvent;
|
||||
use libp2p_swarm::{
|
||||
ConnectionHandler, ConnectionHandlerEvent, ConnectionHandlerUpgrErr, ConnectionId, KeepAlive,
|
||||
ConnectionHandler, ConnectionHandlerEvent, ConnectionHandlerUpgrErr, KeepAlive,
|
||||
SubstreamProtocol,
|
||||
};
|
||||
use std::task::{Context, Poll};
|
||||
@ -31,23 +31,14 @@ use void::Void;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Event {
|
||||
DirectConnectionUpgradeSucceeded { relayed_connection_id: ConnectionId },
|
||||
DirectConnectionEstablished,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Handler {
|
||||
relayed_connection_id: ConnectionId,
|
||||
reported: bool,
|
||||
}
|
||||
|
||||
impl Handler {
|
||||
pub(crate) fn new(relayed_connection_id: ConnectionId) -> Self {
|
||||
Self {
|
||||
reported: false,
|
||||
relayed_connection_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectionHandler for Handler {
|
||||
type InEvent = void::Void;
|
||||
type OutEvent = Event;
|
||||
@ -81,9 +72,7 @@ impl ConnectionHandler for Handler {
|
||||
if !self.reported {
|
||||
self.reported = true;
|
||||
return Poll::Ready(ConnectionHandlerEvent::Custom(
|
||||
Event::DirectConnectionUpgradeSucceeded {
|
||||
relayed_connection_id: self.relayed_connection_id,
|
||||
},
|
||||
Event::DirectConnectionEstablished,
|
||||
));
|
||||
}
|
||||
Poll::Pending
|
||||
|
@ -92,7 +92,6 @@ pub enum Event {
|
||||
},
|
||||
OutboundConnectNegotiated {
|
||||
remote_addrs: Vec<Multiaddr>,
|
||||
attempt: u8,
|
||||
},
|
||||
}
|
||||
|
||||
@ -118,13 +117,9 @@ impl fmt::Debug for Event {
|
||||
.debug_struct("Event::OutboundNegotiationFailed")
|
||||
.field("error", error)
|
||||
.finish(),
|
||||
Event::OutboundConnectNegotiated {
|
||||
remote_addrs,
|
||||
attempt,
|
||||
} => f
|
||||
Event::OutboundConnectNegotiated { remote_addrs } => f
|
||||
.debug_struct("Event::OutboundConnectNegotiated")
|
||||
.field("remote_addrs", remote_addrs)
|
||||
.field("attempt", attempt)
|
||||
.finish(),
|
||||
}
|
||||
}
|
||||
@ -195,7 +190,7 @@ impl Handler {
|
||||
&mut self,
|
||||
FullyNegotiatedOutbound {
|
||||
protocol: protocol::outbound::Connect { obs_addrs },
|
||||
info: attempt,
|
||||
..
|
||||
}: FullyNegotiatedOutbound<
|
||||
<Self as ConnectionHandler>::OutboundProtocol,
|
||||
<Self as ConnectionHandler>::OutboundOpenInfo,
|
||||
@ -208,7 +203,6 @@ impl Handler {
|
||||
self.queued_events.push_back(ConnectionHandlerEvent::Custom(
|
||||
Event::OutboundConnectNegotiated {
|
||||
remote_addrs: obs_addrs,
|
||||
attempt,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
Reference in New Issue
Block a user