use crate::{
stream::{testing::dcquic::Context, Protocol},
testing::server_name,
};
use std::{io, time::Duration};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tracing::{info_span, Instrument};
#[derive(Clone, Debug)]
enum Side {
Client,
Server,
Both,
}
#[derive(Clone, Debug)]
struct Harness {
protocol: Protocol,
drop: Side,
}
impl Default for Harness {
fn default() -> Self {
Self {
protocol: Protocol::Udp,
drop: Side::Both,
}
}
}
async fn check_stream(
context: &Context,
bidirectional: bool,
sleep_before_shutdown: bool,
) -> io::Result<()> {
let handshake_addr = context.handshake_addr();
let acceptor_addr = context.acceptor_addr();
tokio::try_join!(
async {
let mut a = context
.client
.connect(handshake_addr, acceptor_addr, server_name())
.await?;
let _ = a.write_all(b"testing").await;
if sleep_before_shutdown {
tokio::time::sleep(Duration::from_millis(10)).await;
}
let _ = a.shutdown().await;
if bidirectional {
let mut buffer = vec![];
a.read_to_end(&mut buffer).await?;
assert_eq!(buffer, b"testing");
}
Ok(())
}
.instrument(info_span!("client")),
async {
let (mut b, _) = context.server.accept().await.expect("accept");
let mut buffer = vec![];
b.read_to_end(&mut buffer).await.unwrap();
assert_eq!(buffer, b"testing");
if bidirectional {
b.write_all(&buffer).await.unwrap();
b.shutdown().await.unwrap();
}
Ok(())
}
.instrument(info_span!("server"))
)
.map(|_| ())
}
impl Harness {
async fn run(self) {
for bidirectional in [false, true] {
for sleep_before_shutdown in [false, true] {
dbg!(bidirectional, sleep_before_shutdown);
let task = self.run_one(bidirectional, sleep_before_shutdown);
tokio::time::timeout(core::time::Duration::from_secs(60), task)
.await
.expect("test timed out after 60 seconds");
}
}
}
async fn run_one(&self, bidirectional: bool, sleep_before_shutdown: bool) {
tracing::info!(bidirectional, sleep_before_shutdown);
let context = Context::bind(self.protocol, "127.0.0.1:0".parse().unwrap()).await;
check_stream(&context, bidirectional, sleep_before_shutdown)
.instrument(info_span!("first"))
.await
.unwrap();
match self.drop {
Side::Client => context.client.drop_state(),
Side::Server => context.server.drop_state(),
Side::Both => {
context.client.drop_state();
context.server.drop_state();
}
}
tracing::info!(
"initial: {:?}",
tokio::time::timeout(
Duration::from_secs(2),
check_stream(&context, bidirectional, sleep_before_shutdown)
)
.instrument(info_span!("second"))
.await
);
tokio::time::sleep(Duration::from_secs(2)).await;
tracing::info!(
"recovery handshake: {:?}",
tokio::time::timeout(
Duration::from_secs(2),
check_stream(&context, bidirectional, sleep_before_shutdown)
)
.instrument(info_span!("third"))
.await
);
tokio::time::sleep(Duration::from_millis(50)).await;
check_stream(&context, bidirectional, sleep_before_shutdown)
.instrument(info_span!("four"))
.await
.unwrap();
}
}
macro_rules! tests {
() => {
#[tokio::test]
async fn client() {
Harness {
drop: Side::Client,
..harness()
}
.run()
.await
}
#[tokio::test]
async fn server() {
Harness {
drop: Side::Server,
..harness()
}
.run()
.await
}
#[tokio::test]
async fn both() {
Harness {
drop: Side::Both,
..harness()
}
.run()
.await
}
};
}
mod tcp {
use super::*;
fn harness() -> Harness {
Harness {
protocol: Protocol::Tcp,
..Default::default()
}
}
tests!();
}
#[cfg(not(target_os = "macos"))]
mod udp {
use super::*;
fn harness() -> Harness {
Harness {
protocol: Protocol::Udp,
..Default::default()
}
}
tests!();
}