Задний план
Сервис-воркеры дают веб-разработчикам возможность отвечать на сетевые запросы, сделанные их веб-приложениями, позволяя им продолжать работу даже в автономном режиме, бороться с ложью и реализовывать сложные взаимодействия с кешем, такие как устаревшее при повторной проверке. Но сервис-воркеры исторически были привязаны к определенному источнику - вы, как владелец веб-приложения, обязаны написать и развернуть сервис-воркера для перехвата всех сетевых запросов, которые делает ваше веб-приложение. В этой модели каждый работник службы отвечает за обработку даже запросов из разных источников, например, к стороннему API или за веб-шрифты.
Что, если бы сторонний поставщик API, веб-шрифтов или другой часто используемой службы имел возможность развернуть своего собственного служебного воркера, у которого была возможность обрабатывать запросы, сделанные другими источниками к их источнику ? Провайдеры могут реализовать свою собственную сетевую логику и использовать единственный авторитетный экземпляр кеша для хранения своих ответов. Теперь, благодаря внешней выборке, такое развертывание сторонних сервис-воркеров стало реальностью.
Развертывание сервис-воркера, реализующего стороннюю выборку, имеет смысл для любого поставщика сервиса, доступ к которому осуществляется через HTTPS-запросы из браузеров - просто подумайте о сценариях, в которых вы могли бы предоставить независимую от сети версию своего сервиса, в которой браузеры могли бы воспользоваться преимуществами общий ресурсный кеш. Услуги, которые могут получить от этого выгоду, включают, но не ограничиваются:
- Поставщики API с интерфейсами RESTful
- Поставщики веб-шрифтов
- Провайдеры аналитики
- Провайдеры хостинга изображений
- Универсальные сети доставки контента
Представьте, например, что вы поставщик аналитических услуг. Развертывая стороннего работника службы выборки, вы можете гарантировать, что все запросы к вашей службе, которые завершаются ошибкой, пока пользователь находится в автономном режиме, ставятся в очередь и воспроизводятся после восстановления подключения. Хотя клиенты службы могли реализовать подобное поведение с помощью сторонних рабочих служб, требовать от каждого клиента написания индивидуальной логики для вашей службы не так масштабируемо, как использование совместно используемого внешнего работника службы выборки, который вы развертываете.
Предпосылки
Жетон пробной версии Origin
Зарубежная загрузка по-прежнему считается экспериментальной. Чтобы избежать преждевременного внедрения этого дизайна до того, как он будет полностью определен и согласован поставщиками браузеров, он был реализован в Chrome 54 как пробная версия. Пока внешняя выборка остается экспериментальной, чтобы использовать эту новую функцию с сервисом, который вы размещаете, вам нужно будет запросить токен, привязанный к конкретному источнику вашего сервиса. Маркер должен быть включен в качестве заголовка HTTP-ответа во все запросы между источниками для ресурсов, которые вы хотите обрабатывать с помощью внешней выборки, а также в ответ для ресурса JavaScript вашего сервис-воркера:
Origin-Trial: token_obtained_from_signup
Пробная версия завершится в марте 2017 года. К этому моменту мы ожидаем выяснить все изменения, необходимые для стабилизации функции, и (надеюсь) включить ее по умолчанию. Если к тому времени сторонняя выборка не включена по умолчанию, функциональность, связанная с существующими токенами Origin Trial, перестанет работать.
Чтобы упростить эксперименты с зарубежной выборкой перед регистрацией для получения официального токена Origin Trial, вы можете обойти требование в Chrome для своего локального компьютера, перейдя в chrome: // flags / # enable-experimental-web-platform-features и включить флаг «Экспериментальные возможности веб-платформы». Обратите внимание, что это необходимо сделать в каждом экземпляре Chrome, который вы хотите использовать в своих локальных экспериментах, тогда как с токеном Origin Trial эта функция будет доступна всем вашим пользователям Chrome.
HTTPS
Как и во всех развертываниях сервис-воркеров, веб-сервер, который вы используете для обслуживания своих ресурсов и скрипта сервис-воркера, должен быть доступен через HTTPS. Кроме того, перехват внешней выборки применяется только к запросам, исходящим со страниц, размещенных в безопасных источниках, поэтому клиентам вашей службы необходимо использовать HTTPS, чтобы воспользоваться преимуществами реализации вашей внешней выборки.
Использование внешней выборки
Разобравшись с предварительными условиями, давайте углубимся в технические детали, необходимые для запуска и работы иностранного работника службы fetch.
Регистрация вашего обслуживающего работника
Первая проблема, с которой вы, вероятно, столкнетесь, - это как зарегистрировать своего обслуживающего работника. Если вы раньше работали с обслуживающим персоналом, вы, вероятно, знакомы со следующим:
// You can't do this!
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js');
}
Этот код JavaScript для регистрации основного сервис-воркера имеет смысл в контексте веб-приложения, запускаемого пользователем, переходящим по URL-адресу, который вы контролируете. Но это нежизнеспособный подход к регистрации стороннего сервис-воркера, когда единственное взаимодействие браузера с вашим сервером - это запрос определенного подресурса, а не полной навигации. Если браузер запрашивает, скажем, изображение с сервера CDN, который вы обслуживаете, вы не можете добавить этот фрагмент кода JavaScript к своему ответу и ожидать, что он будет запущен. Требуется другой метод регистрации сервис-воркера, выходящий за рамки обычного контекста выполнения JavaScript.
Решение представлено в виде HTTP-заголовка, который ваш сервер может включать в любой ответ:
Link: </service-worker.js>; rel="serviceworker"; scope="/"
Давайте разберем этот заголовок примера на компоненты, каждый из которых разделен знаком; персонаж.
- ‹/Service-worker.js› является обязательным и используется для указания пути к файлу вашего сервис-воркера (замените /service-worker.js на соответствующий путь к вашему скрипту). Это напрямую соответствует строке scriptURL, которая в противном случае была бы передана в качестве первого параметра в navigator.serviceWorker.register (). Значение должно быть заключено в символы ‹› (в соответствии с требованиями спецификации заголовка ссылки), и если указан относительный, а не абсолютный URL-адрес, он будет интерпретироваться как относящийся к местоположению ответа.
- rel = ”serviceworker” также является обязательным и должен быть включен без какой-либо настройки.
- scope = ”/” - необязательное объявление области, эквивалентное строке options.scope, которую вы можете передать в качестве второго параметра в navigator.serviceWorker.register (). Для многих случаев использования вам подходит область видимости по умолчанию, поэтому не стесняйтесь опускать это, если вы не знаете, что вам это нужно. Те же ограничения в отношении максимально допустимой области, наряду с возможностью ослабить эти ограничения с помощью заголовка Service-Worker-Allowed, применяются к регистрации заголовков ссылок.
Как и в случае с «традиционной» регистрацией сервис-воркера, использование заголовка Link установит сервис-воркер, который будет использоваться для следующего запроса, сделанного против зарегистрированной области. Тело ответа, содержащее специальный заголовок, будет использоваться как есть и сразу же будет доступно для страницы, не дожидаясь завершения установки сторонним работником службы поддержки.
Помните, что сторонняя выборка в настоящее время реализована как Origin Trial, поэтому наряду с заголовком ответа Link вам также необходимо включить действительный заголовок Origin-Trial. Минимальный набор заголовков ответов, которые нужно добавить для регистрации внешнего работника службы выборки, составляет
Link: </service-worker.js>; rel="serviceworker" Origin-Trial: token_obtained_from_signup
Примечание. Проницательные читатели спецификации сервис-воркера, возможно, заметили еще один способ регистрации сервис-воркера с помощью элемента DOM ‹link rel =” serviceworker ”›. Поддержка регистрации на основе ‹link› в Chrome в настоящее время контролируется той же пробной версией Origin, что и заголовок ссылки, поэтому по умолчанию она еще не включена. Регистрация на основе ‹link› имеет те же ограничения, что и регистрация на основе JavaScript, когда дело доходит до регистрации зарубежной выборки, поэтому для целей этой статьи вам следует использовать заголовок Link.
Регистрация отладки
Во время разработки вы, вероятно, захотите убедиться, что ваш сторонний работник службы выборки правильно установлен и обрабатывает запросы. Есть несколько вещей, которые вы можете проверить в Инструментах разработчика Chrome, чтобы убедиться, что все работает должным образом.
Отправляются ли правильные заголовки ответа?
Чтобы зарегистрировать внешнего работника службы выборки, вам необходимо установить заголовок Link для ответа на ресурс, размещенный в вашем домене, как описано ранее в этом посте. В течение пробного периода Origin и при условии, что у вас не установлен chrome: // flags / # enable-experimental-web-platform-features, вам также необходимо установить заголовок ответа Origin-Trial. Вы можете убедиться, что ваш веб-сервер устанавливает эти заголовки, посмотрев на запись в Сетевая панель DevTools:

Правильно ли зарегистрирован иностранный работник службы доставки?
Вы также можете подтвердить регистрацию базового сервис-воркера, включая его объем, просмотрев полный список сервис-воркеров на панели приложения DevTools. Обязательно выберите параметр Показать все, поскольку по умолчанию вы будете видеть только сервис-воркеров для текущего происхождения.

Обработчик события установки
Теперь, когда вы зарегистрировали своего стороннего сервис-воркера, он получит возможность реагировать на события установки и активации, как и любой другой сервис-воркер. Он может использовать преимущества этих событий, например, для заполнения кэшей необходимыми ресурсами во время события установки или удаления устаревших кешей в событии активации.
Помимо обычных действий кэширования событий установки, есть дополнительный этап, который требуется внутри обработчика событий установки стороннего сервис-воркера. Ваш код должен вызвать registerForeignFetch (), как в следующем примере:
self.addEventListener('install', event => {
event.registerForeignFetch({
scopes: [self.registration.scope], // or some sub-scope
origins: ['*'] // or ['https://example.com']
});
});
Есть два варианта конфигурации, оба обязательны:
- scopes принимает массив из одной или нескольких строк, каждая из которых представляет область для запросов, которые вызовут событие foreignfetch. Но подождите, вы можете подумать, Я уже определил область во время регистрации сервис-воркера! Это правда, и эта общая область по-прежнему актуальна - каждая указанная вами область здесь должно быть либо равно, либо быть частью общего объема работника службы поддержки. Дополнительные ограничения области видимости позволяют вам развернуть универсального сервис-воркера, который может обрабатывать как события первичной выборки (для запросов, сделанных с вашего собственного сайта), так и сторонние события внешней выборки (для запросов, сделанных из других доменов), а также выполнять ясно, что только подмножество вашей более крупной области должно запускать foreignfetch. На практике, если вы развертываете сервис-воркер, предназначенный для обработки только сторонних событий, externalfetch, вам просто нужно использовать единственную явную область действия, которая равна общей области действия вашего служебного воркера. Это то, что будет делать в приведенном выше примере, используя значение self.registration.scope.
- origins также принимает массив из одной или нескольких строк и позволяет ограничить обработчик foreignfetch только ответом на запросы из определенных доменов. Например, если вы явно занесли в белый список https://example.com, то запрос, сделанный со страницы, размещенной по адресу https://example.com/path/to/page.html, для ресурса, обслуживаемого из вашего зарубежного Область fetch запустит ваш внешний обработчик выборки, но запросы, сделанные из https://random-domain.com/path/to/page.html, не будут запускать ваш обработчик. Если у вас нет особой причины запускать логику внешней выборки только для подмножества удаленных источников, вы можете просто указать * в качестве единственного значения в массиве, и все источники будут занесены в белый список.
Обработчик событий foreignfetch
Теперь, когда вы установили стороннего сервис-воркера и настроили его с помощью registerForeignFetch (), он получит возможность перехватывать кросс-исходные запросы субресурсов к вашему серверу, которые попадают в область внешней выборки.
Примечание. В текущей реализации Chrome существует дополнительное ограничение: только запросы GET, POST или HEAD, которые содержат только заголовки, внесенные в список безопасных CORS, подходят для внешней выборки. Это ограничение не является частью спецификации внешней выборки и может быть ослаблено в будущих версиях Chrome.
В традиционном собственном сервис-воркере каждый запрос запускал событие выборки, на которое ваш сервис-воркер имел возможность ответить. Наш сторонний сервис-воркер получает возможность обработать немного другое событие с именем foreignfetch. Концептуально эти два события очень похожи, и они дают вам возможность проверить входящий запрос и, при необходимости, предоставить ответ на него через responseWith ():
self.addEventListener('foreignfetch', event => {
// Assume that requestLogic() is a custom function that takes
// a Request and returns a Promise which resolves with a Response.
event.respondWith(
requestLogic(event.request).then(response => {
return {
response: response,
// Omit to origin to return an opaque response.
// With this set, the client will receive a CORS response.
origin: event.origin,
// Omit headers unless you need additional header filtering.
// With this set, only Content-Type will be exposed.
headers: ['Content-Type']
};
})
);
});
Несмотря на концептуальное сходство, на практике есть несколько различий при вызове responseWith () в ForeignFetchEvent. Вместо того, чтобы просто предоставлять Response (или Promise, которое разрешается с помощью Response) для responseWith (), как вы делаете с FetchEvent, вам нужно передать Promise, которое разрешается с помощью объекта с определенными свойствами, в ForeignFetchEvent. responseWith ():
- response является обязательным и должен быть установлен на объект Response, который будет возвращен клиенту, сделавшему запрос. Если вы предоставите что-либо, кроме действительного ответа, запрос клиента будет прекращен из-за сетевой ошибки. В отличие от вызова responseWith () внутри обработчика события выборки, вы должны предоставить здесь Response, а не обещание, которое разрешается с помощью Response! Вы можете создать свой ответ с помощью цепочки обещаний и передать эту цепочку в качестве параметра функции responseWith () из foreignfetch, но цепочка должна разрешаться с помощью объекта, который содержит свойство ответа, установленное для объекта Response. Вы можете увидеть демонстрацию этого в приведенном выше примере кода.
- origin не является обязательным и используется для определения того, является ли возвращаемый ответ непрозрачным. Если вы не укажете этот параметр, ответ будет непрозрачным, а доступ клиента к телу и заголовкам ответа будет ограничен. Если запрос был сделан с mode:‘ cors ’, то возврат непрозрачного ответа будет рассматриваться как ошибка. Однако, если вы укажете строковое значение, равное источнику удаленного клиента (которое можно получить через event.origin), вы явно включите предоставление клиенту ответа с поддержкой CORS.
- заголовки также являются необязательными и полезны только в том случае, если вы также указываете источник и возвращаете ответ CORS. По умолчанию в ваш ответ будут включены только заголовки из списка заголовков ответов, включенных в список надежных CORS. Если вам нужно дополнительно отфильтровать то, что возвращается, заголовки принимают список из одного или нескольких имен заголовков, и он будет использовать его в качестве белого списка заголовков, отображаемых в ответе. Это позволяет вам подписаться на CORS, в то же время предотвращая передачу потенциально конфиденциальных заголовков ответов напрямую удаленному клиенту.
Важно отметить, что при запуске обработчика foreignfetch он имеет доступ ко всем учетным данным и внешним полномочиям источника, на котором размещен сервис-воркер. Как разработчик, развертывающий стороннего сервис-воркера с поддержкой выборки, вы несете ответственность за то, чтобы не допустить утечки каких-либо привилегированных данных ответа, которые в противном случае были бы недоступны благодаря этим учетным данным. Требование согласия на получение ответов CORS - это один из шагов для ограничения непреднамеренного воздействия, но как разработчик вы можете явно делать запросы fetch () внутри своего обработчика foreignfetch, которые не используют подразумеваемые учетные данные через:
self.addEventListener('foreignfetch', event => {
// The new Request will have credentials omitted by default.
const noCredentialsRequest = new Request(event.request.url);
event.respondWith(
// Replace with your own request logic as appropriate.
fetch(noCredentialsRequest)
.catch(() => caches.match(noCredentialsRequest))
.then(response => ({response}))
);
});
Соображения клиента
Есть некоторые дополнительные соображения, которые влияют на то, как ваш сторонний работник службы выборки обрабатывает запросы, сделанные от клиентов вашей службы.
Клиенты, у которых есть собственный сервисный работник
У некоторых клиентов вашего сервиса уже может быть свой собственный сервис-воркер, обрабатывающий запросы, исходящие из их веб-приложения. Что это означает для стороннего сотрудника службы доставки извне?
Обработчики выборки в стороннем сервис-воркере получают первую возможность ответить на все запросы, сделанные веб-приложением, даже если есть сторонний сервис-воркер с включенной внешней выборкой и областью действия, покрывающей запрос. Но клиенты со сторонними сервис-воркерами по-прежнему могут воспользоваться вашим зарубежным сервис-воркером!
Внутри стороннего сервис-воркера использование fetch () для извлечения ресурсов из разных источников вызовет соответствующий внешний сервис-воркер выборки. Это означает, что код, подобный следующему, может воспользоваться вашим обработчиком foreignfetch:
// Inside a client's first-party service-worker.js:
self.addEventListener('fetch', event => {
// If event.request is under your foreign fetch service worker's
// scope, this will trigger your foreignfetch handler.
event.respondWith(fetch(event.request));
});
Точно так же, если есть собственные обработчики выборки, но они не вызывают event.respondWith () при обработке запросов к вашему ресурсу из разных источников, запрос автоматически «провалится» вашему обработчику foreignfetch:
// Inside a client's first-party service-worker.js:
self.addEventListener('fetch', event => {
if (event.request.mode === 'same-origin') {
event.respondWith(localRequestLogic(event.request));
}
// Since event.respondWith() isn't called for
// cross-origin requests, any foreignfetch handlers scoped to
// the request will get a chance to provide a response.
});
Если собственный обработчик выборки вызывает event.respondWith (), но не использует fetch () для запроса ресурса в пределах вашей внешней области действия выборки, тогда ваш работник службы внешней выборки не получит возможности обработать запрос.
Клиенты, у которых нет собственного обслуживающего персонала
Все клиенты, которые отправляют запросы к стороннему сервису, могут получить выгоду, когда сервис развертывает стороннего сервисного воркера fetch, даже если они еще не используют своего собственного сервис-воркера. Нет ничего особенного, что клиенты должны делать, чтобы отказаться от использования стороннего работника службы выборки, если они используют браузер, который его поддерживает. Это означает, что при развертывании внешнего работника службы выборки ваша настраиваемая логика запросов и общий кеш немедленно принесут пользу многим клиентам вашей службы, без каких-либо дополнительных действий.
Собираем все вместе: где клиенты ждут ответа
Принимая во внимание приведенную выше информацию, мы можем собрать иерархию источников, которые клиент будет использовать для поиска ответа на запрос кросс-происхождения.
- Обработчик выборки стороннего сервис-воркера (если есть)
- Обработчик foreignfetch стороннего сервис-воркера (если есть, и только для запросов из разных источников)
- HTTP-кеш браузера (если есть свежий ответ)
- Сеть
Браузер запускается сверху и, в зависимости от реализации сервис-воркера, будет продолжать движение вниз по списку, пока не найдет источник ответа.
Выучить больше
Будьте в курсе
Реализация внешней пробной версии Origin в Chrome может быть изменена, поскольку мы принимаем во внимание отзывы разработчиков. Мы будем поддерживать этот пост в актуальном состоянии, внося в него встроенные изменения, и будем отмечать конкретные изменения ниже по мере их появления. Мы также будем делиться информацией о серьезных изменениях через аккаунт Twitter @chromiumdev.
Первоначально опубликовано на developers.google.com.