Cómo manejar solicitudes de rango en un service worker

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

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

Un service worker es un código de JavaScript que se encuentra 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 trabajadores del servicio no han funcionado bien juntos. Fue necesario tomar medidas especiales para evitar resultados negativos en tu service worker. Afortunadamente, esto está empezando a cambiar. En los navegadores que muestran el comportamiento correcto, las solicitudes de rango “funcionarán” cuando pasen por un service worker.

¿Cuál es el problema?

Considera un trabajador de servicio 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 los navegadores con el comportamiento incorrecto, si event.request incluye un encabezado Range:, ese encabezado se descartará de forma silenciosa. La solicitud que recibió el servidor remoto no incluiría Range:. Esto no necesariamente “rompe” 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: en la solicitud original. Sin embargo, esto provocaría que se transfirieran más datos de los estrictamente necesarios desde la perspectiva del navegador.

Los desarrolladores que conocían este comportamiento podían evitarlo verificando explícitamente la presencia de un encabezado Range: y no llamando a event.respondWith() si había uno. De esta manera, el trabajador del servicio se quita de la imagen de generación de respuestas y, en su lugar, se usa la lógica de red predeterminada del navegador, 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, es seguro decir que la mayoría de los desarrolladores no estaban al tanto de la necesidad de hacerlo. Tampoco estaba claro por qué se debía exigir. En última instancia, esta limitación se debió a que los navegadores debían ponerse al día con los cambios en la especificación subyacente, que agregaron compatibilidad con esta funcionalidad.

¿Qué se corrigió?

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

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

Ahora, el servidor tiene la oportunidad de controlar 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 octubre de 2020, Firefox aún no corrigió este comportamiento, por lo que es posible que debas tener en cuenta este problema cuando implementes el código de tu trabajador de servicio en producción.

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

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

Los service workers pueden hacer mucho más que simplemente 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 entregar solicitudes de esa caché, omitiendo por completo la red.

Todos los navegadores, incluido Firefox, admiten la inspección de una solicitud dentro de un controlador fetch, la verificació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 trabajador del servicio para analizar correctamente el encabezado Range: y mostrar solo el segmento adecuado de la respuesta completa almacenada en caché no es trivial.

Afortunadamente, los desarrolladores que quieran obtener ayuda pueden recurrir a Workbox, que es un conjunto de bibliotecas que simplifica los casos de uso comunes de los service workers. 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 hero de esta publicación es de Natalie Rhea Riggs en Unsplash.