diff --git a/Changelog.md b/Changelog.md index ffb5b6ff8..e36bd7564 100644 --- a/Changelog.md +++ b/Changelog.md @@ -15,13 +15,6 @@ -
- 🧹 移除变更 - -- 移除订阅下载 TLS 校验失败后的静态根证书回退重试 - -
-
🚀 优化改进 diff --git a/src-tauri/src/utils/network.rs b/src-tauri/src/utils/network.rs index 37a9011e7..e9463cc99 100644 --- a/src-tauri/src/utils/network.rs +++ b/src-tauri/src/utils/network.rs @@ -6,7 +6,7 @@ use reqwest::{ header::{HeaderMap, HeaderValue, USER_AGENT}, }; use smartstring::alias::String; -use std::time::Duration; +use std::{sync::Arc, time::Duration}; use sysproxy::Sysproxy; use tauri::Url; @@ -42,6 +42,12 @@ pub enum ProxyType { System, } +#[derive(Debug, Clone, Copy)] +enum TlsRootMode { + PlatformVerifier, + StaticWebpkiRoots, +} + pub struct NetworkManager; impl Default for NetworkManager { @@ -61,6 +67,7 @@ impl NetworkManager { default_headers: HeaderMap, accept_invalid_certs: bool, timeout_secs: Option, + tls_root_mode: TlsRootMode, ) -> Result { let mut builder = Client::builder() .tls_backend_rustls() @@ -69,6 +76,10 @@ impl NetworkManager { .pool_max_idle_per_host(0) .pool_idle_timeout(None); + if matches!(tls_root_mode, TlsRootMode::StaticWebpkiRoots) { + builder = builder.tls_backend_preconfigured(Self::build_static_webpki_tls_config()?); + } + // 设置代理 if let Some(proxy_str) = proxy_url { let proxy = Proxy::all(proxy_str)?; @@ -96,6 +107,44 @@ impl NetworkManager { Ok(builder.build()?) } + fn build_static_webpki_tls_config() -> Result { + let root_store = rustls::RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); + let mut 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(); + + config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; + + Ok(config) + } + + fn should_retry_with_static_webpki_roots(err: &anyhow::Error) -> bool { + if err.chain().any(Self::is_legacy_tls_protocol_error) { + return false; + } + + err.chain().any(|e| { + let msg = e.to_string().to_ascii_lowercase(); + [ + "certificate", + "cert", + "tls", + "ssl", + "rustls", + "webpki", + "revocation", + "ocsp", + "crl", + "issuer", + "unknownissuer", + ] + .iter() + .any(|kw| msg.contains(kw)) + }) + } + fn context_reqwest_error(err: reqwest::Error, context: &'static str) -> anyhow::Error { let legacy_tls = Self::is_legacy_tls_protocol_error(&err); let err = anyhow::Error::new(err).context(context); @@ -119,17 +168,24 @@ impl NetworkManager { user_agent: Option, accept_invalid_certs: bool, ) -> Result { - self.create_request_with_tls_mode(proxy_type, timeout_secs, user_agent, accept_invalid_certs) - .await + self.create_request_with_tls_mode( + proxy_type, + timeout_secs, + user_agent, + accept_invalid_certs, + TlsRootMode::PlatformVerifier, + ) + .await } - async fn get( + async fn get_with_tls_mode( &self, url: &str, proxy_type: ProxyType, timeout_secs: Option, user_agent: Option, accept_invalid_certs: bool, + tls_root_mode: TlsRootMode, ) -> Result { let mut parsed = Url::parse(url)?; let mut extra_headers = HeaderMap::new(); @@ -151,7 +207,13 @@ impl NetworkManager { // 创建请求 let client = self - .create_request_with_tls_mode(proxy_type, timeout_secs, user_agent, accept_invalid_certs) + .create_request_with_tls_mode( + proxy_type, + timeout_secs, + user_agent, + accept_invalid_certs, + tls_root_mode, + ) .await?; let mut request_builder = client.get(parsed); @@ -185,6 +247,7 @@ impl NetworkManager { timeout_secs: Option, user_agent: Option, accept_invalid_certs: bool, + tls_root_mode: TlsRootMode, ) -> Result { let proxy_url: Option = match proxy_type { ProxyType::None => None, @@ -219,7 +282,7 @@ impl NetworkManager { ); } - self.build_client(proxy_url, headers, accept_invalid_certs, timeout_secs) + self.build_client(proxy_url, headers, accept_invalid_certs, timeout_secs, tls_root_mode) } pub async fn get_with_interrupt( @@ -230,7 +293,33 @@ impl NetworkManager { user_agent: Option, accept_invalid_certs: bool, ) -> Result { - self.get(url, proxy_type, timeout_secs, user_agent, accept_invalid_certs) - .await + let platform_result = self + .get_with_tls_mode( + url, + proxy_type, + timeout_secs, + user_agent.clone(), + accept_invalid_certs, + TlsRootMode::PlatformVerifier, + ) + .await; + + match platform_result { + Ok(response) => Ok(response), + Err(err) if !accept_invalid_certs && Self::should_retry_with_static_webpki_roots(&err) => self + .get_with_tls_mode( + url, + proxy_type, + timeout_secs, + user_agent, + accept_invalid_certs, + TlsRootMode::StaticWebpkiRoots, + ) + .await + .map_err(|fallback_err| { + fallback_err.context("static webpki roots fallback failed after platform TLS verifier failed") + }), + Err(err) => Err(err), + } } }