Обработка запросов диапазона в сервисном работнике

Убедитесь, что ваш сервисный работник знает, что делать, когда запрашивается частичный ответ.

Некоторые HTTP-запросы содержат заголовок Range: :, указывающий, что должна быть возвращена только часть полного ресурса. Они обычно используются для потоковой передачи аудио- или видеоконтента, чтобы обеспечить загрузку небольших фрагментов мультимедиа по требованию вместо того, чтобы запрашивать весь удаленный файл сразу.

Сервис-воркер — это код JavaScript, который находится между вашим веб-приложением и сетью, потенциально перехватывая исходящие сетевые запросы и генерируя на них ответы.

Исторически сложилось так, что запросы диапазона и сервисные работники не очень хорошо взаимодействовали друг с другом. Было необходимо принять специальные меры, чтобы избежать плохих результатов для вашего сервис-воркера. К счастью, ситуация начинает меняться. В браузерах, демонстрирующих правильное поведение, запросы диапазона будут «просто работать» при прохождении через сервис-воркера.

В чем проблема?

Рассмотрим сервис-воркера со следующим прослушивателем событий fetch , который принимает каждый входящий запрос и передает его в сеть:

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

В браузерах с неправильным поведением, если event.request включал заголовок Range: этот заголовок автоматически удалялся. Запрос, полученный удаленным сервером, вообще не будет включать Range: Это не обязательно что-либо «сломает», поскольку серверу технически разрешено возвращать полное тело ответа с кодом состояния 200 , даже если в исходном запросе присутствует заголовок Range: Но это приведет к передаче большего количества данных, чем это строго необходимо с точки зрения браузера.

Разработчики, которые знали об этом поведении, могли обойти это, явно проверив наличие заголовка Range: и не вызывая event.respondWith() , если он присутствует. Сделав это, сервис-воркер фактически удаляет себя из картины формирования ответа и вместо этого используется сетевая логика браузера по умолчанию, которая знает, как сохранять запросы диапазона.

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

Однако можно с уверенностью сказать, что большинство разработчиков не осознавали необходимости этого. И было непонятно, зачем это нужно. В конечном счете, это ограничение было связано с тем, что браузерам нужно было успевать за изменениями в базовой спецификации , которая добавляла поддержку этой функциональности.

Что исправлено?

Браузеры, которые ведут себя правильно, сохраняют заголовок Range: при передаче event.request в fetch() . Это означает, что код сервисного работника в моем первоначальном примере позволит удаленному серверу видеть заголовок Range: если он был установлен браузером:

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

Теперь сервер получает возможность правильно обработать запрос диапазона и вернуть частичный ответ с кодом состояния 206 .

Какие браузеры ведут себя корректно?

Последние версии Safari имеют правильную функциональность . Chrome и Edge, начиная с версии 87 , также ведут себя корректно.

По состоянию на октябрь 2020 года Firefox еще не исправил это поведение, поэтому вам все равно придется учитывать его при развертывании кода вашего сервис-воркера в рабочей среде.

Проверка строки «Включить заголовок диапазона в сетевой запрос» на панели инструментов «Тесты веб-платформы» — лучший способ проверить, исправил ли данный браузер это поведение.

А как насчет обслуживания запросов диапазона из кеша?

Сервисные работники могут сделать гораздо больше, чем просто передать запрос в сеть. Распространенным вариантом использования является добавление ресурсов, таких как аудио- и видеофайлы, в локальный кеш . Затем сервисный работник может выполнять запросы из этого кэша, полностью минуя сеть.

Все браузеры, включая Firefox, поддерживают проверку запроса внутри обработчика fetch , проверку наличия заголовка Range: а затем локальное выполнение запроса с ответом 206 , поступающим из кеша. Однако код сервисного работника для правильного анализа заголовка Range: и возврата только соответствующего сегмента полного кэшированного ответа не является тривиальным.

К счастью, разработчики, которым нужна помощь, могут обратиться к Workbox , который представляет собой набор библиотек, упрощающий типичные сценарии использования сервис-воркеров. workbox-range-request module реализует всю логику, необходимую для обслуживания частичных ответов непосредственно из кеша. Полный рецепт этого варианта использования можно найти в документации Workbox .

Изображение героя в этом посте создано Натали Реей Риггс на Unsplash.