diff options
Diffstat (limited to 'src/net/tcp/socket.rs')
-rw-r--r-- | src/net/tcp/socket.rs | 336 |
1 files changed, 324 insertions, 12 deletions
diff --git a/src/net/tcp/socket.rs b/src/net/tcp/socket.rs index f3e27c3..35a589c 100644 --- a/src/net/tcp/socket.rs +++ b/src/net/tcp/socket.rs @@ -1,14 +1,14 @@ -use crate::net::{TcpStream, TcpListener}; -use crate::sys; - use std::io; use std::mem; use std::net::SocketAddr; -use std::time::Duration; #[cfg(unix)] -use std::os::unix::io::{AsRawFd, RawFd, FromRawFd}; +use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; #[cfg(windows)] use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket}; +use std::time::Duration; + +use crate::net::{TcpListener, TcpStream}; +use crate::sys; /// A non-blocking TCP socket used to configure a stream or listener. /// @@ -22,23 +22,42 @@ pub struct TcpSocket { sys: sys::tcp::TcpSocket, } +/// Configures a socket's TCP keepalive parameters. +#[derive(Debug, Default, Clone)] +pub struct TcpKeepalive { + pub(crate) time: Option<Duration>, + #[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", + target_os = "windows", + ))] + pub(crate) interval: Option<Duration>, + #[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", + ))] + pub(crate) retries: Option<u32>, +} + impl TcpSocket { /// Create a new IPv4 TCP socket. /// /// This calls `socket(2)`. pub fn new_v4() -> io::Result<TcpSocket> { - sys::tcp::new_v4_socket().map(|sys| TcpSocket { - sys - }) + sys::tcp::new_v4_socket().map(|sys| TcpSocket { sys }) } /// Create a new IPv6 TCP socket. /// /// This calls `socket(2)`. pub fn new_v6() -> io::Result<TcpSocket> { - sys::tcp::new_v6_socket().map(|sys| TcpSocket { - sys - }) + sys::tcp::new_v6_socket().map(|sys| TcpSocket { sys }) } pub(crate) fn new_for_addr(addr: SocketAddr) -> io::Result<TcpSocket> { @@ -106,6 +125,201 @@ impl TcpSocket { sys::tcp::set_linger(self.sys, dur) } + /// Gets the value of `SO_LINGER` on this socket + pub fn get_linger(&self) -> io::Result<Option<Duration>> { + sys::tcp::get_linger(self.sys) + } + + /// Sets the value of `SO_RCVBUF` on this socket. + pub fn set_recv_buffer_size(&self, size: u32) -> io::Result<()> { + sys::tcp::set_recv_buffer_size(self.sys, size) + } + + /// Get the value of `SO_RCVBUF` set on this socket. + /// + /// 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_recv_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 receive 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 get_recv_buffer_size(&self) -> io::Result<u32> { + sys::tcp::get_recv_buffer_size(self.sys) + } + + /// Sets the value of `SO_SNDBUF` on this socket. + pub fn set_send_buffer_size(&self, size: u32) -> io::Result<()> { + sys::tcp::set_send_buffer_size(self.sys, size) + } + + /// Get the value of `SO_SNDBUF` set on this socket. + /// + /// 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 + /// 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_send_buffer_size`]: #method.set_send_buffer_size + pub fn get_send_buffer_size(&self) -> io::Result<u32> { + sys::tcp::get_send_buffer_size(self.sys) + } + + /// Sets whether keepalive messages are enabled to be sent on this socket. + /// + /// This will set the `SO_KEEPALIVE` option on this socket. + pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> { + sys::tcp::set_keepalive(self.sys, keepalive) + } + + /// Returns whether or not TCP keepalive probes will be sent by this socket. + pub fn get_keepalive(&self) -> io::Result<bool> { + sys::tcp::get_keepalive(self.sys) + } + + /// 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 + /// ``` + /// use mio::net::{TcpSocket, TcpKeepalive}; + /// use std::time::Duration; + /// + /// # fn main() -> Result<(), std::io::Error> { + /// let socket = TcpSocket::new_v6()?; + /// 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)?; + /// # Ok(()) } + /// ``` + /// + /// [`TcpKeepalive`]: ../struct.TcpKeepalive.html + /// [keepalive time]: ../struct.TcpKeepalive.html#method.with_time + pub fn set_keepalive_params(&self, keepalive: TcpKeepalive) -> io::Result<()> { + self.set_keepalive(true)?; + sys::tcp::set_keepalive_params(self.sys, 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 get_keepalive_time(&self) -> io::Result<Option<Duration>> { + sys::tcp::get_keepalive_time(self.sys) + } + + /// 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. + #[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 get_keepalive_interval(&self) -> io::Result<Option<Duration>> { + sys::tcp::get_keepalive_interval(self.sys) + } + + /// 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 get_keepalive_retries(&self) -> io::Result<Option<u32>> { + sys::tcp::get_keepalive_retries(self.sys) + } + /// Returns the local address of this socket /// /// Will return `Err` result in windows if called before calling `bind` @@ -121,6 +335,16 @@ impl Drop for TcpSocket { } #[cfg(unix)] +impl IntoRawFd for TcpSocket { + fn into_raw_fd(self) -> RawFd { + let ret = self.sys; + // Avoid closing the socket + mem::forget(self); + ret + } +} + +#[cfg(unix)] impl AsRawFd for TcpSocket { fn as_raw_fd(&self) -> RawFd { self.sys @@ -172,6 +396,94 @@ impl FromRawSocket for TcpSocket { /// The caller is responsible for ensuring that the socket is in /// non-blocking mode. unsafe fn from_raw_socket(socket: RawSocket) -> TcpSocket { - TcpSocket { sys: socket as sys::tcp::TcpSocket } + TcpSocket { + sys: socket as sys::tcp::TcpSocket, + } + } +} + +impl TcpKeepalive { + // Sets the amount of time after which TCP keepalive probes will be sent + /// on idle connections. + /// + /// This will set 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, this sets the value of + /// the `tcp_keepalive` struct's `keepalivetime` field. + /// + /// Some platforms specify this value in seconds, so sub-second + /// specifications may be omitted. + pub fn with_time(self, time: Duration) -> Self { + Self { + time: Some(time), + ..self + } + } + + /// Sets the time interval between TCP keepalive probes. + /// This sets the value of `TCP_KEEPINTVL` on supported Unix operating + /// systems. On Windows, this sets the value of the `tcp_keepalive` struct's + /// `keepaliveinterval` field. + /// + /// Some platforms specify this value in seconds, so sub-second + /// specifications may be omitted. + #[cfg_attr( + docsrs, + doc(cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", + target_os = "windows" + ))) + )] + #[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", + target_os = "windows" + ))] + pub fn with_interval(self, interval: Duration) -> Self { + Self { + interval: Some(interval), + ..self + } + } + + /// Sets the maximum number of TCP keepalive probes that will be sent before + /// dropping a connection, if TCP keepalive is enabled on this socket. + /// + /// This will set the value of `TCP_KEEPCNT` on Unix operating systems that + /// support this option. + #[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 with_retries(self, retries: u32) -> Self { + Self { + retries: Some(retries), + ..self + } + } + + /// Returns a new, empty set of TCP keepalive parameters. + pub fn new() -> Self { + Self::default() } } |