Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for UnixStream and UnixListener on Windows #1667

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ pub mod windows {
//! Windows only extensions.

pub use crate::sys::named_pipe::NamedPipe;
// blocking windows uds which mimick std implementation used for tests
cfg_net! {
pub use crate::sys::windows::stdnet;
}
Comment on lines +94 to +97
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're not going to expose this.

Copy link
Contributor Author

@KolbyML KolbyML Sep 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has previously been stated the tests shouldn't change. To make the tests not change we need to have a blocking windows UDS implementation. Having this requirement only leaves us 2 options.

  • 1: Add #[cfg(test)] to the expose so it can only be used in tests
  • 2: Implement UDS support in the rust STD

Would option 1 even be allowed? or an Option?

If it isn't an option then if we want windows UDS in tokio there is a requirement for it being implemented in the STD First. Originally it was thought that we could add Windows UDS since MIO already supports Windows Named Pipes without std support.

So is the precedent that std support is required. That is fine, it is nice to just know that is definitly the step forward. Because the only non-std way to fulfill the no-changing-test-requirement would be option 1.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has previously been stated the tests shouldn't change.

Yes, we need to ensure that the existing behaviour works on Windows as well.

To make the tests not change we need to have a blocking windows UDS implementation. Having this requirement only leaves us 2 options.

  • 1: Add #[cfg(test)] to the expose so it can only be used in tests
  • 2: Implement UDS support in the rust STD

Would option 1 even be allowed? or an Option?

The first option doesn't work as we use Rust's integration tests, which doesn't have access to another crate's #[cfg(test)] exposed items. The second option would be ideal, but that's currently not possible as far as I know.

If it isn't an option then if we want windows UDS in tokio there is a requirement for it being implemented in the STD First. Originally it was thought that we could add Windows UDS since MIO already supports Windows Named Pipes without std support.

So is the precedent that std support is required. That is fine, it is nice to just know that is definitly the step forward. Because the only non-std way to fulfill the no-changing-test-requirement would be option 1.

You're overlooking a third option. Have a blocking implementation inside the test code.

}

pub mod features {
Expand Down
11 changes: 8 additions & 3 deletions src/net/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,13 @@ pub use self::tcp::{TcpListener, TcpStream};
mod udp;
#[cfg(not(target_os = "wasi"))]
pub use self::udp::UdpSocket;

#[cfg(unix)]
#[cfg(not(target_os = "wasi"))]
mod uds;
#[cfg(not(target_os = "wasi"))]
pub use self::uds::{SocketAddr, UnixListener, UnixStream};

#[cfg(not(target_os = "wasi"))]
pub(crate) use self::uds::AddressKind;

#[cfg(unix)]
pub use self::uds::{SocketAddr, UnixDatagram, UnixListener, UnixStream};
pub use self::uds::UnixDatagram;
97 changes: 97 additions & 0 deletions src/net/uds/addr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use crate::sys;
use std::path::Path;
use std::{ascii, fmt};

/// An address associated with a `mio` specific Unix socket.
///
/// This is implemented instead of imported from [`net::SocketAddr`] because
/// there is no way to create a [`net::SocketAddr`]. One must be returned by
/// [`accept`], so this is returned instead.
///
/// [`net::SocketAddr`]: std::os::unix::net::SocketAddr
/// [`accept`]: #method.accept
pub struct SocketAddr {
inner: sys::SocketAddr,
}

struct AsciiEscaped<'a>(&'a [u8]);

pub(crate) enum AddressKind<'a> {
Unnamed,
Pathname(&'a Path),
Abstract(&'a [u8]),
}

impl SocketAddr {
pub(crate) fn new(inner: sys::SocketAddr) -> Self {
SocketAddr { inner }
}

fn address(&self) -> AddressKind<'_> {
self.inner.address()
}
}

cfg_os_poll! {
impl SocketAddr {
/// Returns `true` if the address is unnamed.
///
/// Documentation reflected in [`SocketAddr`]
///
/// [`SocketAddr`]: std::os::unix::net::SocketAddr
pub fn is_unnamed(&self) -> bool {
matches!(self.address(), AddressKind::Unnamed)
}

/// Returns the contents of this address if it is a `pathname` address.
///
/// Documentation reflected in [`SocketAddr`]
///
/// [`SocketAddr`]: std::os::unix::net::SocketAddr
pub fn as_pathname(&self) -> Option<&Path> {
if let AddressKind::Pathname(path) = self.address() {
Some(path)
} else {
None
}
}

/// Returns the contents of this address if it is an abstract namespace
/// without the leading null byte.
// Link to std::os::unix::net::SocketAddr pending
// https://github.com/rust-lang/rust/issues/85410.
pub fn as_abstract_namespace(&self) -> Option<&[u8]> {
if let AddressKind::Abstract(path) = self.address() {
Some(path)
} else {
None
}
}
}
}

impl fmt::Debug for SocketAddr {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "{:?}", self.address())
}
}

impl fmt::Debug for AddressKind<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AddressKind::Unnamed => write!(fmt, "(unnamed)"),
AddressKind::Abstract(name) => write!(fmt, "{} (abstract)", AsciiEscaped(name)),
AddressKind::Pathname(path) => write!(fmt, "{:?} (pathname)", path),
}
}
}

impl<'a> fmt::Display for AsciiEscaped<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "\"")?;
for byte in self.0.iter().cloned().flat_map(ascii::escape_default) {
write!(fmt, "{}", byte as char)?;
}
write!(fmt, "\"")
}
}
12 changes: 7 additions & 5 deletions src/net/uds/datagram.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::io_source::IoSource;
use crate::net::SocketAddr;
use crate::{event, sys, Interest, Registry, Token};

use std::net::Shutdown;
Expand Down Expand Up @@ -54,24 +55,25 @@ impl UnixDatagram {
}

/// Returns the address of this socket.
pub fn local_addr(&self) -> io::Result<sys::SocketAddr> {
sys::uds::datagram::local_addr(&self.inner)
pub fn local_addr(&self) -> io::Result<SocketAddr> {
sys::uds::datagram::local_addr(&self.inner).map(SocketAddr::new)
}

/// Returns the address of this socket's peer.
///
/// The `connect` method will connect the socket to a peer.
pub fn peer_addr(&self) -> io::Result<sys::SocketAddr> {
sys::uds::datagram::peer_addr(&self.inner)
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
sys::uds::datagram::peer_addr(&self.inner).map(SocketAddr::new)
}

/// Receives data from the socket.
///
/// On success, returns the number of bytes read and the address from
/// whence the data came.
pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, sys::SocketAddr)> {
pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
self.inner
.do_io(|inner| sys::uds::datagram::recv_from(inner, buf))
.map(|(nread, addr)| (nread, SocketAddr::new(addr)))
}

/// Receives data from the socket.
Expand Down
44 changes: 41 additions & 3 deletions src/net/uds/listener.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@ use crate::io_source::IoSource;
use crate::net::{SocketAddr, UnixStream};
use crate::{event, sys, Interest, Registry, Token};

#[cfg(windows)]
use crate::sys::windows::stdnet as net;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(unix)]
use std::os::unix::net;
#[cfg(windows)]
use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket};
use std::path::Path;
use std::{fmt, io};

Expand Down Expand Up @@ -40,12 +46,14 @@ impl UnixListener {
/// The call is responsible for ensuring that the listening socket is in
/// non-blocking mode.
pub fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> {
sys::uds::listener::accept(&self.inner)
self.inner
.do_io(sys::uds::listener::accept)
.map(|(stream, addr)| (stream, SocketAddr::new(addr)))
}

/// Returns the local socket address of this listener.
pub fn local_addr(&self) -> io::Result<sys::SocketAddr> {
sys::uds::listener::local_addr(&self.inner)
pub fn local_addr(&self) -> io::Result<SocketAddr> {
sys::uds::listener::local_addr(&self.inner).map(SocketAddr::new)
}

/// Returns the value of the `SO_ERROR` option.
Expand Down Expand Up @@ -84,18 +92,24 @@ impl fmt::Debug for UnixListener {
}
}

#[cfg(unix)]
#[cfg_attr(docsrs, doc(cfg(unix)))]
impl IntoRawFd for UnixListener {
fn into_raw_fd(self) -> RawFd {
self.inner.into_inner().into_raw_fd()
}
}

#[cfg(unix)]
#[cfg_attr(docsrs, doc(cfg(unix)))]
impl AsRawFd for UnixListener {
fn as_raw_fd(&self) -> RawFd {
self.inner.as_raw_fd()
}
}

#[cfg(unix)]
#[cfg_attr(docsrs, doc(cfg(unix)))]
impl FromRawFd for UnixListener {
/// Converts a `RawFd` to a `UnixListener`.
///
Expand All @@ -107,3 +121,27 @@ impl FromRawFd for UnixListener {
UnixListener::from_std(FromRawFd::from_raw_fd(fd))
}
}

#[cfg(windows)]
#[cfg_attr(docsrs, doc(cfg(windows)))]
impl IntoRawSocket for UnixListener {
fn into_raw_socket(self) -> RawSocket {
self.inner.into_inner().into_raw_socket()
}
}

#[cfg(windows)]
#[cfg_attr(docsrs, doc(cfg(windows)))]
impl AsRawSocket for UnixListener {
fn as_raw_socket(&self) -> RawSocket {
self.inner.as_raw_socket()
}
}

#[cfg(windows)]
#[cfg_attr(docsrs, doc(cfg(windows)))]
impl FromRawSocket for UnixListener {
unsafe fn from_raw_socket(sock: RawSocket) -> Self {
UnixListener::from_std(FromRawSocket::from_raw_socket(sock))
}
}
7 changes: 6 additions & 1 deletion src/net/uds/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#[cfg(unix)]
mod datagram;
#[cfg(unix)]
#[cfg_attr(docsrs, doc(cfg(unix)))]
pub use self::datagram::UnixDatagram;

mod listener;
Expand All @@ -7,4 +10,6 @@ pub use self::listener::UnixListener;
mod stream;
pub use self::stream::UnixStream;

pub use crate::sys::SocketAddr;
mod addr;
pub(crate) use self::addr::AddressKind;
pub use self::addr::SocketAddr;
Loading