#[cfg(target_os = "android")]
use std::os::android::net::SocketAddrExt;
#[cfg(target_os = "linux")]
use std::os::linux::net::SocketAddrExt;
use std::{
cell::Cell,
future::poll_fn,
io::{self, Read, Write},
net::Shutdown,
os::fd::AsRawFd,
path::PathBuf,
pin::{Pin, pin},
sync::{
Arc,
atomic::{AtomicUsize, Ordering},
},
task::Poll,
thread,
};
use futures::{
AsyncReadExt, AsyncWriteExt, StreamExt,
executor::block_on,
future::{join, poll_immediate},
io::{AsyncWrite, copy},
};
use ntest::timeout;
use socket2::SockRef;
#[cfg(any(target_os = "linux", target_os = "android"))]
use socket2::{Domain, SockAddr, Socket, Type};
use super::{DELAY, after, counting_polls, in_both_modes, pattern};
use crate::{
Local, Mode, Runtime, Shared, SharedRuntime,
net::unix::{Incoming, UnixDatagram, UnixListener, UnixStream},
};
in_both_modes! {
fn a_connection_echoes_both_ways<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("echo");
let path = directory.socket("listener");
let listener = UnixListener::bind(&runtime, &path).unwrap();
runtime.block_on(async {
let mut client = UnixStream::connect(&runtime, &path).await.unwrap();
let (mut server, peer) = listener.accept().await.unwrap();
client.write_all(b"ping").await.unwrap();
let mut received = [0; 4];
server.read_exact(&mut received).await.unwrap();
assert_eq!(&received, b"ping");
server.write_all(b"pong").await.unwrap();
client.read_exact(&mut received).await.unwrap();
assert_eq!(&received, b"pong");
assert_eq!(listener.local_addr().unwrap().as_pathname(), Some(path.as_path()));
assert_eq!(client.peer_addr().unwrap().as_pathname(), Some(path.as_path()));
assert_eq!(server.local_addr().unwrap().as_pathname(), Some(path.as_path()));
assert!(client.local_addr().unwrap().is_unnamed());
assert!(server.peer_addr().unwrap().is_unnamed());
assert!(peer.is_unnamed());
});
}
}
in_both_modes! {
fn a_connection_to_a_missing_file_fails_with_not_found<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("missing");
let error = runtime
.block_on(UnixStream::connect(&runtime, directory.socket("nobody")))
.unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::NotFound);
}
}
in_both_modes! {
fn a_connection_to_a_socket_nobody_listens_at_fails_with_connection_refused<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("refused");
let path = directory.socket("gone");
drop(std::os::unix::net::UnixListener::bind(&path).unwrap());
assert!(path.exists(), "the listener took its file with it");
let error = runtime
.block_on(UnixStream::connect(&runtime, &path))
.unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::ConnectionRefused);
}
}
in_both_modes! {
fn a_path_too_long_for_a_socket_is_invalid_input<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("long");
let path = directory.socket(&"x".repeat(300));
let error = UnixListener::bind(&runtime, &path).unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::InvalidInput);
let error = UnixDatagram::bind(&runtime, &path).unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::InvalidInput);
let error = runtime
.block_on(UnixStream::connect(&runtime, &path))
.unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::InvalidInput);
}
}
in_both_modes! {
fn a_path_with_a_zero_byte_in_it_is_invalid_input<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("zero-byte");
let path = directory.socket("listening");
let _listener = UnixListener::bind(&runtime, &path).unwrap();
let mut cut_short = path.clone().into_os_string();
cut_short.push("\0and more");
let error = runtime
.block_on(UnixStream::connect(&runtime, &cut_short))
.unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::InvalidInput);
}
}
in_both_modes! {
fn connect_addr_connects_to_the_address_of_a_listener<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("addr");
let path = directory.socket("listener");
let listener = UnixListener::bind(&runtime, &path).unwrap();
let address = listener.local_addr().unwrap();
runtime.block_on(async {
let mut client = UnixStream::connect_addr(&runtime, &address).await.unwrap();
let (mut server, _) = listener.accept().await.unwrap();
client.write_all(b"ping").await.unwrap();
let mut received = [0; 4];
server.read_exact(&mut received).await.unwrap();
assert_eq!(&received, b"ping");
server.write_all(b"pong").await.unwrap();
client.read_exact(&mut received).await.unwrap();
assert_eq!(&received, b"pong");
assert_eq!(client.peer_addr().unwrap().as_pathname(), Some(path.as_path()));
assert!(client.local_addr().unwrap().is_unnamed());
});
}
}
in_both_modes! {
fn connect_addr_to_a_path_with_no_socket_behind_it_reports_why<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("addr-gone");
let path = directory.socket("gone");
let listener = std::os::unix::net::UnixListener::bind(&path).unwrap();
let address = listener.local_addr().unwrap();
drop(listener);
assert!(path.exists(), "the listener took its file with it");
let error = runtime
.block_on(UnixStream::connect_addr(&runtime, &address))
.unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::ConnectionRefused);
std::fs::remove_file(&path).unwrap();
let error = runtime
.block_on(UnixStream::connect_addr(&runtime, &address))
.unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::NotFound);
}
}
in_both_modes! {
#[cfg(any(target_os = "linux", target_os = "android"))]
fn connect_addr_connects_to_an_abstract_name<M>() {
let runtime = Runtime::<M>::new().unwrap();
let name = abstract_name("echo");
let address = std::os::unix::net::SocketAddr::from_abstract_name(&name).unwrap();
let listener = std::os::unix::net::UnixListener::bind_addr(&address).unwrap();
runtime.block_on(async {
let mut client = UnixStream::connect_addr(&runtime, &address).await.unwrap();
let (accepted, _) = listener.accept().unwrap();
let mut server = UnixStream::from_std(&runtime, accepted).unwrap();
client.write_all(b"ping").await.unwrap();
let mut received = [0; 4];
server.read_exact(&mut received).await.unwrap();
assert_eq!(&received, b"ping");
server.write_all(b"pong").await.unwrap();
client.read_exact(&mut received).await.unwrap();
assert_eq!(&received, b"pong");
assert_eq!(client.peer_addr().unwrap().as_abstract_name(), Some(&name[..]));
assert!(client.peer_addr().unwrap().as_pathname().is_none());
});
}
}
in_both_modes! {
#[cfg(any(target_os = "linux", target_os = "android"))]
fn connect_addr_takes_a_zero_byte_inside_an_abstract_name_as_part_of_it<M>() {
let runtime = Runtime::<M>::new().unwrap();
let mut name = abstract_name("zero-byte");
let cut_short = std::os::unix::net::SocketAddr::from_abstract_name(&name).unwrap();
let cut_short_listener = std::os::unix::net::UnixListener::bind_addr(&cut_short).unwrap();
cut_short_listener.set_nonblocking(true).unwrap();
name.extend_from_slice(b"\0and more");
let address = std::os::unix::net::SocketAddr::from_abstract_name(&name).unwrap();
let listener = std::os::unix::net::UnixListener::bind_addr(&address).unwrap();
runtime.block_on(async {
let client = UnixStream::connect_addr(&runtime, &address).await.unwrap();
assert_eq!(client.peer_addr().unwrap().as_abstract_name(), Some(&name[..]));
listener.accept().unwrap();
let error = cut_short_listener.accept().unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::WouldBlock);
});
}
}
in_both_modes! {
#[cfg(any(target_os = "linux", target_os = "android"))]
fn a_connection_to_an_abstract_name_nobody_listens_at_fails_with_connection_refused<M>() {
let runtime = Runtime::<M>::new().unwrap();
let address = std::os::unix::net::SocketAddr::from_abstract_name(abstract_name("gone"))
.unwrap();
let error = runtime
.block_on(UnixStream::connect_addr(&runtime, &address))
.unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::ConnectionRefused);
}
}
in_both_modes! {
fn connect_addr_with_an_unnamed_address_is_invalid_input<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (stream, _peer) = std::os::unix::net::UnixStream::pair().unwrap();
let unnamed = stream.local_addr().unwrap();
assert!(unnamed.is_unnamed());
let error = runtime
.block_on(UnixStream::connect_addr(&runtime, &unnamed))
.unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::InvalidInput);
}
}
in_both_modes! {
#[cfg(any(target_os = "linux", target_os = "android"))]
fn a_full_backlog_is_waited_out<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("backlog");
let path = directory.socket("listener");
let listener = Socket::new(Domain::UNIX, Type::STREAM, None).unwrap();
listener.bind(&SockAddr::unix(&path).unwrap()).unwrap();
listener.listen(0).unwrap();
runtime.block_on(async {
let _queued = UnixStream::connect(&runtime, &path).await.unwrap();
let mut pending = pin!(UnixStream::connect(&runtime, &path));
assert!(
poll_immediate(pending.as_mut()).await.is_none(),
"the connection was reported before the listener had room for it",
);
let accepting = thread::spawn(move || {
thread::sleep(DELAY);
listener.accept().unwrap();
listener.accept().unwrap();
});
let stream = pending.await.unwrap();
accepting.join().unwrap();
assert_eq!(stream.peer_addr().unwrap().as_pathname(), Some(path.as_path()));
});
}
}
in_both_modes! {
fn an_accepted_stream_is_non_blocking<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("accepted");
let path = directory.socket("listener");
let listener = UnixListener::bind(&runtime, &path).unwrap();
runtime.block_on(async {
let mut client = UnixStream::connect(&runtime, &path).await.unwrap();
let (mut server, _) = listener.accept().await.unwrap();
let mut received = [0; 4];
let (written, read) = join(
after(&runtime, client.write_all(b"late")),
server.read_exact(&mut received),
)
.await;
written.unwrap();
read.unwrap();
assert_eq!(&received, b"late");
});
}
}
in_both_modes! {
fn a_pair_of_streams_echoes_both_ways_and_is_non_blocking<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (mut left, mut right) = UnixStream::pair(&runtime).unwrap();
runtime.block_on(async {
left.write_all(b"ping").await.unwrap();
let mut received = [0; 4];
right.read_exact(&mut received).await.unwrap();
assert_eq!(&received, b"ping");
right.write_all(b"pong").await.unwrap();
left.read_exact(&mut received).await.unwrap();
assert_eq!(&received, b"pong");
let (written, read) = join(
after(&runtime, left.write_all(b"late")),
right.read_exact(&mut received),
)
.await;
written.unwrap();
read.unwrap();
assert_eq!(&received, b"late");
let (written, read) = join(
after(&runtime, right.write_all(b"back")),
left.read_exact(&mut received),
)
.await;
written.unwrap();
read.unwrap();
assert_eq!(&received, b"back");
});
}
}
in_both_modes! {
fn from_std_makes_a_blocking_listener_non_blocking<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("std-listener");
let path = directory.socket("listener");
let std_listener = std::os::unix::net::UnixListener::bind(&path).unwrap();
let listener = UnixListener::from_std(&runtime, std_listener).unwrap();
runtime.block_on(async {
let (accepted, client) = join(
listener.accept(),
after(&runtime, UnixStream::connect(&runtime, &path)),
)
.await;
let (_server, peer) = accepted.unwrap();
assert!(peer.is_unnamed());
let client = client.unwrap();
assert_eq!(client.peer_addr().unwrap().as_pathname(), Some(path.as_path()));
});
}
}
in_both_modes! {
fn from_std_makes_a_blocking_stream_non_blocking<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (std_stream, mut peer) = std::os::unix::net::UnixStream::pair().unwrap();
let mut stream = UnixStream::from_std(&runtime, std_stream).unwrap();
runtime.block_on(async {
let mut received = [0; 4];
let (written, read) = join(
after(&runtime, async { peer.write_all(b"late") }),
stream.read_exact(&mut received),
)
.await;
written.unwrap();
read.unwrap();
assert_eq!(&received, b"late");
});
}
}
in_both_modes! {
fn from_std_makes_a_blocking_datagram_socket_non_blocking<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (std_socket, peer) = std::os::unix::net::UnixDatagram::pair().unwrap();
let socket = UnixDatagram::from_std(&runtime, std_socket).unwrap();
runtime.block_on(async {
let mut received = [0; 4];
let (sent, len) = join(
after(&runtime, async { peer.send(b"late") }),
socket.recv(&mut received),
)
.await;
assert_eq!(sent.unwrap(), 4);
let len = len.unwrap();
assert_eq!(&received[..len], b"late");
});
}
}
in_both_modes! {
fn into_std_hands_back_the_same_connection_in_non_blocking_mode<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("into-std");
runtime.block_on(async {
let (client, mut server) = connected(&runtime, &directory).await;
let mut std_client = client.into_std();
assert!(std_client.local_addr().unwrap().is_unnamed());
assert_eq!(
std_client.peer_addr().unwrap().as_pathname(),
Some(directory.socket("connected").as_path()),
);
let error = std_client.read(&mut [0; 1]).unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::WouldBlock);
server.write_all(b"ping").await.unwrap();
std_client.set_nonblocking(false).unwrap();
let mut received = [0; 4];
std_client.read_exact(&mut received).unwrap();
assert_eq!(&received, b"ping");
std_client.write_all(b"pong").unwrap();
server.read_exact(&mut received).await.unwrap();
assert_eq!(&received, b"pong");
});
}
}
in_both_modes! {
fn into_std_leaves_the_bytes_that_arrived_for_the_std_stream<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (client, mut server) = UnixStream::pair(&runtime).unwrap();
runtime.block_on(async {
server.write_all(b"early").await.unwrap();
let mut std_client = client.into_std();
let mut received = [0; 5];
std_client.read_exact(&mut received).unwrap();
assert_eq!(&received, b"early");
});
}
}
in_both_modes! {
fn a_socket_handed_back_by_into_std_can_be_taken_up_again<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("into-std-again");
runtime.block_on(async {
let (client, mut server) = connected(&runtime, &directory).await;
let std_client = client.into_std();
let mut client = UnixStream::from_std(&runtime, std_client).unwrap();
let mut received = [0; 4];
let (written, read) = join(
after(&runtime, server.write_all(b"late")),
client.read_exact(&mut received),
)
.await;
written.unwrap();
read.unwrap();
assert_eq!(&received, b"late");
client.write_all(b"back").await.unwrap();
server.read_exact(&mut received).await.unwrap();
assert_eq!(&received, b"back");
});
}
}
in_both_modes! {
fn close_makes_the_peer_read_the_end_of_the_stream<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("close");
runtime.block_on(async {
let (mut client, mut server) = connected(&runtime, &directory).await;
client.write_all(b"bye").await.unwrap();
client.close().await.unwrap();
let mut received = Vec::new();
server.read_to_end(&mut received).await.unwrap();
assert_eq!(received, b"bye");
server.write_all(b"ack").await.unwrap();
let mut answer = [0; 3];
client.read_exact(&mut answer).await.unwrap();
assert_eq!(&answer, b"ack");
client.close().await.unwrap();
});
}
}
in_both_modes! {
fn closing_through_a_reference_closes_the_stream_for_every_handle<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("reference");
runtime.block_on(async {
let (mut client, mut server) = connected(&runtime, &directory).await;
(&client).close().await.unwrap();
let error = client.write_all(b"too late").await.unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::BrokenPipe);
let mut received = Vec::new();
server.read_to_end(&mut received).await.unwrap();
assert!(received.is_empty());
});
}
}
in_both_modes! {
fn shutdown_of_the_write_half_makes_the_peer_read_the_end<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("shutdown");
runtime.block_on(async {
let (mut client, mut server) = connected(&runtime, &directory).await;
client.write_all(b"last words").await.unwrap();
client.shutdown(Shutdown::Write).unwrap();
let mut received = Vec::new();
server.read_to_end(&mut received).await.unwrap();
assert_eq!(received, b"last words");
});
}
}
in_both_modes! {
fn a_write_to_a_peer_that_has_gone_fails_with_broken_pipe<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("gone");
runtime.block_on(async {
let (mut client, server) = connected(&runtime, &directory).await;
drop(server);
let error = loop {
if let Err(error) = client.write_all(b"anybody there?").await {
break error;
}
};
assert_eq!(error.kind(), io::ErrorKind::BrokenPipe);
});
}
}
in_both_modes! {
fn a_reader_and_a_writer_share_a_stream<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("share");
runtime.block_on(async {
let (client, server) = connected(&runtime, &directory).await;
let data = pattern(1 << 20);
let writer = async {
(&client).write_all(&data).await.unwrap();
(&client).close().await.unwrap();
};
let reader = async {
let mut echoed = Vec::new();
(&client).read_to_end(&mut echoed).await.unwrap();
echoed
};
let echo = async {
copy(&server, &mut &server).await.unwrap();
(&server).close().await.unwrap();
};
let (((), echoed), ()) = join(join(writer, reader), echo).await;
assert_eq!(echoed.len(), data.len());
assert!(echoed == data, "the echo differs from what was sent");
});
}
}
in_both_modes! {
fn a_write_that_waits_resumes_once_the_peer_reads<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (std_stream, mut peer) = std::os::unix::net::UnixStream::pair().unwrap();
let chunk = pattern(64 * 1024);
runtime.block_on(async {
let stream = UnixStream::from_std(&runtime, std_stream).unwrap();
let mut writer = &stream;
let mut filled = 0;
poll_fn(|cx| loop {
match Pin::new(&mut writer).poll_write(cx, &chunk) {
Poll::Ready(written) => filled += written.unwrap(),
Poll::Pending => break Poll::Ready(()),
}
})
.await;
let total = filled + chunk.len();
let reading = thread::spawn(move || {
thread::sleep(DELAY);
peer.read_exact(&mut vec![0; total]).unwrap();
});
writer.write_all(&chunk).await.unwrap();
reading.join().unwrap();
});
}
}
in_both_modes! {
fn a_read_an_accept_or_a_receive_that_waits_does_not_spin<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("spin");
let path = directory.socket("listener");
let listener = UnixListener::bind(&runtime, &path).unwrap();
let (mut client, mut server) = UnixStream::pair(&runtime).unwrap();
let (sender, receiver) = UnixDatagram::pair(&runtime).unwrap();
runtime.block_on(async {
let mut buffer = [0; 1];
let read_polls = Cell::new(0);
let (read, written) = join(
counting_polls(&read_polls, server.read_exact(&mut buffer)),
after(&runtime, client.write_all(b"1")),
)
.await;
read.unwrap();
written.unwrap();
assert!(read_polls.get() <= 10, "the read was polled {} times", read_polls.get());
let accept_polls = Cell::new(0);
let (accepted, connection) = join(
counting_polls(&accept_polls, listener.accept()),
after(&runtime, UnixStream::connect(&runtime, &path)),
)
.await;
accepted.unwrap();
connection.unwrap();
assert!(
accept_polls.get() <= 10,
"the accept was polled {} times",
accept_polls.get(),
);
let receive_polls = Cell::new(0);
let (received, sent) = join(
counting_polls(&receive_polls, receiver.recv(&mut buffer)),
after(&runtime, sender.send(b"2")),
)
.await;
received.unwrap();
sent.unwrap();
assert!(
receive_polls.get() <= 10,
"the `recv` was polled {} times",
receive_polls.get(),
);
let receive_from_polls = Cell::new(0);
let (received, sent) = join(
counting_polls(&receive_from_polls, receiver.recv_from(&mut buffer)),
after(&runtime, sender.send(b"3")),
)
.await;
received.unwrap();
sent.unwrap();
assert!(
receive_from_polls.get() <= 10,
"the `recv_from` was polled {} times",
receive_from_polls.get(),
);
});
}
}
in_both_modes! {
fn a_dropped_accept_loses_no_connection<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("dropped");
let path = directory.socket("listener");
let listener = UnixListener::bind(&runtime, &path).unwrap();
runtime.block_on(async {
let mut client = {
let mut accept = pin!(listener.accept());
assert!(poll_immediate(accept.as_mut()).await.is_none());
let client = UnixStream::connect(&runtime, &path).await.unwrap();
runtime.sleep(DELAY).await;
client
};
let (mut server, _) = listener.accept().await.unwrap();
client.write_all(b"here").await.unwrap();
let mut received = [0; 4];
server.read_exact(&mut received).await.unwrap();
assert_eq!(&received, b"here");
});
}
}
in_both_modes! {
fn incoming_yields_one_stream_per_connection<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("incoming");
let path = directory.socket("listener");
let listener = UnixListener::bind(&runtime, &path).unwrap();
runtime.block_on(async {
let mut incoming = listener.incoming();
assert!(poll_immediate(incoming.next()).await.is_none());
let (first_accepted, first) = join(
incoming.next(),
after(&runtime, UnixStream::connect(&runtime, &path)),
)
.await;
let mut first_accepted = first_accepted.expect("the stream never ends").unwrap();
let mut first = first.unwrap();
let mut second = UnixStream::connect(&runtime, &path).await.unwrap();
let mut third = UnixStream::connect(&runtime, &path).await.unwrap();
let mut second_accepted =
incoming.next().await.expect("the stream never ends").unwrap();
let mut third_accepted =
incoming.next().await.expect("the stream never ends").unwrap();
first.write_all(b"1").await.unwrap();
second.write_all(b"2").await.unwrap();
third.write_all(b"3").await.unwrap();
let mut tag = [0; 1];
first_accepted.read_exact(&mut tag).await.unwrap();
assert_eq!(&tag, b"1");
second_accepted.read_exact(&mut tag).await.unwrap();
assert_eq!(&tag, b"2");
third_accepted.read_exact(&mut tag).await.unwrap();
assert_eq!(&tag, b"3");
});
}
}
in_both_modes! {
fn datagrams_travel_between_bound_sockets<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("bound");
let first_path = directory.socket("first");
let second_path = directory.socket("second");
let first = UnixDatagram::bind(&runtime, &first_path).unwrap();
let second = UnixDatagram::bind(&runtime, &second_path).unwrap();
runtime.block_on(async {
assert_eq!(first.send_to(b"ping", &second_path).await.unwrap(), 4);
let mut received = [0; 16];
let (len, sender) = second.recv_from(&mut received).await.unwrap();
assert_eq!(&received[..len], b"ping");
assert_eq!(sender.as_pathname(), Some(first_path.as_path()));
let sender = sender.as_pathname().unwrap();
assert_eq!(second.send_to(b"pong", sender).await.unwrap(), 4);
let (len, sender) = first.recv_from(&mut received).await.unwrap();
assert_eq!(&received[..len], b"pong");
assert_eq!(sender.as_pathname(), Some(second_path.as_path()));
assert_eq!(first.local_addr().unwrap().as_pathname(), Some(first_path.as_path()));
assert_eq!(second.local_addr().unwrap().as_pathname(), Some(second_path.as_path()));
assert_eq!(first.peer_addr().unwrap_err().kind(), io::ErrorKind::NotConnected);
assert!(second.send(b"nowhere").await.is_err());
});
}
}
in_both_modes! {
fn a_connected_datagram_socket_sends_and_receives<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("connected");
let first_path = directory.socket("first");
let second_path = directory.socket("second");
let first = UnixDatagram::bind(&runtime, &first_path).unwrap();
let second = UnixDatagram::bind(&runtime, &second_path).unwrap();
first.connect(&second_path).unwrap();
second.connect(&first_path).unwrap();
runtime.block_on(async {
assert_eq!(first.send(b"ping").await.unwrap(), 4);
let mut received = [0; 16];
let len = second.recv(&mut received).await.unwrap();
assert_eq!(&received[..len], b"ping");
assert_eq!(second.send(b"pong").await.unwrap(), 4);
let len = first.recv(&mut received).await.unwrap();
assert_eq!(&received[..len], b"pong");
assert_eq!(first.peer_addr().unwrap().as_pathname(), Some(second_path.as_path()));
assert_eq!(second.peer_addr().unwrap().as_pathname(), Some(first_path.as_path()));
});
}
}
in_both_modes! {
fn an_unbound_socket_sends_to_a_bound_one<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("unbound");
let path = directory.socket("receiver");
let receiver = UnixDatagram::bind(&runtime, &path).unwrap();
let sender = UnixDatagram::unbound(&runtime).unwrap();
assert!(sender.local_addr().unwrap().is_unnamed());
assert_eq!(sender.peer_addr().unwrap_err().kind(), io::ErrorKind::NotConnected);
runtime.block_on(async {
assert_eq!(sender.send_to(b"hello", &path).await.unwrap(), 5);
let mut received = [0; 16];
let (len, from) = receiver.recv_from(&mut received).await.unwrap();
assert_eq!(&received[..len], b"hello");
assert!(from.is_unnamed());
});
}
}
in_both_modes! {
fn a_datagram_to_a_path_with_no_socket_fails<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("nobody");
let missing = directory.socket("missing");
let gone = directory.socket("gone");
drop(std::os::unix::net::UnixDatagram::bind(&gone).unwrap());
let socket = UnixDatagram::unbound(&runtime).unwrap();
runtime.block_on(async {
let error = socket.send_to(b"anybody?", &missing).await.unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::NotFound);
let error = socket.send_to(b"anybody?", &gone).await.unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::ConnectionRefused);
});
assert_eq!(socket.connect(&missing).unwrap_err().kind(), io::ErrorKind::NotFound);
assert_eq!(socket.connect(&gone).unwrap_err().kind(), io::ErrorKind::ConnectionRefused);
}
}
in_both_modes! {
fn a_datagram_pair_keeps_the_datagrams_apart<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (left, right) = UnixDatagram::pair(&runtime).unwrap();
runtime.block_on(async {
assert_eq!(left.send(b"one").await.unwrap(), 3);
assert_eq!(left.send(b"second").await.unwrap(), 6);
let mut received = [0; 16];
let len = right.recv(&mut received).await.unwrap();
assert_eq!(&received[..len], b"one");
let len = right.recv(&mut received).await.unwrap();
assert_eq!(&received[..len], b"second");
assert_eq!(right.send(b"back").await.unwrap(), 4);
let len = left.recv(&mut received).await.unwrap();
assert_eq!(&received[..len], b"back");
assert_eq!(left.send(b"truncated").await.unwrap(), 9);
assert_eq!(left.send(b"next").await.unwrap(), 4);
let mut short = [0; 4];
let len = right.recv(&mut short).await.unwrap();
assert_eq!(&short[..len], b"trun");
let len = right.recv(&mut received).await.unwrap();
assert_eq!(&received[..len], b"next");
});
}
}
in_both_modes! {
fn a_receive_waits_for_a_delayed_send<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (left, right) = UnixDatagram::pair(&runtime).unwrap();
runtime.block_on(async {
let mut received = [0; 16];
let (sent, len) = join(
after(&runtime, left.send(b"late")),
right.recv(&mut received),
)
.await;
assert_eq!(sent.unwrap(), 4);
let len = len.unwrap();
assert_eq!(&received[..len], b"late");
let (sent, from) = join(
after(&runtime, left.send(b"later")),
right.recv_from(&mut received),
)
.await;
assert_eq!(sent.unwrap(), 5);
let (len, from) = from.unwrap();
assert_eq!(&received[..len], b"later");
assert!(from.is_unnamed());
});
}
}
in_both_modes! {
#[cfg(any(target_os = "linux", target_os = "android"))]
fn a_send_to_a_full_receiver_waits_without_spinning<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("full");
let path = directory.socket("receiver");
let receiver = UnixDatagram::bind(&runtime, &path).unwrap();
fill_the_queue(&path);
let sender = UnixDatagram::unbound(&runtime).unwrap();
runtime.block_on(async {
let polls = Cell::new(0);
let made_room = Cell::new(false);
let send = async {
let sent = sender.send_to(b"last", &path).await;
assert!(made_room.get(), "the datagram was sent before the receiver made room");
sent
};
let receive = async {
runtime.sleep(2 * DELAY).await;
let mut received = [0; 16];
receiver.recv(&mut received).await.unwrap();
made_room.set(true);
};
let (sent, ()) = join(counting_polls(&polls, send), receive).await;
assert_eq!(sent.unwrap(), 4);
assert!(polls.get() <= 20, "the `send_to` was polled {} times", polls.get());
});
}
}
in_both_modes! {
fn shutdown_of_the_write_half_ends_the_sending_of_datagrams<M>() {
let runtime = Runtime::<M>::new().unwrap();
let (left, right) = UnixDatagram::pair(&runtime).unwrap();
left.shutdown(Shutdown::Write).unwrap();
runtime.block_on(async {
let error = left.send(b"too late").await.unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::BrokenPipe);
right.send(b"still open").await.unwrap();
let mut received = [0; 16];
let len = left.recv(&mut received).await.unwrap();
assert_eq!(&received[..len], b"still open");
});
}
}
#[test]
#[timeout(15000)]
fn a_shared_stream_crosses_threads() {
let runtime = SharedRuntime::new().unwrap();
let directory = Directory::new("crossing");
let (client, mut server) = runtime.block_on(connected(&runtime, &directory));
let user = thread::spawn({
let runtime = runtime.clone();
move || {
runtime.block_on(async move {
let mut client = client;
let mut request = [0; 5];
client.read_exact(&mut request).await.unwrap();
client
.write_all(&request.to_ascii_uppercase())
.await
.unwrap();
});
}
});
thread::sleep(DELAY);
block_on(server.write_all(b"hello")).unwrap();
user.join().unwrap();
let mut response = [0; 5];
runtime.block_on(server.read_exact(&mut response)).unwrap();
assert_eq!(&response, b"HELLO");
}
#[test]
#[timeout(15000)]
fn shared_tasks_use_sockets() {
let runtime = SharedRuntime::new().unwrap();
let directory = Directory::new("tasks");
let path = directory.socket("listener");
let listener = UnixListener::bind(&runtime, &path).unwrap();
runtime.block_on(async {
let server = runtime.spawn("an echo server", async move {
let (stream, _) = listener.accept().await.unwrap();
copy(&stream, &mut &stream).await.unwrap();
(&stream).close().await.unwrap();
});
let client = runtime.spawn("a client", {
let runtime = runtime.clone();
async move {
let mut stream = UnixStream::connect(&runtime, path).await.unwrap();
stream.write_all(b"hello").await.unwrap();
stream.close().await.unwrap();
let mut echoed = Vec::new();
stream.read_to_end(&mut echoed).await.unwrap();
echoed
}
});
assert_eq!(client.await.unwrap(), b"hello");
server.await.unwrap();
});
}
#[test]
#[timeout(15000)]
fn several_tasks_accept_from_one_listener() {
let runtime = SharedRuntime::new().unwrap();
let directory = Directory::new("accepts");
let path = directory.socket("listener");
let listener = Arc::new(UnixListener::bind(&runtime, &path).unwrap());
runtime.block_on(async {
let acceptors: Vec<_> = (0..3)
.map(|_| {
let listener = listener.clone();
runtime.spawn("an acceptor", async move {
let (stream, _) = listener.accept().await.unwrap();
stream
})
})
.collect();
runtime.sleep(DELAY).await;
let mut clients = Vec::new();
for id in 0..3u8 {
let mut client = UnixStream::connect(&runtime, &path).await.unwrap();
client.write_all(&[id]).await.unwrap();
clients.push(client);
}
let mut ids = Vec::new();
for acceptor in acceptors {
let mut stream = acceptor.await.unwrap();
let mut id = [0];
stream.read_exact(&mut id).await.unwrap();
ids.push(id[0]);
}
ids.sort();
assert_eq!(ids, [0, 1, 2]);
});
}
#[test]
#[timeout(15000)]
fn accept_and_incoming_wait_together() {
let runtime = SharedRuntime::new().unwrap();
let directory = Directory::new("together");
let path = directory.socket("listener");
let listener = Arc::new(UnixListener::bind(&runtime, &path).unwrap());
runtime.block_on(async {
let acceptor = runtime.spawn("an acceptor", {
let listener = listener.clone();
async move {
let (stream, _) = listener.accept().await.unwrap();
stream
}
});
let streamer = runtime.spawn("an incoming stream", {
let listener = listener.clone();
async move {
listener
.incoming()
.next()
.await
.expect("the stream never ends")
.unwrap()
}
});
runtime.sleep(DELAY).await;
let mut clients = Vec::new();
for id in 0..2u8 {
let mut client = UnixStream::connect(&runtime, &path).await.unwrap();
client.write_all(&[id]).await.unwrap();
clients.push(client);
}
let mut ids = Vec::new();
for mut stream in [acceptor.await.unwrap(), streamer.await.unwrap()] {
let mut id = [0];
stream.read_exact(&mut id).await.unwrap();
ids.push(id[0]);
}
ids.sort();
assert_eq!(ids, [0, 1]);
});
}
#[test]
#[timeout(15000)]
fn several_tasks_receive_on_one_datagram_socket() {
let runtime = SharedRuntime::new().unwrap();
let (client, server) = UnixDatagram::pair(&runtime).unwrap();
let server = Arc::new(server);
runtime.block_on(async {
let receivers: Vec<_> = (0..2)
.map(|_| {
let server = server.clone();
runtime.spawn("a receiver", async move {
let mut buffer = [0; 16];
let (len, _) = server.recv_from(&mut buffer).await.unwrap();
buffer[..len].to_vec()
})
})
.collect();
runtime.sleep(DELAY).await;
client.send(b"first").await.unwrap();
client.send(b"second").await.unwrap();
let mut received = Vec::new();
for receiver in receivers {
received.push(receiver.await.unwrap());
}
received.sort();
assert_eq!(received, [b"first".to_vec(), b"second".to_vec()]);
});
}
in_both_modes! {
fn sockets_print_and_expose_the_descriptors_of_the_std_sockets_they_wrap<M>() {
let runtime = Runtime::<M>::new().unwrap();
let directory = Directory::new("debug");
let listener_path = directory.socket("listener");
let datagram_path = directory.socket("datagram");
let std_listener = std::os::unix::net::UnixListener::bind(&listener_path).unwrap();
let std_stream = std::os::unix::net::UnixStream::connect(&listener_path).unwrap();
let std_datagram = std::os::unix::net::UnixDatagram::bind(&datagram_path).unwrap();
let printed_listener = format!("{std_listener:?}");
let printed_stream = format!("{std_stream:?}");
let printed_datagram = format!("{std_datagram:?}");
let raw_listener = std_listener.as_raw_fd();
let raw_stream = std_stream.as_raw_fd();
let raw_datagram = std_datagram.as_raw_fd();
let listener = UnixListener::from_std(&runtime, std_listener).unwrap();
let stream = UnixStream::from_std(&runtime, std_stream).unwrap();
let datagram = UnixDatagram::from_std(&runtime, std_datagram).unwrap();
assert_eq!(format!("{listener:?}"), printed_listener);
assert_eq!(format!("{stream:?}"), printed_stream);
assert_eq!(format!("{datagram:?}"), printed_datagram);
assert_eq!(
format!("{:?}", listener.incoming()),
format!("Incoming {{ listener: {printed_listener} }}"),
);
assert_eq!(listener.as_raw_fd(), raw_listener);
assert_eq!(stream.as_raw_fd(), raw_stream);
assert_eq!(datagram.as_raw_fd(), raw_datagram);
let listener_address = SockRef::from(&listener).local_addr().unwrap();
assert_eq!(listener_address.as_pathname(), Some(listener_path.as_path()));
let stream_peer = SockRef::from(&stream).peer_addr().unwrap();
assert_eq!(stream_peer.as_pathname(), Some(listener_path.as_path()));
let datagram_address = SockRef::from(&datagram).local_addr().unwrap();
assert_eq!(datagram_address.as_pathname(), Some(datagram_path.as_path()));
}
}
#[test]
#[timeout(15000)]
fn shared_sockets_are_send_sync_and_unpin() {
fn sent_and_shared<T>()
where
T: Send + Sync + Unpin,
{
}
sent_and_shared::<UnixListener<Shared>>();
sent_and_shared::<UnixStream<Shared>>();
sent_and_shared::<UnixDatagram<Shared>>();
sent_and_shared::<Incoming<'static, Shared>>();
}
#[test]
#[timeout(15000)]
fn local_sockets_are_unpin() {
fn unpin<T>()
where
T: Unpin,
{
}
unpin::<UnixListener<Local>>();
unpin::<UnixStream<Local>>();
unpin::<UnixDatagram<Local>>();
unpin::<Incoming<'static, Local>>();
}
async fn connected<M>(runtime: &Runtime<M>, directory: &Directory) -> (UnixStream<M>, UnixStream<M>)
where
M: Mode,
{
let path = directory.socket("connected");
let listener = UnixListener::bind(runtime, &path).unwrap();
let client = UnixStream::connect(runtime, &path).await.unwrap();
let (server, _) = listener.accept().await.unwrap();
(client, server)
}
#[cfg(any(target_os = "linux", target_os = "android"))]
fn fill_the_queue(path: &std::path::Path) {
for _ in 0..100_000 {
let sender = std::os::unix::net::UnixDatagram::unbound().unwrap();
sender.set_nonblocking(true).unwrap();
match sender.send_to(b"filler", path) {
Ok(_) => {}
Err(e) if e.kind() == io::ErrorKind::WouldBlock => return,
Err(e) => panic!("a datagram to the receiver failed: {e}"),
}
}
panic!("the receiver's queue never filled up");
}
#[cfg(any(target_os = "linux", target_os = "android"))]
fn abstract_name(test: &str) -> Vec<u8> {
static MADE: AtomicUsize = AtomicUsize::new(0);
format!(
"zruntime-unix-{}-{test}-{}",
std::process::id(),
MADE.fetch_add(1, Ordering::Relaxed),
)
.into_bytes()
}
struct Directory {
path: PathBuf,
}
impl Directory {
fn new(test: &str) -> Self {
static MADE: AtomicUsize = AtomicUsize::new(0);
let path = std::env::temp_dir().join(format!(
"zruntime-unix-{}-{test}-{}",
std::process::id(),
MADE.fetch_add(1, Ordering::Relaxed),
));
let _ = std::fs::remove_dir_all(&path);
std::fs::create_dir(&path).expect("a directory of its own can be made");
Self { path }
}
fn socket(&self, name: &str) -> PathBuf {
self.path.join(name)
}
}
impl Drop for Directory {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.path);
}
}