use super::{Socket, error};
use crate::{AcceptDefaults, Error, ErrorKind, KeepAlive, Result, SocketOption, SocketOptionKind};
use std::time::Duration;
use wasip3::sockets::types::TcpSocket;
fn unsupported<T>() -> Result<T> {
Err(Error::new(ErrorKind::Unsupported))
}
fn invalid<T>() -> Result<T> {
Err(Error::new(ErrorKind::InvalidInput))
}
fn hops(value: u32) -> Result<u8> {
match u8::try_from(value) {
Ok(0) | Err(_) => invalid(),
Ok(hops) => Ok(hops),
}
}
fn tcp(socket: &Socket) -> Result<&TcpSocket> {
match socket {
Socket::Tcp(socket) => Ok(socket),
_ => unsupported(),
}
}
fn nonzero(value: Duration) -> Result<u64> {
match u64::try_from(value.as_nanos()) {
Ok(0) | Err(_) => invalid(),
Ok(nanos) => Ok(nanos),
}
}
fn set_keep_alive(socket: &TcpSocket, value: Option<KeepAlive>) -> Result<()> {
let Some(schedule) = value else {
return socket.set_keep_alive_enabled(false).map_err(error);
};
let idle = schedule.idle.map(nonzero).transpose()?;
let interval = schedule.interval.map(nonzero).transpose()?;
if schedule.count == Some(0) {
return invalid();
}
socket.set_keep_alive_enabled(true).map_err(error)?;
if let Some(idle) = idle {
socket.set_keep_alive_idle_time(idle).map_err(error)?;
}
if let Some(interval) = interval {
socket.set_keep_alive_interval(interval).map_err(error)?;
}
if let Some(count) = schedule.count {
socket.set_keep_alive_count(count).map_err(error)?;
}
Ok(())
}
fn get_keep_alive(socket: &TcpSocket) -> Result<Option<KeepAlive>> {
if !socket.get_keep_alive_enabled().map_err(error)? {
return Ok(None);
}
Ok(Some(KeepAlive {
idle: Some(Duration::from_nanos(
socket.get_keep_alive_idle_time().map_err(error)?,
)),
interval: Some(Duration::from_nanos(
socket.get_keep_alive_interval().map_err(error)?,
)),
count: Some(socket.get_keep_alive_count().map_err(error)?),
}))
}
fn bytes(value: u64) -> u32 {
u32::try_from(value).unwrap_or(u32::MAX)
}
pub(super) fn set(socket: &Socket, option: SocketOption) -> Result<()> {
match option {
SocketOption::KeepAlive(schedule) => set_keep_alive(tcp(socket)?, schedule),
SocketOption::RecvBufferSize(size) => match socket {
Socket::Tcp(socket) => socket.set_receive_buffer_size(u64::from(size)),
Socket::Udp(socket) => socket.set_receive_buffer_size(u64::from(size)),
Socket::Stdio => return unsupported(),
}
.map_err(error),
SocketOption::SendBufferSize(size) => match socket {
Socket::Tcp(socket) => socket.set_send_buffer_size(u64::from(size)),
Socket::Udp(socket) => socket.set_send_buffer_size(u64::from(size)),
Socket::Stdio => return unsupported(),
}
.map_err(error),
SocketOption::Ttl(value) => {
let value = hops(value)?;
match socket {
Socket::Tcp(socket) => socket.set_hop_limit(value),
Socket::Udp(socket) => socket.set_unicast_hop_limit(value),
Socket::Stdio => return unsupported(),
}
.map_err(error)
}
SocketOption::NoDelay(_)
| SocketOption::Linger(_)
| SocketOption::Ipv6Only(_)
| SocketOption::Broadcast(_)
| SocketOption::MulticastTtl(_)
| SocketOption::MulticastLoop(_)
| SocketOption::MulticastJoin(_)
| SocketOption::MulticastLeave(_) => unsupported(),
}
}
pub(super) fn get(socket: &Socket, kind: SocketOptionKind) -> Result<SocketOption> {
Ok(match kind {
SocketOptionKind::KeepAlive => SocketOption::KeepAlive(get_keep_alive(tcp(socket)?)?),
SocketOptionKind::RecvBufferSize => SocketOption::RecvBufferSize(bytes(
match socket {
Socket::Tcp(socket) => socket.get_receive_buffer_size(),
Socket::Udp(socket) => socket.get_receive_buffer_size(),
Socket::Stdio => return unsupported(),
}
.map_err(error)?,
)),
SocketOptionKind::SendBufferSize => SocketOption::SendBufferSize(bytes(
match socket {
Socket::Tcp(socket) => socket.get_send_buffer_size(),
Socket::Udp(socket) => socket.get_send_buffer_size(),
Socket::Stdio => return unsupported(),
}
.map_err(error)?,
)),
SocketOptionKind::Ttl => SocketOption::Ttl(u32::from(
match socket {
Socket::Tcp(socket) => socket.get_hop_limit(),
Socket::Udp(socket) => socket.get_unicast_hop_limit(),
Socket::Stdio => return unsupported(),
}
.map_err(error)?,
)),
SocketOptionKind::NoDelay
| SocketOptionKind::Linger
| SocketOptionKind::Ipv6Only
| SocketOptionKind::Broadcast
| SocketOptionKind::MulticastTtl
| SocketOptionKind::MulticastLoop => return unsupported(),
})
}
pub(super) fn apply_accept_defaults(socket: &TcpSocket, defaults: AcceptDefaults) -> Result<()> {
if defaults.nodelay {
return unsupported();
}
if let Some(schedule) = defaults.keep_alive {
set_keep_alive(socket, Some(schedule))?;
}
Ok(())
}
pub(super) fn validate_accept_defaults(defaults: AcceptDefaults) -> Result<()> {
if defaults.nodelay {
return unsupported();
}
Ok(())
}