You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
131 lines
3.8 KiB
131 lines
3.8 KiB
use crate::cli;
|
|
use backoff::backoff::Backoff;
|
|
use backoff::ExponentialBackoff;
|
|
use futures::future::FutureExt;
|
|
use libp2p::core::connection::ConnectionId;
|
|
use libp2p::core::Multiaddr;
|
|
use libp2p::swarm::protocols_handler::DummyProtocolsHandler;
|
|
use libp2p::swarm::{DialPeerCondition, NetworkBehaviour, NetworkBehaviourAction, PollParameters};
|
|
use libp2p::PeerId;
|
|
use std::pin::Pin;
|
|
use std::task::{Context, Poll};
|
|
use std::time::Duration;
|
|
use tokio::time::{Instant, Sleep};
|
|
use void::Void;
|
|
|
|
pub enum OutEvent {
|
|
AllAttemptsExhausted { peer: PeerId },
|
|
}
|
|
|
|
/// A [`NetworkBehaviour`] that tracks whether we are connected to the given
|
|
/// peer and attempts to re-establish a connection with an exponential backoff
|
|
/// if we lose the connection.
|
|
pub struct Behaviour {
|
|
/// The peer we are interested in.
|
|
peer: PeerId,
|
|
/// If present, tracks for how long we need to sleep until we dial again.
|
|
sleep: Option<Pin<Box<Sleep>>>,
|
|
/// Tracks the current backoff state.
|
|
backoff: ExponentialBackoff,
|
|
}
|
|
|
|
impl Behaviour {
|
|
pub fn new(peer: PeerId, interval: Duration) -> Self {
|
|
Self {
|
|
peer,
|
|
sleep: None,
|
|
backoff: ExponentialBackoff {
|
|
initial_interval: interval,
|
|
current_interval: interval,
|
|
// give up dialling after 5 minutes
|
|
max_elapsed_time: Some(Duration::from_secs(5 * 60)),
|
|
..ExponentialBackoff::default()
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn until_next_redial(&self) -> Option<Duration> {
|
|
let until_next_redial = self
|
|
.sleep
|
|
.as_ref()?
|
|
.deadline()
|
|
.checked_duration_since(Instant::now())?;
|
|
|
|
Some(until_next_redial)
|
|
}
|
|
}
|
|
|
|
impl NetworkBehaviour for Behaviour {
|
|
type ProtocolsHandler = DummyProtocolsHandler;
|
|
type OutEvent = OutEvent;
|
|
|
|
fn new_handler(&mut self) -> Self::ProtocolsHandler {
|
|
DummyProtocolsHandler::default()
|
|
}
|
|
|
|
fn addresses_of_peer(&mut self, _: &PeerId) -> Vec<Multiaddr> {
|
|
Vec::new()
|
|
}
|
|
|
|
fn inject_connected(&mut self, peer_id: &PeerId) {
|
|
if peer_id != &self.peer {
|
|
return;
|
|
}
|
|
|
|
// established a connection to the desired peer, cancel any active re-dialling
|
|
self.sleep = None;
|
|
}
|
|
|
|
fn inject_disconnected(&mut self, peer_id: &PeerId) {
|
|
if peer_id != &self.peer {
|
|
return;
|
|
}
|
|
|
|
// lost connection to the configured peer, trigger re-dialling with an
|
|
// exponential backoff
|
|
self.backoff.reset();
|
|
self.sleep = Some(Box::pin(tokio::time::sleep(self.backoff.initial_interval)));
|
|
}
|
|
|
|
fn inject_event(&mut self, _: PeerId, _: ConnectionId, _: Void) {}
|
|
|
|
fn poll(
|
|
&mut self,
|
|
cx: &mut Context<'_>,
|
|
_: &mut impl PollParameters,
|
|
) -> Poll<NetworkBehaviourAction<Void, Self::OutEvent>> {
|
|
let sleep = match self.sleep.as_mut() {
|
|
None => return Poll::Pending, // early exit if we shouldn't be re-dialling
|
|
Some(future) => future,
|
|
};
|
|
|
|
futures::ready!(sleep.poll_unpin(cx));
|
|
|
|
let next_dial_in = match self.backoff.next_backoff() {
|
|
Some(next_dial_in) => next_dial_in,
|
|
None => {
|
|
return Poll::Ready(NetworkBehaviourAction::GenerateEvent(
|
|
OutEvent::AllAttemptsExhausted { peer: self.peer },
|
|
));
|
|
}
|
|
};
|
|
|
|
self.sleep = Some(Box::pin(tokio::time::sleep(next_dial_in)));
|
|
|
|
Poll::Ready(NetworkBehaviourAction::DialPeer {
|
|
peer_id: self.peer,
|
|
condition: DialPeerCondition::Disconnected,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl From<OutEvent> for cli::OutEvent {
|
|
fn from(event: OutEvent) -> Self {
|
|
match event {
|
|
OutEvent::AllAttemptsExhausted { peer } => {
|
|
cli::OutEvent::AllRedialAttemptsExhausted { peer }
|
|
}
|
|
}
|
|
}
|
|
}
|