Опубликовано: 6 октября 2020 г.
Некоторые 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 .