From 8a8c0d4064eab7883f9a1ceb4a02b8165dfb5a20 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Thu, 26 Dec 2024 13:12:59 +0100 Subject: [PATCH] feat(client): allow to set response timeout per request --- client/src/middleware/redirect.rs | 17 ++++++++++++++-- client/src/request.rs | 32 ++++++++++++++++++++----------- client/src/service.rs | 26 +++++++++++++------------ 3 files changed, 50 insertions(+), 25 deletions(-) diff --git a/client/src/middleware/redirect.rs b/client/src/middleware/redirect.rs index f0dd7b92..5ad5df57 100644 --- a/client/src/middleware/redirect.rs +++ b/client/src/middleware/redirect.rs @@ -28,13 +28,26 @@ where type Error = Error; async fn call(&self, req: ServiceRequest<'r, 'c>) -> Result { - let ServiceRequest { req, client, timeout } = req; + let ServiceRequest { + req, + client, + request_timeout, + response_timeout, + } = req; let mut headers = req.headers().clone(); let mut method = req.method().clone(); let mut uri = req.uri().clone(); let ext = req.extensions().clone(); loop { - let mut res = self.service.call(ServiceRequest { req, client, timeout }).await?; + let mut res = self + .service + .call(ServiceRequest { + req, + client, + request_timeout, + response_timeout, + }) + .await?; match res.status() { StatusCode::MOVED_PERMANENTLY | StatusCode::FOUND | StatusCode::SEE_OTHER => { if method != Method::HEAD { diff --git a/client/src/request.rs b/client/src/request.rs index c45df7d6..ef965568 100644 --- a/client/src/request.rs +++ b/client/src/request.rs @@ -21,7 +21,8 @@ pub struct RequestBuilder<'a, M = marker::Http> { pub(crate) req: http::Request, err: Vec, client: &'a Client, - timeout: Duration, + request_timeout: Duration, + response_timeout: Duration, _marker: PhantomData, } @@ -103,7 +104,8 @@ impl<'a, M> RequestBuilder<'a, M> { req: req.map(BoxBody::new), err: Vec::new(), client, - timeout: client.timeout_config.request_timeout, + request_timeout: client.timeout_config.request_timeout, + response_timeout: client.timeout_config.response_timeout, _marker: PhantomData, } } @@ -113,7 +115,8 @@ impl<'a, M> RequestBuilder<'a, M> { req: self.req, err: self.err, client: self.client, - timeout: self.timeout, + request_timeout: self.request_timeout, + response_timeout: self.response_timeout, _marker: PhantomData, } } @@ -124,7 +127,8 @@ impl<'a, M> RequestBuilder<'a, M> { mut req, err, client, - timeout, + request_timeout, + response_timeout, .. } = self; @@ -137,7 +141,8 @@ impl<'a, M> RequestBuilder<'a, M> { .call(ServiceRequest { req: &mut req, client, - timeout, + request_timeout, + response_timeout, }) .await } @@ -199,14 +204,19 @@ impl<'a, M> RequestBuilder<'a, M> { self } - /// Set timeout of this request. + /// Set timeout for request. /// - /// The value passed would override global [ClientBuilder::set_request_timeout]. + /// Default to client's [TimeoutConfig::request_timeout]. + pub fn set_request_timeout(mut self, dur: Duration) -> Self { + self.request_timeout = dur; + self + } + + /// Set timeout for collecting response body. /// - /// [ClientBuilder::set_request_timeout]: crate::builder::ClientBuilder::set_request_timeout - #[inline] - pub fn timeout(mut self, dur: Duration) -> Self { - self.timeout = dur; + /// Default to client's [TimeoutConfig::response_timeout]. + pub fn set_response_timeout(mut self, dur: Duration) -> Self { + self.response_timeout = dur; self } diff --git a/client/src/service.rs b/client/src/service.rs index cd56adcc..459f0cdf 100644 --- a/client/src/service.rs +++ b/client/src/service.rs @@ -67,7 +67,8 @@ where pub struct ServiceRequest<'r, 'c> { pub req: &'r mut Request, pub client: &'c Client, - pub timeout: Duration, + pub request_timeout: Duration, + pub response_timeout: Duration, } /// type alias for object safe wrapper of type implement [Service] trait. @@ -85,7 +86,12 @@ pub(crate) fn base_service() -> HttpService { #[cfg(any(feature = "http1", feature = "http2", feature = "http3"))] use crate::{error::TimeoutError, timeout::Timeout}; - let ServiceRequest { req, client, timeout } = req; + let ServiceRequest { + req, + client, + request_timeout, + response_timeout, + } = req; let uri = Uri::try_parse(req.uri())?; @@ -102,7 +108,7 @@ pub(crate) fn base_service() -> HttpService { match version { Version::HTTP_2 | Version::HTTP_3 => match client.shared_pool.acquire(&connect.uri).await { shared::AcquireOutput::Conn(mut _conn) => { - let mut _timer = Box::pin(tokio::time::sleep(timeout)); + let mut _timer = Box::pin(tokio::time::sleep(request_timeout)); *req.version_mut() = version; #[allow(unreachable_code)] return match _conn.conn { @@ -112,10 +118,7 @@ pub(crate) fn base_service() -> HttpService { .timeout(_timer.as_mut()) .await { - Ok(Ok(res)) => { - let timeout = client.timeout_config.response_timeout; - Ok(Response::new(res, _timer, timeout)) - } + Ok(Ok(res)) => Ok(Response::new(res, _timer, response_timeout)), Ok(Err(e)) => { _conn.destroy_on_drop(); Err(e.into()) @@ -133,8 +136,7 @@ pub(crate) fn base_service() -> HttpService { .await .map_err(|_| TimeoutError::Request)??; - let timeout = client.timeout_config.response_timeout; - Ok(Response::new(res, _timer, timeout)) + Ok(Response::new(res, _timer, response_timeout)) } }; } @@ -218,7 +220,7 @@ pub(crate) fn base_service() -> HttpService { #[cfg(feature = "http1")] { - let mut timer = Box::pin(tokio::time::sleep(timeout)); + let mut timer = Box::pin(tokio::time::sleep(request_timeout)); let res = crate::h1::proto::send(&mut *_conn, _date, req) .timeout(timer.as_mut()) .await; @@ -230,8 +232,8 @@ pub(crate) fn base_service() -> HttpService { } let body = crate::h1::body::ResponseBody::new(_conn, buf, decoder); let res = res.map(|_| crate::body::ResponseBody::H1(body)); - let timeout = client.timeout_config.response_timeout; - Ok(Response::new(res, timer, timeout)) + + Ok(Response::new(res, timer, response_timeout)) } Ok(Err(e)) => { _conn.destroy_on_drop();