Skip to content

Commit

Permalink
Rollup merge of #88495 - ibraheemdev:tcp-linger, r=joshtriplett
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
m-ou-se authored Aug 31, 2021
2 parents 41249ca + 072e8c9 commit c5a34d8
Show file tree
Hide file tree
Showing 11 changed files with 165 additions and 1 deletion.
47 changes: 47 additions & 0 deletions library/std/src/net/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 15 additions & 0 deletions library/std/src/net/tcp/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
8 changes: 8 additions & 0 deletions library/std/src/sys/hermit/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand Down
8 changes: 8 additions & 0 deletions library/std/src/sys/sgx/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
Expand Down
16 changes: 16 additions & 0 deletions library/std/src/sys/unix/l4re.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!();
}
Expand Down Expand Up @@ -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!();
}
Expand Down
23 changes: 23 additions & 0 deletions library/std/src/sys/unix/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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)
}
Expand Down
8 changes: 8 additions & 0 deletions library/std/src/sys/unsupported/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
8 changes: 8 additions & 0 deletions library/std/src/sys/wasi/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
8 changes: 8 additions & 0 deletions library/std/src/sys/windows/c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand Down
17 changes: 16 additions & 1 deletion library/std/src/sys/windows/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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)
}
Expand Down
8 changes: 8 additions & 0 deletions library/std/src/sys_common/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down

0 comments on commit c5a34d8

Please sign in to comment.