core/muxing: Flatten StreamMuxer interface to poll_{inbound,outbound,address_change,close} (#2724)

Instead of having a mix of `poll_event`, `poll_outbound` and `poll_close`, we
flatten the entire interface of `StreamMuxer` into 4 individual functions:

- `poll_inbound`
- `poll_outbound`
- `poll_address_change`
- `poll_close`

This design is closer to the design of other async traits like `AsyncRead` and
`AsyncWrite`. It also allows us to delete the `StreamMuxerEvent`.
This commit is contained in:
Thomas Eizinger
2022-07-18 04:20:11 +01:00
committed by GitHub
parent d4f8ec2d48
commit 1a553db596
61 changed files with 281 additions and 696 deletions

View File

@ -20,7 +20,6 @@
mod error;
mod handler_wrapper;
mod substream;
pub(crate) mod pool;
@ -30,18 +29,19 @@ pub use error::{
};
pub use pool::{ConnectionCounters, ConnectionLimits};
pub use pool::{EstablishedConnection, PendingConnection};
pub use substream::{Close, SubstreamEndpoint};
use crate::handler::ConnectionHandler;
use crate::IntoConnectionHandler;
use futures::future::poll_fn;
use handler_wrapper::HandlerWrapper;
use libp2p_core::connection::ConnectedPoint;
use libp2p_core::multiaddr::Multiaddr;
use libp2p_core::muxing::StreamMuxerBox;
use libp2p_core::upgrade;
use libp2p_core::PeerId;
use std::{error::Error, fmt, pin::Pin, task::Context, task::Poll};
use substream::{Muxing, SubstreamEvent};
use libp2p_core::{upgrade, StreamMuxer};
use std::collections::VecDeque;
use std::future::Future;
use std::{error::Error, fmt, io, pin::Pin, task::Context, task::Poll};
/// Information about a successfully established connection.
#[derive(Debug, Clone, PartialEq, Eq)]
@ -52,6 +52,13 @@ pub struct Connected {
pub peer_id: PeerId,
}
/// Endpoint for a received substream.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum SubstreamEndpoint<TDialInfo> {
Dialer(TDialInfo),
Listener,
}
/// Event generated by a [`Connection`].
#[derive(Debug, Clone)]
pub enum Event<T> {
@ -67,19 +74,22 @@ where
THandler: ConnectionHandler,
{
/// Node that handles the muxing.
muxing: substream::Muxing<StreamMuxerBox, handler_wrapper::OutboundOpenInfo<THandler>>,
muxing: StreamMuxerBox,
/// Handler that processes substreams.
handler: HandlerWrapper<THandler>,
/// List of "open_info" that is waiting for new outbound substreams.
open_info: VecDeque<handler_wrapper::OutboundOpenInfo<THandler>>,
}
impl<THandler> fmt::Debug for Connection<THandler>
where
THandler: ConnectionHandler + fmt::Debug,
THandler::OutboundOpenInfo: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Connection")
.field("muxing", &self.muxing)
.field("handler", &self.handler)
.field("open_info", &self.open_info)
.finish()
}
}
@ -108,8 +118,9 @@ where
max_negotiating_inbound_streams,
);
Connection {
muxing: Muxing::new(muxer),
muxing: muxer,
handler: wrapped_handler,
open_info: VecDeque::with_capacity(8),
}
}
@ -120,10 +131,10 @@ where
/// Begins an orderly shutdown of the connection, returning the connection
/// handler and a `Future` that resolves when connection shutdown is complete.
pub fn close(self) -> (THandler, Close<StreamMuxerBox>) {
pub fn close(self) -> (THandler, impl Future<Output = io::Result<()>>) {
(
self.handler.into_connection_handler(),
self.muxing.close().0,
poll_fn(move |cx| self.muxing.poll_close(cx)),
)
}
@ -138,38 +149,38 @@ where
match self.handler.poll(cx)? {
Poll::Pending => {}
Poll::Ready(handler_wrapper::Event::OutboundSubstreamRequest(user_data)) => {
self.muxing.open_substream(user_data);
continue;
self.open_info.push_back(user_data);
continue; // Poll handler until exhausted.
}
Poll::Ready(handler_wrapper::Event::Custom(event)) => {
return Poll::Ready(Ok(Event::Handler(event)));
}
}
// Perform I/O on the connection through the muxer, informing the handler
// of new substreams.
match self.muxing.poll(cx)? {
Poll::Pending => {}
Poll::Ready(SubstreamEvent::InboundSubstream { substream }) => {
self.handler
.inject_substream(substream, SubstreamEndpoint::Listener);
continue;
}
Poll::Ready(SubstreamEvent::OutboundSubstream {
user_data,
substream,
}) => {
if !self.open_info.is_empty() {
if let Poll::Ready(substream) = self.muxing.poll_outbound(cx)? {
let user_data = self
.open_info
.pop_front()
.expect("`open_info` is not empty");
let endpoint = SubstreamEndpoint::Dialer(user_data);
self.handler.inject_substream(substream, endpoint);
continue;
}
Poll::Ready(SubstreamEvent::AddressChange(address)) => {
self.handler.inject_address_change(&address);
return Poll::Ready(Ok(Event::AddressChange(address)));
continue; // Go back to the top, handler can potentially make progress again.
}
}
return Poll::Pending;
if let Poll::Ready(substream) = self.muxing.poll_inbound(cx)? {
self.handler
.inject_substream(substream, SubstreamEndpoint::Listener);
continue; // Go back to the top, handler can potentially make progress again.
}
if let Poll::Ready(address) = self.muxing.poll_address_change(cx)? {
self.handler.inject_address_change(&address);
return Poll::Ready(Ok(Event::AddressChange(address)));
}
return Poll::Pending; // Nothing can make progress, return `Pending`.
}
}
}