Como usar o WebTransport

O WebTransport é uma API que oferece mensagens cliente-servidor bidirecionais 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 é o WebTransport?

O WebTransport é uma API da Web que usa o protocolo HTTP/3 como um transporte bidirecional. Ele é destinado a comunicações bidirecionais 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 datagrama e de forma confiável usando as APIs de streams.

Os datagramas são ideais para enviar e receber dados que não precisam de garantias de entrega fortes. O tamanho dos pacotes de dados individuais é limitado pela unidade máxima de transmissão (MTU) da conexão e pode ou não ser transmitido. Se forem transferidos, eles podem chegar em uma ordem arbitrária. Essas características tornam as APIs de datagrama ideais para transmissão de dados de baixa latência e de melhor esforço. Pense nos datagramas como mensagens de protocolo de datagramas do usuário (UDP), mas criptografadas e controladas por congestionamento.

As APIs de fluxos, por outro lado, oferecem transferência de dados ordenada e confiável. Eles são adequados para cenários em que você precisa enviar ou receber um ou mais fluxos de dados ordenados. O uso de várias streams do WebTransport é análogo ao estabelecimento de várias conexões TCP, mas, como o HTTP/3 usa o protocolo QUIC mais leve, elas podem ser abertas e fechadas sem muita sobrecarga.

Casos de uso

Esta é uma pequena lista de maneiras possíveis de 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 enviados de um servidor com latência mínima, independente de outros streams de dados.
  • Receber notificações enviadas por push de um servidor enquanto uma página da Web está aberta.

Queremos saber mais sobre como você planeja usar o WebTransport.

Suporte ao navegador

Browser Support

  • Chrome: 97.
  • Edge: 97.
  • Firefox: 114.
  • Safari: not supported.

Source

Como todos os recursos que não têm suporte universal ao navegador, a prática recomendada é programar de forma defensiva usando a detecção de recursos.

Status atual

Etapa Status
1. Criar uma explicação Concluído
2. Criar um rascunho inicial da especificação Concluído
3. Coletar feedback e iterar o design Concluído
4. Teste de origem Concluído
5. Lançamento Chromium 97

Relação do WebTransport com outras tecnologias

O WebTransport é um substituto para WebSockets?

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

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

Usar o WebTransport, pelas APIs de datagrama ou por várias instâncias simultâneas da API Streams, significa que você não precisa se preocupar com o bloqueio de linha principal, que pode ser um problema com WebSockets. Além disso, há benefícios de desempenho ao estabelecer novas conexões, já que o handshake QUIC subjacente é mais rápido do que iniciar o TCP sobre TLS.

O WebTransport faz parte de um novo rascunho de especificação. Por isso, o ecossistema do WebSocket em torno das bibliotecas de cliente e servidor é muito mais robusto. Se você precisa de algo que funcione "pronto para uso" com configurações de servidor comuns e com amplo suporte a clientes da Web, os WebSockets são a melhor escolha.

O WebTransport é igual a uma API de soquete UDP?

Não. O WebTransport não é uma API de soquete UDP. Embora o WebTransport use o HTTP/3, que por sua vez usa o UDP, o WebTransport tem requisitos relacionados à criptografia e ao controle de congestionamento que o tornam mais do que uma API básica de soquete UDP.

O WebTransport é uma alternativa aos canais de dados do WebRTC?

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

Geralmente, a execução de um servidor compatível com o HTTP/3 requer menos configuração do que a manutenção de um servidor WebRTC, que envolve a compreensão de vários protocolos (ICE, DTLS e SCTP) para conseguir um transporte funcional. O WebRTC envolve muitas peças móveis que podem levar a falhas nas negociações entre cliente e servidor.

A API WebTransport foi projetada pensando nos casos de uso de desenvolvedores da Web e deve parecer mais com a criação de código moderno de plataforma da Web do que com o uso de interfaces de canal de dados do WebRTC. Diferentemente do WebRTC, o WebTransport tem suporte em Web Workers, o que permite realizar comunicações cliente-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 em torno da contrapressão.

No entanto, se você já tiver uma configuração de cliente/servidor do WebRTC que funcione e que você goste, a mudança para o WebTransport pode não oferecer muitas vantagens.

Faça um teste

A melhor maneira de testar o WebTransport é iniciar um servidor HTTP/3 compatível. Em seguida, use esta página com um cliente JavaScript básico para testar as comunicações cliente/servidor.

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

Como usar a API

O WebTransport foi projetado com base em primitivas modernas da plataforma da Web, como a API Streams. Ele depende muito de promessas e funciona bem com async e await.

A implementação atual do WebTransport no Chromium oferece suporte a três tipos distintos de tráfego: datagramas, além de fluxos unidirecionais e bidirecionais.

Conectar-se a um servidor

É possível se conectar a um servidor HTTP/3 criando uma instância WebTransport. O esquema do URL precisa ser https. É necessário especificar explicitamente o número da porta.

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

A promessa closed é cumprida quando a conexão é encerrada normalmente e é rejeitada se o encerramento for inesperado.

Se o servidor rejeitar a conexão devido a um erro de indicação do cliente (por exemplo, o caminho do URL é inválido), o closed será rejeitado, enquanto o ready permanece 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

Depois de ter uma instância do WebTransport conectada a um servidor, você pode 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. Ambos os fluxos são inerentemente não confiáveis. Portanto, é possível que os dados gravados não sejam recebidos pelo servidor e vice-versa.

Os dois tipos de transmissão 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, você também pode usar o WebTransport para enviar e receber dados pelas APIs Streams.

Cada bloco de todos os streams é um Uint8Array. Ao contrário das APIs Datagram, esses fluxos são confiáveis. No entanto, cada fluxo é independente, então a ordem dos dados entre os fluxos 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() do WritableStreamDefaultWriter para fechar o fluxo HTTP/3 associado. O navegador tenta enviar todos os dados pendentes antes de fechar o stream associado.

// 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() do WritableStreamDefaultWriter para enviar uma RESET_STREAM ao servidor. Ao usar abort(), o navegador pode descartar todos os dados pendentes que ainda não foram 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. A obtenção de um WebTransportReceiveStream é um processo de duas etapas para um cliente da Web. Primeiro, ele chama o atributo incomingUnidirectionalStreams de uma instância WebTransport, que retorna um ReadableStream. Cada parte 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 fechamento do stream usando a promessa closed do ReadableStreamDefaultReader. Quando o fluxo HTTP/3 de origem é fechado com o bit FIN, a promessa closed é cumprida depois que todos os dados são lidos. Quando o fluxo HTTP/3 é fechado 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 parte 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 um deles.

Mais exemplos

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

WebTransport nas DevTools do Chrome

No momento, as Ferramentas do desenvolvedor do Chrome não oferecem suporte ao WebTransport. Você pode "marcar com estrela" este problema do Chrome para receber notificações sobre atualizações na interface do DevTools.

Polyfill

Um polyfill (ou seja, um ponyfill que oferece funcionalidade como um módulo independente que pode ser usado) 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 da especificação para ver orientações oficiais.

Feedback

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

Feedback sobre o design da API

Há algo na API que é estranho ou não funciona como esperado? Ou há partes que faltam para implementar sua ideia?

Registre um problema no repositório do GitHub do Web Transport ou adicione seus comentários 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, além de instruções simples para reproduzir o problema.

Planeja usar a API?

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

Discussão geral

Use o Google Group web-transport-dev para perguntas gerais ou problemas que não se encaixam em uma das outras categorias.

Agradecimentos

Este artigo incorpora informações do WebTransport Explainer, do draft specification e dos documentos de design relacionados. Agradecemos aos respectivos autores por fornecerem essa base.

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