Como usar o WebTransport

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

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 pelas APIs de datagrama e de forma confiável pelas 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 individuais de dados é limitado pela unidade máxima de transmissão (MTU) da conexão subjacente. Eles podem ou não ser transmitidos com sucesso e, se forem transferidos, 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 com o melhor esforço possível. Pense nos datagramas como mensagens do protocolo de datagramas do usuário (UDP), mas criptografadas e com controle de congestionamento.

Já as APIs de streams oferecem transferência de dados confiável e ordenada. Eles são adequados para cenários em que você precisa enviar ou receber um ou mais fluxos de dados ordenados. Usar vários fluxos WebTransport é análogo a estabelecer várias conexões TCP, mas como o HTTP/3 usa o protocolo QUIC mais leve por baixo dos panos, eles podem ser abertos e fechados sem tanta sobrecarga.

Casos de uso

Esta é uma pequena lista de possíveis maneiras de os desenvolvedores usarem 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 fluxos de mídia enviados por push de um servidor com latência mínima, independente de outros fluxos de dados.
  • Receber notificações enviadas 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: behind a flag.

Source

Assim como todos os recursos que não têm suporte universal do navegador, a programação defensiva via detecção de recursos é uma prática recomendada.

Status atual

Etapa Status
1. Criar explicação Concluído
2. Criar o 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 WebSockets ou WebTransport podem ser protocolos de comunicação válidos.

As comunicações do WebSockets são modeladas em torno de um único fluxo de mensagens confiável e ordenado, o que é adequado 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 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.

Ao usar o WebTransport, pelas APIs de datagrama ou por várias instâncias simultâneas da API Streams, você não precisa se preocupar com o bloqueio de início de linha, 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 uma nova especificação de rascunho. Por isso, o ecossistema WebSocket em torno das bibliotecas de cliente e servidor é muito mais robusto no momento. Se você precisar de algo que funcione "pronto para uso" com configurações comuns de servidor e com suporte amplo a clientes da Web, o WebSockets é uma opção melhor hoje.

O WebTransport é o mesmo que uma API de soquete UDP?

Não. O WebTransport não é uma API de soquete UDP. Embora o WebTransport use HTTP/3, que por sua vez usa UDP "por baixo dos panos", ele 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 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.

Em geral, executar um servidor compatível com HTTP/3 exige menos configuração do que manter um servidor WebRTC, que envolve entender vários protocolos (ICE, DTLS e SCTP) para ter um transporte funcional. O WebRTC envolve muito mais partes móveis que podem levar a negociações com falha entre cliente e servidor.

A API WebTransport foi projetada pensando nos casos de uso de desenvolvedores da Web e deve parecer mais com a escrita de código moderno da 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 Web Workers (links em inglês), 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 relacionadas à contrapressão.

No entanto, se você já tiver uma configuração de cliente/servidor WebRTC funcional e estiver satisfeito com ela, a mudança para o WebTransport talvez não ofereça muitas vantagens.

Faça um teste

A melhor maneira de testar o WebTransport é iniciar um servidor HTTP/3 compatível. Em seguida, use essa 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

A WebTransport foi projetada com base em primitivos modernos da plataforma 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 diferentes de tráfego: datagramas e 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 o estabelecimento da conexão. Essa promessa não será cumprida até que a configuração seja concluída e será rejeitada se a conexão falhar na etapa QUIC/TLS.

A promessa closed é cumprida quando a conexão é fechada normalmente e rejeitada se o fechamento 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), isso fará com que closed seja rejeitado, enquanto ready permanecerá sem resoluçã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.t>hen(() = {
  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 de datagrama

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. Como os dois fluxos são inerentemente não confiáveis, é possível que os dados gravados não sejam recebidos pelo servidor e vice-versa.

Os dois tipos de fluxos 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 Streams

Depois de se conectar ao servidor, você também pode usar o WebTransport para enviar e receber dados pelas APIs Streams.

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

WebTransportSendStream

Um WebTransportSendStream é criado 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() da WritableStreamDefaultWriter para enviar um RESET_STREAM ao servidor. Ao usar abort(), o navegador pode descartar 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

Um WebTransportReceiveStream é iniciado 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 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 subjacente é 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.');
}).ca>tch(() = {
  console.error('The receiveStream closed abruptly.');
});

WebTransportBidirectionalStream

Um WebTransportBidirectionalStream pode ser criado 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 um WebTransportSendStream e um WebTransportReceiveStream. Os exemplos das duas seções anteriores explicam como usar cada uma delas.

Mais exemplos

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

WebTransport no Chrome DevTools

No momento, as DevTools do Chrome não são compatíveis com o WebTransport. Você pode marcar este problema do Chrome com uma estrela para receber notificações sobre atualizações na interface do DevTools.

Polyfill

Um polyfill (ou melhor, um ponyfill que oferece funcionalidade como um módulo independente 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 no seu caso de uso.

Considerações sobre privacidade e segurança

Consulte a seção correspondente do rascunho da especificação para 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á peças faltando que você precisa para implementar sua ideia?

Registre um problema no repositório do WebTransport no 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, além de instruções simples para reprodução.

Você planeja usar a API?

Seu apoio 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 grupo do Google web-transport-dev para dúvidas gerais ou problemas que não se encaixam em nenhuma das outras categorias.

Agradecimentos

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

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