#![allow(unsafe_code)]
#[cfg(not(any(
all(
any(target_os = "linux", target_os = "macos", target_os = "freebsd"),
any(target_arch = "x86_64", target_arch = "aarch64")
),
all(windows, any(target_arch = "x86_64", target_arch = "aarch64")),
)))]
compile_error!(
"net-io declares its OS surface for x86_64 and aarch64 on Linux, macOS, FreeBSD, and \
Windows; the std-only fallback for other targets arrives in 0.6"
);
#[cfg(unix)]
mod unix;
#[cfg(unix)]
pub(crate) use unix::*;
#[cfg(windows)]
mod windows;
#[cfg(windows)]
pub(crate) use windows::*;
#[cfg(all(feature = "reactor", unix))]
mod poll;
#[cfg(all(feature = "reactor", unix))]
pub(crate) use poll::{PollEvent, Registered, WAKE_KEY};
#[cfg(all(feature = "reactor", target_os = "linux"))]
mod epoll;
#[cfg(all(feature = "reactor", target_os = "linux"))]
pub(crate) use epoll::{PollWaker, Poller, WriteArm};
#[cfg(all(feature = "reactor", any(target_os = "macos", target_os = "freebsd")))]
mod kqueue;
#[cfg(all(feature = "reactor", any(target_os = "macos", target_os = "freebsd")))]
pub(crate) use kqueue::{PollWaker, Poller, WriteArm};
#[cfg(all(feature = "reactor", windows))]
mod iocp;
#[cfg(all(feature = "reactor", windows))]
pub(crate) use iocp::{
Associated, Finished, InFlight, OpKind, PollWaker, Port, Started, is_aborted,
};
#[cfg(all(windows, feature = "local"))]
mod pipe;
#[cfg(all(windows, feature = "local"))]
pub(crate) use pipe::{
Descriptor, PipeParams, PipeServer, PipeStream, is_name_taken, is_no_pipe, owner_sddl,
pipe_connect,
};
mod kill;
use std::io::{self, IoSlice, Read, Write};
use std::net::{Shutdown, TcpStream};
use std::time::Duration;
#[derive(Debug)]
pub(crate) enum Stream {
Tcp(TcpStream),
#[cfg(all(unix, feature = "local"))]
Unix(std::os::unix::net::UnixStream),
#[cfg(all(windows, feature = "local"))]
Pipe(PipeStream),
}
impl Stream {
pub(crate) fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
match self {
Stream::Tcp(s) => (&mut &*s).read(buf),
#[cfg(all(unix, feature = "local"))]
Stream::Unix(s) => (&mut &*s).read(buf),
#[cfg(all(windows, feature = "local"))]
Stream::Pipe(p) => p.read(buf),
}
}
#[cfg_attr(unix, allow(dead_code))]
pub(crate) fn write(&self, buf: &[u8]) -> io::Result<usize> {
match self {
Stream::Tcp(s) => (&mut &*s).write(buf),
#[cfg(all(unix, feature = "local"))]
Stream::Unix(s) => (&mut &*s).write(buf),
#[cfg(all(windows, feature = "local"))]
Stream::Pipe(p) => p.write(buf),
}
}
#[cfg_attr(unix, allow(dead_code))]
pub(crate) fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
match self {
Stream::Tcp(s) => (&mut &*s).write_vectored(bufs),
#[cfg(all(unix, feature = "local"))]
Stream::Unix(s) => (&mut &*s).write_vectored(bufs),
#[cfg(all(windows, feature = "local"))]
Stream::Pipe(p) => p.write_vectored(bufs),
}
}
#[cfg_attr(windows, allow(dead_code))]
pub(crate) fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
match self {
Stream::Tcp(s) => s.set_read_timeout(timeout),
#[cfg(all(unix, feature = "local"))]
Stream::Unix(s) => s.set_read_timeout(timeout),
#[cfg(all(windows, feature = "local"))]
Stream::Pipe(_) => Ok(()),
}
}
pub(crate) fn shutdown(&self, how: Shutdown) -> io::Result<()> {
match self {
Stream::Tcp(s) => s.shutdown(how),
#[cfg(all(unix, feature = "local"))]
Stream::Unix(s) => s.shutdown(how),
#[cfg(all(windows, feature = "local"))]
Stream::Pipe(_) => Ok(()),
}
}
pub(crate) fn set_nonblocking(&self, on: bool) -> io::Result<()> {
match self {
Stream::Tcp(s) => s.set_nonblocking(on),
#[cfg(all(unix, feature = "local"))]
Stream::Unix(s) => s.set_nonblocking(on),
#[cfg(all(windows, feature = "local"))]
Stream::Pipe(_) => Ok(()),
}
}
pub(crate) fn into_tcp(self) -> Option<TcpStream> {
match self {
Stream::Tcp(s) => Some(s),
#[cfg(all(unix, feature = "local"))]
Stream::Unix(_) => None,
#[cfg(all(windows, feature = "local"))]
Stream::Pipe(_) => None,
}
}
pub(crate) fn tcp(&self) -> Option<&TcpStream> {
match self {
Stream::Tcp(s) => Some(s),
#[cfg(all(unix, feature = "local"))]
Stream::Unix(_) => None,
#[cfg(all(windows, feature = "local"))]
Stream::Pipe(_) => None,
}
}
pub(crate) fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
match self {
Stream::Tcp(s) => s.peek(buf),
#[cfg(all(unix, feature = "local"))]
Stream::Unix(_) => Err(io::Error::from(io::ErrorKind::Unsupported)),
#[cfg(all(windows, feature = "local"))]
Stream::Pipe(_) => Err(io::Error::from(io::ErrorKind::Unsupported)),
}
}
pub(crate) fn wait(
&self,
interest: Interest,
wake: Option<&Wake>,
timeout: Option<Duration>,
) -> io::Result<Ready> {
#[cfg(all(windows, feature = "local"))]
if let Stream::Pipe(p) = self {
return p.wait(interest, wake, timeout);
}
wait(self.sock(), interest, wake, timeout)
}
pub(crate) fn set_abortive(&self) {
#[cfg(all(windows, feature = "local"))]
if let Stream::Pipe(p) = self {
return p.set_abortive();
}
let _ = set_linger(self.sock(), Some(Duration::ZERO));
}
pub(crate) fn half_closes(&self) -> bool {
#[cfg(all(windows, feature = "local"))]
if let Stream::Pipe(_) = self {
return false;
}
true
}
#[cfg(all(windows, feature = "local", feature = "reactor"))]
pub(crate) fn pipe_handle(&self) -> Option<std::os::windows::io::RawHandle> {
match self {
Stream::Pipe(p) => Some(p.raw()),
_ => None,
}
}
}
#[cfg(unix)]
impl std::os::fd::AsFd for Stream {
fn as_fd(&self) -> std::os::fd::BorrowedFd<'_> {
match self {
Stream::Tcp(s) => s.as_fd(),
#[cfg(feature = "local")]
Stream::Unix(s) => s.as_fd(),
}
}
}
#[cfg(windows)]
impl std::os::windows::io::AsSocket for Stream {
fn as_socket(&self) -> std::os::windows::io::BorrowedSocket<'_> {
match self {
Stream::Tcp(s) => s.as_socket(),
#[cfg(feature = "local")]
Stream::Pipe(p) => p.as_sock(),
}
}
}
#[cfg(feature = "reactor")]
pub(crate) use kill::KillSink;
pub(crate) use kill::KillSlot;
#[cfg(any(test, net_io_shim))]
pub(crate) mod shim;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Interest {
Read,
Write,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Ready {
Socket,
Woken,
TimedOut,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum SockKind {
Tcp(std::net::SocketAddr),
#[cfg(unix)]
Unix(Vec<u8>),
}
#[derive(Debug)]
#[cfg_attr(windows, allow(dead_code))]
pub(crate) enum KindError {
NotASocket,
NotListening,
WrongKind,
Os(io::Error),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum AcceptFailure {
PerConnection,
#[cfg_attr(windows, allow(dead_code))]
Descriptors,
Pressure,
ListenerFailed,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum AddrFailure {
InUse,
NotAvailable,
Permission,
Refused,
HostUnreachable,
NetworkUnreachable,
TimedOut,
Other,
}
pub(crate) fn poll_millis(timeout: Option<Duration>) -> i32 {
match timeout {
None => -1,
Some(d) => {
let ms = d
.as_millis()
.saturating_add(u128::from(d.subsec_nanos() % 1_000_000 != 0));
i32::try_from(ms).unwrap_or(i32::MAX)
}
}
}
pub(crate) fn retry_interrupted<T>(mut op: impl FnMut() -> io::Result<T>) -> io::Result<T> {
loop {
match op() {
Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
other => return other,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_poll_millis_rounds_up_and_caps() {
assert_eq!(poll_millis(None), -1);
assert_eq!(poll_millis(Some(Duration::from_millis(5))), 5);
assert_eq!(poll_millis(Some(Duration::from_micros(1))), 1);
assert_eq!(poll_millis(Some(Duration::from_secs(u64::MAX))), i32::MAX);
assert_eq!(poll_millis(Some(Duration::ZERO)), 0);
}
#[test]
fn test_retry_interrupted_retries_then_returns() {
let mut n = 0;
let r = retry_interrupted(|| {
n += 1;
if n < 3 {
Err(io::Error::from(io::ErrorKind::Interrupted))
} else {
Ok(n)
}
});
assert_eq!(r.unwrap(), 3);
}
}