Skip to content

Commit

Permalink
fix(http): set I/O timeout to 1 minute rather than whole request timeout
Browse files Browse the repository at this point in the history
Before the fix HTTP client
had no connection timeout,
so it only had a chance
to test one IPv6 and one IPv4
address if the first addresses timed out.
Now it can test at least 4 addresses
of each family and more if some addresses
refuse connection rather than time out.
  • Loading branch information
link2xt committed Aug 25, 2024
1 parent 36e5e96 commit 4bb4dc4
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 1 deletion.
7 changes: 7 additions & 0 deletions src/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ use tls::wrap_tls;
/// This constant should be more than the largest expected RTT.
pub(crate) const TIMEOUT: Duration = Duration::from_secs(60);

/// Transaction timeout, e.g. for a GET or POST request
/// together with all connection attempts.
///
/// This is the worst case time user has to wait on a very slow network
/// after clicking a button and before getting an error message.
pub(crate) const TRANSACTION_TIMEOUT: Duration = Duration::from_secs(300);

/// TTL for caches in seconds.
pub(crate) const CACHE_TTL: u64 = 30 * 24 * 60 * 60;

Expand Down
18 changes: 17 additions & 1 deletion src/net/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,24 @@ pub(crate) async fn get_client(context: &Context, load_cache: bool) -> Result<re
let socks5_config = Socks5Config::from_database(&context.sql).await?;
let resolver = Arc::new(CustomResolver::new(context.clone(), load_cache));

// `reqwest` uses `hyper-util` crate internally which implements
// [Happy Eyeballs](https://datatracker.ietf.org/doc/html/rfc6555) algorithm.
// On a dual-stack host it starts IPv4 connection attempts in parallel
// to IPv6 connection attempts after 300 ms.
// In the worst case of all connection attempts
// timing out this allows to try four IPv6 and four IPv4
// addresses before request expires
// if request timeout is set to 5 minutes
// and connection timeout is set to 1 minute.
//
// We do not set write timeout because `reqwest`
// does not support it, but request timeout
// should prevent deadlocks if the server
// does not read the data.
let builder = reqwest::ClientBuilder::new()
.timeout(super::TIMEOUT)
.connect_timeout(super::TIMEOUT)
.read_timeout(super::TIMEOUT)
.timeout(super::TRANSACTION_TIMEOUT)
.add_root_certificate(LETSENCRYPT_ROOT.clone())
.dns_resolver(resolver);

Expand Down

0 comments on commit 4bb4dc4

Please sign in to comment.