use std::net::TcpStream;
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 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 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(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) 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,
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 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,
}
}
}
#[derive(Debug)]
pub struct SocketOptions<'a> {
pub(crate) stream: &'a TcpStream,
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.stream.sock()).map_err(os("TCP_NODELAY"))
}
pub fn set_nodelay(&self, on: bool) -> Result<(), NetError> {
sys::set_nodelay(self.stream.sock(), on).map_err(os("TCP_NODELAY"))
}
pub fn keepalive(&self) -> Result<Option<Keepalive>, NetError> {
Ok(sys::keepalive(self.stream.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.stream.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.stream.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.stream.sock(), l.to_sys()).map_err(os("SO_LINGER"))
}
pub fn send_buffer(&self) -> Result<u32, NetError> {
sys::send_buffer(self.stream.sock()).map_err(os("SO_SNDBUF"))
}
pub fn set_send_buffer(&self, bytes: u32) -> Result<u32, NetError> {
sys::set_send_buffer(self.stream.sock(), bytes).map_err(os("SO_SNDBUF"))
}
pub fn receive_buffer(&self) -> Result<u32, NetError> {
sys::receive_buffer(self.stream.sock()).map_err(os("SO_RCVBUF"))
}
pub fn set_receive_buffer(&self, bytes: u32) -> Result<u32, NetError> {
sys::set_receive_buffer(self.stream.sock(), bytes).map_err(os("SO_RCVBUF"))
}
pub fn ttl(&self) -> Result<u8, NetError> {
sys::ttl(self.stream.sock(), self.v6).map_err(os("IP_TTL"))
}
pub fn set_ttl(&self, hops: u8) -> Result<(), NetError> {
sys::set_ttl(self.stream.sock(), self.v6, hops).map_err(os("IP_TTL"))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[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());
}
}