use std::time::Duration;
use crate::error::NetError;
use crate::sys::{self, AsSock};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Keepalive {
pub idle: Duration,
pub interval: Duration,
pub count: u32,
}
impl Keepalive {
pub const DEFAULT: Keepalive = Keepalive {
idle: Duration::from_secs(60),
interval: Duration::from_secs(10),
count: 6,
};
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Linger {
Off,
Abortive,
Timeout(Duration),
}
impl Linger {
fn to_sys(self) -> Option<Duration> {
match self {
Linger::Off => None,
Linger::Abortive => Some(Duration::ZERO),
Linger::Timeout(d) => Some(d),
}
}
}
#[must_use]
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct TcpOptions {
pub nodelay: Option<bool>,
pub keepalive: Option<Keepalive>,
pub linger: Option<Linger>,
pub send_buffer: Option<u32>,
pub receive_buffer: Option<u32>,
pub user_timeout: Option<Duration>,
pub ttl: Option<u8>,
}
impl TcpOptions {
pub fn new() -> TcpOptions {
TcpOptions::default()
}
pub fn nodelay(mut self, on: bool) -> TcpOptions {
self.nodelay = Some(on);
self
}
pub fn keepalive(mut self, k: Keepalive) -> TcpOptions {
self.keepalive = Some(k);
self
}
pub fn linger(mut self, l: Linger) -> TcpOptions {
self.linger = Some(l);
self
}
pub fn send_buffer(mut self, bytes: u32) -> TcpOptions {
self.send_buffer = Some(bytes);
self
}
pub fn receive_buffer(mut self, bytes: u32) -> TcpOptions {
self.receive_buffer = Some(bytes);
self
}
pub fn user_timeout(mut self, timeout: Duration) -> TcpOptions {
self.user_timeout = Some(timeout);
self
}
pub fn ttl(mut self, hops: u8) -> TcpOptions {
self.ttl = Some(hops);
self
}
pub(crate) fn apply<S: AsSock>(&self, socket: &S, v6: bool) -> Result<(), NetError> {
let sock = socket.sock();
let os = |op: &'static str| move |e| NetError::from_os(op, e);
if let Some(on) = self.nodelay {
sys::set_nodelay(sock, on).map_err(os("TCP_NODELAY"))?;
}
if let Some(k) = self.keepalive {
check_keepalive(&k)?;
sys::set_keepalive(sock, Some((k.idle, k.interval, k.count)))
.map_err(os("keepalive"))?;
}
if let Some(l) = self.linger {
sys::set_linger(sock, l.to_sys()).map_err(os("SO_LINGER"))?;
}
if let Some(n) = self.send_buffer {
sys::set_send_buffer(sock, n).map_err(os("SO_SNDBUF"))?;
}
if let Some(n) = self.receive_buffer {
sys::set_receive_buffer(sock, n).map_err(os("SO_RCVBUF"))?;
}
if let Some(t) = self.user_timeout {
sys::set_user_timeout(sock, t).map_err(|e| {
if e.kind() == std::io::ErrorKind::Unsupported {
NetError::Unsupported {
feature: "user_timeout",
platform: std::env::consts::OS,
}
} else {
NetError::from_os("TCP_USER_TIMEOUT", e)
}
})?;
}
if let Some(h) = self.ttl {
sys::set_ttl(sock, v6, h).map_err(os("IP_TTL"))?;
}
Ok(())
}
pub(crate) fn is_empty(&self) -> bool {
*self == TcpOptions::default()
}
}
fn check_keepalive(k: &Keepalive) -> Result<(), NetError> {
if cfg!(windows) && k.count > 255 {
return Err(NetError::InvalidConfig {
field: "keepalive",
reason: crate::error::ConfigReason::TooLarge { max: 255 },
});
}
if k.idle.is_zero() || k.interval.is_zero() || k.count == 0 {
return Err(NetError::InvalidConfig {
field: "keepalive",
reason: crate::error::ConfigReason::Zero,
});
}
Ok(())
}
#[must_use]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ListenOptions {
pub(crate) backlog: u32,
pub(crate) reuse_address: bool,
pub(crate) reuse_port: bool,
pub(crate) ipv6_only: bool,
pub(crate) send_buffer: Option<u32>,
pub(crate) receive_buffer: Option<u32>,
pub(crate) defer_accept: Option<Duration>,
pub(crate) fast_open: Option<u32>,
pub(crate) incoming_cpu: Option<u32>,
pub(crate) per_worker: bool,
pub(crate) tcp: TcpOptions,
}
impl Default for ListenOptions {
fn default() -> ListenOptions {
ListenOptions {
backlog: 1024,
reuse_address: cfg!(unix),
reuse_port: false,
ipv6_only: true,
send_buffer: None,
receive_buffer: None,
defer_accept: None,
fast_open: None,
incoming_cpu: None,
per_worker: false,
tcp: TcpOptions::new().nodelay(true),
}
}
}
impl ListenOptions {
pub fn new() -> ListenOptions {
ListenOptions::default()
}
pub fn backlog(mut self, n: u32) -> ListenOptions {
self.backlog = n;
self
}
pub fn reuse_address(mut self, on: bool) -> ListenOptions {
self.reuse_address = on;
self
}
pub fn reuse_port(mut self, on: bool) -> ListenOptions {
self.reuse_port = on;
self
}
pub fn ipv6_only(mut self, on: bool) -> ListenOptions {
self.ipv6_only = on;
self
}
pub fn send_buffer(mut self, bytes: u32) -> ListenOptions {
self.send_buffer = Some(bytes);
self
}
pub fn receive_buffer(mut self, bytes: u32) -> ListenOptions {
self.receive_buffer = Some(bytes);
self
}
pub fn defer_accept(mut self, wait: Option<Duration>) -> ListenOptions {
self.defer_accept = wait;
self
}
pub fn fast_open(mut self, queue: Option<u32>) -> ListenOptions {
self.fast_open = queue;
self
}
pub fn incoming_cpu(mut self, cpu: Option<u32>) -> ListenOptions {
self.incoming_cpu = cpu;
self
}
pub fn per_worker(mut self, on: bool) -> ListenOptions {
self.per_worker = on;
self.reuse_port = self.reuse_port || on;
self
}
pub fn tcp_options(mut self, options: TcpOptions) -> ListenOptions {
self.tcp = options;
self
}
pub(crate) fn params(&self) -> sys::ListenParams {
sys::ListenParams {
reuse_address: self.reuse_address,
reuse_port: self.reuse_port,
ipv6_only: self.ipv6_only,
send_buffer: self.send_buffer,
receive_buffer: self.receive_buffer,
backlog: self.backlog,
defer_accept: self.defer_accept,
fast_open: self.fast_open,
incoming_cpu: self.incoming_cpu,
}
}
}
#[derive(Debug)]
pub struct SocketOptions<'a> {
pub(crate) sock: sys::Sock<'a>,
pub(crate) v6: bool,
}
fn os(op: &'static str) -> impl Fn(std::io::Error) -> NetError {
move |e| NetError::from_os(op, e)
}
impl SocketOptions<'_> {
pub fn nodelay(&self) -> Result<bool, NetError> {
sys::nodelay(self.sock).map_err(os("TCP_NODELAY"))
}
pub fn set_nodelay(&self, on: bool) -> Result<(), NetError> {
sys::set_nodelay(self.sock, on).map_err(os("TCP_NODELAY"))
}
pub fn keepalive(&self) -> Result<Option<Keepalive>, NetError> {
Ok(sys::keepalive(self.sock)
.map_err(os("keepalive"))?
.map(|(idle, interval, count)| Keepalive {
idle,
interval,
count,
}))
}
pub fn set_keepalive(&self, k: Option<Keepalive>) -> Result<(), NetError> {
if let Some(k) = &k {
check_keepalive(k)?;
}
sys::set_keepalive(self.sock, k.map(|k| (k.idle, k.interval, k.count)))
.map_err(os("keepalive"))
}
pub fn linger(&self) -> Result<Linger, NetError> {
Ok(match sys::linger(self.sock).map_err(os("SO_LINGER"))? {
None => Linger::Off,
Some(d) if d.is_zero() => Linger::Abortive,
Some(d) => Linger::Timeout(d),
})
}
pub fn set_linger(&self, l: Linger) -> Result<(), NetError> {
sys::set_linger(self.sock, l.to_sys()).map_err(os("SO_LINGER"))
}
pub fn send_buffer(&self) -> Result<u32, NetError> {
sys::send_buffer(self.sock).map_err(os("SO_SNDBUF"))
}
pub fn set_send_buffer(&self, bytes: u32) -> Result<u32, NetError> {
sys::set_send_buffer(self.sock, bytes).map_err(os("SO_SNDBUF"))
}
pub fn receive_buffer(&self) -> Result<u32, NetError> {
sys::receive_buffer(self.sock).map_err(os("SO_RCVBUF"))
}
pub fn set_receive_buffer(&self, bytes: u32) -> Result<u32, NetError> {
sys::set_receive_buffer(self.sock, bytes).map_err(os("SO_RCVBUF"))
}
pub fn ttl(&self) -> Result<u8, NetError> {
sys::ttl(self.sock, self.v6).map_err(os("IP_TTL"))
}
pub fn set_ttl(&self, hops: u8) -> Result<(), NetError> {
sys::set_ttl(self.sock, self.v6, hops).map_err(os("IP_TTL"))
}
}
#[must_use]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LocalOptions {
pub(crate) mode: u32,
pub(crate) owner: Option<u32>,
pub(crate) group: Option<u32>,
pub(crate) remove_stale: bool,
pub(crate) require_uid: Option<u32>,
pub(crate) backlog: u32,
}
impl Default for LocalOptions {
fn default() -> LocalOptions {
LocalOptions::new()
}
}
impl LocalOptions {
pub fn new() -> LocalOptions {
LocalOptions {
mode: 0o600,
owner: None,
group: None,
remove_stale: true,
require_uid: None,
backlog: 1024,
}
}
pub fn mode(mut self, mode: u32) -> LocalOptions {
self.mode = mode & 0o777;
self
}
pub fn owner(mut self, uid: u32) -> LocalOptions {
self.owner = Some(uid);
self
}
pub fn group(mut self, gid: u32) -> LocalOptions {
self.group = Some(gid);
self
}
pub fn group_access(mut self, gid: u32) -> LocalOptions {
self.group = Some(gid);
self.mode = 0o660;
self
}
pub fn remove_stale(mut self, on: bool) -> LocalOptions {
self.remove_stale = on;
self
}
pub fn require_uid(mut self, uid: u32) -> LocalOptions {
self.require_uid = Some(uid);
self
}
pub fn backlog(mut self, backlog: u32) -> LocalOptions {
self.backlog = backlog;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_local_options_builders() {
let o = LocalOptions::new();
assert_eq!((o.mode, o.remove_stale, o.backlog), (0o600, true, 1024));
let o = o
.group_access(7)
.owner(3)
.require_uid(9)
.mode(0o1777)
.backlog(5);
assert_eq!(o.mode, 0o777);
assert_eq!(
(o.group, o.owner, o.require_uid),
(Some(7), Some(3), Some(9))
);
assert_eq!(LocalOptions::default(), LocalOptions::new());
}
#[test]
fn test_keepalive_zero_rejected() {
let k = Keepalive {
idle: Duration::ZERO,
interval: Duration::from_secs(1),
count: 1,
};
assert!(check_keepalive(&k).is_err());
assert!(check_keepalive(&Keepalive::DEFAULT).is_ok());
}
#[test]
fn test_listen_defaults() {
let o = ListenOptions::new();
assert_eq!(o.backlog, 1024);
assert!(o.ipv6_only);
assert_eq!(o.reuse_address, cfg!(unix));
assert_eq!(o.tcp.nodelay, Some(true));
}
#[test]
fn test_tcp_options_is_empty() {
assert!(TcpOptions::new().is_empty());
assert!(!TcpOptions::new().ttl(5).is_empty());
}
}