mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-13 01:51:23 +00:00
Introduce several concrete future types. (#433)
* multisteam-select: introduce `DialerFuture`. * multistream-select: add more concrete futures. * multistream-select: add ListenerFuture. * multistream-select: add ListenerSelectFuture * Formatting. * Add DialerSelectFuture type alias. * Add UpgradeApplyFuture and NegotiationFuture. * In iterator wrappers also pass-through size_hint. * Minor refactoring. * Address review comments. * Add some comments. * Hide state enums in wrapping structs.
This commit is contained in:
@ -19,9 +19,9 @@
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use bytes::Bytes;
|
||||
use futures::{prelude::*, future};
|
||||
use multistream_select;
|
||||
use std::io::{Error as IoError, ErrorKind as IoErrorKind};
|
||||
use futures::{prelude::*, future::Either};
|
||||
use multistream_select::{self, DialerSelectFuture, ListenerSelectFuture};
|
||||
use std::{io::{Error as IoError, ErrorKind as IoErrorKind}, mem};
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use upgrade::{ConnectionUpgrade, Endpoint};
|
||||
|
||||
@ -29,31 +29,97 @@ use upgrade::{ConnectionUpgrade, Endpoint};
|
||||
///
|
||||
/// Returns a `Future` that returns the outcome of the connection upgrade.
|
||||
#[inline]
|
||||
pub fn apply<C, U, Maf>(
|
||||
connection: C,
|
||||
upgrade: U,
|
||||
endpoint: Endpoint,
|
||||
remote_addr: Maf,
|
||||
) -> impl Future<Item = (U::Output, U::MultiaddrFuture), Error = IoError>
|
||||
pub fn apply<C, U, Maf>(conn: C, upgrade: U, e: Endpoint, remote: Maf) -> UpgradeApplyFuture<C, U, Maf>
|
||||
where
|
||||
U: ConnectionUpgrade<C, Maf>,
|
||||
U::NamesIter: Clone, // TODO: not elegant
|
||||
C: AsyncRead + AsyncWrite,
|
||||
{
|
||||
negotiate(connection, &upgrade, endpoint)
|
||||
.and_then(move |(upgrade_id, connection)| {
|
||||
upgrade.upgrade(connection, upgrade_id, endpoint, remote_addr)
|
||||
})
|
||||
.into_future()
|
||||
.then(|val| {
|
||||
match val {
|
||||
Ok(_) => debug!("Successfully applied negotiated protocol"),
|
||||
Err(ref err) => debug!("Failed to apply negotiated protocol: {:?}", err),
|
||||
}
|
||||
val
|
||||
})
|
||||
UpgradeApplyFuture {
|
||||
inner: UpgradeApplyState::Init {
|
||||
future: negotiate(conn, &upgrade, e),
|
||||
upgrade,
|
||||
endpoint: e,
|
||||
remote
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Future, returned from `apply` which performs a connection upgrade.
|
||||
pub struct UpgradeApplyFuture<C, U, Maf>
|
||||
where
|
||||
U: ConnectionUpgrade<C, Maf>,
|
||||
C: AsyncRead + AsyncWrite
|
||||
{
|
||||
inner: UpgradeApplyState<C, U, Maf>
|
||||
}
|
||||
|
||||
|
||||
enum UpgradeApplyState<C, U, Maf>
|
||||
where
|
||||
U: ConnectionUpgrade<C, Maf>,
|
||||
C: AsyncRead + AsyncWrite
|
||||
{
|
||||
Init {
|
||||
future: NegotiationFuture<C, ProtocolNames<U::NamesIter>, U::UpgradeIdentifier>,
|
||||
upgrade: U,
|
||||
endpoint: Endpoint,
|
||||
remote: Maf
|
||||
},
|
||||
Upgrade {
|
||||
future: U::Future
|
||||
},
|
||||
Undefined
|
||||
}
|
||||
|
||||
impl<C, U, Maf> Future for UpgradeApplyFuture<C, U, Maf>
|
||||
where
|
||||
U: ConnectionUpgrade<C, Maf>,
|
||||
U::NamesIter: Clone,
|
||||
C: AsyncRead + AsyncWrite
|
||||
{
|
||||
type Item = (U::Output, U::MultiaddrFuture);
|
||||
type Error = IoError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
loop {
|
||||
match mem::replace(&mut self.inner, UpgradeApplyState::Undefined) {
|
||||
UpgradeApplyState::Init { mut future, upgrade, endpoint, remote } => {
|
||||
let (upgrade_id, connection) = match future.poll()? {
|
||||
Async::Ready(x) => x,
|
||||
Async::NotReady => {
|
||||
self.inner = UpgradeApplyState::Init { future, upgrade, endpoint, remote };
|
||||
return Ok(Async::NotReady)
|
||||
}
|
||||
};
|
||||
self.inner = UpgradeApplyState::Upgrade {
|
||||
future: upgrade.upgrade(connection, upgrade_id, endpoint, remote)
|
||||
};
|
||||
}
|
||||
UpgradeApplyState::Upgrade { mut future } => {
|
||||
match future.poll() {
|
||||
Ok(Async::NotReady) => {
|
||||
self.inner = UpgradeApplyState::Upgrade { future };
|
||||
return Ok(Async::NotReady)
|
||||
}
|
||||
Ok(Async::Ready(x)) => {
|
||||
debug!("Successfully applied negotiated protocol");
|
||||
return Ok(Async::Ready(x))
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("Failed to apply negotiated protocol: {:?}", e);
|
||||
return Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
UpgradeApplyState::Undefined =>
|
||||
panic!("UpgradeApplyState::poll called after completion")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Negotiates a protocol on a stream.
|
||||
///
|
||||
/// Returns a `Future` that returns the negotiated protocol and the stream.
|
||||
@ -62,29 +128,73 @@ pub fn negotiate<C, I, U, Maf>(
|
||||
connection: C,
|
||||
upgrade: &U,
|
||||
endpoint: Endpoint,
|
||||
) -> impl Future<Item = (U::UpgradeIdentifier, C), Error = IoError>
|
||||
) -> NegotiationFuture<C, ProtocolNames<U::NamesIter>, U::UpgradeIdentifier>
|
||||
where
|
||||
U: ConnectionUpgrade<I, Maf>,
|
||||
U::NamesIter: Clone, // TODO: not elegant
|
||||
C: AsyncRead + AsyncWrite,
|
||||
{
|
||||
let iter = upgrade
|
||||
.protocol_names()
|
||||
.map::<_, fn(_) -> _>(|(n, t)| (n, <Bytes as PartialEq>::eq, t));
|
||||
debug!("Starting protocol negotiation");
|
||||
|
||||
let negotiation = match endpoint {
|
||||
Endpoint::Listener => future::Either::A(multistream_select::listener_select_proto(connection, iter)),
|
||||
Endpoint::Dialer => future::Either::B(multistream_select::dialer_select_proto(connection, iter)),
|
||||
};
|
||||
|
||||
negotiation
|
||||
.map_err(|err| IoError::new(IoErrorKind::Other, err))
|
||||
.then(move |negotiated| {
|
||||
match negotiated {
|
||||
Ok(_) => debug!("Successfully negotiated protocol upgrade"),
|
||||
Err(ref err) => debug!("Error while negotiated protocol upgrade: {:?}", err),
|
||||
};
|
||||
negotiated
|
||||
})
|
||||
let iter = ProtocolNames(upgrade.protocol_names());
|
||||
NegotiationFuture {
|
||||
inner: match endpoint {
|
||||
Endpoint::Listener => Either::A(multistream_select::listener_select_proto(connection, iter)),
|
||||
Endpoint::Dialer => Either::B(multistream_select::dialer_select_proto(connection, iter)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Future, returned by `negotiate`, which negotiates a protocol and stream.
|
||||
pub struct NegotiationFuture<R: AsyncRead + AsyncWrite, I, P> {
|
||||
inner: Either<ListenerSelectFuture<R, I, P>, DialerSelectFuture<R, I, P>>
|
||||
}
|
||||
|
||||
impl<R, I, M, P> Future for NegotiationFuture<R, I, P>
|
||||
where
|
||||
R: AsyncRead + AsyncWrite,
|
||||
I: Iterator<Item=(Bytes, M, P)> + Clone,
|
||||
M: FnMut(&Bytes, &Bytes) -> bool,
|
||||
{
|
||||
type Item = (P, R);
|
||||
type Error = IoError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
match self.inner.poll() {
|
||||
Ok(Async::NotReady) => Ok(Async::NotReady),
|
||||
Ok(Async::Ready(x)) => {
|
||||
debug!("Successfully negotiated protocol upgrade");
|
||||
Ok(Async::Ready(x))
|
||||
}
|
||||
Err(e) => {
|
||||
let err = IoError::new(IoErrorKind::Other, e);
|
||||
debug!("Error while negotiated protocol upgrade: {:?}", err);
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Iterator adapter which adds equality matching predicates to items.
|
||||
/// Used in `NegotiationFuture`.
|
||||
#[derive(Clone)]
|
||||
pub struct ProtocolNames<I>(I);
|
||||
|
||||
impl<I, Id> Iterator for ProtocolNames<I>
|
||||
where
|
||||
I: Iterator<Item=(Bytes, Id)>
|
||||
{
|
||||
type Item = (Bytes, fn(&Bytes, &Bytes) -> bool, Id);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let f = <Bytes as PartialEq>::eq as fn(&Bytes, &Bytes) -> bool;
|
||||
self.0.next().map(|(b, id)| (b, f, id))
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.0.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user