From 85bf4439e614f2971523e380b69bb3be4c995894 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 5 Jan 2024 21:22:42 +0000 Subject: [PATCH] os::net: expanding TcpStreamExt for Linux with `tcp_deferaccept`. allows for socket to process only when there is data to process, the option sets a number of seconds until the data is ready. --- library/std/src/os/net/linux_ext/tcp.rs | 55 +++++++++++++++++++++++ library/std/src/os/net/linux_ext/tests.rs | 26 +++++++++++ library/std/src/sys/pal/unix/net.rs | 12 +++++ 3 files changed, 93 insertions(+) diff --git a/library/std/src/os/net/linux_ext/tcp.rs b/library/std/src/os/net/linux_ext/tcp.rs index 5e9ee65a4152e..ff29afe7ed311 100644 --- a/library/std/src/os/net/linux_ext/tcp.rs +++ b/library/std/src/os/net/linux_ext/tcp.rs @@ -53,6 +53,51 @@ pub trait TcpStreamExt: Sealed { /// ``` #[unstable(feature = "tcp_quickack", issue = "96256")] fn quickack(&self) -> io::Result; + + /// A socket listener will be awakened solely when data arrives. + /// + /// The `accept` argument set the delay in seconds until the + /// data is available to read, reducing the number of short lived + /// connections without data to process. + /// Contrary to other platforms `SO_ACCEPTFILTER` feature equivalent, there is + /// no necessity to set it after the `listen` call. + /// + /// See [`man 7 tcp`](https://man7.org/linux/man-pages/man7/tcp.7.html) + /// + /// # Examples + /// + /// ```no run + /// #![feature(tcp_deferaccept)] + /// use std::net::TcpStream; + /// use std::os::linux::net::TcpStreamExt; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_deferaccept(1).expect("set_deferaccept call failed"); + /// ``` + #[unstable(feature = "tcp_deferaccept", issue = "119639")] + #[cfg(target_os = "linux")] + fn set_deferaccept(&self, accept: u32) -> io::Result<()>; + + /// Gets the accept delay value (in seconds) of the `TCP_DEFER_ACCEPT` option. + /// + /// For more information about this option, see [`TcpStreamExt::set_deferaccept`]. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(tcp_deferaccept)] + /// use std::net::TcpStream; + /// use std::os::linux::net::TcpStreamExt; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_deferaccept(1).expect("set_deferaccept call failed"); + /// assert_eq!(stream.deferaccept().unwrap_or(0), 1); + /// ``` + #[unstable(feature = "tcp_deferaccept", issue = "119639")] + #[cfg(target_os = "linux")] + fn deferaccept(&self) -> io::Result; } #[unstable(feature = "tcp_quickack", issue = "96256")] @@ -67,4 +112,14 @@ impl TcpStreamExt for net::TcpStream { fn quickack(&self) -> io::Result { self.as_inner().as_inner().quickack() } + + #[cfg(target_os = "linux")] + fn set_deferaccept(&self, accept: u32) -> io::Result<()> { + self.as_inner().as_inner().set_deferaccept(accept) + } + + #[cfg(target_os = "linux")] + fn deferaccept(&self) -> io::Result { + self.as_inner().as_inner().deferaccept() + } } diff --git a/library/std/src/os/net/linux_ext/tests.rs b/library/std/src/os/net/linux_ext/tests.rs index 2db4deed03630..f8dbbfc18e28e 100644 --- a/library/std/src/os/net/linux_ext/tests.rs +++ b/library/std/src/os/net/linux_ext/tests.rs @@ -26,3 +26,29 @@ fn quickack() { t!(stream.set_quickack(false)); assert_eq!(false, t!(stream.quickack())); } + +#[test] +#[cfg(target_os = "linux")] +fn deferaccept() { + use crate::{ + net::{test::next_test_ip4, TcpListener, TcpStream}, + os::net::linux_ext::tcp::TcpStreamExt, + }; + + macro_rules! t { + ($e:expr) => { + match $e { + Ok(t) => t, + Err(e) => panic!("received error for `{}`: {}", stringify!($e), e), + } + }; + } + + let addr = next_test_ip4(); + let _listener = t!(TcpListener::bind(&addr)); + let stream = t!(TcpStream::connect(&("localhost", addr.port()))); + stream.set_deferaccept(1).expect("set_deferaccept failed"); + assert_eq!(stream.deferaccept().unwrap(), 1); + stream.set_deferaccept(0).expect("set_deferaccept failed"); + assert_eq!(stream.deferaccept().unwrap(), 0); +} diff --git a/library/std/src/sys/pal/unix/net.rs b/library/std/src/sys/pal/unix/net.rs index 1b6a6bb2c5c77..54b74d3f35286 100644 --- a/library/std/src/sys/pal/unix/net.rs +++ b/library/std/src/sys/pal/unix/net.rs @@ -441,6 +441,18 @@ impl Socket { Ok(raw != 0) } + // bionic libc makes no use of this flag + #[cfg(target_os = "linux")] + pub fn set_deferaccept(&self, accept: u32) -> io::Result<()> { + setsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT, accept as c_int) + } + + #[cfg(target_os = "linux")] + pub fn deferaccept(&self) -> io::Result { + let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT)?; + Ok(raw as u32) + } + #[cfg(any(target_os = "android", target_os = "linux",))] pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { setsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED, passcred as libc::c_int)