mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-04-13 05:20:28 +08:00
feat: use real TLS latency for website testing
This commit is contained in:
parent
4ceb7e6043
commit
7ae3b7b0de
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -1165,6 +1165,7 @@ dependencies = [
|
||||
"reqwest_dav",
|
||||
"runas",
|
||||
"rust_iso3166",
|
||||
"rustls",
|
||||
"scopeguard",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -1189,8 +1190,10 @@ dependencies = [
|
||||
"tauri-plugin-updater",
|
||||
"tauri-plugin-window-state",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-stream",
|
||||
"warp",
|
||||
"webpki-roots",
|
||||
"windows 0.62.2",
|
||||
"winreg 0.56.0",
|
||||
"zip 8.2.0",
|
||||
@ -6315,6 +6318,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"log",
|
||||
"once_cell",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
|
||||
@ -102,6 +102,9 @@ clash_verge_service_ipc = { version = "2.1.3", features = [
|
||||
"client",
|
||||
], git = "https://github.com/clash-verge-rev/clash-verge-service-ipc" }
|
||||
arc-swap = "1.8.2"
|
||||
tokio-rustls = "0.26"
|
||||
rustls = { version = "0.23", features = ["ring"] }
|
||||
webpki-roots = "1.0"
|
||||
rust_iso3166 = "0.1.14"
|
||||
# Use the git repo until the next release after v2.0.0.
|
||||
dark-light = { git = "https://github.com/rust-dark-light/dark-light" }
|
||||
|
||||
@ -102,40 +102,69 @@ pub async fn change_clash_mode(mode: String) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Test connection delay to a URL
|
||||
/// Test TLS handshake delay to a URL (through proxy when not in TUN mode)
|
||||
pub async fn test_delay(url: String) -> anyhow::Result<u32> {
|
||||
use crate::utils::network::{NetworkManager, ProxyType};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::time::Instant;
|
||||
|
||||
let tun_mode = Config::verge().await.latest_arc().enable_tun_mode.unwrap_or(false);
|
||||
let parsed = tauri::Url::parse(&url)?;
|
||||
let is_https = parsed.scheme() == "https";
|
||||
let host = parsed
|
||||
.host_str()
|
||||
.ok_or_else(|| anyhow::anyhow!("Invalid URL: no host"))?
|
||||
.to_string();
|
||||
let port = parsed.port().unwrap_or(if is_https { 443 } else { 80 });
|
||||
|
||||
// 如果是TUN模式,不使用代理,否则使用自身代理
|
||||
let proxy_type = if !tun_mode {
|
||||
ProxyType::Localhost
|
||||
} else {
|
||||
ProxyType::None
|
||||
let verge = Config::verge().await.latest_arc();
|
||||
let tun_mode = verge.enable_tun_mode.unwrap_or(false);
|
||||
|
||||
let inner = async {
|
||||
let start = Instant::now();
|
||||
|
||||
// TCP connect: direct in TUN mode, via CONNECT tunnel otherwise
|
||||
let stream = if tun_mode {
|
||||
TcpStream::connect(format!("{host}:{port}")).await?
|
||||
} else {
|
||||
let proxy_port = match verge.verge_mixed_port {
|
||||
Some(p) => p,
|
||||
None => Config::clash().await.data_arc().get_mixed_port(),
|
||||
};
|
||||
let mut stream = TcpStream::connect(format!("127.0.0.1:{proxy_port}")).await?;
|
||||
stream
|
||||
.write_all(format!("CONNECT {host}:{port} HTTP/1.1\r\nHost: {host}:{port}\r\n\r\n").as_bytes())
|
||||
.await?;
|
||||
let mut buf = [0u8; 1024];
|
||||
let n = stream.read(&mut buf).await?;
|
||||
if !std::str::from_utf8(&buf[..n]).unwrap_or("").contains("200") {
|
||||
return Err(anyhow::anyhow!("Proxy CONNECT failed"));
|
||||
}
|
||||
stream
|
||||
};
|
||||
|
||||
// TLS handshake for HTTPS; for HTTP the TCP connect above is the result
|
||||
if is_https {
|
||||
let root_store = rustls::RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
|
||||
let config =
|
||||
rustls::ClientConfig::builder_with_provider(Arc::new(rustls::crypto::ring::default_provider()))
|
||||
.with_safe_default_protocol_versions()?
|
||||
.with_root_certificates(root_store)
|
||||
.with_no_client_auth();
|
||||
let connector = tokio_rustls::TlsConnector::from(Arc::new(config));
|
||||
let server_name = rustls::pki_types::ServerName::try_from(host.as_str())
|
||||
.map_err(|_| anyhow::anyhow!("Invalid DNS name: {host}"))?
|
||||
.to_owned();
|
||||
connector.connect(server_name, stream).await?;
|
||||
}
|
||||
|
||||
// Ensure at least 1ms — frontend treats 0 as timeout
|
||||
Ok((start.elapsed().as_millis() as u32).max(1))
|
||||
};
|
||||
|
||||
let user_agent = Some("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0".into());
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
let response = NetworkManager::new()
|
||||
.get_with_interrupt(&url, proxy_type, Some(10), user_agent, false)
|
||||
.await;
|
||||
|
||||
match response {
|
||||
Ok(response) => {
|
||||
logging!(trace, Type::Network, "test_delay response: {response:#?}");
|
||||
if response.status().is_success() {
|
||||
Ok(start.elapsed().as_millis() as u32)
|
||||
} else {
|
||||
Ok(10000u32)
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
logging!(trace, Type::Network, "test_delay error: {err:#?}");
|
||||
Err(err)
|
||||
}
|
||||
match tokio::time::timeout(Duration::from_secs(10), inner).await {
|
||||
Ok(result) => result,
|
||||
Err(_) => Ok(10000u32),
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user