Split ConnectionUpgrade. (#642)

Introduce `InboundUpgrade` and `OutboundUpgrade`.
This commit is contained in:
Toralf Wittner
2018-11-15 17:41:11 +01:00
committed by Pierre Krieger
parent 466385a58a
commit 2e549884ef
52 changed files with 2010 additions and 1658 deletions

View File

@ -20,25 +20,26 @@
use futures::prelude::*;
use libp2p_core::{
nodes::{NodeHandlerEndpoint, ProtocolsHandler, ProtocolsHandlerEvent},
upgrade::toggleable,
ConnectionUpgrade,
OutboundUpgrade,
nodes::{ProtocolsHandler, ProtocolsHandlerEvent},
upgrade::{self, DeniedUpgrade}
};
use protocol::{Ping, PingDialer, PingOutput};
use log::warn;
use protocol::{Ping, PingDialer};
use std::{
io, mem,
time::{Duration, Instant},
};
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_timer::Delay;
use void::Void;
use void::{Void, unreachable};
/// Protocol handler that handles pinging the remote at a regular period.
///
/// If the remote doesn't respond, produces `Unresponsive` and closes the connection.
pub struct PeriodicPingHandler<TSubstream> {
/// Configuration for the ping protocol.
ping_config: toggleable::Toggleable<Ping<Instant>>,
ping_config: upgrade::Toggleable<Ping<Instant>>,
/// State of the outgoing ping.
out_state: OutState<TSubstream>,
@ -126,7 +127,7 @@ impl<TSubstream> PeriodicPingHandler<TSubstream> {
let ping_timeout = Duration::from_secs(30);
PeriodicPingHandler {
ping_config: toggleable::toggleable(Default::default()),
ping_config: upgrade::toggleable(Default::default()),
out_state: OutState::NeedToOpen {
expires: Delay::new(Instant::now() + ping_timeout),
},
@ -151,36 +152,36 @@ where
type InEvent = Void;
type OutEvent = OutEvent;
type Substream = TSubstream;
type Protocol = toggleable::Toggleable<Ping<Instant>>;
type InboundProtocol = DeniedUpgrade;
type OutboundProtocol = upgrade::Toggleable<Ping<Instant>>;
type OutboundOpenInfo = ();
#[inline]
fn listen_protocol(&self) -> Self::Protocol {
let mut config = self.ping_config;
config.disable();
config
fn listen_protocol(&self) -> Self::InboundProtocol {
DeniedUpgrade
}
fn inject_fully_negotiated(
#[inline]
fn dialer_protocol(&self) -> Self::OutboundProtocol {
self.ping_config
}
fn inject_fully_negotiated_inbound(&mut self, protocol: Void) {
unreachable(protocol)
}
fn inject_fully_negotiated_outbound(
&mut self,
protocol: <Self::Protocol as ConnectionUpgrade<TSubstream>>::Output,
_endpoint: NodeHandlerEndpoint<Self::OutboundOpenInfo>,
mut substream: <Self::OutboundProtocol as OutboundUpgrade<TSubstream>>::Output,
_info: Self::OutboundOpenInfo
) {
match protocol {
PingOutput::Pinger(mut substream) => {
debug_assert!(_endpoint.is_dialer());
match mem::replace(&mut self.out_state, OutState::Poisoned) {
OutState::Upgrading { expires } => {
// We always upgrade with the intent of immediately pinging.
substream.ping(Instant::now());
self.out_state = OutState::WaitingForPong { substream, expires };
}
_ => (),
}
}
PingOutput::Ponger(_) => {
debug_assert!(false, "Received an unexpected incoming ping substream");
match mem::replace(&mut self.out_state, OutState::Poisoned) {
OutState::Upgrading { expires } => {
// We always upgrade with the intent of immediately pinging.
substream.ping(Instant::now());
self.out_state = OutState::WaitingForPong { substream, expires };
}
_ => (),
}
}
@ -218,7 +219,7 @@ where
fn poll(
&mut self,
) -> Poll<
Option<ProtocolsHandlerEvent<Self::Protocol, Self::OutboundOpenInfo, Self::OutEvent>>,
Option<ProtocolsHandlerEvent<Self::OutboundProtocol, Self::OutboundOpenInfo, Self::OutEvent>>,
io::Error,
> {
// Shortcut for polling a `tokio_timer::Delay`

View File

@ -56,38 +56,35 @@
//! extern crate tokio;
//!
//! use futures::{Future, Stream};
//! use libp2p_ping::protocol::{Ping, PingOutput};
//! use libp2p_core::Transport;
//! use libp2p_core::{transport::Transport, upgrade::apply_outbound};
//! use libp2p_ping::protocol::Ping;
//! use tokio::runtime::current_thread::Runtime;
//!
//! # fn main() {
//! let ping_finished_future = libp2p_tcp_transport::TcpConfig::new()
//! .with_upgrade(Ping::default())
//! .dial("127.0.0.1:12345".parse::<libp2p_core::Multiaddr>().unwrap()).unwrap_or_else(|_| panic!())
//! .and_then(|out| {
//! match out {
//! PingOutput::Ponger(processing) => Box::new(processing) as Box<Future<Item = _, Error = _> + Send>,
//! PingOutput::Pinger(mut pinger) => {
//! pinger.ping(());
//! let f = pinger.into_future().map(|_| ()).map_err(|(err, _)| err);
//! Box::new(f) as Box<Future<Item = _, Error = _> + Send>
//! },
//! }
//! let ping_dialer = libp2p_tcp_transport::TcpConfig::new()
//! .and_then(|socket, _| {
//! apply_outbound(socket, Ping::default()).map_err(|e| e.into_io_error())
//! })
//! .dial("/ip4/127.0.0.1/tcp/12345".parse::<libp2p_core::Multiaddr>().unwrap()).unwrap_or_else(|_| panic!())
//! .and_then(|mut pinger| {
//! pinger.ping(());
//! let f = pinger.into_future()
//! .map(|_| println!("received pong"))
//! .map_err(|(e, _)| e);
//! Box::new(f) as Box<Future<Item = _, Error = _> + Send>
//! });
//!
//! // Runs until the ping arrives.
//! let mut rt = Runtime::new().unwrap();
//! let _ = rt.block_on(ping_finished_future).unwrap();
//! let _ = rt.block_on(ping_dialer).unwrap();
//! # }
//! ```
//!
extern crate arrayvec;
extern crate bytes;
#[macro_use]
extern crate futures;
extern crate libp2p_core;
#[macro_use]
extern crate log;
extern crate multistream_select;
extern crate parking_lot;

View File

@ -21,13 +21,15 @@
use arrayvec::ArrayVec;
use futures::prelude::*;
use libp2p_core::{
nodes::{NodeHandlerEndpoint, ProtocolsHandler, ProtocolsHandlerEvent},
ConnectionUpgrade,
InboundUpgrade,
nodes::{ProtocolsHandler, ProtocolsHandlerEvent},
upgrade::DeniedUpgrade
};
use protocol::{Ping, PingListener, PingOutput};
use log::warn;
use protocol::{Ping, PingListener};
use std::io;
use tokio_io::{AsyncRead, AsyncWrite};
use void::Void;
use void::{Void, unreachable};
/// Handler for handling pings received from a remote.
pub struct PingListenHandler<TSubstream> {
@ -68,33 +70,32 @@ where
type InEvent = Void;
type OutEvent = Void;
type Substream = TSubstream;
type Protocol = Ping<()>;
type InboundProtocol = Ping<()>;
type OutboundProtocol = DeniedUpgrade;
type OutboundOpenInfo = ();
#[inline]
fn listen_protocol(&self) -> Self::Protocol {
fn listen_protocol(&self) -> Self::InboundProtocol {
self.ping_config
}
fn inject_fully_negotiated(
#[inline]
fn dialer_protocol(&self) -> Self::OutboundProtocol {
DeniedUpgrade
}
fn inject_fully_negotiated_inbound(
&mut self,
protocol: <Self::Protocol as ConnectionUpgrade<TSubstream>>::Output,
_endpoint: NodeHandlerEndpoint<Self::OutboundOpenInfo>,
protocol: <Self::InboundProtocol as InboundUpgrade<TSubstream>>::Output
) {
if self.shutdown {
return;
}
let _ = self.ping_in_substreams.try_push(protocol);
}
match protocol {
PingOutput::Pinger(_) => {
debug_assert!(false, "Received an unexpected outgoing ping substream");
}
PingOutput::Ponger(listener) => {
debug_assert!(_endpoint.is_listener());
// Try insert the element, but don't care if the list is full.
let _ = self.ping_in_substreams.try_push(listener);
}
}
fn inject_fully_negotiated_outbound(&mut self, protocol: Void, _info: Self::OutboundOpenInfo) {
unreachable(protocol)
}
#[inline]
@ -118,7 +119,7 @@ where
fn poll(
&mut self,
) -> Poll<
Option<ProtocolsHandlerEvent<Self::Protocol, Self::OutboundOpenInfo, Self::OutEvent>>,
Option<ProtocolsHandlerEvent<Self::OutboundProtocol, Self::OutboundOpenInfo, Self::OutEvent>>,
io::Error,
> {
// Removes each substream one by one, and pushes them back if they're not ready (which

View File

@ -19,8 +19,9 @@
// DEALINGS IN THE SOFTWARE.
use bytes::{BufMut, Bytes, BytesMut};
use futures::{prelude::*, future::FutureResult, future::IntoFuture};
use libp2p_core::{ConnectionUpgrade, Endpoint};
use futures::{prelude::*, future::{self, FutureResult}, try_ready};
use libp2p_core::{InboundUpgrade, OutboundUpgrade, UpgradeInfo};
use log::debug;
use rand::{distributions::Standard, prelude::*, rngs::EntropyRng};
use std::collections::VecDeque;
use std::io::Error as IoError;
@ -42,71 +43,53 @@ impl<TUserData> Default for Ping<TUserData> {
}
}
/// Output of a `Ping` upgrade.
pub enum PingOutput<TSocket, TUserData> {
/// We are on the dialing side.
Pinger(PingDialer<TSocket, TUserData>),
/// We are on the listening side.
Ponger(PingListener<TSocket>),
}
impl<TUserData> UpgradeInfo for Ping<TUserData> {
type UpgradeId = ();
type NamesIter = iter::Once<(Bytes, Self::UpgradeId)>;
impl<TSocket, TUserData> ConnectionUpgrade<TSocket> for Ping<TUserData>
where
TSocket: AsyncRead + AsyncWrite,
{
type NamesIter = iter::Once<(Bytes, Self::UpgradeIdentifier)>;
type UpgradeIdentifier = ();
#[inline]
fn protocol_names(&self) -> Self::NamesIter {
iter::once(("/ipfs/ping/1.0.0".into(), ()))
}
}
type Output = PingOutput<TSocket, TUserData>;
type Future = FutureResult<Self::Output, IoError>;
impl<TSocket, TUserData> InboundUpgrade<TSocket> for Ping<TUserData>
where
TSocket: AsyncRead + AsyncWrite,
{
type Output = PingListener<TSocket>;
type Error = IoError;
type Future = FutureResult<Self::Output, Self::Error>;
#[inline]
fn upgrade(
self,
socket: TSocket,
_: Self::UpgradeIdentifier,
endpoint: Endpoint,
) -> Self::Future {
let out = match endpoint {
Endpoint::Dialer => upgrade_as_dialer(socket),
Endpoint::Listener => upgrade_as_listener(socket),
fn upgrade_inbound(self, socket: TSocket, _: Self::UpgradeId) -> Self::Future {
let listener = PingListener {
inner: Framed::new(socket, Codec),
state: PingListenerState::Listening,
};
Ok(out).into_future()
future::ok(listener)
}
}
/// Upgrades a connection from the dialer side.
fn upgrade_as_dialer<TSocket, TUserData>(socket: TSocket) -> PingOutput<TSocket, TUserData>
where TSocket: AsyncRead + AsyncWrite,
impl<TSocket, TUserData> OutboundUpgrade<TSocket> for Ping<TUserData>
where
TSocket: AsyncRead + AsyncWrite,
{
let dialer = PingDialer {
inner: Framed::new(socket, Codec),
need_writer_flush: false,
needs_close: false,
sent_pings: VecDeque::with_capacity(4),
rng: EntropyRng::default(),
pings_to_send: VecDeque::with_capacity(4),
};
type Output = PingDialer<TSocket, TUserData>;
type Error = IoError;
type Future = FutureResult<Self::Output, Self::Error>;
PingOutput::Pinger(dialer)
}
/// Upgrades a connection from the listener side.
fn upgrade_as_listener<TSocket, TUserData>(socket: TSocket) -> PingOutput<TSocket, TUserData>
where TSocket: AsyncRead + AsyncWrite,
{
let listener = PingListener {
inner: Framed::new(socket, Codec),
state: PingListenerState::Listening,
};
PingOutput::Ponger(listener)
#[inline]
fn upgrade_outbound(self, socket: TSocket, _: Self::UpgradeId) -> Self::Future {
let dialer = PingDialer {
inner: Framed::new(socket, Codec),
need_writer_flush: false,
needs_close: false,
sent_pings: VecDeque::with_capacity(4),
rng: EntropyRng::default(),
pings_to_send: VecDeque::with_capacity(4),
};
future::ok(dialer)
}
}
/// Sends pings and receives the pongs.
@ -342,9 +325,9 @@ mod tests {
use self::tokio_tcp::TcpListener;
use self::tokio_tcp::TcpStream;
use super::{Ping, PingOutput};
use super::Ping;
use futures::{Future, Stream};
use libp2p_core::{ConnectionUpgrade, Endpoint};
use libp2p_core::upgrade::{InboundUpgrade, OutboundUpgrade};
// TODO: rewrite tests with the MemoryTransport
@ -358,32 +341,18 @@ mod tests {
.into_future()
.map_err(|(e, _)| e.into())
.and_then(|(c, _)| {
Ping::<()>::default().upgrade(
c.unwrap(),
(),
Endpoint::Listener,
)
Ping::<()>::default().upgrade_inbound(c.unwrap(), ())
})
.and_then(|out| match out {
PingOutput::Ponger(service) => service,
_ => unreachable!(),
});
.flatten();
let client = TcpStream::connect(&listener_addr)
.map_err(|e| e.into())
.and_then(|c| {
Ping::<()>::default().upgrade(
c,
(),
Endpoint::Dialer,
)
Ping::<()>::default().upgrade_outbound(c, ())
})
.and_then(|out| match out {
PingOutput::Pinger(mut pinger) => {
pinger.ping(());
pinger.into_future().map(|_| ()).map_err(|_| panic!())
},
_ => unreachable!(),
.and_then(|mut pinger| {
pinger.ping(());
pinger.into_future().map(|_| ()).map_err(|_| panic!())
})
.map(|_| ());
@ -402,39 +371,23 @@ mod tests {
.into_future()
.map_err(|(e, _)| e.into())
.and_then(|(c, _)| {
Ping::<u32>::default().upgrade(
c.unwrap(),
(),
Endpoint::Listener,
)
Ping::<u32>::default().upgrade_inbound(c.unwrap(), ())
})
.and_then(|out| match out {
PingOutput::Ponger(service) => service,
_ => unreachable!(),
});
.flatten();
let client = TcpStream::connect(&listener_addr)
.map_err(|e| e.into())
.and_then(|c| {
Ping::<u32>::default().upgrade(
c,
(),
Endpoint::Dialer,
)
Ping::<u32>::default().upgrade_outbound(c, ())
})
.and_then(|out| match out {
PingOutput::Pinger(mut pinger) => {
for n in 0..20 {
pinger.ping(n);
}
pinger
.take(20)
.collect()
.map(|val| { assert_eq!(val, (0..20).collect::<Vec<_>>()); })
.map_err(|_| panic!())
},
_ => unreachable!(),
.and_then(|mut pinger| {
for n in 0..20 {
pinger.ping(n);
}
pinger.take(20)
.collect()
.map(|val| { assert_eq!(val, (0..20).collect::<Vec<_>>()); })
.map_err(|_| panic!())
});
let mut runtime = tokio::runtime::Runtime::new().unwrap();