From 3b6777f1ab7952c058d69be15805a06a8ce0f1da Mon Sep 17 00:00:00 2001 From: ibraheemdev Date: Mon, 30 Aug 2021 13:02:15 -0400 Subject: [PATCH] add `TcpStream::set_linger` and `TcpStream::linger` --- library/std/src/lib.rs | 1 + library/std/src/net/tcp.rs | 47 ++++++++++++++++++++++++++ library/std/src/net/tcp/tests.rs | 15 ++++++++ library/std/src/sys/hermit/net.rs | 8 +++++ library/std/src/sys/sgx/net.rs | 8 +++++ library/std/src/sys/unix/l4re.rs | 16 +++++++++ library/std/src/sys/unix/net.rs | 23 +++++++++++++ library/std/src/sys/unsupported/net.rs | 8 +++++ library/std/src/sys/wasi/net.rs | 8 +++++ library/std/src/sys/windows/c.rs | 8 +++++ library/std/src/sys/windows/net.rs | 17 +++++++++- library/std/src/sys_common/net.rs | 8 +++++ 12 files changed, 166 insertions(+), 1 deletion(-) diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 028a066b5a130..d5e7668e47ce4 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -321,6 +321,7 @@ #![feature(stdsimd)] #![feature(stmt_expr_attributes)] #![feature(str_internals)] +#![feature(tcp_linger)] #![feature(test)] #![feature(thread_local)] #![feature(thread_local_internals)] diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 336891ec1eb94..5b4a9fa7979de 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -401,6 +401,53 @@ impl TcpStream { self.0.peek(buf) } + /// Sets the value of the `SO_LINGER` option on this socket. + /// + /// This value controls how the socket is closed when data remains + /// to be sent. If `SO_LINGER` is set, the socket will remain open + /// for the specified duration as the system attempts to send pending data. + /// Otherwise, the system may close the socket immediately, or wait for a + /// default timeout. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(tcp_linger)] + /// + /// use std::net::TcpStream; + /// use std::time::Duration; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_linger(Some(Duration::from_secs(0))).expect("set_linger call failed"); + /// ``` + #[unstable(feature = "tcp_linger", issue = "88494")] + pub fn set_linger(&self, linger: Option) -> io::Result<()> { + self.0.set_linger(linger) + } + + /// Gets the value of the `SO_LINGER` option on this socket. + /// + /// For more information about this option, see [`TcpStream::set_linger`]. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(tcp_linger)] + /// + /// use std::net::TcpStream; + /// use std::time::Duration; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_linger(Some(Duration::from_secs(0))).expect("set_linger call failed"); + /// assert_eq!(stream.linger().unwrap(), Some(Duration::from_secs(0))); + /// ``` + #[unstable(feature = "tcp_linger", issue = "88494")] + pub fn linger(&self) -> io::Result> { + self.0.linger() + } + /// Sets the value of the `TCP_NODELAY` option on this socket. /// /// If set, this option disables the Nagle algorithm. This means that diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index 387a3617e5e9a..c2061c1351262 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -767,6 +767,21 @@ fn test_timeout_zero_duration() { drop(listener); } +#[test] +#[cfg_attr(target_env = "sgx", ignore)] +fn linger() { + let addr = next_test_ip4(); + let _listener = t!(TcpListener::bind(&addr)); + + let stream = t!(TcpStream::connect(&("localhost", addr.port()))); + + assert_eq!(None, t!(stream.linger())); + t!(stream.set_linger(Some(Duration::from_secs(1)))); + assert_eq!(Some(Duration::from_secs(1)), t!(stream.linger())); + t!(stream.set_linger(None)); + assert_eq!(None, t!(stream.linger())); +} + #[test] #[cfg_attr(target_env = "sgx", ignore)] fn nodelay() { diff --git a/library/std/src/sys/hermit/net.rs b/library/std/src/sys/hermit/net.rs index 3f0c99cf74289..880ef678a4f7a 100644 --- a/library/std/src/sys/hermit/net.rs +++ b/library/std/src/sys/hermit/net.rs @@ -182,6 +182,14 @@ impl TcpStream { Ok(self.clone()) } + pub fn set_linger(&self, linger: Option) -> io::Result<()> { + unsupported() + } + + pub fn linger(&self) -> io::Result> { + unsupported() + } + pub fn set_nodelay(&self, mode: bool) -> io::Result<()> { abi::tcpstream::set_nodelay(*self.0.as_inner(), mode) .map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"set_nodelay failed")) diff --git a/library/std/src/sys/sgx/net.rs b/library/std/src/sys/sgx/net.rs index 3a69aa039ef2e..89c5af6124f20 100644 --- a/library/std/src/sys/sgx/net.rs +++ b/library/std/src/sys/sgx/net.rs @@ -183,6 +183,14 @@ impl TcpStream { Ok(self.clone()) } + pub fn set_linger(&self, _: Option) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn linger(&self) -> io::Result> { + sgx_ineffective(None) + } + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { sgx_ineffective(()) } diff --git a/library/std/src/sys/unix/l4re.rs b/library/std/src/sys/unix/l4re.rs index 3cf637c82285a..ba63b41534c1a 100644 --- a/library/std/src/sys/unix/l4re.rs +++ b/library/std/src/sys/unix/l4re.rs @@ -98,6 +98,14 @@ pub mod net { unimpl!(); } + pub fn set_linger(&self, _: Option) -> io::Result<()> { + unimpl!(); + } + + pub fn linger(&self) -> io::Result> { + unimpl!(); + } + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { unimpl!(); } @@ -214,6 +222,14 @@ pub mod net { unimpl!(); } + pub fn set_linger(&self, _: Option) -> io::Result<()> { + unimpl!(); + } + + pub fn linger(&self) -> io::Result> { + unimpl!(); + } + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { unimpl!(); } diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index c2f5da1dbbb11..d2e8c43a66595 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -12,6 +12,14 @@ use crate::time::{Duration, Instant}; use libc::{c_int, c_void, size_t, sockaddr, socklen_t, MSG_PEEK}; +cfg_if::cfg_if! { + if #[cfg(target_vendor = "apple")] { + use libc::SO_LINGER_SEC as SO_LINGER; + } else { + use libc::SO_LINGER; + } +} + pub use crate::sys::{cvt, cvt_r}; #[allow(unused_extern_crates)] @@ -376,6 +384,21 @@ impl Socket { Ok(()) } + pub fn set_linger(&self, linger: Option) -> io::Result<()> { + let linger = libc::linger { + l_onoff: linger.is_some() as libc::c_int, + l_linger: linger.map(|dur| dur.as_secs() as libc::c_int).unwrap_or_default(), + }; + + setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger) + } + + pub fn linger(&self) -> io::Result> { + let val: libc::linger = getsockopt(self, libc::SOL_SOCKET, SO_LINGER)?; + + Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) + } + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) } diff --git a/library/std/src/sys/unsupported/net.rs b/library/std/src/sys/unsupported/net.rs index 96203c74b576c..dbb6ce22c22de 100644 --- a/library/std/src/sys/unsupported/net.rs +++ b/library/std/src/sys/unsupported/net.rs @@ -76,6 +76,14 @@ impl TcpStream { self.0 } + pub fn set_linger(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn linger(&self) -> io::Result> { + self.0 + } + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { self.0 } diff --git a/library/std/src/sys/wasi/net.rs b/library/std/src/sys/wasi/net.rs index c7c4a9f6efdfb..a4dbb225376ee 100644 --- a/library/std/src/sys/wasi/net.rs +++ b/library/std/src/sys/wasi/net.rs @@ -127,6 +127,14 @@ impl TcpStream { unsupported() } + pub fn set_linger(&self, _: Option) -> io::Result<()> { + unsupported() + } + + pub fn linger(&self) -> io::Result> { + unsupported() + } + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { unsupported() } diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index 63f9be7b7e350..cedf389fbf503 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -197,6 +197,7 @@ pub const SOCK_DGRAM: c_int = 2; pub const SOCK_STREAM: c_int = 1; pub const SOCKET_ERROR: c_int = -1; pub const SOL_SOCKET: c_int = 0xffff; +pub const SO_LINGER: c_int = 0x0080; pub const SO_RCVTIMEO: c_int = 0x1006; pub const SO_SNDTIMEO: c_int = 0x1005; pub const IPPROTO_IP: c_int = 0; @@ -216,6 +217,13 @@ pub const IPV6_ADD_MEMBERSHIP: c_int = 12; pub const IPV6_DROP_MEMBERSHIP: c_int = 13; pub const MSG_PEEK: c_int = 0x2; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct linger { + pub l_onoff: c_ushort, + pub l_linger: c_ushort, +} + #[repr(C)] pub struct ip_mreq { pub imr_multiaddr: in_addr, diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs index 55aacb38c6f78..1ad4f0c70a3c6 100644 --- a/library/std/src/sys/windows/net.rs +++ b/library/std/src/sys/windows/net.rs @@ -15,7 +15,7 @@ use crate::sys_common::net; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; -use libc::{c_int, c_long, c_ulong}; +use libc::{c_int, c_long, c_ulong, c_ushort}; pub type wrlen_t = i32; @@ -446,6 +446,21 @@ impl Socket { cvt(result).map(drop) } + pub fn set_linger(&self, linger: Option) -> io::Result<()> { + let linger = c::linger { + l_onoff: linger.is_some() as c_ushort, + l_linger: linger.map(|dur| dur.as_secs() as c_ushort).unwrap_or_default(), + }; + + net::setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger) + } + + pub fn linger(&self) -> io::Result> { + let val: c::linger = net::getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?; + + Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) + } + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { net::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BYTE) } diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index 0ffa5c01dd33b..c5c3df361f34b 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -297,6 +297,14 @@ impl TcpStream { self.inner.duplicate().map(|s| TcpStream { inner: s }) } + pub fn set_linger(&self, linger: Option) -> io::Result<()> { + self.inner.set_linger(linger) + } + + pub fn linger(&self) -> io::Result> { + self.inner.linger() + } + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { self.inner.set_nodelay(nodelay) }