From 3b6777f1ab7952c058d69be15805a06a8ce0f1da Mon Sep 17 00:00:00 2001
From: ibraheemdev <ibrah1440@gmail.com>
Date: Mon, 30 Aug 2021 13:02:15 -0400
Subject: [PATCH 1/3] 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<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
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<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"))
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<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(())
     }
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<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!();
         }
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<Duration>) -> 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<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)
     }
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<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
     }
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<Duration>) -> io::Result<()> {
+        unsupported()
+    }
+
+    pub fn linger(&self) -> io::Result<Option<Duration>> {
+        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<Duration>) -> 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<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)
     }
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<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)
     }

From dafc14794f9d71b3ccc5fde0aeb47cd6bb25e83e Mon Sep 17 00:00:00 2001
From: ibraheemdev <ibrah1440@gmail.com>
Date: Mon, 30 Aug 2021 14:00:21 -0400
Subject: [PATCH 2/3] clean up `c::linger` conversion

---
 library/std/src/sys/unix/net.rs    | 2 +-
 library/std/src/sys/windows/net.rs | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs
index d2e8c43a66595..9ae6d12dcb95c 100644
--- a/library/std/src/sys/unix/net.rs
+++ b/library/std/src/sys/unix/net.rs
@@ -387,7 +387,7 @@ impl Socket {
     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.map(|dur| dur.as_secs() as libc::c_int).unwrap_or_default(),
+            l_linger: linger.unwrap_or_default().as_secs() as libc::c_int,
         };
 
         setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger)
diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs
index 1ad4f0c70a3c6..33152cc97abc0 100644
--- a/library/std/src/sys/windows/net.rs
+++ b/library/std/src/sys/windows/net.rs
@@ -449,7 +449,7 @@ impl Socket {
     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.map(|dur| dur.as_secs() as c_ushort).unwrap_or_default(),
+            l_linger: linger.unwrap_or_default().as_secs() as c_ushort,
         };
 
         net::setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger)

From 072e8c977a8158b4def93bdfe23ab77427399f6f Mon Sep 17 00:00:00 2001
From: Ibraheem Ahmed <ibrah1440@gmail.com>
Date: Tue, 31 Aug 2021 11:19:39 -0400
Subject: [PATCH 3/3] disable `tcp_linger` feature in `std`

Co-authored-by: Mara Bos <m-ou.se@m-ou.se>
---
 library/std/src/lib.rs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index d5e7668e47ce4..028a066b5a130 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -321,7 +321,6 @@
 #![feature(stdsimd)]
 #![feature(stmt_expr_attributes)]
 #![feature(str_internals)]
-#![feature(tcp_linger)]
 #![feature(test)]
 #![feature(thread_local)]
 #![feature(thread_local_internals)]