Skip to content

Commit 290c43a

Browse files
authored
net: add keepalive support to TcpSocket
Signed-off-by: Eliza Weisman <[email protected]>
1 parent 152e075 commit 290c43a

File tree

7 files changed

+681
-22
lines changed

7 files changed

+681
-22
lines changed

src/net/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
//! [portability guidelines]: ../struct.Poll.html#portability
99
1010
mod tcp;
11-
pub use self::tcp::{TcpListener, TcpSocket, TcpStream};
11+
pub use self::tcp::{TcpListener, TcpSocket, TcpStream, TcpKeepalive};
1212

1313
mod udp;
1414
pub use self::udp::UdpSocket;

src/net/tcp/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ mod listener;
22
pub use self::listener::TcpListener;
33

44
mod socket;
5-
pub use self::socket::TcpSocket;
5+
pub use self::socket::{TcpSocket, TcpKeepalive};
66

77
mod stream;
88
pub use self::stream::TcpStream;

src/net/tcp/socket.rs

Lines changed: 234 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,45 @@ pub struct TcpSocket {
2222
sys: sys::tcp::TcpSocket,
2323
}
2424

25+
/// Configures a socket's TCP keepalive parameters.
26+
#[derive(Debug, Default, Clone)]
27+
pub struct TcpKeepalive {
28+
pub(crate) time: Option<Duration>,
29+
#[cfg(any(
30+
target_os = "linux",
31+
target_os = "macos",
32+
target_os = "ios",
33+
target_os = "freebsd",
34+
target_os = "netbsd",
35+
target_os = "windows",
36+
))]
37+
pub(crate) interval: Option<Duration>,
38+
#[cfg(any(
39+
target_os = "linux",
40+
target_os = "macos",
41+
target_os = "ios",
42+
target_os = "freebsd",
43+
target_os = "netbsd",
44+
))]
45+
pub(crate) retries: Option<u32>,
46+
}
47+
2548
impl TcpSocket {
2649
/// Create a new IPv4 TCP socket.
2750
///
2851
/// This calls `socket(2)`.
2952
pub fn new_v4() -> io::Result<TcpSocket> {
30-
sys::tcp::new_v4_socket().map(|sys| TcpSocket {
31-
sys
53+
sys::tcp::new_v4_socket().map(|sys| {
54+
TcpSocket { sys }
3255
})
3356
}
3457

3558
/// Create a new IPv6 TCP socket.
3659
///
3760
/// This calls `socket(2)`.
3861
pub fn new_v6() -> io::Result<TcpSocket> {
39-
sys::tcp::new_v6_socket().map(|sys| TcpSocket {
40-
sys
62+
sys::tcp::new_v6_socket().map(|sys| {
63+
TcpSocket { sys }
4164
})
4265
}
4366

@@ -168,7 +191,133 @@ impl TcpSocket {
168191
pub fn get_send_buffer_size(&self) -> io::Result<u32> {
169192
sys::tcp::get_send_buffer_size(self.sys)
170193
}
171-
194+
195+
/// Sets whether keepalive messages are enabled to be sent on this socket.
196+
///
197+
/// This will set the `SO_KEEPALIVE` option on this socket.
198+
pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> {
199+
sys::tcp::set_keepalive(self.sys, keepalive)
200+
}
201+
202+
/// Returns whether or not TCP keepalive probes will be sent by this socket.
203+
pub fn get_keepalive(&self) -> io::Result<bool> {
204+
sys::tcp::get_keepalive(self.sys)
205+
}
206+
207+
/// Sets parameters configuring TCP keepalive probes for this socket.
208+
///
209+
/// The supported parameters depend on the operating system, and are
210+
/// configured using the [`TcpKeepalive`] struct. At a minimum, all systems
211+
/// support configuring the [keepalive time]: the time after which the OS
212+
/// will start sending keepalive messages on an idle connection.
213+
///
214+
/// # Notes
215+
///
216+
/// * This will enable TCP keepalive on this socket, if it is not already
217+
/// enabled.
218+
/// * On some platforms, such as Windows, any keepalive parameters *not*
219+
/// configured by the `TcpKeepalive` struct passed to this function may be
220+
/// overwritten with their default values. Therefore, this function should
221+
/// either only be called once per socket, or the same parameters should
222+
/// be passed every time it is called.
223+
///
224+
/// # Examples
225+
/// ```
226+
/// use mio::net::{TcpSocket, TcpKeepalive};
227+
/// use std::time::Duration;
228+
///
229+
/// # fn main() -> Result<(), std::io::Error> {
230+
/// let socket = TcpSocket::new_v6()?;
231+
/// let keepalive = TcpKeepalive::default()
232+
/// .with_time(Duration::from_secs(4));
233+
/// // Depending on the target operating system, we may also be able to
234+
/// // configure the keepalive probe interval and/or the number of retries
235+
/// // here as well.
236+
///
237+
/// socket.set_keepalive_params(keepalive)?;
238+
/// # Ok(()) }
239+
/// ```
240+
///
241+
/// [`TcpKeepalive`]: ../struct.TcpKeepalive.html
242+
/// [keepalive time]: ../struct.TcpKeepalive.html#method.with_time
243+
pub fn set_keepalive_params(&self, keepalive: TcpKeepalive) -> io::Result<()> {
244+
self.set_keepalive(true)?;
245+
sys::tcp::set_keepalive_params(self.sys, keepalive)
246+
}
247+
248+
/// Returns the amount of time after which TCP keepalive probes will be sent
249+
/// on idle connections.
250+
///
251+
/// If `None`, then keepalive messages are disabled.
252+
///
253+
/// This returns the value of `SO_KEEPALIVE` + `IPPROTO_TCP` on OpenBSD,
254+
/// NetBSD, and Haiku, `TCP_KEEPALIVE` on macOS and iOS, and `TCP_KEEPIDLE`
255+
/// on all other Unix operating systems. On Windows, it is not possible to
256+
/// access the value of TCP keepalive parameters after they have been set.
257+
///
258+
/// Some platforms specify this value in seconds, so sub-second
259+
/// specifications may be omitted.
260+
#[cfg_attr(docsrs, doc(cfg(not(target_os = "windows"))))]
261+
#[cfg(not(target_os = "windows"))]
262+
pub fn get_keepalive_time(&self) -> io::Result<Option<Duration>> {
263+
sys::tcp::get_keepalive_time(self.sys)
264+
}
265+
266+
/// Returns the time interval between TCP keepalive probes, if TCP keepalive is
267+
/// enabled on this socket.
268+
///
269+
/// If `None`, then keepalive messages are disabled.
270+
///
271+
/// This returns the value of `TCP_KEEPINTVL` on supported Unix operating
272+
/// systems. On Windows, it is not possible to access the value of TCP
273+
/// keepalive parameters after they have been set..
274+
///
275+
/// Some platforms specify this value in seconds, so sub-second
276+
/// specifications may be omitted.
277+
#[cfg_attr(docsrs, doc(cfg(any(
278+
target_os = "linux",
279+
target_os = "macos",
280+
target_os = "ios",
281+
target_os = "freebsd",
282+
target_os = "netbsd",
283+
))))]
284+
#[cfg(any(
285+
target_os = "linux",
286+
target_os = "macos",
287+
target_os = "ios",
288+
target_os = "freebsd",
289+
target_os = "netbsd",
290+
))]
291+
pub fn get_keepalive_interval(&self) -> io::Result<Option<Duration>> {
292+
sys::tcp::get_keepalive_interval(self.sys)
293+
}
294+
295+
/// Returns the maximum number of TCP keepalive probes that will be sent before
296+
/// dropping a connection, if TCP keepalive is enabled on this socket.
297+
///
298+
/// If `None`, then keepalive messages are disabled.
299+
///
300+
/// This returns the value of `TCP_KEEPCNT` on Unix operating systems that
301+
/// support this option. On Windows, it is not possible to access the value
302+
/// of TCP keepalive parameters after they have been set.
303+
#[cfg_attr(docsrs, doc(cfg(any(
304+
target_os = "linux",
305+
target_os = "macos",
306+
target_os = "ios",
307+
target_os = "freebsd",
308+
target_os = "netbsd",
309+
))))]
310+
#[cfg(any(
311+
target_os = "linux",
312+
target_os = "macos",
313+
target_os = "ios",
314+
target_os = "freebsd",
315+
target_os = "netbsd",
316+
))]
317+
pub fn get_keepalive_retries(&self) -> io::Result<Option<u32>> {
318+
sys::tcp::get_keepalive_retries(self.sys)
319+
}
320+
172321
/// Returns the local address of this socket
173322
///
174323
/// Will return `Err` result in windows if called before calling `bind`
@@ -238,3 +387,83 @@ impl FromRawSocket for TcpSocket {
238387
TcpSocket { sys: socket as sys::tcp::TcpSocket }
239388
}
240389
}
390+
391+
impl TcpKeepalive {
392+
// Sets the amount of time after which TCP keepalive probes will be sent
393+
/// on idle connections.
394+
///
395+
/// This will set the value of `SO_KEEPALIVE` + `IPPROTO_TCP` on OpenBSD,
396+
/// NetBSD, and Haiku, `TCP_KEEPALIVE` on macOS and iOS, and `TCP_KEEPIDLE`
397+
/// on all other Unix operating systems. On Windows, this sets the value of
398+
/// the `tcp_keepalive` struct's `keepalivetime` field.
399+
///
400+
/// Some platforms specify this value in seconds, so sub-second
401+
/// specifications may be omitted.
402+
pub fn with_time(self, time: Duration) -> Self {
403+
Self {
404+
time: Some(time),
405+
..self
406+
}
407+
}
408+
409+
/// Sets the time interval between TCP keepalive probes.
410+
/// This sets the value of `TCP_KEEPINTVL` on supported Unix operating
411+
/// systems. On Windows, this sets the value of the `tcp_keepalive` struct's
412+
/// `keepaliveinterval` field.
413+
///
414+
/// Some platforms specify this value in seconds, so sub-second
415+
/// specifications may be omitted.
416+
#[cfg_attr(docsrs, doc(cfg(any(
417+
target_os = "linux",
418+
target_os = "macos",
419+
target_os = "ios",
420+
target_os = "freebsd",
421+
target_os = "netbsd",
422+
target_os = "windows"
423+
))))]
424+
#[cfg(any(
425+
target_os = "linux",
426+
target_os = "macos",
427+
target_os = "ios",
428+
target_os = "freebsd",
429+
target_os = "netbsd",
430+
target_os = "windows"
431+
))]
432+
pub fn with_interval(self, interval: Duration) -> Self {
433+
Self {
434+
interval: Some(interval),
435+
..self
436+
}
437+
}
438+
439+
/// Sets the maximum number of TCP keepalive probes that will be sent before
440+
/// dropping a connection, if TCP keepalive is enabled on this socket.
441+
///
442+
/// This will set the value of `TCP_KEEPCNT` on Unix operating systems that
443+
/// support this option.
444+
#[cfg_attr(docsrs, doc(cfg(any(
445+
target_os = "linux",
446+
target_os = "macos",
447+
target_os = "ios",
448+
target_os = "freebsd",
449+
target_os = "netbsd",
450+
))))]
451+
#[cfg(any(
452+
target_os = "linux",
453+
target_os = "macos",
454+
target_os = "ios",
455+
target_os = "freebsd",
456+
target_os = "netbsd",
457+
))]
458+
pub fn with_retries(self, retries: u32) -> Self {
459+
Self {
460+
retries: Some(retries),
461+
..self
462+
}
463+
}
464+
465+
/// Returns a new, empty set of TCP keepalive parameters.
466+
pub fn new() -> Self {
467+
Self::default()
468+
}
469+
}

src/sys/shell/tcp.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::io;
22
use std::net::{self, SocketAddr};
33
use std::time::Duration;
4+
use crate::net::TcpKeepalive;
45

56
pub(crate) type TcpSocket = i32;
67

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

74+
pub(crate) fn set_keepalive(_: TcpSocket, _: bool) -> io::Result<()> {
75+
os_required!();
76+
}
77+
78+
pub(crate) fn get_keepalive(_: TcpSocket) -> io::Result<bool> {
79+
os_required!();
80+
}
81+
82+
#[cfg(any(
83+
target_os = "linux",
84+
target_os = "macos",
85+
target_os = "ios",
86+
target_os = "freebsd",
87+
target_os = "netbsd",
88+
target_os = "windows",
89+
))]
90+
pub(crate) fn set_keepalive_params(_: TcpSocket, _: TcpKeepalive) -> io::Result<()> {
91+
os_required!()
92+
}
93+
94+
#[cfg(any(
95+
target_os = "linux",
96+
target_os = "macos",
97+
target_os = "ios",
98+
target_os = "freebsd",
99+
target_os = "netbsd",
100+
))]
101+
pub(crate) fn get_keepalive_time(_: TcpSocket) -> io::Result<Option<Duration>> {
102+
os_required!()
103+
}
104+
105+
#[cfg(any(
106+
target_os = "linux",
107+
target_os = "macos",
108+
target_os = "ios",
109+
target_os = "freebsd",
110+
target_os = "netbsd",
111+
))]
112+
pub(crate) fn get_keepalive_interval(_: TcpSocket) -> io::Result<Option<Duration>> {
113+
os_required!()
114+
}
115+
116+
#[cfg(any(
117+
target_os = "linux",
118+
target_os = "macos",
119+
target_os = "ios",
120+
target_os = "freebsd",
121+
target_os = "netbsd",
122+
))]
123+
pub(crate) fn get_keepalive_retries(_: TcpSocket) -> io::Result<Option<u32>> {
124+
os_required!()
125+
}
126+
73127
pub fn accept(_: &net::TcpListener) -> io::Result<(net::TcpStream, SocketAddr)> {
74128
os_required!();
75129
}

0 commit comments

Comments
 (0)