diff --git a/src/net.rs b/src/net.rs index 493971f..b4388bb 100644 --- a/src/net.rs +++ b/src/net.rs @@ -126,10 +126,16 @@ pub(crate) async fn resolve_host_with_doh_tls( return Ok(vec![SocketAddr::new(ip, 0)]); } let Some(dns_server) = dns_server else { - return tokio::net::lookup_host((host, 0)) - .await - .map(|addrs| addrs.collect()) - .map_err(|err| FetchError::Runtime(format!("lookup {host}: {err}"))); + return resolve_system_host_with( + host, + timeout, + Box::pin(async move { + tokio::net::lookup_host((host, 0)) + .await + .map(|addrs| addrs.collect()) + }), + ) + .await; }; let addrs = if is_doh_dns_server(dns_server) { @@ -144,6 +150,23 @@ pub(crate) async fn resolve_host_with_doh_tls( .collect()) } +type SystemLookupFuture<'a> = + Pin>> + Send + 'a>>; + +async fn resolve_system_host_with( + host: &str, + timeout: TimeoutBudget, + system_lookup: SystemLookupFuture<'_>, +) -> Result, FetchError> { + timeout + .run(async move { + system_lookup + .await + .map_err(|err| FetchError::Runtime(format!("lookup {host}: {err}"))) + }) + .await +} + async fn resolve_host_family( host: &str, port: u16, @@ -1408,6 +1431,20 @@ async fn timeout_fetch( mod tests { use super::*; + #[tokio::test] + async fn system_host_resolution_honors_timeout_budget() { + let timeout = Duration::from_millis(10); + let err = resolve_system_host_with( + "example.com", + TimeoutBudget::new(Some(timeout)), + Box::pin(std::future::pending::>>()), + ) + .await + .unwrap_err(); + + assert_eq!(err.to_string(), "request timed out after 10ms"); + } + #[test] fn host_header_value_brackets_ipv6_literals() { let url = Url::parse("http://[::1]/").unwrap();