iroh_relay/
ping_tracker.rs

1use n0_future::time::{self, Duration, Instant};
2use tracing::debug;
3
4/// Maximum time for a ping response in the relay protocol.
5pub const PING_TIMEOUT: Duration = Duration::from_secs(5);
6
7/// Tracks pings on a single relay connection.
8///
9/// Only the last ping needs is useful, any previously sent ping is forgotten and ignored.
10#[derive(Debug)]
11pub struct PingTracker {
12    inner: Option<PingInner>,
13    default_timeout: Duration,
14}
15
16#[derive(Debug)]
17struct PingInner {
18    data: [u8; 8],
19    deadline: Instant,
20}
21
22impl Default for PingTracker {
23    fn default() -> Self {
24        Self::new(PING_TIMEOUT)
25    }
26}
27
28impl PingTracker {
29    /// Creates a new ping tracker, setting the ping timeout for pings.
30    pub fn new(default_timeout: Duration) -> Self {
31        Self {
32            inner: None,
33            default_timeout,
34        }
35    }
36
37    /// Returns the current timeout set for pings.
38    pub fn default_timeout(&self) -> Duration {
39        self.default_timeout
40    }
41
42    /// Starts a new ping.
43    pub fn new_ping(&mut self) -> [u8; 8] {
44        let ping_data = rand::random();
45        debug!(data = ?ping_data, "Sending ping to relay server.");
46        self.inner = Some(PingInner {
47            data: ping_data,
48            deadline: Instant::now() + self.default_timeout,
49        });
50        ping_data
51    }
52
53    /// Updates the ping tracker with a received pong.
54    ///
55    /// Only the pong of the most recent ping will do anything.  There is no harm feeding
56    /// any pong however.
57    pub fn pong_received(&mut self, data: [u8; 8]) {
58        if self.inner.as_ref().map(|inner| inner.data) == Some(data) {
59            debug!(?data, "Pong received from relay server");
60            self.inner = None;
61        }
62    }
63
64    /// Cancel-safe waiting for a ping timeout.
65    ///
66    /// Unless the most recent sent ping times out, this will never return.
67    pub async fn timeout(&mut self) {
68        match self.inner {
69            Some(PingInner { deadline, data }) => {
70                time::sleep_until(deadline).await;
71                debug!(?data, "Ping timeout.");
72                self.inner = None;
73            }
74            None => std::future::pending().await,
75        }
76    }
77}