0.3.5-alpha.9

This commit is contained in:
2026-09-22 08:32:36 +02:00
parent 17d9e18dcd
commit 9f1a1c3d06
13 changed files with 638 additions and 21 deletions

View File

@@ -0,0 +1,20 @@
# file: crates/apps/game-realtime-webtransport-datagram-smoke/Cargo.toml
# version: 1
[package]
name = "game-realtime-webtransport-datagram-smoke"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
authors.workspace = true
publish.workspace = true
[dependencies]
game-logging-lib = { path = "../../common/game-logging-lib" }
game-realtime-webtransport-lib = { path = "../../common/game-realtime-webtransport-lib" }
tokio = { workspace = true, features = ["macros", "rt", "time"] }
tracing.workspace = true
[lints]
workspace = true

View File

@@ -0,0 +1,101 @@
// file: crates/apps/game-realtime-webtransport-datagram-smoke/src/main.rs
// version: 1
#![warn(missing_docs)]
#![deny(unreachable_pub)]
#![forbid(unsafe_code)]
//! Executable localhost smoke for the backend-specific WebTransport datagram capability.
const CLIENT_DATAGRAM: &[u8] = b"games.sasedev-webtransport-client-datagram";
const DATAGRAM_RECEIVE_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(2);
const SERVER_DATAGRAM: &[u8] = b"games.sasedev-webtransport-server-datagram";
const SMOKE_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10);
const TRACING_TARGET: &str = "games::realtime::webtransport::datagram-smoke";
#[tokio::main(flavor = "current_thread")]
async fn main() -> std::process::ExitCode {
let _logging_guard = match game_logging_lib::init_console_tracing() {
std::result::Result::Ok(guard) => guard,
std::result::Result::Err(error) => {
eprintln!("failed to initialize WebTransport datagram smoke tracing: {error}");
return std::process::ExitCode::FAILURE;
},
};
tracing::info!(target: TRACING_TARGET, "WebTransport datagram smoke started");
let result = tokio::time::timeout(SMOKE_TIMEOUT, run_smoke()).await;
return match result {
std::result::Result::Ok(std::result::Result::Ok(())) => {
tracing::info!(target: TRACING_TARGET, "WebTransport datagram smoke passed");
println!("game-realtime-webtransport-datagram-smoke: PASS");
std::process::ExitCode::SUCCESS
},
std::result::Result::Ok(std::result::Result::Err(error)) => {
tracing::error!(target: TRACING_TARGET, detail = error.as_str(), "WebTransport datagram smoke failed");
eprintln!("game-realtime-webtransport-datagram-smoke: FAIL: {error}");
std::process::ExitCode::FAILURE
},
std::result::Result::Err(_) => {
tracing::error!(target: TRACING_TARGET, timeout_ms = SMOKE_TIMEOUT.as_millis(), "WebTransport datagram smoke timed out");
eprintln!("game-realtime-webtransport-datagram-smoke: FAIL: smoke timed out");
std::process::ExitCode::FAILURE
},
};
}
async fn run_smoke() -> std::result::Result<(), String> {
let identity = match game_realtime_webtransport_lib::WebTransportServerIdentity::generate_loopback() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(format!("loopback identity generation failed: {error}")),
};
let certificate_hash = identity.certificate_hash().clone();
let bind_address = std::net::SocketAddr::from(([127, 0, 0, 1], 0));
let server_config = game_realtime_webtransport_lib::WebTransportServerConfig::new(bind_address, identity);
let mut listener = match game_realtime_webtransport_lib::WebTransportListener::bind(server_config) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(format!("listener bind failed: {error}")),
};
let endpoint = format!("https://{}/datagram-smoke", listener.local_addr());
let client_config = match game_realtime_webtransport_lib::WebTransportClientConfig::new(endpoint.as_str(), certificate_hash) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(format!("client configuration failed: {error}")),
};
let (server_result, client_result) = tokio::join!(listener.accept(), game_realtime_webtransport_lib::connect(&client_config));
let server_session = match server_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(format!("server accept failed: {error}")),
};
let client_session = match client_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(format!("client connect failed: {error}")),
};
let client_max = client_session.max_datagram_size();
let server_max = server_session.max_datagram_size();
if CLIENT_DATAGRAM.len() > client_max || SERVER_DATAGRAM.len() > server_max {
return std::result::Result::Err(format!("negotiated datagram capacity too small: client_max={client_max}, server_max={server_max}"));
}
tracing::info!(target: TRACING_TARGET, client_max, server_max, "WebTransport datagram capacities negotiated");
if let std::result::Result::Err(error) = client_session.send_datagram(CLIENT_DATAGRAM) {
return std::result::Result::Err(format!("client datagram send failed: {error}"));
}
let server_received = match tokio::time::timeout(DATAGRAM_RECEIVE_TIMEOUT, server_session.receive_datagram()).await {
std::result::Result::Ok(std::result::Result::Ok(value)) => value,
std::result::Result::Ok(std::result::Result::Err(error)) => return std::result::Result::Err(format!("server datagram receive failed: {error}")),
std::result::Result::Err(_) => return std::result::Result::Err(String::from("server datagram receive timed out; datagram delivery is not guaranteed")),
};
if server_received.as_slice() != CLIENT_DATAGRAM {
return std::result::Result::Err(String::from("server received an unexpected datagram payload"));
}
if let std::result::Result::Err(error) = server_session.send_datagram(SERVER_DATAGRAM) {
return std::result::Result::Err(format!("server datagram send failed: {error}"));
}
let client_received = match tokio::time::timeout(DATAGRAM_RECEIVE_TIMEOUT, client_session.receive_datagram()).await {
std::result::Result::Ok(std::result::Result::Ok(value)) => value,
std::result::Result::Ok(std::result::Result::Err(error)) => return std::result::Result::Err(format!("client datagram receive failed: {error}")),
std::result::Result::Err(_) => return std::result::Result::Err(String::from("client datagram receive timed out; datagram delivery is not guaranteed")),
};
if client_received.as_slice() != SERVER_DATAGRAM {
return std::result::Result::Err(String::from("client received an unexpected datagram payload"));
}
return std::result::Result::Ok(());
}