Publicado: 6 de octubre de 2020
Algunas solicitudes HTTP contienen un encabezado Range:, que indica que solo se debe devolver una parte del recurso completo. Se suelen usar para transmitir contenido de audio o video, lo que permite que se carguen fragmentos más pequeños de contenido multimedia a pedido, en lugar de solicitar todo el archivo remoto de una vez.
Un service worker es un código de JavaScript que se encuentra entre tu app web y la red, y que puede interceptar solicitudes de red salientes y generar respuestas para ellas.
Históricamente, las solicitudes de rango y los Service Workers no se han llevado bien. Fue necesario tomar medidas especiales para evitar resultados negativos en tu service worker. Afortunadamente, esto está comenzando a cambiar. En los navegadores que muestran 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 los 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á Range: en absoluto. Esto no necesariamente "romperá" nada, ya que técnicamente se permite que un servidor devuelva el cuerpo de respuesta completo, con un código de estado 200, incluso cuando hay un encabezado Range: en la solicitud original. Sin embargo, esto generaría una transferencia de más datos de los que se necesitan estrictamente desde la perspectiva del navegador.
Los desarrolladores que conocen este comportamiento pueden evitarlo verificando explícitamente la presencia de un encabezado Range: y no llamando a event.respondWith() si hay uno presente. De esta manera, el service worker se quita de la generación de respuestas y se usa la lógica de redes predeterminada del navegador, que sabe cómo conservar 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 sabían que era necesario hacerlo. Y no estaba claro por qué debería ser obligatorio. 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 agregó 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 devolver una respuesta parcial con un código de estado 206.
¿Qué navegadores funcionan 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 aún no corrigió este comportamiento, por lo que es posible que debas tenerlo en cuenta cuando implementes el código de tu Service Worker en producción.
Marcar la fila "Include range header in network request" del panel de Web Platform Tests es la mejor manera de confirmar si un navegador determinado corrigió este comportamiento.
¿Qué sucede con la entrega de solicitudes de rango desde la caché?
Los service workers pueden hacer mucho más que solo 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 satisfacer las solicitudes de esa caché, lo que omite la red por completo.
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, el cumplimiento 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 devolver solo el segmento adecuado de la respuesta completa almacenada en caché no es trivial.
Afortunadamente, los desarrolladores que desean obtener ayuda pueden recurrir a Workbox, que es un conjunto de bibliotecas que simplifica los casos de uso comunes de los service workers. El 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.