mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-26 08:11:39 +00:00
Split ConnectionUpgrade
. (#642)
Introduce `InboundUpgrade` and `OutboundUpgrade`.
This commit is contained in:
committed by
Pierre Krieger
parent
466385a58a
commit
2e549884ef
@ -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`
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
Reference in New Issue
Block a user