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

时间:2017-07-27 13:31:05

标签: 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);
}

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

  

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

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

  

链接:其中路径到脚本取代;相对=预载荷;如=脚本

以下是我的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)现在导致一个非常恼人和压力诱导的消息偶尔出现在控制台中。

话虽如此,这是我希望得到一些帮助的问题:有没有办法向浏览器发信号,使用清单或脚本,我对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. 获取最新版本或近乎最新版本的开发版
  2. 创建扩展文件夹
  3. 使用类似的CSP创建清单
  4. 将扩展程序加载到chrome中
  5. 在devtools
  6. 中打开扩展程序的后台页面
  7. 在控制台类型提取中(&#39; https://www.yahoo.com&#39;)。
  8. 检查控制台中显示的结果错误消息:拒绝加载脚本&#39; https://www.yahoo.com/sy/rq/darla/2-9-20/js/g-r-min.js&#39;因为它违反了以下内容安全策略指令:&#34; script-src&#39; self&#39;&#34;。
  9. 附加说明:

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

1 个答案:

答案 0 :(得分:5)

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

  1. 使用chrome.webRequest拦截对扩展程序请求的回复。
  2. 使用onHeadersRecieved的屏蔽形式删除包含rel=preload
  3. 的标题
  4. 允许响应继续更新的标题。
  5. 我不得不承认我花了很多时间试图弄清楚为什么这似乎有用,因为我不认为剥离链接标题应该适用于所有情况。我认为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']);
    

    扩展程序的显示:

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

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

    • 这可以避免使用代理,也不需要在请求中设置链接标头。

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

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