How do I set a timeout for HTTP request using asynchronous Hyper (>= 0.11)?
Here is the example of the code without timeout:
extern crate hyper;
extern crate tokio_core;
extern crate futures;
use futures::Future;
use hyper::Client;
use tokio_core::reactor::Core;
fn main() {
let mut core = Core::new().unwrap();
let client = Client::new(&core.handle());
let uri = "http://stackoverflow.com".parse().unwrap();
let work = client.get(uri).map(|res| {
res.status()
});
match core.run(work) {
Ok(status) => println!("Status: {}", status),
Err(e) => println!("Error: {:?}", e)
}
}
A good approach when making network requests is to configure a request timeout of about 8 - 10 seconds. As shown in the post, using setTimeout () and abort controller you can create fetch () requests configured to timeout when you'd like to. Check the browser support of the abort controller because as of 2020 it is an experimental technology.
First, CancellationToken will have a 1 second timeout, and HttpClient.Timeout will be 5 seconds. This outputs the following, indicating that it used the 1 second timeout set by the CancellationToken. Now change it so CancellationToken’s timeout > HttpClient.Timeout: Repeat the test. It outputs: This indicates it used the HttpClient.Timeout value.
Just FYI this has gotten a lot easier with Tokyo >= 1.0, because they now have a dedicated timeout wrapper that can be applied to a future (such as a request) and which wraps the original future type inside a Result whose Ok is the original future type and whose Err is a timeout error.
Since it’s best practice to reuse HttpClient instances, naturally you may think you can change the Timeout property. Everyone runs into this problem. It may seem counterintuitive that you can’t change this property, but it makes perfect sense if you think about it. The HttpClient class was designed to be used to send multiple requests concurrently.
Answering my own question with a working code example, based on the link provided by seanmonstar to the Hyper Guide / General Timeout:
extern crate hyper;
extern crate tokio_core;
extern crate futures;
use futures::Future;
use futures::future::Either;
use hyper::Client;
use tokio_core::reactor::Core;
use std::time::Duration;
use std::io;
fn main() {
let mut core = Core::new().unwrap();
let handle = core.handle();
let client = Client::new(&handle);
let uri: hyper::Uri = "http://stackoverflow.com".parse().unwrap();
let request = client.get(uri.clone()).map(|res| res.status());
let timeout = tokio_core::reactor::Timeout::new(Duration::from_millis(170), &handle).unwrap();
let work = request.select2(timeout).then(|res| match res {
Ok(Either::A((got, _timeout))) => Ok(got),
Ok(Either::B((_timeout_error, _get))) => {
Err(hyper::Error::Io(io::Error::new(
io::ErrorKind::TimedOut,
"Client timed out while connecting",
)))
}
Err(Either::A((get_error, _timeout))) => Err(get_error),
Err(Either::B((timeout_error, _get))) => Err(From::from(timeout_error)),
});
match core.run(work) {
Ok(status) => println!("OK: {:?}", status),
Err(e) => println!("Error: {:?}", e)
}
}
Just FYI this has gotten a lot easier with Tokyo >= 1.0, because they now have a dedicated timeout
wrapper that can be applied to a future (such as a request) and which wraps the original future type inside a Result
whose Ok
is the original future type and whose Err
is a timeout error.
Thus your code in the question can now handle timeouts as follows:
extern crate tokio; // 1.7.1, full features
use hyper::Client;
use std::time::Duration;
#[tokio::main]
async fn main() {
let client = Client::new();
let uri = "http://stackoverflow.com".parse().unwrap();
let work = client.get(uri);
match tokio::time::timeout(Duration::from_millis(10), work).await {
Ok(result) => match result {
Ok(response) => println!("Status: {}", response.status()),
Err(e) => println!("Network error: {:?}", e),
},
Err(_) => println!("Timeout: no response in 10 milliseconds."),
};
}
(Of course, this code will always give you a timeout. To see the expected 301 response from the network, try going to 200 milliseconds.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With