From 2a881fb50489b21aa6c879eea0cb339755240fb5 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Mon, 2 Oct 2023 15:36:39 +0100 Subject: [PATCH] fix: split connect timeout for multiple IPs (#1940) --- .github/workflows/ci.yml | 3 ++- src/async_impl/client.rs | 3 ++- src/error.rs | 5 ++++ tests/timeouts.rs | 57 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cd3ccfce9..957186a0d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -277,10 +277,11 @@ jobs: with: toolchain: ${{ steps.metadata.outputs.msrv }} - - name: Make sure log v0.4.18 is used + - name: Fix log and tokio versions run: | cargo update cargo update -p log --precise 0.4.18 + cargo update -p tokio --precise 1.29.1 - uses: Swatinem/rust-cache@v2 diff --git a/src/async_impl/client.rs b/src/async_impl/client.rs index db828665e..b6b515b89 100644 --- a/src/async_impl/client.rs +++ b/src/async_impl/client.rs @@ -283,7 +283,8 @@ impl ClientBuilder { config.dns_overrides, )); } - let http = HttpConnector::new_with_resolver(DynResolver::new(resolver.clone())); + let mut http = HttpConnector::new_with_resolver(DynResolver::new(resolver.clone())); + http.set_connect_timeout(config.connect_timeout); #[cfg(all(feature = "http3", feature = "__rustls"))] let build_h3_connector = diff --git a/src/error.rs b/src/error.rs index 0e6bd247d..9453fcd92 100644 --- a/src/error.rs +++ b/src/error.rs @@ -105,6 +105,11 @@ impl Error { if err.is::() { return true; } + if let Some(io) = err.downcast_ref::() { + if io.kind() == io::ErrorKind::TimedOut { + return true; + } + } source = err.source(); } diff --git a/tests/timeouts.rs b/tests/timeouts.rs index caf48c27b..355e059ce 100644 --- a/tests/timeouts.rs +++ b/tests/timeouts.rs @@ -86,6 +86,63 @@ async fn connect_timeout() { assert!(err.is_connect() && err.is_timeout()); } +#[cfg(not(target_arch = "wasm32"))] +#[tokio::test] +async fn connect_many_timeout_succeeds() { + let _ = env_logger::try_init(); + + let server = server::http(move |_req| async { http::Response::default() }); + let port = server.addr().port(); + + let client = reqwest::Client::builder() + .resolve_to_addrs( + "many_addrs", + &["10.255.255.1:81".parse().unwrap(), server.addr()], + ) + .connect_timeout(Duration::from_millis(100)) + .build() + .unwrap(); + + let url = format!("http://many_addrs:{port}/eventual"); + + let _res = client + .get(url) + .timeout(Duration::from_millis(1000)) + .send() + .await + .unwrap(); +} + +#[cfg(not(target_arch = "wasm32"))] +#[tokio::test] +async fn connect_many_timeout() { + let _ = env_logger::try_init(); + + let client = reqwest::Client::builder() + .resolve_to_addrs( + "many_addrs", + &[ + "10.255.255.1:81".parse().unwrap(), + "10.255.255.2:81".parse().unwrap(), + ], + ) + .connect_timeout(Duration::from_millis(100)) + .build() + .unwrap(); + + let url = format!("http://many_addrs:81/slow"); + + let res = client + .get(url) + .timeout(Duration::from_millis(1000)) + .send() + .await; + + let err = res.unwrap_err(); + + assert!(err.is_connect() && err.is_timeout()); +} + #[tokio::test] async fn response_timeout() { let _ = env_logger::try_init();