Skip to content

Commit c5a34d8

Browse files
authored
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)
2 parents 41249ca + 072e8c9 commit c5a34d8

File tree

11 files changed

+165
-1
lines changed

11 files changed

+165
-1
lines changed

library/std/src/net/tcp.rs

+47
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,53 @@ impl TcpStream {
401401
self.0.peek(buf)
402402
}
403403

404+
/// Sets the value of the `SO_LINGER` option on this socket.
405+
///
406+
/// This value controls how the socket is closed when data remains
407+
/// to be sent. If `SO_LINGER` is set, the socket will remain open
408+
/// for the specified duration as the system attempts to send pending data.
409+
/// Otherwise, the system may close the socket immediately, or wait for a
410+
/// default timeout.
411+
///
412+
/// # Examples
413+
///
414+
/// ```no_run
415+
/// #![feature(tcp_linger)]
416+
///
417+
/// use std::net::TcpStream;
418+
/// use std::time::Duration;
419+
///
420+
/// let stream = TcpStream::connect("127.0.0.1:8080")
421+
/// .expect("Couldn't connect to the server...");
422+
/// stream.set_linger(Some(Duration::from_secs(0))).expect("set_linger call failed");
423+
/// ```
424+
#[unstable(feature = "tcp_linger", issue = "88494")]
425+
pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
426+
self.0.set_linger(linger)
427+
}
428+
429+
/// Gets the value of the `SO_LINGER` option on this socket.
430+
///
431+
/// For more information about this option, see [`TcpStream::set_linger`].
432+
///
433+
/// # Examples
434+
///
435+
/// ```no_run
436+
/// #![feature(tcp_linger)]
437+
///
438+
/// use std::net::TcpStream;
439+
/// use std::time::Duration;
440+
///
441+
/// let stream = TcpStream::connect("127.0.0.1:8080")
442+
/// .expect("Couldn't connect to the server...");
443+
/// stream.set_linger(Some(Duration::from_secs(0))).expect("set_linger call failed");
444+
/// assert_eq!(stream.linger().unwrap(), Some(Duration::from_secs(0)));
445+
/// ```
446+
#[unstable(feature = "tcp_linger", issue = "88494")]
447+
pub fn linger(&self) -> io::Result<Option<Duration>> {
448+
self.0.linger()
449+
}
450+
404451
/// Sets the value of the `TCP_NODELAY` option on this socket.
405452
///
406453
/// If set, this option disables the Nagle algorithm. This means that

library/std/src/net/tcp/tests.rs

+15
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,21 @@ fn test_timeout_zero_duration() {
767767
drop(listener);
768768
}
769769

770+
#[test]
771+
#[cfg_attr(target_env = "sgx", ignore)]
772+
fn linger() {
773+
let addr = next_test_ip4();
774+
let _listener = t!(TcpListener::bind(&addr));
775+
776+
let stream = t!(TcpStream::connect(&("localhost", addr.port())));
777+
778+
assert_eq!(None, t!(stream.linger()));
779+
t!(stream.set_linger(Some(Duration::from_secs(1))));
780+
assert_eq!(Some(Duration::from_secs(1)), t!(stream.linger()));
781+
t!(stream.set_linger(None));
782+
assert_eq!(None, t!(stream.linger()));
783+
}
784+
770785
#[test]
771786
#[cfg_attr(target_env = "sgx", ignore)]
772787
fn nodelay() {

library/std/src/sys/hermit/net.rs

+8
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,14 @@ impl TcpStream {
182182
Ok(self.clone())
183183
}
184184

185+
pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
186+
unsupported()
187+
}
188+
189+
pub fn linger(&self) -> io::Result<Option<Duration>> {
190+
unsupported()
191+
}
192+
185193
pub fn set_nodelay(&self, mode: bool) -> io::Result<()> {
186194
abi::tcpstream::set_nodelay(*self.0.as_inner(), mode)
187195
.map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"set_nodelay failed"))

library/std/src/sys/sgx/net.rs

+8
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,14 @@ impl TcpStream {
183183
Ok(self.clone())
184184
}
185185

186+
pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
187+
sgx_ineffective(())
188+
}
189+
190+
pub fn linger(&self) -> io::Result<Option<Duration>> {
191+
sgx_ineffective(None)
192+
}
193+
186194
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
187195
sgx_ineffective(())
188196
}

library/std/src/sys/unix/l4re.rs

+16
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,14 @@ pub mod net {
9898
unimpl!();
9999
}
100100

101+
pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
102+
unimpl!();
103+
}
104+
105+
pub fn linger(&self) -> io::Result<Option<Duration>> {
106+
unimpl!();
107+
}
108+
101109
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
102110
unimpl!();
103111
}
@@ -214,6 +222,14 @@ pub mod net {
214222
unimpl!();
215223
}
216224

225+
pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
226+
unimpl!();
227+
}
228+
229+
pub fn linger(&self) -> io::Result<Option<Duration>> {
230+
unimpl!();
231+
}
232+
217233
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
218234
unimpl!();
219235
}

library/std/src/sys/unix/net.rs

+23
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ use crate::time::{Duration, Instant};
1212

1313
use libc::{c_int, c_void, size_t, sockaddr, socklen_t, MSG_PEEK};
1414

15+
cfg_if::cfg_if! {
16+
if #[cfg(target_vendor = "apple")] {
17+
use libc::SO_LINGER_SEC as SO_LINGER;
18+
} else {
19+
use libc::SO_LINGER;
20+
}
21+
}
22+
1523
pub use crate::sys::{cvt, cvt_r};
1624

1725
#[allow(unused_extern_crates)]
@@ -376,6 +384,21 @@ impl Socket {
376384
Ok(())
377385
}
378386

387+
pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
388+
let linger = libc::linger {
389+
l_onoff: linger.is_some() as libc::c_int,
390+
l_linger: linger.unwrap_or_default().as_secs() as libc::c_int,
391+
};
392+
393+
setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger)
394+
}
395+
396+
pub fn linger(&self) -> io::Result<Option<Duration>> {
397+
let val: libc::linger = getsockopt(self, libc::SOL_SOCKET, SO_LINGER)?;
398+
399+
Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64)))
400+
}
401+
379402
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
380403
setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int)
381404
}

library/std/src/sys/unsupported/net.rs

+8
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,14 @@ impl TcpStream {
7676
self.0
7777
}
7878

79+
pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
80+
self.0
81+
}
82+
83+
pub fn linger(&self) -> io::Result<Option<Duration>> {
84+
self.0
85+
}
86+
7987
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
8088
self.0
8189
}

library/std/src/sys/wasi/net.rs

+8
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,14 @@ impl TcpStream {
127127
unsupported()
128128
}
129129

130+
pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
131+
unsupported()
132+
}
133+
134+
pub fn linger(&self) -> io::Result<Option<Duration>> {
135+
unsupported()
136+
}
137+
130138
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
131139
unsupported()
132140
}

library/std/src/sys/windows/c.rs

+8
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ pub const SOCK_DGRAM: c_int = 2;
197197
pub const SOCK_STREAM: c_int = 1;
198198
pub const SOCKET_ERROR: c_int = -1;
199199
pub const SOL_SOCKET: c_int = 0xffff;
200+
pub const SO_LINGER: c_int = 0x0080;
200201
pub const SO_RCVTIMEO: c_int = 0x1006;
201202
pub const SO_SNDTIMEO: c_int = 0x1005;
202203
pub const IPPROTO_IP: c_int = 0;
@@ -216,6 +217,13 @@ pub const IPV6_ADD_MEMBERSHIP: c_int = 12;
216217
pub const IPV6_DROP_MEMBERSHIP: c_int = 13;
217218
pub const MSG_PEEK: c_int = 0x2;
218219

220+
#[repr(C)]
221+
#[derive(Copy, Clone)]
222+
pub struct linger {
223+
pub l_onoff: c_ushort,
224+
pub l_linger: c_ushort,
225+
}
226+
219227
#[repr(C)]
220228
pub struct ip_mreq {
221229
pub imr_multiaddr: in_addr,

library/std/src/sys/windows/net.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::sys_common::net;
1515
use crate::sys_common::{AsInner, FromInner, IntoInner};
1616
use crate::time::Duration;
1717

18-
use libc::{c_int, c_long, c_ulong};
18+
use libc::{c_int, c_long, c_ulong, c_ushort};
1919

2020
pub type wrlen_t = i32;
2121

@@ -446,6 +446,21 @@ impl Socket {
446446
cvt(result).map(drop)
447447
}
448448

449+
pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
450+
let linger = c::linger {
451+
l_onoff: linger.is_some() as c_ushort,
452+
l_linger: linger.unwrap_or_default().as_secs() as c_ushort,
453+
};
454+
455+
net::setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger)
456+
}
457+
458+
pub fn linger(&self) -> io::Result<Option<Duration>> {
459+
let val: c::linger = net::getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?;
460+
461+
Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64)))
462+
}
463+
449464
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
450465
net::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BYTE)
451466
}

library/std/src/sys_common/net.rs

+8
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,14 @@ impl TcpStream {
297297
self.inner.duplicate().map(|s| TcpStream { inner: s })
298298
}
299299

300+
pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
301+
self.inner.set_linger(linger)
302+
}
303+
304+
pub fn linger(&self) -> io::Result<Option<Duration>> {
305+
self.inner.linger()
306+
}
307+
300308
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
301309
self.inner.set_nodelay(nodelay)
302310
}

0 commit comments

Comments
 (0)