mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-08-05 20:19:45 +00:00
fix(web-client): time out stalled config-server dials (#2461)
Co-authored-by: 225284228a-droid <239500008+225284228a-droid@users.noreply.github.com>
This commit is contained in:
@@ -28,8 +28,19 @@ use super::{
|
||||
};
|
||||
|
||||
const RETRY_INTERVAL: std::time::Duration = std::time::Duration::from_secs(1);
|
||||
// Keep retry ownership in this loop when transport or protocol handshakes stall.
|
||||
const CONNECT_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(20);
|
||||
const FEATURE_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(3);
|
||||
|
||||
async fn connect_config_server(
|
||||
connector: &dyn TunnelDialer,
|
||||
timeout: std::time::Duration,
|
||||
) -> anyhow::Result<Box<dyn Tunnel>> {
|
||||
time::timeout(timeout, connector.connect())
|
||||
.await
|
||||
.map_err(|_| anyhow::anyhow!("config-server connection timed out after {timeout:?}"))?
|
||||
}
|
||||
|
||||
/// Normalized config-server endpoint and authentication token.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ConfigServerEndpoint {
|
||||
@@ -164,7 +175,8 @@ where
|
||||
connector: Box<dyn TunnelDialer>,
|
||||
) {
|
||||
loop {
|
||||
let connection = match connector.connect().await {
|
||||
let connection = match connect_config_server(connector.as_ref(), CONNECT_TIMEOUT).await
|
||||
{
|
||||
Ok(connection) => connection,
|
||||
Err(error) => {
|
||||
tracing::warn!(%error, "failed to connect to config server; retrying");
|
||||
@@ -191,7 +203,9 @@ where
|
||||
|
||||
if support_encryption && web_security::web_secure_tunnel_supported() {
|
||||
drop(session);
|
||||
let connection = match connector.connect().await {
|
||||
let connection = match connect_config_server(connector.as_ref(), CONNECT_TIMEOUT)
|
||||
.await
|
||||
{
|
||||
Ok(connection) => connection,
|
||||
Err(error) => {
|
||||
connected.store(false, Ordering::Release);
|
||||
@@ -364,7 +378,52 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{
|
||||
future::pending,
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use super::*;
|
||||
use crate::tunnel::ring::create_ring_tunnel_pair;
|
||||
|
||||
struct StalledThenReadyDialer {
|
||||
attempts: AtomicUsize,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl TunnelDialer for StalledThenReadyDialer {
|
||||
async fn connect(&self) -> anyhow::Result<Box<dyn Tunnel>> {
|
||||
if self.attempts.fetch_add(1, Ordering::Relaxed) == 0 {
|
||||
return pending().await;
|
||||
}
|
||||
|
||||
let (tunnel, _peer) = create_ring_tunnel_pair();
|
||||
Ok(tunnel)
|
||||
}
|
||||
|
||||
fn remote_url(&self) -> Url {
|
||||
"ring://config-server".parse().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn stalled_connection_attempt_times_out_and_allows_redial() {
|
||||
let connector = StalledThenReadyDialer {
|
||||
attempts: AtomicUsize::new(0),
|
||||
};
|
||||
|
||||
let error = connect_config_server(&connector, std::time::Duration::from_millis(10))
|
||||
.await
|
||||
.unwrap_err();
|
||||
assert!(error.to_string().contains("connection timed out"));
|
||||
|
||||
connect_config_server(&connector, std::time::Duration::from_secs(1))
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(connector.attempts.load(Ordering::Relaxed), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn endpoint_normalizes_non_websocket_paths() {
|
||||
|
||||
Reference in New Issue
Block a user