Skip to content

Commit 3e6203d

Browse files
honsunriseHonsun Zhu
authored and
Honsun Zhu
committed
feat(tls): Add tls handshake timeout support
1 parent 93c92d8 commit 3e6203d

File tree

5 files changed

+54
-8
lines changed

5 files changed

+54
-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
assume_http2: bool,
3537
#[cfg(feature = "tls-native-roots")] with_native_roots: bool,
3638
#[cfg(feature = "tls-webpki-roots")] with_webpki_roots: bool,
39+
timeout: Option<Duration>,
3740
) -> Result<Self, crate::BoxError> {
3841
fn with_provider(
3942
provider: Arc<crypto::CryptoProvider>,
@@ -92,16 +95,22 @@ impl TlsConnector {
9295
config: Arc::new(config),
9396
domain: Arc::new(ServerName::try_from(domain)?.to_owned()),
9497
assume_http2,
98+
timeout,
9599
})
96100
}
97101

98102
pub(crate) async fn connect<I>(&self, io: I) -> Result<BoxedIo, crate::BoxError>
99103
where
100104
I: AsyncRead + AsyncWrite + Send + Unpin + 'static,
101105
{
102-
let io = RustlsConnector::from(self.config.clone())
103-
.connect(self.domain.as_ref().to_owned(), io)
104-
.await?;
106+
let conn_fut =
107+
RustlsConnector::from(self.config.clone()).connect(self.domain.as_ref().to_owned(), io);
108+
let io = match self.timeout {
109+
Some(timeout) => time::timeout(timeout, conn_fut)
110+
.await
111+
.map_err(|_| TlsError::HandshakeTimeout)?,
112+
None => conn_fut.await,
113+
}?;
105114

106115
// Generally we require ALPN to be negotiated, but if the user has
107116
// 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.
@@ -18,6 +19,7 @@ pub struct ClientTlsConfig {
1819
with_native_roots: bool,
1920
#[cfg(feature = "tls-webpki-roots")]
2021
with_webpki_roots: bool,
22+
timeout: Option<Duration>,
2123
}
2224

2325
impl ClientTlsConfig {
@@ -112,6 +114,14 @@ impl ClientTlsConfig {
112114
config
113115
}
114116

117+
/// Sets the timeout for the TLS handshake.
118+
pub fn timeout(self, timeout: Duration) -> Self {
119+
ClientTlsConfig {
120+
timeout: Some(timeout),
121+
..self
122+
}
123+
}
124+
115125
pub(crate) fn into_tls_connector(self, uri: &Uri) -> Result<TlsConnector, crate::BoxError> {
116126
let domain = match &self.domain {
117127
Some(domain) => domain,
@@ -127,6 +137,7 @@ impl ClientTlsConfig {
127137
self.with_native_roots,
128138
#[cfg(feature = "tls-webpki-roots")]
129139
self.with_webpki_roots,
140+
self.timeout,
130141
)
131142
}
132143
}

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,32 @@
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 {
2125
pub(crate) fn new(
2226
identity: Identity,
2327
client_ca_root: Option<Certificate>,
2428
client_auth_optional: bool,
29+
timeout: Option<Duration>,
2530
) -> Result<Self, crate::BoxError> {
2631
let builder = ServerConfig::builder();
2732

@@ -46,6 +51,7 @@ impl TlsAcceptor {
4651
config.alpn_protocols.push(ALPN_H2.into());
4752
Ok(Self {
4853
inner: Arc::new(config),
54+
timeout,
4955
})
5056
}
5157

@@ -54,7 +60,14 @@ impl TlsAcceptor {
5460
IO: AsyncRead + AsyncWrite + Unpin,
5561
{
5662
let acceptor = RustlsAcceptor::from(self.inner.clone());
57-
acceptor.accept(io).await.map_err(Into::into)
63+
let accept_fut = acceptor.accept(io);
64+
match self.timeout {
65+
Some(timeout) => time::timeout(timeout, accept_fut)
66+
.await
67+
.map_err(|_| TlsError::HandshakeTimeout)?,
68+
None => accept_fut.await,
69+
}
70+
.map_err(Into::into)
5871
}
5972
}
6073

tonic/src/transport/server/tls.rs

Lines changed: 12 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};
@@ -9,6 +9,7 @@ pub struct ServerTlsConfig {
99
identity: Option<Identity>,
1010
client_ca_root: Option<Certificate>,
1111
client_auth_optional: bool,
12+
timeout: Option<Duration>,
1213
}
1314

1415
impl fmt::Debug for ServerTlsConfig {
@@ -24,6 +25,7 @@ impl ServerTlsConfig {
2425
identity: None,
2526
client_ca_root: None,
2627
client_auth_optional: false,
28+
timeout: None,
2729
}
2830
}
2931

@@ -56,11 +58,20 @@ impl ServerTlsConfig {
5658
}
5759
}
5860

61+
/// Sets the timeout for the TLS handshake.
62+
pub fn timeout(self, timeout: Duration) -> Self {
63+
ServerTlsConfig {
64+
timeout: Some(timeout),
65+
..self
66+
}
67+
}
68+
5969
pub(crate) fn tls_acceptor(&self) -> Result<TlsAcceptor, crate::BoxError> {
6070
TlsAcceptor::new(
6171
self.identity.clone().unwrap(),
6272
self.client_ca_root.clone(),
6373
self.client_auth_optional,
74+
self.timeout,
6475
)
6576
}
6677
}

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)