use everruns_contracts::url_validation::{validate_url_dns_pinned, validate_url_with_resolver};
use std::future::Future;
use std::net::SocketAddr;
use std::pin::Pin;
use std::sync::Arc;
use std::time::Duration;
pub(super) const CONNECT_TIMEOUT: Duration = Duration::from_secs(10);
pub type DnsResolveFuture = Pin<Box<dyn Future<Output = std::io::Result<Vec<SocketAddr>>> + Send>>;
pub type DnsResolver = Arc<dyn Fn(String, u16) -> DnsResolveFuture + Send + Sync>;
pub(super) async fn hardened_a2a_http_client(
urls: &[&str],
allow_local: bool,
resolver: Option<&DnsResolver>,
) -> std::result::Result<reqwest::Client, String> {
let mut builder = reqwest::Client::builder()
.redirect(reqwest::redirect::Policy::none())
.connect_timeout(CONNECT_TIMEOUT);
if !allow_local {
for raw in urls {
let validated = match resolver {
Some(resolve) => {
let resolve = Arc::clone(resolve);
validate_url_with_resolver(raw, move |host, port| {
let resolve = Arc::clone(&resolve);
async move { resolve(host, port).await }
})
.await
}
None => validate_url_dns_pinned(raw).await,
};
let (url, addrs) = validated.map_err(|e| format!("A2A URL unsafe: {e}"))?;
if let (Some(host), false) = (url.host_str(), addrs.is_empty()) {
builder = builder.resolve_to_addrs(host, &addrs);
}
}
}
builder
.build()
.map_err(|e| format!("Failed to build A2A HTTP client: {e}"))
}