From ab7b5a457473ad7fdcb0f4f9c6d003af82b3e50e Mon Sep 17 00:00:00 2001 From: Benoit Verkindt Date: Sun, 12 Dec 2021 03:51:02 -0800 Subject: [PATCH] swarm/src/either: Implement `NetworkBehaviour` on `Either` (#2370) Implement `NetworkBehaviour` on `either::Either` where both L and R both implement `NetworkBehaviour`. Add NetworkBehaviour derive tests for Either and Toggle Co-authored-by: Max Inden --- swarm-derive/Cargo.toml | 1 + swarm-derive/tests/test.rs | 95 +++++++++++++ swarm/CHANGELOG.md | 3 + swarm/src/behaviour.rs | 45 ++++++ swarm/src/behaviour/either.rs | 248 ++++++++++++++++++++++++++++++++++ 5 files changed, 392 insertions(+) create mode 100644 swarm/src/behaviour/either.rs diff --git a/swarm-derive/Cargo.toml b/swarm-derive/Cargo.toml index e562859b..c53240ad 100644 --- a/swarm-derive/Cargo.toml +++ b/swarm-derive/Cargo.toml @@ -19,4 +19,5 @@ quote = "1.0" [dev-dependencies] libp2p = { path = "../" } +either = "1.6.0" futures = "0.3.1" diff --git a/swarm-derive/tests/test.rs b/swarm-derive/tests/test.rs index 829c9b71..c5182b04 100644 --- a/swarm-derive/tests/test.rs +++ b/swarm-derive/tests/test.rs @@ -324,3 +324,98 @@ fn event_process_false() { }; } } + +#[test] +fn with_toggle() { + use libp2p::swarm::behaviour::toggle::Toggle; + + #[allow(dead_code)] + #[derive(NetworkBehaviour)] + #[behaviour(event_process = true)] + struct Foo { + identify: libp2p::identify::Identify, + ping: Toggle, + } + + impl libp2p::swarm::NetworkBehaviourEventProcess for Foo { + fn inject_event(&mut self, _: libp2p::identify::IdentifyEvent) {} + } + + impl libp2p::swarm::NetworkBehaviourEventProcess for Foo { + fn inject_event(&mut self, _: libp2p::ping::PingEvent) {} + } + + #[allow(dead_code)] + fn foo() { + require_net_behaviour::(); + } +} + +#[test] +fn with_either() { + use either::Either; + + #[allow(dead_code)] + #[derive(NetworkBehaviour)] + #[behaviour(event_process = true)] + struct Foo { + kad: libp2p::kad::Kademlia, + ping_or_identify: Either, + } + + impl libp2p::swarm::NetworkBehaviourEventProcess for Foo { + fn inject_event(&mut self, _: libp2p::kad::KademliaEvent) {} + } + + impl + libp2p::swarm::NetworkBehaviourEventProcess< + Either, + > for Foo + { + fn inject_event( + &mut self, + _: Either, + ) { + } + } + + #[allow(dead_code)] + fn foo() { + require_net_behaviour::(); + } +} + +#[test] +fn no_event_with_either() { + use either::Either; + + enum BehaviourOutEvent { + Kad(libp2p::kad::KademliaEvent), + PingOrIdentify(Either), + } + + #[allow(dead_code)] + #[derive(NetworkBehaviour)] + #[behaviour(out_event = "BehaviourOutEvent", event_process = false)] + struct Foo { + kad: libp2p::kad::Kademlia, + ping_or_identify: Either, + } + + impl From for BehaviourOutEvent { + fn from(event: libp2p::kad::KademliaEvent) -> Self { + BehaviourOutEvent::Kad(event) + } + } + + impl From> for BehaviourOutEvent { + fn from(event: Either) -> Self { + BehaviourOutEvent::PingOrIdentify(event) + } + } + + #[allow(dead_code)] + fn foo() { + require_net_behaviour::(); + } +} diff --git a/swarm/CHANGELOG.md b/swarm/CHANGELOG.md index 2e524f91..867d07a6 100644 --- a/swarm/CHANGELOG.md +++ b/swarm/CHANGELOG.md @@ -10,9 +10,12 @@ - Move `swarm::Toggle` to `swarm::behaviour::Toggle` (see [PR 2375]). +- Implement `swarm::NetworkBehaviour` on `either::Either` (see [PR 2370]). + [PR 2339]: https://github.com/libp2p/rust-libp2p/pull/2339 [PR 2350]: https://github.com/libp2p/rust-libp2p/pull/2350 [PR 2362]: https://github.com/libp2p/rust-libp2p/pull/2362 +[PR 2370]: https://github.com/libp2p/rust-libp2p/pull/2370 [PR 2375]: https://github.com/libp2p/rust-libp2p/pull/2375 # 0.32.0 [2021-11-16] diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index cdab3f0c..269bddf3 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -18,6 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +pub mod either; pub mod toggle; use crate::dial_opts::DialOpts; @@ -718,6 +719,50 @@ where } } +impl NetworkBehaviourAction +where + THandlerOld: IntoProtocolsHandler, + ::Handler: ProtocolsHandler, +{ + /// Map the handler and handler event. + pub fn map_handler_and_in( + self, + f_handler: impl FnOnce(THandlerOld) -> THandlerNew, + f_in_event: impl FnOnce(TInEventOld) -> TInEventNew, + ) -> NetworkBehaviourAction + where + THandlerNew: IntoProtocolsHandler, + ::Handler: ProtocolsHandler, + { + match self { + NetworkBehaviourAction::GenerateEvent(e) => NetworkBehaviourAction::GenerateEvent(e), + NetworkBehaviourAction::Dial { opts, handler } => NetworkBehaviourAction::Dial { + opts, + handler: f_handler(handler), + }, + NetworkBehaviourAction::NotifyHandler { + peer_id, + handler, + event, + } => NetworkBehaviourAction::NotifyHandler { + peer_id, + handler, + event: f_in_event(event), + }, + NetworkBehaviourAction::ReportObservedAddr { address, score } => { + NetworkBehaviourAction::ReportObservedAddr { address, score } + } + NetworkBehaviourAction::CloseConnection { + peer_id, + connection, + } => NetworkBehaviourAction::CloseConnection { + peer_id, + connection, + }, + } + } +} + /// The options w.r.t. which connection handler to notify of an event. #[derive(Debug, Clone)] pub enum NotifyHandler { diff --git a/swarm/src/behaviour/either.rs b/swarm/src/behaviour/either.rs new file mode 100644 index 00000000..3dd6d28a --- /dev/null +++ b/swarm/src/behaviour/either.rs @@ -0,0 +1,248 @@ +// 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. + +use crate::protocols_handler::{either::IntoEitherHandler, IntoProtocolsHandler, ProtocolsHandler}; +use crate::{ + DialError, NetworkBehaviour, NetworkBehaviourAction, NetworkBehaviourEventProcess, + PollParameters, +}; +use either::Either; +use libp2p_core::{ + connection::{ConnectionId, ListenerId}, + ConnectedPoint, Multiaddr, PeerId, +}; +use std::{task::Context, task::Poll}; + +/// Implementation of [`NetworkBehaviour`] that can be either of two implementations. +impl NetworkBehaviour for Either +where + L: NetworkBehaviour, + R: NetworkBehaviour, +{ + type ProtocolsHandler = IntoEitherHandler; + type OutEvent = Either; + + fn new_handler(&mut self) -> Self::ProtocolsHandler { + match self { + Either::Left(a) => IntoEitherHandler::Left(a.new_handler()), + Either::Right(b) => IntoEitherHandler::Right(b.new_handler()), + } + } + + fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec { + match self { + Either::Left(a) => a.addresses_of_peer(peer_id), + Either::Right(b) => b.addresses_of_peer(peer_id), + } + } + + fn inject_connected(&mut self, peer_id: &PeerId) { + match self { + Either::Left(a) => a.inject_connected(peer_id), + Either::Right(b) => b.inject_connected(peer_id), + }; + } + + fn inject_disconnected(&mut self, peer_id: &PeerId) { + match self { + Either::Left(a) => a.inject_disconnected(peer_id), + Either::Right(b) => b.inject_disconnected(peer_id), + } + } + + fn inject_connection_established( + &mut self, + peer_id: &PeerId, + connection: &ConnectionId, + endpoint: &ConnectedPoint, + errors: Option<&Vec>, + ) { + match self { + Either::Left(a) => { + a.inject_connection_established(peer_id, connection, endpoint, errors) + } + Either::Right(b) => { + b.inject_connection_established(peer_id, connection, endpoint, errors) + } + } + } + + fn inject_connection_closed( + &mut self, + peer_id: &PeerId, + connection: &ConnectionId, + endpoint: &ConnectedPoint, + handler: ::Handler, + ) { + match (self, handler) { + (Either::Left(behaviour), Either::Left(handler)) => { + behaviour.inject_connection_closed(peer_id, connection, endpoint, handler) + } + (Either::Right(behaviour), Either::Right(handler)) => { + behaviour.inject_connection_closed(peer_id, connection, endpoint, handler) + } + _ => unreachable!(), + } + } + + fn inject_address_change( + &mut self, + peer_id: &PeerId, + connection: &ConnectionId, + old: &ConnectedPoint, + new: &ConnectedPoint, + ) { + match self { + Either::Left(a) => a.inject_address_change(peer_id, connection, old, new), + Either::Right(b) => b.inject_address_change(peer_id, connection, old, new), + } + } + + fn inject_event( + &mut self, + peer_id: PeerId, + connection: ConnectionId, + event: <::Handler as ProtocolsHandler>::OutEvent, + ) { + match (self, event) { + (Either::Left(behaviour), Either::Left(event)) => { + behaviour.inject_event(peer_id, connection, event) + } + (Either::Right(behaviour), Either::Right(event)) => { + behaviour.inject_event(peer_id, connection, event) + } + _ => unreachable!(), + } + } + + fn inject_dial_failure( + &mut self, + peer_id: Option, + handler: Self::ProtocolsHandler, + error: &DialError, + ) { + match (self, handler) { + (Either::Left(behaviour), IntoEitherHandler::Left(handler)) => { + behaviour.inject_dial_failure(peer_id, handler, error) + } + (Either::Right(behaviour), IntoEitherHandler::Right(handler)) => { + behaviour.inject_dial_failure(peer_id, handler, error) + } + _ => unreachable!(), + } + } + + fn inject_listen_failure( + &mut self, + local_addr: &Multiaddr, + send_back_addr: &Multiaddr, + handler: Self::ProtocolsHandler, + ) { + match (self, handler) { + (Either::Left(behaviour), IntoEitherHandler::Left(handler)) => { + behaviour.inject_listen_failure(local_addr, send_back_addr, handler) + } + (Either::Right(behaviour), IntoEitherHandler::Right(handler)) => { + behaviour.inject_listen_failure(local_addr, send_back_addr, handler) + } + _ => unreachable!(), + } + } + + fn inject_new_listener(&mut self, id: ListenerId) { + match self { + Either::Left(a) => a.inject_new_listener(id), + Either::Right(b) => b.inject_new_listener(id), + } + } + + fn inject_new_listen_addr(&mut self, id: ListenerId, addr: &Multiaddr) { + match self { + Either::Left(a) => a.inject_new_listen_addr(id, addr), + Either::Right(b) => b.inject_new_listen_addr(id, addr), + } + } + + fn inject_expired_listen_addr(&mut self, id: ListenerId, addr: &Multiaddr) { + match self { + Either::Left(a) => a.inject_expired_listen_addr(id, addr), + Either::Right(b) => b.inject_expired_listen_addr(id, addr), + } + } + + fn inject_new_external_addr(&mut self, addr: &Multiaddr) { + match self { + Either::Left(a) => a.inject_new_external_addr(addr), + Either::Right(b) => b.inject_new_external_addr(addr), + } + } + + fn inject_expired_external_addr(&mut self, addr: &Multiaddr) { + match self { + Either::Left(a) => a.inject_expired_external_addr(addr), + Either::Right(b) => b.inject_expired_external_addr(addr), + } + } + + fn inject_listener_error(&mut self, id: ListenerId, err: &(dyn std::error::Error + 'static)) { + match self { + Either::Left(a) => a.inject_listener_error(id, err), + Either::Right(b) => b.inject_listener_error(id, err), + } + } + + fn inject_listener_closed(&mut self, id: ListenerId, reason: Result<(), &std::io::Error>) { + match self { + Either::Left(a) => a.inject_listener_closed(id, reason), + Either::Right(b) => b.inject_listener_closed(id, reason), + } + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + params: &mut impl PollParameters, + ) -> Poll> { + let event = match self { + Either::Left(behaviour) => futures::ready!(behaviour.poll(cx, params)) + .map_out(|e| Either::Left(e)) + .map_handler_and_in(|h| IntoEitherHandler::Left(h), |e| Either::Left(e)), + Either::Right(behaviour) => futures::ready!(behaviour.poll(cx, params)) + .map_out(|e| Either::Right(e)) + .map_handler_and_in(|h| IntoEitherHandler::Right(h), |e| Either::Right(e)), + }; + + Poll::Ready(event) + } +} + +impl NetworkBehaviourEventProcess + for Either +where + TBehaviourLeft: NetworkBehaviourEventProcess, + TBehaviourRight: NetworkBehaviourEventProcess, +{ + fn inject_event(&mut self, event: TEvent) { + match self { + Either::Left(a) => a.inject_event(event), + Either::Right(b) => b.inject_event(event), + } + } +}