Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set timeout for HTTP request with hyper, tokio and futures in Rust?

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)
    }
}
like image 973
Sergey Potapov Avatar asked Jul 25 '17 20:07

Sergey Potapov


People also ask

How to set the timeout for a network request?

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.

How to test if a cancellationtoken has used the httpclient timeout?

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.

Is it possible to apply timeout to a future type?

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.

Can you change the timeout property of a httpclient?

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.


Video Answer


2 Answers

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)
    }
}
like image 108
Sergey Potapov Avatar answered Nov 09 '22 08:11

Sergey Potapov


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.)

like image 43
brotskydotcom Avatar answered Nov 09 '22 08:11

brotskydotcom