在 Service Worker 中处理范围请求

确保您的 Service Worker 知道在请求部分响应时要执行的操作。

某些 HTTP 请求包含 Range: 标头,这表示只应返回完整资源的一部分。它们常用于流式播放音频或视频内容,允许按需加载较小的媒体块,而不是一次性请求整个远程文件。

Service Worker 是位于 Web 应用与网络之间的 JavaScript 代码,可能会拦截传出的网络请求并为其生成响应。

一直以来,范围请求和 Service Worker 并不能很好地配合。必须采取特殊措施来避免 Service Worker 出现不良结果。幸运的是,情况已经开始改变。在行为正常的浏览器中,范围请求在通过 Service Worker 传递时将“正常运行”。

具体是什么问题?

假设有一个具有以下 fetch 事件监听器的 Service Worker,它接受每个传入请求并将其传递给网络:

self.addEventListener('fetch', (event) => {
  // The Range: header will not pass through in
  // browsers that behave incorrectly.
  event.respondWith(fetch(event.request));
});

在行为不正确的浏览器中,如果 event.request 包含 Range: 标头,该标头将被静默丢弃。远程服务器收到的请求根本不包含 Range:。这不一定会“破坏”任何内容,因为从技术层面来讲,服务器可以返回包含 200 状态代码的完整响应正文,即使原始请求中存在 Range: 标头也是如此。但会导致传输的数据比浏览器严格需要的要多。

知道此行为的开发者可以明确检查是否存在 Range: 标头,并且不调用 event.respondWith()(如果存在)来解决此问题。这样,Service Worker 就可以有效地将自己从响应生成图片中移除,并改为使用默认的浏览器网络逻辑,该逻辑知道如何保留范围请求。

self.addEventListener('fetch', (event) => {
  // Return without calling event.respondWith()
  // if this is a range request.
  if (event.request.headers.has('range')) {
    return;
  }

  event.respondWith(fetch(event.request));
});

但可以肯定的是,大多数开发者都没有意识到这的必要性。并且没有明确说明为什么应该这样做。这最终是因为浏览器需要跟上底层规范的更改,从而增加了对此功能的支持。

修复了哪些内容?

event.request 传递给 fetch() 时,行为正常的浏览器会保留 Range: 标头。这意味着,如果浏览器设置了 Range: 标头,我的初始示例中的 Service Worker 代码将允许远程服务器查看该标头:

self.addEventListener('fetch', (event) => {
  // The Range: header will pass through in browsers
  // that behave correctly.
  event.respondWith(fetch(event.request));
});

服务器现在有机会正确处理范围请求,并返回带有 206 状态代码的部分响应。

哪些浏览器能正常运行?

最新版本的 Safari 具有正确的功能。从 87 版开始,Chrome 和 Edge 也可以正常运行。

截至 2020 年 10 月,Firefox 尚未修复此行为,因此在将 Service Worker 的代码部署到生产环境时,您可能仍需考虑此问题。

查看 Web 平台测试信息中心的“在网络请求中包含范围标头”行是确认给定浏览器是否已纠正此行为的最佳方式。

如何处理来自缓存的范围请求?

Service Worker 不仅可以将请求传递到网络,一种常见用例是将音频和视频文件等资源添加到本地缓存。然后,Service Worker 就可以完全绕过网络来执行来自该缓存的请求。

包括 Firefox 在内的所有浏览器都支持在 fetch 处理程序内检查请求,检查是否存在 Range: 标头,然后使用来自缓存的 206 响应在本地执行该请求。不过,用于正确解析 Range: 标头并仅返回完整缓存响应的适当片段的 Service Worker 代码并非易事。

幸运的是,开发者如果需要帮助,可以求助于 Workbox,这是一组可简化 Service Worker 常见用例的库。workbox-range-request module 实现了直接从缓存提供部分响应所需的所有逻辑。如需查看此用例的完整方案,请参阅 Workbox 文档

本博文的主打图片由 Natalie Rhea Riggs 在 Unsplash 发布。