use std::panic::{self, AssertUnwindSafe};
use std::time::Duration;
fn panic_message(payload: Box<dyn std::any::Any + Send>) -> String {
if let Some(msg) = payload.downcast_ref::<&str>() {
(*msg).to_string()
} else if let Some(msg) = payload.downcast_ref::<String>() {
msg.clone()
} else {
"unknown panic".to_string()
}
}
fn build_once(
timeout: Option<Duration>,
disable_proxy_autodiscovery: bool,
) -> Result<reqwest::Client, String> {
let mut builder = reqwest::Client::builder();
if let Some(timeout) = timeout {
builder = builder.timeout(timeout);
}
if disable_proxy_autodiscovery {
builder = builder.no_proxy();
}
match panic::catch_unwind(AssertUnwindSafe(|| builder.build())) {
Ok(Ok(client)) => Ok(client),
Ok(Err(err)) => Err(err.to_string()),
Err(payload) => Err(panic_message(payload)),
}
}
pub(crate) fn build_http_client(timeout: Option<Duration>) -> Result<reqwest::Client, String> {
match build_once(timeout, false) {
Ok(client) => Ok(client),
Err(primary_err) => {
tracing::warn!(
"HTTP client build failed with system proxy autodiscovery: {}. Retrying without proxies.",
primary_err
);
build_once(timeout, true).map_err(|fallback_err| {
format!(
"primary build failed: {}; fallback without proxies failed: {}",
primary_err, fallback_err
)
})
}
}
}
pub(crate) fn build_fetch_client(timeout: Option<Duration>) -> Result<reqwest::Client, String> {
let build = |t: Option<Duration>| -> Result<reqwest::Client, String> {
let mut builder = reqwest::Client::builder()
.no_proxy()
.redirect(reqwest::redirect::Policy::none());
if let Some(t) = t {
builder = builder.timeout(t);
}
match panic::catch_unwind(AssertUnwindSafe(|| builder.build())) {
Ok(Ok(client)) => Ok(client),
Ok(Err(err)) => Err(err.to_string()),
Err(payload) => Err(panic_message(payload)),
}
};
build(timeout)
}