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

Jos*_*osh 25 javascript google-chrome-extension http2 fetch-api

我正在使用新的fetch API在Javascript中编写一个基本应用程序.以下是代码相关部分的基本示例:

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);
}
Run Code Online (Sandbox Code Playgroud)

在进行获取请求时,我偶尔会在控制台中看到如下所示的错误:

拒绝加载脚本'<url>',因为它违反了以下内容安全策略指令...

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

链接:<路径到脚本>; 相对=预载荷; 为=脚本

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

{
  "content_security_policy": "script-src 'self'; object-src 'self'"
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

在我的使用案例中,这种推送(1)完全浪费了资源,并且(2)现在导致一个非常恼人和压力诱导的消息偶尔出现在控制台中.

话虽如此,这是我希望得到一些帮助的问题:有没有办法向浏览器发信号,使用清单或脚本,我对HTTP/2推送不感兴趣?是否有我可以为获取请求设置的标头告诉Web服务器不响应推送?我可以在我的应用程序清单中使用CSP设置,以某种方式触发不要推送我的响应吗?

我查看了https://w3c.github.io/preload/第3.3节,它没有多大帮助.我看到我可以发送标题Link: </dont/want/to/push/this>; rel=preload; as=script; nopush.问题是我还不知道响应中会有哪些链接头,我不确定fetch是否允许在初始请求中设置链接头.我想知道我是否可以发送某些类型的请求,可以在响应中看到链接头但是避免它们,然后发送追加所有相应nopush头的后续请求?

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

  1. 获取最新或近乎最新的chrome的开发版本
  2. 创建扩展文件夹
  3. 使用类似的CSP创建清单
  4. 加载扩展为解压缩到chrome中
  5. 在devtools中打开扩展的后台页面
  6. 在控制台类型获取(' https://www.yahoo.com ').
  7. 检查控制台中显示的结果错误消息:拒绝加载脚本" https://www.yahoo.com/sy/rq/darla/2-9-20/js/gr-min.js ",因为它违反了以下内容安全策略指令:"script-src'self'".

补充说明:

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

Ale*_*fis 5

在跟随您的测试用例之后,我能够以下列方式解决此问题(示例),但我不知道它适用于所有更一般的情况:

  1. 使用chrome.webRequest拦截对扩展的请求的响应.
  2. 使用阻止形式onHeadersRecieved去除包含的标题rel=preload
  3. 允许响应继续更新的标头.

我不得不承认我花了很多时间试图弄清楚为什么这似乎有效,因为我不认为剥离链接头应该适用于所有情况.我认为Server Push会在发送请求后才开始推送文件.

正如您在附加说明中所提到的,其中SETTINGS_ENABLE_PUSH大部分内容实际上已经融入了Chrome并且隐藏在我们的视图中.如果你想深入挖掘,我发现了详细信息chrome://net-internals/#http2.也许Chrome会杀死Server 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']);
Run Code Online (Sandbox Code Playgroud)

扩展的清单:

{
  "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"
  ]
}
Run Code Online (Sandbox Code Playgroud)

补充说明:

  • 我只是天真地将这个限制到扩展的最新请求网址,有 webRequest.requestFilters烤到chrome.webRequest你可以在这里查看

  • 您可能还希望更加具体地了解您删除哪些标头.我觉得剥离所有的链接都会有一些额外的效果.

  • 这避免了代理,并且不需要在请求中设置链接头.

  • 这是一个非常强大的扩展,我个人避免使用权限扩展<all_urls>,希望你可以缩小范围.

  • 我没有测试阻止删除标头响应导致的延迟.