Rollup merge of #88495 - ibraheemdev:tcp-linger, r=joshtriplett

Add `TcpStream::set_linger` and `TcpStream::linger`

Adds methods for getting/setting the `SO_LINGER` option on TCP sockets. Behavior is consistent across Unix and Windows.

r? `@joshtriplett` (I noticed you've been reviewing net related PRs)
This commit is contained in:
Mara Bos 2021-08-31 17:54:58 +02:00 committed by GitHub
commit c5a34d802d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 165 additions and 1 deletions

View file

@ -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<Duration>) -> 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<Option<Duration>> {
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

View file

@ -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() {

View file

@ -182,6 +182,14 @@ impl TcpStream {
Ok(self.clone())
}
pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
unsupported()
}
pub fn linger(&self) -> io::Result<Option<Duration>> {
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"))

View file

@ -183,6 +183,14 @@ impl TcpStream {
Ok(self.clone())
}
pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
sgx_ineffective(())
}
pub fn linger(&self) -> io::Result<Option<Duration>> {
sgx_ineffective(None)
}
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
sgx_ineffective(())
}

View file

@ -98,6 +98,14 @@ pub mod net {
unimpl!();
}
pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
unimpl!();
}
pub fn linger(&self) -> io::Result<Option<Duration>> {
unimpl!();
}
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
unimpl!();
}
@ -214,6 +222,14 @@ pub mod net {
unimpl!();
}
pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
unimpl!();
}
pub fn linger(&self) -> io::Result<Option<Duration>> {
unimpl!();
}
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
unimpl!();
}

View file

@ -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<Duration>) -> io::Result<()> {
let linger = libc::linger {
l_onoff: linger.is_some() as libc::c_int,
l_linger: linger.unwrap_or_default().as_secs() as libc::c_int,
};
setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger)
}
pub fn linger(&self) -> io::Result<Option<Duration>> {
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)
}

View file

@ -76,6 +76,14 @@ impl TcpStream {
self.0
}
pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
self.0
}
pub fn linger(&self) -> io::Result<Option<Duration>> {
self.0
}
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
self.0
}

View file

@ -127,6 +127,14 @@ impl TcpStream {
unsupported()
}
pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
unsupported()
}
pub fn linger(&self) -> io::Result<Option<Duration>> {
unsupported()
}
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
unsupported()
}

View file

@ -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,

View file

@ -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<Duration>) -> io::Result<()> {
let linger = c::linger {
l_onoff: linger.is_some() as c_ushort,
l_linger: linger.unwrap_or_default().as_secs() as c_ushort,
};
net::setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger)
}
pub fn linger(&self) -> io::Result<Option<Duration>> {
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)
}

View file

@ -297,6 +297,14 @@ impl TcpStream {
self.inner.duplicate().map(|s| TcpStream { inner: s })
}
pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
self.inner.set_linger(linger)
}
pub fn linger(&self) -> io::Result<Option<Duration>> {
self.inner.linger()
}
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
self.inner.set_nodelay(nodelay)
}