use std::time::Duration;
use tracing::warn;
pub fn resolve_proxy_url(platform_env_var: Option<&str>) -> Option<String> {
lingshu_security::proxy::resolve_proxy_url(platform_env_var)
}
pub fn build_proxy_client(proxy_url: Option<&str>, timeout: Duration) -> reqwest::Client {
let builder = reqwest::Client::builder().timeout(timeout);
let builder = lingshu_security::proxy::apply_proxy_to_builder(builder, proxy_url);
builder.build().unwrap_or_else(|e| {
warn!(error = %e, "Failed to build proxy client, using default");
reqwest::Client::new()
})
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::{LazyLock, Mutex, MutexGuard};
static ENV_MUTEX: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));
struct EnvGuard {
_lock: MutexGuard<'static, ()>,
vars: Vec<(String, Option<String>)>,
}
impl EnvGuard {
fn new(var_names: &[&str]) -> Self {
let lock = ENV_MUTEX.lock().expect("env mutex poisoned");
let vars = var_names
.iter()
.map(|&name| {
let prev = std::env::var(name).ok();
unsafe { std::env::remove_var(name) };
(name.to_string(), prev)
})
.collect();
Self { _lock: lock, vars }
}
}
impl Drop for EnvGuard {
fn drop(&mut self) {
for (name, prev) in &self.vars {
match prev {
Some(val) => unsafe { std::env::set_var(name, val) },
None => unsafe { std::env::remove_var(name) },
}
}
}
}
#[test]
fn resolve_returns_none_without_env_vars() {
let _guard = EnvGuard::new(&[
"HTTPS_PROXY",
"https_proxy",
"HTTP_PROXY",
"http_proxy",
"ALL_PROXY",
"all_proxy",
]);
assert!(resolve_proxy_url(None).is_none());
}
#[test]
fn resolve_platform_var_takes_priority() {
let _guard = EnvGuard::new(&[
"TEST_PLATFORM_PROXY",
"HTTPS_PROXY",
"https_proxy",
"HTTP_PROXY",
"http_proxy",
"ALL_PROXY",
"all_proxy",
]);
unsafe { std::env::set_var("TEST_PLATFORM_PROXY", "socks5://platform:1080") };
unsafe { std::env::set_var("HTTPS_PROXY", "http://generic:8080") };
assert_eq!(
resolve_proxy_url(Some("TEST_PLATFORM_PROXY")),
Some("socks5://platform:1080".to_string())
);
}
#[test]
fn resolve_https_proxy_when_no_platform_var() {
let _guard = EnvGuard::new(&[
"HTTPS_PROXY",
"https_proxy",
"HTTP_PROXY",
"http_proxy",
"ALL_PROXY",
"all_proxy",
]);
unsafe { std::env::set_var("HTTPS_PROXY", "http://proxy:3128") };
assert_eq!(
resolve_proxy_url(None),
Some("http://proxy:3128".to_string())
);
}
#[test]
fn resolve_all_proxy_as_fallback() {
let _guard = EnvGuard::new(&[
"HTTPS_PROXY",
"https_proxy",
"HTTP_PROXY",
"http_proxy",
"ALL_PROXY",
"all_proxy",
]);
unsafe { std::env::set_var("ALL_PROXY", "socks5h://tunnel:1080") };
assert_eq!(
resolve_proxy_url(None),
Some("socks5h://tunnel:1080".to_string())
);
}
#[test]
fn build_http_proxy_client() {
let client = build_proxy_client(Some("http://proxy:8080"), Duration::from_secs(10));
drop(client);
}
#[test]
fn build_socks5_proxy_client() {
let client = build_proxy_client(Some("socks5://socks:1080"), Duration::from_secs(10));
drop(client);
}
#[test]
fn build_socks5h_rdns_proxy_client() {
let client = build_proxy_client(Some("socks5h://tunnel:1080"), Duration::from_secs(10));
drop(client);
}
#[test]
fn build_no_proxy_client() {
let client = build_proxy_client(None, Duration::from_secs(10));
drop(client);
}
#[test]
fn build_malformed_proxy_graceful() {
let client = build_proxy_client(Some("not-a-url"), Duration::from_secs(10));
drop(client);
}
}