#![allow(clippy::unwrap_used, clippy::expect_used)]
use ant_quic::{
TransportConfig, VarInt,
config::{ClientConfig, ServerConfig},
high_level::{Connection, DROPPED_UNFINISHED_ERROR_CODE, Endpoint, ReadError, ReadToEndError},
masque::MasqueRelaySocket,
};
use rustls::pki_types::{CertificateDer, PrivateKeyDer};
use std::{net::SocketAddr, sync::Arc};
use tokio::time::{Duration, timeout};
const STALLED_STREAM_WINDOW: u32 = 1;
const UNSENDABLE_PAYLOAD_LEN: usize = 5 * 1024;
const READ_LIMIT: usize = 64 * 1024;
fn gen_self_signed_cert() -> (Vec<CertificateDer<'static>>, PrivateKeyDer<'static>) {
let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_string()])
.expect("generate self-signed");
let cert_der = CertificateDer::from(cert.cert);
let key_der = PrivateKeyDer::Pkcs8(cert.signing_key.serialize_der().into());
(vec![cert_der], key_der)
}
async fn loopback_pair(server_stream_window: Option<u32>) -> (Connection, Connection) {
let (chain, key) = gen_self_signed_cert();
let mut server_cfg = ServerConfig::with_single_cert(chain.clone(), key).expect("server cfg");
if let Some(window) = server_stream_window {
let mut transport = TransportConfig::default();
transport.stream_receive_window(VarInt::from_u32(window));
server_cfg.transport = Arc::new(transport);
}
let server = Endpoint::server(server_cfg, ([127, 0, 0, 1], 0).into()).expect("server ep");
let addr: SocketAddr = server.local_addr().unwrap();
let accept = tokio::spawn(async move {
let inc = timeout(Duration::from_secs(10), server.accept())
.await
.unwrap()
.unwrap();
timeout(Duration::from_secs(10), inc)
.await
.unwrap()
.unwrap()
});
let mut roots = rustls::RootCertStore::empty();
for c in chain {
roots.add(c).unwrap();
}
let client_cfg = ClientConfig::with_root_certificates(Arc::new(roots)).unwrap();
let mut client = Endpoint::client(([127, 0, 0, 1], 0).into()).expect("client ep");
client.set_default_client_config(client_cfg);
let c_conn: Connection = timeout(
Duration::from_secs(10),
client.connect(addr, "localhost").expect("start"),
)
.await
.unwrap()
.unwrap();
let s_conn: Connection = accept.await.unwrap();
(c_conn, s_conn)
}
#[tokio::test]
async fn dropping_cancelled_write_resets_stream() {
let (client_conn, server_conn) = loopback_pair(Some(STALLED_STREAM_WINDOW)).await;
let mut send = client_conn.open_uni().await.expect("open uni");
let payload = vec![0xABu8; UNSENDABLE_PAYLOAD_LEN];
let write = timeout(Duration::from_millis(300), send.write_all(&payload)).await;
assert!(
write.is_err(),
"write_all unexpectedly completed against a {STALLED_STREAM_WINDOW}-byte window; the \
test can no longer exercise the cancellation path"
);
drop(send);
let mut recv = timeout(Duration::from_secs(10), server_conn.accept_uni())
.await
.expect("accept_uni timed out")
.expect("accept uni");
let result = timeout(Duration::from_secs(10), recv.read_to_end(READ_LIMIT))
.await
.expect("read_to_end timed out");
match result {
Err(ReadToEndError::Read(ReadError::Reset(code))) => {
assert_eq!(
code, DROPPED_UNFINISHED_ERROR_CODE,
"stream was reset, but not with the dropped-unfinished code"
);
}
Ok(data) => panic!(
"receiver observed a graceful end-of-stream with {} of {UNSENDABLE_PAYLOAD_LEN} bytes \
— a truncated message indistinguishable from a complete one (issue #244)",
data.len()
),
Err(other) => panic!("expected a stream reset, got {other:?}"),
}
}
#[tokio::test]
async fn explicit_finish_before_drop_delivers_data() {
let (client_conn, server_conn) = loopback_pair(None).await;
let message = b"issue-244 graceful finish".to_vec();
let mut send = client_conn.open_uni().await.expect("open uni");
send.write_all(&message).await.expect("write_all");
send.finish().expect("finish");
drop(send);
let mut recv = timeout(Duration::from_secs(10), server_conn.accept_uni())
.await
.expect("accept_uni timed out")
.expect("accept uni");
let data = timeout(Duration::from_secs(10), recv.read_to_end(READ_LIMIT))
.await
.expect("read_to_end timed out")
.expect("read_to_end should succeed on an explicitly finished stream");
assert_eq!(
data, message,
"explicitly finished stream lost or altered data"
);
}
#[tokio::test]
async fn masque_relay_socket_drop_finishes_relay_stream() {
let (client_conn, server_conn) = loopback_pair(None).await;
let preamble = b"connect-udp";
let (mut client_send, client_recv) = client_conn.open_bi().await.expect("open bi");
client_send
.write_all(preamble)
.await
.expect("write preamble");
let relay_addr: SocketAddr = ([127, 0, 0, 1], 9).into();
let socket = MasqueRelaySocket::new(client_send, client_recv, relay_addr);
let (mut server_send, mut server_recv) =
timeout(Duration::from_secs(10), server_conn.accept_bi())
.await
.expect("accept_bi timed out")
.expect("accept bi");
let mut seen = vec![0u8; preamble.len()];
timeout(Duration::from_secs(10), server_recv.read_exact(&mut seen))
.await
.expect("read_exact timed out")
.expect("read preamble");
assert_eq!(&seen[..], preamble, "relay preamble corrupted");
drop(socket);
server_send.finish().expect("relay finish");
let tail = timeout(Duration::from_secs(10), server_recv.read_to_end(READ_LIMIT))
.await
.expect("read_to_end timed out");
assert!(
tail.is_ok(),
"dropping the relay socket must finish the relay stream, not reset it; got {tail:?}"
);
}