Skip to content

Commit d8977d6

Browse files
authored
feat(transport): Allow setting TCP_KEEPINTVL and TCP_KEEPCNT (#2299)
* Allow setting TCP_KEEPINTVL and TCP_KEEPCNT * fix windows * fix clippy windows
1 parent 1e6d2e8 commit d8977d6

File tree

3 files changed

+151
-4
lines changed

3 files changed

+151
-4
lines changed

tonic/src/transport/channel/endpoint.rs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ pub struct Endpoint {
3939
pub(crate) init_stream_window_size: Option<u32>,
4040
pub(crate) init_connection_window_size: Option<u32>,
4141
pub(crate) tcp_keepalive: Option<Duration>,
42+
pub(crate) tcp_keepalive_interval: Option<Duration>,
43+
pub(crate) tcp_keepalive_retries: Option<u32>,
4244
pub(crate) tcp_nodelay: bool,
4345
pub(crate) http2_keep_alive_interval: Option<Duration>,
4446
pub(crate) http2_keep_alive_timeout: Option<Duration>,
@@ -84,6 +86,8 @@ impl Endpoint {
8486
init_stream_window_size: None,
8587
init_connection_window_size: None,
8688
tcp_keepalive: None,
89+
tcp_keepalive_interval: None,
90+
tcp_keepalive_retries: None,
8791
tcp_nodelay: true,
8892
http2_keep_alive_interval: None,
8993
http2_keep_alive_timeout: None,
@@ -111,6 +115,8 @@ impl Endpoint {
111115
init_stream_window_size: None,
112116
init_connection_window_size: None,
113117
tcp_keepalive: None,
118+
tcp_keepalive_interval: None,
119+
tcp_keepalive_retries: None,
114120
tcp_nodelay: true,
115121
http2_keep_alive_interval: None,
116122
http2_keep_alive_timeout: None,
@@ -258,14 +264,38 @@ impl Endpoint {
258264
/// probes.
259265
///
260266
/// Default is no keepalive (`None`)
261-
///
262267
pub fn tcp_keepalive(self, tcp_keepalive: Option<Duration>) -> Self {
263268
Endpoint {
264269
tcp_keepalive,
265270
..self
266271
}
267272
}
268273

274+
/// Set the duration between two successive TCP keepalive retransmissions,
275+
/// if acknowledgement to the previous keepalive transmission is not received.
276+
///
277+
/// This is only used if `tcp_keepalive` is not None.
278+
///
279+
/// Defaults to None, which is the system default.
280+
pub fn tcp_keepalive_interval(self, tcp_keepalive_interval: Option<Duration>) -> Self {
281+
Endpoint {
282+
tcp_keepalive_interval,
283+
..self
284+
}
285+
}
286+
287+
/// Set the number of retransmissions to be carried out before declaring that remote end is not available.
288+
///
289+
/// This is only used if `tcp_keepalive` is not None.
290+
///
291+
/// Defaults to None, which is the system default.
292+
pub fn tcp_keepalive_retries(self, tcp_keepalive_retries: Option<u32>) -> Self {
293+
Endpoint {
294+
tcp_keepalive_retries,
295+
..self
296+
}
297+
}
298+
269299
/// Apply a concurrency limit to each request.
270300
///
271301
/// ```
@@ -428,6 +458,8 @@ impl Endpoint {
428458
http.enforce_http(false);
429459
http.set_nodelay(self.tcp_nodelay);
430460
http.set_keepalive(self.tcp_keepalive);
461+
http.set_keepalive_interval(self.tcp_keepalive_interval);
462+
http.set_keepalive_retries(self.tcp_keepalive_retries);
431463
http.set_connect_timeout(self.connect_timeout);
432464
http.set_local_address(self.local_address);
433465
self.connector(http)
@@ -543,6 +575,16 @@ impl Endpoint {
543575
pub fn get_tcp_keepalive(&self) -> Option<Duration> {
544576
self.tcp_keepalive
545577
}
578+
579+
/// Get whether TCP keepalive interval.
580+
pub fn get_tcp_keepalive_interval(&self) -> Option<Duration> {
581+
self.tcp_keepalive_interval
582+
}
583+
584+
/// Get whether TCP keepalive retries.
585+
pub fn get_tcp_keepalive_retries(&self) -> Option<u32> {
586+
self.tcp_keepalive_retries
587+
}
546588
}
547589

548590
impl From<Uri> for Endpoint {

tonic/src/transport/server/incoming.rs

Lines changed: 106 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ pub struct TcpIncoming {
1919
inner: TcpListenerStream,
2020
nodelay: Option<bool>,
2121
keepalive: Option<TcpKeepalive>,
22+
keepalive_time: Option<Duration>,
23+
keepalive_interval: Option<Duration>,
24+
keepalive_retries: Option<u32>,
2225
}
2326

2427
impl TcpIncoming {
@@ -66,9 +69,42 @@ impl TcpIncoming {
6669
}
6770

6871
/// Sets the `TCP_KEEPALIVE` option on the accepted connection.
69-
pub fn with_keepalive(self, keepalive: Option<Duration>) -> Self {
70-
let keepalive = keepalive.map(|t| TcpKeepalive::new().with_time(t));
71-
Self { keepalive, ..self }
72+
pub fn with_keepalive(self, keepalive_time: Option<Duration>) -> Self {
73+
Self {
74+
keepalive_time,
75+
keepalive: make_keepalive(
76+
keepalive_time,
77+
self.keepalive_interval,
78+
self.keepalive_retries,
79+
),
80+
..self
81+
}
82+
}
83+
84+
/// Sets the `TCP_KEEPINTVL` option on the accepted connection.
85+
pub fn with_keepalive_interval(self, keepalive_interval: Option<Duration>) -> Self {
86+
Self {
87+
keepalive_interval,
88+
keepalive: make_keepalive(
89+
self.keepalive_time,
90+
keepalive_interval,
91+
self.keepalive_retries,
92+
),
93+
..self
94+
}
95+
}
96+
97+
/// Sets the `TCP_KEEPCNT` option on the accepted connection.
98+
pub fn with_keepalive_retries(self, keepalive_retries: Option<u32>) -> Self {
99+
Self {
100+
keepalive_retries,
101+
keepalive: make_keepalive(
102+
self.keepalive_time,
103+
self.keepalive_interval,
104+
keepalive_retries,
105+
),
106+
..self
107+
}
72108
}
73109

74110
/// Returns the local address that this tcp incoming is bound to.
@@ -83,6 +119,9 @@ impl From<TcpListener> for TcpIncoming {
83119
inner: TcpListenerStream::new(listener),
84120
nodelay: None,
85121
keepalive: None,
122+
keepalive_time: None,
123+
keepalive_interval: None,
124+
keepalive_retries: None,
86125
}
87126
}
88127
}
@@ -121,6 +160,70 @@ fn set_accepted_socket_options(
121160
}
122161
}
123162

163+
fn make_keepalive(
164+
keepalive_time: Option<Duration>,
165+
keepalive_interval: Option<Duration>,
166+
keepalive_retries: Option<u32>,
167+
) -> Option<TcpKeepalive> {
168+
let mut dirty = false;
169+
let mut keepalive = TcpKeepalive::new();
170+
if let Some(t) = keepalive_time {
171+
keepalive = keepalive.with_time(t);
172+
dirty = true;
173+
}
174+
175+
#[cfg(
176+
// See https://docs.rs/socket2/0.5.8/src/socket2/lib.rs.html#511-525
177+
any(
178+
target_os = "android",
179+
target_os = "dragonfly",
180+
target_os = "freebsd",
181+
target_os = "fuchsia",
182+
target_os = "illumos",
183+
target_os = "ios",
184+
target_os = "visionos",
185+
target_os = "linux",
186+
target_os = "macos",
187+
target_os = "netbsd",
188+
target_os = "tvos",
189+
target_os = "watchos",
190+
target_os = "windows",
191+
)
192+
)]
193+
if let Some(t) = keepalive_interval {
194+
keepalive = keepalive.with_interval(t);
195+
dirty = true;
196+
}
197+
198+
#[cfg(
199+
// See https://docs.rs/socket2/0.5.8/src/socket2/lib.rs.html#557-570
200+
any(
201+
target_os = "android",
202+
target_os = "dragonfly",
203+
target_os = "freebsd",
204+
target_os = "fuchsia",
205+
target_os = "illumos",
206+
target_os = "ios",
207+
target_os = "visionos",
208+
target_os = "linux",
209+
target_os = "macos",
210+
target_os = "netbsd",
211+
target_os = "tvos",
212+
target_os = "watchos",
213+
)
214+
)]
215+
if let Some(r) = keepalive_retries {
216+
keepalive = keepalive.with_retries(r);
217+
dirty = true;
218+
}
219+
220+
// avoid clippy errors for targets that do not use these fields.
221+
let _ = keepalive_retries;
222+
let _ = keepalive_interval;
223+
224+
dirty.then_some(keepalive)
225+
}
226+
124227
#[cfg(test)]
125228
mod tests {
126229
use crate::transport::server::TcpIncoming;

tonic/src/transport/server/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,8 @@ impl<L> Server<L> {
344344
/// specified will be the time to remain idle before sending TCP keepalive
345345
/// probes.
346346
///
347+
/// Important: This setting is only respected when not using `serve_with_incoming`.
348+
///
347349
/// Default is no keepalive (`None`)
348350
///
349351
#[must_use]

0 commit comments

Comments
 (0)
close