Provide request timeout for blocking API (#764)
This commit is contained in:
		
				
					committed by
					
						 Sean McArthur
						Sean McArthur
					
				
			
			
				
	
			
			
			
						parent
						
							b159963f6c
						
					
				
				
					commit
					22fe6566ff
				
			| @@ -670,6 +670,8 @@ impl ClientHandle { | |||||||
|         let (tx, rx) = oneshot::channel(); |         let (tx, rx) = oneshot::channel(); | ||||||
|         let (req, body) = req.into_async(); |         let (req, body) = req.into_async(); | ||||||
|         let url = req.url().clone(); |         let url = req.url().clone(); | ||||||
|  |         let timeout = req.timeout().copied().or(self.timeout.0); | ||||||
|  |  | ||||||
|         self.inner |         self.inner | ||||||
|             .tx |             .tx | ||||||
|             .as_ref() |             .as_ref() | ||||||
| @@ -683,12 +685,12 @@ impl ClientHandle { | |||||||
|                     body.send().await?; |                     body.send().await?; | ||||||
|                     rx.await.map_err(|_canceled| event_loop_panicked()) |                     rx.await.map_err(|_canceled| event_loop_panicked()) | ||||||
|                 }; |                 }; | ||||||
|                 wait::timeout(f, self.timeout.0) |                 wait::timeout(f, timeout) | ||||||
|             } else { |             } else { | ||||||
|                 let f = async move { |                 let f = async move { | ||||||
|                     rx.await.map_err(|_canceled| event_loop_panicked()) |                     rx.await.map_err(|_canceled| event_loop_panicked()) | ||||||
|                 }; |                 }; | ||||||
|                 wait::timeout(f, self.timeout.0) |                 wait::timeout(f, timeout) | ||||||
|             }; |             }; | ||||||
|  |  | ||||||
|         match result { |         match result { | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| use std::fmt; | use std::fmt; | ||||||
| use std::convert::TryFrom; | use std::convert::TryFrom; | ||||||
|  | use std::time::Duration; | ||||||
|  |  | ||||||
| use base64::encode; | use base64::encode; | ||||||
| use serde::Serialize; | use serde::Serialize; | ||||||
| @@ -84,6 +85,18 @@ impl Request { | |||||||
|         &mut self.body |         &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<Duration> { | ||||||
|  |         self.inner.timeout_mut() | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /// Attempts to clone the `Request`. |     /// 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 |     /// 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 |         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. |     /// Modify the query string of the URL. | ||||||
|     /// |     /// | ||||||
|     /// Modifies the URL of this request, adding the parameters provided. |     /// Modifies the URL of this request, adding the parameters provided. | ||||||
|   | |||||||
| @@ -117,6 +117,34 @@ fn timeout_closes_connection() { | |||||||
|     assert_eq!(err.url().map(|u| u.as_str()), Some(url.as_str())); |     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")] | #[cfg(feature = "blocking")] | ||||||
| #[test] | #[test] | ||||||
| fn write_timeout_large_body() { | fn write_timeout_large_body() { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user