Como usar o WebTransport

A WebTransport é uma API que oferece mensagens bidirecional e de baixa latência. Saiba mais sobre os casos de uso e como enviar feedback sobre o futuro da implementação.

Contexto

O que é WebTransport?

WebTransport é uma API da Web que usa o protocolo HTTP/3 como um transporte bidirecional. Ele é destinado a comunicações bidirecional entre um cliente da Web e um servidor HTTP/3. Ele oferece suporte ao envio de dados de forma não confiável usando as APIs de datagramas e de forma confiável usando as APIs de fluxos.

Os datagramas são ideais para enviar e receber dados que não precisam de garantias de entrega. Pacotes individuais de dados são limitados em tamanho pela unidade máxima de transmissão (MTU, na sigla em inglês) da conexão subjacente, e podem ou não ser transmitidos. Se forem transferidos, eles podem chegar em ordem aleatória. Essas características tornam as APIs de datagramas ideais para transmissão de dados de baixa latência e de melhor esforço. Pense nos datagramas como mensagens do protocolo de datagramas do usuário (UDP), mas criptografadas e com controle de congestionamento.

As APIs de fluxo, por outro lado, fornecem uma transferência de dados ordenada e confiável. Eles são ideais para cenários em que você precisa enviar ou receber um ou mais fluxos de dados ordenados. O uso de vários fluxos WebTransport é análogo ao estabelecimento de várias conexões TCP, mas como o HTTP/3 usa o protocolo QUIC mais leve em segundo plano, eles podem ser abertos e fechados sem tanta sobrecarga.

Casos de uso

Esta é uma pequena lista de possíveis maneiras pelas quais os desenvolvedores podem usar o WebTransport.

  • Enviar o estado do jogo em um intervalo regular com latência mínima para um servidor usando mensagens pequenas, não confiáveis e fora de ordem.
  • Receber streams de mídia por push de um servidor com latência mínima, independentemente de outros fluxos de dados.
  • Receber notificações por push de um servidor enquanto uma página da Web está aberta.

Estamos interessados em saber mais sobre como você planeja usar o WebTransport.

Suporte ao navegador

Compatibilidade com navegadores

  • 97
  • 97
  • 114
  • x

Origem

Assim como com todos os recursos que não têm suporte universal a navegadores, a codificação defensiva por meio da detecção de recursos é uma prática recomendada.

Status atual

Step Status
1. Criar explicação Concluído
2. Criar rascunho inicial da especificação Concluído
3. Reunir feedbacks e iterar o design Completos
4. Teste de origem Completos
5. Lançamento Chromium 97

Relação do WebTransport com outras tecnologias

O WebTransport é um substituto dos WebSockets?

Talvez. Há casos de uso em que WebSockets ou WebTransport podem ser protocolos de comunicação válidos.

As comunicações do WebSockets são modeladas em torno de um fluxo de mensagens único, confiável e ordenado, o que é bom para alguns tipos de necessidades de comunicação. Se você precisar dessas características, as APIs de streams da WebTransport também podem fornecê-las. Em comparação, as APIs de datagramas da WebTransport oferecem entrega de baixa latência, sem garantias de confiabilidade ou ordem, por isso não são uma substituição direta para WebSockets.

Ao usar a WebTransport, pelas APIs de datagramas ou por várias instâncias simultâneas da API Streams, você não precisa se preocupar com o bloqueio de "head-of-line", que pode ser um problema com WebSockets. Além disso, há benefícios de desempenho ao estabelecer novas conexões, já que o handshake de QUIC subjacente é mais rápido do que a inicialização do TCP por TLS.

O WebTransport é parte de uma nova especificação de rascunho e, como tal, o ecossistema WebSocket em torno de bibliotecas de cliente e servidor é atualmente muito mais robusto. Se você precisar de algo que funcione "pronto para uso" com configurações comuns de servidor e com amplo suporte ao cliente da Web, o WebSockets é a melhor escolha hoje.

O WebTransport é o mesmo que uma API UDP Socket?

Não. O WebTransport não é uma API UDP Socket. Enquanto o WebTransport usa HTTP/3, que, por sua vez, usa UDP "em segundo plano", o WebTransport tem requisitos de criptografia e controle de congestionamento que o tornam mais do que uma API UDP Socket básica.

O WebTransport é uma alternativa aos canais de dados WebRTC?

Sim, para conexões cliente-servidor. O WebTransport compartilha muitas das mesmas propriedades que os canais de dados WebRTC, embora os protocolos subjacentes sejam diferentes.

Geralmente, executar um servidor compatível com HTTP/3 requer menos configuração do que manter um servidor WebRTC, o que envolve a compreensão de vários protocolos (ICE, DTLS e SCTP) para conseguir um transporte funcionando. O WebRTC envolve muitas outras partes que podem levar a falhas nas negociações entre cliente/servidor.

A API WebTransport foi desenvolvida tendo em mente os casos de uso dos desenvolvedores da Web e deve ser mais semelhante a escrever código moderno de plataforma da Web do que usar as interfaces de canais de dados do WebRTC. Diferente do WebRTC, o WebTransport é compatível com os Web Workers, que permitem realizar comunicações entre o cliente e o servidor independentemente de uma determinada página HTML. Como o WebTransport expõe uma interface compatível com Streams, ele oferece suporte a otimizações relacionadas à pressão de retorno.

No entanto, se você já tiver uma configuração de cliente/servidor WebRTC que esteja feliz, mudar para o WebTransport pode não oferecer muitas vantagens.

Testar

A melhor maneira de testar o WebTransport é iniciar um servidor HTTP/3 compatível. Você pode usar esta página com um cliente JavaScript básico para testar as comunicações entre cliente e servidor.

Além disso, um servidor de eco mantido pela comunidade está disponível em webtransport.day.

Como usar a API

O WebTransport foi desenvolvido com base em primitivos modernos da plataforma da Web, como a API Streams. Ela depende muito de promessas e funciona bem com async e await.

A implementação atual do WebTransport no Chromium é compatível com três tipos distintos de tráfego: datagramas e streams unidirecionais e bidirecionais.

Conectar-se a um servidor

Para se conectar a um servidor HTTP/3, crie uma instância WebTransport. O esquema do URL precisa ser https. Você precisa especificar o número da porta.

Use a promessa ready para aguardar a conexão ser estabelecida. Esta promessa não será cumprida até que a configuração seja concluída e será rejeitada se a conexão falhar no estágio QUIC/TLS.

A promessa closed é atendida quando a conexão fecha normalmente e é rejeitada se a interdição for inesperada.

Se o servidor rejeitar a conexão devido a um erro de indicação do cliente (por exemplo, o caminho do URL é inválido), isso fará com que closed seja rejeitado, enquanto ready permanecerá sem solução.

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;

APIs Datagram

Quando você tiver uma instância WebTransport conectada a um servidor, será possível usá-la para enviar e receber bits discretos de dados, conhecidos como datagramas.

O getter writeable retorna um WritableStream, que um cliente da Web pode usar para enviar dados ao servidor. O getter readable retorna um ReadableStream, permitindo que você detecte dados do servidor. Os dois fluxos não são confiáveis por natureza, então é possível que os dados gravados não sejam recebidos pelo servidor e vice-versa.

Os dois tipos de streams usam instâncias Uint8Array para transferência de dados.

// 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);
}

APIs de Streams

Depois de se conectar ao servidor, também é possível usar o WebTransport para enviar e receber dados por meio das APIs Streams.

Cada bloco de todos os streams é um Uint8Array. Ao contrário das APIs Datagram, esses streams são confiáveis. Como os fluxos são independentes, a ordem dos dados não é garantida.

WebTransportSendStream

Uma WebTransportSendStream é criada pelo cliente da Web usando o método createUnidirectionalStream() de uma instância WebTransport, que retorna uma promessa para o WebTransportSendStream.

Use o método close() da WritableStreamDefaultWriter para fechar a conexão HTTP/3 associada. O navegador tenta enviar todos os dados pendentes antes de fechar a conexão associada.

// 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}`);
}

Da mesma forma, use o método abort() da WritableStreamDefaultWriter para enviar um RESET\_STREAM ao servidor. Ao usar abort(), o navegador pode descartar quaisquer dados pendentes que ainda não tenham sido enviados.

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

Uma WebTransportReceiveStream é iniciada pelo servidor. Conseguir um WebTransportReceiveStream é um processo de duas etapas para um cliente da Web. Primeiro, ele chama o atributo incomingUnidirectionalStreams de uma instância do WebTransport, que retorna um ReadableStream. Cada bloco desse ReadableStream, por sua vez, é um WebTransportReceiveStream que pode ser usado para ler instâncias de Uint8Array enviadas pelo servidor.

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);
}

É possível detectar o encerramento do fluxo usando a promessa closed da ReadableStreamDefaultReader. Quando a conexão HTTP/3 subjacente é fechada com o bit FIN, a promessa closed é atendida depois que todos os dados forem lidos. Quando a conexão HTTP/3 é encerrada abruptamente (por exemplo, por RESET\_STREAM), a promessa closed é rejeitada.

// 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

Uma WebTransportBidirectionalStream pode ser criada pelo servidor ou pelo cliente.

Os clientes da Web podem criar um usando o método createBidirectionalStream() de uma instância WebTransport, que retorna uma promessa para um WebTransportBidirectionalStream.

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

É possível detectar um WebTransportBidirectionalStream criado pelo servidor com o atributo incomingBidirectionalStreams de uma instância WebTransport, que retorna um ReadableStream. Cada bloco desse ReadableStream é, por sua vez, um 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
}

Um WebTransportBidirectionalStream é apenas uma combinação de WebTransportSendStream e WebTransportReceiveStream. Os exemplos das duas seções anteriores explicam como usar cada uma delas.

Mais exemplos

O rascunho de especificação do WebTransport inclui vários exemplos inline adicionais, além da documentação completa de todos os métodos e propriedades.

WebTransport no DevTools do Chrome

No momento, o Chrome DevTools não é compatível com o WebTransport. Marque esse problema do Chrome com uma estrela para receber notificações sobre atualizações na interface do DevTools.

Plástico poligonal

Um polyfill, ou ponyfill, que fornece funcionalidade como um módulo autônomo que você pode usar, chamado webtransport-ponyfill-websocket, que implementa alguns dos recursos do WebTransport, está disponível. Leia com atenção as restrições no README do projeto para determinar se essa solução pode funcionar para seu caso de uso.

Considerações sobre privacidade e segurança

Consulte a seção correspondente do rascunho de especificação para receber orientações oficiais.

Feedback

A equipe do Chrome quer saber sua opinião e saber sua experiência com essa API.

Feedback sobre o design da API

Há algo estranho na API ou que não funciona como esperado? Ou há elementos que você precisa implementar para implementar sua ideia?

Registre um problema no repositório Web Transport GitHub ou adicione suas ideias a um problema existente.

Problemas com a implementação?

Você encontrou um bug na implementação do Chrome?

Registre um bug em https://new.crbug.com. Inclua o máximo de detalhes possível e instruções simples para a reprodução.

Pretende usar a API?

Seu suporte público ajuda o Chrome a priorizar recursos e mostra a outros fornecedores de navegador a importância do suporte a eles.

Discussão geral

Use o Grupo do Google web-transport-dev (link em inglês) para perguntas gerais ou problemas que não se encaixam em uma das outras categorias.

Agradecimentos

Este artigo incorpora informações do WebTransport Explainer, da especificação do rascunho e dos documentos de design relacionados (links em inglês). Agradecemos aos respectivos autores por nos fornecer essa base.

A imagem principal nesta postagem é de Robin Pierre no Unsplash.