# Utilisation de game-realtime-webtransport-lib Ce guide décrit les chemins fiables natif et client navigateur/WASM exposés par `game-realtime-webtransport-lib`, ainsi que la capacité datagram backend-spécifique. Il ne décrit ni gameplay ni protocole wire métier. ## Configuration transport `WebTransportConfig` porte les limites et deadlines du chemin fiable. La configuration par défaut garde une limite de 1 MiB par message. Les deadlines sont appliquées sur les chemins natif et navigateur pour la connexion, la sélection du stream primaire et chaque envoi complet. Exemple de configuration plus stricte : ```rust let transport = game_realtime_webtransport_lib::WebTransportConfig::default() .with_max_message_size(256 * 1024) .with_connect_timeout(std::time::Duration::from_secs(5)) .with_primary_stream_timeout(std::time::Duration::from_secs(2)) .with_send_timeout(std::time::Duration::from_secs(2)); ``` La validation effective se fait lors du bind serveur ou de la connexion client. Une limite nulle, une limite non représentable en `u32` ou une deadline nulle est rejetée comme `InvalidConfiguration`. Côté navigateur, les deadlines doivent également tenir dans la plage `u32` millisecondes imposée par le timer WASM. ## Serveur natif Créer d'abord l'identité TLS et le listener : ```rust let identity = match game_realtime_webtransport_lib::WebTransportServerIdentity::generate_loopback() { Ok(value) => value, Err(error) => return Err(error), }; let config = game_realtime_webtransport_lib::WebTransportServerConfig::new( std::net::SocketAddr::from(([127, 0, 0, 1], 4433)), identity, ) .with_transport_config(transport); let mut listener = match game_realtime_webtransport_lib::WebTransportListener::bind(config) { Ok(value) => value, Err(error) => return Err(error), }; let session = match listener.accept().await { Ok(value) => value, Err(error) => return Err(error), }; let connection = match session.accept_primary_connection().await { Ok(value) => value, Err(error) => return Err(error), }; ``` L'attente du prochain client dans `listener.accept()` n'a pas de timeout périodique. Une fois une requête WebTransport surfacée, sa réponse finale utilise la deadline de connexion configurée. `open_primary_connection()` écrit l'en-tête WebTransport requis pour identifier le stream avant de retourner. Le serveur peut donc attendre `accept_primary_connection()` puis commencer les échanges applicatifs ; aucune frame artificielle n'est nécessaire pour rendre le stream visible. ## Client natif Le client doit connaître le SHA-256 exact du certificat serveur : ```rust let config = match game_realtime_webtransport_lib::WebTransportClientConfig::new( "https://127.0.0.1:4433/game", certificate_hash, ) { Ok(value) => value.with_transport_config(transport), Err(error) => return Err(error), }; let session = match game_realtime_webtransport_lib::connect(&config).await { Ok(value) => value, Err(error) => return Err(error), }; let connection = match session.open_primary_connection().await { Ok(value) => value, Err(error) => return Err(error), }; ``` Le pinning est obligatoire dans cette API native ; il n'existe pas de variante qui désactive globalement la vérification TLS. ## Client navigateur/WASM Pour `wasm32-unknown-unknown`, les mêmes noms `WebTransportCertificateHash`, `WebTransportClientConfig`, `connect` et `WebTransportSession::open_primary_connection()` sont disponibles. Le serveur reste natif ; le navigateur est uniquement client. Exemple de séquence Rust côté WASM : ```rust let certificate_hash = game_realtime_webtransport_lib::WebTransportCertificateHash::from_sha256(server_sha256); let config = match game_realtime_webtransport_lib::WebTransportClientConfig::new( "https://127.0.0.1:4433/game", certificate_hash, ) { Ok(value) => value.with_transport_config(transport), Err(error) => return Err(error), }; let session = match game_realtime_webtransport_lib::connect(&config).await { Ok(value) => value, Err(error) => return Err(error), }; let connection = match session.open_primary_connection().await { Ok(value) => value, Err(error) => return Err(error), }; ``` Le build workspace fournit `web_sys_unstable_apis` uniquement à `wasm32-unknown-unknown`. Le hash SHA-256 est transmis au navigateur comme `serverCertificateHashes`. La limite de message configurée est appliquée au framing WASM. `connect_timeout`, `primary_stream_timeout` et `send_timeout` sont réalisés par des timers WASM ; une expiration retourne `TransportErrorKind::Timeout`, et un send expiré reset le stream comme sur le chemin natif. Le host Vite et l'adapter `wasm-bindgen` de `game-realtime-webtransport-browser-smoke` servent uniquement de preuve runtime. Ils ne sont pas nécessaires à un consommateur qui intègre déjà la bibliothèque dans son propre frontend. ## Contrat realtime Une fois le stream primaire sélectionné, utiliser les traits de `game-realtime-transport-lib` pour le chemin fiable normal : ```rust let (mut sender, mut receiver) = game_realtime_transport_lib::RealtimeConnection::split(connection); let message = game_realtime_transport_lib::TransportMessage::new(vec![1, 2, 3, 4]); if let Err(error) = game_realtime_transport_lib::RealtimeSender::send(&mut sender, message).await { return Err(error); } let received = match game_realtime_transport_lib::RealtimeReceiver::receive(&mut receiver).await { Ok(value) => value, Err(error) => return Err(error), }; if let Err(error) = game_realtime_transport_lib::RealtimeSender::close(&mut sender).await { return Err(error); } ``` Le backend encode chaque `TransportMessage` sous la forme `u32` big-endian + payload. Le consommateur ne doit pas reproduire ce framing lui-même. La flow-control QUIC est respectée naturellement par l'écriture asynchrone. Sur les chemins natif et navigateur, un send qui dépasse sa deadline est considéré terminal : le stream est reset et le même sender ne doit pas être réutilisé. ## Fermeture et abort `RealtimeSender::close()` termine proprement la direction d'émission du stream primaire. Le pair observe ensuite `TransportReceive::Closed` lorsqu'il atteint le FIN après les messages déjà écrits. Pour abandonner explicitement une direction WebTransport : ```rust if let Err(error) = sender.abort(42) { return Err(error); } ``` ou côté réception : ```rust if let Err(error) = receiver.abort(43) { return Err(error); } ``` Ces deux méthodes sont backend-spécifiques : elles ne sont pas ajoutées au contrat commun car WebSocket n'expose pas la même primitive QUIC de reset/stop. Dropper un sender actif ou annuler une future `send()` en cours provoque un reset explicite. Dropper un receiver actif provoque un stop explicite. Cela évite qu'une cancellation d'écriture partielle soit interprétée comme une fermeture propre ou qu'une frame suivante reprenne au mauvais offset. Une future `receive()` peut en revanche être annulée puis relancée : le backend conserve l'état partiel du header/payload et reprend le framing à l'octet correct. ## Datagrams WebTransport Les datagrams sont une capacité propre à `WebTransportSession`; ils ne passent pas par `RealtimeConnection`. Toujours consulter la taille courante avant émission : ```rust let max_payload = session.max_datagram_size(); if payload.len() <= max_payload { session.send_datagram(payload)?; } ``` La réception est asynchrone : ```rust let payload = session.receive_datagram().await?; ``` Ces appels ne donnent aucune garantie de livraison ni d'ordre. Une perte n'est pas une violation du transport datagram. Toute deadline de réception appartient donc au scénario consommateur ; elle n'est pas injectée dans `WebTransportConfig`. Le chemin navigateur expose les mêmes noms, avec `send_datagram(...).await`. La surface reste backend-spécifique afin de ne pas imposer une sémantique non fiable à WebSocket ou au contrat fiable commun.