Подтвердить что ты не робот

Как отключить HTTP/2-сервер при использовании выборки?

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

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

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

Отказано для загрузки script '<url> ' потому что он нарушает следующую директиву политики безопасности контента...

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

ссылка: < путь-to script > ; отно = предварительная нагрузка; а = script

Ниже приведена соответствующая часть моего файла расширения manifest.json.

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

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

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

То, что я не смог найти в своих исследованиях, заключалось в том, как избежать такого поведения. Похоже, что в спешке, чтобы поддержать эту замечательную новую функцию, люди, которые делали HTTP/2 и выборку, не рассматривали вариант использования, когда я не получаю удаленную страницу с целью ее отображения или любых связанных с ней ресурсов, таких как css/изображение/ script. Я (приложение) никогда не будет использовать какой-либо связанный ресурс; только содержимое самого ресурса.

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

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

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

Вот простой тестовый пример, чтобы воспроизвести проблему:

  • Получить версию разработчика последней или самой последней версии chrome
  • Создать папку расширения
  • Создать манифест с аналогичным CSP
  • Расширение загрузки как распакованное в хром
  • Откройте базовую страницу для расширения в devtools
  • В консоли типа fetch ('https://www.yahoo.com').
  • Изучите полученное сообщение об ошибке, появившееся в консоли: Отказано для загрузки script 'https://www.yahoo.com/sy/rq/darla/2-9-20/js/g-r-min.js', поскольку оно нарушает следующие Политика политики безопасности содержимого: "script -src" self ".

Дополнительные примечания:

  • Я не хочу использовать прокси-сервер. Ясное объяснение того, почему это будет моим единственным вариантом, будет приемлемым ответом.
  • Я не знаю URL-адреса, которые будут выбраны во время настройки CSP.
  • См. https://tools.ietf.org/html/rfc7540#section-6.5.1, в котором говорится, что" SETTINGS_ENABLE_PUSH (0x2): этот параметр можно использовать для отключения нажатия на сервер (раздел 8.2). Конечная точка НЕ ​​ДОЛЖНА отправлять кадр PUSH_PROMISE, если он принимает этот параметр, чтобы установить значение 0. "Есть ли способ указать этот параметр из script или манифест или он испечен в Chrome?
4b9b3361

Ответ 1

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

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

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

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

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


Фон расширения script:

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, вы можете проверить здесь

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

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

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

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