diff --git a/src/blocking/client.rs b/src/blocking/client.rs index acc3798..259cc6a 100644 --- a/src/blocking/client.rs +++ b/src/blocking/client.rs @@ -670,6 +670,8 @@ impl ClientHandle { let (tx, rx) = oneshot::channel(); let (req, body) = req.into_async(); let url = req.url().clone(); + let timeout = req.timeout().copied().or(self.timeout.0); + self.inner .tx .as_ref() @@ -683,12 +685,12 @@ impl ClientHandle { body.send().await?; rx.await.map_err(|_canceled| event_loop_panicked()) }; - wait::timeout(f, self.timeout.0) + wait::timeout(f, timeout) } else { let f = async move { rx.await.map_err(|_canceled| event_loop_panicked()) }; - wait::timeout(f, self.timeout.0) + wait::timeout(f, timeout) }; match result { diff --git a/src/blocking/request.rs b/src/blocking/request.rs index 76e08da..96c4ac1 100644 --- a/src/blocking/request.rs +++ b/src/blocking/request.rs @@ -1,5 +1,6 @@ use std::fmt; use std::convert::TryFrom; +use std::time::Duration; use base64::encode; use serde::Serialize; @@ -84,6 +85,18 @@ impl Request { &mut self.body } + /// Get the timeout. + #[inline] + pub fn timeout(&self) -> Option<&Duration> { + self.inner.timeout() + } + + /// Get a mutable reference to the timeout. + #[inline] + pub fn timeout_mut(&mut self) -> &mut Option { + self.inner.timeout_mut() + } + /// Attempts to clone the `Request`. /// /// None is returned if a body is which can not be cloned. This can be because the body is a @@ -300,6 +313,18 @@ impl RequestBuilder { self } + /// Enables a request timeout. + /// + /// The timeout is applied from the when the request starts connecting + /// until the response body has finished. It affects only this request + /// and overrides the timeout configured using `ClientBuilder::timeout()`. + pub fn timeout(mut self, timeout: Duration) -> RequestBuilder { + if let Ok(ref mut req) = self.request { + *req.timeout_mut() = Some(timeout); + } + self + } + /// Modify the query string of the URL. /// /// Modifies the URL of this request, adding the parameters provided. diff --git a/tests/timeouts.rs b/tests/timeouts.rs index 10663d4..7c1e158 100644 --- a/tests/timeouts.rs +++ b/tests/timeouts.rs @@ -117,6 +117,34 @@ fn timeout_closes_connection() { assert_eq!(err.url().map(|u| u.as_str()), Some(url.as_str())); } +#[cfg(feature = "blocking")] +#[test] +fn timeout_blocking_request() { + let _ = env_logger::try_init(); + + // Make Client drop *after* the Server, so the background doesn't + // close too early. + let client = reqwest::blocking::Client::builder().build().unwrap(); + + let server = server::http(move |_req| { + async { + // delay returning the response + tokio::time::delay_for(Duration::from_secs(2)).await; + http::Response::default() + } + }); + + let url = format!("http://{}/closes", server.addr()); + let err = client + .get(&url) + .timeout(Duration::from_millis(500)) + .send() + .unwrap_err(); + + assert!(err.is_timeout()); + assert_eq!(err.url().map(|u| u.as_str()), Some(url.as_str())); +} + #[cfg(feature = "blocking")] #[test] fn write_timeout_large_body() {