2018-09-19 16:33:29 +02:00
|
|
|
// 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};
|
2018-10-15 10:42:11 +02:00
|
|
|
use futures::{prelude::*, stream::Fuse};
|
2018-09-19 16:33:29 +02:00
|
|
|
use std::io::Error as IoError;
|
|
|
|
|
|
|
|
/// Handler for the substreams of a node.
|
2018-10-01 14:49:17 +02:00
|
|
|
// TODO: right now it is possible for a node handler to be built, then shut down right after if we
|
|
|
|
// realize we dialed the wrong peer for example ; this could be surprising and should either
|
|
|
|
// be documented or changed (favouring the "documented" right now)
|
2018-10-17 10:17:40 +01:00
|
|
|
pub trait NodeHandler {
|
2018-09-19 16:33:29 +02:00
|
|
|
/// 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;
|
2018-10-17 10:17:40 +01:00
|
|
|
/// The type of the substream containing the data.
|
|
|
|
type Substream;
|
2018-09-19 16:33:29 +02:00
|
|
|
/// 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.
|
2018-10-17 10:17:40 +01:00
|
|
|
fn inject_substream(&mut self, substream: Self::Substream, endpoint: NodeHandlerEndpoint<Self::OutboundOpenInfo>);
|
2018-09-19 16:33:29 +02:00
|
|
|
|
2018-10-10 16:27:07 +02:00
|
|
|
/// Indicates to the handler that the inbound part of the muxer has been closed, and that
|
2018-09-19 16:33:29 +02:00
|
|
|
/// therefore no more inbound substream will be produced.
|
|
|
|
fn inject_inbound_closed(&mut self);
|
|
|
|
|
2018-10-10 16:27:07 +02:00
|
|
|
/// Indicates to the handler that an outbound substream failed to open because the outbound
|
2018-09-19 16:33:29 +02:00
|
|
|
/// part of the muxer has been closed.
|
|
|
|
fn inject_outbound_closed(&mut self, user_data: Self::OutboundOpenInfo);
|
|
|
|
|
2018-10-10 16:27:07 +02:00
|
|
|
/// Injects an event coming from the outside into the handler.
|
2018-09-19 16:33:29 +02:00
|
|
|
fn inject_event(&mut self, event: Self::InEvent);
|
|
|
|
|
2018-10-10 16:27:07 +02:00
|
|
|
/// Indicates that the node that it should shut down. After that, it is expected that `poll()`
|
2018-09-19 16:33:29 +02:00
|
|
|
/// 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,
|
|
|
|
}
|
|
|
|
|
2018-10-17 10:17:40 +01:00
|
|
|
impl<TOutboundOpenInfo> NodeHandlerEndpoint<TOutboundOpenInfo> {
|
|
|
|
/// Returns true for `Dialer`.
|
|
|
|
#[inline]
|
|
|
|
pub fn is_dialer(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
NodeHandlerEndpoint::Dialer(_) => true,
|
|
|
|
NodeHandlerEndpoint::Listener => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns true for `Listener`.
|
|
|
|
#[inline]
|
|
|
|
pub fn is_listener(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
NodeHandlerEndpoint::Dialer(_) => false,
|
|
|
|
NodeHandlerEndpoint::Listener => true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-10 16:27:07 +02:00
|
|
|
/// Event produced by a handler.
|
2018-09-19 16:33:29 +02:00
|
|
|
#[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),
|
|
|
|
}
|
|
|
|
|
2018-10-10 16:27:07 +02:00
|
|
|
/// Event produced by a handler.
|
2018-09-19 16:33:29 +02:00
|
|
|
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
|
2018-10-17 10:17:40 +01:00
|
|
|
pub struct HandledNode<TMuxer, THandler>
|
2018-09-19 16:33:29 +02:00
|
|
|
where
|
|
|
|
TMuxer: StreamMuxer,
|
2018-10-17 10:17:40 +01:00
|
|
|
THandler: NodeHandler<Substream = Substream<TMuxer>>,
|
2018-09-19 16:33:29 +02:00
|
|
|
{
|
2018-10-15 10:42:11 +02:00
|
|
|
/// Node that handles the muxing.
|
2018-10-17 10:17:40 +01:00
|
|
|
node: Fuse<NodeStream<TMuxer, THandler::OutboundOpenInfo>>,
|
2018-09-19 16:33:29 +02:00
|
|
|
/// Handler that processes substreams.
|
|
|
|
handler: THandler,
|
2018-10-20 13:12:05 +02:00
|
|
|
/// If true, `handler` has returned `Ready(None)` and therefore shouldn't be polled again.
|
|
|
|
handler_is_done: bool,
|
2018-10-15 10:42:11 +02:00
|
|
|
// True, if the node is shutting down.
|
|
|
|
is_shutting_down: bool
|
2018-09-19 16:33:29 +02:00
|
|
|
}
|
|
|
|
|
2018-10-17 10:17:40 +01:00
|
|
|
impl<TMuxer, THandler> HandledNode<TMuxer, THandler>
|
2018-09-19 16:33:29 +02:00
|
|
|
where
|
|
|
|
TMuxer: StreamMuxer,
|
2018-10-17 10:17:40 +01:00
|
|
|
THandler: NodeHandler<Substream = Substream<TMuxer>>,
|
2018-09-19 16:33:29 +02:00
|
|
|
{
|
|
|
|
/// Builds a new `HandledNode`.
|
|
|
|
#[inline]
|
2018-10-17 10:17:40 +01:00
|
|
|
pub fn new(muxer: TMuxer, handler: THandler) -> Self {
|
2018-09-19 16:33:29 +02:00
|
|
|
HandledNode {
|
2018-10-17 10:17:40 +01:00
|
|
|
node: NodeStream::new(muxer).fuse(),
|
2018-09-19 16:33:29 +02:00
|
|
|
handler,
|
2018-10-20 13:12:05 +02:00
|
|
|
handler_is_done: false,
|
2018-10-15 10:42:11 +02:00
|
|
|
is_shutting_down: false
|
2018-09-19 16:33:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Injects an event to the handler.
|
|
|
|
#[inline]
|
|
|
|
pub fn inject_event(&mut self, event: THandler::InEvent) {
|
|
|
|
self.handler.inject_event(event);
|
|
|
|
}
|
|
|
|
|
2018-10-15 10:42:11 +02:00
|
|
|
/// Returns true if the inbound channel of the muxer is open.
|
2018-09-19 16:33:29 +02:00
|
|
|
///
|
2018-10-15 10:42:11 +02:00
|
|
|
/// If `true` is returned, more inbound substream will be received.
|
2018-09-19 16:33:29 +02:00
|
|
|
#[inline]
|
2018-10-15 10:42:11 +02:00
|
|
|
pub fn is_inbound_open(&self) -> bool {
|
|
|
|
self.node.get_ref().is_inbound_open()
|
2018-09-19 16:33:29 +02:00
|
|
|
}
|
|
|
|
|
2018-10-15 10:42:11 +02:00
|
|
|
/// Returns true if the outbound channel of the muxer is open.
|
2018-09-19 16:33:29 +02:00
|
|
|
///
|
2018-10-15 10:42:11 +02:00
|
|
|
/// If `true` is returned, more outbound substream will be opened.
|
2018-09-19 16:33:29 +02:00
|
|
|
#[inline]
|
2018-10-15 10:42:11 +02:00
|
|
|
pub fn is_outbound_open(&self) -> bool {
|
|
|
|
self.node.get_ref().is_outbound_open()
|
2018-09-19 16:33:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns true if the handled node is in the process of shutting down.
|
|
|
|
#[inline]
|
|
|
|
pub fn is_shutting_down(&self) -> bool {
|
2018-10-15 10:42:11 +02:00
|
|
|
self.is_shutting_down
|
2018-09-19 16:33:29 +02:00
|
|
|
}
|
|
|
|
|
2018-10-10 16:27:07 +02:00
|
|
|
/// Indicates to the handled node that it should shut down. After calling this method, the
|
2018-09-19 16:33:29 +02:00
|
|
|
/// `Stream` will end in the not-so-distant future.
|
|
|
|
///
|
|
|
|
/// After this method returns, `is_shutting_down()` should return true.
|
|
|
|
pub fn shutdown(&mut self) {
|
2018-10-15 10:42:11 +02:00
|
|
|
self.node.get_mut().shutdown_all();
|
|
|
|
for user_data in self.node.get_mut().cancel_outgoing() {
|
|
|
|
self.handler.inject_outbound_closed(user_data);
|
2018-09-19 16:33:29 +02:00
|
|
|
}
|
2018-10-20 13:12:05 +02:00
|
|
|
self.handler.shutdown();
|
|
|
|
self.is_shutting_down = true;
|
2018-09-19 16:33:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-17 10:17:40 +01:00
|
|
|
impl<TMuxer, THandler> Stream for HandledNode<TMuxer, THandler>
|
2018-09-19 16:33:29 +02:00
|
|
|
where
|
|
|
|
TMuxer: StreamMuxer,
|
2018-10-17 10:17:40 +01:00
|
|
|
THandler: NodeHandler<Substream = Substream<TMuxer>>,
|
2018-09-19 16:33:29 +02:00
|
|
|
{
|
|
|
|
type Item = THandler::OutEvent;
|
|
|
|
type Error = IoError;
|
|
|
|
|
|
|
|
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
2018-10-01 14:24:01 +02:00
|
|
|
loop {
|
2018-10-20 13:12:05 +02:00
|
|
|
if self.node.is_done() && self.handler_is_done {
|
|
|
|
return Ok(Async::Ready(None));
|
|
|
|
}
|
|
|
|
|
2018-10-01 14:24:01 +02:00
|
|
|
let mut node_not_ready = false;
|
|
|
|
|
2018-10-15 10:42:11 +02:00
|
|
|
match self.node.poll()? {
|
2018-10-20 13:12:05 +02:00
|
|
|
Async::NotReady => node_not_ready = true,
|
2018-10-15 10:42:11 +02:00
|
|
|
Async::Ready(Some(NodeEvent::InboundSubstream { substream })) => {
|
|
|
|
self.handler.inject_substream(substream, NodeHandlerEndpoint::Listener)
|
|
|
|
}
|
|
|
|
Async::Ready(Some(NodeEvent::OutboundSubstream { user_data, substream })) => {
|
2018-10-01 14:24:01 +02:00
|
|
|
let endpoint = NodeHandlerEndpoint::Dialer(user_data);
|
2018-10-15 10:42:11 +02:00
|
|
|
self.handler.inject_substream(substream, endpoint)
|
|
|
|
}
|
|
|
|
Async::Ready(None) => {
|
|
|
|
if !self.is_shutting_down {
|
2018-10-20 13:12:05 +02:00
|
|
|
self.is_shutting_down = true;
|
2018-10-15 10:42:11 +02:00
|
|
|
self.handler.shutdown()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Async::Ready(Some(NodeEvent::OutboundClosed { user_data })) => {
|
|
|
|
self.handler.inject_outbound_closed(user_data)
|
|
|
|
}
|
|
|
|
Async::Ready(Some(NodeEvent::InboundClosed)) => {
|
|
|
|
self.handler.inject_inbound_closed()
|
|
|
|
}
|
2018-09-19 16:33:29 +02:00
|
|
|
}
|
|
|
|
|
2018-10-20 13:12:05 +02:00
|
|
|
match if self.handler_is_done { Async::Ready(None) } else { self.handler.poll()? } {
|
2018-10-15 10:42:11 +02:00
|
|
|
Async::NotReady => {
|
2018-10-01 14:24:01 +02:00
|
|
|
if node_not_ready {
|
2018-10-15 10:42:11 +02:00
|
|
|
break
|
2018-10-01 14:24:01 +02:00
|
|
|
}
|
2018-10-15 10:42:11 +02:00
|
|
|
}
|
|
|
|
Async::Ready(Some(NodeHandlerEvent::OutboundSubstreamRequest(user_data))) => {
|
|
|
|
if self.node.get_ref().is_outbound_open() {
|
|
|
|
match self.node.get_mut().open_substream(user_data) {
|
2018-09-19 16:33:29 +02:00
|
|
|
Ok(()) => (),
|
|
|
|
Err(user_data) => self.handler.inject_outbound_closed(user_data),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
self.handler.inject_outbound_closed(user_data);
|
|
|
|
}
|
2018-10-15 10:42:11 +02:00
|
|
|
}
|
|
|
|
Async::Ready(Some(NodeHandlerEvent::Custom(event))) => {
|
2018-09-19 16:33:29 +02:00
|
|
|
return Ok(Async::Ready(Some(event)));
|
2018-10-15 10:42:11 +02:00
|
|
|
}
|
|
|
|
Async::Ready(None) => {
|
2018-10-20 13:12:05 +02:00
|
|
|
self.handler_is_done = true;
|
|
|
|
if !self.is_shutting_down {
|
|
|
|
self.is_shutting_down = true;
|
|
|
|
self.node.get_mut().cancel_outgoing();
|
|
|
|
self.node.get_mut().shutdown_all();
|
|
|
|
}
|
2018-10-15 10:42:11 +02:00
|
|
|
}
|
2018-09-19 16:33:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Async::NotReady)
|
|
|
|
}
|
|
|
|
}
|
2018-10-01 11:17:34 +02:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2018-10-11 10:35:14 +02:00
|
|
|
use muxing::{StreamMuxer, Shutdown};
|
2018-10-17 10:17:40 +01:00
|
|
|
use std::marker::PhantomData;
|
2018-10-01 11:17:34 +02:00
|
|
|
use tokio::runtime::current_thread;
|
|
|
|
|
|
|
|
// TODO: move somewhere? this could be useful as a dummy
|
|
|
|
struct InstaCloseMuxer;
|
|
|
|
impl StreamMuxer for InstaCloseMuxer {
|
|
|
|
type Substream = ();
|
|
|
|
type OutboundSubstream = ();
|
|
|
|
fn poll_inbound(&self) -> Poll<Option<Self::Substream>, IoError> { Ok(Async::Ready(None)) }
|
|
|
|
fn open_outbound(&self) -> Self::OutboundSubstream { () }
|
|
|
|
fn poll_outbound(&self, _: &mut Self::OutboundSubstream) -> Poll<Option<Self::Substream>, IoError> { Ok(Async::Ready(None)) }
|
|
|
|
fn destroy_outbound(&self, _: Self::OutboundSubstream) {}
|
2018-10-11 10:35:14 +02:00
|
|
|
fn read_substream(&self, _: &mut Self::Substream, _: &mut [u8]) -> Poll<usize, IoError> { panic!() }
|
|
|
|
fn write_substream(&self, _: &mut Self::Substream, _: &[u8]) -> Poll<usize, IoError> { panic!() }
|
|
|
|
fn flush_substream(&self, _: &mut Self::Substream) -> Poll<(), IoError> { panic!() }
|
|
|
|
fn shutdown_substream(&self, _: &mut Self::Substream, _: Shutdown) -> Poll<(), IoError> { panic!() }
|
2018-10-01 11:17:34 +02:00
|
|
|
fn destroy_substream(&self, _: Self::Substream) { panic!() }
|
2018-10-11 10:35:14 +02:00
|
|
|
fn shutdown(&self, _: Shutdown) -> Poll<(), IoError> { Ok(Async::Ready(())) }
|
|
|
|
fn flush_all(&self) -> Poll<(), IoError> { Ok(Async::Ready(())) }
|
2018-10-01 11:17:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn proper_shutdown() {
|
|
|
|
// Test that `shutdown()` is properly called on the handler once a node stops.
|
2018-10-17 10:17:40 +01:00
|
|
|
struct Handler<T> {
|
2018-10-01 11:17:34 +02:00
|
|
|
did_substream_attempt: bool,
|
|
|
|
inbound_closed: bool,
|
|
|
|
substream_attempt_cancelled: bool,
|
|
|
|
shutdown_called: bool,
|
2018-10-17 10:17:40 +01:00
|
|
|
marker: PhantomData<T>,
|
2018-10-01 11:17:34 +02:00
|
|
|
};
|
2018-10-17 10:17:40 +01:00
|
|
|
impl<T> NodeHandler for Handler<T> {
|
2018-10-01 11:17:34 +02:00
|
|
|
type InEvent = ();
|
|
|
|
type OutEvent = ();
|
2018-10-17 10:17:40 +01:00
|
|
|
type Substream = T;
|
2018-10-01 11:17:34 +02:00
|
|
|
type OutboundOpenInfo = ();
|
|
|
|
fn inject_substream(&mut self, _: T, _: NodeHandlerEndpoint<()>) { panic!() }
|
|
|
|
fn inject_inbound_closed(&mut self) {
|
|
|
|
assert!(!self.inbound_closed);
|
|
|
|
self.inbound_closed = true;
|
|
|
|
}
|
|
|
|
fn inject_outbound_closed(&mut self, _: ()) {
|
|
|
|
assert!(!self.substream_attempt_cancelled);
|
|
|
|
self.substream_attempt_cancelled = true;
|
|
|
|
}
|
|
|
|
fn inject_event(&mut self, _: Self::InEvent) { panic!() }
|
|
|
|
fn shutdown(&mut self) {
|
|
|
|
assert!(self.inbound_closed);
|
|
|
|
assert!(self.substream_attempt_cancelled);
|
|
|
|
self.shutdown_called = true;
|
|
|
|
}
|
|
|
|
fn poll(&mut self) -> Poll<Option<NodeHandlerEvent<(), ()>>, IoError> {
|
|
|
|
if self.shutdown_called {
|
|
|
|
Ok(Async::Ready(None))
|
|
|
|
} else if !self.did_substream_attempt {
|
|
|
|
self.did_substream_attempt = true;
|
|
|
|
Ok(Async::Ready(Some(NodeHandlerEvent::OutboundSubstreamRequest(()))))
|
|
|
|
} else {
|
|
|
|
Ok(Async::NotReady)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-10-17 10:17:40 +01:00
|
|
|
impl<T> Drop for Handler<T> {
|
2018-10-01 11:17:34 +02:00
|
|
|
fn drop(&mut self) {
|
|
|
|
assert!(self.shutdown_called);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-17 10:17:40 +01:00
|
|
|
let handled = HandledNode::new(InstaCloseMuxer, Handler {
|
2018-10-01 11:17:34 +02:00
|
|
|
did_substream_attempt: false,
|
|
|
|
inbound_closed: false,
|
|
|
|
substream_attempt_cancelled: false,
|
|
|
|
shutdown_called: false,
|
2018-10-17 10:17:40 +01:00
|
|
|
marker: PhantomData,
|
2018-10-01 11:17:34 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
current_thread::Runtime::new().unwrap().block_on(handled.for_each(|_| Ok(()))).unwrap();
|
|
|
|
}
|
|
|
|
}
|