Como usar o WebTransport

A WebTransport é uma API que oferece mensagens bidirecionais e de baixa latência entre servidor e cliente. 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 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 não confiáveis pelas APIs de datagrama e de forma confiável pelas APIs de fluxo.

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

As APIs de streams, por outro lado, oferecem transferência de dados ordenada e confiável. Eles são adequados para situações em que você precisa enviar ou receber um ou mais fluxos de dados ordenados. O uso de vários streams do WebTransport é análogo ao estabelecimento de múltiplas 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 com que 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.
  • Recebimento de streams de mídia enviados por um servidor com latência mínima, independentemente de outros fluxos 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

Compatibilidade com navegadores

  • Chrome: 97.
  • Borda: 97.
  • Firefox: 114
  • Safari: incompatível.

Origem

Como acontece com todos os recursos sem suporte universal ao navegador, a codificação defensiva por meio da detecção de recursos é uma prática recomendada.

Status atual

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

A relação do WebTransport com outras tecnologias

O WebTransport substitui os WebSockets?

Talvez. Em alguns casos, o WebSockets ou o WebTransport podem ser protocolos de comunicação válidos.

As comunicações dos 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 do WebTransport também poderão fornecê-las. Em comparação, as APIs de datagramas do WebTransport oferecem entrega de baixa latência, sem garantias de confiabilidade ou ordenação, então não substituem diretamente os WebSockets.

Usar o WebTransport, por meio das APIs de datagramas ou de várias instâncias simultâneas da API Streams, significa que você não precisa se preocupar com bloqueios de cabeçalho, o que pode ser um problema com WebSockets. Além disso, há benefícios de desempenho ao estabelecer novas conexões, uma vez que o handshake de QUIC subjacente é mais rápido do que inicializar o TCP por TLS.

O WebTransport faz parte de uma nova especificação de rascunho e, como tal, o ecossistema WebSocket em torno das bibliotecas de cliente e servidor está muito mais robusto no momento. Se você precisa de algo que funcione "para uso imediato" com configurações comuns de servidor e com amplo suporte a clientes Web, o WebSockets é a melhor escolha atualmente.

O WebTransport é o mesmo que uma API UDP Socket?

Não. O WebTransport não é uma API UDP. 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 de soquete UDP básica.

O WebTransport é uma alternativa aos canais de dados WebRTC?

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

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

A API WebTransport foi criada pensando nos casos de uso de desenvolvedores da Web e deve se parecer mais com a escrita de um código moderno de plataforma da Web do que com o uso das interfaces de canal de dados do WebRTC. Ao contrário do WebRTC, o WebTransport é compatível com os Web Workers, o que permite a realização de comunicações entre cliente e servidor independentemente de uma determinada página HTML. Como o WebTransport expõe uma interface compatível com Streams, ele é compatível com otimizações relacionadas a contrapressão.

No entanto, se você já estiver satisfeito com uma configuração de cliente/servidor WebRTC funcionando, a mudança para o WebTransport pode não oferecer muitas vantagens.

Faça um teste

A melhor maneira de experimentar com o WebTransport é iniciar um servidor HTTP/3 compatível. Você pode usar essa página com um cliente JavaScript básico para testar as comunicações de 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 desenvolvido com base nas primitivas das modernas plataformas 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 é compatível com três tipos distintos de tráfego: datagramas e streams 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 o estabelecimento da conexão. Essa promessa não será atendida 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 quando o fechamento é 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), isso fará com que closed seja rejeitado, enquanto ready permanecerá não resolvido.

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 do WebTransport conectada a um servidor, poderá usá-la para enviar e receber bits de dados discretos, 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 streams não são confiáveis por natureza. Portanto, é possível que os dados que você grava não sejam recebidos pelo servidor e vice-versa.

Os dois tipos de fluxo usam instâncias do Uint8Array para a 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 Streams

Depois de se conectar ao servidor, você também poderá usar o WebTransport para enviar e receber dados por meio das APIs Streams.

Cada parte de todos os streams é um Uint8Array. Ao contrário das APIs Datagram, esses streams são confiáveis. Mas cada stream é independente, então a ordem dos dados entre eles não é garantida.

WebTransportSendStream

Um WebTransportSendStream é criado pelo cliente 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 realmente 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 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 de 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 fechamento do stream usando a promessa closed do ReadableStreamDefaultReader. Quando a conexão HTTP/3 subjacente é fechada com o bit FIN, a promessa closed é atendida depois que todos os dados são lidos. Quando a conexão HTTP/3 é fechada 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

Um WebTransportBidirectionalStream pode ser criado pelo servidor ou pelo cliente.

Os clientes da Web podem criar uma 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

A especificação de rascunho do WebTransport (em inglês) inclui vários outros exemplos inline, além da documentação completa de todos os métodos e propriedades.

WebTransport no DevTools do Chrome

Infelizmente, o Chrome DevTools não é compatível com o 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 ponyfill que fornece funcionalidade como um módulo autônomo 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 o 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 conferir as orientações oficiais.

Feedback

A equipe do Chrome quer saber sua opinião e conhecer as experiências de uso dessa API.

Feedback sobre o design da API

Existe algo estranho na API ou que não funciona como esperado? Ou faltam partes para que sua ideia seja implementada?

Registre um problema no repositório do GitHub sobre transporte Web (link em inglês) ou adicione sua opinião a um problema já 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, junto com instruções simples de reprodução.

Planeja usar a API?

Seu apoio público ajuda o Chrome a priorizar recursos e mostra a outros fornecedores de navegadores como é essencial oferecer suporte a eles.

Discussão geral

Você pode usar o Grupo do Google web-transport-dev para dúvidas gerais ou problemas que não se enquadram em nenhuma das outras categorias.

Agradecimentos

Este artigo incorpora informações do Explicador do WebTransport, da especificação do rascunho e dos documentos de design relacionados. Agradecemos aos respectivos autores por fornecer essa base.

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