#![doc = include_str!("../README.md")]
#![allow(clippy::multiple_inherent_impl)]
#[cfg(not(unix))]
mod default;
#[cfg(unix)]
mod unix;
use std::io;
use std::net::{SocketAddr, ToSocketAddrs as _};
pub use socket2::TcpKeepalive;
use uni_addr::{UniAddr, UniAddrInner};
#[cfg(not(unix))]
pub use self::default::{OwnedReadHalf, OwnedWriteHalf, UniStream};
#[cfg(unix)]
pub use self::unix::{OwnedReadHalf, OwnedWriteHalf, UniStream};
wrapper_lite::wrapper! {
#[wrapper_impl(Debug)]
#[wrapper_impl(AsRef)]
#[wrapper_impl(AsMut)]
#[wrapper_impl(DerefMut)]
pub struct UniSocket {
inner: socket2::Socket,
#[cfg(unix)]
is_unix_socket: bool,
}
}
impl UniSocket {
pub fn bind(addr: &UniAddr) -> io::Result<Self> {
let ty = socket2::Type::STREAM;
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd"
))]
let ty = ty.nonblocking();
#[cfg(unix)]
if let UniAddrInner::Unix(addr) = addr.as_inner() {
let inner = socket2::Socket::new(socket2::Domain::UNIX, ty, None)?;
#[cfg(not(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd"
)))]
inner.set_nonblocking(true)?;
inner.bind(&socket2::SockAddr::unix(addr.to_os_string())?)?;
return Ok(Self {
inner,
is_unix_socket: true,
});
}
let (addr, domain) = match addr.as_inner() {
UniAddrInner::Inet(addr @ SocketAddr::V4(_)) => (*addr, socket2::Domain::IPV4),
UniAddrInner::Inet(addr @ SocketAddr::V6(_)) => (*addr, socket2::Domain::IPV6),
UniAddrInner::Host(addr) => {
let addr = addr
.to_socket_addrs()?
.next()
.ok_or_else(|| io::Error::other("no addresses found"))?;
match addr {
SocketAddr::V4(_) => (addr, socket2::Domain::IPV4),
SocketAddr::V6(_) => (addr, socket2::Domain::IPV6),
}
}
_ => {
return Err(io::Error::new(
io::ErrorKind::Other,
"unsupported address type",
))
}
};
let inner = socket2::Socket::new(domain, ty, Some(socket2::Protocol::TCP))?;
#[cfg(not(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd"
)))]
inner.set_nonblocking(true)?;
inner.set_reuse_address(true)?;
#[cfg(all(
unix,
not(target_os = "solaris"),
not(target_os = "illumos"),
not(target_os = "cygwin"),
))]
inner.set_reuse_port(true)?;
inner.bind(&socket2::SockAddr::from(addr))?;
Ok(Self {
inner,
#[cfg(unix)]
is_unix_socket: false,
})
}
pub fn listen(self, backlog: u32) -> io::Result<UniListener> {
self.inner.listen(backlog as i32)?;
#[cfg(unix)]
if self.is_unix_socket {
return tokio::net::UnixListener::from_std(self.inner.into()).map(UniListener::Unix);
}
tokio::net::TcpListener::from_std(self.inner.into()).map(UniListener::Tcp)
}
}
#[derive(Debug)]
pub enum UniListener {
Tcp(tokio::net::TcpListener),
#[cfg(unix)]
Unix(tokio::net::UnixListener),
}
impl UniListener {
pub async fn bind(addr: &UniAddr) -> io::Result<Self> {
match addr.as_inner() {
UniAddrInner::Inet(addr) => tokio::net::TcpListener::bind(addr).await.map(Self::Tcp),
#[cfg(unix)]
UniAddrInner::Unix(addr) => {
tokio::net::UnixListener::bind(addr.to_os_string()).map(Self::Unix)
}
UniAddrInner::Host(addr) => tokio::net::TcpListener::bind(&**addr).await.map(Self::Tcp),
_ => Err(io::Error::new(
io::ErrorKind::Other,
"unsupported address type",
)),
}
}
pub fn local_addr(&self) -> io::Result<UniAddr> {
match self {
UniListener::Tcp(listener) => listener.local_addr().map(UniAddr::from),
#[cfg(unix)]
UniListener::Unix(listener) => listener.local_addr().map(UniAddr::from),
}
}
pub fn as_socket_ref(&self) -> socket2::SockRef<'_> {
match self {
UniListener::Tcp(listener) => listener.into(),
#[cfg(unix)]
UniListener::Unix(listener) => listener.into(),
}
}
pub async fn accept(&self) -> io::Result<(UniStream, UniAddr)> {
match self {
UniListener::Tcp(listener) => {
let (stream, addr) = listener.accept().await?;
Ok((UniStream::try_from(stream)?, UniAddr::from(addr)))
}
#[cfg(unix)]
UniListener::Unix(listener) => {
let (stream, addr) = listener.accept().await?;
Ok((UniStream::try_from(stream)?, UniAddr::from(addr)))
}
}
}
}