サービス ワーカーで範囲リクエストを処理する

公開日: 2020 年 10 月 6 日

一部の HTTP リクエストには Range: ヘッダーが含まれており、リソース全体の一部のみを返す必要があることを示しています。一般的に、オーディオや動画のコンテンツをストリーミングするために使用されます。これにより、リモート ファイル全体を一度にリクエストするのではなく、メディアの小さなチャンクをオンデマンドで読み込むことができます。

Service Worker は、ウェブアプリとネットワークの間に配置され、送信ネットワーク リクエストをインターセプトしてレスポンスを生成する可能性のある JavaScript コードです。

これまで、範囲リクエストとサービス ワーカーはうまく連携していませんでした。サービス ワーカーで悪い結果を回避するために、特別な手順を踏む必要がありました。幸いなことに、この状況は変わりつつあります。正しい動作を示すブラウザでは、サービス ワーカーを通過する際に範囲リクエストが「正常に動作」します。

問題の詳細について教えてください

次の fetch イベント リスナーを持つサービス ワーカーを考えてみましょう。このリスナーは、すべての受信リクエストを取得してネットワークに渡します。

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

正しく動作しないブラウザでは、event.requestRange: ヘッダーが含まれている場合、そのヘッダーは自動的に削除されます。リモート サーバーが受信したリクエストには、Range: は含まれません。サーバーは、元のリクエストに Range: ヘッダーが存在する場合でも、200 ステータス コードとともに完全なレスポンス本文を返すことが技術的に許可されているため、必ずしも「壊れる」わけではありません。ただし、ブラウザの観点からすると、厳密には必要のないデータまで転送されることになります。

この動作を認識しているデベロッパーは、Range: ヘッダーの存在を明示的にチェックし、存在する場合は event.respondWith() を呼び出さないことで、この動作を回避できます。これにより、サービス ワーカーはレスポンス生成の処理から実質的に除外され、代わりに範囲リクエストを保持する方法を認識しているデフォルトのブラウザ ネットワーキング ロジックが使用されます。

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.requestfetch() に渡されるときに Range: ヘッダーを保持します。つまり、最初の例のサービス ワーカー コードでは、ブラウザによって設定された Range: ヘッダーをリモート サーバーが確認できるようになります。

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

サーバーは、範囲リクエストを適切に処理し、206 ステータス コードを含む部分レスポンスを返すことができます。

どのブラウザが正しく動作しますか?

最新バージョンの Safari では、正しい機能が実装されています。Chrome と Edge も、バージョン 87 以降では正しく動作します。

2020 年 10 月の時点で、Firefox ではこの動作はまだ修正されていません。そのため、サービス ワーカーのコードを本番環境にデプロイする際に、この動作を考慮する必要がある場合があります。

特定のブラウザでこの動作が修正されたかどうかを確認するには、ウェブ プラットフォーム テスト ダッシュボードの [Include range header in network request] 行を確認するのが最適です。

キャッシュから範囲リクエストを処理する場合はどうなりますか?

Service Worker は、リクエストをネットワークに渡すだけでなく、さまざまな処理を行うことができます。一般的なユースケースは、音声ファイルや動画ファイルなどのリソースをローカル キャッシュに追加することです。Service Worker は、ネットワークを完全にバイパスして、そのキャッシュからのリクエストを処理できます。

Firefox を含むすべてのブラウザは、fetch ハンドラ内のリクエストの検査、Range: ヘッダーの有無の確認、キャッシュから取得した 206 レスポンスによるリクエストのローカルでの処理をサポートしています。ただし、Range: ヘッダーを適切に解析し、キャッシュに保存された完全なレスポンスの適切なセグメントのみを返すサービス ワーカー コードは、簡単ではありません。

幸いなことに、サポートが必要なデベロッパーは Workbox を利用できます。これは、一般的なサービス ワーカーのユースケースを簡素化するライブラリのセットです。workbox-range-request module は、キャッシュから部分的なレスポンスを直接提供するために必要なすべてのロジックを実装します。このユースケースの完全なレシピは、Workbox のドキュメントで確認できます。