Como processar solicitações de intervalo em um service worker

Verifique se o service worker sabe o que fazer quando uma resposta parcial é solicitada.

Algumas solicitações HTTP contêm um cabeçalho Range:, indicando que apenas uma parte do recurso completo será retornada. Eles geralmente são usados para transmitir conteúdo de áudio ou vídeo para permitir que blocos menores de mídia sejam carregados sob demanda, em vez de solicitar todo o arquivo remoto de uma vez.

Um service worker é um código JavaScript que fica entre seu app da Web e a rede, possivelmente interceptando solicitações de rede de saída e gerando respostas para elas.

Historicamente, as solicitações de intervalo e os service workers não funcionavam bem juntos. Foi necessário tomar medidas especiais para evitar resultados ruins para o service worker. Felizmente, isso está começando a mudar. Em navegadores que apresentam o comportamento correto, as solicitações de intervalo "simplesmente funcionam" ao passar por um service worker.

Qual é o problema?

Considere um service worker com o seguinte listener de eventos fetch, que recebe todas as solicitações recebidas e as transmite para a rede:

self.addEventListener('fetch', (event) => {
  // The Range: header will not pass through in
  // browsers that behave incorrectly.
  event.respondWith(fetch(event.request));
});

Em navegadores com comportamento incorreto, se event.request incluísse um cabeçalho Range:, esse cabeçalho seria descartado silenciosamente. A solicitação recebida pelo servidor remoto não incluiria Range:. Isso não necessariamente "falharia" em nada, já que um servidor tem tecnicamente permissão para retornar todo o corpo da resposta, com um código de status 200, mesmo quando um cabeçalho Range: está presente na solicitação original. No entanto, isso resultaria na transferência de mais dados do que o estritamente necessário do ponto de vista do navegador.

Os desenvolvedores que estavam cientes desse comportamento poderiam contorná-lo verificando explicitamente a presença de um cabeçalho Range: e não chamando event.respondWith(), se houver um. Ao fazer isso, o service worker remove a si mesmo da imagem de geração de resposta, e a lógica de rede do navegador padrão, que sabe como preservar solicitações de intervalo, é usada.

self.addEventListener('fetch', (event) => {
  // Return without calling event.respondWith()
  // if this is a range request.
  if (event.request.headers.has('range')) {
    return;
  }

  event.respondWith(fetch(event.request));
});

No entanto, a maioria dos desenvolvedores não sabia da necessidade de fazer isso. E não estava claro por que isso deveria ser necessário. Essa limitação ocorria porque os navegadores precisavam acompanhar as mudanças na especificação subjacente, o que adicionou suporte a essa funcionalidade.

O que foi corrigido?

Os navegadores que se comportam corretamente preservam o cabeçalho Range: quando event.request é transmitido para fetch(). Isso significa que o código do service worker no meu exemplo inicial vai permitir que o servidor remoto veja o cabeçalho Range:, se ele tiver sido definido pelo navegador:

self.addEventListener('fetch', (event) => {
  // The Range: header will pass through in browsers
  // that behave correctly.
  event.respondWith(fetch(event.request));
});

Agora o servidor tem a chance de processar corretamente a solicitação de intervalo e retornar uma resposta parcial com um código de status 206.

Quais navegadores funcionam corretamente?

As versões recentes do Safari têm a funcionalidade correta. O Chrome e o Edge, a partir da versão 87, também se comportam corretamente.

Em outubro de 2020, o Firefox ainda não corrigiu esse comportamento, então talvez você ainda precise considerar isso ao implantar o código do service worker na produção.

Marcar a linha "Incluir cabeçalho de intervalo na solicitação de rede" do painel Web Platform Tests é a melhor maneira de confirmar se um determinado navegador corrigiu esse comportamento ou não.

E as solicitações de intervalo de exibição do cache?

Os service workers podem fazer muito mais do que apenas passar uma solicitação à rede. Um caso de uso comum é adicionar recursos, como arquivos de áudio e vídeo, a um cache local. Assim, um service worker pode atender às solicitações desse cache, ignorando completamente a rede.

Todos os navegadores, incluindo o Firefox, são compatíveis com a inspeção de uma solicitação em um gerenciador fetch, a verificação da presença do cabeçalho Range: e o preenchimento local da solicitação com uma resposta 206 proveniente de um cache. No entanto, o código do service worker para analisar corretamente o cabeçalho Range: e retornar apenas o segmento apropriado da resposta completa em cache não é trivial.

Felizmente, os desenvolvedores que precisam de ajuda podem recorrer ao Workbox, um conjunto de bibliotecas que simplifica os casos de uso comuns dos service workers. O workbox-range-request module implementa toda a lógica necessária para mostrar respostas parciais diretamente do cache. Uma receita completa para esse caso de uso pode ser encontrada na documentação do Workbox.

A imagem principal desta postagem é de Natalie Rhea Riggs no Unsplash.