Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions tokio/src/net/tcp/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1170,6 +1170,71 @@ impl TcpStream {
self.io.set_nodelay(nodelay)
}

/// Gets the value of the `TCP_QUICKACK` option on this socket.
///
/// For more information about this option, see [`TcpStream::set_quickack`].
///
/// # Examples
///
/// ```no_run
/// use tokio::net::TcpStream;
///
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
/// let stream = TcpStream::connect("127.0.0.1:8080").await?;
///
/// stream.quickack()?;
/// # Ok(())
/// # }
/// ```
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "fuchsia",
target_os = "cygwin",
))]
#[cfg_attr(
docsrs,
doc(cfg(any(target_os = "linux", target_os = "android", target_os = "fuchsia")))
)]
pub fn quickack(&self) -> io::Result<bool> {
socket2::SockRef::from(self).tcp_quickack()
}

/// Enable or disable `TCP_QUICKACK`.
///
/// This flag causes Linux to eagerly send `ACK`s rather than delaying them.
/// Linux may reset this flag after further operations on the socket.
///
/// See [`man 7 tcp`](https://man7.org/linux/man-pages/man7/tcp.7.html) and
/// [TCP delayed acknowledgment](https://en.wikipedia.org/wiki/TCP_delayed_acknowledgment)
/// for more information.
///
/// # Examples
///
/// ```no_run
/// use tokio::net::TcpStream;
///
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
/// let stream = TcpStream::connect("127.0.0.1:8080").await?;
///
/// stream.set_quickack(true)?;
/// # Ok(())
/// # }
/// ```
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "fuchsia",
target_os = "cygwin",
))]
#[cfg_attr(
docsrs,
doc(cfg(any(target_os = "linux", target_os = "android", target_os = "fuchsia")))
)]
pub fn set_quickack(&self, quickack: bool) -> io::Result<()> {
socket2::SockRef::from(self).set_tcp_quickack(quickack)
}

cfg_not_wasi! {
/// Reads the linger duration for this socket by getting the `SO_LINGER`
/// option.
Expand Down
71 changes: 71 additions & 0 deletions tokio/tests/net_quickack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#![warn(rust_2018_idioms)]
#![cfg(all(
feature = "net",
any(
target_os = "linux",
target_os = "android",
target_os = "fuchsia",
target_os = "cygwin",
)
))]
#![cfg(not(miri))] // No `socket` in miri.

use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpListener;
use tokio::net::TcpStream;
use tokio::sync::oneshot;

#[tokio::test]
async fn socket_works_with_quickack() {
const MESSAGE: &str = "Hello, tokio!";

let (tx_port, rx_port) = oneshot::channel();

let server = tokio::spawn(async move {
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();

tx_port.send(addr.port()).unwrap();

let (mut stream, _) = listener.accept().await.unwrap();
stream.set_quickack(true).unwrap();
assert!(stream.quickack().unwrap());

stream.write_all(MESSAGE.as_bytes()).await.unwrap();

let mut buf = vec![0; MESSAGE.len()];
stream.read_exact(&mut buf).await.unwrap();
assert_eq!(buf, MESSAGE.as_bytes());

// There is nothing special about setting quickack to false
// at this point, we just want to test the `false` case.
stream.set_quickack(false).unwrap();
assert!(!stream.quickack().unwrap());

stream.shutdown().await.unwrap();
});

let port = rx_port.await.unwrap();
let client = tokio::spawn(async move {
let mut stream = TcpStream::connect(format!("127.0.0.1:{port}"))
.await
.unwrap();
stream.set_quickack(true).unwrap();
assert!(stream.quickack().unwrap());

let mut buf = vec![0; MESSAGE.len()];
stream.read_exact(&mut buf).await.unwrap();
assert_eq!(buf, MESSAGE.as_bytes());

stream.write_all(MESSAGE.as_bytes()).await.unwrap();

// There is nothing special about setting quickack to false
// at this point, we just want to test the `false` case.
stream.set_quickack(false).unwrap();
assert!(!stream.quickack().unwrap());

stream.shutdown().await.unwrap();
});

tokio::try_join!(server, client).unwrap();
}
Loading