From 2222472cfa63212065b54911c32d2d3b4a75058e Mon Sep 17 00:00:00 2001 From: Lucas Tembras Date: Mon, 8 Jun 2026 14:58:24 -0400 Subject: [PATCH 1/2] feat: added tls handshake timeout --- src/connector.rs | 6 ++++++ src/connector/builder.rs | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/connector.rs b/src/connector.rs index 9e3e58d..807d19e 100644 --- a/src/connector.rs +++ b/src/connector.rs @@ -2,6 +2,7 @@ use std::future::Future; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; +use std::time::Duration; use std::{fmt, io}; use http::Uri; @@ -24,6 +25,7 @@ pub struct HttpsConnector { force_https: bool, http: T, tls_config: Arc, + handshake_timeout: Option, server_name_resolver: Arc, } @@ -48,6 +50,7 @@ impl HttpsConnector { http, tls_config: tls_config.into(), force_https, + handshake_timeout: None, server_name_resolver, } } @@ -101,6 +104,7 @@ where }; let cfg = self.tls_config.clone(); + let handshake_timeout = self.handshake_timeout; let hostname = match self.server_name_resolver.resolve(&dst) { Ok(hostname) => hostname, Err(e) => { @@ -115,6 +119,7 @@ where .map_err(Into::into)?; Ok(MaybeHttpsStream::Https(TokioIo::new( TlsConnector::from(cfg) + .with_handshake_timeout(handshake_timeout) .connect(hostname, TokioIo::new(tcp)) .await .map_err(io::Error::other)?, @@ -132,6 +137,7 @@ where force_https: false, http, tls_config: cfg.into(), + handshake_timeout: None, server_name_resolver: Arc::new(DefaultServerNameResolver::default()), } } diff --git a/src/connector/builder.rs b/src/connector/builder.rs index 1b4a825..c08d86b 100644 --- a/src/connector/builder.rs +++ b/src/connector/builder.rs @@ -1,4 +1,5 @@ use std::sync::Arc; +use std::time::Duration; use hyper_util::client::legacy::connect::HttpConnector; #[cfg(any( @@ -197,6 +198,7 @@ impl ConnectorBuilder { ConnectorBuilder(WantsProtocols1 { tls_config: self.0.tls_config, https_only: true, + handshake_timeout: None, server_name_resolver: None, }) } @@ -209,6 +211,7 @@ impl ConnectorBuilder { ConnectorBuilder(WantsProtocols1 { tls_config: self.0.tls_config, https_only: false, + handshake_timeout: None, server_name_resolver: None, }) } @@ -221,6 +224,7 @@ impl ConnectorBuilder { pub struct WantsProtocols1 { tls_config: ClientConfig, https_only: bool, + handshake_timeout: Option, server_name_resolver: Option>, } @@ -230,6 +234,7 @@ impl WantsProtocols1 { force_https: self.https_only, http: conn, tls_config: Arc::new(self.tls_config), + handshake_timeout: self.handshake_timeout, server_name_resolver: self .server_name_resolver .unwrap_or_else(|| Arc::new(DefaultServerNameResolver::default())), @@ -299,6 +304,14 @@ impl ConnectorBuilder { self } + /// Set the maximum amount of time to allow for TLS handshakes. + /// + /// `None` disables the handshake timeout. + pub fn with_tls_handshake_timeout(mut self, timeout: Option) -> Self { + self.0.handshake_timeout = timeout; + self + } + /// Override server name for the TLS stack /// /// By default, for each connection hyper-rustls will extract host portion @@ -469,6 +482,32 @@ mod tests { ); } + #[test] + #[cfg(feature = "http1")] + fn test_tls_handshake_timeout() { + ensure_global_state(); + let roots = rustls::RootCertStore::empty(); + let tls_config = rustls::ClientConfig::builder() + .with_root_certificates(roots) + .with_no_client_auth(); + let timeout = std::time::Duration::from_secs(5); + + let connector = super::ConnectorBuilder::new() + .with_tls_config(tls_config.clone()) + .https_only() + .enable_http1() + .build(); + assert_eq!(connector.handshake_timeout, None); + + let connector = super::ConnectorBuilder::new() + .with_tls_config(tls_config) + .https_only() + .with_tls_handshake_timeout(Some(timeout)) + .enable_http1() + .build(); + assert_eq!(connector.handshake_timeout, Some(timeout)); + } + #[test] #[cfg(all(not(feature = "http1"), feature = "http2"))] fn test_alpn_http2() { From 195748bf403a93c90f7e0d214599a1d81f741274 Mon Sep 17 00:00:00 2001 From: Lucas Tembras Date: Mon, 8 Jun 2026 15:31:39 -0400 Subject: [PATCH 2/2] chore: temp update of cargo files --- Cargo.lock | 3 +-- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e34aadf..9436ae6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -875,8 +875,7 @@ dependencies = [ [[package]] name = "tokio-rustls" version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +source = "git+https://github.com/lucastemb/tokio-rustls?branch=lt%2Ftls-timeout#de2c12b6899b0599a7f53d28d36b5e726fd57daa" dependencies = [ "rustls", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 659cb48..c8c2dc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ rustls-native-certs = { version = "0.8", optional = true } rustls-platform-verifier = { version = "0.7", optional = true } rustls = { version = "0.23", default-features = false } tokio = "1.0" -tokio-rustls = { version = "0.26", default-features = false } +tokio-rustls = { git = "https://github.com/lucastemb/tokio-rustls", branch = "lt/tls-timeout", default-features = false } tower-service = "0.3" webpki-roots = { version = "1", optional = true }