use crate::{
Multiaddr,
Transport,
StreamMuxer,
connection::{
Connected,
ConnectedPoint,
ConnectionHandler,
Connection,
ConnectionId,
ConnectionLimit,
EstablishedConnection,
EstablishedConnectionIter,
IntoConnectionHandler,
PendingConnection,
Substream,
pool::Pool,
},
PeerId
};
use fnv::FnvHashMap;
use smallvec::SmallVec;
use std::{
collections::hash_map,
error,
fmt,
};
use super::{Network, DialingOpts};
pub enum Peer<'a, TTrans, TInEvent, TOutEvent, THandler>
where
TTrans: Transport,
THandler: IntoConnectionHandler
{
Connected(ConnectedPeer<'a, TTrans, TInEvent, TOutEvent, THandler>),
Dialing(DialingPeer<'a, TTrans, TInEvent, TOutEvent, THandler>),
Disconnected(DisconnectedPeer<'a, TTrans, TInEvent, TOutEvent, THandler>),
Local,
}
impl<'a, TTrans, TInEvent, TOutEvent, THandler> fmt::Debug for
Peer<'a, TTrans, TInEvent, TOutEvent, THandler>
where
TTrans: Transport,
THandler: IntoConnectionHandler,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self {
Peer::Connected(p) => {
f.debug_struct("Connected")
.field("peer", &p)
.finish()
}
Peer::Dialing(p) => {
f.debug_struct("Dialing")
.field("peer", &p)
.finish()
}
Peer::Disconnected(p) => {
f.debug_struct("Disconnected")
.field("peer", &p)
.finish()
}
Peer::Local => {
f.debug_struct("Local")
.finish()
}
}
}
}
impl<'a, TTrans, TInEvent, TOutEvent, THandler>
Peer<'a, TTrans, TInEvent, TOutEvent, THandler>
where
TTrans: Transport,
THandler: IntoConnectionHandler,
{
pub(super) fn new(
network: &'a mut Network<TTrans, TInEvent, TOutEvent, THandler>,
peer_id: PeerId
) -> Self {
if peer_id == network.local_peer_id {
return Peer::Local;
}
if network.pool.is_connected(&peer_id) {
return Self::connected(network, peer_id)
}
if network.dialing.get_mut(&peer_id).is_some() {
return Self::dialing(network, peer_id);
}
Self::disconnected(network, peer_id)
}
fn disconnected(
network: &'a mut Network<TTrans, TInEvent, TOutEvent, THandler>,
peer_id: PeerId
) -> Self {
Peer::Disconnected(DisconnectedPeer { network, peer_id })
}
fn connected(
network: &'a mut Network<TTrans, TInEvent, TOutEvent, THandler>,
peer_id: PeerId
) -> Self {
Peer::Connected(ConnectedPeer { network, peer_id })
}
fn dialing(
network: &'a mut Network<TTrans, TInEvent, TOutEvent, THandler>,
peer_id: PeerId
) -> Self {
Peer::Dialing(DialingPeer { network, peer_id })
}
}
impl<'a, TTrans, TMuxer, TInEvent, TOutEvent, THandler>
Peer<'a, TTrans, TInEvent, TOutEvent, THandler>
where
TTrans: Transport<Output = (PeerId, TMuxer)> + Clone,
TTrans::Error: Send + 'static,
TTrans::Dial: Send + 'static,
TMuxer: StreamMuxer + Send + Sync + 'static,
TMuxer::OutboundSubstream: Send,
TInEvent: Send + 'static,
TOutEvent: Send + 'static,
THandler: IntoConnectionHandler + Send + 'static,
THandler::Handler: ConnectionHandler<Substream = Substream<TMuxer>, InEvent = TInEvent, OutEvent = TOutEvent> + Send,
<THandler::Handler as ConnectionHandler>::OutboundOpenInfo: Send,
<THandler::Handler as ConnectionHandler>::Error: error::Error + Send + 'static,
{
pub fn is_connected(&self) -> bool {
match self {
Peer::Connected(..) => true,
Peer::Dialing(peer) => peer.is_connected(),
Peer::Disconnected(..) => false,
Peer::Local => false
}
}
pub fn is_dialing(&self) -> bool {
match self {
Peer::Dialing(_) => true,
Peer::Connected(peer) => peer.is_dialing(),
Peer::Disconnected(..) => false,
Peer::Local => false
}
}
pub fn is_disconnected(&self) -> bool {
matches!(self, Peer::Disconnected(..))
}
pub fn dial<I>(self, address: Multiaddr, remaining: I, handler: THandler)
-> Result<
(ConnectionId, DialingPeer<'a, TTrans, TInEvent, TOutEvent, THandler>),
ConnectionLimit
>
where
I: IntoIterator<Item = Multiaddr>,
{
let (peer_id, network) = match self {
Peer::Connected(p) => (p.peer_id, p.network),
Peer::Dialing(p) => (p.peer_id, p.network),
Peer::Disconnected(p) => (p.peer_id, p.network),
Peer::Local => return Err(ConnectionLimit { current: 0, limit: 0 })
};
let id = network.dial_peer(DialingOpts {
peer: peer_id,
handler,
address,
remaining: remaining.into_iter().collect(),
})?;
Ok((id, DialingPeer { network, peer_id }))
}
pub fn into_connected(self) -> Option<
ConnectedPeer<'a, TTrans, TInEvent, TOutEvent, THandler>
> {
match self {
Peer::Connected(peer) => Some(peer),
Peer::Dialing(peer) => peer.into_connected(),
Peer::Disconnected(..) => None,
Peer::Local => None,
}
}
pub fn into_dialing(self) -> Option<
DialingPeer<'a, TTrans, TInEvent, TOutEvent, THandler>
> {
match self {
Peer::Dialing(peer) => Some(peer),
Peer::Connected(peer) => peer.into_dialing(),
Peer::Disconnected(..) => None,
Peer::Local => None
}
}
pub fn into_disconnected(self) -> Option<
DisconnectedPeer<'a, TTrans, TInEvent, TOutEvent, THandler>
> {
match self {
Peer::Disconnected(peer) => Some(peer),
_ => None,
}
}
}
pub struct ConnectedPeer<'a, TTrans, TInEvent, TOutEvent, THandler>
where
TTrans: Transport,
THandler: IntoConnectionHandler,
{
network: &'a mut Network<TTrans, TInEvent, TOutEvent, THandler>,
peer_id: PeerId,
}
impl<'a, TTrans, TInEvent, TOutEvent, THandler>
ConnectedPeer<'a, TTrans, TInEvent, TOutEvent, THandler>
where
TTrans: Transport,
THandler: IntoConnectionHandler,
{
pub fn id(&self) -> &PeerId {
&self.peer_id
}
pub fn into_peer(self) -> Peer<'a, TTrans, TInEvent, TOutEvent, THandler> {
Peer::Connected(self)
}
pub fn connection(&mut self, id: ConnectionId)
-> Option<EstablishedConnection<TInEvent>>
{
self.network.pool.get_established(id)
}
pub fn num_connections(&self) -> u32 {
self.network.pool.num_peer_established(&self.peer_id)
}
pub fn is_dialing(&self) -> bool {
self.network.dialing.contains_key(&self.peer_id)
}
pub fn into_dialing(self) -> Option<
DialingPeer<'a, TTrans, TInEvent, TOutEvent, THandler>
> {
if self.network.dialing.contains_key(&self.peer_id) {
Some(DialingPeer { network: self.network, peer_id: self.peer_id })
} else {
None
}
}
pub fn connections(&mut self) ->
EstablishedConnectionIter<
impl Iterator<Item = ConnectionId>,
TInEvent,
TOutEvent,
THandler,
TTrans::Error,
<THandler::Handler as ConnectionHandler>::Error>
{
self.network.pool.iter_peer_established(&self.peer_id)
}
pub fn some_connection(&mut self)
-> EstablishedConnection<TInEvent>
{
self.connections()
.into_first()
.expect("By `Peer::new` and the definition of `ConnectedPeer`.")
}
pub fn disconnect(self)
-> DisconnectedPeer<'a, TTrans, TInEvent, TOutEvent, THandler>
{
self.network.disconnect(&self.peer_id);
DisconnectedPeer { network: self.network, peer_id: self.peer_id }
}
}
impl<'a, TTrans, TInEvent, TOutEvent, THandler> fmt::Debug for
ConnectedPeer<'a, TTrans, TInEvent, TOutEvent, THandler>
where
TTrans: Transport,
THandler: IntoConnectionHandler,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("ConnectedPeer")
.field("peer_id", &self.peer_id)
.field("established", &self.network.pool.iter_peer_established_info(&self.peer_id))
.field("attempts", &self.network.dialing.get(&self.peer_id))
.finish()
}
}
pub struct DialingPeer<'a, TTrans, TInEvent, TOutEvent, THandler>
where
TTrans: Transport,
THandler: IntoConnectionHandler,
{
network: &'a mut Network<TTrans, TInEvent, TOutEvent, THandler>,
peer_id: PeerId,
}
impl<'a, TTrans, TInEvent, TOutEvent, THandler>
DialingPeer<'a, TTrans, TInEvent, TOutEvent, THandler>
where
TTrans: Transport,
THandler: IntoConnectionHandler,
{
pub fn id(&self) -> &PeerId {
&self.peer_id
}
pub fn into_peer(self) -> Peer<'a, TTrans, TInEvent, TOutEvent, THandler> {
Peer::Dialing(self)
}
pub fn disconnect(self)
-> DisconnectedPeer<'a, TTrans, TInEvent, TOutEvent, THandler>
{
self.network.disconnect(&self.peer_id);
DisconnectedPeer { network: self.network, peer_id: self.peer_id }
}
pub fn is_connected(&self) -> bool {
self.network.pool.is_connected(&self.peer_id)
}
pub fn into_connected(self)
-> Option<ConnectedPeer<'a, TTrans, TInEvent, TOutEvent, THandler>>
{
if self.is_connected() {
Some(ConnectedPeer { peer_id: self.peer_id, network: self.network })
} else {
None
}
}
pub fn attempt(&mut self, id: ConnectionId)
-> Option<DialingAttempt<'_, TInEvent>>
{
if let hash_map::Entry::Occupied(attempts) = self.network.dialing.entry(self.peer_id) {
if let Some(pos) = attempts.get().iter().position(|s| s.current.0 == id) {
if let Some(inner) = self.network.pool.get_outgoing(id) {
return Some(DialingAttempt { pos, inner, attempts })
}
}
}
None
}
pub fn attempts(&mut self)
-> DialingAttemptIter<'_,
TInEvent,
TOutEvent,
THandler,
TTrans::Error,
<THandler::Handler as ConnectionHandler>::Error>
{
DialingAttemptIter::new(&self.peer_id, &mut self.network.pool, &mut self.network.dialing)
}
pub fn some_attempt(&mut self)
-> DialingAttempt<'_, TInEvent>
{
self.attempts()
.into_first()
.expect("By `Peer::new` and the definition of `DialingPeer`.")
}
}
impl<'a, TTrans, TInEvent, TOutEvent, THandler> fmt::Debug for
DialingPeer<'a, TTrans, TInEvent, TOutEvent, THandler>
where
TTrans: Transport,
THandler: IntoConnectionHandler,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("DialingPeer")
.field("peer_id", &self.peer_id)
.field("established", &self.network.pool.iter_peer_established_info(&self.peer_id))
.field("attempts", &self.network.dialing.get(&self.peer_id))
.finish()
}
}
pub struct DisconnectedPeer<'a, TTrans, TInEvent, TOutEvent, THandler>
where
TTrans: Transport,
THandler: IntoConnectionHandler,
{
peer_id: PeerId,
network: &'a mut Network<TTrans, TInEvent, TOutEvent, THandler>,
}
impl<'a, TTrans, TInEvent, TOutEvent, THandler> fmt::Debug for
DisconnectedPeer<'a, TTrans, TInEvent, TOutEvent, THandler>
where
TTrans: Transport,
THandler: IntoConnectionHandler,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("DisconnectedPeer")
.field("peer_id", &self.peer_id)
.finish()
}
}
impl<'a, TTrans, TInEvent, TOutEvent, THandler>
DisconnectedPeer<'a, TTrans, TInEvent, TOutEvent, THandler>
where
TTrans: Transport,
THandler: IntoConnectionHandler,
{
pub fn id(&self) -> &PeerId {
&self.peer_id
}
pub fn into_peer(self) -> Peer<'a, TTrans, TInEvent, TOutEvent, THandler> {
Peer::Disconnected(self)
}
pub fn set_connected<TMuxer>(
self,
connected: Connected,
connection: Connection<TMuxer, THandler::Handler>,
) -> Result<
ConnectedPeer<'a, TTrans, TInEvent, TOutEvent, THandler>,
ConnectionLimit
> where
TInEvent: Send + 'static,
TOutEvent: Send + 'static,
THandler: Send + 'static,
TTrans::Error: Send + 'static,
THandler::Handler: ConnectionHandler<Substream = Substream<TMuxer>, InEvent = TInEvent, OutEvent = TOutEvent> + Send,
<THandler::Handler as ConnectionHandler>::OutboundOpenInfo: Send,
<THandler::Handler as ConnectionHandler>::Error: error::Error + Send + 'static,
TMuxer: StreamMuxer + Send + Sync + 'static,
TMuxer::OutboundSubstream: Send,
{
if connected.peer_id != self.peer_id {
panic!("Invalid peer ID given: {:?}. Expected: {:?}", connected.peer_id, self.peer_id)
}
self.network.pool.add(connection, connected)
.map(move |_id| ConnectedPeer {
network: self.network,
peer_id: self.peer_id,
})
}
}
#[derive(Debug, Clone)]
pub(super) struct DialingState {
pub(super) current: (ConnectionId, Multiaddr),
pub(super) remaining: Vec<Multiaddr>,
}
pub struct DialingAttempt<'a, TInEvent> {
inner: PendingConnection<'a, TInEvent>,
attempts: hash_map::OccupiedEntry<'a, PeerId, SmallVec<[DialingState; 10]>>,
pos: usize,
}
impl<'a, TInEvent>
DialingAttempt<'a, TInEvent>
{
pub fn id(&self) -> ConnectionId {
self.inner.id()
}
pub fn peer_id(&self) -> &PeerId {
self.attempts.key()
}
pub fn address(&self) -> &Multiaddr {
match self.inner.endpoint() {
ConnectedPoint::Dialer { address } => address,
ConnectedPoint::Listener { .. } => unreachable!("by definition of a `DialingAttempt`.")
}
}
pub fn abort(mut self) {
self.attempts.get_mut().remove(self.pos);
if self.attempts.get().is_empty() {
self.attempts.remove();
}
self.inner.abort();
}
pub fn add_address(&mut self, addr: Multiaddr) {
let remaining = &mut self.attempts.get_mut()[self.pos].remaining;
if remaining.iter().all(|a| a != &addr) {
remaining.push(addr);
}
}
}
pub struct DialingAttemptIter<'a, TInEvent, TOutEvent, THandler, TTransErr, THandlerErr> {
peer_id: &'a PeerId,
pool: &'a mut Pool<TInEvent, TOutEvent, THandler, TTransErr, THandlerErr>,
dialing: &'a mut FnvHashMap<PeerId, SmallVec<[DialingState; 10]>>,
pos: usize,
end: usize,
}
impl<'a, TInEvent, TOutEvent, THandler, TTransErr, THandlerErr>
DialingAttemptIter<'a, TInEvent, TOutEvent, THandler, TTransErr, THandlerErr>
{
fn new(
peer_id: &'a PeerId,
pool: &'a mut Pool<TInEvent, TOutEvent, THandler, TTransErr, THandlerErr>,
dialing: &'a mut FnvHashMap<PeerId, SmallVec<[DialingState; 10]>>,
) -> Self {
let end = dialing.get(peer_id).map_or(0, |conns| conns.len());
Self { pos: 0, end, pool, dialing, peer_id }
}
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Option<DialingAttempt<'_, TInEvent>> {
let end = self.dialing.get(self.peer_id).map_or(0, |conns| conns.len());
if self.end > end {
self.end = end;
self.pos -= 1;
}
if self.pos == self.end {
return None
}
if let hash_map::Entry::Occupied(attempts) = self.dialing.entry(*self.peer_id) {
let id = attempts.get()[self.pos].current.0;
if let Some(inner) = self.pool.get_outgoing(id) {
let conn = DialingAttempt { pos: self.pos, inner, attempts };
self.pos += 1;
return Some(conn)
}
}
None
}
pub fn into_first<'b>(self)
-> Option<DialingAttempt<'b, TInEvent>>
where 'a: 'b
{
if self.pos == self.end {
return None
}
if let hash_map::Entry::Occupied(attempts) = self.dialing.entry(*self.peer_id) {
let id = attempts.get()[self.pos].current.0;
if let Some(inner) = self.pool.get_outgoing(id) {
return Some(DialingAttempt { pos: self.pos, inner, attempts })
}
}
None
}
}