javascript - 使用 fetch 时如何选择退出 HTTP/2 服务器推送?

标签 javascript google-chrome-extension http2 fetch-api

我正在用 Javascript 编写一个使用新的 fetch 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);
}

在发出获取请求时,我偶尔会看到控制台中出现如下错误:

Refused to load the script '<url>' because it violates the following Content Security Policy directive ...

在阅读和了解 HTTP/2 后,看起来出现此消息是因为响应正在推回预加载的脚本。使用 devtools,我可以在响应中看到以下 header :

link:<path-to-script>; rel=preload; as=script

以下是我的 Chrome 扩展程序的 manifest.json 文件的相关部分:

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

以下是有关 Chrome 的 manifest.json 格式以及如何将内容安全策略应用于扩展程序进行的提取的文档:https://developer.chrome.com/extensions/contentSecurityPolicy

我做了一些测试,并能够确定此错误消息是在提取期间发生的,而不是稍后在解析响应文本时发生的。脚本元素加载到实时 DOM 中是没有问题的,这一切都在获取时发生。

我在研究中找不到的是如何避免这种行为。看起来,在急于支持这个伟大的新功能的过程中,创建 HTTP/2 和 fetch 的人并没有考虑这样的用例:我没有为了显示它或其任何相关资源(如 css)而获取远程页面/图像/脚本。我(应用程序)以后将不会使用任何相关资源;仅资源本身的内容。

在我的用例中,此推送 (1) 完全是资源浪费,并且 (2) 现在导致控制台中偶尔出现一条非常烦人且令人紧张的消息。

话虽如此,这是我希望得到帮助的问题:有没有一种方法可以使用 list 或脚本向浏览器发出信号,表明我对 HTTP/2 推送不感兴趣?我是否可以为获取请求设置一个 header ,告诉 Web 服务器不要通过推送进行响应?我可以在应用程序 list 中使用 CSP 设置来以某种方式触发“不要推送我”响应吗?

我看过https://w3c.github.io/preload/ 3.3节,没有太大帮助。我看到我可以发送像 Link: </dont/want/to/push/this>; rel=preload; as=script; nopush 这样的 header 。问题是我还不知道响应中将包含哪些链接 header ,并且我不确定 fetch 是否允许在初始请求中设置链接 header 。我想知道是否可以发送某种类型的请求,该请求可以在响应中看到链接 header 但避免它们,然后发送附加所有适当的 nopush header 的后续请求?

这是一个重现该问题的简单测试用例:

  1. 获取最新或接近最新 Chrome 的开发版本
  2. 创建扩展文件夹
  3. 使用类似的 CSP 创建 list
  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'”。

附加说明:

  • 我不想使用代理服务器。明确解释为什么这是我唯一的选择将是一个可以接受的答案。
  • 我不知道配置 CSP 时将获取的网址。
  • 参见https://www.rfc-editor.org/rfc/rfc7540#section-6.5.1其中在相关部分中指出“SETTINGS_ENABLE_PUSH (0x2):此设置可用于禁用服务器推送(第 8.2 节)。如果端点接收到此参数设置为值,则端点不得发送 PUSH_PROMISE 帧 0。”有没有办法从脚本或 list 中指定此设置,或者将其烘焙到 Chrome 中?

最佳答案

按照您的测试用例进行操作后,我能够通过以下方式解决此问题(示例),但我不知道它是否适用于所有更一般的情况:

  1. 使用chrome.webRequest拦截对扩展程序请求的响应。
  2. 使用阻止形式 onHeadersRecieved删除包含 rel=preload 的 header
  3. 允许响应使用更新后的 header 继续进行。

我必须承认我花了很多时间试图弄清楚为什么这似乎有效,因为我不认为剥离链接 header 应该在所有情况下都有效。我以为服务器推送会在发送请求后开始推送文件。

正如您在关于 SETTINGS_ENABLE_PUSH 的附加说明中提到的那样事实上,其中大部分内容都被嵌入到了 Chrome 中,并且隐藏在我们的视野之外。如果你想深入挖掘,我可以在 chrome://net-internals/#http2 找到详细信息。 。也许 Chrome 正在删除由服务器推送发送的文件,这些文件在初始响应中没有相应的链接 header 。

该解决方案取决于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']);
<小时/>

扩展程序的 list :

{
  "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"
  ]
}

附加说明:

  • 我只是天真地将其限制为来自扩展程序的最新请求网址,有 webRequest.requestFilters烘烤成chrome.webRequest您可以查看here

  • 您可能还想更具体地了解要删除的 header 。我觉得剥离所有链接会有一些额外的效果。

  • 这可以避免代理,并且不需要在请求中设置链接 header 。

  • 这使得扩展变得非常强大,我个人避免使用像<all_urls>这样的权限的扩展。 ,希望你能缩小范围。

  • 我没有测试因阻止删除 header 的响应而导致的延迟。

关于javascript - 使用 fetch 时如何选择退出 HTTP/2 服务器推送?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45352300/

相关文章:

javascript : window. location.href/window.open 在 iframe 上不起作用

javascript - 清理代码片段的 ng-content

google-chrome-extension - 我可以在 chrome 扩展中启用源映射吗?

javascript - 消息传递 : Background script to closing tab

javascript - 渲染事件后是否有 YUI 数据表?

javascript - 两个看似相同的 MapReduce 函数的令人费解的行为

javascript - Chrome 扩展 : chrome. tabs.executeScript 不工作

http - 即使强制尝试设置为 false,Go http 请求也会回退到 http2

Python `hyper` 库在我的环境中不起作用

http - HTTP/2 协议(protocol)中的 WINDOW_UPDATE 和 SETTINGS 帧有什么区别?