use http::Request;
use hyper::body::Incoming;
use socket2::SockRef;
use std::{
fmt,
future::{Future, Ready},
io,
net::SocketAddr,
time::Duration,
};
use tokio::net::TcpStream;
use tower_service::Service;
pub trait Accept<I, S> {
type Stream;
type Service;
type Future: Future<Output = io::Result<(Self::Stream, Self::Service)>>;
fn accept(&self, stream: I, service: S) -> Self::Future;
}
#[derive(Clone, Copy, Debug, Default)]
pub struct DefaultAcceptor;
impl DefaultAcceptor {
pub fn new() -> Self {
Self
}
}
impl<I, S> Accept<I, S> for DefaultAcceptor {
type Stream = I;
type Service = S;
type Future = Ready<io::Result<(Self::Stream, Self::Service)>>;
fn accept(&self, stream: I, service: S) -> Self::Future {
std::future::ready(Ok((stream, service)))
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct NoDelayAcceptor;
impl NoDelayAcceptor {
pub fn new() -> Self {
Self
}
}
impl<S> Accept<TcpStream, S> for NoDelayAcceptor {
type Stream = TcpStream;
type Service = S;
type Future = Ready<io::Result<(Self::Stream, Self::Service)>>;
fn accept(&self, stream: TcpStream, service: S) -> Self::Future {
std::future::ready(stream.set_nodelay(true).and(Ok((stream, service))))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct LocalAddr(pub SocketAddr);
#[derive(Clone, Copy, Debug, Default)]
pub struct LocalAddrAcceptor;
impl LocalAddrAcceptor {
pub fn new() -> Self {
Self
}
}
impl<S> Accept<TcpStream, S> for LocalAddrAcceptor {
type Stream = TcpStream;
type Service = LocalAddrService<S>;
type Future = Ready<io::Result<(Self::Stream, Self::Service)>>;
fn accept(&self, stream: TcpStream, service: S) -> Self::Future {
std::future::ready(stream.local_addr().map(|local_addr| {
let service = LocalAddrService {
inner: service,
local_addr: LocalAddr(local_addr),
};
(stream, service)
}))
}
}
#[derive(Clone)]
pub struct LocalAddrService<S> {
inner: S,
local_addr: LocalAddr,
}
impl<S> fmt::Debug for LocalAddrService<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LocalAddrService")
.field("local_addr", &self.local_addr)
.finish_non_exhaustive()
}
}
impl<S> Service<Request<Incoming>> for LocalAddrService<S>
where
S: Service<Request<Incoming>>,
{
type Response = S::Response;
type Error = S::Error;
type Future = S::Future;
fn poll_ready(
&mut self,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, mut req: Request<Incoming>) -> Self::Future {
req.extensions_mut().insert(self.local_addr);
self.inner.call(req)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[allow(dead_code)]
pub struct TcpKeepaliveConfig {
time: Option<Duration>,
interval: Option<Duration>,
retries: Option<u32>,
}
impl TcpKeepaliveConfig {
pub fn new() -> Self {
Self::default()
}
pub fn time(mut self, time: Duration) -> Self {
self.time = Some(time);
self
}
pub fn interval(mut self, interval: Duration) -> Self {
self.interval = Some(interval);
self
}
pub fn retries(mut self, retries: u32) -> Self {
self.retries = Some(retries);
self
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct TcpSocketConfig {
nodelay: bool,
keepalive: Option<TcpKeepaliveConfig>,
}
impl TcpSocketConfig {
pub fn new() -> Self {
Self::default()
}
pub fn nodelay(mut self, nodelay: bool) -> Self {
self.nodelay = nodelay;
self
}
pub fn keepalive(mut self, keepalive: TcpKeepaliveConfig) -> Self {
self.keepalive = Some(keepalive);
self
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct TcpConfigAcceptor {
config: TcpSocketConfig,
}
impl TcpConfigAcceptor {
pub fn new(config: TcpSocketConfig) -> Self {
Self { config }
}
}
impl<S> Accept<TcpStream, S> for TcpConfigAcceptor {
type Stream = TcpStream;
type Service = S;
type Future = Ready<io::Result<(Self::Stream, Self::Service)>>;
fn accept(&self, stream: TcpStream, service: S) -> Self::Future {
std::future::ready(
(|| {
if self.config.nodelay {
stream.set_nodelay(true)?;
}
if let Some(config) = self.config.keepalive {
let mut keepalive = socket2::TcpKeepalive::new();
if let Some(time) = config.time {
keepalive = keepalive.with_time(time);
}
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "ios",
target_os = "visionos",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "tvos",
target_os = "watchos",
target_os = "windows",
target_os = "cygwin",
))]
if let Some(interval) = config.interval {
keepalive = keepalive.with_interval(interval);
}
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "ios",
target_os = "visionos",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "tvos",
target_os = "watchos",
target_os = "windows",
target_os = "cygwin",
))]
if let Some(retries) = config.retries {
keepalive = keepalive.with_retries(retries);
}
SockRef::from(&stream).set_tcp_keepalive(&keepalive)?;
}
Ok(())
})()
.map(|()| (stream, service)),
)
}
}
#[cfg(test)]
mod tests {
use super::{Accept, TcpConfigAcceptor, TcpKeepaliveConfig, TcpSocketConfig};
use std::time::Duration;
use tokio::net::{TcpListener, TcpStream};
#[tokio::test]
async fn applies_nodelay_and_keepalive() {
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
let (client, server) = tokio::join!(TcpStream::connect(addr), listener.accept());
let _client = client.unwrap();
let (server, _) = server.unwrap();
let acceptor = TcpConfigAcceptor::new(
TcpSocketConfig::new()
.nodelay(true)
.keepalive(TcpKeepaliveConfig::new().time(Duration::from_secs(30))),
);
let (stream, ()) = acceptor.accept(server, ()).await.unwrap();
assert!(stream.nodelay().unwrap());
#[cfg(target_os = "linux")]
{
use socket2::SockRef;
let socket = SockRef::from(&stream);
assert_eq!(
socket.tcp_keepalive_time().unwrap(),
Duration::from_secs(30)
);
}
}
}