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);
|
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);
|
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.
|
/// Normalized config-server endpoint and authentication token.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct ConfigServerEndpoint {
|
pub struct ConfigServerEndpoint {
|
||||||
@@ -164,7 +175,8 @@ where
|
|||||||
connector: Box<dyn TunnelDialer>,
|
connector: Box<dyn TunnelDialer>,
|
||||||
) {
|
) {
|
||||||
loop {
|
loop {
|
||||||
let connection = match connector.connect().await {
|
let connection = match connect_config_server(connector.as_ref(), CONNECT_TIMEOUT).await
|
||||||
|
{
|
||||||
Ok(connection) => connection,
|
Ok(connection) => connection,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
tracing::warn!(%error, "failed to connect to config server; retrying");
|
tracing::warn!(%error, "failed to connect to config server; retrying");
|
||||||
@@ -191,7 +203,9 @@ where
|
|||||||
|
|
||||||
if support_encryption && web_security::web_secure_tunnel_supported() {
|
if support_encryption && web_security::web_secure_tunnel_supported() {
|
||||||
drop(session);
|
drop(session);
|
||||||
let connection = match connector.connect().await {
|
let connection = match connect_config_server(connector.as_ref(), CONNECT_TIMEOUT)
|
||||||
|
.await
|
||||||
|
{
|
||||||
Ok(connection) => connection,
|
Ok(connection) => connection,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
connected.store(false, Ordering::Release);
|
connected.store(false, Ordering::Release);
|
||||||
@@ -364,7 +378,52 @@ where
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::{
|
||||||
|
future::pending,
|
||||||
|
sync::atomic::{AtomicUsize, Ordering},
|
||||||
|
};
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
use super::*;
|
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]
|
#[test]
|
||||||
fn endpoint_normalizes_non_websocket_paths() {
|
fn endpoint_normalizes_non_websocket_paths() {
|
||||||
|
|||||||
Reference in New Issue
Block a user