Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion tonic/src/transport/channel/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ pub struct Endpoint {
pub(crate) init_stream_window_size: Option<u32>,
pub(crate) init_connection_window_size: Option<u32>,
pub(crate) tcp_keepalive: Option<Duration>,
pub(crate) tcp_keepalive_interval: Option<Duration>,
pub(crate) tcp_keepalive_retries: Option<u32>,
pub(crate) tcp_nodelay: bool,
pub(crate) http2_keep_alive_interval: Option<Duration>,
pub(crate) http2_keep_alive_timeout: Option<Duration>,
Expand Down Expand Up @@ -84,6 +86,8 @@ impl Endpoint {
init_stream_window_size: None,
init_connection_window_size: None,
tcp_keepalive: None,
tcp_keepalive_interval: None,
tcp_keepalive_retries: None,
tcp_nodelay: true,
http2_keep_alive_interval: None,
http2_keep_alive_timeout: None,
Expand Down Expand Up @@ -111,6 +115,8 @@ impl Endpoint {
init_stream_window_size: None,
init_connection_window_size: None,
tcp_keepalive: None,
tcp_keepalive_interval: None,
tcp_keepalive_retries: None,
tcp_nodelay: true,
http2_keep_alive_interval: None,
http2_keep_alive_timeout: None,
Expand Down Expand Up @@ -258,14 +264,38 @@ impl Endpoint {
/// probes.
///
/// Default is no keepalive (`None`)
///
pub fn tcp_keepalive(self, tcp_keepalive: Option<Duration>) -> Self {
Endpoint {
tcp_keepalive,
..self
}
}

/// Set the duration between two successive TCP keepalive retransmissions,
/// if acknowledgement to the previous keepalive transmission is not received.
///
/// This is only used if `tcp_keepalive` is not None.
///
/// Defaults to None, which is the system default.
pub fn tcp_keepalive_interval(self, tcp_keepalive_interval: Option<Duration>) -> Self {
Endpoint {
tcp_keepalive_interval,
..self
}
}

/// Set the number of retransmissions to be carried out before declaring that remote end is not available.
///
/// This is only used if `tcp_keepalive` is not None.
///
/// Defaults to None, which is the system default.
pub fn tcp_keepalive_retries(self, tcp_keepalive_retries: Option<u32>) -> Self {
Endpoint {
tcp_keepalive_retries,
..self
}
}

/// Apply a concurrency limit to each request.
///
/// ```
Expand Down Expand Up @@ -428,6 +458,8 @@ impl Endpoint {
http.enforce_http(false);
http.set_nodelay(self.tcp_nodelay);
http.set_keepalive(self.tcp_keepalive);
http.set_keepalive_interval(self.tcp_keepalive_interval);
http.set_keepalive_retries(self.tcp_keepalive_retries);
http.set_connect_timeout(self.connect_timeout);
http.set_local_address(self.local_address);
self.connector(http)
Expand Down Expand Up @@ -543,6 +575,16 @@ impl Endpoint {
pub fn get_tcp_keepalive(&self) -> Option<Duration> {
self.tcp_keepalive
}

/// Get whether TCP keepalive interval.
pub fn get_tcp_keepalive_interval(&self) -> Option<Duration> {
self.tcp_keepalive_interval
}

/// Get whether TCP keepalive retries.
pub fn get_tcp_keepalive_retries(&self) -> Option<u32> {
self.tcp_keepalive_retries
}
}

impl From<Uri> for Endpoint {
Expand Down
109 changes: 106 additions & 3 deletions tonic/src/transport/server/incoming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ pub struct TcpIncoming {
inner: TcpListenerStream,
nodelay: Option<bool>,
keepalive: Option<TcpKeepalive>,
keepalive_time: Option<Duration>,
keepalive_interval: Option<Duration>,
keepalive_retries: Option<u32>,
}

impl TcpIncoming {
Expand Down Expand Up @@ -66,9 +69,42 @@ impl TcpIncoming {
}

/// Sets the `TCP_KEEPALIVE` option on the accepted connection.
pub fn with_keepalive(self, keepalive: Option<Duration>) -> Self {
let keepalive = keepalive.map(|t| TcpKeepalive::new().with_time(t));
Self { keepalive, ..self }
pub fn with_keepalive(self, keepalive_time: Option<Duration>) -> Self {
Self {
keepalive_time,
keepalive: make_keepalive(
keepalive_time,
self.keepalive_interval,
self.keepalive_retries,
),
..self
}
}

/// Sets the `TCP_KEEPINTVL` option on the accepted connection.
pub fn with_keepalive_interval(self, keepalive_interval: Option<Duration>) -> Self {
Self {
keepalive_interval,
keepalive: make_keepalive(
self.keepalive_time,
keepalive_interval,
self.keepalive_retries,
),
..self
}
}

/// Sets the `TCP_KEEPCNT` option on the accepted connection.
pub fn with_keepalive_retries(self, keepalive_retries: Option<u32>) -> Self {
Self {
keepalive_retries,
keepalive: make_keepalive(
self.keepalive_time,
self.keepalive_interval,
keepalive_retries,
),
..self
}
}

/// Returns the local address that this tcp incoming is bound to.
Expand All @@ -83,6 +119,9 @@ impl From<TcpListener> for TcpIncoming {
inner: TcpListenerStream::new(listener),
nodelay: None,
keepalive: None,
keepalive_time: None,
keepalive_interval: None,
keepalive_retries: None,
}
}
}
Expand Down Expand Up @@ -121,6 +160,70 @@ fn set_accepted_socket_options(
}
}

fn make_keepalive(
keepalive_time: Option<Duration>,
keepalive_interval: Option<Duration>,
keepalive_retries: Option<u32>,
) -> Option<TcpKeepalive> {
let mut dirty = false;
let mut keepalive = TcpKeepalive::new();
if let Some(t) = keepalive_time {
keepalive = keepalive.with_time(t);
dirty = true;
}

#[cfg(
// See https://docs.rs/socket2/0.5.8/src/socket2/lib.rs.html#511-525
any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "ios",
target_os = "visionos",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "tvos",
target_os = "watchos",
target_os = "windows",
)
)]
if let Some(t) = keepalive_interval {
keepalive = keepalive.with_interval(t);
dirty = true;
}

#[cfg(
// See https://docs.rs/socket2/0.5.8/src/socket2/lib.rs.html#557-570
any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "ios",
target_os = "visionos",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "tvos",
target_os = "watchos",
)
)]
if let Some(r) = keepalive_retries {
keepalive = keepalive.with_retries(r);
dirty = true;
}

// avoid clippy errors for targets that do not use these fields.
let _ = keepalive_retries;
let _ = keepalive_interval;

dirty.then_some(keepalive)
}

#[cfg(test)]
mod tests {
use crate::transport::server::TcpIncoming;
Expand Down
2 changes: 2 additions & 0 deletions tonic/src/transport/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@ impl<L> Server<L> {
/// specified will be the time to remain idle before sending TCP keepalive
/// probes.
///
/// Important: This setting is only respected when not using `serve_with_incoming`.
///
/// Default is no keepalive (`None`)
///
#[must_use]
Expand Down