HTTP/2 の概要

HTTP/2 は、めったにない組み合わせで、アプリケーションの速度、シンプルさ、堅牢性を高めます。これまでアプリケーション内で行っていた HTTP/1.1 の回避策の多くを元に戻し、トランスポート レイヤ自体でこれらの問題に対処できるようになります。さらに、アプリケーションを最適化してパフォーマンスを改善するまったく新しい機会も数多く生まれます。

HTTP/2 の主な目標は、リクエストとレスポンス全体の多重化を有効にしてレイテンシを短縮し、HTTP ヘッダー フィールドを効率的に圧縮することでプロトコルのオーバーヘッドを最小限に抑え、リクエストの優先順位付けとサーバー push のサポートを追加することです。これらの要件を実装するために、他のプロトコル機能強化(新しいフロー制御、エラー処理、アップグレード メカニズムなど)が大量にサポートされていますが、これらはすべてのウェブ デベロッパーが理解してアプリケーションで活用すべき最も重要な機能です。

HTTP/2 によって HTTP のアプリケーション セマンティクスが変更されることはありません。HTTP メソッド、ステータス コード、URI、ヘッダー フィールドなどの基本的なコンセプトはすべてそのまま維持されます。代わりに HTTP/2 は、データのフォーマット(フレーム化)と転送方法を変更します。クライアントとサーバーの両方がプロセス全体を管理し、新しいフレーミング レイヤ内ではアプリケーションの複雑さをすべて隠します。そのため、既存のすべてのアプリケーションを変更せずに配信できます。

HTTP/1.2 ではないのはなぜですか?

HTTP ワーキング グループが設定したパフォーマンス目標を達成するために、HTTP/2 では、以前の HTTP/1.x サーバーおよびクライアントとの下位互換性がない新しいバイナリ フレーミング レイヤが導入されました。そのため、メジャー プロトコル バージョンは HTTP/2 に増分されます。

とはいえ、未加工の TCP ソケットを使用してウェブサーバー(またはカスタム クライアント)を実装しない限り、違いはありません。新しい低レベルのフレーミングはすべて、クライアントとサーバーによって実行されます。確認可能な唯一の違いは、リクエストの優先順位付け、フロー制御、サーバー push などの新しい機能のパフォーマンスと可用性の向上です。

SPDY と HTTP/2 の略歴

SPDY は Google で開発され、2009 年半ばに発表された試験運用版のプロトコルです。その主な目的は、HTTP/1.1 のよく知られているパフォーマンス制限のいくつかに対処して、ウェブページの読み込みレイテンシを短縮することでした。具体的には、プロジェクトの目標を次のように設定しました。

  • ページ読み込み時間(PLT)を 50% 短縮することを目指す。
  • ウェブサイトの作成者がコンテンツを変更する必要がない。
  • デプロイの複雑さを最小限に抑え、ネットワーク インフラストラクチャの変更を回避します。
  • この新しいプロトコルは、オープンソース コミュニティと協力して開発してください。
  • 実際のパフォーマンス データを収集し、試験運用プロトコルを検証(無効)します。

最初の発表から間もなく、Mike Belshe と Roberto Peon(Google のソフトウェア エンジニア)が、新しい SPDY プロトコルの試験的実装の最初の結果、ドキュメント、ソースコードを共有しました。

今のところ、SPDY はラボ環境でのみテストされています。最初の結果は非常に有望です。シミュレートされたホーム ネットワーク接続で上位 25 のウェブサイトをダウンロードすると、ページ読み込みが最大 55% 速くなり、パフォーマンスが大幅に改善されました。(Chromium ブログ)

2012 年になって、新しい試験運用版プロトコルは Chrome、Firefox、Opera でサポートされ、大規模なサイト(Google、Twitter、Facebook など)から小規模のサイトまで、インフラストラクチャ内に SPDY をデプロイするサイトの数が急増しました。事実上、SPDY は業界での採用が拡大し、事実上の標準となりつつあります。

この傾向を受けて、HTTP ワーキング グループ(HTTP-WG)は SPDY から学んだ教訓を応用し、それを基に構築と改善を行い、正式な「HTTP/2」標準を提供する新たな取り組みを開始しました。新しい計画書がドラフトされ、HTTP/2 提案が開かれ、作業グループで多くの議論が交わされた後、新しい HTTP/2 プロトコルの出発点として SPDY 仕様が採用されました。

今後数年間、SPDY と HTTP/2 は並行して共進化し、SPDY は HTTP/2 標準の新機能と提案のテストに使用される試験運用版ブランチとして機能しました。紙では問題ないように見えても、実際にはうまくいかないことがあり、その逆も同様です。SPDY では、HTTP/2 標準に含める前に各提案をテストして評価する方法を提供しました。最終的に、このプロセスには 3 年間かかり、10 を超える中間ドラフトが作成されました。

  • 2012 年 3 月: HTTP/2 の提案募集
  • 2012 年 11 月: HTTP/2 の最初のドラフト(SPDY に基づく)
  • 2014 年 8 月: HTTP/2 ドラフト 17 と HPACK ドラフト 12 を公開
  • 2014 年 8 月: ワーキング グループ、HTTP/2 の最終提案
  • 2015 年 2 月: IESG が HTTP/2 と HPACK のドラフトを承認
  • 2015 年 5 月: RFC 7540(HTTP/2)と RFC 7541(HPACK)を公開

2015 年初頭、IESG は新しい HTTP/2 標準の審査を行い、公開を承認しました。その直後、Google Chrome チームは TLS の SPDY 拡張機能と NPN 拡張機能のサポートを終了するスケジュールを発表しました。

HTTP/1.1 からの主な変更点は、HTTP/2 のパフォーマンスの向上です。多重化、ヘッダー圧縮、優先順位付け、プロトコル ネゴシエーションなどのいくつかの重要な機能は、SPDY という以前のオープンだが標準以外のプロトコルで行われた作業から発展しました。Chrome は Chrome 6 以降 SPDY をサポートしていますが、そのメリットのほとんどは HTTP/2 にあるため、廃止を迫られています。2016 年初頭に SPDY のサポートを終了すると同時に、Chrome の ALPN を優先して NPN という TLS 拡張機能のサポートも終了する予定です。サーバー デベロッパーは、HTTP/2 と ALPN に移行することを強くおすすめします。

Google は、HTTP/2 につながるオープン スタンダード プロセスに貢献でき、標準化と実装に業界が幅広く関わっていることから、広く採用されることを期待しています。(Chromium ブログ)

SPDY と HTTP/2 が共進化したことで、サーバー、ブラウザ、サイトのデベロッパーは、開発中の新しいプロトコルを実際に体験できるようになりました。その結果、HTTP/2 標準は、導入後すぐに、最も広範にテストされた最良の標準の一つです。HTTP/2 が IESG によって承認されるまで、徹底的にテストされ、本番環境に対応したクライアントとサーバーの実装が何十件も実施されました。実際、最終的なプロトコルが承認されてからわずか数週間で、いくつかの一般的なブラウザ(および多くのサイト)が完全な HTTP/2 サポートをデプロイしたため、多くのユーザーがすでにそのメリットを享受していました。

設計と技術の目標

以前のバージョンの HTTP プロトコルは、実装を簡単にするために意図的に設計されていました。HTTP/0.9 は、ワールド ワイド ウェブをブートストラップするための 1 行プロトコルです。HTTP/1.0 では、HTTP/0.9 の一般的な拡張機能が情報標準として文書化されています。HTTP/1.1 では、公式の IETF 標準が導入されています。HTTP の略歴をご覧ください。このように、HTTP/0.9-1.x は目標通りに実現しました。HTTP は、インターネットで最も広く採用されているアプリケーション プロトコルの一つです。

残念ながら、実装の簡素化はアプリケーションのパフォーマンスを犠牲にしています。HTTP/1.x クライアントは、同時実行を実現してレイテンシを短縮するために複数の接続を使用する必要があり、HTTP/1.x ではリクエスト ヘッダーとレスポンス ヘッダーが圧縮されないため、不要なネットワーク トラフィックが発生します。HTTP/1.x では効果的なリソースの優先順位付けができないため、基盤となる TCP 接続の使用が不適切になります。

こうした制限は致命的ではありませんが、ウェブ アプリケーションの範囲、複雑性、日常生活における重要性が拡大し続けるにつれて、ウェブのデベロッパーとユーザーの双方に負担が増大することになりました。このギャップは、HTTP/2 がまさに対処するために設計されたものです。

HTTP/2 では、ヘッダー フィールド圧縮を導入し、同じ接続で複数の同時交換を可能にすることで、ネットワーク リソースをより効率的に使用し、レイテンシの認識を低減できます。具体的には、同じ接続でリクエスト メッセージとレスポンス メッセージをインターリーブでき、HTTP ヘッダー フィールドの効率的なコーディングを使用できます。また、リクエストの優先順位付けもできるため、より重要なリクエストをより迅速に完了でき、パフォーマンスがさらに向上します。

結果として得られるプロトコルは、HTTP/1.x と比較して使用できる TCP 接続が少ないため、ネットワークにとってより使いやすいものになります。これにより、他のフローとの競合が減り、接続時間が長くなるため、使用可能なネットワーク容量をより有効に活用できます。最後に、HTTP/2 では、バイナリ メッセージ フレーミングを使用することで、より効率的なメッセージ処理も可能になります。(Hypertext Transfer Protocol バージョン 2、ドラフト 17)

HTTP/2 は、以前の HTTP 標準に代わるものではなく、拡張されていることに注意してください。HTTP のアプリケーション セマンティクスは同じであり、HTTP メソッド、ステータス コード、URI、ヘッダー フィールドなど、提供される機能や主要なコンセプトに変更はありません。これらの変更は、明らかに HTTP/2 の取り組みの範囲外でした。とはいえ、高レベルの API は同じままですが、低レベルの変更によって以前のプロトコルのパフォーマンス制限にどのように対処するかを理解することが重要です。バイナリ フレーミング レイヤとその機能を簡単に見ていきましょう。

バイナリ フレーミング レイヤ

HTTP/2 のすべてのパフォーマンス強化の中心となるのは、新しいバイナリ フレーミング レイヤです。このレイヤは、クライアントとサーバー間で HTTP メッセージをカプセル化および転送する方法を決定します。

HTTP/2 バイナリ フレーミング レイヤ

「レイヤ」とは、ソケット インターフェースとアプリケーションに公開される上位の HTTP API の間に、最適化された新しいエンコード メカニズムを導入するための設計上の選択のことを指します。動詞、メソッド、ヘッダーなどの HTTP セマンティクスは影響を受けませんが、転送中のエンコード方法は異なります。改行区切りの平文の HTTP/1.x プロトコルとは異なり、すべての HTTP/2 通信は小さなメッセージとフレームに分割され、それぞれがバイナリ形式でエンコードされます。

そのため、クライアントとサーバーの両方が新しいバイナリ エンコード メカニズムを使用して相互に理解する必要があります。HTTP/1.x クライアントは HTTP/2 のみのサーバーを認識せず、その逆も同様です。幸いなことに、クライアントとサーバーが Google に代わって必要なすべてのフレーミング作業を実行するため、アプリケーションはこれらの変更を認識しません。

ストリーム、メッセージ、フレーム

新しいバイナリ フレーミング メカニズムの導入により、クライアントとサーバー間でのデータの交換方法が変わります。このプロセスを説明するために、HTTP/2 の用語を確認しましょう。

  • ストリーム: 確立された接続におけるバイトの双方向フロー。1 つ以上のメッセージを伝送する場合があります。
  • メッセージ: 論理的なリクエストまたはレスポンス メッセージにマッピングされるフレームの完全なシーケンス。
  • フレーム: HTTP/2 の通信の最小単位。それぞれにフレーム ヘッダーが含まれ、少なくともフレームが属するストリームを識別します。

これらの用語の関係をまとめると次のようになります。

  • すべての通信は、任意の数の双方向ストリームを伝送できる単一の TCP 接続を介して実行されます。
  • 各ストリームには、双方向メッセージの伝送に使用される一意の識別子とオプションの優先度情報があります。
  • 各メッセージは、リクエストやレスポンスなどの論理 HTTP メッセージであり、1 つ以上のフレームで構成されます。
  • フレームは、特定のタイプのデータを伝送する通信の最小単位です。HTTP ヘッダー、メッセージペイロードなど異なるストリームからのフレームをインターリーブし、各フレームのヘッダーに埋め込まれたストリーム識別子を介して再構成できます。

HTTP/2 ストリーム、メッセージ、フレーム

要するに、HTTP/2 は HTTP プロトコル通信をバイナリでエンコードされたフレームの交換に分解します。このフレームは特定のストリームに属するメッセージにマッピングされ、そのすべてが単一の TCP 接続内で多重化されます。これは、HTTP/2 プロトコルによって提供される他のすべての機能とパフォーマンス最適化を可能にする基盤です。

リクエストとレスポンスの多重化

HTTP/1.x では、クライアントがパフォーマンスを向上させるために複数の並列リクエストを行う場合、複数の TCP 接続を使用する必要があります(複数の TCP 接続の使用をご覧ください)。この動作は、接続ごとに一度に 1 つのレスポンスのみを配信(レスポンス キューイング)する HTTP/1.x 配信モデルの直接的な結果です。さらに悪いことに、ヘッドオブライン ブロッキングが発生し、基盤となる TCP 接続の使用効率が低下します。

HTTP/2 の新しいバイナリ フレーミング レイヤではこれらの制限がなくなり、クライアントとサーバーが HTTP メッセージを独立したフレームに分割してインターリーブし、相手側でそれらを再構築できるようにすることで、完全なリクエストとレスポンスの多重化が可能になります。

共有接続内での HTTP/2 リクエストとレスポンスの多重化

スナップショットは、同じ接続内の処理中の複数のストリームをキャプチャします。クライアントは DATA フレーム(ストリーム 5)をサーバーに送信し、サーバーはフレームのインターリーブ シーケンスをストリーム 1 と 3 のクライアントに送信しています。その結果、3 つの並列ストリームが処理中になります。

HTTP メッセージを独立したフレームに分割し、インターリーブして相手側で再構成する機能は、HTTP/2 の最も重要な拡張機能です。実際、すべてのウェブ テクノロジーのスタック全体に多数のパフォーマンス上のメリットをもたらす波及効果があるため、次のことが可能になります。

  • リクエストをブロックすることなく、複数のリクエストを並行してインターリーブします。
  • レスポンスをブロックすることなく、複数のレスポンスを並行してインターリーブします。
  • 単一の接続を使用して、複数のリクエストとレスポンスを並行して配信できます。
  • 不要な HTTP/1.x 回避策を削除します(連結ファイル、イメージ スプライト、ドメイン シャーディングなど、HTTP/1.x の最適化をご覧ください)。
  • 不要なレイテンシを排除し、使用可能なネットワーク容量の利用率を改善することで、ページの読み込み時間を短縮する。
  • その他多数

HTTP/2 の新しいバイナリ フレーミング レイヤにより、HTTP/1.x で見つかったヘッドオブライン ブロッキングの問題が解決され、リクエストとレスポンスの並列処理と配信を可能にするために複数の接続が不要になります。その結果、アプリケーションのデプロイを高速化、簡素化、低コスト化できます。

ストリームの優先順位付け

HTTP メッセージを多くの個別のフレームに分割し、複数のストリームからのフレームを多重化できるようにすると、クライアントとサーバーの両方でフレームがインターリーブされて配信される順序が、パフォーマンスに関する重要な考慮事項になります。これを容易にするために、HTTP/2 標準では各ストリームに重みと依存関係を関連付けることができます。

  • 各ストリームには、1 ~ 256 の整数の重みを割り当てることができます。
  • 各ストリームには、別のストリームへの明示的な依存関係を指定できます。

ストリームの依存関係と重みを組み合わせることで、クライアントはレスポンスの受信方法を表す「優先順位ツリー」を構築して通信できます。サーバーはこの情報を使用して、CPU、メモリ、その他のリソースの割り当てを制御することでストリーム処理の優先順位付けを行い、レスポンス データが利用可能になったら、クライアントに優先度の高いレスポンスを最適に配信するための帯域幅を割り当てます。

HTTP/2 ストリームの依存関係と重み

HTTP/2 内のストリームの依存関係は、別のストリームの一意の識別子を親として参照することで宣言されます。識別子を省略すると、ストリームは「ルート ストリーム」に依存することになります。ストリームの依存関係を宣言すると、可能であれば、親ストリームをその依存関係の前にリソースを割り当てる必要があります。つまり「レスポンス C の前にレスポンス D を 処理して配信してください」

同じ親を共有するストリーム(つまり、兄弟ストリーム)は、その重みに比例してリソースを割り当てる必要があります。たとえば、ストリーム A の重みが 12 で、兄弟姉妹 B の重みが 4 の場合、これらの各ストリームが受信するリソースの割合を決定するには、次のようにします。

  1. すべての重みを合計する: 4 + 12 = 16
  2. 各ストリームの重みを合計の重みで割る: A = 12/16, B = 4/16

したがって、ストリーム A は 4 分の 3、ストリーム B は利用可能なリソースの 4 分の 1 を受け取る必要があり、ストリーム B はストリーム A に割り当てられたリソースの 3 分の 1 を受け取る必要があります。上の画像の実践的な例をさらに見てみましょうこれらのオプションについて、左から右の順で説明します。

  1. ストリーム A と B のどちらも親依存関係を指定しておらず、暗黙的な「ルート ストリーム」に依存していると言われます。A の重みは 12、B の重みは 4 です。したがって、比例する重み付けに基づいて、ストリーム B はストリーム A に割り当てられたリソースの 3 分の 1 を受け取る必要があります。
  2. ストリーム D はルート ストリームに依存し、ストリーム C は D に依存します。したがって、D は C より前にすべてのリソースの割り当てを受け取る必要があります。C の依存関係のほうがより強く優先されるため、重みは重要ではありません。
  3. ストリーム D は C の前にすべてのリソースが割り当てられます。C は A と B の前にすべてのリソースが割り当てられます。ストリーム B はストリーム A に割り当てられたリソースの 3 分の 1 を受け取ります。
  4. ストリーム D は、E と C の前にすべてのリソースが割り当てられます。E と C は、A と B より前に均等に割り当てられます。A と B は、それぞれの重みに基づいて比例配分されます。

上記の例が示すように、ストリームの依存関係と重み付けを組み合わせることで、リソースの優先順位付けのための表現力豊かな言語が提供されます。これは、さまざまな依存関係と重みを持つ多くのリソースタイプが存在する場合、ブラウジングのパフォーマンスを向上させるために重要な機能です。さらに、HTTP/2 プロトコルでは、クライアントがいつでもこれらの設定を更新できるため、ブラウザをさらに最適化できます。つまり、ユーザー操作やその他のシグナルに応じて、依存関係を変更して重みを再割り当てできます。

送信元ごとに 1 つの接続

新しいバイナリ フレーミング メカニズムを導入すると、HTTP/2 ではストリームを並列に多重化する複数の TCP 接続が不要になります。各ストリームは多くのフレームに分割され、インターリーブして優先順位を付けることができます。その結果、すべての HTTP/2 接続は永続的になり、送信元ごとに 1 つの接続のみが必要になります。これにより、パフォーマンス上の多くのメリットが得られます。

SPDY と HTTP/2 のどちらも、キラー機能は、輻輳制御された 1 つのチャネルでの任意の多重化です。驚くほど重要で うまく機能しているようですこれに関する優れた指標の 1 つは、作成された接続のうち、単一の HTTP トランザクションだけを伝送する割合です(そのため、そのトランザクションがすべてのオーバーヘッドを負担します)。HTTP/1 の場合、アクティブな接続の 74% は単一のトランザクションのみを伝送します。永続的な接続は私たちが望むほどの役に立ちません。しかし HTTP/2 では、この数値は 25% に減少します。これは、オーバーヘッドを削減できる大きな利点です。(HTTP/2 is Live in Firefox, Patrick McManus)

ほとんどの HTTP 転送は短時間で急増しますが、TCP は長期間の一括データ転送用に最適化されています。同じ接続を再利用することで、HTTP/2 は各 TCP 接続をより効率的に使用し、全体的なプロトコル オーバーヘッドを大幅に削減できます。さらに、使用する接続が少なくなるため、完全な接続パス(クライアント、中間サーバー、送信元サーバー)に沿ったメモリと処理フットプリントが削減されます。これにより、全体的な運用費用が削減され、ネットワークの使用率と容量が改善されます。その結果、HTTP/2 に移行することで、ネットワーク レイテンシが短縮されるだけでなく、スループットの向上と運用費用の削減にもつながります。

フロー制御

フロー制御は、センダーが処理の必要のないデータや処理できないデータでレシーバに過負荷をかけないようにするためのメカニズムです。レシーバはビジー状態、負荷の大きい状態、特定のストリームに一定量のリソースを割り当てるだけの場合があります。たとえば、クライアントが大きな動画ストリームを高い優先度でリクエストしたが、ユーザーが動画を一時停止したため、クライアントは不要なデータの取得とバッファリングを避けるために、サーバーからの配信を一時停止またはスロットリングする必要がある場合があります。あるいは、プロキシ サーバーがダウンストリーム接続が高速で、アップストリーム接続が低速である場合も、同様に、リソース使用量を制御するためにアップストリームの速度に合わせて、ダウンストリームがデータを配信する速度を調整する必要があります。

上記の要件は TCP フロー制御を思い出しましたか?問題は実質的に同じであるため、両者は一致すべきです(フロー制御を参照)。ただし、HTTP/2 ストリームは単一の TCP 接続内で多重化されるため、TCP フロー制御では粒度が十分ではなく、個々のストリームの配信の制御に必要なアプリケーション レベルの API が提供されません。これに対処するために、HTTP/2 には、クライアントとサーバーが独自のストリームレベルおよび接続レベルのフロー制御を実装できるようにする、一連のシンプルな構成要素が用意されています。

  • フロー制御には方向性があります。各レシーバーは、各ストリームと接続全体に必要なウィンドウ サイズを設定できます。
  • フロー制御はクレジットベースです。各レシーバーは、初期接続とストリーム フロー制御ウィンドウ(バイト単位)をアドバタイズします。このウィンドウは、送信者が DATA フレームを発行するたびに減少し、レシーバーが送信した WINDOW_UPDATE フレームを介して増加します。
  • フロー制御を無効にすることはできません。HTTP/2 接続が確立されると、クライアントとサーバーは SETTINGS フレームを交換します。これにより、両方向のフロー制御ウィンドウ サイズが設定されます。フロー制御ウィンドウのデフォルト値は 65,535 バイトに設定されていますが、レシーバは最大ウィンドウ サイズ(2^31-1 バイト)を大きく設定し、データを受信するたびに WINDOW_UPDATE フレームを送信することでそれを維持できます。
  • フロー制御はエンドツーエンドではなく、ホップバイホップです。つまり、中継者はこれを使用して、リソースの使用を制御し、独自の基準とヒューリスティックに基づいてリソース割り当てメカニズムを実装できます。

HTTP/2 では、フロー制御を実装するための特定のアルゴリズムは指定されていません。代わりに、シンプルな構成要素を提供し、クライアントとサーバーに実装を委ねます。クライアントとサーバーはそれを使用して、リソースの使用と割り当てを規制するカスタム戦略を実装できます。また、ウェブ アプリケーションの実際のパフォーマンスと目に見えるパフォーマンスの両方の改善に役立つ新しい配信機能を実装できます(速度、パフォーマンス、人間の認識を参照)。

たとえば、アプリケーション レイヤのフロー制御では、ブラウザで特定のリソースの一部のみを取得し、ストリーム フローの制御ウィンドウをゼロにして取得を保留にして、後で再開できます。つまり、ブラウザが画像のプレビューまたは最初のスキャンを取得して表示し、優先度の高い他の取得を続行して、より重要なリソースの読み込みが完了したら取得を再開できるようになります。

サーバー push

HTTP/2 のもう 1 つの強力な新機能は、1 つのクライアント リクエストに対して複数のレスポンスを送信するサーバーの機能です。つまり、元のリクエストに対するレスポンスに加えて、サーバーは追加のリソースをクライアントに push できます(図 12-5)。クライアントが各リソースを明示的にリクエストする必要はありません。

サーバーがリソースを push するための新しいストリーム(Promise)を開始する

なぜブラウザにこのようなメカニズムが必要なのでしょうか?一般的なウェブ アプリケーションは多数のリソースで構成されており、クライアントはすべて、サーバーが提供するドキュメントを調べることでリソースを検出します。その結果、余分なレイテンシをなくし、関連するリソースをサーバーが事前に push できるようにしてはどうでしょうか。サーバーは、クライアントが必要とするリソースをすでに認識しています。それがサーバー push です。

実際に、データ URI(リソースのインライン化を参照)を使用して CSS や JavaScript などのアセットをインライン化したことがあるなら、サーバー プッシュを実際に試したことになります。リソースを手動でドキュメントにインライン化すると、クライアントがリクエストするのを待たずに、リソースをクライアントに push できます。HTTP/2 でも同じ結果が得られますが、パフォーマンス上の利点もあります。push リソースには次のものがあります。

  • クライアントによってキャッシュに保存されました
  • 別のページで再利用されています
  • 他のリソースと多重化
  • サーバーによって優先
  • クライアントにより不承認

PUSH_PROMISE 入門ガイド

すべてのサーバー プッシュ ストリームは PUSH_PROMISE フレームを介して開始されます。このフレームは、記述されたリソースをクライアントに push するサーバーの意図を示し、プッシュされたリソースをリクエストするレスポンス データの前に配信する必要があります。この配信順序は重要です。クライアントはリソースに対して重複したリクエストが作成されないように、サーバーが push するリソースを知っている必要があります。この要件を満たす最も簡単な方法は、親のレスポンス(DATA フレーム)の前に、約束されたリソースの HTTP ヘッダーのみを含む PUSH_PROMISE フレームをすべて送信することです。

PUSH_PROMISE フレームを受信した後、クライアントは必要に応じてストリームを拒否できます(RST_STREAM フレームを使用)。(リソースがすでにキャッシュにある場合などです)。これは HTTP/1.x からの重要な改善です。一方、HTTP/1.x の一般的な「最適化」であるリソースのインライン化の使用は「強制 push」と同等です。クライアントはインライン リソースを個別にオプトアウト、キャンセル、処理できません。

HTTP/2 でも、クライアントはサーバー プッシュの使用方法を完全に制御できます。クライアントは、同時に push されるストリームの数を制限できます。また、初期フロー制御ウィンドウを調整して、ストリームが最初に開かれたときに push されるデータの量を制御することも、サーバー push を完全に無効にすることもできます。これらの設定は、HTTP/2 接続の開始時に SETTINGS フレームを介して伝えられ、いつでも更新できます。

push された各リソースはストリームであり、インライン リソースとは異なり、クライアントが個別に多重化、優先順位付け、処理を行うことができます。ブラウザによって適用される唯一のセキュリティ上の制限として、push されたリソースは同一オリジン ポリシーに従う必要があります。つまり、提供されたコンテンツに対してサーバーが信頼できるものでなければなりません。

ヘッダー圧縮

各 HTTP 転送には、転送されるリソースとそのプロパティを説明する一連のヘッダーが含まれます。HTTP/1.x では、このメタデータは常に平文として送信され、転送ごとに 500 ~ 800 バイトのオーバーヘッドが追加されます。HTTP Cookie が使用されている場合は、KB を超える場合があります。(プロトコル オーバーヘッドの測定と制御をご覧ください)。このオーバーヘッドを削減し、パフォーマンスを向上させるため、HTTP/2 では、シンプルかつ強力な 2 つの手法を使用する HPACK 圧縮形式を使用して、リクエストとレスポンスのヘッダー メタデータを圧縮します。

  1. これにより、送信されるヘッダー フィールドを静的ハフマンコードでエンコードできるため、個々の転送サイズを削減できます。
  2. クライアントとサーバーの両方で、以前に参照されたヘッダー フィールドのインデックス付きリストを維持、更新する必要があります(つまり、共有圧縮コンテキストを確立します)。これは、以前に送信された値を効率的にエンコードするための参照として使用されます。

ハフマン コーディングにより、転送時に個々の値を圧縮できます。また、以前に転送された値のインデックス付きリストにより、完全なヘッダーキーと値を効率的に検索および再構築するために使用できるインデックス値を転送することで、重複値をエンコードできます。

HPACK: HTTP/2 のヘッダー圧縮

さらなる最適化として、HPACK 圧縮コンテキストは、静的テーブルと動的テーブルで構成されます。静的テーブルは仕様で定義され、すべての接続で使用される可能性のある一般的な HTTP ヘッダー フィールドのリスト(有効なヘッダー名など)を提供します。動的テーブルは最初は空で、特定の接続内で交換された値に基づいて更新されます。その結果、これまでに見たことのない値に静的ハフマン コーディングを使用し、両側の静的テーブルまたは動的テーブルにすでに存在する値のインデックスを置換することで、各リクエストのサイズが削減されます。

HPACK のセキュリティとパフォーマンス

HTTP/2 と SPDY の初期バージョンでは、zlib とカスタム辞書を使用して、すべての HTTP ヘッダーを圧縮していました。これにより、転送されるヘッダーデータのサイズが 85% ~ 88% 削減され、ページの読み込み時間のレイテンシが大幅に改善されました。

アップロード リンクが 375 Kbps しかない低帯域幅の DSL リンクでは、特にリクエスト ヘッダー圧縮により、特定のサイト(つまり、大量のリソース リクエストを発行するサイト)でページの読み込み時間が大幅に改善されました。ヘッダーを圧縮しただけで、ページの読み込み時間が 45 ~ 1,142 ミリ秒短縮されました。(SPDY ホワイトペーパー、chromium.org)

しかし、2012 年の夏に、TLS および SPDY 圧縮アルゴリズムに対する「CRIME」セキュリティ攻撃が発行されました。これにより、セッション ハイジャックにつながる可能性があります。その結果、zlib 圧縮アルゴリズムは HPACK に置き換えられました。HPACK は、発見されたセキュリティ問題に対処すること、効率的かつ簡単に正しく実装できること、そしてもちろん HTTP ヘッダー メタデータを適切に圧縮することを目的として設計されたものです。

HPACK 圧縮アルゴリズムの詳細については、IETF HPACK - Header Compression for HTTP/2 をご覧ください。

関連情報

  • "HTTP/2" – Ilya Grigorik による記事の全文
  • 「HTTP/2 の設定」 - Surma によって異なるバックエンドに HTTP/2 を設定する方法
  • 「HTTP/2 を導入しました。最適化しましょう」– Velocity 2015 の Ilya Grigorik によるプレゼンテーション
  • 「Rules of Thumb for HTTP/2 Push」– Tom Bergan、Simon Pelchat、Michael Buettner による push の使用タイミングと使用方法に関する分析。