Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net: expose keepalive configuration on TcpSocket #3146

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion tokio/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ bytes = { version = "0.6.0", optional = true }
futures-core = { version = "0.3.0", optional = true }
lazy_static = { version = "1.4.0", optional = true }
memchr = { version = "2.2", optional = true }
mio = { version = "0.7.5", optional = true }
mio = { version = "0.7.6", optional = true }
num_cpus = { version = "1.8.0", optional = true }
parking_lot = { version = "0.11.0", optional = true }
slab = { version = "0.4.1", optional = true }
Expand Down
2 changes: 1 addition & 1 deletion tokio/src/net/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ cfg_net! {

pub mod tcp;
pub use tcp::listener::TcpListener;
pub use tcp::socket::TcpSocket;
pub use tcp::socket::{TcpSocket, TcpKeepalive};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably can keep this type in tokio::net::tcp and not re-export it.

pub use tcp::stream::TcpStream;

pub mod udp;
Expand Down
255 changes: 255 additions & 0 deletions tokio/src/net/tcp/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::net::{TcpListener, TcpStream};
use std::fmt;
use std::io;
use std::net::SocketAddr;
use std::time::Duration;

#[cfg(unix)]
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
Expand Down Expand Up @@ -86,6 +87,11 @@ cfg_net! {
}
}

cfg_net! {
#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411
pub use mio::net::TcpKeepalive;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are not exposing mio as part of Tokio's public API. If we do this, we will need to re-define the type.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes a mio type part of the public API, which is probably not ideal.

}

impl TcpSocket {
/// Create a new socket configured for IPv4.
///
Expand Down Expand Up @@ -280,6 +286,255 @@ impl TcpSocket {
self.inner.get_reuseport()
}

/// Sets the size of the TCP send buffer on this socket.
///
/// On most operating systems, this sets the `SO_SNDBUF` socket option.
pub fn set_send_buffer_size(&self, size: u32) -> io::Result<()> {
self.inner.set_send_buffer_size(size)
}

/// Returns the size of the TCP send buffer for this socket.
///
/// On most operating systems, this is the value of the `SO_SNDBUF` socket
/// option.
///
/// Note that if [`set_send_buffer_size`] has been called on this socket
/// previously, the value returned by this function may not be the same as
/// the argument provided to `set_send_buffer_size`. This is for the
/// following reasons:
///
/// * Most operating systems have minimum and maximum allowed sizes for the
/// send buffer, and will clamp the provided value if it is below the
/// minimum or above the maximum. The minimum and maximum buffer sizes are
/// OS-dependent.
/// * Linux will double the buffer size to account for internal bookkeeping
/// data, and returns the doubled value from `getsockopt(2)`. As per `man
/// 7 socket`:
/// > Sets or gets the maximum socket send buffer in bytes. The
/// > kernel doubles this value (to allow space for bookkeeping
/// > overhead) when it is set using `setsockopt(2)`, and this doubled
/// > value is returned by `getsockopt(2)`.
///
/// [`set_send_buffer_size`]: #method.set_send_buffer_size
pub fn send_buffer_size(&self) -> io::Result<u32> {
self.inner.get_send_buffer_size()
}

/// Sets the size of the TCP receive buffer on this socket.
///
/// On most operating systems, this sets the `SO_RCVBUF` socket option.
pub fn set_recv_buffer_size(&self, size: u32) -> io::Result<()> {
self.inner.set_recv_buffer_size(size)
}

/// Returns the size of the TCP receive buffer for this socket.
///
/// On most operating systems, this is the value of the `SO_RCVBUF` socket
/// option.
///
/// Note that if [`set_recv_buffer_size`] has been called on this socket
/// previously, the value returned by this function may not be the same as
/// the argument provided to `set_send_buffer_size`. This is for the
/// following reasons:
///
/// * Most operating systems have minimum and maximum allowed sizes for the
/// receive buffer, and will clamp the provided value if it is below the
/// minimum or above the maximum. The minimum and maximum buffer sizes are
/// OS-dependent.
/// * Linux will double the buffer size to account for internal bookkeeping
/// data, and returns the doubled value from `getsockopt(2)`. As per `man
/// 7 socket`:
/// > Sets or gets the maximum socket send buffer in bytes. The
/// > kernel doubles this value (to allow space for bookkeeping
/// > overhead) when it is set using `setsockopt(2)`, and this doubled
/// > value is returned by `getsockopt(2)`.
///
/// [`set_recv_buffer_size`]: #method.set_recv_buffer_size
pub fn recv_buffer_size(&self) -> io::Result<u32> {
self.inner.get_recv_buffer_size()
}

/// Sets whether keepalive messages are enabled to be sent on this socket.
///
/// This will set the `SO_KEEPALIVE` option on this socket.
///
/// # Examples
///
/// ```no_run
/// use tokio::net::TcpSocket;
///
/// use std::io;
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// let addr = "127.0.0.1:8080".parse().unwrap();
///
/// let socket = TcpSocket::new_v4()?;
/// socket.set_keepalive(true)?;
/// assert!(socket.keepalive().unwrap());
/// socket.bind(addr)?;
///
/// let listener = socket.listen(1024)?;
/// Ok(())
/// }
/// ```
pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> {
self.inner.set_keepalive(keepalive)
}

/// Returns whether or not TCP keepalive probes will be sent by this socket.
///
/// # Examples
///
/// ```no_run
/// use tokio::net::TcpSocket;
///
/// use std::io;
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// let addr = "127.0.0.1:8080".parse().unwrap();
///
/// let socket = TcpSocket::new_v4()?;
/// socket.set_keepalive(true)?;
/// socket.bind(addr)?;
///
/// let listener = socket.listen(1024)?;
/// Ok(())
/// }
/// ```
pub fn keepalive(&self) -> io::Result<bool> {
self.inner.get_keepalive()
}

/// Sets parameters configuring TCP keepalive probes for this socket.
///
/// The supported parameters depend on the operating system, and are
/// configured using the [`TcpKeepalive`] struct. At a minimum, all systems
/// support configuring the [keepalive time]: the time after which the OS
/// will start sending keepalive messages on an idle connection.
///
/// # Notes
///
/// * This will enable TCP keepalive on this socket, if it is not already
/// enabled.
/// * On some platforms, such as Windows, any keepalive parameters *not*
/// configured by the `TcpKeepalive` struct passed to this function may be
/// overwritten with their default values. Therefore, this function should
/// either only be called once per socket, or the same parameters should
/// be passed every time it is called.
///
/// # Examples
///
/// ```no_run
/// use tokio::net::{TcpSocket, TcpKeepalive};
/// use std::time::Duration;
/// use std::io;
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// let addr = "127.0.0.1:8080".parse().unwrap();
///
/// let socket = TcpSocket::new_v4()?;
/// let keepalive = TcpKeepalive::default()
/// .with_time(Duration::from_secs(4));
/// // Depending on the target operating system, we may also be able to
/// // configure the keepalive probe interval and/or the number of retries
/// // here as well.
///
/// socket.set_keepalive_params(keepalive)?;
/// socket.bind(addr)?;
///
/// let listener = socket.listen(1024)?;
/// Ok(())
/// }
/// ```
///
/// [`TcpKeepalive`]: TcpKeepalive
/// [keepalive time]: TcpKeepalive::with_time
pub fn set_keepalive_params(&self, keepalive: TcpKeepalive) -> io::Result<()> {
self.inner.set_keepalive_params(keepalive)
}

/// Returns the amount of time after which TCP keepalive probes will be sent
/// on idle connections.
///
/// If `None`, then keepalive messages are disabled.
///
/// This returns the value of `SO_KEEPALIVE` + `IPPROTO_TCP` on OpenBSD,
/// NetBSD, and Haiku, `TCP_KEEPALIVE` on macOS and iOS, and `TCP_KEEPIDLE`
/// on all other Unix operating systems. On Windows, it is not possible to
/// access the value of TCP keepalive parameters after they have been set.
///
/// Some platforms specify this value in seconds, so sub-second
/// specifications may be omitted.
#[cfg_attr(docsrs, doc(cfg(not(target_os = "windows"))))]
#[cfg(not(target_os = "windows"))]
pub fn keepalive_time(&self) -> io::Result<Option<Duration>> {
self.inner.get_keepalive_time()
}

/// Returns the time interval between TCP keepalive probes, if TCP keepalive is
/// enabled on this socket.
///
/// If `None`, then keepalive messages are disabled.
///
/// This returns the value of `TCP_KEEPINTVL` on supported Unix operating
/// systems. On Windows, it is not possible to access the value of TCP
/// keepalive parameters after they have been set..
///
/// Some platforms specify this value in seconds, so sub-second
/// specifications may be omitted.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rounded down or up? What happens if this rounds to zero?

#[cfg_attr(
docsrs,
doc(cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
)))
)]
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
))]
pub fn keepalive_interval(&self) -> io::Result<Option<Duration>> {
self.inner.get_keepalive_interval()
}

/// Returns the maximum number of TCP keepalive probes that will be sent before
/// dropping a connection, if TCP keepalive is enabled on this socket.
///
/// If `None`, then keepalive messages are disabled.
///
/// This returns the value of `TCP_KEEPCNT` on Unix operating systems that
/// support this option. On Windows, it is not possible to access the value
/// of TCP keepalive parameters after they have been set.
#[cfg_attr(
docsrs,
doc(cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
)))
)]
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
))]
pub fn keepalive_retries(&self) -> io::Result<Option<u32>> {
self.inner.get_keepalive_retries()
}

/// Get the local address of this socket.
///
/// Will fail on windows if called before `bind`.
Expand Down