Skip to content

Commit

Permalink
os::net: expanding TcpStreamExt for Linux with tcp_deferaccept.
Browse files Browse the repository at this point in the history
allows for socket to process only when there is data to process,
the option sets a number of seconds until the data is ready.
  • Loading branch information
devnexen committed Jan 5, 2024
1 parent 11035f9 commit e2c4ab4
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 0 deletions.
51 changes: 51 additions & 0 deletions library/std/src/os/net/linux_ext/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,49 @@ pub trait TcpStreamExt: Sealed {
/// ```
#[unstable(feature = "tcp_quickack", issue = "96256")]
fn quickack(&self) -> io::Result<bool>;

/// 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 = "none")]
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 = "none")]
fn deferaccept(&self) -> io::Result<u32>;
}

#[unstable(feature = "tcp_quickack", issue = "96256")]
Expand All @@ -67,4 +110,12 @@ impl TcpStreamExt for net::TcpStream {
fn quickack(&self) -> io::Result<bool> {
self.as_inner().as_inner().quickack()
}

fn set_deferaccept(&self, accept: u32) -> io::Result<()> {
self.as_inner().as_inner().set_deferaccept(accept)
}

fn deferaccept(&self) -> io::Result<u32> {
self.as_inner().as_inner().deferaccept()
}
}
25 changes: 25 additions & 0 deletions library/std/src/os/net/linux_ext/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,28 @@ fn quickack() {
t!(stream.set_quickack(false));
assert_eq!(false, t!(stream.quickack()));
}

#[test]
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);
}
12 changes: 12 additions & 0 deletions library/std/src/sys/unix/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,18 @@ impl Socket {
Ok(raw != 0)
}

// bionic libc makes not 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<u32> {
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)
Expand Down

0 comments on commit e2c4ab4

Please sign in to comment.