Как отказаться от HTTP / 2 server push при использовании fetch?

Я пишу базовое приложение на Javascript, которое использует новый API выборки. Вот основной пример соответствующей части кода:

function foo(url) {
  const options = {};
  options.credentials = 'omit';
  options.method = 'get';
  options.headers = {'Accept': 'text/html'};
  options.mode = 'cors';
  options.cache = 'default';
  options.redirect = 'follow';
  options.referrer = 'no-referrer';
  options.referrerPolicy = 'no-referrer';
  return fetch(url, options);
}

при выполнении запроса выборки я иногда вижу, что в консоли появляются ошибки, которые выглядят следующим образом:

отказался загружать скрипт ' ', поскольку он нарушает следующую директиву политики безопасности контента ...

после некоторого чтения и изучения HTTP / 2, это выглядит так это сообщение появляется, потому что ответ отталкивает предварительно загруженный сценарий. Используя devtools, я вижу следующий заголовок в ответе:

ссылка:; rel=предварительная загрузка; as=script

вот соответствующая часть манифеста моего расширения Chrome.файл json:

{
  "content_security_policy": "script-src 'self'; object-src 'self'"
}

вот документация по манифесту Chrome.формат json и применение политики безопасности содержимого к выборкам, выполняемым расширением: https://developer.chrome.com/extensions/contentSecurityPolicy

Я провел некоторое тестирование и смог определить, что это сообщение об ошибке происходит во время выборки, а не позже при разборе текста ответа. Нет проблем, когда элемент сценария загружается в live DOM, все это происходит во время выборки.

в моем случае использования этот толчок (1) является полной тратой ресурсов, и (2) теперь вызывает действительно раздражающее и вызывающее стресс сообщение, которое спорадически появляется в приставка.

С учетом сказанного, вот вопрос, с которым я хотел бы помочь: есть ли способ сигнализировать браузеру, используя манифест или скрипт, что я не заинтересован в HTTP/2 push? Есть ли заголовок, который я могу установить для запроса выборки, который говорит веб-серверу не отвечать push? Есть ли параметр CSP, который я могу использовать в манифесте приложения, который каким-то образом запускает ответ "не толкайте меня"?

Я посмотрел на https://w3c.github.io/preload/ раздел 3.3, it это не очень помогло. Я вижу, что могу отправлять заголовки типа Link: </dont/want/to/push/this>; rel=preload; as=script; nopush. Проблема в том, что я еще не знаю, какие заголовки ссылок будут в ответе, и я не уверен, что fetch даже разрешает устанавливать заголовки ссылок в начальном запросе. Интересно, могу ли я отправить какой-то запрос, который может видеть заголовки ссылок в ответе, но избегает их, а затем отправить следующий запрос, который добавляет все соответствующие заголовки nopush?

вот простой тестовый пример для воспроизведения выпуск:

  1. получить dev версию последней или около последней chrome
  2. создать папку расширения
  3. создать манифест с аналогичным CSP
  4. загрузить расширение как распаковано в chrome
  5. откройте фоновую страницу для расширения в devtools
  6. в консоли введите fetch ('https://www.yahoo.com').
  7. Проверьте полученное сообщение об ошибке, которое появляется в консоли:отказалась чтобы загрузить скрипт'https://www.yahoo.com/sy/rq/darla/2-9-20/js/g-r-min.js ' потому что это нарушает следующую директиву политики безопасности контента:"script-src 'self'".

дополнительно:

  • я не хочу использовать прокси-сервер. Ясное объяснение, почему это было бы моим единственным вариантом, было бы приемлемым ответом.
  • я не знаю URL, которые будут извлечены во время настройки СПС.
  • см.https://tools.ietf.org/html/rfc7540#section-6.5.1 в соответствующей части указано ,что " SETTINGS_ENABLE_PUSH (0x2): этот параметр можно использовать для отключения push-сервера (раздел 8.2). Конечная точка не должна отправлять кадр PUSH_PROMISE, если она получает этот параметр, установленный в значение 0."Есть ли способ указать этот параметр из сценария или манифеста или он запечен в Chrome?

1 ответов


после следования вместе с вашим тестовым случаем я смог решить эту (пример) проблему следующим образом, хотя я не знаю, что это относится ко всем более общим случаям:

  1. использовать chrome.webRequest для перехвата ответов на запросы расширения.
  2. используйте блокирующую форму onHeadersRecieved чтобы удалить заголовки, содержащие rel=preload
  3. разрешить ответу перейти к обновленным заголовкам.

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

как вы упомянули в своем дополнительном примечании о SETTINGS_ENABLE_PUSH многое из этого на самом деле запечено в хроме и скрыто от нашего взгляда. Если вы хотите копать глубже, я нашел детали в chrome://net-internals/#http2. Возможно, Chrome убивает файлы, отправленные сервером Push, у которых нет соответствующий заголовок ссылки в исходном ответе.

это решение зависит от chrome.webRequest Docs


фоновый скрипт расширения:

let trackedUrl;

function foo(url) {
  trackedUrl = url;
  const options = {};
  options.credentials = 'omit';
  options.method = 'get';
  options.headers = { 'Accept': 'text/html' };
  options.mode = 'cors';
  options.cache = 'default';
  options.redirect = 'follow';
  options.referrer = 'no-referrer';
  options.referrerPolicy = 'no-referrer';
  return fetch(url, options)
}

chrome.webRequest.onHeadersReceived.addListener(function (details) {
  let newHeaders;
  if (details.url.indexOf(trackedUrl) > -1) {
    newHeaders = details.responseHeaders.filter(header => {
      return header.value.indexOf('rel=preload') < 0;
    })
  }

  return { responseHeaders: newHeaders };
}, { urls: ['<all_urls>'] }, ['responseHeaders', 'blocking']);

манифест расширения:

{
  "manifest_version": 2,
  "name": "Example",
  "description": "WebRequest Blocking",
  "version": "1.0",
  "browser_action": {
    "default_icon": "icon.png"
  },
  "background": {
    "scripts": [
      "back.js"
    ]
  },
  "content_security_policy": "script-src 'self'; object-src 'self'",
  "permissions": [
    "<all_urls>",
    "background",
    "webRequest",
    "webRequestBlocking"
  ]
}

Дополнительная Информация:

  • Я просто наивно ограничиваю это последним url-адресом запроса от расширения, есть webRequest.requestFilters запеченные в chrome.webRequest вы можете проверить здесь

  • вы, вероятно,также захотите быть более конкретными о том, какие заголовки вы снимаете. Я чувствую, что удаление всех ссылок будет иметь некоторые дополнительные эффекты.

  • это позволяет избежать прокси и не требует установки заголовка ссылки в запросе.

  • это делает довольно мощное расширение, лично я избегаю расширений с разрешениями, такими как <all_urls>, надеюсь, вы можете сузить масштаб.

  • Я не проверял задержки, вызванные блокировкой ответов на удаление заголовков.