mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-25 15:51:34 +00:00
Add a NodeHandler trait (#495)
* Add a NodeHandler trait * Fix compilation * Some fixes
This commit is contained in:
261
core/src/nodes/handled_node.rs
Normal file
261
core/src/nodes/handled_node.rs
Normal file
@ -0,0 +1,261 @@
|
||||
// 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.
|
||||
|
||||
use muxing::StreamMuxer;
|
||||
use nodes::node::{NodeEvent, NodeStream, Substream};
|
||||
use futures::prelude::*;
|
||||
use std::io::Error as IoError;
|
||||
use Multiaddr;
|
||||
|
||||
/// Handler for the substreams of a node.
|
||||
///
|
||||
/// > Note: When implementing the various methods, don't forget that you have to register the
|
||||
/// > task that was the latest to poll and notify it.
|
||||
pub trait NodeHandler<TSubstream> {
|
||||
/// Custom event that can be received from the outside.
|
||||
type InEvent;
|
||||
/// Custom event that can be produced by the handler and that will be returned by the swarm.
|
||||
type OutEvent;
|
||||
/// Information about a substream. Can be sent to the handler through a `NodeHandlerEndpoint`,
|
||||
/// and will be passed back in `inject_substream` or `inject_outbound_closed`.
|
||||
type OutboundOpenInfo;
|
||||
|
||||
/// Sends a new substream to the handler.
|
||||
///
|
||||
/// The handler is responsible for upgrading the substream to whatever protocol it wants.
|
||||
fn inject_substream(&mut self, substream: TSubstream, endpoint: NodeHandlerEndpoint<Self::OutboundOpenInfo>);
|
||||
|
||||
/// Indicates the handler that the inbound part of the muxer has been closed, and that
|
||||
/// therefore no more inbound substream will be produced.
|
||||
fn inject_inbound_closed(&mut self);
|
||||
|
||||
/// Indicates the handler that an outbound substream failed to open because the outbound
|
||||
/// part of the muxer has been closed.
|
||||
fn inject_outbound_closed(&mut self, user_data: Self::OutboundOpenInfo);
|
||||
|
||||
/// Indicates the handler that the multiaddr future has resolved.
|
||||
fn inject_multiaddr(&mut self, multiaddr: Result<Multiaddr, IoError>);
|
||||
|
||||
/// Injects an event coming from the outside in the handler.
|
||||
fn inject_event(&mut self, event: Self::InEvent);
|
||||
|
||||
/// Indicates the node that it should shut down. After that, it is expected that `poll()`
|
||||
/// returns `Ready(None)` as soon as possible.
|
||||
///
|
||||
/// This method allows an implementation to perform a graceful shutdown of the substreams, and
|
||||
/// send back various events.
|
||||
fn shutdown(&mut self);
|
||||
|
||||
/// Should behave like `Stream::poll()`. Should close if no more event can be produced and the
|
||||
/// node should be closed.
|
||||
fn poll(&mut self) -> Poll<Option<NodeHandlerEvent<Self::OutboundOpenInfo, Self::OutEvent>>, IoError>;
|
||||
}
|
||||
|
||||
/// Endpoint for a received substream.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum NodeHandlerEndpoint<TOutboundOpenInfo> {
|
||||
Dialer(TOutboundOpenInfo),
|
||||
Listener,
|
||||
}
|
||||
|
||||
/// Event produces by a handler.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum NodeHandlerEvent<TOutboundOpenInfo, TCustom> {
|
||||
/// Require a new outbound substream to be opened with the remote.
|
||||
OutboundSubstreamRequest(TOutboundOpenInfo),
|
||||
|
||||
/// Other event.
|
||||
Custom(TCustom),
|
||||
}
|
||||
|
||||
/// Event produces by a handler.
|
||||
impl<TOutboundOpenInfo, TCustom> NodeHandlerEvent<TOutboundOpenInfo, TCustom> {
|
||||
/// If this is `OutboundSubstreamRequest`, maps the content to something else.
|
||||
#[inline]
|
||||
pub fn map_outbound_open_info<F, I>(self, map: F) -> NodeHandlerEvent<I, TCustom>
|
||||
where F: FnOnce(TOutboundOpenInfo) -> I
|
||||
{
|
||||
match self {
|
||||
NodeHandlerEvent::OutboundSubstreamRequest(val) => {
|
||||
NodeHandlerEvent::OutboundSubstreamRequest(map(val))
|
||||
},
|
||||
NodeHandlerEvent::Custom(val) => NodeHandlerEvent::Custom(val),
|
||||
}
|
||||
}
|
||||
|
||||
/// If this is `Custom`, maps the content to something else.
|
||||
#[inline]
|
||||
pub fn map_custom<F, I>(self, map: F) -> NodeHandlerEvent<TOutboundOpenInfo, I>
|
||||
where F: FnOnce(TCustom) -> I
|
||||
{
|
||||
match self {
|
||||
NodeHandlerEvent::OutboundSubstreamRequest(val) => {
|
||||
NodeHandlerEvent::OutboundSubstreamRequest(val)
|
||||
},
|
||||
NodeHandlerEvent::Custom(val) => NodeHandlerEvent::Custom(map(val)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A node combined with an implementation of `NodeHandler`.
|
||||
// TODO: impl Debug
|
||||
pub struct HandledNode<TMuxer, TAddrFut, THandler>
|
||||
where
|
||||
TMuxer: StreamMuxer,
|
||||
THandler: NodeHandler<Substream<TMuxer>>,
|
||||
{
|
||||
/// Node that handles the muxing. Can be `None` if the handled node is shutting down.
|
||||
node: Option<NodeStream<TMuxer, TAddrFut, THandler::OutboundOpenInfo>>,
|
||||
/// Handler that processes substreams.
|
||||
handler: THandler,
|
||||
}
|
||||
|
||||
impl<TMuxer, TAddrFut, THandler> HandledNode<TMuxer, TAddrFut, THandler>
|
||||
where
|
||||
TMuxer: StreamMuxer,
|
||||
THandler: NodeHandler<Substream<TMuxer>>,
|
||||
TAddrFut: Future<Item = Multiaddr, Error = IoError>,
|
||||
{
|
||||
/// Builds a new `HandledNode`.
|
||||
#[inline]
|
||||
pub fn new(muxer: TMuxer, multiaddr_future: TAddrFut, handler: THandler) -> Self {
|
||||
HandledNode {
|
||||
node: Some(NodeStream::new(muxer, multiaddr_future)),
|
||||
handler,
|
||||
}
|
||||
}
|
||||
|
||||
/// Injects an event to the handler.
|
||||
#[inline]
|
||||
pub fn inject_event(&mut self, event: THandler::InEvent) {
|
||||
self.handler.inject_event(event);
|
||||
}
|
||||
|
||||
/// Returns true if the inbound channel of the muxer is closed.
|
||||
///
|
||||
/// If `true` is returned, then no more inbound substream will be received.
|
||||
#[inline]
|
||||
pub fn is_inbound_closed(&self) -> bool {
|
||||
self.node.as_ref().map(|n| n.is_inbound_closed()).unwrap_or(true)
|
||||
}
|
||||
|
||||
/// Returns true if the outbound channel of the muxer is closed.
|
||||
///
|
||||
/// If `true` is returned, then no more outbound substream will be opened.
|
||||
#[inline]
|
||||
pub fn is_outbound_closed(&self) -> bool {
|
||||
self.node.as_ref().map(|n| n.is_outbound_closed()).unwrap_or(true)
|
||||
}
|
||||
|
||||
/// Returns true if the handled node is in the process of shutting down.
|
||||
#[inline]
|
||||
pub fn is_shutting_down(&self) -> bool {
|
||||
self.node.is_none()
|
||||
}
|
||||
|
||||
/// Indicates the handled node that it should shut down. After calling this method, the
|
||||
/// `Stream` will end in the not-so-distant future.
|
||||
///
|
||||
/// After this method returns, `is_shutting_down()` should return true.
|
||||
pub fn shutdown(&mut self) {
|
||||
if let Some(node) = self.node.take() {
|
||||
for user_data in node.close() {
|
||||
self.handler.inject_outbound_closed(user_data);
|
||||
}
|
||||
}
|
||||
|
||||
self.handler.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
impl<TMuxer, TAddrFut, THandler> Stream for HandledNode<TMuxer, TAddrFut, THandler>
|
||||
where
|
||||
TMuxer: StreamMuxer,
|
||||
THandler: NodeHandler<Substream<TMuxer>>,
|
||||
TAddrFut: Future<Item = Multiaddr, Error = IoError>,
|
||||
{
|
||||
type Item = THandler::OutEvent;
|
||||
type Error = IoError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
// We extract the value from `self.node` and put it back in place if `NotReady`.
|
||||
if let Some(mut node) = self.node.take() {
|
||||
loop {
|
||||
match node.poll() {
|
||||
Ok(Async::NotReady) => {
|
||||
self.node = Some(node);
|
||||
break;
|
||||
},
|
||||
Ok(Async::Ready(Some(NodeEvent::InboundSubstream { substream }))) => {
|
||||
self.handler.inject_substream(substream, NodeHandlerEndpoint::Listener);
|
||||
},
|
||||
Ok(Async::Ready(Some(NodeEvent::OutboundSubstream { user_data, substream }))) => {
|
||||
let endpoint = NodeHandlerEndpoint::Dialer(user_data);
|
||||
self.handler.inject_substream(substream, endpoint);
|
||||
},
|
||||
Ok(Async::Ready(None)) => {
|
||||
// Breaking from the loop without putting back the node.
|
||||
break;
|
||||
},
|
||||
Ok(Async::Ready(Some(NodeEvent::Multiaddr(result)))) => {
|
||||
self.handler.inject_multiaddr(result);
|
||||
},
|
||||
Ok(Async::Ready(Some(NodeEvent::OutboundClosed { user_data }))) => {
|
||||
self.handler.inject_outbound_closed(user_data);
|
||||
},
|
||||
Ok(Async::Ready(Some(NodeEvent::InboundClosed))) => {
|
||||
self.handler.inject_inbound_closed();
|
||||
},
|
||||
Err(err) => {
|
||||
// Breaking from the loop without putting back the node.
|
||||
return Err(err);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
match self.handler.poll() {
|
||||
Ok(Async::NotReady) => break,
|
||||
Ok(Async::Ready(Some(NodeHandlerEvent::OutboundSubstreamRequest(user_data)))) => {
|
||||
if let Some(node) = self.node.as_mut() {
|
||||
match node.open_substream(user_data) {
|
||||
Ok(()) => (),
|
||||
Err(user_data) => self.handler.inject_outbound_closed(user_data),
|
||||
}
|
||||
} else {
|
||||
self.handler.inject_outbound_closed(user_data);
|
||||
}
|
||||
},
|
||||
Ok(Async::Ready(Some(NodeHandlerEvent::Custom(event)))) => {
|
||||
return Ok(Async::Ready(Some(event)));
|
||||
},
|
||||
Ok(Async::Ready(None)) => {
|
||||
return Ok(Async::Ready(None));
|
||||
},
|
||||
Err(err) => {
|
||||
return Err(err);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Async::NotReady)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user