我正在用 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 的后续请求?
这是一个重现该问题的简单测试用例:
- 获取最新或接近最新 Chrome 的开发版本
- 创建扩展文件夹
- 使用类似的 CSP 创建 list
- 将扩展程序加载到 Chrome 中解压
- 在 devtools 中打开扩展程序的后台页面
- 在控制台中输入 fetch('https://www.yahoo.com')。
- 检查控制台中显示的错误消息:拒绝加载脚本“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 中?
最佳答案
按照您的测试用例进行操作后,我能够通过以下方式解决此问题(示例),但我不知道它是否适用于所有更一般的情况:
- 使用
chrome.webRequest
拦截对扩展程序请求的响应。 - 使用阻止形式
onHeadersRecieved
删除包含rel=preload
的 header - 允许响应使用更新后的 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/