Traiter les requêtes de plage dans un service worker

Assurez-vous que votre service worker sait quoi faire lorsqu'une réponse partielle est demandée.

Certaines requêtes HTTP contiennent un en-tête Range: qui indique que seule une partie de la ressource complète doit être renvoyée. Ils sont couramment utilisés pour le streaming de contenus audio ou vidéo afin de permettre le chargement de petits segments multimédias à la demande, au lieu de demander l'intégralité du fichier distant en une seule fois.

Un service worker est un code JavaScript situé entre votre application Web et le réseau. Il peut intercepter les requêtes réseau sortantes et générer des réponses pour celles-ci.

Jusqu'à présent, les requêtes de plage et les service workers ne fonctionnaient pas bien ensemble. Nous avons pris des mesures spéciales pour éviter les mauvais résultats de votre service worker. Heureusement, cela commence à changer. Dans les navigateurs qui se comportent correctement, les requêtes de plage "fonctionnent" lorsqu'elles sont transmises par un service worker.

Quel est le problème ?

Prenons l'exemple d'un service worker avec l'écouteur d'événements fetch suivant, qui reçoit chaque requête entrante et la transmet au réseau:

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

Dans les navigateurs dont le comportement est incorrect, si event.request incluait un en-tête Range:, cet en-tête est supprimé sans notification. La requête reçue par le serveur distant n'incluait pas du tout Range:. Cela n'entraînerait pas nécessairement une interruption de service, car un serveur est techniquement autorisé à renvoyer l'intégralité du corps de la réponse, avec un code d'état 200, même si la requête d'origine contient un en-tête Range:. Toutefois, le transfert de données serait plus important que ce qui est strictement nécessaire du point de vue du navigateur.

Les développeurs qui étaient informés de ce comportement peuvent contourner ce problème en vérifiant explicitement la présence d'un en-tête Range: et en n'appelant pas event.respondWith() s'il est présent. Ainsi, le service worker se retire de l'image de génération de réponse, et la logique de mise en réseau du navigateur par défaut, qui sait comment conserver les requêtes de plage, est utilisée à la place.

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

Nous pouvons cependant dire que la plupart des développeurs n'étaient pas conscients de la nécessité de procéder ainsi. La raison de cette obligation n'était pas claire. En fin de compte, cette limitation était due au fait que les navigateurs devaient suivre les modifications de la spécification sous-jacente, ce qui a rendu cette fonctionnalité compatible.

Quelles sont les corrections apportées ?

Les navigateurs qui se comportent correctement conservent l'en-tête Range: lorsque event.request est transmis à fetch(). Cela signifie que le code du service worker de mon exemple initial permettra au serveur distant de voir l'en-tête Range:, s'il a été défini par le navigateur:

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

Le serveur peut maintenant gérer correctement la requête de plage et renvoyer une réponse partielle avec un code d'état 206.

Quels navigateurs se comportent correctement ?

Les versions récentes de Safari présentent le fonctionnement approprié. Chrome et Edge, à partir de la version 87, se comportent également correctement.

Depuis octobre 2020, Firefox n'a pas encore corrigé ce comportement. Vous devrez peut-être encore en tenir compte lors du déploiement du code de votre service worker en production.

Le meilleur moyen de confirmer si un navigateur donné a corrigé ce comportement est de vérifier la ligne "Inclure l'en-tête de plage dans la requête réseau" du tableau de bord des tests de plate-forme Web.

Qu'en est-il des requêtes de plage de diffusion à partir du cache ?

Les service workers peuvent faire bien plus que simplement transmettre une requête au réseau. Un cas d'utilisation courant consiste à ajouter des ressources, telles que des fichiers audio et vidéo, à un cache local. Un service worker peut alors traiter les requêtes contenues dans ce cache, contournant ainsi entièrement le réseau.

Tous les navigateurs, y compris Firefox, permettent d'inspecter une requête dans un gestionnaire fetch, de vérifier la présence de l'en-tête Range:, puis de traiter localement la requête avec une réponse 206 provenant d'un cache. Toutefois, le code du service worker permettant d'analyser correctement l'en-tête Range: et de ne renvoyer que le segment approprié de la réponse complète mise en cache n'est pas simple.

Heureusement, les développeurs qui ont besoin d'aide peuvent se tourner vers Workbox, un ensemble de bibliothèques qui simplifie les cas d'utilisation courants des service workers. Le workbox-range-request module implémente toute la logique nécessaire pour diffuser des réponses partielles directement à partir du cache. La recette complète pour ce cas d'utilisation est disponible dans la documentation de Workbox.

L'image héros de ce post a été créée par Natalie Rhea Riggs sur Unsplash.