Remove the old API (#565)

* Remove the old API

* Fix integration test
This commit is contained in:
Pierre Krieger
2018-10-15 16:17:55 +01:00
committed by GitHub
parent 2c98d06942
commit 724d0f5d82
18 changed files with 1 additions and 2445 deletions

View File

@ -1,816 +0,0 @@
// 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.
//! Contains the `ConnectionReuse` struct. Stores open muxed connections to nodes so that dialing
//! a node reuses the same connection instead of opening a new one.
//!
//! A `ConnectionReuse` can only be created from an `UpgradedNode` whose `ConnectionUpgrade`
//! yields as `StreamMuxer`.
//!
//! # Behaviour
//!
//! The API exposed by the `ConnectionReuse` struct consists in the `Transport` trait
//! implementation, with the `dial` and `listen_on` methods.
//!
//! When called on a `ConnectionReuse`, the `listen_on` method will listen on the given
//! multiaddress (by using the underlying `Transport`), then will apply a `flat_map` on the
//! incoming connections so that we actually listen to the incoming substreams of each connection.
//!
//! When called on a `ConnectionReuse`, the `dial` method will try to use a connection that has
//! already been opened earlier, and open an outgoing substream on it. If none is available, it
//! will dial the given multiaddress. Dialed node can also spontaneously open new substreams with
//! us. In order to handle these new substreams you should use the `next_incoming` method of the
//! `MuxedTransport` trait.
use fnv::FnvHashMap;
use futures::future::{self, FutureResult};
use futures::{Async, Future, Poll, Stream, stream, task};
use futures::stream::FuturesUnordered;
use multiaddr::Multiaddr;
use muxing::{self, StreamMuxer};
use parking_lot::Mutex;
use std::collections::hash_map::Entry;
use std::io::{Error as IoError, ErrorKind as IoErrorKind, Read, Write};
use std::mem;
use std::ops::{Deref, DerefMut};
use std::sync::{Arc, atomic::AtomicUsize, atomic::Ordering};
use tokio_io::{AsyncRead, AsyncWrite};
use transport::{MuxedTransport, Transport};
/// Allows reusing the same muxed connection multiple times.
///
/// Can be created from an `UpgradedNode` through the `From` trait.
///
/// Implements the `Transport` trait.
pub struct ConnectionReuse<T, D, M>
where
T: Transport,
T: Transport<Output = (D, M)>,
M: StreamMuxer,
{
/// Struct shared between most of the `ConnectionReuse` infrastructure.
shared: Arc<Mutex<Shared<T, D, M>>>,
}
impl<T, D, M> Clone for ConnectionReuse<T, D, M>
where
T: Transport,
T: Transport<Output = (D, M)>,
M: StreamMuxer,
{
#[inline]
fn clone(&self) -> Self {
ConnectionReuse {
shared: self.shared.clone(),
}
}
}
/// Struct shared between most of the `ConnectionReuse` infrastructure.
struct Shared<T, D, M>
where
T: Transport,
T: Transport<Output = (D, M)>,
M: StreamMuxer,
{
/// Underlying transport and connection upgrade, used when we need to dial or listen.
transport: T,
/// All the connections that were opened, whether successful and/or active or not.
// TODO: this will grow forever
connections: FnvHashMap<Multiaddr, PeerState<D, M>>,
/// Tasks to notify when one or more new elements were added to `connections`.
notify_on_new_connec: FnvHashMap<usize, task::Task>,
/// Next `connection_id` to use when opening a connection.
next_connection_id: u64,
/// Next `listener_id` for the next listener we create.
next_listener_id: u64,
}
enum PeerState<D, M> where M: StreamMuxer {
/// Connection is active and can be used to open substreams.
Active {
/// The muxer to open new substreams.
muxer: Arc<M>,
/// Custom data passed to the output.
custom_data: D,
/// Future of the address of the client.
client_addr: Multiaddr,
/// Unique identifier for this connection in the `ConnectionReuse`.
connection_id: u64,
/// Number of open substreams.
num_substreams: u64,
/// Id of the listener that created this connection, or `None` if it was opened by a
/// dialer.
listener_id: Option<u64>,
},
/// Connection is pending.
// TODO: stronger Future type
Pending {
/// Future that produces the muxer.
future: Box<Future<Item = ((D, M), Multiaddr), Error = IoError> + Send>,
/// All the tasks to notify when `future` resolves.
notify: FnvHashMap<usize, task::Task>,
},
/// An earlier connection attempt errored.
Errored(IoError),
/// The `PeerState` is poisonned. Happens if a panic happened while executing some of the
/// functions.
Poisonned,
}
impl<T, D, M> ConnectionReuse<T, D, M>
where
T: Transport,
T: Transport<Output = (D, M)>,
M: StreamMuxer,
{
#[inline]
pub(crate) fn new(node: T) -> ConnectionReuse<T, D, M> {
ConnectionReuse {
shared: Arc::new(Mutex::new(Shared {
transport: node,
connections: Default::default(),
notify_on_new_connec: Default::default(),
next_connection_id: 0,
next_listener_id: 0,
})),
}
}
}
impl<T, D, M> Transport for ConnectionReuse<T, D, M>
where
T: Transport + Send + 'static, // TODO: 'static :(
T::Dial: Send,
T::MultiaddrFuture: Send,
T::Listener: Send,
T::ListenerUpgrade: Send,
T: Transport<Output = (D, M)> + Clone + 'static, // TODO: 'static :(
M: Send + Sync + StreamMuxer + 'static,
D: Send + Clone + 'static,
T: Clone,
{
type Output = (D, ConnectionReuseSubstream<T, D, M>);
type MultiaddrFuture = future::FutureResult<Multiaddr, IoError>;
type Listener = Box<Stream<Item = Self::ListenerUpgrade, Error = IoError> + Send>;
type ListenerUpgrade = FutureResult<(Self::Output, Self::MultiaddrFuture), IoError>;
type Dial = ConnectionReuseDial<T, D, M>;
fn listen_on(self, addr: Multiaddr) -> Result<(Self::Listener, Multiaddr), (Self, Multiaddr)> {
let mut shared = self.shared.lock();
let (listener, new_addr) = match shared.transport.clone().listen_on(addr.clone()) {
Ok((l, a)) => (l, a),
Err((_, addr)) => {
return Err((
ConnectionReuse {
shared: self.shared.clone(),
},
addr,
));
}
};
let listener = listener
.map(|upgr| {
upgr.and_then(|(out, addr)| {
trace!("Waiting for remote's address as listener");
addr.map(move |addr| (out, addr))
})
})
.fuse();
let listener_id = shared.next_listener_id;
shared.next_listener_id += 1;
let listener = ConnectionReuseListener {
shared: self.shared.clone(),
listener,
listener_id,
current_upgrades: FuturesUnordered::new(),
};
Ok((Box::new(listener) as Box<_>, new_addr))
}
#[inline]
fn dial(self, addr: Multiaddr) -> Result<Self::Dial, (Self, Multiaddr)> {
let mut shared = self.shared.lock();
// If an earlier attempt to dial this multiaddress failed, we clear the error. Otherwise
// the returned `Future` will immediately produce the error.
let must_clear = match shared.connections.get(&addr) {
Some(&PeerState::Errored(ref err)) => {
trace!("Clearing existing connection to {} which errored earlier: {:?}", addr, err);
true
},
_ => false,
};
if must_clear {
shared.connections.remove(&addr);
}
Ok(ConnectionReuseDial {
outbound: None,
shared: self.shared.clone(),
addr,
})
}
#[inline]
fn nat_traversal(&self, server: &Multiaddr, observed: &Multiaddr) -> Option<Multiaddr> {
self.shared.lock().transport.nat_traversal(server, observed)
}
}
impl<T, D, M> MuxedTransport for ConnectionReuse<T, D, M>
where
T: Transport + Send + 'static, // TODO: 'static :(
T::Dial: Send,
T::MultiaddrFuture: Send,
T::Listener: Send,
T::ListenerUpgrade: Send,
T: Transport<Output = (D, M)> + Clone + 'static, // TODO: 'static :(
M: Send + Sync + StreamMuxer + 'static,
D: Send + Clone + 'static,
T: Clone,
{
type Incoming = ConnectionReuseIncoming<T, D, M>;
type IncomingUpgrade =
future::FutureResult<((D, ConnectionReuseSubstream<T, D, M>), Self::MultiaddrFuture), IoError>;
#[inline]
fn next_incoming(self) -> Self::Incoming {
ConnectionReuseIncoming {
shared: self.shared.clone(),
}
}
}
static NEXT_TASK_ID: AtomicUsize = AtomicUsize::new(0);
// `TASK_ID` is used internally to uniquely identify each task.
task_local!{
static TASK_ID: usize = NEXT_TASK_ID.fetch_add(1, Ordering::Relaxed)
}
/// Implementation of `Future` for dialing a node.
pub struct ConnectionReuseDial<T, D, M>
where
T: Transport,
T: Transport<Output = (D, M)>,
M: StreamMuxer,
{
/// The future that will construct the substream, the connection id the muxer comes from, and
/// the `Future` of the client's multiaddr.
/// If `None`, we need to grab a new outbound substream from the muxer.
outbound: Option<ConnectionReuseDialOut<D, M>>,
// Shared between the whole connection reuse mechanism.
shared: Arc<Mutex<Shared<T, D, M>>>,
// The address we're trying to dial.
addr: Multiaddr,
}
struct ConnectionReuseDialOut<D, M>
where
M: StreamMuxer,
{
/// Custom data for the connection.
custom_data: D,
/// The pending outbound substream.
stream: muxing::OutboundSubstreamRefWrapFuture<Arc<M>>,
/// Id of the connection that was used to create the substream.
connection_id: u64,
/// Address of the remote.
client_addr: Multiaddr,
}
impl<T, D, M> Future for ConnectionReuseDial<T, D, M>
where
T: Transport<Output = (D, M)> + Clone,
M: Send + StreamMuxer + 'static,
D: Send + Clone + 'static,
<T as Transport>::Dial: Send + 'static,
<T as Transport>::MultiaddrFuture: Send + 'static,
{
type Item = ((D, ConnectionReuseSubstream<T, D, M>), FutureResult<Multiaddr, IoError>);
type Error = IoError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
loop {
let should_kill_existing_muxer;
if let Some(mut outbound) = self.outbound.take() {
match outbound.stream.poll() {
Ok(Async::Ready(Some(inner))) => {
trace!("Opened new outgoing substream to {}", self.addr);
let substream = ConnectionReuseSubstream {
connection_id: outbound.connection_id,
shared: self.shared.clone(),
inner,
addr: outbound.client_addr.clone(),
};
return Ok(Async::Ready(((outbound.custom_data, substream), future::ok(outbound.client_addr))));
},
Ok(Async::NotReady) => {
self.outbound = Some(outbound);
return Ok(Async::NotReady);
},
Ok(Async::Ready(None)) => {
// The muxer can no longer produce outgoing substreams.
// Let's reopen a connection.
trace!("Closing existing connection to {} ; can't produce outgoing substreams", self.addr);
should_kill_existing_muxer = true;
},
Err(err) => {
// If we get an error while opening a substream, we decide to ignore it
// and open a new muxer.
// If opening the muxer produces an error, *then* we will return it.
debug!("Error while opening outgoing substream to {}: {:?}", self.addr, err);
should_kill_existing_muxer = true;
},
}
} else {
should_kill_existing_muxer = false;
}
// If we reach this point, that means we have to fill `self.outbound`.
// If `should_kill_existing_muxer`, do not use any existing connection but create a
// new one instead.
let mut shared = self.shared.lock();
let shared = &mut *shared; // Avoids borrow errors
// TODO: could be optimized
if should_kill_existing_muxer {
shared.connections.remove(&self.addr);
}
let connec = match shared.connections.entry(self.addr.clone()) {
Entry::Occupied(e) => e.into_mut(),
Entry::Vacant(e) => {
// Build the connection.
let state = match shared.transport.clone().dial(self.addr.clone()) {
Ok(future) => {
trace!("Opened new connection to {:?}", self.addr);
let future = future.and_then(|(out, addr)| addr.map(move |a| (out, a)));
let future = Box::new(future);
PeerState::Pending { future, notify: Default::default() }
},
Err(_) => {
trace!("Failed to open connection to {:?}, multiaddr not supported", self.addr);
let err = IoError::new(IoErrorKind::ConnectionRefused, "multiaddr not supported");
PeerState::Errored(err)
},
};
for task in shared.notify_on_new_connec.drain() {
task.1.notify();
}
e.insert(state)
},
};
match mem::replace(&mut *connec, PeerState::Poisonned) {
PeerState::Active { muxer, custom_data, connection_id, listener_id, mut num_substreams, client_addr } => {
let outbound = muxing::outbound_from_ref_and_wrap(muxer.clone());
num_substreams += 1;
*connec = PeerState::Active { muxer, custom_data: custom_data.clone(), connection_id, listener_id, num_substreams, client_addr: client_addr.clone() };
trace!("Using existing connection to {} to open outbound substream", self.addr);
self.outbound = Some(ConnectionReuseDialOut {
custom_data,
stream: outbound,
connection_id,
client_addr,
});
},
PeerState::Pending { mut future, mut notify } => {
match future.poll() {
Ok(Async::Ready(((custom_data, muxer), client_addr))) => {
trace!("Successful new connection to {} ({})", self.addr, client_addr);
for task in notify {
task.1.notify();
}
let muxer = Arc::new(muxer);
let first_outbound = muxing::outbound_from_ref_and_wrap(muxer.clone());
let connection_id = shared.next_connection_id;
shared.next_connection_id += 1;
*connec = PeerState::Active { muxer, custom_data: custom_data.clone(), connection_id, num_substreams: 1, listener_id: None, client_addr: client_addr.clone() };
self.outbound = Some(ConnectionReuseDialOut {
custom_data,
stream: first_outbound,
connection_id,
client_addr,
});
},
Ok(Async::NotReady) => {
notify.insert(TASK_ID.with(|&t| t), task::current());
*connec = PeerState::Pending { future, notify };
return Ok(Async::NotReady);
},
Err(err) => {
trace!("Failed new connection to {}: {:?}", self.addr, err);
let io_err = IoError::new(err.kind(), err.to_string());
*connec = PeerState::Errored(err);
return Err(io_err);
},
}
},
PeerState::Errored(err) => {
trace!("Existing new connection to {} errored earlier: {:?}", self.addr, err);
let io_err = IoError::new(err.kind(), err.to_string());
*connec = PeerState::Errored(err);
return Err(io_err);
},
PeerState::Poisonned => {
panic!("Poisonned peer state");
},
}
}
}
}
impl<T, D, M> Drop for ConnectionReuseDial<T, D, M>
where
T: Transport,
T: Transport<Output = (D, M)>,
M: StreamMuxer,
{
fn drop(&mut self) {
if let Some(outbound) = self.outbound.take() {
let mut shared = self.shared.lock();
remove_one_substream(&mut *shared, outbound.connection_id, &outbound.client_addr);
}
}
}
/// Implementation of `Stream` for the connections incoming from listening on a specific address.
pub struct ConnectionReuseListener<T, D, M, L>
where
T: Transport,
T: Transport<Output = (D, M)>,
M: StreamMuxer,
{
/// The main listener.
listener: stream::Fuse<L>,
/// Identifier for this listener. Used to determine which connections were opened by it.
listener_id: u64,
/// Opened connections that need to be upgraded.
current_upgrades: FuturesUnordered<Box<Future<Item = (T::Output, Multiaddr), Error = IoError> + Send>>,
/// Shared between the whole connection reuse mechanism.
shared: Arc<Mutex<Shared<T, D, M>>>,
}
impl<T, D, M, L, Lu> Stream for ConnectionReuseListener<T, D, M, L>
where
T: Transport,
T: Transport<Output = (D, M)>,
M: StreamMuxer,
D: Clone,
L: Stream<Item = Lu, Error = IoError>,
Lu: Future<Item = (T::Output, Multiaddr), Error = IoError> + Send + 'static,
{
type Item = FutureResult<((D, ConnectionReuseSubstream<T, D, M>), FutureResult<Multiaddr, IoError>), IoError>;
type Error = IoError;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
// Check for any incoming connection on the listening socket.
// Note that since `self.listener` is a `Fuse`, it's not a problem to continue polling even
// after it is finished or after it error'ed.
loop {
match self.listener.poll() {
Ok(Async::Ready(Some(upgrade))) => {
trace!("New incoming connection");
self.current_upgrades.push(Box::new(upgrade));
}
Ok(Async::NotReady) => break,
Ok(Async::Ready(None)) => {
debug!("Listener has been closed");
break;
}
Err(err) => {
debug!("Error while polling listener: {:?}", err);
return Err(err);
}
};
}
// Process the connections being upgraded.
loop {
match self.current_upgrades.poll() {
Ok(Async::Ready(Some(((custom_data, muxer), client_addr)))) => {
// Successfully upgraded a new incoming connection.
trace!("New multiplexed connection from {}", client_addr);
let mut shared = self.shared.lock();
let muxer = Arc::new(muxer);
let connection_id = shared.next_connection_id;
shared.next_connection_id += 1;
let state = PeerState::Active { muxer, custom_data, connection_id, listener_id: Some(self.listener_id), num_substreams: 1, client_addr: client_addr.clone() };
shared.connections.insert(client_addr, state);
for to_notify in shared.notify_on_new_connec.drain() {
to_notify.1.notify();
}
}
Ok(Async::Ready(None)) | Ok(Async::NotReady) => {
break;
},
Err(err) => {
// Insert the rest of the pending upgrades, but not the current one.
debug!("Error while upgrading listener connection: {:?}", err);
return Ok(Async::Ready(Some(future::err(err))));
}
}
}
// Poll all the incoming connections on all the connections we opened.
let mut shared = self.shared.lock();
match poll_incoming(&self.shared, &mut shared, Some(self.listener_id)) {
Ok(Async::Ready(None)) => {
if self.listener.is_done() && self.current_upgrades.is_empty() {
Ok(Async::Ready(None))
} else {
Ok(Async::NotReady)
}
},
Ok(Async::Ready(Some(substream))) => {
Ok(Async::Ready(Some(substream)))
},
Ok(Async::NotReady) => {
Ok(Async::NotReady)
}
Err(err) => {
Ok(Async::Ready(Some(future::err(err))))
}
}
}
}
/// Implementation of `Future` that yields the next incoming substream from a dialed connection.
#[must_use = "futures do nothing unless polled"]
pub struct ConnectionReuseIncoming<T, D, M>
where
T: Transport,
T: Transport<Output = (D, M)>,
M: StreamMuxer,
{
// Shared between the whole connection reuse system.
shared: Arc<Mutex<Shared<T, D, M>>>,
}
impl<T, D, M> Future for ConnectionReuseIncoming<T, D, M>
where
T: Transport,
T: Transport<Output = (D, M)>,
M: StreamMuxer,
D: Clone,
{
type Item = future::FutureResult<((D, ConnectionReuseSubstream<T, D, M>), future::FutureResult<Multiaddr, IoError>), IoError>;
type Error = IoError;
#[inline]
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let mut shared = self.shared.lock();
match poll_incoming(&self.shared, &mut shared, None) {
Ok(Async::Ready(Some(substream))) => {
Ok(Async::Ready(substream))
},
Ok(Async::Ready(None)) | Ok(Async::NotReady) => {
// TODO: will add an element to the list every time
shared.notify_on_new_connec.insert(TASK_ID.with(|&v| v), task::current());
Ok(Async::NotReady)
},
Err(err) => Err(err)
}
}
}
/// Polls the incoming substreams on all the incoming connections that match the `listener`.
///
/// Returns `Ready(None)` if no connection is matching the `listener`. Returns `NotReady` if
/// one or more connections are matching the `listener` but they are not ready.
fn poll_incoming<T, D, M>(shared_arc: &Arc<Mutex<Shared<T, D, M>>>, shared: &mut Shared<T, D, M>, listener: Option<u64>)
-> Poll<Option<FutureResult<((D, ConnectionReuseSubstream<T, D, M>), FutureResult<Multiaddr, IoError>), IoError>>, IoError>
where
T: Transport,
T: Transport<Output = (D, M)>,
M: StreamMuxer,
D: Clone,
{
// Keys of the elements in `shared.connections` to remove afterwards.
let mut to_remove = Vec::new();
// Substream to return, if any found.
let mut ret_value = None;
let mut found_one = false;
for (addr, state) in shared.connections.iter_mut() {
match *state {
PeerState::Active { ref custom_data, ref muxer, ref mut num_substreams, connection_id, ref client_addr, listener_id } => {
if listener_id != listener {
continue;
}
found_one = true;
match muxer.poll_inbound() {
Ok(Async::Ready(Some(inner))) => {
trace!("New incoming substream from {}", client_addr);
*num_substreams += 1;
let substream = ConnectionReuseSubstream {
inner: muxing::substream_from_ref(muxer.clone(), inner),
shared: shared_arc.clone(),
connection_id,
addr: client_addr.clone(),
};
ret_value = Some(Ok(((custom_data.clone(), substream), future::ok(client_addr.clone()))));
break;
},
Ok(Async::Ready(None)) => {
// The muxer isn't capable of opening any inbound stream anymore, so
// we close the connection entirely.
trace!("Removing existing connection to {} as it cannot open inbound anymore", addr);
to_remove.push(addr.clone());
},
Ok(Async::NotReady) => (),
Err(err) => {
// If an error happens while opening an inbound stream, we close the
// connection entirely.
trace!("Error while opening inbound substream to {}: {:?}", addr, err);
to_remove.push(addr.clone());
ret_value = Some(Err(err));
break;
},
}
},
PeerState::Pending { ref mut notify, .. } => {
notify.insert(TASK_ID.with(|&t| t), task::current());
},
PeerState::Errored(_) => {},
PeerState::Poisonned => {
panic!("Poisonned peer state");
},
}
}
for to_remove in to_remove {
shared.connections.remove(&to_remove);
}
match ret_value {
Some(Ok(val)) => Ok(Async::Ready(Some(future::ok(val)))),
Some(Err(err)) => Err(err),
None => {
if found_one {
Ok(Async::NotReady)
} else {
Ok(Async::Ready(None))
}
},
}
}
/// Removes one substream from an active connection. Closes the connection if necessary.
fn remove_one_substream<T, D, M>(shared: &mut Shared<T, D, M>, connec_id: u64, addr: &Multiaddr)
where
T: Transport,
T: Transport<Output = (D, M)>,
M: StreamMuxer,
{
shared.connections.retain(|_, connec| {
if let PeerState::Active { connection_id, ref mut num_substreams, .. } = connec {
if *connection_id == connec_id {
*num_substreams -= 1;
if *num_substreams == 0 {
trace!("All substreams to {} closed ; closing main connection", addr);
return false;
}
}
}
true
});
}
/// Wraps around the `Substream`.
pub struct ConnectionReuseSubstream<T, D, M>
where
T: Transport,
T: Transport<Output = (D, M)>,
M: StreamMuxer,
{
inner: muxing::SubstreamRef<Arc<M>>,
shared: Arc<Mutex<Shared<T, D, M>>>,
/// Id this connection was created from.
connection_id: u64,
/// Address of the remote.
addr: Multiaddr,
}
impl<T, D, M> Deref for ConnectionReuseSubstream<T, D, M>
where
T: Transport,
T: Transport<Output = (D, M)>,
M: StreamMuxer,
{
type Target = muxing::SubstreamRef<Arc<M>>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T, D, M> DerefMut for ConnectionReuseSubstream<T, D, M>
where
T: Transport,
T: Transport<Output = (D, M)>,
M: StreamMuxer,
{
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<T, D, M> Read for ConnectionReuseSubstream<T, D, M>
where
T: Transport,
T: Transport<Output = (D, M)>,
M: StreamMuxer,
{
#[inline]
fn read(&mut self, buf: &mut [u8]) -> Result<usize, IoError> {
self.inner.read(buf)
}
}
impl<T, D, M> AsyncRead for ConnectionReuseSubstream<T, D, M>
where
T: Transport,
T: Transport<Output = (D, M)>,
M: StreamMuxer,
{
}
impl<T, D, M> Write for ConnectionReuseSubstream<T, D, M>
where
T: Transport,
T: Transport<Output = (D, M)>,
M: StreamMuxer,
{
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize, IoError> {
self.inner.write(buf)
}
#[inline]
fn flush(&mut self) -> Result<(), IoError> {
self.inner.flush()
}
}
impl<T, D, M> AsyncWrite for ConnectionReuseSubstream<T, D, M>
where
T: Transport,
T: Transport<Output = (D, M)>,
M: StreamMuxer,
{
#[inline]
fn shutdown(&mut self) -> Poll<(), IoError> {
self.inner.shutdown()
}
}
impl<T, D, M> Drop for ConnectionReuseSubstream<T, D, M>
where
T: Transport,
T: Transport<Output = (D, M)>,
M: StreamMuxer,
{
fn drop(&mut self) {
let mut shared = self.shared.lock();
remove_one_substream(&mut *shared, self.connection_id, &self.addr);
}
}

View File

@ -164,47 +164,6 @@
//! also implements the `ConnectionUpgrade` trait and will choose one of the protocols amongst the
//! ones supported.
//!
//! # Swarm
//!
//! Once you have created an object that implements the `Transport` trait, you can put it in a
//! *swarm*. This is done by calling the `swarm()` freestanding function with the transport
//! alongside with a function or a closure that will turn the output of the upgrade (usually an
//! actual protocol, as explained above) into a `Future` producing `()`.
//!
//! ```no_run
//! extern crate futures;
//! extern crate libp2p_ping;
//! extern crate libp2p_core;
//! extern crate libp2p_tcp_transport;
//! extern crate tokio_current_thread;
//!
//! use futures::{Future, Stream};
//! use libp2p_ping::{Ping, PingOutput};
//! use libp2p_core::Transport;
//!
//! # fn main() {
//! let transport = libp2p_tcp_transport::TcpConfig::new()
//! .with_dummy_muxing();
//!
//! let (swarm_controller, swarm_future) = libp2p_core::swarm(transport.with_upgrade(Ping::default()),
//! |out, client_addr| {
//! match out {
//! PingOutput::Ponger(processing) => Box::new(processing) as Box<Future<Item = _, Error = _>>,
//! PingOutput::Pinger(mut pinger) => {
//! pinger.ping(());
//! let f = pinger.into_future().map(|_| ()).map_err(|(err, _)| err);
//! Box::new(f) as Box<Future<Item = _, Error = _>>
//! },
//! }
//! });
//!
//! // The `swarm_controller` can then be used to do some operations.
//! swarm_controller.listen_on("/ip4/0.0.0.0/tcp/0".parse().unwrap());
//!
//! // Runs until everything is finished.
//! tokio_current_thread::block_on_all(swarm_future.for_each(|_| Ok(()))).unwrap();
//! # }
//! ```
extern crate bs58;
extern crate bytes;
@ -244,11 +203,9 @@ extern crate tokio_mock_task;
/// Multi-address re-export.
pub extern crate multiaddr;
mod connection_reuse;
mod keys_proto;
mod peer_id;
mod public_key;
mod unique;
#[cfg(test)]
mod tests;
@ -256,16 +213,12 @@ mod tests;
pub mod either;
pub mod muxing;
pub mod nodes;
pub mod swarm;
pub mod transport;
pub mod upgrade;
pub use self::connection_reuse::ConnectionReuse;
pub use self::multiaddr::Multiaddr;
pub use self::muxing::StreamMuxer;
pub use self::peer_id::PeerId;
pub use self::public_key::PublicKey;
pub use self::swarm::{swarm, SwarmController, SwarmEvents};
pub use self::transport::{MuxedTransport, Transport};
pub use self::unique::{UniqueConnec, UniqueConnecFuture, UniqueConnecState};
pub use self::upgrade::{ConnectionUpgrade, Endpoint};

View File

@ -190,91 +190,3 @@ impl<T: IntoBuf> Into<RwStreamSink<Chan<T>>> for Chan<T> {
RwStreamSink::new(self)
}
}
#[cfg(test)]
mod tests {
use bytes::Bytes;
use futures::{future::{self, Either, Loop}, prelude::*, sync::mpsc};
use std::{io, iter};
use {transport::memory, swarm, ConnectionUpgrade, Endpoint, Transport};
use tokio_codec::{BytesCodec, Framed};
use tokio_current_thread;
#[test]
fn echo() {
#[derive(Clone)]
struct Echo(mpsc::UnboundedSender<()>);
impl<Maf: Send + 'static> ConnectionUpgrade<memory::Channel<Bytes>, Maf> for Echo {
type NamesIter = iter::Once<(Bytes, ())>;
type UpgradeIdentifier = ();
type Output = ();
type MultiaddrFuture = Maf;
type Future = Box<Future<Item=(Self::Output, Self::MultiaddrFuture), Error=io::Error> + Send>;
fn protocol_names(&self) -> Self::NamesIter {
iter::once(("/echo/1.0.0".into(), ()))
}
fn upgrade(self, chan: memory::Channel<Bytes>, _: (), e: Endpoint, maf: Maf) -> Self::Future {
let chan = Framed::new(chan, BytesCodec::new());
match e {
Endpoint::Listener => {
let future = future::loop_fn(chan, move |chan| {
chan.into_future()
.map_err(|(e, _)| e)
.and_then(move |(msg, chan)| {
if let Some(msg) = msg {
println!("listener received: {:?}", msg);
Either::A(chan.send(msg.freeze()).map(Loop::Continue))
} else {
println!("listener received EOF at destination");
Either::B(future::ok(Loop::Break(())))
}
})
});
Box::new(future.map(move |()| ((), maf))) as Box<_>
}
Endpoint::Dialer => {
let future = chan.send("hello world".into())
.and_then(|chan| {
chan.into_future().map_err(|(e, _)| e).map(|(n,_ )| n)
})
.and_then(|msg| {
println!("dialer received: {:?}", msg.unwrap());
self.0.send(())
.map(|_| ())
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
});
Box::new(future.map(move |()| ((), maf))) as Box<_>
}
}
}
}
let (finish_tx, finish_rx) = mpsc::unbounded();
let echo = Echo(finish_tx);
let (dialer, listener) = memory::connector();
let dialer = dialer.with_dummy_muxing().with_upgrade(echo.clone());
let listener = listener.with_dummy_muxing().with_upgrade(echo);
let (control, future) = swarm(listener, |sock, _addr| Ok(sock));
control.listen_on("/memory".parse().expect("/memory is a valid multiaddr")).unwrap();
control.dial("/memory".parse().expect("/memory is a valid multiaddr"), dialer).unwrap();
let finish_rx = finish_rx.into_future()
.map(|_| ())
.map_err(|((), _)| io::Error::new(io::ErrorKind::Other, "receive error"));
let future = future
.for_each(|_| Ok(()))
.select(finish_rx)
.map(|_| ())
.map_err(|(e, _)| e);
tokio_current_thread::block_on_all(future).unwrap();
}
}

View File

@ -29,10 +29,8 @@
//! `UpgradedNode::or_upgrade` methods, you can combine multiple transports and/or upgrades
//! together in a complex chain of protocols negotiation.
use connection_reuse::ConnectionReuse;
use futures::prelude::*;
use multiaddr::Multiaddr;
use muxing::StreamMuxer;
use std::io::Error as IoError;
use tokio_io::{AsyncRead, AsyncWrite};
use upgrade::{ConnectionUpgrade, Endpoint};
@ -243,17 +241,6 @@ pub trait Transport {
DummyMuxing::new(self)
}
/// Turns this `Transport` into a `ConnectionReuse`. If the `Output` implements the
/// `StreamMuxer` trait, the returned object will implement `Transport` and `MuxedTransport`.
#[inline]
fn into_connection_reuse<D, M>(self) -> ConnectionReuse<Self, D, M>
where
Self: Sized + Transport<Output = (D, M)>,
M: StreamMuxer,
{
ConnectionReuse::new(self)
}
/// Wraps around the `Transport` and makes it interruptible.
#[inline]
fn interruptible(self) -> (interruptible::Interruptible<Self>, interruptible::Interrupt)