Initial Dever 0.0.1

This commit is contained in:
Codex committed 2026-11-03 03:32:07 +00:00
commit 81233d098c
1479 files changed
+163782

No files matched your search

@@ -0,0 +1,325 @@
use super::*;
use futures_util::{SinkExt, StreamExt};
use std::sync::atomic::{AtomicUsize, Ordering};
use tokio::sync::oneshot;
#[test]
fn websocket_client_scope_ends_all_aliases_and_timeout_stream_fails_once() {
runtime()
.run(async |scope| {
let (listener, server) = server(scope).await;
let port = listener.port().unwrap();
let owner = scope
.spawn(move |child| async move {
ws::connect("127.0.0.1", port, "/", &limits(), &child)
.await
.unwrap()
})
.unwrap();
let escaped = owner.wait().await.unwrap();
assert!(escaped.send(Message::Text("closed".into())).await.is_err());
let socket = ws::connect(
"127.0.0.1",
port,
"/",
&ws::Limits {
idle_ms: 25,
..limits()
},
scope,
)
.await
.unwrap();
let stream = socket.messages(|message| message);
let alias = stream.clone();
let failure = stream.next().await.unwrap().unwrap().unwrap_err();
assert!(failure.contains("timed out"));
assert!(alias.next().await.unwrap().is_none());
assert!(socket.send(Message::Text("closed".into())).await.is_err());
listener.close().unwrap();
server.wait().await.unwrap().unwrap();
})
.unwrap();
}
#[test]
fn websocket_listener_drain_keeps_session_and_parent_stop_cleans_handler() {
runtime()
.run(async |scope| {
let (listener, mut server) = server(scope).await;
let socket = ws::connect("127.0.0.1", listener.port().unwrap(), "/", &limits(), scope)
.await
.unwrap();
listener.close().unwrap();
tokio::select! {
_ = &mut server => panic!("server must retain its upgraded session"),
() = tokio::time::sleep(Duration::from_millis(25)) => (),
}
socket
.send(Message::Text("after listener close".into()))
.await
.unwrap();
assert_eq!(
socket.receive().await.unwrap(),
Some(Message::Text("after listener close".into()))
);
socket.close(1000, "").await.unwrap();
server.wait().await.unwrap().unwrap();
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
let incoming = listener.clone();
let active = Arc::new(AtomicUsize::new(0));
let running = active.clone();
let (sender, receiver) = oneshot::channel::<WebSocket>();
let sender = Arc::new(std::sync::Mutex::new(Some(sender)));
let server = scope
.spawn(move |child| async move {
http::serve_live(
&incoming,
&Limits::default(),
&http::LiveLimits::default(),
&child,
move |_, reply, scope| {
let sender = sender.clone();
let running = running.clone();
async move {
let socket = ws::accept(&reply, &limits(), &scope).await.unwrap();
running.fetch_add(1, Ordering::SeqCst);
let _running = Running(running);
let _ = sender.lock().unwrap().take().unwrap().send(socket.clone());
let _ = socket.receive().await;
Ok(())
}
},
)
.await
})
.unwrap();
let socket = ws::connect("127.0.0.1", listener.port().unwrap(), "/", &limits(), scope)
.await
.unwrap();
let escaped = receiver.await.unwrap();
server.stop().await.unwrap();
assert_eq!(active.load(Ordering::SeqCst), 0);
assert!(escaped.send(Message::Text("gone".into())).await.is_err());
assert!(socket.receive().await.is_err());
listener.close().unwrap();
})
.unwrap();
}
struct Running(Arc<AtomicUsize>);
impl Drop for Running {
fn drop(&mut self) {
self.0.fetch_sub(1, Ordering::SeqCst);
}
}
#[test]
fn websocket_owner_stop_wakes_receive_in_another_task() {
runtime()
.run(async |scope| {
let (listener, server) = server(scope).await;
let port = listener.port().unwrap();
let (sender, receiver) = oneshot::channel();
let owner = scope
.spawn(move |child| async move {
let socket = ws::connect("127.0.0.1", port, "/", &limits(), &child)
.await
.unwrap();
sender.send(socket).unwrap();
std::future::pending::<()>().await;
})
.unwrap();
let socket = receiver.await.unwrap();
let (started, ready) = oneshot::channel();
let reader = scope
.spawn_leaf(move || async move {
let receiving = socket.receive();
tokio::pin!(receiving);
assert!(futures_util::poll!(&mut receiving).is_pending());
started.send(()).unwrap();
tokio::time::timeout(Duration::from_millis(200), receiving)
.await
.unwrap()
.unwrap_err()
})
.unwrap();
ready.await.unwrap();
owner.stop().await.unwrap();
assert!(reader.wait().await.unwrap().contains("WebSocket is closed"));
listener.close().unwrap();
server.wait().await.unwrap().unwrap();
})
.unwrap();
}
#[test]
fn websocket_closing_message_stream_preserves_independent_socket_alias() {
runtime()
.run(async |scope| {
let (listener, server) = server(scope).await;
let socket = ws::connect("127.0.0.1", listener.port().unwrap(), "/", &limits(), scope)
.await
.unwrap();
let stream = socket.messages(|message| message);
stream.close();
assert!(stream.next().await.unwrap().is_none());
socket
.send(Message::Binary(vec![1, 2].into()))
.await
.unwrap();
assert_eq!(
socket.receive().await.unwrap(),
Some(Message::Binary(vec![1, 2].into()))
);
socket.close(1000, "").await.unwrap();
listener.close().unwrap();
server.wait().await.unwrap().unwrap();
})
.unwrap();
}
#[test]
fn websocket_cancelled_writer_closes_only_after_it_owns_the_direction() {
runtime()
.run(async |scope| {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let port = listener.local_addr().unwrap().port();
let (started, receiving) = oneshot::channel();
let (release, released) = oneshot::channel();
let peer = scope
.spawn_leaf(move || async move {
let (stream, _) = listener.accept().await.unwrap();
let wire = tokio_tungstenite::accept_async(stream).await.unwrap();
wire.get_ref().readable().await.unwrap();
let _ = started.send(());
let _ = released.await;
})
.unwrap();
let socket = ws::connect(
"127.0.0.1",
i64::from(port),
"/",
&ws::Limits {
message_bytes: 16 * 1024 * 1024,
write_ms: 1000,
..limits()
},
scope,
)
.await
.unwrap();
let sender = socket.clone();
let mut writing = scope
.spawn_leaf(move || async move {
sender
.send(Message::Binary(vec![1; 16 * 1024 * 1024].into()))
.await
})
.unwrap();
receiving.await.unwrap();
let waiting = socket.clone();
let waiter = scope
.spawn_leaf(
move || async move { waiting.send(Message::Text("queued".into())).await },
)
.unwrap();
tokio::time::sleep(Duration::from_millis(10)).await;
waiter.stop().await.unwrap();
assert!(
tokio::time::timeout(Duration::from_millis(10), &mut writing)
.await
.is_err()
);
writing.stop().await.unwrap();
assert!(socket.send(Message::Text("closed".into())).await.is_err());
release.send(()).unwrap();
peer.wait().await.unwrap();
})
.unwrap();
}
#[test]
fn websocket_close_keeps_the_handshake_when_a_reader_is_waiting() {
runtime()
.run(async |scope| {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let port = listener.local_addr().unwrap().port();
let (replied, reply) = oneshot::channel();
let peer = scope
.spawn_leaf(move || async move {
let (stream, _) = listener.accept().await.unwrap();
let mut wire = tokio_tungstenite::accept_async(stream).await.unwrap();
assert!(matches!(
wire.next().await,
Some(Ok(tokio_tungstenite::tungstenite::Message::Close(_)))
));
wire.flush().await.unwrap();
replied.send(()).unwrap();
})
.unwrap();
let socket = ws::connect("127.0.0.1", i64::from(port), "/", &limits(), scope)
.await
.unwrap();
let receiving = socket.receive();
tokio::pin!(receiving);
assert!(futures_util::poll!(&mut receiving).is_pending());
let closing = socket.close(1000, "");
tokio::pin!(closing);
assert!(futures_util::poll!(&mut closing).is_pending());
reply.await.unwrap();
tokio::time::sleep(Duration::from_millis(25)).await;
let received = receiving.await;
closing.await.unwrap();
assert!(received.unwrap_err().contains("WebSocket is closing"));
peer.wait().await.unwrap();
})
.unwrap();
}
#[test]
fn websocket_close_deadline_releases_all_aliases() {
runtime()
.run(async |scope| {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let port = listener.local_addr().unwrap().port();
let (release, released) = oneshot::channel();
let peer = scope
.spawn_leaf(move || async move {
let (stream, _) = listener.accept().await.unwrap();
let _wire = tokio_tungstenite::accept_async(stream).await.unwrap();
let _ = released.await;
})
.unwrap();
let socket = ws::connect(
"127.0.0.1",
i64::from(port),
"/",
&ws::Limits {
write_ms: 50,
..limits()
},
scope,
)
.await
.unwrap();
let alias = socket.clone();
let receiving = alias.receive();
tokio::pin!(receiving);
assert!(futures_util::poll!(&mut receiving).is_pending());
let (closed, received) = tokio::join!(socket.close(1000, ""), receiving);
assert!(closed.unwrap_err().contains("timed out"));
assert!(received.unwrap_err().contains("WebSocket is closing"));
assert!(
alias
.send(Message::Text("closed".into()))
.await
.unwrap_err()
.contains("WebSocket is closed")
);
release.send(()).unwrap();
peer.wait().await.unwrap();
})
.unwrap();
}
+242
View File
@@ -0,0 +1,242 @@
use super::*;
use dever_runtime::http::websocket::{self as ws, Message, WebSocket};
mod lifecycle;
mod protocol;
mod shared;
#[path = "../../../performance/websocket/shared/mod.rs"]
mod shared_reference;
fn limits() -> ws::Limits {
ws::Limits {
idle_ms: 1000,
write_ms: 1000,
..ws::Limits::default()
}
}
async fn echo(
_: Request,
reply: http::HttpReply,
scope: Arc<Scope<'static>>,
) -> Result<(), String> {
let socket = ws::accept(&reply, &limits(), &scope)
.await
.map_err(|error| error.to_string())?;
while let Ok(Some(message)) = socket.receive().await {
if matches!(message, Message::Text(_) | Message::Binary(_)) {
socket
.send(message)
.await
.map_err(|error| error.to_string())?;
}
}
Ok(())
}
async fn server(
scope: &Scope<'_>,
) -> (
net::Listener,
dever_runtime::task::Task<Result<(), http::ServeError>>,
) {
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
let incoming = listener.clone();
let task = scope
.spawn(move |child| async move {
http::serve_live(
&incoming,
&Limits::default(),
&http::LiveLimits::default(),
&child,
echo,
)
.await
})
.unwrap();
(listener, task)
}
#[test]
fn websocket_live_upgrade_echo_control_frames_and_close() {
runtime()
.run(async |scope| {
let (listener, server) = server(scope).await;
let socket = ws::connect(
"127.0.0.1",
listener.port().unwrap(),
"/echo?key=value",
&limits(),
scope,
)
.await
.unwrap();
for message in [
Message::Text("你好".into()),
Message::Binary(vec![0, 255, 17].into()),
] {
socket.send(message.clone()).await.unwrap();
assert_eq!(socket.receive().await.unwrap(), Some(message));
}
socket
.send(Message::Ping(b"ping".to_vec().into()))
.await
.unwrap();
assert_eq!(
socket.receive().await.unwrap(),
Some(Message::Pong(b"ping".to_vec().into()))
);
assert!(
socket
.send(Message::Ping(vec![0; 126].into()))
.await
.is_err()
);
assert!(socket.close(1005, "").await.is_err());
assert!(socket.close(1000, &"界".repeat(42)).await.is_err());
socket
.send(Message::Text("still open".into()))
.await
.unwrap();
assert_eq!(
socket.receive().await.unwrap(),
Some(Message::Text("still open".into()))
);
socket.close(1000, "结束").await.unwrap();
assert!(socket.receive().await.unwrap().is_none());
listener.close().unwrap();
server.wait().await.unwrap().unwrap();
})
.unwrap();
}
#[test]
fn websocket_aliases_send_whole_messages_while_receiving() {
runtime()
.run(async |scope| {
let (listener, server) = server(scope).await;
let socket = ws::connect("127.0.0.1", listener.port().unwrap(), "/", &limits(), scope)
.await
.unwrap();
let writers = futures_util::future::join_all((0u8..4).map(|writer| {
let socket = &socket;
async move {
for sequence in 0u8..16 {
let mut bytes = vec![writer; 65536];
bytes[1] = sequence;
socket.send(Message::Binary(bytes.into())).await.unwrap();
}
}
}));
let receiving = async {
let mut next = [0u8; 4];
for _ in 0..64 {
let Some(Message::Binary(bytes)) = socket.receive().await.unwrap() else {
panic!("expected a complete binary message");
};
assert_eq!(bytes.len(), 65536);
let writer = bytes[0];
assert_eq!(bytes[1], next[usize::from(writer)]);
assert!(bytes[2..].iter().all(|byte| *byte == writer));
next[usize::from(writer)] += 1;
}
assert_eq!(next, [16; 4]);
};
tokio::join!(writers, receiving);
socket.close(1000, "").await.unwrap();
listener.close().unwrap();
server.wait().await.unwrap().unwrap();
})
.unwrap();
}
#[test]
fn websocket_wss_uses_supplied_ca_and_dns_with_no_insecure_fallback() {
runtime()
.run(async |scope| {
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
let port = listener.port().unwrap();
let incoming = listener.clone();
let server = scope
.spawn(move |child| async move {
let tls = dever_runtime::tls::server(tls::CERTIFICATE, tls::KEY).unwrap();
http::serve_live_tls(
&incoming,
&Limits::default(),
&http::LiveLimits::default(),
&child,
&tls,
echo,
)
.await
})
.unwrap();
let url = format!("wss://localhost:{port}/echo");
assert!(
ws::open(&url, &dever_runtime::tls::system(), &limits(), scope)
.await
.is_err()
);
let trusted = dever_runtime::tls::client(tls::ROOT).unwrap();
let socket = ws::open(&url, &trusted, &limits(), scope).await.unwrap();
socket
.send(Message::Text("verified TLS".into()))
.await
.unwrap();
assert_eq!(
socket.receive().await.unwrap(),
Some(Message::Text("verified TLS".into()))
);
socket.close(1000, "").await.unwrap();
listener.close().unwrap();
server.wait().await.unwrap().unwrap();
})
.unwrap();
}
#[test]
fn websocket_invalid_inputs_fail_before_connecting() {
runtime()
.run(async |scope| {
for limit in [
ws::Limits {
message_bytes: 0,
..limits()
},
ws::Limits {
idle_ms: 0,
..limits()
},
ws::Limits {
write_ms: -1,
..limits()
},
] {
assert!(
ws::connect("127.0.0.1", 0, "/", &limit, scope)
.await
.is_err()
);
}
for url in [
"http://localhost/",
"ws://user:password@localhost/",
"ws://localhost/#fragment",
"ws://localhost:99999/",
] {
assert!(
ws::open(url, &dever_runtime::tls::system(), &limits(), scope)
.await
.is_err()
);
}
for target in ["http://localhost/", "*", "/#fragment"] {
assert!(
ws::connect("127.0.0.1", 0, target, &limits(), scope)
.await
.is_err()
);
}
})
.unwrap();
}
@@ -0,0 +1,243 @@
use super::*;
use futures_util::{SinkExt, StreamExt};
use tokio_tungstenite::tungstenite::{
Message as Frame,
protocol::frame::{
Frame as RawFrame,
coding::{Data, OpCode},
},
};
#[test]
#[ignore = "explicit release WebSocket handshake performance probe"]
fn websocket_upgrade_cost() {
runtime()
.run(async |scope| {
let (listener, serving) = server(scope).await;
let port = listener.port().unwrap();
println!("\ncase,sample,iterations,elapsed_ns");
for sample in 0..16 {
let started = std::time::Instant::now();
for _ in 0..20 {
let socket = ws::connect("127.0.0.1", port, "/echo", &limits(), scope)
.await
.unwrap();
socket.send(Message::Text("check".into())).await.unwrap();
assert_eq!(
socket.receive().await.unwrap(),
Some(Message::Text("check".into()))
);
socket.close(1000, "").await.unwrap();
}
let elapsed = started.elapsed().as_nanos();
if sample != 0 {
println!("websocket_upgrade,{},20,{elapsed}", sample - 1);
}
}
listener.close().unwrap();
serving.wait().await.unwrap().unwrap();
})
.unwrap();
}
#[test]
fn websocket_client_limits_handshake_without_limiting_later_frames() {
runtime()
.run(async |scope| {
for header_bytes in [60000, 66000] {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let port = listener.local_addr().unwrap().port();
let peer = scope
.spawn_leaf(move || async move {
let (stream, _) = listener.accept().await.unwrap();
let handshake = tokio_tungstenite::accept_hdr_async(
stream,
#[expect(clippy::result_large_err, reason = "Tungstenite 的握手回调要求直接返回 HTTP 错误响应")]
move |_: &tokio_tungstenite::tungstenite::handshake::server::Request,
mut response: tokio_tungstenite::tungstenite::handshake::server::Response| {
response.headers_mut().insert(
"x-padding",
"x".repeat(header_bytes).parse().unwrap(),
);
Ok(response)
},
)
.await;
if header_bytes == 60000 {
let mut wire = handshake.unwrap();
wire.send(Frame::Binary(vec![17; 70000].into())).await.unwrap();
assert!(matches!(wire.next().await, Some(Ok(Frame::Close(_)))));
wire.flush().await.unwrap();
}
})
.unwrap();
let connection = ws::connect("127.0.0.1", i64::from(port), "/", &limits(), scope).await;
if header_bytes == 60000 {
let socket = connection.unwrap();
assert_eq!(socket.receive().await.unwrap(), Some(Message::Binary(vec![17; 70000].into())));
socket.close(1000, "").await.unwrap();
} else {
assert!(connection.is_err());
}
peer.wait().await.unwrap();
}
})
.unwrap();
}
#[test]
fn websocket_client_reassembles_fragments_and_rejects_cumulative_limit_and_utf8() {
runtime()
.run(async |scope| {
for invalid in [false, true] {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let port = listener.local_addr().unwrap().port();
let peer = scope
.spawn_leaf(move || async move {
let (stream, _) = listener.accept().await.unwrap();
let mut wire = tokio_tungstenite::accept_async(stream).await.unwrap();
wire.send(Frame::Frame(RawFrame::message(
vec![0xe4, 0xb8],
OpCode::Data(Data::Text),
false,
)))
.await
.unwrap();
wire.send(Frame::Frame(RawFrame::message(
vec![0xad],
OpCode::Data(Data::Continue),
true,
)))
.await
.unwrap();
if invalid {
wire.send(Frame::Frame(RawFrame::message(
vec![0xff],
OpCode::Data(Data::Text),
true,
)))
.await
.unwrap();
} else {
wire.send(Frame::Frame(RawFrame::message(
b"12345".to_vec(),
OpCode::Data(Data::Text),
false,
)))
.await
.unwrap();
wire.send(Frame::Frame(RawFrame::message(
b"6789".to_vec(),
OpCode::Data(Data::Continue),
true,
)))
.await
.unwrap();
}
let _ = wire.next().await;
})
.unwrap();
let socket = ws::connect(
"127.0.0.1",
i64::from(port),
"/",
&ws::Limits {
message_bytes: 8,
..limits()
},
scope,
)
.await
.unwrap();
assert_eq!(
socket.receive().await.unwrap(),
Some(Message::Text("中".into()))
);
assert!(socket.receive().await.is_err());
assert!(socket.send(Message::Text("closed".into())).await.is_err());
peer.wait().await.unwrap();
}
})
.unwrap();
}
#[test]
fn websocket_accept_failure_can_send_http_error_and_http2_cannot_upgrade() {
runtime()
.run(async |scope| {
for http2 in [false, true] {
let listener = net::listen("127.0.0.1", 0, scope).await.unwrap();
let port = listener.port().unwrap();
let incoming = listener.clone();
let limits = Limits {
http2: http2.then(http::Http2Limits::default),
..Limits::default()
};
let server_limits = limits.clone();
let server = scope
.spawn(move |child| async move {
http::serve_live(
&incoming,
&server_limits,
&http::LiveLimits::default(),
&child,
|_, reply, scope| async move {
assert!(
ws::accept(&reply, &super::limits(), &scope).await.is_err()
);
reply
.respond(Response {
status: 400,
headers: vec![],
body: b"not a websocket".to_vec().into(),
})
.await
.map_err(|error| error.to_string())
},
)
.await
})
.unwrap();
let response = http::send("127.0.0.1", port, request("/", &[]), &limits)
.await
.unwrap();
assert_eq!(response.status, 400);
assert_eq!(response.body.as_ref(), b"not a websocket");
listener.close().unwrap();
server.wait().await.unwrap().unwrap();
}
})
.unwrap();
}
#[test]
fn websocket_upgrade_preserves_prefetched_frames_and_rejects_unmasked_clients() {
runtime().run(async |scope| {
let (listener, server) = server(scope).await;
let port = listener.port().unwrap();
scope.blocking(move || {
let mut peer = TcpStream::connect(("127.0.0.1", port as u16)).unwrap();
peer.set_read_timeout(Some(Duration::from_secs(2))).unwrap();
peer.set_write_timeout(Some(Duration::from_secs(2))).unwrap();
let mut request = b"GET /echo HTTP/1.1\r\nHost: localhost\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n".to_vec();
request.extend([0x81, 0x81, 1, 2, 3, 4, b'x' ^ 1]);
peer.write_all(&request).unwrap();
let mut peer = BufReader::new(peer);
let mut line = String::new();
peer.read_line(&mut line).unwrap();
assert!(line.starts_with("HTTP/1.1 101"), "{line}");
loop {
line.clear();
assert!(peer.read_line(&mut line).unwrap() > 0);
if line == "\r\n" { break; }
}
let mut echo = [0; 3];
peer.read_exact(&mut echo).unwrap();
assert_eq!(echo, [0x81, 1, b'x']);
peer.get_mut().write_all(&[0x81, 1, b'y']).unwrap();
assert_eq!(peer.read(&mut echo).unwrap(), 0);
}).await.unwrap();
listener.close().unwrap();
server.wait().await.unwrap().unwrap();
}).unwrap();
}
+185
View File
@@ -0,0 +1,185 @@
use super::shared_reference::{Limits, Owner, Socket};
use futures_util::{SinkExt, StreamExt};
use std::time::Duration;
use tokio::{
net::{TcpListener, TcpStream},
sync::oneshot,
};
use tokio_tungstenite::{
WebSocketStream,
tungstenite::{Message, protocol::WebSocketConfig},
};
fn limits() -> Limits {
Limits {
message: 16 * 1024 * 1024,
idle: Duration::from_secs(2),
write: Duration::from_secs(2),
}
}
async fn pair(limits: Limits) -> (Socket, Owner, WebSocketStream<TcpStream>) {
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let address = listener.local_addr().unwrap();
let config = WebSocketConfig::default()
.max_message_size(Some(limits.message))
.max_frame_size(Some(limits.message))
.write_buffer_size(0);
let client = async {
let stream = TcpStream::connect(address).await.unwrap();
stream.set_nodelay(true).unwrap();
tokio_tungstenite::client_async_with_config(
format!("ws://{address}/"),
stream,
Some(config),
)
.await
.unwrap()
.0
};
let server = async {
let (stream, _) = listener.accept().await.unwrap();
stream.set_nodelay(true).unwrap();
tokio_tungstenite::accept_async_with_config(stream, Some(config))
.await
.unwrap()
};
let (client, server) = tokio::join!(client, server);
let (socket, owner) = Socket::new(client, limits);
(socket, owner, server)
}
#[tokio::test]
async fn websocket_rust_reference_owner_exit_wakes_reader_and_releases_tcp() {
let (socket, owner, mut peer) = pair(limits()).await;
let alias = socket.clone();
let reading = alias.receive();
tokio::pin!(reading);
assert!(futures_util::poll!(&mut reading).is_pending());
drop(owner);
assert!(
tokio::time::timeout(Duration::from_millis(100), reading)
.await
.unwrap()
.is_err()
);
assert!(socket.send(Message::Text("closed".into())).await.is_err());
assert!(
tokio::time::timeout(Duration::from_millis(100), peer.next())
.await
.unwrap()
.unwrap()
.is_err()
);
}
#[tokio::test]
async fn websocket_rust_reference_full_duplex_aliases_preserve_messages() {
let (socket, _owner, mut peer) = pair(limits()).await;
let server = tokio::spawn(async move {
while let Some(message) = peer.next().await {
let message = message.unwrap();
if message.is_close() {
peer.flush().await.unwrap();
break;
}
peer.send(message).await.unwrap();
}
});
let sender = |writer: u8| {
let socket = socket.clone();
async move {
for sequence in 0..16 {
let mut bytes = vec![writer; 65536];
bytes[1] = sequence;
socket.send(Message::Binary(bytes.into())).await.unwrap();
}
}
};
let receiver = async {
let mut sequences = [0_u8; 2];
for _ in 0..32 {
let Message::Binary(bytes) = socket.receive().await.unwrap().unwrap() else {
panic!("binary expected")
};
let writer = usize::from(bytes[0]);
assert_eq!(bytes[1], sequences[writer]);
assert_eq!(bytes.len(), 65536);
assert!(bytes[2..].iter().all(|byte| *byte == writer as u8));
sequences[writer] += 1;
}
assert_eq!(sequences, [16, 16]);
};
tokio::time::timeout(Duration::from_secs(5), async {
tokio::join!(sender(0), sender(1), receiver);
})
.await
.unwrap();
socket.close(1000, "").await.unwrap();
server.await.unwrap();
}
#[tokio::test]
async fn websocket_rust_reference_close_has_priority_over_readable_reply() {
let (socket, _owner, mut peer) = pair(limits()).await;
let (sent, ready) = oneshot::channel();
let server = tokio::spawn(async move {
assert!(peer.next().await.unwrap().unwrap().is_close());
peer.flush().await.unwrap();
sent.send(()).unwrap();
});
let reading = socket.receive();
tokio::pin!(reading);
assert!(futures_util::poll!(&mut reading).is_pending());
let closing = socket.close(1000, "");
tokio::pin!(closing);
assert!(futures_util::poll!(&mut closing).is_pending());
ready.await.unwrap();
tokio::time::sleep(Duration::from_millis(25)).await;
assert!(reading.await.is_err());
closing.await.unwrap();
server.await.unwrap();
}
#[tokio::test]
async fn websocket_rust_reference_cancelled_writer_only_closes_after_lock() {
let (socket, _owner, _peer) = pair(limits()).await;
let mut writing = Box::pin(socket.send(Message::Binary(vec![1; limits().message].into())));
assert!(futures_util::poll!(&mut writing).is_pending());
let mut queued = Box::pin(socket.send(Message::Text("queued".into())));
assert!(futures_util::poll!(&mut queued).is_pending());
drop(queued);
assert!(futures_util::poll!(&mut writing).is_pending());
drop(writing);
assert!(socket.send(Message::Text("closed".into())).await.is_err());
}
#[tokio::test]
async fn websocket_rust_reference_limits_ping_and_idle_close() {
let (socket, _owner, mut peer) = pair(Limits {
message: 128,
idle: Duration::from_millis(25),
..limits()
})
.await;
assert!(
socket
.send(Message::Binary(vec![0; 129].into()))
.await
.is_err()
);
assert!(
socket
.send(Message::Ping(vec![0; 126].into()))
.await
.is_err()
);
peer.send(Message::Ping(vec![7].into())).await.unwrap();
assert!(socket.receive().await.unwrap().unwrap().is_ping());
assert_eq!(
peer.next().await.unwrap().unwrap(),
Message::Pong(vec![7].into())
);
assert!(socket.receive().await.unwrap_err().contains("timed out"));
assert!(socket.send(Message::Text("closed".into())).await.is_err());
}