1use std::sync::Arc;
14
15use parking_lot::Mutex;
16use web_time::{Duration, Instant};
17
18use crate::connection::Action;
19use crate::frame::{header::Ping, Frame};
20
21const PING_INTERVAL: Duration = Duration::from_secs(10);
22
23#[derive(Clone, Debug)]
24pub(crate) struct Rtt(Arc<Mutex<RttInner>>);
25
26impl Rtt {
27 pub(crate) fn new() -> Self {
28 Self(Arc::new(Mutex::new(RttInner {
29 rtt: None,
30 state: RttState::Waiting {
31 next: Instant::now(),
32 },
33 })))
34 }
35
36 pub(crate) fn next_ping(&mut self) -> Option<Frame<Ping>> {
37 let state = &mut self.0.lock().state;
38
39 match state {
40 RttState::AwaitingPong { .. } => return None,
41 RttState::Waiting { next } => {
42 if *next > Instant::now() {
43 return None;
44 }
45 }
46 }
47
48 let nonce = rand::random();
49 *state = RttState::AwaitingPong {
50 sent_at: Instant::now(),
51 nonce,
52 };
53 log::debug!("sending ping {nonce}");
54 Some(Frame::ping(nonce))
55 }
56
57 pub(crate) fn handle_pong(&mut self, received_nonce: u32) -> Action {
58 let inner = &mut self.0.lock();
59
60 let (sent_at, expected_nonce) = match inner.state {
61 RttState::Waiting { .. } => {
62 log::error!("received unexpected pong {received_nonce}");
63 return Action::Terminate(Frame::protocol_error());
64 }
65 RttState::AwaitingPong { sent_at, nonce } => (sent_at, nonce),
66 };
67
68 if received_nonce != expected_nonce {
69 log::error!("received pong with {received_nonce} but expected {expected_nonce}");
70 return Action::Terminate(Frame::protocol_error());
71 }
72
73 let rtt = sent_at.elapsed();
74 inner.rtt = Some(rtt);
75 log::debug!("received pong {received_nonce}, estimated round-trip-time {rtt:?}");
76
77 inner.state = RttState::Waiting {
78 next: Instant::now() + PING_INTERVAL,
79 };
80
81 Action::None
82 }
83
84 pub(crate) fn get(&self) -> Option<Duration> {
85 self.0.lock().rtt
86 }
87}
88
89#[cfg(test)]
90impl quickcheck::Arbitrary for Rtt {
91 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
92 Self(Arc::new(Mutex::new(RttInner::arbitrary(g))))
93 }
94}
95
96#[derive(Debug)]
97#[cfg_attr(test, derive(Clone))]
98struct RttInner {
99 state: RttState,
100 rtt: Option<Duration>,
101}
102
103#[cfg(test)]
104impl quickcheck::Arbitrary for RttInner {
105 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
106 Self {
107 state: RttState::arbitrary(g),
108 rtt: if bool::arbitrary(g) {
109 Some(Duration::arbitrary(g))
110 } else {
111 None
112 },
113 }
114 }
115}
116
117#[derive(Debug)]
118#[cfg_attr(test, derive(Clone))]
119enum RttState {
120 AwaitingPong { sent_at: Instant, nonce: u32 },
121 Waiting { next: Instant },
122}
123
124#[cfg(test)]
125impl quickcheck::Arbitrary for RttState {
126 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
127 if bool::arbitrary(g) {
128 RttState::AwaitingPong {
129 sent_at: Instant::now(),
130 nonce: u32::arbitrary(g),
131 }
132 } else {
133 RttState::Waiting {
134 next: Instant::now(),
135 }
136 }
137 }
138}