Pubblicato: 8 giugno 2020
WebTransport è un'API web che utilizza il protocollo HTTP/3 come trasporto bidirezionale. È destinato alle comunicazioni bidirezionali tra un client web e un server HTTP/3. Supporta l'invio di dati in modo inaffidabile con le API datagramma e in modo affidabile con le API stream.
I datagrammi sono ideali per l'invio e la ricezione di dati che non richiedono garanzie di consegna solide. Le dimensioni dei singoli pacchetti di dati sono limitate dall'unità massima di trasmissione (MTU) della connessione sottostante e possono essere trasmesse o meno correttamente. Se vengono trasferiti, possono arrivare in un ordine arbitrario. Queste caratteristiche rendono le API datagramma ideali per la trasmissione di dati a bassa latenza e con il criterio del "best effort". Puoi considerare i datagrammi come messaggi User Datagram Protocol (UDP), ma criptati e con controllo della congestione.
Le API di streaming, al contrario, forniscono un trasferimento di dati affidabile e ordinato. Sono ideali per gli scenari in cui devi inviare o ricevere uno o più stream di dati ordinati. L'utilizzo di più stream WebTransport è analogo alla creazione di più connessioni TCP, ma poiché HTTP/3 utilizza il protocollo QUIC più leggero, possono essere aperti e chiusi senza un overhead eccessivo.
Casi d'uso
Questo è un breve elenco di possibili modi in cui gli sviluppatori potrebbero utilizzare WebTransport.
- Invio dello stato del gioco a intervalli regolari con latenza minima a un server in un messaggi piccoli, inaffidabili e fuori ordine.
- Ricezione di flussi multimediali inviati da un server con latenza minima, indipendentemente da altri flussi di dati.
- Ricezione di notifiche push da un server mentre una pagina web è aperta.
Ci interessa saperne di più su come intendi utilizzare WebTransport.
Supporto browser
Come per tutte le funzionalità che non supportano tutti i browser, ti consigliamo di aggiungere il rilevamento delle funzionalità.
Relazione con altre tecnologie
WebTransport sostituisce WebSocket?
Forse. Esistono casi d'uso in cui WebSockets o WebTransport potrebbero essere protocolli di comunicazione validi da utilizzare.
Le comunicazioni WebSocket sono modellate su un flusso singolo, affidabile e ordinato di messaggi, il che va bene per alcuni tipi di esigenze di comunicazione. Se hai bisogno di queste caratteristiche, anche le API di streaming di WebTransport possono fornirle. Al contrario, le API datagrammi di WebTransport forniscono una distribuzione a bassa latenza, senza garanzie di affidabilità o ordinamento, quindi non sono un sostituto diretto di WebSocket.
Quando utilizzi WebTransport, con le API datagramma o più istanze API Stream simultanee, non devi preoccuparti del blocco head-of-line, che può essere un problema con WebSocket. Inoltre, l'utilizzo di QUIC offre vantaggi in termini di prestazioni quando vengono stabilite nuove connessioni, in quanto l'handshake QUIC sottostante è più veloce dell'avvio di TCP su TLS.
WebTransport fa parte di una nuova bozza di specifica e, in quanto tale, l'ecosistema WebSocket intorno alle librerie client e server è molto più solido. Se hai bisogno di qualcosa che funzioni "out of the box" con configurazioni comuni del server e con un ampio supporto del client web, oggi WebSockets è una scelta migliore.
WebTransport è uguale a un'API socket UDP?
No. WebTransport non è un'API UDP Socket. Sebbene WebTransport utilizzi HTTP/3, che a sua volta utilizza UDP "sotto il cofano", WebTransport ha requisiti relativi alla crittografia e al controllo della congestione che lo rendono più di una semplice API UDP Socket.
WebTransport è un'alternativa ai canali di dati WebRTC?
Sì, per le connessioni client-server. WebTransport condivide molte delle stesse proprietà dei canali di dati WebRTC, anche se i protocolli sottostanti sono diversi.
In genere, l'esecuzione di un server compatibile con HTTP/3 richiede meno configurazione rispetto alla gestione di un server WebRTC, che comporta la comprensione di più protocolli (ICE, DTLS e SCTP) per ottenere un trasporto funzionante. WebRTC comporta molti più elementi mobili che potrebbero portare a negoziazioni client/server non riuscite.
L'API WebTransport è stata progettata tenendo conto dei casi d'uso degli sviluppatori web e dovrebbe sembrare più simile alla scrittura di codice della piattaforma web moderna che all'utilizzo delle interfacce del canale di dati di WebRTC. A differenza di WebRTC, WebTransport è supportato all'interno dei Web Worker, il che consente di eseguire comunicazioni client-server indipendenti da una determinata pagina HTML. Poiché WebTransport espone un'interfaccia conforme a Stream, supporta le ottimizzazioni relative alla contropressione.
Tuttavia, se hai già una configurazione client/server WebRTC funzionante che ti soddisfa, il passaggio a WebTransport potrebbe non offrire molti vantaggi.
Esperimento
Il modo migliore per sperimentare WebTransport è avviare un server HTTP/3 compatibile. Utilizza questa pagina con un client JavaScript di base per provare le comunicazioni client e server.
Inoltre, è disponibile un server di echo gestito dalla community all'indirizzo webtransport.day.
Utilizzare l'API
WebTransport è stato progettato sulla base di primitive moderne della piattaforma web, come l'API Streams. Si basa molto sulle promesse e funziona bene con async e await.
L'attuale implementazione di WebTransport in Chromium supporta tre diversi tipi di traffico: datagrammi, nonché stream unidirezionali e bidirezionali.
Connettersi a un server
Puoi connetterti a un server HTTP/3 creando un'istanza WebTransport. Lo schema dell'URL deve essere https. Devi specificare esplicitamente il numero di porta.
Devi utilizzare la promessa ready per attendere che la connessione venga stabilita.
Questa promessa rimane insoddisfatta fino al completamento della configurazione e viene rifiutata se
la connessione non riesce nella fase QUIC/TLS.
La promessa closed viene soddisfatta quando la connessione si chiude normalmente e viene rifiutata se la chiusura è imprevista.
Se il server rifiuta la connessione a causa di un errore di indicazione del client (ad esempio, il percorso dell'URL non è valido), closed rifiuta la richiesta, mentre ready rimane irrisolto.
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 Datagram
Una volta che hai un'istanza WebTransport connessa a un server, puoi utilizzarla per inviare e ricevere singoli bit di dati, noti come datagrammi.
Il getter writeable restituisce un WritableStream, che un client web può utilizzare per inviare dati al server. Il getter readable restituisce un ReadableStream, che ti consente di ascoltare i dati provenienti dal server. Entrambi i flussi sono intrinsecamente inaffidabili, quindi è possibile che i dati che scrivi non vengano ricevuti dal server e viceversa.
Entrambi i tipi di stream utilizzano istanze Uint8Array per il trasferimento dei dati.
// 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
Una volta connesso al server, puoi anche utilizzare WebTransport per inviare e ricevere dati tramite le relative API Streams.
Ogni blocco di tutti gli stream è un Uint8Array. A differenza delle API Datagram, questi stream sono affidabili. Tuttavia, ogni stream è indipendente, quindi l'ordine dei dati tra gli stream non è garantito.
WebTransportSendStream
Un WebTransportSendStream viene creato dal client web utilizzando il metodo createUnidirectionalStream() di un'istanza WebTransport, che restituisce una promessa per WebTransportSendStream.
Utilizza il metodo close() di WritableStreamDefaultWriter per chiudere il flusso HTTP/3 associato. Il browser tenta di inviare tutti i dati in attesa prima di chiudere effettivamente lo stream associato.
// 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}`);
}
Analogamente, utilizza il metodo abort() di WritableStreamDefaultWriter per inviare un RESET_STREAM al server. Quando utilizzi abort(), il browser potrebbe eliminare i dati in attesa che non sono ancora stati inviati.
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
Il server avvia WebTransportReceiveStream.
L'ottenimento di un WebTransportReceiveStream è una procedura in due passaggi per un client web. Innanzitutto, il client chiama l'attributo incomingUnidirectionalStreams di un'istanza WebTransport, che restituisce un ReadableStream. Ogni blocco di ReadableStream è, a sua volta, un WebTransportReceiveStream che può essere utilizzato per leggere le istanze Uint8Array inviate dal server.
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);
}
Puoi rilevare la chiusura dello stream utilizzando la promessa closed di ReadableStreamDefaultReader. Quando lo stream HTTP/3 sottostante viene chiuso con il bit FIN, la promessa closed viene soddisfatta dopo la lettura di tutti i dati. Quando lo stream HTTP/3 viene chiuso bruscamente (ad esempio da RESET_STREAM), la promessa closed viene rifiutata.
// 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
potrebbe essere creato dal server o dal client.
I client web possono crearne uno utilizzando il metodo createBidirectionalStream() di un'istanza WebTransport, che restituisce una promessa per un WebTransportBidirectionalStream.
const stream = await transport.createBidirectionalStream();
// stream is a WebTransportBidirectionalStream
// stream.readable is a ReadableStream
// stream.writable is a WritableStream
Puoi ascoltare un WebTransportBidirectionalStream creato dal server con l'attributo incomingBidirectionalStreams di un'istanza WebTransport, che restituisce un ReadableStream. Ogni blocco di ReadableStream è, a sua volta, 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 è semplicemente una combinazione di un WebTransportSendStream e un WebTransportReceiveStream. Gli esempi delle due sezioni precedenti spiegano come utilizzare ciascuna di queste opzioni.
Polyfill
È disponibile un polyfill (o meglio un ponyfill che fornisce funzionalità come modulo autonomo che puoi utilizzare) chiamato
webtransport-ponyfill-websocket
che implementa alcune delle funzionalità di WebTransport. Leggi attentamente i vincoli nel README del progetto per determinare se questa soluzione può funzionare per il tuo caso d'uso.
Considerazioni su privacy e sicurezza
Per indicazioni autorevoli, consulta la sezione corrispondente della bozza della specifica.
Feedback
C'è qualcosa nell'API che è scomodo o non funziona come previsto? Oppure mancano dei pezzi che ti servono per implementare la tua idea?
- Segnala un problema nel repository GitHub di Web Transport o aggiungi i tuoi commenti a un problema esistente.
- Segnala un bug relativo all'implementazione di Chromium. Fornisci più dettagli possibili, insieme alle istruzioni per la riproduzione.
Il tuo supporto pubblico aiuta Chrome a dare la priorità alle funzionalità e mostra ad altri fornitori di browser quanto sia fondamentale supportarle.
- Invia un tweet a @ChromiumDev utilizzando l'hashtag
#WebTransporte i dettagli su dove e come lo utilizzi.
Discussione generale
Puoi utilizzare il gruppo Google web-transport-dev per domande o problemi generali che non rientrano in una delle altre categorie.
Ringraziamenti
Abbiamo incorporato informazioni tratte dalla spiegazione di WebTransport e dalla bozza di specifica. Grazie ai rispettivi autori per aver fornito queste basi.