Gestione delle richieste di intervalli in un service worker

Assicurati che il tuo service worker sappia cosa fare quando viene richiesta una risposta parziale.

Alcune richieste HTTP contengono un'intestazione Range:, che indica che deve essere restituita solo una parte della risorsa completa. Sono comunemente utilizzati per lo streaming di contenuti audio o video per consentire il caricamento di blocchi di contenuti multimediali più piccoli su richiesta, anziché richiedere l'intero file remoto contemporaneamente.

Un service worker è un codice JavaScript che si trova tra l'app web e la rete, potenzialmente intercettando le richieste di rete in uscita e generando risposte.

In passato, le richieste di intervallo e i service worker non hanno funzionato bene insieme. È stato necessario adottare misure speciali per evitare risultati indesiderati nel tuo worker di servizio. Fortunatamente, la situazione sta cambiando. Nei browser che mostrano il comportamento corretto, le richieste di intervallo "funzionano e basta" quando passano attraverso un service worker.

Qual è il problema?

Considera un service worker con il seguente listener di eventi fetch, che accetta ogni richiesta in entrata e la passa alla rete:

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

Nei browser con il comportamento errato, se event.request includeva un'intestazione Range:, questa viene eliminata automaticamente. La richiesta ricevuta dal server remoto non includerebbe Range:. Ciò non causerebbe necessariamente "errori", poiché un server è tecnicamente autorizzato a restituire il corpo completo della risposta, con un codice di stato 200, anche quando nella richiesta originale è presente un'intestazione Range:. Tuttavia, il numero di dati trasferiti sarà maggiore di quello strettamente necessario dal punto di vista del browser.

Gli sviluppatori a conoscenza di questo comportamento potevano aggirarlo controllando esplicitamente la presenza di un'intestazione Range: e non chiamando event.respondWith() se presente. In questo modo, il service worker si rimuove efficacemente dal contesto di generazione della risposta e viene utilizzata la logica di rete del browser predefinita, che sa come conservare le richieste di intervallo.

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

Possiamo affermare con certezza che la maggior parte degli sviluppatori, però, non era a conoscenza della necessità di farlo. Inoltre, non era chiaro perché dovesse essere richiesto. Alla fine, questa limitazione era dovuta al fatto che i browser dovevano adeguarsi alle modifiche alla specifica di base, che ha aggiunto il supporto per questa funzionalità.

Cosa è stato corretto?

I browser che si comportano correttamente mantengono l'intestazione Range: quando event.request viene passato a fetch(). Ciò significa che il codice del worker di servizio nel mio esempio iniziale consentirà al server remoto di vedere l'intestazione Range:, se impostata dal browser:

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

Ora il server ha la possibilità di gestire correttamente la richiesta di intervallo e restituire una risposta parziale con un codice di stato 206.

Quali browser si comportano correttamente?

Le versioni recenti di Safari hanno la funzionalità corretta. Anche Chrome ed Edge, a partire dalla versione 87, si comportano correttamente.

A partire da ottobre 2020, Firefox non ha ancora corretto questo comportamento, quindi potrebbe essere necessario tenerlo conto durante il deployment del codice del tuo service worker in produzione.

Il modo migliore per confermare se un determinato browser ha corretto questo comportamento è controllare la riga "Includi intestazione intervallo nella richiesta di rete" della dashboard Web Platform Tests.

Che dire delle richieste di intervallo di pubblicazione dalla cache?

I worker di servizio possono fare molto di più che semplicemente inoltrare una richiesta alla rete. Un caso d'uso comune è l'aggiunta di risorse, come file audio e video, a una cache locale. Un worker di servizio può quindi soddisfare le richieste da questa cache, bypassando completamente la rete.

Tutti i browser, incluso Firefox, supportano l'ispezione di una richiesta all'interno di un gestore fetch, il controllo della presenza dell'intestazione Range: e la successiva soddisfazione locale della richiesta con una risposta 206 proveniente da una cache. Tuttavia, il codice del worker per analizzare correttamente l'intestazione Range: e restituire solo il segmento appropriato della risposta completa memorizzata nella cache non è banale.

Fortunatamente, gli sviluppatori che hanno bisogno di aiuto possono rivolgersi a Workbox, un insieme di librerie che semplifica i casi d'uso comuni dei service worker. workbox-range-request module implementa tutta la logica necessaria per inviare risposte parziali direttamente dalla cache. Una ricetta completa per questo caso d'uso è disponibile nella documentazione di Workbox.

L'immagine hero in questo post è di Natalie Rhea Riggs su Unsplash.