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:
Toralf Wittner
2018-08-30 23:25:16 +02:00
committed by GitHub
parent f457ca5490
commit c02dea8128
7 changed files with 665 additions and 249 deletions

View File

@ -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()
}
}