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: add TcpSocket::{set, get}_keepalive #1385

Merged
merged 37 commits into from
Nov 11, 2020
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
6c00878
net: add `TcpSocket::{set, get}_keepalive`
hawkw Oct 31, 2020
f5c0857
make rustfmt happy
hawkw Oct 31, 2020
93b45d7
windows: use i32::max_value
hawkw Nov 1, 2020
b9a3a52
fixup windows
hawkw Nov 1, 2020
04cd34f
maybe these pointer casts will be good
hawkw Nov 2, 2020
0f52a28
fix WSAIoctl argument order etc
hawkw Nov 2, 2020
20a018c
tcp: add separate APIs for every keepalive option
hawkw Nov 3, 2020
eb8ed01
add doc_cfg
hawkw Nov 3, 2020
e769f9d
undo rustfmt
hawkw Nov 3, 2020
7c14e89
apparently Macs do have TCP_KEEPINTVL/TCP_KEEPCNT
hawkw Nov 4, 2020
51164b7
fix shell rt cfgs
hawkw Nov 4, 2020
600a662
fix missing target_os cfgs
hawkw Nov 4, 2020
21c4c7b
????
hawkw Nov 4, 2020
dcbd0ec
maybe we can at least see what's going on
hawkw Nov 4, 2020
bbeac6f
maybe this has to be a valid ptr???
hawkw Nov 4, 2020
f0a704b
asdf
hawkw Nov 4, 2020
2f9a2a4
more println
hawkw Nov 4, 2020
d251450
agh
hawkw Nov 5, 2020
9bfad08
hmm let's see what happens if we use getsockopt
hawkw Nov 5, 2020
21ff829
also remove printlns
hawkw Nov 5, 2020
4c1b6a9
oops
hawkw Nov 5, 2020
a75330c
lol oh no it's in a different header entirely
hawkw Nov 5, 2020
096fd96
AGH NO IT WAS A *char ALL ALONG
hawkw Nov 5, 2020
018336d
fsdjal
hawkw Nov 5, 2020
322c0b6
it was suggested that I try WSPIoctl
hawkw Nov 5, 2020
ff15a1b
switch to a builder API because winsock is awful
hawkw Nov 5, 2020
7b9163b
fixup doctest
hawkw Nov 6, 2020
ff49a10
Merge branch 'master' into eliza/even-more-sockopts
hawkw Nov 6, 2020
3effa9b
rustfmt
hawkw Nov 6, 2020
ea2dee9
oops
hawkw Nov 6, 2020
0444d5d
fix merge mistake
hawkw Nov 6, 2020
9570c7d
windows fixup
hawkw Nov 6, 2020
942a53d
blurgh, shell
hawkw Nov 6, 2020
b83957d
nicer cfgs
hawkw Nov 6, 2020
884bf67
Merge branch 'master' into eliza/even-more-sockopts
hawkw Nov 9, 2020
91d9e9b
unix: just forward inner errors
hawkw Nov 10, 2020
605d07a
add platform notes
hawkw Nov 10, 2020
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
2 changes: 1 addition & 1 deletion src/net/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
//! [portability guidelines]: ../struct.Poll.html#portability

mod tcp;
pub use self::tcp::{TcpListener, TcpSocket, TcpStream};
pub use self::tcp::{TcpListener, TcpSocket, TcpStream, TcpKeepalive};

mod udp;
pub use self::udp::UdpSocket;
Expand Down
2 changes: 1 addition & 1 deletion src/net/tcp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ mod listener;
pub use self::listener::TcpListener;

mod socket;
pub use self::socket::TcpSocket;
pub use self::socket::{TcpSocket, TcpKeepalive};

mod stream;
pub use self::stream::TcpStream;
232 changes: 227 additions & 5 deletions src/net/tcp/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,45 @@ 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 }
})
}

Expand Down Expand Up @@ -168,7 +191,126 @@ impl TcpSocket {
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.
///
/// Additionally, this will enable TCP keepalive on this socket, if it is
/// not already enabled.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you add a # Notes section saying that all parameters related to keepalive will be overwritten.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, that's actually only the case on Windows; on Unix, we can avoid overwriting previously set parameters. Do you think we should have the same behavior on Unix OSes even though it's less than ideal?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's change it to that it might overwrite other keepalive parameters and that either only a single call should be made, or that all desired parameters should be set in all calls. I don't want to make unnecessary system calls on Unix.

///
/// For example:
/// ```
/// 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`
Expand Down Expand Up @@ -238,3 +380,83 @@ impl FromRawSocket for 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()
}
}
54 changes: 54 additions & 0 deletions src/sys/shell/tcp.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::io;
use std::net::{self, SocketAddr};
use std::time::Duration;
use crate::net::TcpKeepalive;

pub(crate) type TcpSocket = i32;

Expand Down Expand Up @@ -70,6 +71,59 @@ pub(crate) fn get_send_buffer_size(_: TcpSocket) -> io::Result<u32> {
os_required!();
}

pub(crate) fn set_keepalive(_: TcpSocket, _: bool) -> io::Result<()> {
os_required!();
}

pub(crate) fn get_keepalive(_: TcpSocket) -> io::Result<bool> {
os_required!();
}

#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
target_os = "windows",
))]
pub(crate) fn set_keepalive_params(_: TcpSocket, _: TcpKeepalive) -> io::Result<()> {
os_required!()
}

#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
))]
pub(crate) fn get_keepalive_time(_: TcpSocket) -> io::Result<Option<Duration>> {
os_required!()
}

#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
))]
pub(crate) fn get_keepalive_interval(_: TcpSocket) -> io::Result<Option<Duration>> {
os_required!()
}

#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
))]
pub(crate) fn get_keepalive_retries(_: TcpSocket) -> io::Result<Option<u32>> {
os_required!()
}

pub fn accept(_: &net::TcpListener) -> io::Result<(net::TcpStream, SocketAddr)> {
os_required!();
}
Expand Down
Loading