HTTP/2推送之难,远超想象(3)

本文会多次���到“凭据”。凭据是指浏览器发送的,代表特定用户身份的信息。通常这意味着Cookie,但也可能意味着HTTP基本身份验证,以及连接层面的身份,例如客户端证书。

可以将HTTP/2连接想象成电话通话,一旦你说出自己身份,此次通话就不再是匿名的了,而你在自我介绍前说过的一切也都不再是匿名的。出于隐私方面的考虑,浏览器会为“匿名”请求设置一个单独的“通话”。

然而由于推送缓存是与连接共存的,因此在发起不含凭据的缓存后,可能会失去某些缓存的项。举例来说,如果伴随页面(使用包含凭据的请求)推送了资源,随后(使用不含凭据的请求)fetch()该资源,这会新建一个连接,导致无法获取已推送的项。

如果跨源样式表(含凭据)推送了一个字体,浏览器的字体请求(不含凭据)将无法获得推送缓存中的字体。

建议

确保请求使用相同的凭据模式。大部分情况下,这意味着需要确保请求包含凭据,因为页面请求通常总是包含凭据的。

若要在包含凭据的情况下Fetch,请使用:

fetch(url, {credentials: 'include'});

虽然无法为跨源字体请求添加凭据,但可以将其从样式表中删除:

<link href="https://www.linuxidc.com/…" crossorigin>

……这意味着样式表和字体请求将使用同一个连接。然而,如果该样式表还应用了背景图片,这些请求将始终包含凭据,因此最终依然需要再建立一个连接。此时唯一的解决方案是使用服务工作进程,这样便可更改每个请求执行Fetch的方式。

我曾听开发者说不含凭据的请求性能更高,因为不需要发送Cookie,但需要将这个问题与建立新连接的成本进行权衡。此外HTTP/2可对不同请求之间重复的报头进行压缩,因此Cookie其实算不得一个什么大问题。

也许我们可以更改一下规则

Edge是唯一不遵守这些规则的浏览器。Edge允许含凭据和不含凭据的请求共享同一个连接。然而这里就不像上面那样对每个浏览器进行对比了,因为我更希望看到相关规范以后能有所变化。

如果页面向源发起了不含凭据的请求,此时再建立一个连接的意义并不大。含凭据的资源发起了请求,因此也可以通过URL将自己的凭据添加给“匿名”请求(这也叫“环境权限:Ambient Authority”)。

其他情况我不是很确定,但如果你要对同一个服务器同时发起含凭据和不含凭据的请求,由于浏览器指纹的存在,基本上无法实现匿名。如果希望深入了解这个问题,可以参阅GitHub上的讨论、,以及Firefox的Bug跟踪器

嗯,这段内容的“行话”有点多,抱歉哈。

推送缓存中的项只能使用一次

一旦浏览器使用了推送缓存中的内容,该内容会从缓存中删除。虽然该内容最后可能会保留在HTTP缓存中(取决于缓存报头),但不会继续保存在推送缓存中。

Chrome - 良好支持

Safari - 糟糕支持

Firefox - 良好支持

Edge - 良好支持

Safari在这方面存在竟态条件的问题。如果一个资源在推送过程中被多次Fetch,那么将多次获得推送的项(问题描述,含视频)。如果项推送完成之后被Fetch两次,此时的表现是正确的,第一次从推送缓存中获取,而第二次是从其他地方获取。

建议

如果决定向Safari用户推送资源,那么在推送非缓存资源(例如JSON数据)时需要注意这个Bug。也许可以在响应中传递一个随机ID,如果同一个ID收到两次,那么就可以确定自己遇到这个Bug了,此时可以等待一秒后重试。

一般来说,可以使用缓存报头或服务工作进程对Fetch完毕的已推送资源进行缓存,除非并不需要进行缓存(例如只需要进行一次的JSON Fetch)。

如果已经具备,浏览器可能忽略推送的项

在推送内容时,其实并不需要与客户端进行太多协商。这意味着推送的某些内容可能已经位于浏览器的某个缓存中。这种情况下,HTTP/2规范允许浏览器使用CANCEL或REFUSED_STREAM代码忽略传入的数据流,这样可以避免浪费带宽。

Chrome - 部分支持

Safari - 部分支持

Firefox - 糟糕支持

Edge - 良好支持

这个规范的要求并不严格,因此我觉得可以根据开发者的实际情况自行决定。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/90e04b79b9d458bc3a9d0955b0bda1c8.html