qconnection/path/
validate.rs

1use 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                // 外部发生变化,导致路径验证任务作废
37                Ok(None) => return Err(ValidateFailure::PathInactive),
38                // 超时或者收到不对的response,按"停-等协议",继续再发一次Challenge,最多3次
39                _ => continue,
40            }
41        }
42        Err(ValidateFailure::Timeout {
43            elapsed: start.elapsed(),
44        })
45    }
46}