feat(swarm)!: allow NetworkBehaviours to manage connections

Previously, a `ConnectionHandler` was immediately requested from the `NetworkBehaviour` as soon as a new dial was initiated or a new incoming connection accepted.

With this patch, we delay the creation of the handler until the connection is actually established and fully upgraded, i.e authenticated and multiplexed.

As a consequence, `NetworkBehaviour::new_handler` is now deprecated in favor of a new set of callbacks:

- `NetworkBehaviour::handle_pending_inbound_connection`
- `NetworkBehaviour::handle_pending_outbound_connection`
- `NetworkBehaviour::handle_established_inbound_connection`
- `NetworkBehaviour::handle_established_outbound_connection`

All callbacks are fallible, allowing the `NetworkBehaviour` to abort the connection either immediately or after it is fully established. All callbacks also receive a `ConnectionId` parameter which uniquely identifies the connection. For example, in case a `NetworkBehaviour` issues a dial via `NetworkBehaviourAction::Dial`, it can unambiguously detect this dial in these lifecycle callbacks via the `ConnectionId`.

Finally, `NetworkBehaviour::handle_pending_outbound_connection` also replaces `NetworkBehaviour::addresses_of_peer` by allowing the behaviour to return more addresses to be used for the dial.

Resolves #2824.

Pull-Request: #3254.
This commit is contained in:
Thomas Eizinger
2023-02-24 10:43:33 +11:00
committed by GitHub
parent 794b2a23d0
commit 19a554965f
42 changed files with 1543 additions and 540 deletions

View File

@ -28,11 +28,13 @@ pub use listen_addresses::ListenAddresses;
use crate::connection::ConnectionId;
use crate::dial_opts::DialOpts;
#[allow(deprecated)]
use crate::handler::IntoConnectionHandler;
use crate::{
AddressRecord, AddressScore, DialError, ListenError, THandlerInEvent, THandlerOutEvent,
AddressRecord, AddressScore, ConnectionDenied, DialError, ListenError, THandler,
THandlerInEvent, THandlerOutEvent,
};
use libp2p_core::{transport::ListenerId, ConnectedPoint, Multiaddr, PeerId};
use libp2p_core::{transport::ListenerId, ConnectedPoint, Endpoint, Multiaddr, PeerId};
use std::{task::Context, task::Poll};
/// A [`NetworkBehaviour`] defines the behaviour of the local node on the network.
@ -119,6 +121,7 @@ use std::{task::Context, task::Poll};
/// ```
pub trait NetworkBehaviour: 'static {
/// Handler for all the protocols the network behaviour supports.
#[allow(deprecated)]
type ConnectionHandler: IntoConnectionHandler;
/// Event generated by the `NetworkBehaviour` and that the swarm will report back.
@ -141,7 +144,102 @@ pub trait NetworkBehaviour: 'static {
///
/// Note that the handler is returned to the [`NetworkBehaviour`] on connection failure and
/// connection closing.
fn new_handler(&mut self) -> Self::ConnectionHandler;
#[deprecated(
since = "0.42.0",
note = "Use one or more of `NetworkBehaviour::{handle_pending_inbound_connection,handle_established_inbound_connection,handle_pending_outbound_connection,handle_established_outbound_connection}` instead."
)]
fn new_handler(&mut self) -> Self::ConnectionHandler {
panic!("You must implement `handle_established_inbound_connection` and `handle_established_outbound_connection`.")
}
/// Callback that is invoked for every new inbound connection.
///
/// At this point in the connection lifecycle, only the remote's and our local address are known.
/// We have also already allocated a [`ConnectionId`].
///
/// Any error returned from this function will immediately abort the dial attempt.
fn handle_pending_inbound_connection(
&mut self,
_connection_id: ConnectionId,
_local_addr: &Multiaddr,
_remote_addr: &Multiaddr,
) -> Result<(), ConnectionDenied> {
Ok(())
}
/// Callback that is invoked for every established inbound connection.
///
/// This is invoked once another peer has successfully dialed us.
///
/// At this point, we have verified their [`PeerId`] and we know, which particular [`Multiaddr`] succeeded in the dial.
/// In order to actually use this connection, this function must return a [`ConnectionHandler`](crate::ConnectionHandler).
/// Returning an error will immediately close the connection.
fn handle_established_inbound_connection(
&mut self,
_connection_id: ConnectionId,
peer: PeerId,
local_addr: &Multiaddr,
remote_addr: &Multiaddr,
) -> Result<THandler<Self>, ConnectionDenied> {
#[allow(deprecated)]
Ok(self.new_handler().into_handler(
&peer,
&ConnectedPoint::Listener {
local_addr: local_addr.clone(),
send_back_addr: remote_addr.clone(),
},
))
}
/// Callback that is invoked for every outbound connection attempt.
///
/// We have access to:
///
/// - The [`PeerId`], if known. Remember that we can dial without a [`PeerId`].
/// - All addresses passed to [`DialOpts`] are passed in here too.
/// - The effective [`Role`](Endpoint) of this peer in the dial attempt. Typically, this is set to [`Endpoint::Dialer`] except if we are attempting a hole-punch.
/// - The [`ConnectionId`] identifying the future connection resulting from this dial, if successful.
///
/// Note that the addresses returned from this function are only used for dialing if [`WithPeerIdWithAddresses::extend_addresses_through_behaviour`](crate::dial_opts::WithPeerIdWithAddresses::extend_addresses_through_behaviour) is set.
///
/// Any error returned from this function will immediately abort the dial attempt.
fn handle_pending_outbound_connection(
&mut self,
_connection_id: ConnectionId,
maybe_peer: Option<PeerId>,
_addresses: &[Multiaddr],
_effective_role: Endpoint,
) -> Result<Vec<Multiaddr>, ConnectionDenied> {
#[allow(deprecated)]
if let Some(peer_id) = maybe_peer {
Ok(self.addresses_of_peer(&peer_id))
} else {
Ok(vec![])
}
}
/// Callback that is invoked for every established outbound connection.
///
/// This is invoked once we have successfully dialed a peer.
/// At this point, we have verified their [`PeerId`] and we know, which particular [`Multiaddr`] succeeded in the dial.
/// In order to actually use this connection, this function must return a [`ConnectionHandler`](crate::ConnectionHandler).
/// Returning an error will immediately close the connection.
fn handle_established_outbound_connection(
&mut self,
_connection_id: ConnectionId,
peer: PeerId,
addr: &Multiaddr,
role_override: Endpoint,
) -> Result<THandler<Self>, ConnectionDenied> {
#[allow(deprecated)]
Ok(self.new_handler().into_handler(
&peer,
&ConnectedPoint::Dialer {
address: addr.clone(),
role_override,
},
))
}
/// Addresses that this behaviour is aware of for this specific peer, and that may allow
/// reaching the peer.
@ -149,6 +247,7 @@ pub trait NetworkBehaviour: 'static {
/// The addresses will be tried in the order returned by this function, which means that they
/// should be ordered by decreasing likelihood of reachability. In other words, the first
/// address should be the most likely to be reachable.
#[deprecated(note = "Use `NetworkBehaviour::handle_pending_outbound_connection` instead.")]
fn addresses_of_peer(&mut self, _: &PeerId) -> Vec<Multiaddr> {
vec![]
}
@ -380,6 +479,7 @@ pub enum CloseConnection {
/// Enumeration with the list of the possible events
/// to pass to [`on_swarm_event`](NetworkBehaviour::on_swarm_event).
#[allow(deprecated)]
pub enum FromSwarm<'a, Handler: IntoConnectionHandler> {
/// Informs the behaviour about a newly established connection to a peer.
ConnectionEstablished(ConnectionEstablished<'a>),
@ -434,6 +534,7 @@ pub struct ConnectionEstablished<'a> {
/// This event is always paired with an earlier
/// [`FromSwarm::ConnectionEstablished`] with the same peer ID, connection ID
/// and endpoint.
#[allow(deprecated)]
pub struct ConnectionClosed<'a, Handler: IntoConnectionHandler> {
pub peer_id: PeerId,
pub connection_id: ConnectionId,
@ -524,6 +625,7 @@ pub struct ExpiredExternalAddr<'a> {
pub addr: &'a Multiaddr,
}
#[allow(deprecated)]
impl<'a, Handler: IntoConnectionHandler> FromSwarm<'a, Handler> {
fn map_handler<NewHandler>(
self,