Utiliser WebTransport

WebTransport est une API offrant une messagerie client-serveur bidirectionnelle à faible latence. Découvrez ses cas d'utilisation et comment envoyer des commentaires sur l'avenir de l'implémentation.

Contexte

Qu'est-ce que WebTransport ?

WebTransport est une API Web qui utilise le protocole HTTP/3 comme transport bidirectionnel. Il est destiné aux communications bidirectionnelles entre un client Web et un serveur HTTP/3. Il permet l'envoi de données de manière non fiable via ses API de datagramme et via ses API de flux de manière fiable.

Les datagrammes sont parfaits pour envoyer et recevoir des données qui n'ont pas besoin de garanties de livraison solides. Les paquets de données individuels sont limités en taille par l'unité de transmission maximale (MTU) de la connexion sous-jacente, et peuvent être transmis ou non avec succès. S'ils sont transférés, ils peuvent arriver dans un ordre arbitraire. Ces caractéristiques font des API de datagrammes idéales pour une transmission de données optimale et à faible latence. Vous pouvez considérer les datagrammes comme des messages du protocole de datagramme utilisateur (UDP), mais chiffrés et maîtrisés en congestion.

Les API de flux, quant à elles, permettent un transfert de données fiable et ordonné. Elles sont bien adaptées aux scénarios dans lesquels vous devez envoyer ou recevoir un ou plusieurs flux de données ordonnées. L'utilisation de plusieurs flux WebTransport est comparable à l'établissement de plusieurs connexions TCP. Cependant, comme HTTP/3 utilise en arrière-plan le protocole plus léger QUIC, il est possible d'ouvrir et de fermer ces flux sans trop d'efforts.

Cas d'utilisation

Il s'agit d'une courte liste des façons dont les développeurs peuvent utiliser WebTransport.

  • Envoyer l'état du jeu à intervalle régulier avec une latence minimale à un serveur via de petits messages non fiables et dans le désordre
  • Recevoir des flux multimédias envoyés depuis un serveur avec une latence minimale, indépendamment des autres flux de données
  • Réception de notifications envoyées par un serveur lorsqu'une page Web est ouverte.

Nous aimerions en savoir plus sur la façon dont vous prévoyez d'utiliser WebTransport.

Prise en charge des navigateurs

Navigateurs pris en charge

  • Chrome: 97 <ph type="x-smartling-placeholder">
  • Edge: 97 <ph type="x-smartling-placeholder">
  • Firefox: 114 <ph type="x-smartling-placeholder">
  • Safari: non compatible. <ph type="x-smartling-placeholder">

Source

Comme pour toutes les fonctionnalités qui ne sont pas compatibles avec les navigateurs, nous vous conseillons de coder de manière défensive grâce à la détection de fonctionnalités.

État actuel

Étape État
1. Créer une vidéo explicative Fin
2. Créer l'ébauche initiale de la spécification Fin
3. Recueillir les commentaires et itérer la conception Terminé
4. Phase d'évaluation Terminé
5. Lancement Chromium 97

Relation entre WebTransport et d'autres technologies

WebTransport remplace-t-il WebSockets ?

Peut-être. Dans certains cas, les protocoles de communication WebSockets ou WebTransport peuvent être des protocoles de communication valides.

Les communications WebSockets sont modélisées autour d'un flux de messages unique, fiable et ordonné, ce qui convient à certains types de besoins de communication. Si vous avez besoin de ces caractéristiques, les API de flux de WebTransport peuvent également les fournir. En comparaison, les API de datagramme de WebTransport offrent une diffusion à faible latence, sans garantie de fiabilité ni de classement. Elles ne remplacent donc pas directement WebSockets.

En utilisant WebTransport, via les API de datagramme ou via plusieurs instances simultanées de l'API Streams, vous n'avez pas à vous soucier du blocage en tête de ligne, qui peut être un problème avec WebSockets. En outre, l'établissement de nouvelles connexions offre des avantages en termes de performances, car le handshake QUIC sous-jacent est plus rapide que le démarrage de TCP sur TLS.

WebTransport fait partie d'un nouveau projet de spécification. À ce titre, l'écosystème WebSocket autour des bibliothèques clientes et de serveurs est actuellement beaucoup plus robuste. Si vous avez besoin d'une solution qui fonctionne immédiatement avec des configurations de serveur courantes et une compatibilité étendue avec les clients Web, WebSockets est aujourd'hui un meilleur choix.

WebTransport est-il identique à une API Socket UDP ?

Non. WebTransport n'est pas une API de socket UDP. Alors que WebTransport utilise HTTP/3, qui à son tour utilise UDP « en arrière-plan », WebTransport a des exigences de chiffrement et de contrôle de la congestion qui en font plus qu'une API Socket UDP de base.

WebTransport est-il une alternative aux canaux de données WebRTC ?

Oui, pour les connexions client-serveur. WebTransport partage de nombreuses propriétés des canaux de données WebRTC, bien que les protocoles sous-jacents soient différents.

En général, l'exécution d'un serveur compatible avec HTTP/3 nécessite moins de configuration que la maintenance d'un serveur WebRTC, ce qui implique de comprendre plusieurs protocoles (ICE, DTLS et SCTP) pour obtenir un transport fonctionnel. WebRTC implique beaucoup d'autres éléments mobiles qui pourraient entraîner l'échec des négociations client/serveur.

L'API WebTransport a été conçue pour répondre aux besoins des développeurs Web. Elle devrait ressembler davantage à l'écriture de code de plate-forme Web moderne qu'à l'utilisation des interfaces de canaux de données WebRTC. Contrairement à WebRTC, WebTransport est compatible avec les nœuds de calcul Web, qui vous permettent d'effectuer des communications client-serveur indépendamment d'une page HTML donnée. Étant donné que WebTransport expose une interface conforme à Streams, il accepte les optimisations liées à la contre-pression.

Toutefois, si vous disposez déjà d'une configuration client/serveur WebRTC opérationnelle et que vous êtes satisfait, le passage à WebTransport risque de ne pas offrir beaucoup d'avantages.

Essayer

La meilleure façon de tester WebTransport est de démarrer un serveur HTTP/3 compatible. Vous pouvez ensuite utiliser cette page avec un client JavaScript de base pour tester les communications client/serveur.

De plus, un serveur d'écho géré par la communauté est disponible à l'adresse webtransport.day.

Utilisation de l'API

WebTransport a été conçu sur la base des primitives des plates-formes Web modernes, telles que l'API Streams. Elle s'appuie fortement sur des promesses, et fonctionne bien avec async et await.

La mise en œuvre actuelle de WebTransport dans Chromium accepte trois types de trafic distincts: les datagrammes, ainsi que les flux unidirectionnels et bidirectionnels.

Connexion à un serveur

Vous pouvez vous connecter à un serveur HTTP/3 en créant une instance WebTransport. Le schéma de l'URL doit être https. Vous devez spécifier explicitement le numéro de port.

Vous devez utiliser la promesse ready pour attendre que la connexion soit établie. Cette promesse ne sera honorée qu'une fois la configuration terminée et sera refusée si la connexion échoue à l'étape QUIC/TLS.

La promesse closed est exécutée lorsque la connexion se ferme normalement et est refusée si la fermeture est inattendue.

Si le serveur refuse la connexion en raison d'une erreur client (par exemple, le chemin de l'URL n'est pas valide), cela entraîne le refus de closed, tandis que ready n'est pas résolu.

const url = 'https://example.com:4999/foo/bar';
const transport = new WebTransport(url);

// Optionally, set up functions to respond to
// the connection closing:
transport.closed.then(() => {
  console.log(`The HTTP/3 connection to ${url} closed gracefully.`);
}).catch((error) => {
  console.error(`The HTTP/3 connection to ${url} closed due to ${error}.`);
});

// Once .ready fulfills, the connection can be used.
await transport.ready;

API de datagramme

Une fois que vous disposez d'une instance WebTransport connectée à un serveur, vous pouvez l'utiliser pour envoyer et recevoir des bits de données distincts, appelés datagrammes.

Le getter writeable renvoie un WritableStream, qu'un client Web peut utiliser pour envoyer des données au serveur. Le getter readable renvoie un ReadableStream, ce qui vous permet d'écouter les données du serveur. Les deux flux sont intrinsèquement peu fiables. Il est donc possible que les données que vous écrivez ne soient pas reçues par le serveur, et inversement.

Les deux types de flux utilisent des instances Uint8Array pour le transfert de données.

// Send two datagrams to the server.
const writer = transport.datagrams.writable.getWriter();
const data1 = new Uint8Array([65, 66, 67]);
const data2 = new Uint8Array([68, 69, 70]);
writer.write(data1);
writer.write(data2);

// Read datagrams from the server.
const reader = transport.datagrams.readable.getReader();
while (true) {
  const {value, done} = await reader.read();
  if (done) {
    break;
  }
  // value is a Uint8Array.
  console.log(value);
}

API Streams

Une fois connecté au serveur, vous pouvez également utiliser WebTransport pour envoyer et recevoir des données via ses API Streams.

Chaque fragment de tous les flux est une Uint8Array. Contrairement aux API Datagram, ces flux sont fiables. Cependant, chaque flux est indépendant. L'ordre des données entre les flux n'est donc pas garanti.

WebTransportSendStream

Un WebTransportSendStream est créé par le client Web à l'aide de la méthode createUnidirectionalStream() d'une instance WebTransport, qui renvoie une promesse pour WebTransportSendStream.

Utilisez la méthode close() de WritableStreamDefaultWriter pour fermer la connexion HTTP/3 associée. Le navigateur tente d'envoyer toutes les données en attente avant de fermer la connexion associée.

// Send two Uint8Arrays to the server.
const stream = await transport.createUnidirectionalStream();
const writer = stream.writable.getWriter();
const data1 = new Uint8Array([65, 66, 67]);
const data2 = new Uint8Array([68, 69, 70]);
writer.write(data1);
writer.write(data2);
try {
  await writer.close();
  console.log('All data has been sent.');
} catch (error) {
  console.error(`An error occurred: ${error}`);
}

De même, utilisez la méthode abort() du WritableStreamDefaultWriter pour envoyer un RESET\_STREAM au serveur. Lorsque vous utilisez abort(), le navigateur peut supprimer toutes les données en attente qui n'ont pas encore été envoyées.

const ws = await transport.createUnidirectionalStream();
const writer = ws.getWriter();
writer.write(...);
writer.write(...);
await writer.abort();
// Not all the data may have been written.

WebTransportReceiveStream

Un WebTransportReceiveStream est lancé par le serveur. L'obtention d'un WebTransportReceiveStream est un processus en deux étapes pour un client Web. Tout d'abord, elle appelle l'attribut incomingUnidirectionalStreams d'une instance WebTransport, qui renvoie un ReadableStream. Chaque fragment de ce ReadableStream est, à son tour, un WebTransportReceiveStream qui peut être utilisé pour lire les instances Uint8Array envoyées par le serveur.

async function readFrom(receiveStream) {
  const reader = receiveStream.readable.getReader();
  while (true) {
    const {done, value} = await reader.read();
    if (done) {
      break;
    }
    // value is a Uint8Array
    console.log(value);
  }
}

const rs = transport.incomingUnidirectionalStreams;
const reader = rs.getReader();
while (true) {
  const {done, value} = await reader.read();
  if (done) {
    break;
  }
  // value is an instance of WebTransportReceiveStream
  await readFrom(value);
}

Vous pouvez détecter la fermeture d'une diffusion à l'aide de la promesse closed du ReadableStreamDefaultReader. Lorsque la connexion HTTP/3 sous-jacente est fermée avec le bit FIN, la promesse closed est remplie une fois toutes les données lues. Lorsque la connexion HTTP/3 est fermée brusquement (par exemple, par RESET\_STREAM), la promesse closed est refusée.

// Assume an active receiveStream
const reader = receiveStream.readable.getReader();
reader.closed.then(() => {
  console.log('The receiveStream closed gracefully.');
}).catch(() => {
  console.error('The receiveStream closed abruptly.');
});

WebTransportBidirectionalStream

Un WebTransportBidirectionalStream peut être créé par le serveur ou le client.

Les clients Web peuvent en créer une à l'aide de la méthode createBidirectionalStream() d'une instance WebTransport, qui renvoie une promesse pour un WebTransportBidirectionalStream.

const stream = await transport.createBidirectionalStream();
// stream is a WebTransportBidirectionalStream
// stream.readable is a ReadableStream
// stream.writable is a WritableStream

Vous pouvez écouter un WebTransportBidirectionalStream créé par le serveur avec l'attribut incomingBidirectionalStreams d'une instance WebTransport, qui renvoie un ReadableStream. Chaque fragment de ce ReadableStream est, à son tour, un WebTransportBidirectionalStream.

const rs = transport.incomingBidirectionalStreams;
const reader = rs.getReader();
while (true) {
  const {done, value} = await reader.read();
  if (done) {
    break;
  }
  // value is a WebTransportBidirectionalStream
  // value.readable is a ReadableStream
  // value.writable is a WritableStream
}

Un WebTransportBidirectionalStream est simplement la combinaison d'un WebTransportSendStream et d'un WebTransportReceiveStream. Les exemples des deux sections précédentes expliquent comment utiliser chacun d'eux.

Autres exemples

La spécification WebTransport inclut plusieurs exemples intégrés supplémentaires, ainsi qu'une documentation complète sur toutes les méthodes et propriétés.

WebTransport dans les outils de développement de Chrome

Malheureusement, les outils de développement de Chrome ne sont actuellement pas compatibles avec WebTransport. Vous pouvez activer le suivi ce problème lié à Chrome pour être informé des mises à jour disponibles dans l'interface des outils de développement.

Polyfill

Un polyfill (ou plutôt ponyfill qui fournit une fonctionnalité en tant que module autonome que vous pouvez utiliser) appelé webtransport-ponyfill-websocket qui implémente certaines des fonctionnalités de WebTransport est disponible. Lisez attentivement les contraintes README du projet pour déterminer si cette solution peut fonctionner pour votre cas d'utilisation.

Points à prendre en compte concernant la confidentialité et la sécurité

Consultez la section correspondante du brouillon de spécification pour obtenir des conseils fiables.

Commentaires

L'équipe Chrome aimerait connaître votre avis et votre expérience avec cette API.

Commentaires sur la conception de l'API

Y a-t-il un aspect de l'API gênant ou qui ne fonctionne pas comme prévu ? Ou manque-t-il des éléments dont vous avez besoin pour mettre en œuvre votre idée ?

Signalez un problème dans le dépôt GitHub Transport Web ou ajoutez vos commentaires à un problème existant.

Vous rencontrez un problème lors de l'implémentation ?

Avez-vous détecté un bug dans l'implémentation de Chrome ?

Signalez un bug sur https://new.crbug.com. Fournissez autant de détails que possible, ainsi que des instructions simples pour reproduire le problème.

Vous prévoyez d'utiliser l'API ?

Votre assistance publique permet à Chrome de donner la priorité aux fonctionnalités et montre aux autres fournisseurs de navigateurs à quel point il est essentiel d'assurer leur assistance.

  • Envoyez un tweet à @ChromiumDev en utilisant le hashtag. #WebTransport et des détails sur où et comment vous l'utilisez.

Discussion générale

Vous pouvez utiliser le groupe Google "web-transport-dev" pour les questions d'ordre général ou les problèmes qui n'entrent dans aucune des autres catégories.

Remerciements

Cet article intègre des informations issues du document WebTransport Explainer, du brouillon de spécification et des documents de conception associés. Merci aux auteurs respectifs d'avoir fourni ces bases.

L'image héros de ce post a été conçue par Robin Pierre sur Unsplash.