Cómo manejar solicitudes de rango en un service worker

Asegúrate de que tu service worker sepa qué hacer cuando se solicita una respuesta parcial.

Algunas solicitudes HTTP contienen un encabezado Range:, lo que indica que solo se debe mostrar una parte del recurso completo. Por lo general, se usan para transmitir contenido de audio o video y permitir que se carguen fragmentos más pequeños de contenido multimedia on demand, en lugar de solicitar todo el archivo remoto de una sola vez.

Un service worker es el código JavaScript que se ubica entre tu app web y la red, lo que podría interceptar solicitudes de red salientes y generar respuestas para ellas.

Históricamente, las solicitudes de rango y los service workers no han funcionado bien en conjunto. Es necesario tomar medidas especiales para evitar malos resultados en tu service worker. Por suerte, esto está empezando a cambiar. En navegadores que muestren el comportamiento correcto, las solicitudes de rango "simplemente funcionarán" cuando pasen por un service worker.

¿Cuál es el problema?

Considera un service worker con el siguiente objeto de escucha de eventos fetch, que toma cada solicitud entrante y la pasa a la red:

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

En navegadores con el comportamiento incorrecto, si event.request incluía un encabezado Range:, ese encabezado se descartaría de forma silenciosa. La solicitud que recibió el servidor remoto no incluiría Range: en absoluto. Esto no necesariamente “rompía” nada, ya que un servidor técnicamente puede mostrar el cuerpo de la respuesta completo, con un código de estado 200, incluso cuando hay un encabezado Range: presente en la solicitud original. Sin embargo, provocaría que se transfieran más datos de los que se necesitan estrictamente desde la perspectiva del navegador.

Los desarrolladores que sabían este comportamiento podían solucionarlo verificando de forma explícita la presencia de un encabezado Range: y no llamando a event.respondWith() si hubiera uno presente. Al hacer esto, el service worker se quita efectivamente de la imagen de generación de respuesta y, en su lugar, se usa la lógica de red del navegador predeterminada, que sabe cómo preservar las solicitudes de rango.

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

Sin embargo, se puede decir que la mayoría de los desarrolladores no estaban al tanto de la necesidad de hacer esto. Y no estaba claro por qué eso debería ser necesario. En última instancia, esta limitación se debió a que los navegadores necesitaban actualizar los cambios en la especificación subyacente, lo que agregó compatibilidad con esta funcionalidad.

¿Qué corregimos?

Los navegadores que se comportan de forma correcta conservan el encabezado Range: cuando se pasa event.request a fetch() Esto significa que el código del service worker de mi ejemplo inicial permitirá que el servidor remoto vea el encabezado Range:, si lo configuró el navegador:

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

El servidor ahora tiene la oportunidad de manejar correctamente la solicitud de rango y mostrar una respuesta parcial con un código de estado 206.

¿Qué navegadores se comportan correctamente?

Las versiones recientes de Safari tienen la funcionalidad correcta. Chrome y Edge (a partir de la versión 87) también se comportan correctamente.

A partir de octubre de 2020, Firefox no ha corregido este comportamiento, por lo que es posible que debas tenerlo en cuenta cuando implementes el código del service worker en producción.

La mejor manera de confirmar si un navegador en particular corrigió este comportamiento es verificar la fila "Incluye el encabezado de rango en la solicitud de red" del panel de pruebas de la plataforma web.

¿Qué pasa con las solicitudes de rango de entrega desde la caché?

Los service workers pueden hacer mucho más que pasar una solicitud a la red. Un caso de uso común es agregar recursos, como archivos de audio y video, a una caché local. Luego, un service worker puede completar solicitudes desde esa caché y así omitir toda la red.

Todos los navegadores, incluido Firefox, admiten la inspección de una solicitud dentro de un controlador fetch, la comprobación de la presencia del encabezado Range: y, luego, la entrega local de la solicitud con una respuesta 206 que proviene de una caché. Sin embargo, el código del service worker para analizar correctamente el encabezado Range: y mostrar solo el segmento apropiado de la respuesta almacenada en caché completa no es trivial.

Por suerte, los desarrolladores que quieran obtener ayuda pueden recurrir a Workbox, que es un conjunto de bibliotecas que simplifica los casos de uso comunes de service worker. El objeto workbox-range-request module implementa toda la lógica necesaria para entregar respuestas parciales directamente desde la caché. Puedes encontrar una receta completa para este caso de uso en la documentación de Workbox.

La imagen destacada de esta publicación es de Natalie Rhea Riggs en Unsplash.