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% 削減することを目指す。
  • ウェブサイトの作成者がコンテンツを変更する必要はありません。
  • デプロイの複雑さを最小限に抑え、ネットワーク インフラストラクチャの変更を回避します。
  • この新しいプロトコルは、オープンソース コミュニティと協力して開発します。
  • 実際のパフォーマンス データを収集し、試験運用プロトコルを(無効に)する。

最初の発表からまもなく、Google のソフトウェア エンジニアである Mike Belshe と Roberto Peon が、新しい 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 draft-17 と HPACK draft-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 は、World Wide Web をブートストラップする 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 のみのサーバーを認識できません。その逆も同様です。幸い、クライアントとサーバーは必要なフレーミング作業をすべて代行するため、アプリケーションはこれらの変更をすべて認識しません。

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

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

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

これらの用語の関係は、次のように要約できます。

  • すべての通信は、任意の数の双方向ストリームを伝送できる 1 つの TCP 接続を介して実行されます。
  • 各ストリームには、双方向メッセージの伝送に使用される一意の識別子とオプションの優先度情報があります。
  • 各メッセージは、1 つ以上のフレームで構成される、リクエストやレスポンスなどの論理 HTTP メッセージです。
  • フレームは、特定の種類のデータを伝送する通信の最小単位です(例: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/2 の最も重要な機能強化は、HTTP メッセージを独立したフレームに分割し、インターリーブして、もう一方の端で再構成できることです。実際、すべてのウェブ技術のスタック全体に多数のパフォーマンス上のメリットが波及効果をもたらし、以下を実現できるようになります。

  • リクエストをブロックせずに複数のリクエストを並行してインターリーブする。
  • どのレスポンスもブロックすることなく、複数のレスポンスを並行してインターリーブします。
  • 単一の接続を使用して、複数のリクエストとレスポンスを並行して配信します。
  • 不要な 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 は 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 つの HTTP トランザクションのみを処理する、作成された接続の割合です(そのため、そのトランザクションがすべてオーバーヘッドを負担します)。HTTP/1 では、アクティブな接続の 74% が 1 つのトランザクションのみを実行します。永続的な接続は、私たちが必要とするほど有用ではありません。しかし 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 つのクライアント リクエストに対して複数のレスポンスを送信できることです。つまり、元のリクエストに対するレスポンスに加えて、サーバーは追加のリソースをクライアントに push できます(図 12-5)。クライアントが各リソースを明示的にリクエストする必要はありません。

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

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

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

  • クライアントによってキャッシュに保存
  • 異なるページで再利用されています
  • 他のリソースと多重化
  • サーバーによって優先順位付けされます
  • クライアントが拒否しました

PUSH_PROMISE 101

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

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

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

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

ヘッダー圧縮

各 HTTP 転送には、転送されたリソースとそのプロパティを記述する一連のヘッダーが含まれます。HTTP/1.x では、このメタデータは常に書式なしテキストとして送信され、転送ごとに 500 ~ 800 バイトのオーバーヘッドが発生します。HTTP Cookie が使用されている場合は、キロバイトがさらに大きくなることもあります。(プロトコル オーバーヘッドの測定と制御をご覧ください)。このオーバーヘッドを削減し、パフォーマンスを向上させるために、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 - HTTP/2 のヘッダー圧縮をご覧ください。

参考資料

  • "HTTP/2" - Ilya Grigorik による記事の全文
  • 「HTTP/2 の設定」 - Surma によってさまざまなバックエンドで HTTP/2 を設定する方法
  • 「HTTP/2 が登場。最適化しましょう。」– プレゼンテーション: Ilya Grigorik(Velocity 2015)
  • 「HTTP/2 プッシュに関する基本ルール」– プッシュを使用するタイミングと方法に関する Tom Bergan、Simon Pelchat、Michael Buettner による分析。