| インターネット技術タスクフォース (IETF) | C. Krasic |
| Request for Comments: 9204 | M. Bishop |
| カテゴリ: 標準トラック | Akamai Technologies |
| ISSN: 2070-1721 | A. Frindell, Editor |
| 2022年6月 |
QPACK: HTTP/3 のフィールド圧縮
概要
本仕様は QPACK を定義します:HTTP/3 で使用される HTTP フィールドを効率的に表現するための圧縮形式です。これは HPACK 圧縮の変種であり、ヘッドオブラインブロッキング(Head-of-Line blocking)の低減を目指しています。
本メモの状態
これはインターネット標準トラック文書です。
この文書はインターネット技術タスクフォース (IETF) の成果物です。IETF コミュニティのコンセンサスを表しており、公開レビューを受け、Internet Engineering Steering Group (IESG) によって出版が承認されています。インターネット標準に関する詳細は RFC 7841 のセクション 2 を参照してください。
本文書の現在の状態、訂正情報 (errata)、およびフィードバックの提供方法に関する情報は https://www.rfc-editor.org/info/rfc9204 で入手できます。
Copyright Notice
Copyright (c) 2022 IETF Trust and the persons identified as the document authors. All rights reserved.
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.
1. 導入
The QUIC transport protocol ([QUIC-TRANSPORT]) は HTTP セマンティクスをサポートするよう設計されており、その設計は多くの HTTP/2 ([HTTP/2]) の機能を包含します。HTTP/2 はヘッダとトレーラ部分の圧縮に HPACK ([RFC7541]) を使用します。もし HPACK を HTTP/3 ([HTTP/3]) に使用した場合、すべてのストリーム上のフレームに対する総順序が暗黙の前提となるため、フィールドセクションに対してヘッドオブラインブロッキングを引き起こします。
QPACK は HPACK のコア概念を再利用しますが、順序外配信が存在する状況でも正しく動作するよう再設計されています。実装がヘッドオブラインブロッキングに対する耐性と最適な圧縮率の間でバランスをとる柔軟性を持てるようにしています。設計目標は、同じ損失条件下で HPACK とほぼ同等の圧縮率に近づきつつ、はるかに少ないヘッドオブラインブロッキングを実現することです。
1.1. 規約と定義
本書におけるキーワード "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", および "OPTIONAL" は、すべて大文字で現れる場合に限り、BCP 14 の記述([RFC2119] および [RFC8174])に従って解釈されます。
本書で使用する用語は次の通りです。
- HTTP fields:
-
HTTP メッセージの一部として送信されるメタデータ。ヘッダフィールドとトレーラフィールドの両方を含みます。一般に「headers」と呼ばれることが多いですが、本書では一般性を持たせるために「fields」を使用します。
- HTTP field line:
-
HTTP フィールドセクションの一部として送信される名前と値のペア。詳細は RFC 9110 セクション 6.3 および 同セクション 6.5 を参照してください。
- HTTP field value:
-
フィールド名に関連するデータで、そのセクション内で同じ名前を持つすべてのフィールド行値をコンマで連結したものです。
- Field section:
-
HTTP メッセージに関連付けられた順序付けられた HTTP フィールド行の集合。複数の同一名のフィールド行や重複するフィールド行を含むことがあります。HTTP メッセージはヘッダとトレーラの両方のセクションを含むことができます。
- Representation:
-
フィールド行を表現する命令であり、動的テーブルや静的テーブルへの参照によって表されることがあります。
- Encoder:
-
フィールドセクションをエンコードする実装。
- Decoder:
-
エンコードされたフィールドセクションをデコードする実装。
- Absolute Index:
-
動的テーブル内の各エントリに対する一意のインデックス。
- Base:
-
相対インデックスやポストベースインデックスの基準点。動的テーブルエントリを参照する表現は Base を基準とします。
- Insert Count:
-
動的テーブルに挿入されたエントリの総数。
QPACK は名前であり、略語ではないことに注意してください。
1.2. 表記上の規約
本書の図は RFC 2360 セクション 3.1 の形式を使用し、次の追加的な規約を用います。
- x (A)
-
x が A ビット長であることを示します。
- x (A+)
-
x が セクション 4.1.1 で定義されたプレフィックス付き整数エンコーディングを使用し、A ビットのプレフィックスで始まることを示します。
- x ...
-
x が可変長で領域の終わりまで続くことを示します。
2. 圧縮プロセスの概要
HPACK と同様に、QPACK はフィールド行(「headers」)をインデックスに関連付けるために二つのテーブルを使用します。静的テーブル(セクション 3.1)は事前定義され、一般的なヘッダフィールド行(その一部は空の値を持つ)を含みます。動的テーブル(セクション 3.2)は接続の経過とともに構築され、エンコーダはこれを用いてエンコードされたフィールドセクション内のヘッダおよびトレーラのフィールド行をインデックスできます。
QPACK はエンコーダからデコーダへ、また逆方向へ命令を送るための一方向ストリームを定義します。
2.1. エンコーダ
エンコーダはヘッダまたはトレーラセクションを一連の表現に変換し、リスト内の各フィールド行に対してインデックス表現かリテラル表現のいずれかを出力します(セクション 4.5参照)。インデックス表現は、リテラルの名前および場合によっては値を静的テーブルまたは動的テーブルへのインデックスに置き換えることで高い圧縮を達成します。静的テーブルへの参照やリテラル表現は動的な状態を必要とせず、ヘッドオブラインブロッキングのリスクを伴いません。動的テーブルへの参照は、エンコーダがそのエントリがデコーダ側で利用可能であることを示す確認を受け取っていない場合にはヘッドオブラインブロッキングのリスクを伴います。
エンコーダは、圧縮しているフィールド行に限定せず、任意のエントリを動的テーブルに挿入しても MAY です。
QPACK は各フィールドセクション内のフィールド行の順序を保持します。エンコーダは入力フィールドセクションに現れる順序でフィールド表現を出力しなければなりません(MUST)。
QPACK はオプションの状態追跡の負担をエンコーダ側に置くよう設計されており、その結果デコーダは比較的単純になります。
2.1.1. 動的テーブル挿入の制限
テーブルに追い出せないエントリが含まれている場合、新しいエントリを動的テーブルに挿入できないことがあります。
動的テーブルのエントリは、挿入直後に直ちに追い出せない場合があります(参照されていない場合でも)。一度動的テーブルエントリの挿入が確認され、かつ未確認の表現に対する未処理の参照が存在しない場合、そのエントリは追い出し可能になります。エンコーダストリーム上の参照は、当該エントリを追い出す命令が処理される前に必ず処理されることが保証されるため、エントリの追い出しを妨げることはありません。
もし動的テーブルに他のエントリを追い出さずには新しいエントリを格納する余地がなく、追い出されるべきエントリが追い出し可能でない場合、エンコーダはそのエントリ(既存エントリの重複を含む)を動的テーブルに挿入してはなりません(MUST NOT)。これを回避するには、動的テーブルを使用するエンコーダは、デコーダが確認するまで各フィールドセクションによって参照される動的テーブルエントリを追跡しておく必要があります(セクション 4.4.1参照)。
2.1.1.1. 禁止される挿入の回避
エンコーダが新しいエントリを追加できない状況を防ぐために、追い出しに近いエントリを参照することを避けることができます。そのようなエントリを参照する代わりに、エンコーダは Duplicate 命令(セクション 4.3.4)を出力し、代わりにその重複を参照することができます。
どのエントリが追い出しに近すぎるかを決定するのはエンコーダの好みです。一つのヒューリスティックは、動的テーブル内で固定量の利用可能空間を目標にすることです:未使用空間か、非ブロッキングなエントリを追い出すことで回収できる空間のどちらかです。これを実現するために、エンコーダは draining index(ドレイニングインデックス)を維持することができます。これはエンコーダが参照を出力する動的テーブル内での最小の絶対インデックス(セクション 3.2.4)です。新しいエントリが挿入されると、エンコーダは参照しないテーブル領域を維持するためにドレイニングインデックスを増加させます。もしエンコーダがドレイニングインデックスより小さい絶対インデックスのエントリへの新しい参照を作らなければ、それらのエントリへの未確認参照数は最終的に 0 になり、追い出し可能になります。
<-- Newer Entries Older Entries -->
(Larger Indices) (Smaller Indices)
+--------+---------------------------------+----------+
| Unused | Referenceable | Draining |
| Space | Entries | Entries |
+--------+---------------------------------+----------+
^ ^ ^
| | |
Insertion Point Draining Index Dropping
Point
図 1: ドレイニング動的テーブルエントリ
2.1.2. ブロックされたストリーム
QUIC は異なるストリーム上のデータ間の順序を保証しないため、デコーダはまだ受け取っていない動的テーブルエントリを参照する表現に遭遇することがあります。
各エンコードされたフィールドセクションは Required Insert Count(セクション 4.5.1)を含みます。これはそのフィールドセクションをデコードするために必要となる可能性のある Insert Count の最小値です。動的テーブルへの参照を用いたフィールドセクションの場合、Required Insert Count は参照されたすべての動的テーブルエントリの最大絶対インデックスより 1 大きい値です。動的テーブルへの参照を含まないエンコード済みフィールドセクションでは、Required Insert Count は 0 です。
デコーダが自身の Insert Count より大きい Required Insert Count を持つエンコード済みフィールドセクションを受け取った場合、そのストリームは直ちには処理できず「blocked(ブロック)」と見なされます(セクション 2.2.1参照)。
デコーダは SETTINGS_QPACK_BLOCKED_STREAMS 設定を用いてブロック可能なストリーム数の上限を指定します(セクション 5参照)。エンコーダは常に、潜在的にブロックされ得るストリームの数を SETTINGS_QPACK_BLOCKED_STREAMS の値に制限しなければなりません(MUST)。もしデコーダが約束した数より多くのブロックされたストリームに遭遇した場合、これは QPACK_DECOMPRESSION_FAILED 型の接続エラーとして扱わなければなりません(MUST)。
デコーダが実際にすべての危険なストリームでブロック状態になるとは限らない点に注意してください。
エンコーダはストリームがブロックされるリスクを取るかどうかを決定できます。SETTINGS_QPACK_BLOCKED_STREAMS の値が許すなら、まだ転送中の動的テーブルエントリを参照することで圧縮効率を改善できることが多いですが、損失や順序入れ替えが発生するとデコーダ側でストリームがブロックされる可能性があります。エンコーダは確認済みの動的テーブルエントリのみを参照することでブロッキングのリスクを避けることができますが、その場合はリテラルを使用することになり得ます。リテラルはエンコード済みフィールドセクションを大きくするため、エンコーダが輻輳やフロー制御の制限でブロックされる原因となることがあります。
2.1.3. フロー制御デッドロックの回避
フロー制御によって制限されたストリームに命令を書き込むとデッドロックが発生する可能性があります。
デコーダは必要な更新がエンコーダストリーム上で受信されるまで、エンコード済みフィールドセクションを運ぶストリームへのフロー制御クレジットの発行を停止するかもしれません。もしエンコーダストリーム(または接続全体)へのフロー制御クレジットの付与が、エンコード済みフィールドセクションを運ぶストリーム上のデータの消費と解放に依存するなら、デッドロックが生じる可能性があります。
一般に、大きな命令を含むストリームは、その命令が完全に受信されるまでデコーダがフロー制御クレジットを保留するとデッドロック状態になるかもしれません。
これらのデッドロックを避けるために、エンコーダは命令全体に対して十分なストリームおよび接続のフロー制御クレジットが利用可能でない限り命令を書き込むべきではありません(SHOULD NOT)。
2.1.4. 既知の受信カウント
Known Received Count はデコーダが確認した動的テーブルへの挿入および複製の総数です。エンコーダはどの動的テーブルエントリを参照してもストリームをブロックしないかを識別するために Known Received Count を追跡します。デコーダは Insert Count Increment 命令を送るために Known Received Count を追跡します。
セクション確認応答命令(セクション 4.4.1)は、デコーダがそのフィールドセクションをデコードするのに必要なすべての動的テーブル状態を受信したことを意味します。もし確認されたフィールドセクションの Required Insert Count が現在の Known Received Count より大きければ、Known Received Count はその Required Insert Count に更新されます。
Insert Count Increment 命令(セクション 4.4.3)はその Increment パラメータによって Known Received Count を増加させます。詳細は セクション 2.2.2.3 を参照してください。
2.2. デコーダ
HPACK と同様に、デコーダは一連の表現を処理して対応するフィールドセクションを出力します。また、動的テーブルを変更するエンコーダストリーム上の命令も処理します。エンコード済みフィールドセクションとエンコーダストリーム命令は別々のストリームで到着する点に注意してください。これは HPACK と異なり、HPACK ではヘッダブロック内に動的テーブルを変更する命令が含まれ得て、HPACK 命令の専用ストリームはありません。
デコーダは、表現が出現する順序でフィールド行を出力しなければなりません(MUST)。
2.2.1. ブロックされたデコード
エンコード済みフィールドセクションを受信すると、デコーダは Required Insert Count を調べます。Required Insert Count がデコーダの Insert Count 以下であれば、そのフィールドセクションは直ちに処理できます。そうでなければ、そのフィールドセクションを受信したストリームはブロックされます。
ブロック中、エンコード済みフィールドセクションのデータはブロックされたストリームのフロー制御ウィンドウ内に残すべきです(SHOULD)。このデータはストリームがアンブロックになるまで使用できず、フロー制御を早期に解放するとデコーダがメモリ枯渇攻撃に脆弱になります。あるストリームがアンブロックされるのは、デコーダがそのストリームから読み始めたすべてのエンコード済みフィールドセクションに対して Insert Count が Required Insert Count 以上になったときです。
エンコード済みフィールドセクションを処理する際、デコーダは Required Insert Count が当該フィールドセクションをデコードするのに必要な最小値に等しいことを期待します(セクション 2.1.2参照)。もし予想より小さい Required Insert Count に遭遇した場合、デコーダは QPACK_DECOMPRESSION_FAILED 型の接続エラーとして扱わなければなりません(MUST)。もし予想より大きい Required Insert Count に遭遇した場合、デコーダは QPACK_DECOMPRESSION_FAILED 型の接続エラーとして扱ってもよい(MAY)です。
2.2.2. 状態の同期
デコーダはデコーダストリーム上でデコーダ命令(セクション 4.4)を発行することで次のイベントを通知します。
2.2.2.1. フィールドセクションの処理完了
デコーダが動的テーブル参照を含む表現でエンコードされたフィールドセクションのデコードを終了した後、デコーダはセクション確認応答命令(セクション 4.4.1)を発行しなければなりません(MUST)。中間応答、トレーラ、プッシュされたリクエストなどのケースでは、ストリームが複数のフィールドセクションを運ぶことがあります。エンコーダは各セクション確認応答命令を、そのストリーム上で送信された動的テーブル参照を含む最も古い未確認フィールドセクションの確認として解釈します。
2.2.2.2. ストリームの放棄
エンドポイントがストリームの終端前にストリームリセットを受け取った場合、あるいはそのストリーム上のすべてのエンコード済みフィールドセクションが処理される前に読み取りを放棄した場合、デコーダはストリームキャンセル命令を生成します(セクション 4.4.2参照)。これは当該ストリーム上の動的テーブルへのすべての参照がもはや未処理でないことをエンコーダに通知します。最大動的テーブル容量(セクション 3.2.3)が 0 のデコーダは、エンコーダが動的テーブル参照を持ち得ないため、ストリームキャンセルを送らないことができます(MAY)。ただし、エンコーダはこの命令から動的テーブルの更新が受信されたと推測することはできません。
セクション確認応答およびストリームキャンセル命令は、エンコーダが動的テーブル内のエントリへの参照を削除できるようにします。既知の受信カウント(Known Received Count)より小さい絶対インデックスのエントリが参照数 0 になった場合、それは追い出し可能とみなされます(セクション 2.1.1参照)。
2.2.2.3. 新しいテーブルエントリ
エンコーダストリームで新しいテーブルエントリを受信した後、デコーダはいつ Insert Count Increment 命令を発行するかを選択します(セクション 4.4.3参照)。新しい動的テーブルエントリごとにこの命令を発行すればエンコーダに最も迅速なフィードバックを提供しますが、他のデコーダフィードバックと冗長になる可能性があります。Insert Count Increment 命令を遅らせることで、デコーダは複数の Insert Count Increment 命令をまとめるか、あるいはそれらをセクション確認応答で置き換えることができます(セクション 4.4.1参照)。しかし、遅らせすぎるとエンコーダがエントリの確認を待って使用する場合に圧縮効率の低下を招く可能性があります。
2.2.3. 無効な参照
デコーダがフィールド行表現内で、既に追い出された動的テーブルエントリ、または宣言された Required Insert Count(セクション 4.5.1)以上の絶対インデックスを参照する場合、デコーダはこれを QPACK_DECOMPRESSION_FAILED 型の接続エラーとして扱わなければなりません(MUST)。
デコーダがエンコーダ命令の参照で既に追い出された動的テーブルエントリに遭遇した場合、デコーダはこれを QPACK_ENCODER_STREAM_ERROR 型の接続エラーとして扱わなければなりません(MUST)。
3. 参照テーブル
HPACK と異なり、QPACK の静的テーブルと動的テーブルのエントリは別々にアドレス指定されます。以下の節では各テーブル内のエントリがどのようにアドレス指定されるかを説明します。
3.1. 静的テーブル
静的テーブルは事前定義されたフィールド行の一覧で構成され、それぞれに時間を通じて固定のインデックスが割り当てられます。そのエントリは 付録 A に定義されています。
静的テーブル内のすべてのエントリには名前と値があります。ただし、値は空(長さ 0)であり得ます。各エントリは一意のインデックスで識別されます。
QPACK の静的テーブルは 0 からインデックスされるのに対し、HPACK の静的テーブルは 1 からインデックスされる点に注意してください。
デコーダがフィールド行表現内で無効な静的テーブルインデックスに遭遇した場合、これは QPACK_DECOMPRESSION_FAILED 型の接続エラーとして扱わなければなりません(MUST)。このインデックスがエンコーダストリーム上で受信された場合は、これは QPACK_ENCODER_STREAM_ERROR 型の接続エラーとして扱わなければなりません(MUST)。
3.2. 動的テーブル
動的テーブルは先入れ先出し順で維持されるフィールド行の一覧で構成されます。QPACK のエンコーダとデコーダは最初は空の共有動的テーブルを持ちます。エンコーダは動的テーブルにエントリを追加し、それらをエンコーダストリーム上の命令を介してデコーダに送信します(セクション 4.3参照)。
動的テーブルは重複するエントリ(同じ名前かつ同じ値を持つエントリ)を含むことができます。したがって、重複エントリはデコーダによってエラーとして扱われてはなりません(MUST NOT)。
動的テーブルのエントリは空の値を持つことができます。
3.2.1. 動的テーブルのサイズ
動的テーブルのサイズは、そのエントリのサイズの合計です。
エントリのサイズは、名前のバイト長、値のバイト長、および追加の 32 バイトの合計です。エントリのサイズは名前と値の長さをハフマン符号化を適用しない状態で計算します。
3.2.2. 動的テーブル容量と追い出し
エンコーダは動的テーブルの容量を設定し、これはそのサイズの上限となります。動的テーブルの初期容量は 0 です。エンコーダは非ゼロの容量を伴う Set Dynamic Table Capacity 命令(セクション 4.3.1)を送信して動的テーブルの使用を開始します。
新しいエントリが動的テーブルに追加される前に、動的テーブルの末尾からエントリを追い出して、新しいエントリのサイズを差し引いた後で動的テーブルのサイズが(テーブル容量 - 新しいエントリのサイズ)以下になるまで続けます。エンコーダは、追い出されるエントリが追い出し可能でない限り、そのエントリを追い出すことを引き起こしてはなりません(MUST NOT)(詳細は セクション 2.1.1参照)。その後、新しいエントリがテーブルに追加されます。エンコーダが動的テーブル容量より大きいエントリの追加を試みるとエラーとなるため、デコーダはこれを QPACK_ENCODER_STREAM_ERROR 型の接続エラーとして扱わなければなりません(MUST)。
新しいエントリは、追加する際に追い出される動的テーブル内のエントリを参照することがあります。実装は、参照された名前や値を、新しいエントリを挿入する前に参照されたエントリが追い出される場合でも削除しないよう注意する必要があります。
エンコーダが動的テーブル容量を縮小した場合(セクション 4.3.1)、動的テーブルのサイズが新しい容量以下になるまで末尾からエントリが追い出されます。この仕組みにより容量を 0 に設定して動的テーブルからエントリを完全にクリアし、その後復元することができます。
3.2.3. 動的テーブル容量の上限
デコーダのメモリ要件を抑えるため、デコーダはエンコーダが動的テーブル容量として設定できる最大値を制限します。HTTP/3 ではこの制限はデコーダから送信される SETTINGS_QPACK_MAX_TABLE_CAPACITY の値によって決まります(セクション 5参照)。エンコーダはこの最大値を超える動的テーブル容量を設定してはなりません(MUST NOT)、ただしより小さい容量を選択することはできます(セクション 4.3.1参照)。
HTTP/3 で 0-RTT データを使用するクライアントの場合、サーバの最大テーブル容量はその設定の記憶値(以前に送信されていなければ 0)です。クライアントの 0-RTT における設定値が 0 の場合、サーバは SETTINGS フレームで非ゼロ値に設定してもよい(MAY)。記憶された値が非ゼロである場合、サーバは SETTINGS フレームで同じ非ゼロ値を送信しなければなりません(MUST)。もし他の値を指定するか、SETTINGS から SETTINGS_QPACK_MAX_TABLE_CAPACITY を省略した場合、エンコーダはこれを QPACK_DECODER_STREAM_ERROR 型の接続エラーとして扱わなければなりません。
0-RTT データを使用しないクライアント(0-RTT を試行しない、または拒否された場合)およびすべての HTTP/3 サーバについては、エンコーダが非ゼロの SETTINGS_QPACK_MAX_TABLE_CAPACITY を含む SETTINGS フレームを処理するまで、最大テーブル容量は 0 です。
最大テーブル容量が 0 の場合、エンコーダは動的テーブルにエントリを挿入してはならず(MUST NOT)、エンコーダストリーム上でいかなるエンコーダ命令も送信してはなりません(MUST NOT)。
3.2.4. 絶対インデックス
各エントリはそのエントリの寿命中に固定される絶対インデックスを持ちます。最初に挿入されたエントリの絶対インデックスは 0 であり、挿入ごとにインデックスは 1 ずつ増加します。
3.2.5. 相対インデックス
相対インデックスは 0 から始まり、絶対インデックスとは逆方向に増加します。相対インデックス 0 がどのエントリを指すかは参照の文脈に依存します。
エンコーダ命令(セクション 4.3)内では、相対インデックス 0 は動的テーブルで直近に挿入された値を指します。これにより、エンコーダストリーム上の命令を解釈している間に、特定の相対インデックスが指すエントリが変化し得ることに注意してください。
+-----+---------------+-------+
| n-1 | ... | d | Absolute Index
+ - - +---------------+ - - - +
| 0 | ... | n-d-1 | Relative Index
+-----+---------------+-------+
^ |
| V
Insertion Point Dropping Point
n = count of entries inserted
d = count of entries dropped
図 2: 動的テーブルインデックスの例 - エンコーダストリーム
エンコーダ命令とは異なり、フィールド行表現内の相対インデックスは、エンコードされたフィールドセクションの開始時点での Base を基準とします(セクション 4.5.1参照)。これにより、エンコードされたフィールドセクションと動的テーブルの更新が順不同で処理されても参照が安定します。
フィールド行表現において、相対インデックス 0 は絶対インデックスが Base - 1 に等しいエントリを指します。
Base
|
V
+-----+-----+-----+-----+-------+
| n-1 | n-2 | n-3 | ... | d | Absolute Index
+-----+-----+ - +-----+ - +
| 0 | ... | n-d-3 | Relative Index
+-----+-----+-------+
n = count of entries inserted
d = count of entries dropped
In this example, Base = n - 2
図 3: 動的テーブルインデックスの例 - 表現内の相対インデックス
3.2.6. ポストベースインデックス
ポストベースインデックスは、Base 以上の絶対インデックスを持つエントリ用にフィールド行表現で使用されます。Base と等しい絶対インデックスのエントリがポストベースインデックス 0 となり、絶対インデックスと同じ方向に増加します。
ポストベースインデックスにより、エンコーダは単一パスでフィールドセクションを処理し、この(または他の)フィールドセクションの処理中に追加されたエントリへの参照を含めることができます。
Base
|
V
+-----+-----+-----+-----+-----+
| n-1 | n-2 | n-3 | ... | d | Absolute Index
+-----+-----+-----+-----+-----+
| 1 | 0 | Post-Base Index
+-----+-----+
n = count of entries inserted
d = count of entries dropped
In this example, Base = n - 2
図 4: 動的テーブルインデックスの例 - 表現内のポストベースインデックス
4. ワイヤ形式
4.1. 原始型
4.1.1. プレフィックス付き整数
RFC 7541 セクション 5.1 のプレフィックス付き整数は本書で広く使用されます。RFC 7541 の形式は変更せずに使用します。ただし、QPACK は HPACK で実際に使用されないプレフィックスサイズを用いることがある点に注意してください。
QPACK 実装は最大 62 ビット長までの整数をデコードできなければなりません(MUST)。
4.1.2. 文字列リテラル
RFC 7541 セクション 5.2 で定義された文字列リテラルも本書で使用されます。この文字列形式にはオプションのハフマン符号化が含まれます。
HPACK は文字列リテラルがオクテット境界で始まることを定義しています。これらは本書で 'H' と表される 1 ビットのフラグ(ハフマン符号化されているかどうか)で始まり、続いて 7 ビットプレフィックスの整数で表現される文字列長、最後に指示されたバイト数のデータが続きます。ハフマン符号化が有効な場合、RFC 7541 の付録 B のハフマンテーブルを変更なく使用し、指示された長さは符号化後の文字列のサイズです。
本書は文字列リテラルがオクテット境界以外で始まることを許容することで定義を拡張します。 "N ビットプレフィックス文字列リテラル" はバイト中間から始まり、最初の (8-N) ビットは前のフィールドに割り当てられます。文字列はハフマンフラグに 1 ビットを使い、続いて (N-1) ビットプレフィックス整数として符号化された符号化後文字列長が続きます。プレフィックスサイズ N は 2 から 8 の間で指定できます。文字列リテラルの残り部分は変更されません。
プレフィックス長が指定されていない文字列リテラルは 8 ビットプレフィックスの文字列リテラルであり、RFC 7541 に定義された通りに動作します。
4.2. エンコーダおよびデコーダストリーム
QPACK は二つの一方向ストリーム型を定義します:
- エンコーダストリームはタイプ 0x02 の一方向ストリームで、エンコーダからデコーダへのフレーム化されていないエンコーダ命令のシーケンスを運びます。
- デコーダストリームはタイプ 0x03 の一方向ストリームで、デコーダからエンコーダへのフレーム化されていないデコーダ命令のシーケンスを運びます。
HTTP/3 エンドポイントは QPACK のエンコーダとデコーダを含みます。各エンドポイントはエンコーダストリームを最大 1 本、デコーダストリームを最大 1 本まで開始しなければなりません(MUST)。これらのストリーム型の第二のインスタンスを受信した場合、それは H3_STREAM_CREATION_ERROR 型の接続エラーとして扱われなければなりません。
送信者はこれらのいずれのストリームも閉じてはならず(MUST NOT)、受信者は送信者に対してこれらのストリームを閉じるよう要求してはなりません(MUST NOT)。いずれか一方の一方向ストリームの閉鎖は H3_CLOSED_CRITICAL_STREAM 型の接続エラーとして扱われなければなりません(MUST)。
エンドポイントは、エンコーダストリームを使用しない場合(たとえばエンコーダが動的テーブルを使用しない、あるいはピアが許可する最大動的テーブルサイズが 0 の場合など)にエンコーダストリームを作成しないことができます(MAY)。
デコーダが動的テーブルの最大容量を 0 に設定している場合、エンドポイントはデコーダストリームを作成しないことができます(MAY)。
エンドポイントは、たとえ接続の設定がそれらの使用を妨げる場合でも、ピアがエンコーダストリームおよびデコーダストリームを作成することを許容しなければなりません(MUST)。
4.3. エンコーダ命令
エンコーダはエンコーダストリーム上で、動的テーブルの容量を設定したり動的テーブルエントリを追加する命令を送信します。エントリを追加する命令は既存エントリを利用して冗長な情報の送信を避けることができます。名前は静的または動的テーブル内の既存エントリへの参照として送信するか、文字列リテラルとして送信できます。既に動的テーブルに存在するエントリについては、重複エントリを作ることで完全なエントリを参照することもできます。
4.3.1. 動的テーブル容量の設定
エンコーダは '001' の 3 ビットパターンで始まる命令を使って動的テーブル容量の変更をデコーダに通知します。これに続いて 5 ビットプレフィックスの整数として表現された新しい動的テーブル容量が続きます(セクション 4.1.1参照)。
0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | 0 | 0 | 1 | Capacity (5+) | +---+---+---+-------------------+
図 5: 動的テーブル容量の設定
新しい容量は セクション 3.2.3 に記載された制限以下でなければなりません(MUST)。HTTP/3 ではこの制限はデコーダが送信した SETTINGS_QPACK_MAX_TABLE_CAPACITY パラメータの値です(セクション 5参照)。デコーダはこの制限を超える新しい動的テーブル容量の値を QPACK_ENCODER_STREAM_ERROR 型の接続エラーとして扱わなければなりません(MUST)。
動的テーブル容量の縮小はエントリの追い出しを引き起こす可能性があります(セクション 3.2.2参照)。この命令はエントリを挿入するものではないため確認応答は行われません。
4.3.2. 名前参照を用いた挿入
エンコーダは '1' の 1 ビットパターンで始まる命令を使って、フィールド名が静的または動的テーブルに格納されたエントリのフィールド名と一致するエントリを動的テーブルに追加します。2 番目のビット('T')は参照が静的テーブルか動的テーブルかを示します。続く 6 ビットプレフィックス整数(セクション 4.1.1)はフィールド名のテーブルエントリを特定するために使われます。T=1 の場合、その数値は静的テーブルインデックスを表し、T=0 の場合は動的テーブル内の相対インデックスを表します。
フィールド名参照の後に、フィールド値が文字列リテラルとして続きます(セクション 4.1.2参照)。
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 | T | Name Index (6+) |
+---+---+-----------------------+
| H | Value Length (7+) |
+---+---------------------------+
| Value String (Length bytes) |
+-------------------------------+
図 6: フィールド行挿入 — インデックスされた名前
4.3.3. リテラル名での挿入
エンコーダは '01' の 2 ビットパターンで始まる命令を使って、フィールド名とフィールド値の両方が文字列リテラルとして表されるエントリを動的テーブルに追加します。
これに続いて 6 ビットプレフィックスの文字列リテラルによる名前、および 8 ビットプレフィックスの文字列リテラルによる値が続きます(セクション 4.1.2参照)。
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 | 1 | H | Name Length (5+) |
+---+---+---+-------------------+
| Name String (Length bytes) |
+---+---------------------------+
| H | Value Length (7+) |
+---+---------------------------+
| Value String (Length bytes) |
+-------------------------------+
図 7: フィールド行挿入 — 新しい名前
4.3.4. 重複
エンコーダは '000' の 3 ビットパターンで始まる命令を使って、動的テーブル内の既存のエントリを複製します。これに続いて既存エントリの相対インデックスが 5 ビットプレフィックスの整数として続きます(セクション 4.1.1参照)。
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | Index (5+) |
+---+---+---+-------------------+
図 8: 重複
既存のエントリは名前や値を再送することなく動的テーブルに再挿入されます。これは、古いエントリへの参照を追加して新しいエントリの挿入を妨げることを避けるのに有用です。
4.4. デコーダ命令
デコーダはデコーダストリーム上でデコーダ命令を送信して、フィールドセクションやテーブル更新の処理をエンコーダに通知し、動的テーブルの整合性を確保します。
4.4.1. セクション確認応答
宣言された Required Insert Count がゼロでないエンコード済みフィールドセクションを処理した後、デコーダはセクション確認応答命令を発行します。この命令は '1' の 1 ビットパターンで始まり、続いて当該フィールドセクションに関連付けられたストリーム ID を 7 ビットプレフィックス整数でエンコードした値が続きます。詳細は セクション 4.1.1 を参照してください。
この命令は セクション 2.1.4 および セクション 2.2.2 に記載された用途で使用されます。
0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | 1 | Stream ID (7+) | +---+---------------------------+
図 9: セクション確認応答
エンコーダが、すでに Required Insert Count が非ゼロのすべてのエンコード済みフィールドセクションが確認済みであるストリームを参照するセクション確認応答命令を受信した場合、これは QPACK_DECODER_STREAM_ERROR 型の接続エラーとして扱わなければなりません(MUST)。
セクション確認応答命令は Known Received Count を増加させることがあります。詳細は セクション 2.1.4 を参照してください。
4.4.2. ストリームキャンセル
ストリームがリセットされたり読み取りが放棄されたとき、デコーダはストリームキャンセル命令を発行します。命令は '01' の 2 ビットパターンで始まり、続いて影響を受けるストリームのストリーム ID を 6 ビットプレフィックス整数でエンコードした値が続きます。
この命令は セクション 2.2.2 に記載された用途で使用されます。
0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | 0 | 1 | Stream ID (6+) | +---+---+-----------------------+
図 10: ストリームキャンセル
4.4.3. 挿入カウント増分
Insert Count Increment 命令は '00' の 2 ビットパターンで始まり、続いて Increment を 6 ビットプレフィックス整数としてエンコードした値が続きます。この命令は Known Received Count(セクション 2.1.4)を Increment の値だけ増加させます。デコーダは、Known Received Count をこれまで処理された動的テーブルの挿入と複製の総数に更新するような Increment 値を送信するべきです。
0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | 0 | 0 | Increment (6+) | +---+---+-----------------------+
図 11: Insert Count Increment
受信した Increment フィールドがゼロである場合、または Known Received Count をエンコーダが送信した合計よりも増加させる場合、エンコーダはこれを QPACK_DECODER_STREAM_ERROR 型の接続エラーとして扱わなければなりません(MUST)。
4.5. フィールド行の表現
エンコードされたフィールドセクションは、プレフィックスと本節で定義された(空であってもよい)一連の表現で構成されます。各表現は単一のフィールド行に対応します。これらの表現は特定の状態にある静的テーブルまたは動的テーブルを参照しますが、それらの状態を変更するものではありません。
エンコードされたフィールドセクションは、囲むプロトコルで定義されたストリーム上のフレームで運ばれます。
4.5.1. エンコードされたフィールドセクション接頭辞
各エンコードされたフィールドセクションは二つの整数で始まります。Required Insert Count は 8 ビットプレフィックスの整数としてエンコードされ、詳細は セクション 4.5.1.1 を参照してください。Base は符号ビット('S')と 7 ビットプレフィックスの Delta Base 値としてエンコードされます(セクション 4.5.1.2 を参照)。
0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | Required Insert Count (8+) | +---+---------------------------+ | S | Delta Base (7+) | +---+---------------------------+ | Encoded Field Lines ... +-------------------------------+
図 12: エンコードされたフィールドセクション
4.5.1.1. Required Insert Count
Required Insert Count は、エンコードされたフィールドセクションを処理するために必要な動的テーブルの状態を識別します。ブロッキングするデコーダは、残りのフィールドセクションを処理しても安全かどうかを判断するために Required Insert Count を使用します。
エンコーダはエンコード前に Required Insert Count を次のように変換します:
if ReqInsertCount == 0:
EncInsertCount = 0
else:
EncInsertCount = (ReqInsertCount mod (2 * MaxEntries)) + 1
ここで MaxEntries は動的テーブルが持ち得る最大エントリ数です。最小のエントリは名前と値の文字列が空で、そのサイズは 32 です。したがって、MaxEntries は次のように計算されます:
MaxEntries = floor( MaxTableCapacity / 32 )
MaxTableCapacity はデコーダが指定した動的テーブルの最大容量です。詳細は セクション 3.2.3 を参照してください。
このエンコードは長期接続におけるプレフィックス長を制限します。
デコーダは次のようなアルゴリズムで Required Insert Count を再構築できます。もしデコーダが EncodedInsertCount の値が準拠するエンコーダによって生成され得ないと判断した場合、それを QPACK_DECOMPRESSION_FAILED 型の接続エラーとして扱わなければなりません(MUST)。
TotalNumberOfInserts はデコーダの動的テーブルへの挿入の総数です。
FullRange = 2 * MaxEntries
if EncodedInsertCount == 0:
ReqInsertCount = 0
else:
if EncodedInsertCount > FullRange:
Error
MaxValue = TotalNumberOfInserts + MaxEntries
# MaxWrapped is the largest possible value of
# ReqInsertCount that is 0 mod 2 * MaxEntries
MaxWrapped = floor(MaxValue / FullRange) * FullRange
ReqInsertCount = MaxWrapped + EncodedInsertCount - 1
# If ReqInsertCount exceeds MaxValue, the Encoder's value
# must have wrapped one fewer time
if ReqInsertCount > MaxValue:
if ReqInsertCount <= FullRange:
Error
ReqInsertCount -= FullRange
# Value of 0 must be encoded as 0.
if ReqInsertCount == 0:
Error
例えば、動的テーブルが 100 バイトの場合、Required Insert Count は 6 を法としてエンコードされます。もしデコーダが 10 回の挿入を受け取っているなら、エンコード値 4 はそのフィールドセクションの Required Insert Count が 9 であることを示します。
4.5.1.2. Base
Base は動的テーブル内の参照を解決するために使用されます。詳細は セクション 3.2.5 を参照してください。
スペースを節約するため、Base は Required Insert Count に対して 1 ビットの符号(図 12 中の 'S')と Delta Base 値として相対的にエンコードされます。符号ビットが 0 の場合、Base は Required Insert Count 以上であり、デコーダは Delta Base の値を Required Insert Count に加えて Base の値を決定します。符号ビットが 1 の場合、Base は Required Insert Count より小さく、デコーダは Delta Base を Required Insert Count から引き、その結果からさらに 1 を引いて Base の値を決定します。すなわち次のようになります:
if Sign == 0:
Base = ReqInsertCount + DeltaBase
else:
Base = ReqInsertCount - DeltaBase - 1
単一パスのエンコーダはフィールドセクションをエンコードする前に Base を決定します。もしエンコーダがフィールドセクションのエンコード中に動的テーブルにエントリを挿入し、それらを参照している場合、Required Insert Count は Base より大きくなり、したがってエンコードされた差は負になり符号ビットは 1 に設定されます。フィールドセクションがテーブル内の最新エントリを参照する表現を使っていなかったり新しいエントリを挿入していなかった場合、Base は Required Insert Count より大きくなり、差は正となって符号ビットは 0 に設定されます。
Base の値は負にしてはなりません(MUST NOT)。プロトコルはポストベースインデックスを使う場合に負の Base でも動作する可能性がありますが、それは不要かつ非効率です。Required Insert Count が Delta Base 以下の場合に符号ビットが 1 のフィールドブロックを生成するエンコーダは無効と見なされなければなりません(MUST)。
フィールドセクションをエンコードする前にテーブル更新を生成するエンコーダは、Base を Required Insert Count の値に設定することがあります。その場合、符号ビットと Delta Base はともにゼロになります。
動的テーブルへの参照を使わずにエンコードされたフィールドセクションは任意の Base を使えますが、Delta Base をゼロにするのが最も効率的なエンコードの一つです。
例えば、Required Insert Count が 9 の場合にデコーダが符号ビット 1 と Delta Base 2 を受け取ると、Base は 6 になり、3 個のエントリに対してポストベースインデックスが有効になります。この例では相対インデックス 1 はテーブルに追加された 5 番目のエントリを指し、ポストベースインデックス 1 は 8 番目のエントリを指します。
4.5.2. インデックス付きフィールド行
インデックス付きフィールド行表現は、静的テーブル内のエントリ、または Base 未満の絶対インデックスを持つ動的テーブル内のエントリを識別します。
0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | 1 | T | Index (6+) | +---+---+-----------------------+
図 13: インデックス付きフィールド行
この表現は '1' の 1 ビットパターンで始まり、続いて参照が静的テーブルか動的テーブルかを示す 'T' ビットが続きます。続く 6 ビットプレフィックス整数(セクション 4.1.1)はフィールド行のテーブルエントリを特定するために使われます。T=1 の場合、その数値は静的テーブルインデックスを表し、T=0 の場合は動的テーブル内の相対インデックスを表します。
4.5.3. ポストベースインデックスを用いたインデックス付きフィールド行
ポストベースインデックスを用いたインデックス付きフィールド行表現は、Base 以上の絶対インデックスを持つ動的テーブル内のエントリを識別します。
0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | 0 | 0 | 0 | 1 | Index (4+) | +---+---+---+---+---------------+
図 14: ポストベースインデックスを用いたインデックス付きフィールド行
この表現は '0001' の 4 ビットパターンで始まります。これに続いて、対応するフィールド行のポストベースインデックス(セクション 3.2.6)が 4 ビットプレフィックスの整数として続きます。詳細は セクション 4.1.1 を参照してください。
4.5.4. 名前参照を用いたリテラルフィールド行
名前参照を用いたリテラルフィールド行表現は、フィールド名が静的テーブルのエントリのフィールド名、または Base 未満の絶対インデックスを持つ動的テーブルのエントリのフィールド名と一致するフィールド行をエンコードします。
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 | 1 | N | T |Name Index (4+)|
+---+---+---+---+---------------+
| H | Value Length (7+) |
+---+---------------------------+
| Value String (Length bytes) |
+-------------------------------+
図 15: 名前参照を用いたリテラルフィールド行
この表現は '01' の 2 ビットパターンで始まります。続くビット 'N' は、仲介者が後続のホップでこのフィールド行を動的テーブルに追加してよいかどうかを示します。'N' ビットがセットされている場合、エンコードされたフィールド行は常にリテラル表現でエンコードされなければなりません(MUST)。特に、ピアが 'N' ビットがセットされたリテラル表現として受信したフィールド行を送信する場合、それを転送する際にはリテラル表現を使わなければなりません(MUST)。このビットは、圧縮によって危険にさらすべきでないフィールド値を保護する目的で用いられます。詳細は セクション 7.1 を参照してください。
4 番目のビット 'T' は参照が静的テーブルか動的テーブルかを示します。続く 4 ビットプレフィックス整数(セクション 4.1.1)はフィールド名のテーブルエントリを特定するために使われます。T=1 の場合は静的テーブルインデックス、T=0 の場合は動的テーブル内の相対インデックスを表します。
動的テーブルエントリから取り出されるのはフィールド名のみであり、フィールド値は 8 ビットプレフィックスの文字列リテラルとしてエンコードされます。詳細は セクション 4.1.2 を参照してください。
4.5.5. ポストベース名前参照を用いたリテラルフィールド行
ポストベース名前参照を用いたリテラルフィールド行表現は、フィールド名が Base 以上の絶対インデックスを持つ動的テーブルエントリのフィールド名と一致するフィールド行をエンコードします。
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | 0 | N |NameIdx(3+)|
+---+---+---+---+---+-----------+
| H | Value Length (7+) |
+---+---------------------------+
| Value String (Length bytes) |
+-------------------------------+
図 16: ポストベース名前参照を用いたリテラルフィールド行
この表現は '0000' の 4 ビットパターンで始まります。5 ビット目は セクション 4.5.4 で説明された 'N' ビットです。これに続いて、ポストベースインデックス(セクション 3.2.6)が 3 ビットプレフィックスの整数としてエンコードされます。詳細は セクション 4.1.1 を参照してください。
動的テーブルエントリから取り出されるのはフィールド名のみであり、フィールド値は 8 ビットプレフィックスの文字列リテラルとしてエンコードされます。詳細は セクション 4.1.2 を参照してください。
4.5.6. リテラル名を用いたリテラルフィールド行
リテラル名を用いたリテラルフィールド行表現は、フィールド名とフィールド値の両方を文字列リテラルとしてエンコードします。
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 | 0 | 1 | N | H |NameLen(3+)|
+---+---+---+---+---+-----------+
| Name String (Length bytes) |
+---+---------------------------+
| H | Value Length (7+) |
+---+---------------------------+
| Value String (Length bytes) |
+-------------------------------+
図 17: リテラル名を用いたリテラルフィールド行
この表現は '001' の 3 ビットパターンで始まります。4 ビット目は セクション 4.5.4 で説明された 'N' ビットです。名前は 4 ビットプレフィックスの文字列リテラルとして続き、その後に値が 8 ビットプレフィックスの文字列リテラルとして続きます。詳細は セクション 4.1.2 を参照してください。
5. 設定
QPACK は HTTP/3 の SETTINGS フレーム向けに 2 つの設定を定義します:
- SETTINGS_QPACK_MAX_TABLE_CAPACITY (0x01):
-
デフォルト値は 0 です。使用法は セクション 3.2 を参照してください。これは HTTP/2 の SETTINGS_HEADER_TABLE_SIZE に相当します。
- SETTINGS_QPACK_BLOCKED_STREAMS (0x07):
-
デフォルト値は 0 です。詳細は セクション 2.1.2 を参照してください。
6. エラー処理
以下のエラーコードは、ストリームまたは接続の継続を妨げる QPACK の失敗を示すために HTTP/3 用として定義されています:
- QPACK_DECOMPRESSION_FAILED (0x0200):
-
デコーダがエンコードされたフィールドセクションの解釈に失敗し、そのフィールドセクションのデコードを続行できないことを示します。
- QPACK_ENCODER_STREAM_ERROR (0x0201):
-
デコーダがエンコーダストリーム上で受信したエンコーダ命令の解釈に失敗したことを示します。
- QPACK_DECODER_STREAM_ERROR (0x0202):
-
エンコーダがデコーダストリーム上で受信したデコーダ命令の解釈に失敗したことを示します。
7. セキュリティに関する考慮事項
この節では QPACK に関する潜在的なセキュリティ上の懸念点を説明します:
- 共有された圧縮コンテキストに圧縮される機密についての推測を、長さを手がかりに検証するためのオラクルとして圧縮を利用すること。
- デコーダ側で処理能力やメモリ容量を枯渇させることによるサービス拒否(DoS)。
7.1. 動的テーブル状態のプロービング
QPACK は HTTP のようなプロトコルに内在する冗長性を利用してフィールドセクションのエンコードサイズを削減します。その究極的な目的は、HTTP リクエストやレスポンスの送信に必要なデータ量を削減することです。
ヘッダやトレーラフィールドをエンコードするために使用される圧縮コンテキストは、エンコードされ送信されるフィールドを定義し、それらのエンコード後の長さを観測できる攻撃者によってプローブされ得ます。攻撃者が両方を行える場合、動的テーブルの状態についての推測を確認するためにリクエストを適応的に変更できます。推測がより短く圧縮されれば、攻撃者はエンコード長を観測してその推測が正しかったと推論できます。
これは、TLS([TLS])や QUIC トランスポート([QUIC-TRANSPORT])を介しても可能です。TLS と QUIC は内容の機密性を保護する一方で、コンテンツの長さに対する保護は限定的だからです。
CRIME([CRIME])のような攻撃は、この種の攻撃能力の存在を実証しました。特定の攻撃は DEFLATE([RFC1951])がプレフィックス一致に基づく冗長性を除去する点を突いたものでした。これにより攻撃者は 1 文字ずつ推測を確認できるようになり、指数時間の攻撃を線形時間にまで短縮しました。
7.1.1. QPACK と HTTP への適用性
QPACK は CRIME に基づく攻撃モデルを緩和しますが完全には防止しません。QPACK は推測を個々の文字ではなくフィールド行全体に一致させることを要求するため、攻撃者は推測が正しいかどうかのみを学習することになり、フィールド値に対して総当たりの推測を行うしかなくなります。
したがって、特定のフィールド値を回復できるかは値のエントロピーに依存します。エントロピーの高い値は回復されにくく、エントロピーの低い値は依然として脆弱です。
この種の攻撃は、2 つの相互に不信なエンティティが単一の HTTP/3 接続に置かれ、それぞれが送信されるリクエストやレスポンスを制御できる場合に常に可能です。共有 QPACK 圧縮器があるエンティティに動的テーブルへのエントリ追加を許し、別のエンティティがそれらのエントリを参照して選択的にフィールド行をエンコードできる場合、攻撃者(後者のエンティティ)はエンコード出力の長さを観測してテーブルの状態を学習できます。
例えば、相互に不信なエンティティからのリクエストやレスポンスは、仲介者が次のいずれかを行う場合に発生します:
- 複数のクライアントからのリクエストを単一接続でオリジンへ送信する、あるいは
- 複数のオリジンサーバからのレスポンスをまとめてクライアントへの単一接続に置く。
ウェブブラウザは、同一接続上で異なるウェブオリジン([RFC6454])が行うリクエストは相互に不信なエンティティによる可能性があると想定する必要があります。他の相互に不信なエンティティを含むシナリオも考えられます。
7.1.2. 緩和策
ヘッダやトレーラフィールドの機密性を必要とする HTTP 利用者は、推測を事実上不可能にする十分なエントロピーを持つ値を使用することができます。しかしこれは一般的な解決策としては現実的でなく、すべての HTTP 利用者に対して対策を強いることになり、新たな制約を課すことになります。
HTTP 利用者への制約を課す代わりに、QPACK の実装は動的テーブルのプロービングの可能性を制限するように圧縮の適用方法を制約することができます。
理想的な解は、メッセージを構築するエンティティごとに動的テーブルへのアクセスを分離することです。テーブルに追加されるフィールド値はあるエンティティに帰属させられ、その値を作成したエンティティのみがその値を抽出できるようにします。
このオプションの圧縮性能を改善するために、特定のエントリを公開としてタグ付けすることが考えられます。例えば、ウェブブラウザは Accept-Encoding ヘッダの値をすべてのリクエストで利用可能にするかもしれません。
フィールド値の由来を十分に把握していないエンコーダは、同じフィールド名で多数の異なる値がある場合にペナルティを導入することができます。このペナルティにより、多数の推測試行が行われると、そのフィールドは将来のメッセージで動的テーブルのエントリと比較されなくなり、実質的にさらなる推測を防ぐことができます。
この応答はフィールド値の長さに反比例して行われることがあります。動的テーブルへのアクセスを無効にする対応は、より短い値に対してより早く、または高い確率で発生するようにできます。
この緩和策は二つのエンドポイント間で最も効果的です。仲介者が元のエンコーダが分離していた圧縮コンテキストを意図せずに結合してしまうような場合、元のエンコーダが分離していた状態を仲介者が知らないために効果が薄れる可能性があります。
7.1.3. 決してインデックスされないリテラル
実装は機微なフィールドを保護するためにそれらを圧縮せずに値をリテラルとしてエンコードすることを選択できます。
フィールド行を動的テーブルに挿入しないことは、経路上のすべてのホップで回避される場合にのみ効果的です。never-indexed literal ビット(セクション 4.5.4参照)は、特定の値が意図的にリテラルとして送信されたことを仲介者に通知するために使用できます。
仲介者は、'N' ビットがセットされたリテラル表現を、インデックスする表現に再エンコードしてはなりません(MUST NOT)。QPACK を再エンコードに使用する場合、'N' ビットがセットされたリテラル表現を使用しなければなりません(MUST)。HPACK を使用する場合は、never-indexed literal 表現(RFC 7541 セクション 6.2.3)を使用しなければなりません(MUST)。
フィールド値を決してインデックスしないとマークするか否かの選択は複数の要因に依存します。QPACK はフィールド値全体の推測に対して保護を提供しないため、短いか低エントロピーの値は攻撃者により容易に回復され得ます。したがって、エンコーダは低エントロピーの値をインデックスしないことを選ぶかもしれません。
エンコーダは Cookie や Authorization のように回復されると非常に価値のあるフィールドについては値をインデックスしないことを選ぶかもしれません。
逆に、エンコーダは露見してもほとんど価値のないフィールドについてはインデックスすることを好むかもしれません。例えば User-Agent ヘッダはリクエスト間で大きく変化せず任意のサーバへ送信されるため、その値が使われていることを確認できてもほとんど価値がありません。
これらの決定基準は新しい攻撃が発見されるにつれて進化することに注意してください。
7.2. 静的ハフマン符号化
現時点で静的ハフマン符号化に対する既知の攻撃はありません。ある研究では静的ハフマン符号表の使用が情報漏えいを生むことが示されましたが、その研究は攻撃者がその情報漏えいを利用して有意義な情報を回復できるとは結論していません([PETAL]参照)。
7.3. メモリ消費
攻撃者はエンドポイントのメモリを枯渇させようと試みる可能性があります。QPACK はエンドポイントによって割り当てられるピークおよび安定したメモリ量の両方を制限するよう設計されています。
QPACK は動的テーブルの最大サイズの定義とブロックされるストリームの最大数を利用して、エンコーダがデコーダに消費させ得るメモリ量を制限します。HTTP/3 では、これらの値はデコーダが設定パラメータ SETTINGS_QPACK_MAX_TABLE_CAPACITY と SETTINGS_QPACK_BLOCKED_STREAMS によって制御します(セクション 3.2.3 および セクション 2.1.2参照)。動的テーブルのサイズ制限は動的テーブルに格納されるデータのサイズに、若干のオーバーヘッドを加えたものを考慮します。ブロックされるストリーム数の制限はデコーダが必要とする最大メモリ量の単なる代理にすぎません。実際の最大メモリ量はデコーダが各ブロックされたストリームを追跡するために使用するメモリ量に依存します。
デコーダは動的テーブルの最大サイズに適切な値を設定することで動的テーブルに使われる状態メモリ量を制限できます。HTTP/3 ではこれは SETTINGS_QPACK_MAX_TABLE_CAPACITY パラメータの適切な値を設定することで実現されます。エンコーダはデコーダが許すより小さい動的テーブルサイズを選択してデコーダにシグナルすることで自らが使用する状態メモリ量を制限できます(セクション 4.3.1参照)。
デコーダはブロックされたストリームに対して使用する状態メモリ量を、ブロックされ得るストリームの最大数の適切な値を設定することで制限できます。HTTP/3 ではこれは SETTINGS_QPACK_BLOCKED_STREAMS パラメータの適切な値を設定することで実現されます。ブロックされるリスクのあるストリームはエンコーダ側で追加の状態メモリを消費しません。
エンコーダは未確認のフィールドセクション内の全ての動的テーブル参照を追跡するためのメモリを割り当てます。実装は追跡する参照の数を制限することで直接的に状態メモリの量を制限できます。デコーダへのシグナリングは不要です。ただし、動的テーブル参照を制限すると圧縮効果が低下します。
エンコーダまたはデコーダが消費する一時的なメモリ量は、フィールド行を逐次処理することで制限できます。デコーダ実装はフィールドセクションをデコードする際にフィールド行の完全な一覧を保持する必要はありません。エンコーダ実装はシングルパスアルゴリズムを使用している場合、フィールドセクションをエンコードする間に完全なフィールド行一覧を保持する必要はありません。アプリケーションが他の理由で完全な一覧を保持する必要がある場合がある点に注意してください。
交渉された動的テーブルサイズの上限は QPACK 実装によって消費され得るメモリの多くを説明しますが、フロー制御により直ちに送信できないデータはこの制限の影響を受けません。実装は未送信データのサイズを制限すべきです。特にデコーダストリーム上では送信する内容を選べる柔軟性が限られているため注意が必要です。未送信データが過剰になった場合の対応としては、ピアが新しいストリームを開ける能力を制限する、エンコーダストリームからのみ読み取る、あるいは接続を閉じるなどが考えられます。
7.4. 実装上の制限
QPACK の実装は、大きな整数値、長い整数エンコーディング、または長い文字列リテラルがセキュリティ上の弱点を生じさせないことを保証する必要があります。
実装は受け入れる整数の値やエンコード長、および文字列リテラルの長さに対して上限を設定する必要があります(セクション 4.1.1、および セクション 4.1.2参照)。これらの上限は、HTTP 実装が処理可能に構成できる最大の個々のフィールドを処理できるだけ十分大きくあるべきです(SHOULD)。
実装がデコードできないより大きな値に遭遇した場合、要求ストリーム上であればこれは QPACK_DECOMPRESSION_FAILED 型のストリームエラーとして扱われなければならず、エンコーダやデコーダストリーム上であれば適切な種類の接続エラーとして扱われなければなりません(MUST)。
8. IANA に関する考慮事項
この文書は [HTTP/3] によって定義されたレジストリへ複数の登録を行います。本文書による割り当てはすべて永久的なステータスに割り当てられ、変更管理者は IETF、連絡先は HTTP 作業部会 (ietf-http-wg@w3.org) としています。
8.1. 設定の登録
本文書は二つの設定を指定します。以下の表のエントリは [HTTP/3] によって確立された「HTTP/3 Settings」レジストリに登録されます。
| 設定名 | コード | 仕様 | デフォルト |
|---|---|---|---|
| QPACK_MAX_TABLE_CAPACITY | 0x01 | セクション 5 | 0 |
| QPACK_BLOCKED_STREAMS | 0x07 | セクション 5 | 0 |
書式上の理由から、ここでの設定名は 'SETTINGS_' プレフィックスを取り除いて短縮されています。
8.2. ストリームタイプの登録
本文書は二つのストリームタイプを指定します。以下の表のエントリは [HTTP/3] によって確立された「HTTP/3 Stream Types」レジストリに登録されます。
8.3. エラーコードの登録
本文書は三つのエラーコードを指定します。以下の表のエントリは [HTTP/3] によって確立された「HTTP/3 Error Codes」レジストリに登録されます。
9. 参考文献
9.1. 規範的参考文献
- [HTTP]
- Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., “HTTP セマンティクス”, STD 97, RFC 9110, DOI 10.17487/RFC9110, 2022年6月, <https://www.rfc-editor.org/info/rfc9110>.
- [HTTP/3]
- Bishop, M., Ed., “HTTP/3”, RFC 9114, DOI 10.17487/RFC9114, 2022年6月, <https://www.rfc-editor.org/info/rfc9114>.
- [QUIC-TRANSPORT]
- Iyengar, J., Ed. and M. Thomson, Ed., “QUIC: A UDP-Based Multiplexed and Secure Transport”, RFC 9000, DOI 10.17487/RFC9000, 2021年5月, <https://www.rfc-editor.org/info/rfc9000>.
- [RFC2119]
- Bradner, S., “RFC における要求レベルを示すキーワード”, BCP 14, RFC 2119, DOI 10.17487/RFC2119, 1997年3月, <https://www.rfc-editor.org/info/rfc2119>.
- [RFC2360]
- Scott, G., “インターネット標準作成者のためのガイド”, BCP 22, RFC 2360, DOI 10.17487/RFC2360, 1998年6月, <https://www.rfc-editor.org/info/rfc2360>.
- [RFC7541]
- Peon, R. and H. Ruellan, “HPACK: Header Compression for HTTP/2”, RFC 7541, DOI 10.17487/RFC7541, 2015年5月, <https://www.rfc-editor.org/info/rfc7541>.
- [RFC8174]
- Leiba, B., “RFC 2119 のキーワードにおける大文字と小文字の曖昧さ”, BCP 14, RFC 8174, DOI 10.17487/RFC8174, 2017年5月, <https://www.rfc-editor.org/info/rfc8174>.
9.2. 情報的参考文献
- [CRIME]
- Wikipedia, “CRIME”, 2015年5月, <http://en.wikipedia.org/w/index.php?title=CRIME&oldid=660948120>.
- [HTTP/2]
- Thomson, M., Ed. and C. Benfield, Ed., “HTTP/2”, RFC 9113, DOI 10.17487/RFC9113, 2022年6月, <https://www.rfc-editor.org/info/rfc9113>.
- [PETAL]
- Tan, J. and J. Nahata, “PETAL: Preset Encoding Table Information Leakage”, 2013年4月, <http://www.pdl.cmu.edu/PDL-FTP/associated/CMU-PDL-13-106.pdf>.
- [RFC1951]
- Deutsch, P., “DEFLATE 圧縮データ形式仕様 バージョン 1.3”, RFC 1951, DOI 10.17487/RFC1951, 1996年5月, <https://www.rfc-editor.org/info/rfc1951>.
- [RFC6454]
- Barth, A., “ウェブオリジンの概念”, RFC 6454, DOI 10.17487/RFC6454, 2011年12月, <https://www.rfc-editor.org/info/rfc6454>.
- [TLS]
- Rescorla, E., “Transport Layer Security (TLS) プロトコル バージョン 1.3”, RFC 8446, DOI 10.17487/RFC8446, 2018年8月, <https://www.rfc-editor.org/info/rfc8446>.
Appendix A. 静的テーブル
この表は 2018 年の実際のインターネットトラフィックを分析して作成され、非対応または非標準の値を除外した上で最も一般的なヘッダフィールドを含めています。この手法のため、一部のエントリは一貫性に欠けたり、類似するが完全には同一でない値で複数回現れることがあります。エントリの順序は、最も一般的なヘッダフィールドを最小のバイト数で符号化するよう最適化されています。
| インデックス | 名前 | 値 |
|---|---|---|
| 0 | :authority | |
| 1 | :path | / |
| 2 | age | 0 |
| 3 | content-disposition | |
| 4 | content-length | 0 |
| 5 | cookie | |
| 6 | date | |
| 7 | etag | |
| 8 | if-modified-since | |
| 9 | if-none-match | |
| 10 | last-modified | |
| 11 | link | |
| 12 | location | |
| 13 | referer | |
| 14 | set-cookie | |
| 15 | :method | CONNECT |
| 16 | :method | DELETE |
| 17 | :method | GET |
| 18 | :method | HEAD |
| 19 | :method | OPTIONS |
| 20 | :method | POST |
| 21 | :method | PUT |
| 22 | :scheme | http |
| 23 | :scheme | https |
| 24 | :status | 103 |
| 25 | :status | 200 |
| 26 | :status | 304 |
| 27 | :status | 404 |
| 28 | :status | 503 |
| 29 | accept | */* |
| 30 | accept | application/dns-message |
| 31 | accept-encoding | gzip, deflate, br |
| 32 | accept-ranges | bytes |
| 33 | access-control-allow-headers | cache-control |
| 34 | access-control-allow-headers | content-type |
| 35 | access-control-allow-origin | * |
| 36 | cache-control | max-age=0 |
| 37 | cache-control | max-age=2592000 |
| 38 | cache-control | max-age=604800 |
| 39 | cache-control | no-cache |
| 40 | cache-control | no-store |
| 41 | cache-control | public, max-age=31536000 |
| 42 | content-encoding | br |
| 43 | content-encoding | gzip |
| 44 | content-type | application/dns-message |
| 45 | content-type | application/javascript |
| 46 | content-type | application/json |
| 47 | content-type | application/x-www-form-urlencoded |
| 48 | content-type | image/gif |
| 49 | content-type | image/jpeg |
| 50 | content-type | image/png |
| 51 | content-type | text/css |
| 52 | content-type | text/html; charset=utf-8 |
| 53 | content-type | text/plain |
| 54 | content-type | text/plain;charset=utf-8 |
| 55 | range | bytes=0- |
| 56 | strict-transport-security | max-age=31536000 |
| 57 | strict-transport-security | max-age=31536000; includesubdomains |
| 58 | strict-transport-security | max-age=31536000; includesubdomains; preload |
| 59 | vary | accept-encoding |
| 60 | vary | origin |
| 61 | x-content-type-options | nosniff |
| 62 | x-xss-protection | 1; mode=block |
| 63 | :status | 100 |
| 64 | :status | 204 |
| 65 | :status | 206 |
| 66 | :status | 302 |
| 67 | :status | 400 |
| 68 | :status | 403 |
| 69 | :status | 421 |
| 70 | :status | 425 |
| 71 | :status | 500 |
| 72 | accept-language | |
| 73 | access-control-allow-credentials | FALSE |
| 74 | access-control-allow-credentials | TRUE |
| 75 | access-control-allow-headers | * |
| 76 | access-control-allow-methods | get |
| 77 | access-control-allow-methods | get, post, options |
| 78 | access-control-allow-methods | options |
| 79 | access-control-expose-headers | content-length |
| 80 | access-control-request-headers | content-type |
| 81 | access-control-request-method | get |
| 82 | access-control-request-method | post |
| 83 | alt-svc | clear |
| 84 | authorization | |
| 85 | content-security-policy | script-src 'none'; object-src 'none'; base-uri 'none' |
| 86 | early-data | 1 |
| 87 | expect-ct | |
| 88 | forwarded | |
| 89 | if-range | |
| 90 | origin | |
| 91 | purpose | prefetch |
| 92 | server | |
| 93 | timing-allow-origin | * |
| 94 | upgrade-insecure-requests | 1 |
| 95 | user-agent | |
| 96 | x-forwarded-for | |
| 97 | x-frame-options | deny |
| 98 | x-frame-options | sameorigin |
フィールド名や値内に現れる改行はフォーマット上の都合によるものです。
付録 B. エンコード/デコードの例
以下の例は、エンコーダとデコーダの間での一連のやり取りを表します。これらのやり取りは、QPACK のほとんどの命令を動作させ、動的テーブルの状態に対する一般的なパターンとその影響を示すことを目的としています。エンコーダは各々 1 行のフィールドを含む 3 つのエンコード済みフィールドセクションと、参照されない 2 つの投機的挿入を送信します。
エンコーダ側の動的テーブルの状態が、その現在のサイズとともに示されています。各エントリはエントリの絶対インデックス(Abs)、そのエントリへの未確定参照数(Ref)、および名前と値で示されます。'acknowledged' ラインより上のエントリはデコーダにより確認されています。
B.1. 名前参照を用いたリテラルフィールド行
エンコーダは静的な名前参照を持つフィールドのリテラル表現を含むエンコード済みフィールドセクションを送信します。
データ | 解釈
| エンコーダの動的テーブル
Stream: 0
0000 | Required Insert Count = 0, Base = 0
510b 2f69 6e64 6578 | Literal Field Line with Name Reference
2e68 746d 6c | Static Table, Index=1
| (:path=/index.html)
Abs Ref Name Value
^-- acknowledged --^
Size=0
B.2. 動的テーブル
エンコーダは動的テーブルの容量を設定し、動的な名前参照を使ってヘッダを挿入してから、この新しいエントリを参照する可能性のあるブロッキングするエンコード済みフィールドセクションを送信します。デコーダはエンコード済みフィールドセクションの処理を確認し、これにより Required Insert Count までのすべての動的テーブル挿入が暗黙に確認されます。
Stream: Encoder
3fbd01 | Set Dynamic Table Capacity=220
c00f 7777 772e 6578 | Insert With Name Reference
616d 706c 652e 636f | Static Table, Index=0
6d | (:authority=www.example.com)
c10c 2f73 616d 706c | Insert With Name Reference
652f 7061 7468 | Static Table, Index=1
| (:path=/sample/path)
Abs Ref Name Value
^-- acknowledged --^
0 0 :authority www.example.com
1 0 :path /sample/path
Size=106
Stream: 4
0381 | Required Insert Count = 2, Base = 0
10 | Indexed Field Line With Post-Base Index
| Absolute Index = Base(0) + Index(0) = 0
| (:authority=www.example.com)
11 | Indexed Field Line With Post-Base Index
| Absolute Index = Base(0) + Index(1) = 1
| (:path=/sample/path)
Abs Ref Name Value
^-- acknowledged --^
0 1 :authority www.example.com
1 1 :path /sample/path
Size=106
Stream: Decoder
84 | Section Acknowledgment (stream=4)
Abs Ref Name Value
0 0 :authority www.example.com
1 0 :path /sample/path
^-- acknowledged --^
Size=106
B.3. 投機的挿入
エンコーダは文字列名で動的テーブルにヘッダを挿入します。デコーダはそのエントリの受信を確認します。エンコーダはエンコード済みフィールドセクションを送信しません。
Stream: Encoder
4a63 7573 746f 6d2d | Insert With Literal Name
6b65 790c 6375 7374 | (custom-key=custom-value)
6f6d 2d76 616c 7565 |
Abs Ref Name Value
0 0 :authority www.example.com
1 0 :path /sample/path
^-- acknowledged --^
2 0 custom-key custom-value
Size=160
Stream: Decoder
01 | Insert Count Increment (1)
Abs Ref Name Value
0 0 :authority www.example.com
1 0 :path /sample/path
2 0 custom-key custom-value
^-- acknowledged --^
Size=160
B.4. Duplicate 命令、ストリームキャンセル
エンコーダは動的テーブル内の既存エントリを複製し、その複製エントリを含む動的テーブルエントリを参照するエンコード済みフィールドセクションを送信します。エンコーダストリームのデータを含むパケットが遅延します。パケット到着の前にデコーダがストリームをキャンセルし、エンコーダに対してそのエンコード済みフィールドセクションが処理されなかったことを通知します。
Stream: Encoder
02 | Duplicate (Relative Index = 2)
| Absolute Index =
| Insert Count(3) - Index(2) - 1 = 0
Abs Ref Name Value
0 0 :authority www.example.com
1 0 :path /sample/path
2 0 custom-key custom-value
^-- acknowledged --^
3 0 :authority www.example.com
Size=217
Stream: 8
0500 | Required Insert Count = 4, Base = 4
80 | Indexed Field Line, Dynamic Table
| Absolute Index = Base(4) - Index(0) - 1 = 3
| (:authority=www.example.com)
c1 | Indexed Field Line, Static Table Index = 1
| (:path=/)
81 | Indexed Field Line, Dynamic Table
| Absolute Index = Base(4) - Index(1) - 1 = 2
| (custom-key=custom-value)
Abs Ref Name Value
0 0 :authority www.example.com
1 0 :path /sample/path
2 1 custom-key custom-value
^-- acknowledged --^
3 1 :authority www.example.com
Size=217
Stream: Decoder
48 | Stream Cancellation (Stream=8)
Abs Ref Name Value
0 0 :authority www.example.com
1 0 :path /sample/path
2 0 custom-key custom-value
^-- acknowledged --^
3 0 :authority www.example.com
Size=217
B.5. 動的テーブルの挿入と追い出し
エンコーダは別のヘッダを動的テーブルに挿入し、これにより最も古いエントリが追い出されます。エンコーダはエンコード済みフィールドセクションを送信しません。
Stream: Encoder
810d 6375 7374 6f6d | Insert With Name Reference
2d76 616c 7565 32 | Dynamic Table, Relative Index = 1
| Absolute Index =
| Insert Count(4) - Index(1) - 1 = 2
| (custom-key=custom-value2)
Abs Ref Name Value
1 0 :path /sample/path
2 0 custom-key custom-value
^-- acknowledged --^
3 0 :authority www.example.com
4 0 custom-key custom-value2
Size=215
付録 C. 単一パスエンコーディングアルゴリズムの例
重複処理、非ブロッキングモード、利用可能なエンコーダストリームのフロー制御、および参照追跡の処理を除外した単一パスエンコーディングの疑似コードです。
# Helper functions:
# ====
# Encode an integer with the specified prefix and length
encodeInteger(buffer, prefix, value, prefixLength)
# Encode a dynamic table insert instruction with optional static
# or dynamic name index (but not both)
encodeInsert(buffer, staticNameIndex, dynamicNameIndex, fieldLine)
# Encode a static index reference
encodeStaticIndexReference(buffer, staticIndex)
# Encode a dynamic index reference relative to Base
encodeDynamicIndexReference(buffer, dynamicIndex, base)
# Encode a literal with an optional static name index
encodeLiteral(buffer, staticNameIndex, fieldLine)
# Encode a literal with a dynamic name index relative to Base
encodeDynamicLiteral(buffer, dynamicNameIndex, base, fieldLine)
# Encoding Algorithm
# ====
base = dynamicTable.getInsertCount()
requiredInsertCount = 0
for line in fieldLines:
staticIndex = staticTable.findIndex(line)
if staticIndex is not None:
encodeStaticIndexReference(streamBuffer, staticIndex)
continue
dynamicIndex = dynamicTable.findIndex(line)
if dynamicIndex is None:
# No matching entry. Either insert+index or encode literal
staticNameIndex = staticTable.findName(line.name)
if staticNameIndex is None:
dynamicNameIndex = dynamicTable.findName(line.name)
if shouldIndex(line) and dynamicTable.canIndex(line):
encodeInsert(encoderBuffer, staticNameIndex,
dynamicNameIndex, line)
dynamicIndex = dynamicTable.add(line)
if dynamicIndex is None:
# Could not index it, literal
if dynamicNameIndex is not None:
# Encode literal with dynamic name, possibly above Base
encodeDynamicLiteral(streamBuffer, dynamicNameIndex,
base, line)
requiredInsertCount = max(requiredInsertCount,
dynamicNameIndex)
else:
# Encodes a literal with a static name or literal name
encodeLiteral(streamBuffer, staticNameIndex, line)
else:
# Dynamic index reference
assert(dynamicIndex is not None)
requiredInsertCount = max(requiredInsertCount, dynamicIndex)
# Encode dynamicIndex, possibly above Base
encodeDynamicIndexReference(streamBuffer, dynamicIndex, base)
# encode the prefix
if requiredInsertCount == 0:
encodeInteger(prefixBuffer, 0x00, 0, 8)
encodeInteger(prefixBuffer, 0x00, 0, 7)
else:
wireRIC = (
requiredInsertCount
% (2 * getMaxEntries(maxTableCapacity))
) + 1;
encodeInteger(prefixBuffer, 0x00, wireRIC, 8)
if base >= requiredInsertCount:
encodeInteger(prefixBuffer, 0x00,
base - requiredInsertCount, 7)
else:
encodeInteger(prefixBuffer, 0x80,
requiredInsertCount - base - 1, 7)
return encoderBuffer, prefixBuffer + streamBuffer
謝辞
IETF QUIC ワーキンググループは多くの方々から多大な支援を受けました。
圧縮設計チームは問題領域の検討と本文書の初期草案版への影響に関して大きな作業を行いました。設計チームメンバーの Roberto Peon、Martin Thomson、および Dmitri Tikhonov の貢献に感謝します。
以下の人々も本書に対して多大な貢献をしました:
-
Bence Beky
-
Alessandro Ghedini
-
Ryan Hamilton
-
Robin Marx
-
Patrick McManus
-
奥 一穂 (Kazuho Oku)
-
Lucas Pardue
-
Biren Roy
-
Ian Swett
この文書は [RFC7541] の本文に大きく依拠しています。これらの著者による間接的なインプットにも感謝します。
Buck Krasic の貢献は、彼が Google に在職していた間に Google によって支援されました。
Mike Bishop の貢献の一部は、彼が Microsoft に在職していた間に同社によって支援されました。