qconnection/path/
validate.rs1use std::{sync::atomic::Ordering, time::Duration};
2
3use qbase::{frame::PathChallengeFrame, net::tx::Signals};
4use qcongestion::Transport;
5use thiserror::Error;
6use tokio::time::Instant;
7
8#[derive(Debug, Error, Clone, Copy)]
9pub enum ValidateFailure {
10 #[error(
11 "Path validation abort due to path inactivity by other reasons(usually connection closed)"
12 )]
13 PathInactive,
14 #[error("Path validation failed after {0} ms", elapsed.as_millis())]
15 Timeout { elapsed: Duration },
16}
17
18impl super::Path {
19 pub fn validated(&self) {
20 self.validated.store(true, Ordering::Release);
21 self.tx_waker.wake_by(Signals::PATH_VALIDATE);
22 }
23
24 pub async fn validate(&self) -> Result<(), ValidateFailure> {
25 let challenge = PathChallengeFrame::random();
26 let start = Instant::now();
27 for _ in 0..30 {
28 let timeout_duration = self.cc().get_pto(qbase::Epoch::Data);
29 self.challenge_sndbuf.write(challenge);
30 match tokio::time::timeout(timeout_duration, self.response_rcvbuf.receive()).await {
31 Ok(Some(response)) if *response == *challenge => {
32 self.validated();
33 self.anti_amplifier.grant();
34 return Ok(());
35 }
36 Ok(None) => return Err(ValidateFailure::PathInactive),
38 _ => continue,
40 }
41 }
42 Err(ValidateFailure::Timeout {
43 elapsed: start.elapsed(),
44 })
45 }
46}