222 lines
8.1 KiB
Rust
222 lines
8.1 KiB
Rust
use dever_runtime::{
|
|
net::{self, Listener, Socket},
|
|
task::{Limits, Runtime, Scope},
|
|
};
|
|
use std::{
|
|
future::{Future, poll_fn},
|
|
task::Poll,
|
|
};
|
|
|
|
#[cfg(feature = "http")]
|
|
#[path = "net/performance.rs"]
|
|
mod performance;
|
|
|
|
fn runtime() -> Runtime {
|
|
Runtime::new(Limits {
|
|
threads: 2,
|
|
workers: 2,
|
|
tasks: 8,
|
|
})
|
|
.unwrap()
|
|
}
|
|
|
|
async fn pair(scope: &Scope<'_>) -> (Socket, Socket) {
|
|
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
|
|
let client = net::connect_timeout("127.0.0.1", listener.port().unwrap(), 1000)
|
|
.await
|
|
.unwrap();
|
|
let server = listener.accept().await.unwrap();
|
|
(client, server)
|
|
}
|
|
|
|
async fn pending(future: std::pin::Pin<&mut impl Future>) {
|
|
let mut future = future;
|
|
poll_fn(|context| {
|
|
assert!(future.as_mut().poll(context).is_pending());
|
|
Poll::Ready(())
|
|
})
|
|
.await;
|
|
}
|
|
|
|
#[test]
|
|
fn dns_connect_bidirectional_io_and_shared_close_use_the_runtime() {
|
|
runtime()
|
|
.run(async |scope| {
|
|
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
|
|
let port = listener.port().unwrap();
|
|
assert!((1..=65535).contains(&port));
|
|
let client = net::connect("localhost", port, scope).await.unwrap();
|
|
let server = listener.accept().await.unwrap();
|
|
client.write(b"abc").await.unwrap();
|
|
assert_eq!(server.read(2).await.unwrap(), Some(b"ab".to_vec()));
|
|
assert_eq!(server.read(2).await.unwrap(), Some(b"c".to_vec()));
|
|
server.write(b"reply").await.unwrap();
|
|
assert_eq!(client.read(5).await.unwrap(), Some(b"reply".to_vec()));
|
|
let alias = client.clone();
|
|
alias.close().unwrap();
|
|
assert!(client.write(b"closed").await.is_err());
|
|
assert!(client.read(1).await.is_err());
|
|
assert!(client.close().is_err());
|
|
assert_eq!(server.read(1).await.unwrap(), None);
|
|
listener.close().unwrap();
|
|
})
|
|
.unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn close_wakes_read_lock_waiters_and_pending_accepts() {
|
|
runtime()
|
|
.run(async |scope| {
|
|
let (client, _server) = pair(scope).await;
|
|
let mut first = std::pin::pin!(client.read(1));
|
|
let mut second = std::pin::pin!(client.read(1));
|
|
pending(first.as_mut()).await;
|
|
pending(second.as_mut()).await;
|
|
client.close().unwrap();
|
|
assert!(first.await.is_err());
|
|
assert!(second.await.is_err());
|
|
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
|
|
let mut accepted = std::pin::pin!(listener.accept());
|
|
pending(accepted.as_mut()).await;
|
|
listener.clone().close().unwrap();
|
|
assert!(accepted.await.is_err());
|
|
assert!(listener.port().is_err());
|
|
})
|
|
.unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn read_timeout_includes_lock_waiting_and_does_not_consume_future_data() {
|
|
runtime()
|
|
.run(async |scope| {
|
|
let (client, server) = pair(scope).await;
|
|
{
|
|
let mut first = std::pin::pin!(client.read(1));
|
|
pending(first.as_mut()).await;
|
|
client.timeout(10).unwrap();
|
|
assert_eq!(
|
|
client.read(1).await.unwrap_err(),
|
|
"network operation timed out"
|
|
);
|
|
}
|
|
client.timeout(1000).unwrap();
|
|
server.write(b"x").await.unwrap();
|
|
assert_eq!(client.read(1).await.unwrap(), Some(b"x".to_vec()));
|
|
assert!(client.timeout(0).is_err());
|
|
assert!(client.read(0).await.is_err());
|
|
})
|
|
.unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn cancelled_partial_write_closes_but_cancelled_queued_write_preserves_socket() {
|
|
runtime()
|
|
.run(async |scope| {
|
|
let (client, server) = pair(scope).await;
|
|
let writer = client.clone();
|
|
let first = scope
|
|
.spawn_leaf(move || async move { writer.write(&vec![7; 32 * 1024 * 1024]).await })
|
|
.unwrap();
|
|
assert_eq!(server.read(1).await.unwrap(), Some(vec![7]));
|
|
let writer = client.clone();
|
|
let (started, ready) = tokio::sync::oneshot::channel();
|
|
let queued = scope
|
|
.spawn_leaf(move || async move {
|
|
let mut write = std::pin::pin!(writer.write(b"queued"));
|
|
pending(write.as_mut()).await;
|
|
started.send(()).unwrap();
|
|
write.await
|
|
})
|
|
.unwrap();
|
|
ready.await.unwrap();
|
|
assert!(queued.stop().await.unwrap().is_none());
|
|
client.timeout(1000).unwrap();
|
|
assert!(first.stop().await.unwrap().is_none());
|
|
assert!(client.timeout(1000).is_err());
|
|
server.timeout(1000).unwrap();
|
|
while server.read(65536).await.unwrap().is_some() {}
|
|
})
|
|
.unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn chunks_close_preserves_the_external_socket_and_terminal_errors_occur_once() {
|
|
runtime()
|
|
.run(async |scope| {
|
|
let (client, server) = pair(scope).await;
|
|
let chunks = client.chunks(2, std::convert::identity).unwrap();
|
|
let mut waiting = std::pin::pin!(chunks.next());
|
|
pending(waiting.as_mut()).await;
|
|
chunks.close();
|
|
assert!(waiting.await.unwrap().is_none());
|
|
server.write(b"ab").await.unwrap();
|
|
assert_eq!(client.read(2).await.unwrap(), Some(b"ab".to_vec()));
|
|
let chunks = client.chunks(2, std::convert::identity).unwrap();
|
|
client.close().unwrap();
|
|
assert!(chunks.next().await.unwrap().unwrap().is_err());
|
|
assert!(chunks.next().await.unwrap().is_none());
|
|
assert!(client.chunks(2, std::convert::identity).is_err());
|
|
})
|
|
.unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn connection_stream_is_lazy_and_closing_it_preserves_the_listener() {
|
|
runtime()
|
|
.run(async |scope| {
|
|
let listener: Listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
|
|
let connections = listener.connections(std::convert::identity);
|
|
connections.close();
|
|
let _client = net::connect_timeout("127.0.0.1", listener.port().unwrap(), 1000)
|
|
.await
|
|
.unwrap();
|
|
let _server = listener.accept().await.unwrap();
|
|
let connections = listener.connections(std::convert::identity);
|
|
listener.close().unwrap();
|
|
assert!(connections.next().await.unwrap().unwrap().is_err());
|
|
assert!(connections.next().await.unwrap().is_none());
|
|
})
|
|
.unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_network_arguments_fail_before_waiting() {
|
|
runtime()
|
|
.run(async |scope| {
|
|
assert!(net::listen("127.0.0.1", -1, scope).await.is_err());
|
|
assert!(net::connect("127.0.0.1", 65536, scope).await.is_err());
|
|
assert_eq!(
|
|
net::connect_timeout("localhost", 80, 1000)
|
|
.await
|
|
.unwrap_err(),
|
|
"timed connect requires a numeric IP address"
|
|
);
|
|
assert!(net::connect_timeout("127.0.0.1", 80, 0).await.is_err());
|
|
})
|
|
.unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn short_reads_exact_limits_and_allocation_failure_preserve_remaining_bytes() {
|
|
runtime()
|
|
.run(async |scope| {
|
|
let (client, server) = pair(scope).await;
|
|
client.timeout(1000).unwrap();
|
|
server.write(&[0, 255, 1, 0, 2, 128, 3]).await.unwrap();
|
|
let mut received = Vec::new();
|
|
while received.len() != 7 {
|
|
let bytes = client.read(3).await.unwrap().unwrap();
|
|
assert!(!bytes.is_empty() && bytes.len() <= 3);
|
|
received.extend(bytes);
|
|
}
|
|
assert_eq!(received, [0, 255, 1, 0, 2, 128, 3]);
|
|
server.write(b"x").await.unwrap();
|
|
assert!(client.read(i64::MAX).await.is_err());
|
|
assert_eq!(client.read(1_048_576).await.unwrap(), Some(b"x".to_vec()));
|
|
server.close().unwrap();
|
|
assert_eq!(client.read(1_048_576).await.unwrap(), None);
|
|
assert_eq!(client.read(3).await.unwrap(), None);
|
|
})
|
|
.unwrap();
|
|
}
|