protocols/relay: Limit inbound streams (#2698)

* protocols/relay: Use prost-codec

* protocols/relay: Respond to at most one incoming reservation request

Also changes poll order prioritizing

- Error handling over everything.
- Queued events over existing circuits.
- Existing circuits over accepting new circuits.
- Reservation management of existing reservation over new reservation
  requests.

* protocols/relay: Deny <= 8 incoming circuit requests with one per peer

* protocols/relay: Deny new circuits before accepting new circuits
This commit is contained in:
Max Inden
2022-06-08 17:33:24 +02:00
committed by GitHub
parent 0bce7f7fff
commit 0d3787ed04
10 changed files with 248 additions and 237 deletions

View File

@ -41,7 +41,7 @@ use libp2p_swarm::{
NotifyHandler, PollParameters,
};
use std::collections::{hash_map, HashMap, VecDeque};
use std::io::{Error, IoSlice};
use std::io::{Error, ErrorKind, IoSlice};
use std::ops::DerefMut;
use std::pin::Pin;
use std::task::{Context, Poll};
@ -84,7 +84,7 @@ pub enum Event {
/// Denying an inbound circuit request failed.
InboundCircuitReqDenyFailed {
src_peer_id: PeerId,
error: std::io::Error,
error: inbound_stop::UpgradeError,
},
}
@ -320,7 +320,7 @@ impl NetworkBehaviour for Client {
/// A [`NegotiatedSubstream`] acting as a [`RelayedConnection`].
pub enum RelayedConnection {
InboundAccepting {
accept: BoxFuture<'static, Result<RelayedConnection, std::io::Error>>,
accept: BoxFuture<'static, Result<RelayedConnection, Error>>,
},
Operational {
read_buffer: Bytes,
@ -338,7 +338,10 @@ impl RelayedConnection {
) -> Self {
RelayedConnection::InboundAccepting {
accept: async {
let (substream, read_buffer) = circuit.accept().await?;
let (substream, read_buffer) = circuit
.accept()
.await
.map_err(|e| Error::new(ErrorKind::Other, e))?;
Ok(RelayedConnection::Operational {
read_buffer,
substream,

View File

@ -39,11 +39,16 @@ use libp2p_swarm::{
KeepAlive, NegotiatedSubstream, SubstreamProtocol,
};
use log::debug;
use std::collections::VecDeque;
use std::collections::{HashMap, VecDeque};
use std::fmt;
use std::task::{Context, Poll};
use std::time::Duration;
/// The maximum number of circuits being denied concurrently.
///
/// Circuits to be denied exceeding the limit are dropped.
const MAX_NUMBER_DENYING_CIRCUIT: usize = 8;
pub enum In {
Reserve {
to_listener: mpsc::Sender<transport::ToListenerMsg>,
@ -100,7 +105,7 @@ pub enum Event {
/// Denying an inbound circuit request failed.
InboundCircuitReqDenyFailed {
src_peer_id: PeerId,
error: std::io::Error,
error: inbound_stop::UpgradeError,
},
}
@ -196,7 +201,8 @@ pub struct Handler {
/// eventually.
alive_lend_out_substreams: FuturesUnordered<oneshot::Receiver<void::Void>>,
circuit_deny_futs: FuturesUnordered<BoxFuture<'static, (PeerId, Result<(), std::io::Error>)>>,
circuit_deny_futs:
HashMap<PeerId, BoxFuture<'static, Result<(), protocol::inbound_stop::UpgradeError>>>,
/// Futures that try to send errors to the transport.
///
@ -251,12 +257,27 @@ impl ConnectionHandler for Handler {
}
Reservation::None => {
let src_peer_id = inbound_circuit.src_peer_id();
self.circuit_deny_futs.push(
inbound_circuit
.deny(Status::NoReservation)
.map(move |result| (src_peer_id, result))
.boxed(),
)
if self.circuit_deny_futs.len() == MAX_NUMBER_DENYING_CIRCUIT
&& !self.circuit_deny_futs.contains_key(&src_peer_id)
{
log::warn!(
"Dropping inbound circuit request to be denied from {:?} due to exceeding limit.",
src_peer_id,
);
} else if self
.circuit_deny_futs
.insert(
src_peer_id,
inbound_circuit.deny(Status::NoReservation).boxed(),
)
.is_some()
{
log::warn!(
"Dropping existing inbound circuit request to be denied from {:?} in favor of new one.",
src_peer_id
)
}
}
}
}
@ -537,20 +558,28 @@ impl ConnectionHandler for Handler {
}
// Deny incoming circuit requests.
if let Poll::Ready(Some((src_peer_id, result))) = self.circuit_deny_futs.poll_next_unpin(cx)
{
match result {
Ok(()) => {
return Poll::Ready(ConnectionHandlerEvent::Custom(
Event::InboundCircuitReqDenied { src_peer_id },
))
}
Err(error) => {
return Poll::Ready(ConnectionHandlerEvent::Custom(
Event::InboundCircuitReqDenyFailed { src_peer_id, error },
))
}
}
let maybe_event =
self.circuit_deny_futs
.iter_mut()
.find_map(|(src_peer_id, fut)| match fut.poll_unpin(cx) {
Poll::Ready(Ok(())) => Some((
*src_peer_id,
Event::InboundCircuitReqDenied {
src_peer_id: *src_peer_id,
},
)),
Poll::Ready(Err(error)) => Some((
*src_peer_id,
Event::InboundCircuitReqDenyFailed {
src_peer_id: *src_peer_id,
error,
},
)),
Poll::Pending => None,
});
if let Some((src_peer_id, event)) = maybe_event {
self.circuit_deny_futs.remove(&src_peer_id);
return Poll::Ready(ConnectionHandlerEvent::Custom(event));
}
// Send errors to transport.

View File

@ -25,13 +25,10 @@ use bytes::Bytes;
use futures::{future::BoxFuture, prelude::*};
use libp2p_core::{upgrade, Multiaddr, PeerId};
use libp2p_swarm::NegotiatedSubstream;
use prost::Message;
use std::convert::TryInto;
use std::io::Cursor;
use std::iter;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use thiserror::Error;
use unsigned_varint::codec::UviBytes;
pub struct Upgrade {
pub reservation_duration: Duration,
@ -54,23 +51,19 @@ impl upgrade::InboundUpgrade<NegotiatedSubstream> for Upgrade {
type Future = BoxFuture<'static, Result<Self::Output, Self::Error>>;
fn upgrade_inbound(self, substream: NegotiatedSubstream, _: Self::Info) -> Self::Future {
let mut codec = UviBytes::default();
codec.set_max_len(MAX_MESSAGE_SIZE);
let mut substream = Framed::new(substream, codec);
let mut substream = Framed::new(substream, prost_codec::Codec::new(MAX_MESSAGE_SIZE));
async move {
let msg: bytes::BytesMut = substream
.next()
.await
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::UnexpectedEof, ""))??;
let HopMessage {
r#type,
peer,
reservation: _,
limit: _,
status: _,
} = HopMessage::decode(Cursor::new(msg))?;
} = substream
.next()
.await
.ok_or(FatalUpgradeError::StreamClosed)??;
let r#type =
hop_message::Type::from_i32(r#type).ok_or(FatalUpgradeError::ParseTypeField)?;
@ -103,28 +96,22 @@ pub enum UpgradeError {
Fatal(#[from] FatalUpgradeError),
}
impl From<prost::DecodeError> for UpgradeError {
fn from(error: prost::DecodeError) -> Self {
Self::Fatal(error.into())
}
}
impl From<std::io::Error> for UpgradeError {
fn from(error: std::io::Error) -> Self {
impl From<prost_codec::Error> for UpgradeError {
fn from(error: prost_codec::Error) -> Self {
Self::Fatal(error.into())
}
}
#[derive(Debug, Error)]
pub enum FatalUpgradeError {
#[error("Failed to decode message: {0}.")]
Decode(
#[error("Failed to encode or decode")]
Codec(
#[from]
#[source]
prost::DecodeError,
prost_codec::Error,
),
#[error(transparent)]
Io(#[from] std::io::Error),
#[error("Stream closed")]
StreamClosed,
#[error("Failed to parse response type field.")]
ParseTypeField,
#[error("Failed to parse peer id.")]
@ -141,14 +128,14 @@ pub enum Req {
}
pub struct ReservationReq {
substream: Framed<NegotiatedSubstream, UviBytes<Cursor<Vec<u8>>>>,
substream: Framed<NegotiatedSubstream, prost_codec::Codec<HopMessage>>,
reservation_duration: Duration,
max_circuit_duration: Duration,
max_circuit_bytes: u64,
}
impl ReservationReq {
pub async fn accept(self, addrs: Vec<Multiaddr>) -> Result<(), std::io::Error> {
pub async fn accept(self, addrs: Vec<Multiaddr>) -> Result<(), UpgradeError> {
let msg = HopMessage {
r#type: hop_message::Type::Status.into(),
peer: None,
@ -175,7 +162,7 @@ impl ReservationReq {
self.send(msg).await
}
pub async fn deny(self, status: Status) -> Result<(), std::io::Error> {
pub async fn deny(self, status: Status) -> Result<(), UpgradeError> {
let msg = HopMessage {
r#type: hop_message::Type::Status.into(),
peer: None,
@ -187,11 +174,8 @@ impl ReservationReq {
self.send(msg).await
}
async fn send(mut self, msg: HopMessage) -> Result<(), std::io::Error> {
let mut encoded_msg = Vec::with_capacity(msg.encoded_len());
msg.encode(&mut encoded_msg)
.expect("Vec to have sufficient capacity.");
self.substream.send(Cursor::new(encoded_msg)).await?;
async fn send(mut self, msg: HopMessage) -> Result<(), UpgradeError> {
self.substream.send(msg).await?;
self.substream.flush().await?;
self.substream.close().await?;
@ -201,7 +185,7 @@ impl ReservationReq {
pub struct CircuitReq {
dst: PeerId,
substream: Framed<NegotiatedSubstream, UviBytes<Cursor<Vec<u8>>>>,
substream: Framed<NegotiatedSubstream, prost_codec::Codec<HopMessage>>,
}
impl CircuitReq {
@ -209,7 +193,7 @@ impl CircuitReq {
self.dst
}
pub async fn accept(mut self) -> Result<(NegotiatedSubstream, Bytes), std::io::Error> {
pub async fn accept(mut self) -> Result<(NegotiatedSubstream, Bytes), UpgradeError> {
let msg = HopMessage {
r#type: hop_message::Type::Status.into(),
peer: None,
@ -234,7 +218,7 @@ impl CircuitReq {
Ok((io, read_buffer.freeze()))
}
pub async fn deny(mut self, status: Status) -> Result<(), std::io::Error> {
pub async fn deny(mut self, status: Status) -> Result<(), UpgradeError> {
let msg = HopMessage {
r#type: hop_message::Type::Status.into(),
peer: None,
@ -243,14 +227,11 @@ impl CircuitReq {
status: Some(status.into()),
};
self.send(msg).await?;
self.substream.close().await
self.substream.close().await.map_err(Into::into)
}
async fn send(&mut self, msg: HopMessage) -> Result<(), std::io::Error> {
let mut encoded_msg = Vec::with_capacity(msg.encoded_len());
msg.encode(&mut encoded_msg)
.expect("Vec to have sufficient capacity.");
self.substream.send(Cursor::new(encoded_msg)).await?;
async fn send(&mut self, msg: HopMessage) -> Result<(), prost_codec::Error> {
self.substream.send(msg).await?;
self.substream.flush().await?;
Ok(())

View File

@ -25,11 +25,8 @@ use bytes::Bytes;
use futures::{future::BoxFuture, prelude::*};
use libp2p_core::{upgrade, PeerId};
use libp2p_swarm::NegotiatedSubstream;
use prost::Message;
use std::io::Cursor;
use std::iter;
use thiserror::Error;
use unsigned_varint::codec::UviBytes;
pub struct Upgrade {}
@ -48,22 +45,18 @@ impl upgrade::InboundUpgrade<NegotiatedSubstream> for Upgrade {
type Future = BoxFuture<'static, Result<Self::Output, Self::Error>>;
fn upgrade_inbound(self, substream: NegotiatedSubstream, _: Self::Info) -> Self::Future {
let mut codec = UviBytes::default();
codec.set_max_len(MAX_MESSAGE_SIZE);
let mut substream = Framed::new(substream, codec);
let mut substream = Framed::new(substream, prost_codec::Codec::new(MAX_MESSAGE_SIZE));
async move {
let msg: bytes::BytesMut = substream
.next()
.await
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::UnexpectedEof, ""))??;
let StopMessage {
r#type,
peer,
limit,
status: _,
} = StopMessage::decode(Cursor::new(msg))?;
} = substream
.next()
.await
.ok_or(FatalUpgradeError::StreamClosed)??;
let r#type =
stop_message::Type::from_i32(r#type).ok_or(FatalUpgradeError::ParseTypeField)?;
@ -91,28 +84,22 @@ pub enum UpgradeError {
Fatal(#[from] FatalUpgradeError),
}
impl From<prost::DecodeError> for UpgradeError {
fn from(error: prost::DecodeError) -> Self {
Self::Fatal(error.into())
}
}
impl From<std::io::Error> for UpgradeError {
fn from(error: std::io::Error) -> Self {
impl From<prost_codec::Error> for UpgradeError {
fn from(error: prost_codec::Error) -> Self {
Self::Fatal(error.into())
}
}
#[derive(Debug, Error)]
pub enum FatalUpgradeError {
#[error("Failed to decode message: {0}.")]
Decode(
#[error("Failed to encode or decode")]
Codec(
#[from]
#[source]
prost::DecodeError,
prost_codec::Error,
),
#[error(transparent)]
Io(#[from] std::io::Error),
#[error("Stream closed")]
StreamClosed,
#[error("Failed to parse response type field.")]
ParseTypeField,
#[error("Failed to parse peer id.")]
@ -124,7 +111,7 @@ pub enum FatalUpgradeError {
}
pub struct Circuit {
substream: Framed<NegotiatedSubstream, UviBytes<Cursor<Vec<u8>>>>,
substream: Framed<NegotiatedSubstream, prost_codec::Codec<StopMessage>>,
src_peer_id: PeerId,
limit: Option<protocol::Limit>,
}
@ -138,7 +125,7 @@ impl Circuit {
self.limit
}
pub async fn accept(mut self) -> Result<(NegotiatedSubstream, Bytes), std::io::Error> {
pub async fn accept(mut self) -> Result<(NegotiatedSubstream, Bytes), UpgradeError> {
let msg = StopMessage {
r#type: stop_message::Type::Status.into(),
peer: None,
@ -162,7 +149,7 @@ impl Circuit {
Ok((io, read_buffer.freeze()))
}
pub async fn deny(mut self, status: Status) -> Result<(), std::io::Error> {
pub async fn deny(mut self, status: Status) -> Result<(), UpgradeError> {
let msg = StopMessage {
r#type: stop_message::Type::Status.into(),
peer: None,
@ -170,14 +157,11 @@ impl Circuit {
status: Some(status.into()),
};
self.send(msg).await
self.send(msg).await.map_err(Into::into)
}
async fn send(&mut self, msg: StopMessage) -> Result<(), std::io::Error> {
let mut encoded_msg = Vec::with_capacity(msg.encoded_len());
msg.encode(&mut encoded_msg)
.expect("Vec to have sufficient capacity.");
self.substream.send(Cursor::new(encoded_msg)).await?;
async fn send(&mut self, msg: StopMessage) -> Result<(), prost_codec::Error> {
self.substream.send(msg).await?;
self.substream.flush().await?;
Ok(())

View File

@ -26,13 +26,10 @@ use futures::{future::BoxFuture, prelude::*};
use futures_timer::Delay;
use libp2p_core::{upgrade, Multiaddr, PeerId};
use libp2p_swarm::NegotiatedSubstream;
use prost::Message;
use std::convert::TryFrom;
use std::io::Cursor;
use std::iter;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use thiserror::Error;
use unsigned_varint::codec::UviBytes;
pub enum Upgrade {
Reserve,
@ -74,28 +71,20 @@ impl upgrade::OutboundUpgrade<NegotiatedSubstream> for Upgrade {
},
};
let mut encoded_msg = Vec::with_capacity(msg.encoded_len());
msg.encode(&mut encoded_msg)
.expect("Vec to have sufficient capacity.");
let mut codec = UviBytes::default();
codec.set_max_len(MAX_MESSAGE_SIZE);
let mut substream = Framed::new(substream, codec);
let mut substream = Framed::new(substream, prost_codec::Codec::new(MAX_MESSAGE_SIZE));
async move {
substream.send(Cursor::new(encoded_msg)).await?;
let msg: bytes::BytesMut = substream
.next()
.await
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::UnexpectedEof, ""))??;
substream.send(msg).await?;
let HopMessage {
r#type,
peer: _,
reservation,
limit,
status,
} = HopMessage::decode(Cursor::new(msg))?;
} = substream
.next()
.await
.ok_or(FatalUpgradeError::StreamClosed)??;
let r#type =
hop_message::Type::from_i32(r#type).ok_or(FatalUpgradeError::ParseTypeField)?;
@ -216,14 +205,8 @@ pub enum UpgradeError {
Fatal(#[from] FatalUpgradeError),
}
impl From<std::io::Error> for UpgradeError {
fn from(error: std::io::Error) -> Self {
Self::Fatal(error.into())
}
}
impl From<prost::DecodeError> for UpgradeError {
fn from(error: prost::DecodeError) -> Self {
impl From<prost_codec::Error> for UpgradeError {
fn from(error: prost_codec::Error) -> Self {
Self::Fatal(error.into())
}
}
@ -250,14 +233,14 @@ pub enum ReservationFailedReason {
#[derive(Debug, Error)]
pub enum FatalUpgradeError {
#[error("Failed to decode message: {0}.")]
Decode(
#[error("Failed to encode or decode")]
Codec(
#[from]
#[source]
prost::DecodeError,
prost_codec::Error,
),
#[error(transparent)]
Io(#[from] std::io::Error),
#[error("Stream closed")]
StreamClosed,
#[error("Expected 'status' field to be set.")]
MissingStatusField,
#[error("Expected 'reservation' field to be set.")]

View File

@ -25,13 +25,10 @@ use bytes::Bytes;
use futures::{future::BoxFuture, prelude::*};
use libp2p_core::{upgrade, PeerId};
use libp2p_swarm::NegotiatedSubstream;
use prost::Message;
use std::convert::TryInto;
use std::io::Cursor;
use std::iter;
use std::time::Duration;
use thiserror::Error;
use unsigned_varint::codec::UviBytes;
pub struct Upgrade {
pub relay_peer_id: PeerId,
@ -72,27 +69,19 @@ impl upgrade::OutboundUpgrade<NegotiatedSubstream> for Upgrade {
status: None,
};
let mut encoded_msg = Vec::with_capacity(msg.encoded_len());
msg.encode(&mut encoded_msg)
.expect("Vec to have sufficient capacity.");
let mut codec = UviBytes::default();
codec.set_max_len(MAX_MESSAGE_SIZE);
let mut substream = Framed::new(substream, codec);
let mut substream = Framed::new(substream, prost_codec::Codec::new(MAX_MESSAGE_SIZE));
async move {
substream.send(std::io::Cursor::new(encoded_msg)).await?;
let msg: bytes::BytesMut = substream
.next()
.await
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::UnexpectedEof, ""))??;
substream.send(msg).await?;
let StopMessage {
r#type,
peer: _,
limit: _,
status,
} = StopMessage::decode(Cursor::new(msg))?;
} = substream
.next()
.await
.ok_or(FatalUpgradeError::StreamClosed)??;
let r#type =
stop_message::Type::from_i32(r#type).ok_or(FatalUpgradeError::ParseTypeField)?;
@ -141,14 +130,8 @@ pub enum UpgradeError {
Fatal(#[from] FatalUpgradeError),
}
impl From<std::io::Error> for UpgradeError {
fn from(error: std::io::Error) -> Self {
Self::Fatal(error.into())
}
}
impl From<prost::DecodeError> for UpgradeError {
fn from(error: prost::DecodeError) -> Self {
impl From<prost_codec::Error> for UpgradeError {
fn from(error: prost_codec::Error) -> Self {
Self::Fatal(error.into())
}
}
@ -163,14 +146,14 @@ pub enum CircuitFailedReason {
#[derive(Debug, Error)]
pub enum FatalUpgradeError {
#[error("Failed to decode message: {0}.")]
Decode(
#[error("Failed to encode or decode")]
Codec(
#[from]
#[source]
prost::DecodeError,
prost_codec::Error,
),
#[error(transparent)]
Io(#[from] std::io::Error),
#[error("Stream closed")]
StreamClosed,
#[error("Expected 'status' field to be set.")]
MissingStatusField,
#[error("Failed to parse response type field.")]

View File

@ -138,14 +138,14 @@ pub enum Event {
/// Accepting an inbound reservation request failed.
ReservationReqAcceptFailed {
src_peer_id: PeerId,
error: std::io::Error,
error: inbound_hop::UpgradeError,
},
/// An inbound reservation request has been denied.
ReservationReqDenied { src_peer_id: PeerId },
/// Denying an inbound reservation request has failed.
ReservationReqDenyFailed {
src_peer_id: PeerId,
error: std::io::Error,
error: inbound_hop::UpgradeError,
},
/// An inbound reservation has timed out.
ReservationTimedOut { src_peer_id: PeerId },
@ -162,7 +162,7 @@ pub enum Event {
CircuitReqDenyFailed {
src_peer_id: PeerId,
dst_peer_id: PeerId,
error: std::io::Error,
error: inbound_hop::UpgradeError,
},
/// An inbound cirucit request has been accepted.
CircuitReqAccepted {
@ -179,7 +179,7 @@ pub enum Event {
CircuitReqAcceptFailed {
src_peer_id: PeerId,
dst_peer_id: PeerId,
error: std::io::Error,
error: inbound_hop::UpgradeError,
},
/// An inbound circuit has closed.
CircuitClosed {

View File

@ -153,11 +153,11 @@ pub enum Event {
renewed: bool,
},
/// Accepting an inbound reservation request failed.
ReservationReqAcceptFailed { error: std::io::Error },
ReservationReqAcceptFailed { error: inbound_hop::UpgradeError },
/// An inbound reservation request has been denied.
ReservationReqDenied {},
/// Denying an inbound reservation request has failed.
ReservationReqDenyFailed { error: std::io::Error },
ReservationReqDenyFailed { error: inbound_hop::UpgradeError },
/// An inbound reservation has timed out.
ReservationTimedOut {},
/// An inbound circuit request has been received.
@ -178,7 +178,7 @@ pub enum Event {
CircuitReqDenyFailed {
circuit_id: Option<CircuitId>,
dst_peer_id: PeerId,
error: std::io::Error,
error: inbound_hop::UpgradeError,
},
/// An inbound cirucit request has been accepted.
CircuitReqAccepted {
@ -189,7 +189,7 @@ pub enum Event {
CircuitReqAcceptFailed {
circuit_id: CircuitId,
dst_peer_id: PeerId,
error: std::io::Error,
error: inbound_hop::UpgradeError,
},
/// An outbound substream for an inbound circuit request has been
/// negotiated.
@ -354,8 +354,7 @@ impl IntoConnectionHandler for Prototype {
config: self.config,
queued_events: Default::default(),
pending_error: Default::default(),
reservation_accept_futures: Default::default(),
reservation_deny_futures: Default::default(),
reservation_request_future: Default::default(),
circuit_accept_futures: Default::default(),
circuit_deny_futures: Default::default(),
alive_lend_out_substreams: Default::default(),
@ -403,17 +402,20 @@ pub struct Handler {
/// Until when to keep the connection alive.
keep_alive: KeepAlive,
/// Futures accepting an inbound reservation request.
reservation_accept_futures: Futures<Result<(), std::io::Error>>,
/// Futures denying an inbound reservation request.
reservation_deny_futures: Futures<Result<(), std::io::Error>>,
/// Future handling inbound reservation request.
reservation_request_future: Option<ReservationRequestFuture>,
/// Timeout for the currently active reservation.
active_reservation: Option<Delay>,
/// Futures accepting an inbound circuit request.
circuit_accept_futures: Futures<Result<CircuitParts, (CircuitId, PeerId, std::io::Error)>>,
circuit_accept_futures:
Futures<Result<CircuitParts, (CircuitId, PeerId, inbound_hop::UpgradeError)>>,
/// Futures deying an inbound circuit request.
circuit_deny_futures: Futures<(Option<CircuitId>, PeerId, Result<(), std::io::Error>)>,
circuit_deny_futures: Futures<(
Option<CircuitId>,
PeerId,
Result<(), inbound_hop::UpgradeError>,
)>,
/// Tracks substreams lend out to other [`Handler`]s.
///
/// Contains a [`futures::future::Future`] for each lend out substream that
@ -427,6 +429,11 @@ pub struct Handler {
circuits: Futures<(CircuitId, PeerId, Result<(), std::io::Error>)>,
}
enum ReservationRequestFuture {
Accepting(BoxFuture<'static, Result<(), inbound_hop::UpgradeError>>),
Denying(BoxFuture<'static, Result<(), inbound_hop::UpgradeError>>),
}
type Futures<T> = FuturesUnordered<BoxFuture<'static, T>>;
impl ConnectionHandler for Handler {
@ -512,15 +519,29 @@ impl ConnectionHandler for Handler {
inbound_reservation_req,
addrs,
} => {
self.reservation_accept_futures
.push(inbound_reservation_req.accept(addrs).boxed());
if self
.reservation_request_future
.replace(ReservationRequestFuture::Accepting(
inbound_reservation_req.accept(addrs).boxed(),
))
.is_some()
{
log::warn!("Dropping existing deny/accept future in favor of new one.")
}
}
In::DenyReservationReq {
inbound_reservation_req,
status,
} => {
self.reservation_deny_futures
.push(inbound_reservation_req.deny(status).boxed());
if self
.reservation_request_future
.replace(ReservationRequestFuture::Denying(
inbound_reservation_req.deny(status).boxed(),
))
.is_some()
{
log::warn!("Dropping existing deny/accept future in favor of new one.")
}
}
In::NegotiateOutboundConnect {
circuit_id,
@ -723,6 +744,7 @@ impl ConnectionHandler for Handler {
return Poll::Ready(event);
}
// Progress existing circuits.
if let Poll::Ready(Some((circuit_id, dst_peer_id, result))) =
self.circuits.poll_next_unpin(cx)
{
@ -744,40 +766,30 @@ impl ConnectionHandler for Handler {
}
}
if let Poll::Ready(Some(result)) = self.reservation_accept_futures.poll_next_unpin(cx) {
// Deny new circuits.
if let Poll::Ready(Some((circuit_id, dst_peer_id, result))) =
self.circuit_deny_futures.poll_next_unpin(cx)
{
match result {
Ok(()) => {
let renewed = self
.active_reservation
.replace(Delay::new(self.config.reservation_duration))
.is_some();
return Poll::Ready(ConnectionHandlerEvent::Custom(
Event::ReservationReqAccepted { renewed },
));
return Poll::Ready(ConnectionHandlerEvent::Custom(Event::CircuitReqDenied {
circuit_id,
dst_peer_id,
}));
}
Err(error) => {
return Poll::Ready(ConnectionHandlerEvent::Custom(
Event::ReservationReqAcceptFailed { error },
));
}
}
}
if let Poll::Ready(Some(result)) = self.reservation_deny_futures.poll_next_unpin(cx) {
match result {
Ok(()) => {
return Poll::Ready(ConnectionHandlerEvent::Custom(
Event::ReservationReqDenied {},
))
}
Err(error) => {
return Poll::Ready(ConnectionHandlerEvent::Custom(
Event::ReservationReqDenyFailed { error },
Event::CircuitReqDenyFailed {
circuit_id,
dst_peer_id,
error,
},
));
}
}
}
// Accept new circuits.
if let Poll::Ready(Some(result)) = self.circuit_accept_futures.poll_next_unpin(cx) {
match result {
Ok(parts) => {
@ -838,32 +850,7 @@ impl ConnectionHandler for Handler {
}
}
if let Poll::Ready(Some((circuit_id, dst_peer_id, result))) =
self.circuit_deny_futures.poll_next_unpin(cx)
{
match result {
Ok(()) => {
return Poll::Ready(ConnectionHandlerEvent::Custom(Event::CircuitReqDenied {
circuit_id,
dst_peer_id,
}));
}
Err(error) => {
return Poll::Ready(ConnectionHandlerEvent::Custom(
Event::CircuitReqDenyFailed {
circuit_id,
dst_peer_id,
error,
},
));
}
}
}
while let Poll::Ready(Some(Err(Canceled))) =
self.alive_lend_out_substreams.poll_next_unpin(cx)
{}
// Check active reservation.
if let Some(Poll::Ready(())) = self
.active_reservation
.as_mut()
@ -875,8 +862,58 @@ impl ConnectionHandler for Handler {
));
}
if self.reservation_accept_futures.is_empty()
&& self.reservation_deny_futures.is_empty()
// Progress reservation request.
match self.reservation_request_future.as_mut() {
Some(ReservationRequestFuture::Accepting(fut)) => {
if let Poll::Ready(result) = fut.poll_unpin(cx) {
self.reservation_request_future = None;
match result {
Ok(()) => {
let renewed = self
.active_reservation
.replace(Delay::new(self.config.reservation_duration))
.is_some();
return Poll::Ready(ConnectionHandlerEvent::Custom(
Event::ReservationReqAccepted { renewed },
));
}
Err(error) => {
return Poll::Ready(ConnectionHandlerEvent::Custom(
Event::ReservationReqAcceptFailed { error },
));
}
}
}
}
Some(ReservationRequestFuture::Denying(fut)) => {
if let Poll::Ready(result) = fut.poll_unpin(cx) {
self.reservation_request_future = None;
match result {
Ok(()) => {
return Poll::Ready(ConnectionHandlerEvent::Custom(
Event::ReservationReqDenied {},
))
}
Err(error) => {
return Poll::Ready(ConnectionHandlerEvent::Custom(
Event::ReservationReqDenyFailed { error },
));
}
}
}
}
None => {}
}
// Check lend out substreams.
while let Poll::Ready(Some(Err(Canceled))) =
self.alive_lend_out_substreams.poll_next_unpin(cx)
{}
// Check keep alive status.
if self.reservation_request_future.is_none()
&& self.circuit_accept_futures.is_empty()
&& self.circuit_deny_futures.is_empty()
&& self.alive_lend_out_substreams.is_empty()