#[cfg(target_os = "android")]
use std::os::android::net::SocketAddrExt;
#[cfg(target_os = "linux")]
use std::os::linux::net::SocketAddrExt;
use std::{
fmt,
io::{self, Read},
net::Shutdown,
os::{
fd::{AsFd, AsRawFd, BorrowedFd, RawFd},
unix::{ffi::OsStrExt, net::SocketAddr},
},
path::Path,
pin::Pin,
task::{Context, Poll, ready},
time::Duration,
};
use futures_core::Stream;
use futures_io::{AsyncRead, AsyncWrite};
use socket2::SockAddr;
use super::{connect, set_nosigpipe};
use crate::{AsyncIo, Local, Mode, Runtime};
pub struct UnixListener<M = Local>
where
M: Mode,
{
io: AsyncIo<std::os::unix::net::UnixListener, M>,
}
impl<M> UnixListener<M>
where
M: Mode,
{
pub fn bind<P>(runtime: &Runtime<M>, path: P) -> io::Result<Self>
where
P: AsRef<Path>,
{
Self::from_std(runtime, std::os::unix::net::UnixListener::bind(path)?)
}
pub fn from_std(
runtime: &Runtime<M>,
listener: std::os::unix::net::UnixListener,
) -> io::Result<Self> {
listener.set_nonblocking(true)?;
set_nosigpipe(&listener)?;
Ok(Self {
io: AsyncIo::from_nonblocking(runtime, listener)?,
})
}
pub async fn accept(&self) -> io::Result<(UnixStream<M>, SocketAddr)> {
let (stream, address) = self.io.read_with(|listener| listener.accept()).await?;
self.accepted(stream, address)
}
pub fn incoming(&self) -> Incoming<'_, M> {
Incoming { listener: self }
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.io.get_ref().local_addr()
}
}
impl<M> fmt::Debug for UnixListener<M>
where
M: Mode,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self.io.get_ref(), f)
}
}
impl<M> AsFd for UnixListener<M>
where
M: Mode,
{
fn as_fd(&self) -> BorrowedFd<'_> {
self.io.get_ref().as_fd()
}
}
impl<M> AsRawFd for UnixListener<M>
where
M: Mode,
{
fn as_raw_fd(&self) -> RawFd {
self.io.get_ref().as_raw_fd()
}
}
pub struct UnixStream<M = Local>
where
M: Mode,
{
io: AsyncIo<std::os::unix::net::UnixStream, M>,
}
impl<M> UnixStream<M>
where
M: Mode,
{
pub async fn connect<P>(runtime: &Runtime<M>, path: P) -> io::Result<Self>
where
P: AsRef<Path>,
{
Self::connect_sockaddr(runtime, &pathname_sockaddr(path.as_ref())?).await
}
pub async fn connect_addr(runtime: &Runtime<M>, address: &SocketAddr) -> io::Result<Self> {
Self::connect_sockaddr(runtime, &named_sockaddr(address)?).await
}
pub fn pair(runtime: &Runtime<M>) -> io::Result<(Self, Self)> {
let (first, second) = std::os::unix::net::UnixStream::pair()?;
Ok((
Self::from_std(runtime, first)?,
Self::from_std(runtime, second)?,
))
}
pub fn from_std(
runtime: &Runtime<M>,
stream: std::os::unix::net::UnixStream,
) -> io::Result<Self> {
stream.set_nonblocking(true)?;
set_nosigpipe(&stream)?;
Ok(Self {
io: AsyncIo::from_nonblocking(runtime, stream)?,
})
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.io.get_ref().local_addr()
}
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
self.io.get_ref().peer_addr()
}
pub fn into_std(self) -> std::os::unix::net::UnixStream {
self.io.into_inner()
}
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
self.io.get_ref().shutdown(how)
}
}
impl<M> AsyncRead for UnixStream<M>
where
M: Mode,
{
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut &*self).poll_read(cx, buf)
}
}
impl<M> AsyncWrite for UnixStream<M>
where
M: Mode,
{
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut &*self).poll_write(cx, buf)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut &*self).poll_flush(cx)
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut &*self).poll_close(cx)
}
}
impl<M> AsyncRead for &UnixStream<M>
where
M: Mode,
{
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
self.io.poll_read_with(cx, |mut stream| stream.read(buf))
}
}
impl<M> AsyncWrite for &UnixStream<M>
where
M: Mode,
{
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
self.io
.poll_write_with(cx, |stream| Ok(rustix::net::send(stream, buf, SEND_FLAGS)?))
}
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
match self.io.get_ref().shutdown(Shutdown::Write) {
Err(e) if e.kind() == io::ErrorKind::NotConnected => Poll::Ready(Ok(())),
result => Poll::Ready(result),
}
}
}
impl<M> fmt::Debug for UnixStream<M>
where
M: Mode,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self.io.get_ref(), f)
}
}
impl<M> AsFd for UnixStream<M>
where
M: Mode,
{
fn as_fd(&self) -> BorrowedFd<'_> {
self.io.get_ref().as_fd()
}
}
impl<M> AsRawFd for UnixStream<M>
where
M: Mode,
{
fn as_raw_fd(&self) -> RawFd {
self.io.get_ref().as_raw_fd()
}
}
pub struct UnixDatagram<M = Local>
where
M: Mode,
{
io: AsyncIo<std::os::unix::net::UnixDatagram, M>,
}
impl<M> UnixDatagram<M>
where
M: Mode,
{
pub fn bind<P>(runtime: &Runtime<M>, path: P) -> io::Result<Self>
where
P: AsRef<Path>,
{
Self::from_std(runtime, std::os::unix::net::UnixDatagram::bind(path)?)
}
pub fn unbound(runtime: &Runtime<M>) -> io::Result<Self> {
Self::from_std(runtime, std::os::unix::net::UnixDatagram::unbound()?)
}
pub fn pair(runtime: &Runtime<M>) -> io::Result<(Self, Self)> {
let (first, second) = std::os::unix::net::UnixDatagram::pair()?;
Ok((
Self::from_std(runtime, first)?,
Self::from_std(runtime, second)?,
))
}
pub fn from_std(
runtime: &Runtime<M>,
socket: std::os::unix::net::UnixDatagram,
) -> io::Result<Self> {
socket.set_nonblocking(true)?;
set_nosigpipe(&socket)?;
Ok(Self {
io: AsyncIo::from_nonblocking(runtime, socket)?,
})
}
pub fn connect<P>(&self, path: P) -> io::Result<()>
where
P: AsRef<Path>,
{
self.io.get_ref().connect(path)
}
pub async fn send_to<P>(&self, buf: &[u8], path: P) -> io::Result<usize>
where
P: AsRef<Path>,
{
let path = path.as_ref();
loop {
match self.io.get_ref().send_to(buf, path) {
Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
self.io.runtime().sleep(SEND_TO_INTERVAL).await;
}
result => return result,
}
}
}
pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
self.io.read_with(|socket| socket.recv_from(buf)).await
}
pub async fn send(&self, buf: &[u8]) -> io::Result<usize> {
self.io
.write_with(|socket| Ok(rustix::net::send(socket, buf, SEND_FLAGS)?))
.await
}
pub async fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
self.io.read_with(|socket| socket.recv(buf)).await
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.io.get_ref().local_addr()
}
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
self.io.get_ref().peer_addr()
}
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
self.io.get_ref().shutdown(how)
}
}
const SEND_TO_INTERVAL: Duration = Duration::from_millis(20);
impl<M> fmt::Debug for UnixDatagram<M>
where
M: Mode,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self.io.get_ref(), f)
}
}
impl<M> AsFd for UnixDatagram<M>
where
M: Mode,
{
fn as_fd(&self) -> BorrowedFd<'_> {
self.io.get_ref().as_fd()
}
}
impl<M> AsRawFd for UnixDatagram<M>
where
M: Mode,
{
fn as_raw_fd(&self) -> RawFd {
self.io.get_ref().as_raw_fd()
}
}
pub struct Incoming<'a, M = Local>
where
M: Mode,
{
listener: &'a UnixListener<M>,
}
impl<M> Stream for Incoming<'_, M>
where
M: Mode,
{
type Item = io::Result<UnixStream<M>>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.listener
.poll_accept(cx)
.map(|accepted| Some(accepted.map(|(stream, _address)| stream)))
}
}
impl<M> fmt::Debug for Incoming<'_, M>
where
M: Mode,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Incoming")
.field("listener", &self.listener)
.finish()
}
}
impl<M> UnixListener<M>
where
M: Mode,
{
fn poll_accept(&self, cx: &mut Context<'_>) -> Poll<io::Result<(UnixStream<M>, SocketAddr)>> {
let (stream, address) = ready!(self.io.poll_read_with(cx, |listener| listener.accept()))?;
Poll::Ready(self.accepted(stream, address))
}
fn accepted(
&self,
stream: std::os::unix::net::UnixStream,
address: SocketAddr,
) -> io::Result<(UnixStream<M>, SocketAddr)> {
UnixStream::from_std(&self.io.runtime(), stream).map(|stream| (stream, address))
}
}
impl<M> UnixStream<M>
where
M: Mode,
{
async fn connect_sockaddr(runtime: &Runtime<M>, address: &SockAddr) -> io::Result<Self> {
let io = connect::connect_unix(runtime, address).await?;
Ok(Self { io })
}
}
fn named_sockaddr(address: &SocketAddr) -> io::Result<SockAddr> {
if let Some(path) = address.as_pathname() {
return pathname_sockaddr(path);
}
#[cfg(any(target_os = "linux", target_os = "android"))]
if let Some(name) = address.as_abstract_name() {
let path = [&[0u8][..], name].concat();
return SockAddr::unix(std::ffi::OsStr::from_bytes(&path));
}
Err(io::Error::new(
io::ErrorKind::InvalidInput,
"an unnamed address names no socket to connect to",
))
}
fn pathname_sockaddr(path: &Path) -> io::Result<SockAddr> {
if path.as_os_str().as_bytes().contains(&0) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"paths must not contain interior null bytes",
));
}
SockAddr::unix(path)
}
#[cfg(not(any(
target_os = "macos",
target_os = "ios",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
target_os = "redox"
)))]
const SEND_FLAGS: rustix::net::SendFlags = rustix::net::SendFlags::NOSIGNAL;
#[cfg(any(
target_os = "macos",
target_os = "ios",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
target_os = "redox"
))]
const SEND_FLAGS: rustix::net::SendFlags = rustix::net::SendFlags::empty();