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

Javascript 使用fetch时,如何选择退出HTTP/2服务器推送?,javascript,google-chrome-extension,http2,fetch-api,Javascript,Google Chrome Extension,Http2,Fetch Api,我正在用Javascript编写一个使用新FetchAPI的基本应用程序。以下是代码相关部分的基本示例: function foo(url) { const options = {}; options.credentials = 'omit'; options.method = 'get'; options.headers = {'Accept': 'text/html'}; options.mode = 'cors'; options.cache = 'default'

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

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=脚本

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

{
  "content_security_policy": "script-src 'self'; object-src 'self'"
}
以下是有关Chrome的manifest.json格式的文档,以及如何将内容安全策略应用于扩展进行的获取:

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

我在研究中没有发现的是如何避免这种行为。看起来,在急于支持这个伟大的新特性时,制作HTTP/2和FETCH的人没有考虑到我不是为了显示它或它的任何相关资源(如CSS/MIV/Script)而获取远程页面的用例。I(应用程序)以后将不再使用任何相关资源;只有资源本身的内容

在我的用例中,这种推送(1)完全是浪费资源,(2)现在导致控制台中偶尔出现一条非常烦人的、导致压力的消息

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

我已经看了第3.3节,它没有多大帮助。我看到我可以发送像
Link:;rel=预载;as=脚本;nopush
。问题是,我还不知道响应中将包含哪些链接头,而且我也不确定fetch是否允许在初始请求中设置链接头。我想知道我是否可以发送一些类型的请求,这些请求可以看到响应中的链接头,但可以避免它们,然后发送一个追加所有适当的nopush头的后续请求

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

  • 获取最新或接近最新chrome的开发版本
  • 创建扩展文件夹
  • 使用类似的CSP创建清单
  • 将未包装的扩展加载到chrome中
  • 在devtools中打开扩展的背景页面
  • 在控制台中键入fetch(“”)
  • 检查控制台中出现的结果错误消息:拒绝加载脚本“”,因为它违反了以下内容安全策略指令:“script src'self'”
  • 补充说明:

    • 我不想使用代理服务器。明确解释为什么这是我唯一的选择是一个可以接受的答案
    • 我不知道在配置CSP时将获取哪些URL
    • 请参阅相关部分中的哪些说明“设置\u启用\u推送(0x2):此设置可用于禁用服务器推送(第8.2节)。如果端点收到此参数设置为的值,则不得发送推送承诺帧 有没有办法从脚本或清单中指定此设置,或者将其烘焙到Chrome中

    在跟随您的测试用例之后,我能够通过以下方式解决这个(示例)问题,尽管我不知道它是否适用于所有更一般的情况:

  • 使用
    chrome.webRequest
    拦截对扩展请求的响应
  • 使用
    OnHeadersReceived
    的阻止形式来去除包含
    rel=preload
  • 允许响应继续更新标题
  • 我必须承认,我花了很多时间试图弄明白为什么这似乎是可行的,因为我不认为剥离链接头应该在所有情况下都有效。我认为服务器推送会在发送请求后开始推送文件

    正如您在关于
    SETTINGS\u ENABLE\u PUSH
    的附加说明中所提到的,事实上,这些内容中的大部分都被嵌入了chrome浏览器中,隐藏在我们的视线之外。如果你想深入挖掘,我在
    chrome://net-internals/#http2
    。也许Chrome正在删除服务器推送发送的文件,这些文件在初始响应中没有相应的链接头

    此解决方案取决于
    chrome.webRequest


    扩展的背景脚本:

    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']);
    
    let trackedrl;
    函数foo(url){
    trackedrl=url;
    常量选项={};
    options.credentials='omit';
    options.method='get';
    options.headers={'Accept':'text/html'};
    options.mode='cors';
    options.cache='default';
    options.redirect='follow';
    options.referer='无推荐人';
    options.refererPolicy='no referer';
    返回获取(url、选项)
    }
    chrome.webRequest.onHeadersReceived.addListener(函数(细节){
    让新的标题;
    if(details.url.indexOf(trackedUrl)>-1){
    newHeaders=details.responseHeaders.filter(header=>{
    返回header.value.indexOf('rel=preload')<0;
    })
    }
    返回{responseHeaders:newHeaders};
    },{url:['

  • 你可能还想更具体地说明你删除了哪些标题。我觉得删除所有链接会有一些额外的效果

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

  • 这是一个非常强大的扩展,
    {
      "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"
      ]
    }