8.6 KiB
game-realtime-webtransport-lib
Backend WebTransport/QUIC candidat pour le realtime de games.sasedev.
Responsabilité
La crate possède le transport WebTransport concret sans introduire de sémantique gameplay, room, joueur, tick ou snapshot. Le chemin natif repose sur web-transport-quinn; le chemin client wasm32-unknown-unknown repose sur web-transport-wasm et l'API WebTransport du navigateur. Les deux conservent les erreurs publiques dans game-realtime-transport-lib.
La frontière fiable disponible couvre désormais :
- configuration client HTTPS avec pin SHA-256 exact ;
- identité serveur X.509 DER + clé privée PKCS#8 DER injectables ;
- génération locale d'une identité self-signed ECDSA P-256 à validité courte pour
localhost, IPv4 loopback et IPv6 loopback ; - bind UDP/QUIC sur adresse explicite ou port éphémère ;
- établissement HTTP/3 WebTransport client/server natif ;
- sélection d'un unique stream bidirectionnel fiable comme chemin realtime principal ;
- framing privé
u32big-endian + payload binaire ; - limite de message configurable, 1 MiB par défaut, vérifiée avant allocation côté réception et avant écriture côté émission ;
- deadlines configurables sur le chemin natif pour la connexion, l'ouverture/accept du stream primaire et un envoi complet ;
- adaptation
RealtimeConnection/RealtimeSender/RealtimeReceiver; - FIN propre via
RealtimeSender::close(); - reset/STOP_SENDING backend-spécifiques via
WebTransportSender::abort(...)etWebTransportReceiver::abort(...); - cancellation/drop terminale : un sender abandonné est reset plutôt que transformé implicitement en FIN ;
- parseur de framing réception incrémental conservant son état si une future
receive()est annulée ; - mapping stable des erreurs reset/close/session/protocole vers
TransportErrorKind; - tracing sous
games::realtime::webtransport; - client WASM avec endpoint HTTPS, hash certificat SHA-256 explicite, établissement de session navigateur et ouverture du stream bidirectionnel primaire;
- adaptation WASM du framing
u32big-endian et des traits realtime, y compris FIN, reset/STOP et réception incrémentale.
Chemin navigateur/WASM
Pour wasm32-unknown-unknown, la crate remplace les dépendances natives Quinn/Tokio/rcgen par web-transport-wasm. Le build final reçoit --cfg=web_sys_unstable_apis uniquement pour cette cible via .cargo/config.toml, conformément à l'exigence actuelle des bindings WebTransport de web-sys.
L'API client garde les mêmes noms de surface que le client natif : WebTransportCertificateHash, WebTransportClientConfig, connect, WebTransportSession, WebTransportConnection, WebTransportSender et WebTransportReceiver. Le navigateur ouvre toujours le stream bidirectionnel primaire côté client ; le serveur reste le backend Rust natif.
Le pin SHA-256 est transmis à WebTransportOptions.serverCertificateHashes; aucune variante navigateur sans validation TLS n'est ajoutée. Le framing applicatif reste strictement identique au natif.
alpha.6 ferme uniquement la preuve de compilation de ce chemin. La limite de message de WebTransportConfig est appliquée immédiatement, mais les deadlines opérationnelles de cette configuration restent une différence explicite : le wrapper navigateur conserve les opérations Web API abandonnées de manière cancel-safe, et la tranche alpha.7 doit décider puis prouver la politique de timer/runtime avant de déclarer la parité comportementale navigateur. Aucun smoke runtime navigateur n'est attribué à alpha.6.
TLS de développement
WebTransportServerIdentity::generate_loopback() crée une identité en mémoire. La clé privée n'est ni écrite ni versionnée. Le certificat est valide sept jours, avec une petite marge de clock skew, et son SHA-256 est exposé à travers WebTransportCertificateHash afin que le client puisse utiliser le pinning fourni par web-transport-quinn.
Une identité préexistante peut être injectée en DER avec WebTransportServerIdentity::from_pkcs8_der(...). La compatibilité certificat/clé est alors vérifiée par le builder TLS au bind du serveur.
Aucune option de désactivation globale de la vérification TLS n'est exposée.
Stream fiable principal
Une session WebTransport établie n'est pas encore le contrat realtime lui-même. Le client appelle WebTransportSession::open_primary_connection() ; le serveur appelle WebTransportSession::accept_primary_connection().
Le chemin logique devient ensuite :
one WebTransport session
-> one primary bidirectional reliable stream
-> u32 big-endian payload length
-> payload bytes
open_primary_connection() écrit déjà l'en-tête de stream WebTransport requis par HTTP/3 avant de retourner. Le pair peut donc terminer accept_primary_connection() avant l'envoi de la première frame applicative ; aucun préambule propre à games.sasedev n'est nécessaire.
RealtimeConnection::split() conserve la session WebTransport dans les deux moitiés afin que la session ne soit pas fermée au moment où l'objet connexion est consommé.
Limites et deadlines
WebTransportConfig::default() conserve la baseline de 1 MiB par message. La limite peut être réduite ou augmentée tant qu'elle reste strictement positive et représentable dans le champ de longueur u32 du framing.
Sur le chemin natif, les deadlines configurables couvrent :
- connexion client et réponse finale à une requête WebTransport déjà surfacée côté serveur ;
- ouverture ou accept du stream bidirectionnel principal ;
- écriture complète header + payload d'une frame.
L'attente d'un nouveau pair sur le listener reste volontairement non bornée : un serveur inactif ne doit pas produire périodiquement une erreur uniquement parce qu'aucun client ne se présente.
QUIC applique sa propre flow-control. Le backend natif n'ajoute pas une seconde file applicative : si un envoi reste bloqué par flow-control/réseau au-delà de send_timeout, l'opération retourne TransportErrorKind::Timeout et le stream est reset afin qu'une frame partiellement transmise ne puisse pas être suivie d'une nouvelle frame invalide. Le chemin navigateur conserve la même politique de reset sur cancellation, mais son timer send_timeout reste explicitement différé à alpha.7.
Lifecycle, abort et cancellation
RealtimeSender::close() reste la fermeture propre de la direction d'émission et produit un FIN. À l'inverse :
WebTransportSender::abort(code)envoie unRESET_STREAMWebTransport ;WebTransportReceiver::abort(code)envoie unSTOP_SENDINGWebTransport ;- dropper un
WebTransportSenderencore actif provoque un reset explicite ; - dropper un
WebTransportReceiverencore actif provoque un stop explicite ; - annuler une future
send()en cours provoque également un reset via une garde de cancellation.
Une erreur terminale de lecture/écriture rend la moitié concernée indisponible pour une réutilisation silencieuse.
La réception n'utilise plus une lecture exacte monolithique. Le header et le payload sont lus progressivement avec l'API de lecture cancel-safe de Quinn ; header_read/payload_read restent dans le receiver. Une future receive() annulée peut donc être relancée sans perdre les octets déjà consommés ni décaler le framing.
Mapping d'erreurs
Le backend distingue notamment :
- payload hors limite ->
MessageTooLarge; - reset/STOP_SENDING valide ->
Aborted; - stream déjà fermé ->
Closed; - fermeture de session WebTransport explicite ->
Closed; - erreur de session/connexion non classée comme fermeture propre ->
Io; - reset/stop invalide ou framing tronqué ->
Protocol; - deadline dépassée ->
Timeoutsur le chemin natif ; la matérialisation des timers navigateur reste différée àalpha.7.
Une longueur entrante hors limite ou un framing tronqué provoque aussi l'arrêt de la direction de réception afin d'éviter de poursuivre sur un flux désynchronisé.
Frontières actuelles
La crate ne possède toujours pas :
- d'API datagram transport-neutral ;
- de serveur WebTransport WASM ;
- de preuve runtime navigateur ;
- de fallback WebSocket ;
- de benchmark WebSocket/WebTransport.
Ces responsabilités restent réservées aux tranches suivantes du plan 0.3.5.
Le chemin natif s'exécute sous un runtime Tokio fourni par le consommateur ; la crate ne crée ni runtime ni thread privé. Les dépendances navigateur sont target-specific et ne sont donc pas tirées par le backend natif. Réciproquement, web-transport-quinn, Tokio et rcgen ne sont pas requis pour construire la crate en wasm32-unknown-unknown.