Repository: tokio-rs/mio
Pull request #1368 (2020-10-14 08:42:29)

Drop net2 dev-dependency
Now that we have out own TcpSocket builder inside of mio, just add a set_linger method to it and switch away from net2.

This doesn't bring socket2 in and hopefully will stop all the recurring "please switch from net2 to socket2" reports

Keruspe: Oops, there were some stale commits in my local clone, dropped those.
Keruspe: Clippy failure is unrelated (and probably requires updating the MSRV to "fix")
Thomasdezeeuw: Mio v0.7 doesn't have net2 as dependency, only as dev-dependency for https://github.com/tokio-rs/mio/blob/a07d38bf61ee7ce54c36cbbab357881e073eae6f/tests/tcp.rs#L464-L545. See https://github.com/tokio-rs/mio/blob/a5381b3e477ece2ef0db57adafad145746bd33cb/Cargo.toml#L50.

The problem with all the issues for net2 is Tokio 0.2, which depends on Mio v0.6. But since Mio v0.6 is in maintenance mode I'm not going to replace a foundational crate for it at this stage. Hopefully Tokio 0.3, which depends on Mio v0.7, should be released soon.
Thomasdezeeuw: So this is really a pr to add `TcpStream::set_linger`?
Keruspe: @Thomasdezeeuw It just adds TcpSocket::set_linger and replaces the use of net2 in tests by TcpSocket now that we have it, yep.

I know net2 is only a dev-dependency in mio 0.7, but if we even remove the dev-dependency, requests to add socket2 instead might stop, since it won't be listed at all in master?
Keruspe: And once tokio 0.3 is out, it will definitely stop, yep :+1: 
Thomasdezeeuw: > @Thomasdezeeuw It just adds TcpSocket::set_linger and replaces the use of net2 in tests by TcpSocket now that we have it, yep.
> 
> I know net2 is only a dev-dependency in mio 0.7, but if we even remove the dev-dependency, requests to add socket2 instead might stop, since it won't be listed at all in master?

Let's hope that is true :)
Keruspe: Fixed the nits, and fixed them in set_reuseaddr too while at it

Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 5206a44..1aac35a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -47,7 +47,6 @@ ntapi  = "0.3"
 
 [dev-dependencies]
 env_logger = { version = "0.6.2", default-features = false }
-net2       = "0.2.33"
 rand = "0.4"
 
 [package.metadata.docs.rs]
diff --git a/src/net/tcp/socket.rs b/src/net/tcp/socket.rs
index 0094de9..a91f665 100644
--- a/src/net/tcp/socket.rs
+++ b/src/net/tcp/socket.rs
@@ -4,6 +4,7 @@ use crate::sys;
 use std::io;
 use std::mem;
 use std::net::SocketAddr;
+use std::time::Duration;
 #[cfg(unix)]
 use std::os::unix::io::{AsRawFd, RawFd, FromRawFd};
 #[cfg(windows)]
@@ -80,6 +81,11 @@ impl TcpSocket {
     pub fn set_reuseaddr(&self, reuseaddr: bool) -> io::Result<()> {
         sys::tcp::set_reuseaddr(self.sys, reuseaddr)
     }
+
+    /// Sets the value of `SO_LINGER` on this socket.
+    pub fn set_linger(&self, dur: Option<Duration>) -> io::Result<()> {
+        sys::tcp::set_linger(self.sys, dur)
+    }
 }
 
 impl Drop for TcpSocket {
diff --git a/src/sys/shell/tcp.rs b/src/sys/shell/tcp.rs
index 963fccb..de1520b 100644
--- a/src/sys/shell/tcp.rs
+++ b/src/sys/shell/tcp.rs
@@ -1,5 +1,6 @@
 use std::io;
 use std::net::{self, SocketAddr};
+use std::time::Duration;
 
 pub(crate) type TcpSocket = i32;
 
@@ -31,6 +32,10 @@ pub(crate) fn set_reuseaddr(_: TcpSocket, _: bool) -> io::Result<()> {
     os_required!();
 }
 
+pub(crate) fn set_linger(_: TcpSocket, _: Option<Duration>) -> io::Result<()> {
+    os_required!();
+}
+
 pub fn accept(_: &net::TcpListener) -> io::Result<(net::TcpStream, SocketAddr)> {
     os_required!();
 }
diff --git a/src/sys/unix/tcp.rs b/src/sys/unix/tcp.rs
index 9d59632..81b371b 100644
--- a/src/sys/unix/tcp.rs
+++ b/src/sys/unix/tcp.rs
@@ -1,6 +1,7 @@
 use std::io;
 use std::mem::{size_of, MaybeUninit};
 use std::net::{self, SocketAddr};
+use std::time::Duration;
 use std::os::unix::io::{AsRawFd, FromRawFd};
 
 use crate::sys::unix::net::{new_socket, socket_addr, to_socket_addr};
@@ -54,8 +55,21 @@ pub(crate) fn set_reuseaddr(socket: TcpSocket, reuseaddr: bool) -> io::Result<()
         libc::SO_REUSEADDR,
         &val as *const libc::c_int as *const libc::c_void,
         size_of::<libc::c_int>() as libc::socklen_t,
-    ))?;
-    Ok(())
+    )).map(|_| ())
+}
+
+pub(crate) fn set_linger(socket: TcpSocket, dur: Option<Duration>) -> io::Result<()> {
+    let val: libc::linger = libc::linger {
+        l_onoff: if dur.is_some() { 1 } else { 0 },
+        l_linger: dur.map(|dur| dur.as_secs() as libc::c_int).unwrap_or_default(),
+    };
+    syscall!(setsockopt(
+        socket,
+        libc::SOL_SOCKET,
+        libc::SO_LINGER,
+        &val as *const libc::linger as *const libc::c_void,
+        size_of::<libc::linger>() as libc::socklen_t,
+    )).map(|_| ())
 }
 
 pub fn accept(listener: &net::TcpListener) -> io::Result<(net::TcpStream, SocketAddr)> {
diff --git a/src/sys/unix/uds/socketaddr.rs b/src/sys/unix/uds/socketaddr.rs
index 69f311a..ddfa2f0 100644
--- a/src/sys/unix/uds/socketaddr.rs
+++ b/src/sys/unix/uds/socketaddr.rs
@@ -74,6 +74,8 @@ cfg_os_poll! {
         /// Documentation reflected in [`SocketAddr`]
         ///
         /// [`SocketAddr`]: std::os::unix::net::SocketAddr
+        // FIXME: The matches macro requires rust 1.42.0 and we still support 1.39.0
+        #[allow(clippy::match_like_matches_macro)]
         pub fn is_unnamed(&self) -> bool {
             if let AddressKind::Unnamed = self.address() {
                 true
diff --git a/src/sys/windows/tcp.rs b/src/sys/windows/tcp.rs
index c5b0410..46ac1ac 100644
--- a/src/sys/windows/tcp.rs
+++ b/src/sys/windows/tcp.rs
@@ -1,11 +1,15 @@
 use std::io;
+use std::mem::size_of;
 use std::net::{self, SocketAddr};
+use std::time::Duration;
 use std::os::windows::io::FromRawSocket;
 use std::os::windows::raw::SOCKET as StdSocket; // winapi uses usize, stdlib uses u32/u64.
 
+use winapi::ctypes::{c_char, c_int, c_ushort};
+use winapi::shared::minwindef::{BOOL, TRUE, FALSE};
 use winapi::um::winsock2::{
-    self,
-    closesocket, PF_INET, PF_INET6, SOCKET, SOCKET_ERROR, SOCK_STREAM,
+    self, closesocket, linger, setsockopt, PF_INET, PF_INET6, SOCKET, SOCKET_ERROR,
+    SOCK_STREAM, SOL_SOCKET, SO_LINGER, SO_REUSEADDR,
 };
 
 use crate::sys::windows::net::{init, new_socket, socket_addr};
@@ -69,11 +73,6 @@ pub(crate) fn close(socket: TcpSocket) {
 }
 
 pub(crate) fn set_reuseaddr(socket: TcpSocket, reuseaddr: bool) -> io::Result<()> {
-    use winapi::ctypes::{c_char, c_int};
-    use winapi::shared::minwindef::{BOOL, TRUE, FALSE};
-    use winapi::um::winsock2::{setsockopt, SOCKET_ERROR, SO_REUSEADDR, SOL_SOCKET};
-    use std::mem::size_of;
-
     let val: BOOL = if reuseaddr { TRUE } else { FALSE };
 
     match unsafe { setsockopt(
@@ -83,9 +82,26 @@ pub(crate) fn set_reuseaddr(socket: TcpSocket, reuseaddr: bool) -> io::Result<()
         &val as *const _ as *const c_char,
         size_of::<BOOL>() as c_int,
     ) } {
-        0 => Ok(()),
         SOCKET_ERROR => Err(io::Error::last_os_error()),
-        _ => panic!("unexpected return value"),
+        _ => Ok(()),
+    }
+}
+
+pub(crate) fn set_linger(socket: TcpSocket, dur: Option<Duration>) -> io::Result<()> {
+    let val: linger = linger {
+        l_onoff: if dur.is_some() { 1 } else { 0 },
+        l_linger: dur.map(|dur| dur.as_secs() as c_ushort).unwrap_or_default(),
+    };
+
+    match unsafe { setsockopt(
+        socket,
+        SOL_SOCKET,
+        SO_LINGER,
+        &val as *const _ as *const c_char,
+        size_of::<linger>() as c_int,
+    ) } {
+        SOCKET_ERROR => Err(io::Error::last_os_error()),
+        _ => Ok(()),
     }
 }
 


Test patch:
diff --git a/tests/tcp.rs b/tests/tcp.rs
index a83f6d7..9691a1a 100644
--- a/tests/tcp.rs
+++ b/tests/tcp.rs
@@ -1,13 +1,9 @@
 #![cfg(all(feature = "os-poll", feature = "tcp"))]
 
-use mio::net::{TcpListener, TcpStream};
+use mio::net::{TcpListener, TcpSocket, TcpStream};
 use mio::{Events, Interest, Poll, Token};
-#[cfg(unix)]
-use net2::TcpStreamExt;
 use std::io::{self, Read, Write};
 use std::net::{self, Shutdown};
-#[cfg(unix)]
-use std::os::unix::io::{FromRawFd, IntoRawFd};
 use std::sync::mpsc::channel;
 use std::thread::{self, sleep};
 use std::time::Duration;
@@ -459,7 +455,6 @@ fn multiple_writes_immediate_success() {
     handle.join().unwrap();
 }
 
-#[cfg(unix)]
 #[test]
 fn connection_reset_by_peer() {
     init();
@@ -473,14 +468,9 @@ fn connection_reset_by_peer() {
     let addr = listener.local_addr().unwrap();
 
     // Connect client
-    let client = net2::TcpBuilder::new_v4().unwrap().to_tcp_stream().unwrap();
-
+    let client = TcpSocket::new_v4().unwrap();
     client.set_linger(Some(Duration::from_millis(0))).unwrap();
-    client.connect(&addr).unwrap();
-
-    // Convert to Mio stream
-    // FIXME: how to convert the stream on Windows?
-    let mut client = unsafe { TcpStream::from_raw_fd(client.into_raw_fd()) };
+    let mut client = client.connect(addr).unwrap();
 
     // Register server
     poll.registry()