Skip to content

Commit 91be74d

Browse files
honsunrisetottoto
andauthored
feat(tls): Add tls handshake timeout support (#2309)
Co-authored-by: tottoto <tottotodev@gmail.com>
1 parent 2d47ef7 commit 91be74d

File tree

5 files changed

+53
-8
lines changed

5 files changed

+53
-8
lines changed

tonic/src/transport/channel/service/tls.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use std::fmt;
2-
use std::sync::Arc;
2+
use std::{sync::Arc, time::Duration};
33

44
use hyper_util::rt::TokioIo;
55
use tokio::io::{AsyncRead, AsyncWrite};
6+
use tokio::time;
67
use tokio_rustls::{
78
rustls::{
89
crypto,
@@ -23,6 +24,7 @@ pub(crate) struct TlsConnector {
2324
config: Arc<ClientConfig>,
2425
domain: Arc<ServerName<'static>>,
2526
assume_http2: bool,
27+
timeout: Option<Duration>,
2628
}
2729

2830
impl TlsConnector {
@@ -34,6 +36,7 @@ impl TlsConnector {
3436
domain: &str,
3537
assume_http2: bool,
3638
use_key_log: bool,
39+
timeout: Option<Duration>,
3740
#[cfg(feature = "tls-native-roots")] with_native_roots: bool,
3841
#[cfg(feature = "tls-webpki-roots")] with_webpki_roots: bool,
3942
) -> Result<Self, crate::BoxError> {
@@ -98,16 +101,22 @@ impl TlsConnector {
98101
config: Arc::new(config),
99102
domain: Arc::new(ServerName::try_from(domain)?.to_owned()),
100103
assume_http2,
104+
timeout,
101105
})
102106
}
103107

104108
pub(crate) async fn connect<I>(&self, io: I) -> Result<BoxedIo, crate::BoxError>
105109
where
106110
I: AsyncRead + AsyncWrite + Send + Unpin + 'static,
107111
{
108-
let io = RustlsConnector::from(self.config.clone())
109-
.connect(self.domain.as_ref().to_owned(), io)
110-
.await?;
112+
let conn_fut =
113+
RustlsConnector::from(self.config.clone()).connect(self.domain.as_ref().to_owned(), io);
114+
let io = match self.timeout {
115+
Some(timeout) => time::timeout(timeout, conn_fut)
116+
.await
117+
.map_err(|_| TlsError::HandshakeTimeout)?,
118+
None => conn_fut.await,
119+
}?;
111120

112121
// Generally we require ALPN to be negotiated, but if the user has
113122
// explicitly set `assume_http2` to true, we'll allow it to be missing.

tonic/src/transport/channel/tls.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::transport::{
44
Error,
55
};
66
use http::Uri;
7+
use std::time::Duration;
78
use tokio_rustls::rustls::pki_types::TrustAnchor;
89

910
/// Configures TLS settings for endpoints.
@@ -19,6 +20,7 @@ pub struct ClientTlsConfig {
1920
#[cfg(feature = "tls-webpki-roots")]
2021
with_webpki_roots: bool,
2122
use_key_log: bool,
23+
timeout: Option<Duration>,
2224
}
2325

2426
impl ClientTlsConfig {
@@ -123,6 +125,14 @@ impl ClientTlsConfig {
123125
config
124126
}
125127

128+
/// Sets the timeout for the TLS handshake.
129+
pub fn timeout(self, timeout: Duration) -> Self {
130+
ClientTlsConfig {
131+
timeout: Some(timeout),
132+
..self
133+
}
134+
}
135+
126136
pub(crate) fn into_tls_connector(self, uri: &Uri) -> Result<TlsConnector, crate::BoxError> {
127137
let domain = match &self.domain {
128138
Some(domain) => domain,
@@ -135,6 +145,7 @@ impl ClientTlsConfig {
135145
domain,
136146
self.assume_http2,
137147
self.use_key_log,
148+
self.timeout,
138149
#[cfg(feature = "tls-native-roots")]
139150
self.with_native_roots,
140151
#[cfg(feature = "tls-webpki-roots")]

tonic/src/transport/server/service/tls.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
1-
use std::{fmt, sync::Arc};
1+
use std::{fmt, sync::Arc, time::Duration};
22

33
use tokio::io::{AsyncRead, AsyncWrite};
4+
use tokio::time;
45
use tokio_rustls::{
56
rustls::{server::WebPkiClientVerifier, RootCertStore, ServerConfig},
67
server::TlsStream,
78
TlsAcceptor as RustlsAcceptor,
89
};
910

1011
use crate::transport::{
11-
service::tls::{convert_certificate_to_pki_types, convert_identity_to_pki_types, ALPN_H2},
12+
service::tls::{
13+
convert_certificate_to_pki_types, convert_identity_to_pki_types, TlsError, ALPN_H2,
14+
},
1215
Certificate, Identity,
1316
};
1417

1518
#[derive(Clone)]
1619
pub(crate) struct TlsAcceptor {
1720
inner: Arc<ServerConfig>,
21+
timeout: Option<Duration>,
1822
}
1923

2024
impl TlsAcceptor {
@@ -24,6 +28,7 @@ impl TlsAcceptor {
2428
client_auth_optional: bool,
2529
ignore_client_order: bool,
2630
use_key_log: bool,
31+
timeout: Option<Duration>,
2732
) -> Result<Self, crate::BoxError> {
2833
let builder = ServerConfig::builder();
2934

@@ -53,6 +58,7 @@ impl TlsAcceptor {
5358
config.alpn_protocols.push(ALPN_H2.into());
5459
Ok(Self {
5560
inner: Arc::new(config),
61+
timeout,
5662
})
5763
}
5864

@@ -61,7 +67,14 @@ impl TlsAcceptor {
6167
IO: AsyncRead + AsyncWrite + Unpin,
6268
{
6369
let acceptor = RustlsAcceptor::from(self.inner.clone());
64-
acceptor.accept(io).await.map_err(Into::into)
70+
let accept_fut = acceptor.accept(io);
71+
match self.timeout {
72+
Some(timeout) => time::timeout(timeout, accept_fut)
73+
.await
74+
.map_err(|_| TlsError::HandshakeTimeout)?,
75+
None => accept_fut.await,
76+
}
77+
.map_err(Into::into)
6578
}
6679
}
6780

tonic/src/transport/server/tls.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::fmt;
1+
use std::{fmt, time::Duration};
22

33
use super::service::TlsAcceptor;
44
use crate::transport::tls::{Certificate, Identity};
@@ -11,6 +11,7 @@ pub struct ServerTlsConfig {
1111
client_auth_optional: bool,
1212
ignore_client_order: bool,
1313
use_key_log: bool,
14+
timeout: Option<Duration>,
1415
}
1516

1617
impl fmt::Debug for ServerTlsConfig {
@@ -73,13 +74,22 @@ impl ServerTlsConfig {
7374
}
7475
}
7576

77+
/// Sets the timeout for the TLS handshake.
78+
pub fn timeout(self, timeout: Duration) -> Self {
79+
ServerTlsConfig {
80+
timeout: Some(timeout),
81+
..self
82+
}
83+
}
84+
7685
pub(crate) fn tls_acceptor(&self) -> Result<TlsAcceptor, crate::BoxError> {
7786
TlsAcceptor::new(
7887
self.identity.as_ref().unwrap(),
7988
self.client_ca_root.as_ref(),
8089
self.client_auth_optional,
8190
self.ignore_client_order,
8291
self.use_key_log,
92+
self.timeout,
8393
)
8494
}
8595
}

tonic/src/transport/service/tls.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub(crate) enum TlsError {
1515
NativeCertsNotFound,
1616
CertificateParseError,
1717
PrivateKeyParseError,
18+
HandshakeTimeout,
1819
}
1920

2021
impl fmt::Display for TlsError {
@@ -29,6 +30,7 @@ impl fmt::Display for TlsError {
2930
f,
3031
"Error parsing TLS private key - no RSA or PKCS8-encoded keys found."
3132
),
33+
TlsError::HandshakeTimeout => write!(f, "TLS handshake timeout."),
3234
}
3335
}
3436
}

0 commit comments

Comments
 (0)