Service Worker で範囲リクエストを処理する

部分的レスポンスがリクエストされた場合に、Service Worker が何をすべきかを認識していることを確認してください。

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

Service Worker は、ウェブアプリとネットワークの間に配置される 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.requestRange: ヘッダーが含まれている場合、そのヘッダーは通知なく破棄されます。リモート サーバーが受信したリクエストに Range: は含まれません。元のリクエストに Range: ヘッダーが存在する場合でも、サーバーは 200 ステータス コードとともにレスポンスの本文全体を返すことが技術的に許可されているため、これは必ずしも「中断」されません。ただし、ブラウザの観点から厳密に必要な数よりも多くのデータが転送されてしまいます。

この動作に気付いたデベロッパーは、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.requestfetch() に渡されたときに Range: ヘッダーが保持されます。つまり、最初の例の Service Worker のコードで、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 ではこの挙動がまだ修正されていないため、Service Worker のコードを本番環境にデプロイする際は、この問題を考慮する必要があります。

特定のブラウザでこの動作が修正されたかどうかを確認するには、Web Platform Tests ダッシュボードの [ネットワーク リクエストに範囲ヘッダーを含める] 行を確認するのが最適です。

キャッシュからの範囲リクエストはどうなるでしょうか。

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

Firefox を含むすべてのブラウザで、fetch ハンドラ内でリクエストを検査し、Range: ヘッダーの存在を確認して、キャッシュからの 206 レスポンスを使用してローカルでリクエストを履行できます。しかし、Range: ヘッダーを適切に解析し、キャッシュされた完全なレスポンスの適切なセグメントのみを返す Service Worker コードは簡単ではありません。

幸いなことに、ヘルプを必要とするデベロッパーは Workbox を使用できます。Workbox は、Service Worker の一般的なユースケースを簡素化するライブラリのセットです。workbox-range-request module は、部分レスポンスをキャッシュから直接提供するために必要なすべてのロジックを実装しています。このユースケースの詳細な手順については、Workbox のドキュメントをご覧ください。

この投稿のヒーロー画像は、Unsplash の Natalie Rhea Riggs 氏によるものです。