use std::{
io::{Read, Write},
net::{IpAddr, Ipv4Addr, Shutdown, SocketAddr, SocketAddrV4},
time::Duration,
};
use socket2::{Domain, Protocol, SockAddr, Socket, Type};
use crate::{Error, Result};
pub struct TcpStream {
inner: Option<std::net::TcpStream>,
peer: SocketAddr,
}
impl TcpStream {
pub fn connect(host: &str, port: u16) -> Result<Self> {
if host.is_empty() {
return Err(Error::invalid_argument("TCP host must not be empty"));
}
let inner = std::net::TcpStream::connect((host, port))
.map_err(|error| Error::io("TCP connect", error))?;
inner
.set_nodelay(true)
.map_err(|error| Error::io("TCP disable Nagle", error))?;
Self::from_std(inner)
}
fn from_std(inner: std::net::TcpStream) -> Result<Self> {
let peer = inner
.peer_addr()
.map_err(|error| Error::io("TCP peer address", error))?;
Ok(Self {
inner: Some(inner),
peer,
})
}
pub fn read(&mut self, bytes: &mut [u8]) -> Result<usize> {
self
.raw_mut()?
.read(bytes)
.map_err(|error| Error::io("TCP read", error))
}
pub fn write(&mut self, bytes: &[u8]) -> Result<usize> {
self
.raw_mut()?
.write(bytes)
.map_err(|error| Error::io("TCP write", error))
}
pub fn write_all(&mut self, bytes: &[u8]) -> Result<()> {
self
.raw_mut()?
.write_all(bytes)
.map_err(|error| Error::io("TCP write all", error))
}
pub fn set_io_timeout(&self, timeout: Duration) -> Result<()> {
let inner = self.raw()?;
let previous_read = inner
.read_timeout()
.map_err(|error| Error::io("TCP read timeout query", error))?;
let timeout = (!timeout.is_zero()).then_some(timeout);
inner
.set_read_timeout(timeout)
.map_err(|error| Error::io("TCP read timeout", error))?;
if let Err(error) = inner.set_write_timeout(timeout) {
let _ = inner.set_read_timeout(previous_read);
return Err(Error::io("TCP write timeout", error));
}
Ok(())
}
pub fn try_clone(&self) -> Result<Self> {
let inner = self
.raw()?
.try_clone()
.map_err(|error| Error::io("TCP clone", error))?;
Ok(Self {
inner: Some(inner),
peer: self.peer,
})
}
pub fn shutdown(&self) -> Result<()> {
self
.raw()?
.shutdown(Shutdown::Both)
.map_err(|error| Error::io("TCP shutdown", error))
}
pub fn close(&mut self) {
self.inner.take();
}
#[must_use]
pub const fn is_open(&self) -> bool {
self.inner.is_some()
}
#[must_use]
pub const fn peer_addr(&self) -> SocketAddr {
self.peer
}
#[must_use]
pub const fn remote_addr(&self) -> IpAddr {
self.peer.ip()
}
#[must_use]
pub const fn remote_port(&self) -> u16 {
self.peer.port()
}
fn raw(&self) -> Result<&std::net::TcpStream> {
self
.inner
.as_ref()
.ok_or_else(|| Error::failed_precondition("TCP stream is closed"))
}
pub(super) fn raw_mut(&mut self) -> Result<&mut std::net::TcpStream> {
self
.inner
.as_mut()
.ok_or_else(|| Error::failed_precondition("TCP stream is closed"))
}
}
pub struct TcpListener {
inner: Option<std::net::TcpListener>,
local: SocketAddr,
}
impl TcpListener {
pub fn bind_any(port: u16) -> Result<Self> {
Self::bind("0.0.0.0", port, 128)
}
pub fn bind(host: &str, port: u16, backlog: i32) -> Result<Self> {
if backlog <= 0 {
return Err(Error::invalid_argument(
"TCP listen backlog must be positive",
));
}
let address = host
.parse::<Ipv4Addr>()
.map_err(|_| Error::invalid_argument("TCP bind host must be an IPv4 address"))?;
let socket = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP))
.map_err(|error| Error::io("TCP socket", error))?;
socket
.set_reuse_address(true)
.map_err(|error| Error::io("TCP reuse address", error))?;
socket
.bind(&SockAddr::from(SocketAddrV4::new(address, port)))
.map_err(|error| Error::io("TCP bind", error))?;
socket
.listen(backlog)
.map_err(|error| Error::io("TCP listen", error))?;
let inner: std::net::TcpListener = socket.into();
let local = inner
.local_addr()
.map_err(|error| Error::io("TCP local address", error))?;
Ok(Self {
inner: Some(inner),
local,
})
}
pub fn accept(&self) -> Result<TcpStream> {
let inner = self
.inner
.as_ref()
.ok_or_else(|| Error::failed_precondition("TCP listener is closed"))?;
let (stream, _) = inner
.accept()
.map_err(|error| Error::io("TCP accept", error))?;
stream
.set_nodelay(true)
.map_err(|error| Error::io("TCP disable Nagle", error))?;
TcpStream::from_std(stream)
}
pub fn close(&mut self) {
self.inner.take();
}
#[must_use]
pub const fn is_open(&self) -> bool {
self.inner.is_some()
}
#[must_use]
pub const fn local_addr(&self) -> SocketAddr {
self.local
}
#[must_use]
pub const fn port(&self) -> u16 {
self.local.port()
}
}