[129] [DFN[[[HPACK]]]] は、[[HTTP/2]] の[[ヘッダー]]で採用されている[[圧縮形式]]です。
[DFN[[[RFC 7541]]]] で定義されています。

* 仕様書

[REFS[
- [73] [CITE@en[RFC 7540 - Hypertext Transfer Protocol Version 2 (HTTP/2)]] ([TIME[2015-05-15 10:14:54 +09:00]] 版) <https://tools.ietf.org/html/rfc7540#section-4.3>
- [84] [CITE@en[RFC 7540 - Hypertext Transfer Protocol Version 2 (HTTP/2)]] ([TIME[2015-05-15 10:14:54 +09:00]] 版) <https://tools.ietf.org/html/rfc7540#section-6.5.2>
- [90] [CITE@en[RFC 7540 - Hypertext Transfer Protocol Version 2 (HTTP/2)]] ([TIME[2015-05-15 10:14:54 +09:00]] 版) <https://tools.ietf.org/html/rfc7540#section-7>
- [104] [CITE@en[RFC 7540 - Hypertext Transfer Protocol Version 2 (HTTP/2)]] ([TIME[2015-05-15 10:14:54 +09:00]] 版) <https://tools.ietf.org/html/rfc7540#section-10.5>
- [111] [CITE@en[RFC 7540 - Hypertext Transfer Protocol Version 2 (HTTP/2)]] ([TIME[2015-05-15 10:14:54 +09:00]] 版) <https://tools.ietf.org/html/rfc7540#section-10.6>
- [1] '''[CITE@en[RFC 7541 - HPACK: Header Compression for HTTP/2]] ([TIME[2015-05-15 10:13:02 +09:00]] 版) <https://tools.ietf.org/html/rfc7541>'''
-- [103] [CITE@en[RFC 7541 - HPACK: Header Compression for HTTP/2]] ([TIME[2015-05-15 10:13:02 +09:00]] 版) <https://tools.ietf.org/html/rfc7541#appendix-A>
-- [105] [CITE@en[RFC 7541 - HPACK: Header Compression for HTTP/2]] ([TIME[2015-05-15 10:13:02 +09:00]] 版) <https://tools.ietf.org/html/rfc7541#appendix-B>
]REFS]

* ヘッダーブロック

[5] [DFN[[RUBYB[ヘッダーリスト]@en[header list]]]]は、
[[ヘッダー欄]]の順序付きリストです。 [SRC[>>1]]

[28] 重複する[[ヘッダー欄]]を含むことができます。
[[HTTP/2]] [[header block]] に含まれる[[ヘッダー欄]]の完全なリストは、
[[ヘッダーリスト]]です。 [SRC[>>1]]

[7] [DFN[[RUBYB[ヘッダーブロック]@en[header block]]]]は、
[[ヘッダー欄表現]]を連結したもので、[[復号]]すると完全な[[ヘッダーリスト]]が得られるものです。
[SRC[>>1]]

[37] [[ヘッダーブロック]]の先頭には[[動的表サイズ更新]]があるかもしれません。

[FIG(railroad)[
= ?
== [[動的表サイズ更新]]
== ?
=== [[動的表サイズ更新]]
= *
== [[ヘッダー欄表現]]
]FIG]

[11] [RUBYB[[[符号化器]]]@en[encoder]]は、入力の[[ヘッダーリスト]]を符号化して[[ヘッダーブロック]]として出力するものです。
[[フレーム]]に含めるデータの生成に用います。
[[符号化器]]は、次のようにします。
[FIG(steps)[
= 必要があれば、
== 1個または2個の[[動的表サイズ更新]]を出力します。
= [[ヘッダーリスト]]内の[[ヘッダー欄]]を順に [SRC[>>1]]、
== 符号化して[[ヘッダー欄表現]]を得ます。
== 得られた[[ヘッダー欄表現]]を出力します。
]FIG]

[49] [RUBYB[[[復号器]]]@en[decoder]]は、入力の[[ヘッダーブロック]]を復号して[[ヘッダーリスト]]として出力するものです。
復号エラーを返すこともあります。
[[フレーム]]に含まれるデータの解釈に用います。
[[復号器]]は、次のようにします。
[FIG(steps)[
= 空の[[ヘッダーリスト]]を用意します。
= 入力の先頭から順に [SRC[>>1]]、
== [[動的表サイズ更新]]なら、
=== 適切に処理します。
=== 復号エラーなら、停止します。
== [[ヘッダー欄表現]]なら、
=== 復号して[[ヘッダー欄]]を得ます。
=== 復号エラーなら、停止します。
=== [[ヘッダーリスト]]の末尾に[[ヘッダー欄]]を追加します。
= [[ヘッダーリスト]]を返します。
]FIG]

[113] なお[[ヘッダーブロック]]の各内容は、先頭ビットにより次のように判断できます。
[FIG(list)[
- [CODE[1]] なら、索引表現 (>>19)
- [CODE[01]] なら、索引付けするリテラル表現 (>>26)
- [CODE[001]] なら、動的表サイズ更新 (>>40)
- [CODE[0001]] なら、決して索引付けしないリテラル表現 (>>24)
- [CODE[0000]] なら、索引付けしないリテラル表現 (>>23)
]FIG]

;; [110] [[HTTP]] の[[ヘッダー]]の順序は、意味があります。[[HPACK]]
では同名の[[ヘッダー]]も含めて順序を保持しなければ[['''なりません''']] [SRC[>>1]]。

* ヘッダー欄

[2] [DFN[[RUBYB[ヘッダー欄]@en[header field]]]]は、名前と値の組です。
いずれも不透明な[[オクテット列]]として扱います。 [SRC[>>1]]

;; [8] [[HTTP/2]] では任意の[[オクテット列]]が認められているわけではありませんが、
[[HPACK]] では緩めになっています。不正な[[オクテット列]]なら、 [[HTTP/2]]
としての処理でエラー ([[奇形]]) となります。

;; [27] [[HTTPヘッダー]]だけでなく、[[疑似ヘッダー]]も含まれます。
[[HTTP/2]] では[[疑似ヘッダー]]が他の [[HTTPヘッダー]]の前に来なければならないことになっていますが、
[[HPACK]] としては制約はありません。

[6] [DFN[[RUBYB[ヘッダー欄表現]@en[header field representation]]]]は、
[[ヘッダー欄]]を索引表現またはリテラル表現によって[[符号化]]したものです。 [SRC[>>1]]

[FIG(railroad)[
= |
== [[索引表現]]
== [[リテラル表現]] (索引付けする)
== [[リテラル表現]] (索引付けしない)
== [[リテラル表現]] (決して索引付けしない)
]FIG]

[9] [[符号化器]]は、[[ヘッダー欄]]をいずれかの方法で[[ヘッダー欄表現]]として[[符号化]]しなければなりません。
その選択の方法は実装に任されていますが、[[ヘッダー名]]や値の性質によって、
[[セキュリティー]]上問題の無いように決定する必要があります。

;; >>85 参照。

[125] 索引付けを行うリテラル表現により[[符号化]]する場合は、符号化に加えて、
必要なら [[eviction]] を行ってから [SRC[>>1]]、
[[ヘッダー欄]]を[[動的表]]の先頭に追加 (できれば) します [SRC[>>1]]。

[29] [[復号器]]は、[[ヘッダー欄表現]]を次のようにしなければ[['''なりません''']]。
[FIG(steps)[
= [30] [[索引]]表現なら、
== 復号エラーが検出されたら、停止します。
== そうでなければ、[[索引]]で参照されている項目の[[ヘッダー欄]]を返します [SRC[>>1]]。
= [31] リテラル表現なら、
== 復号エラーが検出されたら、停止します。
== [32] そうでなければ、
=== リテラル表現から[[ヘッダー欄]]を得ます [SRC[>>1]]。
=== [33] 索引付けを行うリテラル表現なら、
==== 必要なら、 [[eviction]] を行います [SRC[>>1]]。
==== 得られた[[ヘッダー欄]]を[[動的表]]の先頭に追加 (できれば) します [SRC[>>1]]。
=== 得られた[[ヘッダー欄]]を返します。
]FIG]

[114] [[中間器]]の実装に用いる[[符号化器]]と[[復号器]]は、
通常の名前と値の組に加えて、決して索引付けしないリテラル表現であることを保存しなければなりません
(>>86)。

;; [115] [[中間器]]は、 [[HPACK]] の[[接続]]の[[動的表]]への依存性のため、
すべての[[ヘッダー]]を無変更で[[転送]]する場合であっても必ず[[ヘッダーブロック]]を一旦[[復号]]してから改めて[[符号化]]しなければなりません。

** 索引表現

[19] [RUBYB[[[索引]]表現]@en[indexed representation]]は、[[静的表]]または[[動的表]]上の項目を参照する形で[[ヘッダー欄]]を表しています。 [SRC[>>1]]

[64] 索引表現は、最初の1ビットが [CODE[1]] でその後の7ビットを[[接頭辞]]とする[[整数]]
(>>50) で構成され、この[[整数]]が[[索引]] (>>17) を表します。 [SRC[>>1]]

;; [111] [[索引]] (>>17) / [[整数]] (>>50) は、復号エラーとなることがあります。

[FIG(packet)[
:width:8

=1 [CODE[1]]
=7 索引
]FIG]

** リテラル表現

[20] [RUBYB[[[リテラル]]表現]@en[literal representation]]は、
名前と値の組により[[ヘッダー欄]]を表します。
名前は、[[文字列リテラル]]または[[索引]]により記述します。
値は、[[文字列リテラル]]により記述します。 [SRC[>>1]]

[21] 次の3通りがあります [SRC[>>1]]。 
[FIG(list)[
- [22] [[動的表]]の最初に新しい項目として[[ヘッダー欄]]を追加する (索引付けする) もの (>>26)
- [23] [[動的表]]に[[ヘッダー欄]]を追加しない (索引付けしない) もの
- [24] [[動的表]]に[[ヘッダー欄]]を追加しないもので、しかも常にリテラル表現とする
(絶対に索引付けしない) もの
]FIG]

[26] 索引付けを行うリテラル表現では、次のようにします [SRC[>>1]]。
[FIG(list)[
= [69] 最初の2ビットは [CODE[01]] とします。
= [66] [[ヘッダー欄]]の名前を[[索引]]によって表現するなら、
== [70] 次の6ビットを[[接頭辞]]とする[[整数]]によってその[[索引]]を示します。
= [67] [[ヘッダー欄]]の名前を文字列リテラルによって表現するなら、
== [72] 次の6ビットをすべて 0 とし、その後に名前を文字列リテラルとして指定します。
= [68] その後に[[ヘッダー欄]]の値を文字列リテラルとして指定します。
]FIG]

[FIG(packet)[
:width:16:

= 1 [CODE[0]]
= 1 [CODE[1]]
= 6 索引
= 24... 値
]FIG]

[FIG(packet)[
:width:16:

= 1 [CODE[0]]
= 1 [CODE[1]]
= 6 [CODE[0]]
= 8 名前
= 16... 値
]FIG]

[71] 索引付けを行わないリテラル表現では、次のようにします [SRC[>>1]]。
[FIG(list)[
= [74] 最初の3ビットは [CODE[000]] とします。
= [87] 次の1ビットは[[転送]]時に索引付けしても構わないなら [CODE[0]]、
絶対に索引付けしてはならないなら [CODE[1]] とします。
= [75] [[ヘッダー欄]]の名前を[[索引]]によって表現するなら、
== [76] 次の4ビットを[[接頭辞]]とする[[整数]]によってその[[索引]]を示します。
= [77] [[ヘッダー欄]]の名前を文字列リテラルによって表現するなら、
== [78] 次の4ビットをすべて 0 とし、その後に名前を文字列リテラルとして指定します。
= [79] その後に[[ヘッダー欄]]の値を文字列リテラルとして指定します。
]FIG]

[FIG(packet)[
:width:16:

= 1 [CODE[0]]
= 1 [CODE[0]]
= 1 [CODE[0]]
= 1 絶対
= 4 索引
= 24... 値
]FIG]

[FIG(packet)[
:width:16:

= 1 [CODE[0]]
= 1 [CODE[0]]
= 1 [CODE[0]]
= 1 絶対
= 4 [CODE[0]]
= 8 名前
= 16... 値
]FIG]

;; [116] [[索引]] (>>17) / [[文字列リテラル]] (>>56) は、復号エラーとなることがあります。

[86] [[中間器]]は、[[転送]]時に絶対に索引付けしない ([CODE[0001]]) 
表現で符号化された[[ヘッダー欄]]は、
絶対に索引付けしない表現のまま符号化しなければ[['''なりません''']] [SRC[>>1]]。

;; [25] これは[[圧縮]]することで危険になる[[ヘッダー欄]]を保護するためのものです
(>>97)。 [SRC[>>1]]

;; [131] [[ヘッダー名]]を索引により表現するかリテラルにより表現するかを変えて良いのかは定かではありません。

** 表現方法の選択

[148] どの表現方法を選択するかは実装に委ねられています。

[149] [[Firefox]] は、文字列を表す際に、常に[[ハフマン符号化]]するように見えます。
[TIME[2015-10-10T13:27:08.600Z]]

[151] [[Chrome]] は、文字列を表す際に、短ければそのまま、長ければ[[ハフマン符号化]]するように見えます。
[TIME[2015-10-10T13:29:06.300Z]]

[150] [[Firefox]] は、 [CODE[[[cookie]]]] [[ヘッダー]]を送るときに値が短ければ決して索引付けしないとし、
長ければ索引付けするように見えます。短いかどうかをどう判断しているのかはよくわかりません。
[CODE[[[:path]]]] [[ヘッダー]]は索引付けしないで送信するようです。
その他常に送信するヘッダーは、索引付けするようです。 [TIME[2015-10-10T13:28:47.500Z]]

[152] [[Chrome]] は、 [CODE[[[:path]]]] は索引付けしないとし、
それ以外で常に送信するヘッダーは索引付けするように見えます。 [TIME[2015-10-10T13:29:37.600Z]]

[153] [[Firefox]] は [CODE[[[authorization]]]] [[ヘッダー]]を決して索引付けしないとするようです。
[[Chrome]] は索引付けするようです。 [TIME[2015-10-10T13:36:56.400Z]]

* 表

[118] [[符号化器]]と[[復号器]]は、表を持ちます。

[119] [RUBYB[表]@en[table]]は、項目の順序を持った列です。[RUBYB[項目]@en[entry]]は、
[[索引]]によって識別され、[[ヘッダー欄]]の名前と値を表しています。

[120] 表は、[[静的表]]と[[動的表]]で構成されます。

[4] [DFN[[RUBYB[静的表]@en[static table]]]] [SRC[>>103]] は、
頻出[[ヘッダー欄]]を集めたものです。[[静的表]]は読み取り専用で、
あらゆる場面で共通であり、いつでも使うことができます。 [SRC[>>1]]

;; [135] [[静的表]]は、主要 [[Webサイト]]の用例と [[HTTP/2]] 
の[[疑似ヘッダー]]から作成されました [SRC[>>1]]。

;; [137] >>138 に [[JSON]] 形式の[[静的表]]ファイルがあります。
[REFS[
- [138] [CITE@en[data-web-defs/http-frames.txt at master · manakai/data-web-defs]] ([TIME[2015-05-28 00:33:14 +09:00]] 版) <https://github.com/manakai/data-web-defs/blob/master/doc/http-frames.txt>
]REFS]

[3] [DFN[[RUBYB[動的表]@en[dynamic table]]]]は、
特定の[[接続]]で重出する (かもしれない) [[ヘッダー欄]]を追加するものです。
初期状態は空ですが、[[符号化]]や[[復号]]により追加 (や削除) されてゆきます。 [SRC[>>1]]

[15] [[動的表]]は、重複する (同名同値の) 項目を含むことがあります。
[[復号器]]はこれをエラーとしては[['''なりません''']]。 [SRC[>>1]]

[40] [[動的表]]は、最大サイズを持ちます (>>10)。

[14] [[動的表]]は、[[接続]]ごとに異なるものを使用します [SRC[>>73]]。

[122] [[動的表]]は、双方向通信の[[符号化]] (送信/圧縮) 用と[[復号]] (受信/展開) 
用で異なるものを使用します [SRC[>>1, >>73]]。

;; [123] [[RFC 7541]] は、それぞれ[RUBYB[符号化文脈]@en[encoding context]]/[RUBYB[復号文脈]@en[decoding context]]
[SRC[>>1]] と言っています。
[[RFC 7540]] は(おそらく)同じものを[RUBYB[圧縮文脈]@en[compression context]]/[RUBYB[展開文脈]@en[decompression context]] [SRC[>>73]] と呼んでいます。

;; [12] ある[[エンドポイント]]の符号化用の[[動的表]]と、
[[peer]] の復号用の[[動的表]]が (遅延を別にすれば) 同じとなります。
逆も同様です。

[128] 実装によっては、より多くの情報を[[動的表]]の項目に付加し、[[符号化]]時に参照するかもしれません (>>93)。

** 索引

[17] 表中の項目は、[DFN[[RUBYB[索引]@en[index]]]]により参照されます。

[13] [[動的表]]と[[静的表]]は、どちらも共通の[RUBYB[索引番地空間]@en[index address space]]上にあります。
[CODE[[[1]]]] [[以上]]、[[静的表]]の大きさ[[以下]]の[[索引]]は、
[[静的表]]上の項目を表しています。それよりも大きな[[索引]]は、
[[動的表]]上の項目を表しています。 [SRC[>>1]]

[121] [[動的表]]は、 [[FIFO]] であり、最初・最新の項目ほど小さな索引を持ち、
最古の項目ほど大きな索引を持ちます。 [SRC[>>1]]

;; [126] つまり追加により[[動的表]]の項目の[[索引]]は変わっていきます。

;; [157] 索引付けする項目は索引への参照を含んでいることがあります。
参照を解決してから索引付けする必要があります。

[18] 両[[表]]の長さの[[和]]よりも大きな[[索引]]は、
復号エラーとしなければ[['''なりません''']] [SRC[>>1]]。

[65] [[索引]]表現における値 0 は、復号エラーとしなければ[['''なりません''']] [SRC[>>1]]。

;; [109] リテラル表現では、索引の値 0 は索引を持たない場合のために使われます。
索引として 0 が用いられることはありません。

[145] [[HTTP/2]] における[[動的表]]の理論上最大サイズは 2[SUP[32]]-1 です (>>144)。
ヘッダーは名前も値も空の時 (実際には [[HTTP/2]] では不可) 最小サイズ32 (>>35)
なので、[[動的表]]の最大項目数は 2[SUP[27]]。これに[[静的表]]の個数を加えたのが[[索引]]の最大値です。
従って24ビット整数では表せませんが、32ビット整数なら十分です。

** サイズ制限

[34] [[動的表]]の[DFN[[RUBYB[サイズ]@en[size]]]]は、各項目のサイズの[[和]]です。 [SRC[>>1]]

[35] 項目の[DFN[[RUBYB[サイズ]@en[size]]]]は、名前のオクテット長と値のオクテット長と
32 の[[和]]です。 [SRC[>>1]]

;; [36] [[Huffman符号化]]は適用しない状態の長さです。 [SRC[>>1]]

[10] [[動的表]]には、[[メモリー]]消費の制約ということで最大サイズが設けられています。
[[符号化器]]は、[[動的表]]サイズを最大サイズ[[以下]]としなければ[['''なりません''']]
[SRC[>>1]]。[[復号器]]は、[[動的表]]サイズを最大サイズより大きくすることはありません。

*** 動的表サイズ更新

[16] 最大サイズに達すると削除 ([[eviction]]、>>44) が発生するため、
古い項目は[[符号化]]や[[復号]]に使えなくなります。そのため、
[[符号化器]]が最大サイズを変更したら[[復号器]]に通知する必要があります。
[[動的表]]の最大サイズの変更は、[DFN[[RUBYB[動的表サイズ更新]@en[dynamic table size update]]]]により通知されます。
[[動的表サイズ更新]]は、変更の後の最初の[[ヘッダーブロック]]の最初になければ[['''なりません''']]。 [SRC[>>1]]

;; [38] 最初以外に[[動的表サイズ更新]]が出現したらどうするべきなのかは不明です。
復号エラーでしょうか。

[41] [[HTTP/2]] では、変更の後とは[[設定]]の [[acknowledgment]] の後に当たります。 [SRC[>>1]]

[42] 2つの[[ヘッダーブロック]]の転送の間に最大サイズの変更が複数回行われた時は、
最小サイズを[[動的表サイズ更新]]で通知しなければ[['''なりません''']]。
更に最後のサイズも[[動的表サイズ更新]]で通知しなければなりません。
[[動的表サイズ更新]]は、この高々2つとなります。 [SRC[>>1]]

;; [39] 何度変更されたとしても、最小と最後の変更 (と [[eviction]]) さえ実行すれば、
両者の[[動的表]]と最大サイズは同じ状態になります。

;; [127] 2個以上受信した時どう処理するべきかは不明です。

[147] [[Firefox]] も [[Chrome]] も、[[動的表サイズ更新]]がどの位置に何個あってもエラーとはしないようです。
[TIME[2015-10-10T13:13:27.300Z]]

[EG[
[43] 0 を設定し、元に戻すことで、[[動的表]]の内容を消去できます。 [SRC[>>1]]
]EG]

[80] [[動的表サイズ更新]]は、先頭の3ビットが [CODE[001]] で、
その後5ビットを[[接頭辞]]とする[[整数]]によって新しい最大サイズを表します [SRC[>>1]]。

[FIG(packet)[
:width:16

= 1 [CODE[0]]
= 1 [CODE[0]]
= 0 [CODE[1]]
= 5 最大サイズ
]FIG]

*** 設定 [CODE[SETTINGS_HEADER_TABLE_SIZE]]

[81] [[動的表]]の新しい最大サイズは、プロトコルによる上限[[以下]]でなければ[['''なりません''']] [SRC[>>1]]。

[82] [[HTTP/2]] では、復号側から受信し、符号化側が [[acknowledge]] した最新の[[設定]] 
[CODE[[[SETTINGS_HEADER_TABLE_SIZE]]]] の値がこの上限です。 [SRC[>>1]]

[83] 上限を超える値は、復号エラーとしなければ[['''なりません''']]。 [SRC[>>1]]

[124] 最大値の初期値はプロトコルの上限と思われます。

[132] [[HTTP/2]] の[[設定]] [DFN[[CODE[[[SETTINGS_HEADER_TABLE_SIZE]]]]]] ([CODE[[[0x1]]]])
は、[[受信者]]側が[[ヘッダーブロック]]の[[復号]]に使う表の最大サイズを[[バイト]]単位で[[送信者]]側に通知するものです。 [SRC[>>84]]

[134] [[符号化器]]は、本[[設定]]で指定された値[[以下]]のサイズの表を使うことができます。 [SRC[>>84]]

[133] 本[[設定]]の初期値は、 4096 [[バイト]]です。 [SRC[>>84]]

[144] [[HPACK]] の[[索引]]は [[HPACK]] [[整数]]なので、任意の大きさの値を使えます。
つまり [[HPACK]] として動的表のサイズはいくらでも大きくできます。
[[HTTP/2]] の [CODE[[[SETTINGS]]]] [[フレーム]]により指定できる値の最大値は
2[SUP[32]]-1 で、本[[設定]]固有の上限は設けられていないようです。

*** eviction

[44] 最大サイズが減少した時は、[[動的表]]のサイズが最大サイズ[[以下]]となるまで、
末尾から順に削除 ([[eviction]]) します。 [SRC[>>1]]

[45] 新しい項目を追加する前には、[[動的表]]に新しい項目のサイズを足しても最大サイズ[[以下]]となるか、
[[表]]が空となるまで、末尾から順に削除 ([[eviction]]) します。 [SRC[>>1]]

[46] 新しい項目が最大サイズ[[以下]]なら、[[動的表]]に追加します。 [SRC[>>1]]

;; [47] 最大サイズを超えるなら、[[動的表]]が空になります。これはエラーではありません。
[SRC[>>1]]

[48] なお、新しい項目は、それによって[[動的表]]から削除される項目の名前を参照していることがありますから、
注意が必要です。 [SRC[>>1]]

* 文字列リテラル (オクテット列)

[56] [RUBYB[オクテット列]@en[string of octets]]は、[[ヘッダー欄]]の名前や値に使います。
[SRC[>>1]]

;; [112] [[RFC]] では「literal」、「string literal」、「string of octets」
など表記が一定しませんが、いずれにせよ、[[バイト列]]を表すものです。

[57] 文字列リテラルは、オクテット列として直接、
または[[Huffman符号]]により符号化します。 [SRC[>>1]]

[58] 文字列リテラルは、次の欄で構成されます。 [SRC[>>1]]
[FIG(list members)[
:[59] H: [[Huffman符号化]]されているかどうかを示す1ビットのフラグです。
[CODE[0]] は、[[Huffman符号化]]されていない生のオクテット列を表します。
[CODE[1]] は、[[Huffman符号化]]されたオクテット列を表します。 [SRC[>>1]]
:[60] [RUBYB[文字列長]@en[String Length]]:文字列リテラルを符号化するために使ったオクテット数を7ビット[[接頭辞]]の[[整数]] (>>50) として[[符号化]]したものです。 [SRC[>>1]]
:[61] [RUBYB[文字列データ]@en[String Data]]:
文字列リテラルの符号化されたデータです。生のオクテット列または [[Huffman符号化]]されたオクテット列です。 [SRC[>>1]]
]FIG]

[FIG(packet)[
:width:16

=1 H
=7 文字列長
=24... 文字列データ
]FIG]

;; [117] [[整数]] (>>50) は、復号エラーとなることがあります。

[146] 理論上は[[整数]]が任意の長さを扱えますから、文字列も任意の長さとできます。
実際にはメモリー容量や実装の都合などの制約を受けます。例えば 2[SUP[32]]-1
や 2[SUP[31]]-1 が文字列長の上限かもしれません。

** ハフマン符号化

[62] [[Huffman符号化]]の場合、 [[RFC 7541]] の表 [SRC[>>105]] 
による[[Huffman符号]]によって[[符号化]]し、
各オクテットに対応する[[符号]]をビット単位で順に連結したものです。 [SRC[>>1]]

[108] この符号化の表 [SRC[>>105]] は、0-255 の[[オクテット]]と 256 = [[EOS]] (end-of-string)
の257種類の[RUBYB[[[記号]]]@en[symbol]]に対して、5-30ビットの[[符号]]を割り当てるものです。
[SRC[>>1]]

;; [106] この符号化の表は、大量の [[HTTPヘッダー]]の標本から統計的に得られたものです。
これは[RUBYB[[[正準Huffman符号]]]@en[canonical Huffman code]]でどの[[記号]]も異なる長さを持つようにしたものです。
[SRC[>>1]]

;; [140] >>139/>>141 に [[JSON]] 形式の符号化表ファイルがあります。
[REFS[
- [139] [CITE@en[data-web-defs/http-frames.txt at master · manakai/data-web-defs]] ([TIME[2015-05-28 00:33:14 +09:00]] 版) <https://github.com/manakai/data-web-defs/blob/master/doc/http-frames.txt>
- [141] [CITE@en[data-web-defs/http-frames.json at master · manakai/data-web-defs]] ([TIME[2015-09-27 16:27:05 +09:00]] 版) <https://github.com/manakai/data-web-defs/blob/master/data/http-frames.json>
]REFS]

[107] [[オクテット]]境界でちょうど終わらない場合、次の[[オクテット]]境界まで[RUBYB[詰め]@en[padding]]を挿入します。
文字列リテラルの一部と誤解されないよう、 [[EOS]] (end-of-string)
記号に対応する[[符号]]の最上位側数ビットを使います。 [SRC[>>1]]

[63] [[Huffman符号化]]の[[復号]]時には、末尾の不完全な[[符号]]は、
詰めとみなして捨てます。8ビット[[以上]]の詰めは、復号エラーとしなければ[['''なりません''']]。
[[EOS]] 記号に対応する[[符号]]の最上位側数ビットでない詰めも、
復号エラーとしなければ[['''なりません''']]。
[[EOS]] 記号を含む文字列リテラルは、[[復号エラー]]としなければ[['''なりません''']]。 [SRC[>>1]]

;; [102] [[静的Huffman符号]]の符号化表を使うことで情報漏洩が生じるものの、
攻撃者がこの情報漏洩から意味のある情報を得ることはできないとの研究があります。
その他現時点で[[静的Huffman符号]]への攻撃は知られていません。 [SRC[>>1]]

* 整数

[50] [RUBYB[[[整数]]]@en[integer]]は、[[索引]]と文字列長に使います。 [SRC[>>1]]

[51] 整数は、[[オクテット]]中のどこからでもはじめられますが、
必ず[[オクテット]]の最後で終わります。 [SRC[>>1]]

[52] 整数は、開始[[オクテット]]のうちの開始以後の部分である[RUBYB[接頭辞]@en[prefix]]と、
その後の0個[[以上]]の[[オクテット]]の列で構成されます。
[[接頭辞]]のビット数 [VAR[N]] は、文脈によります。
[[接頭辞]]の後に続く[[オクテット]]は、 [[MSB]] が1のものが続いた後、
[[MSB]] が0のもので終わります。 [SRC[>>1]]

[FIG(railroad)[
= |
== [[接頭辞]] (0 を含む)
== =
=== [[接頭辞]] (すべて1)
=== *
==== [[MSB]]=1
=== [[MSB]]=0
]FIG]

[55] [[整数]]の[[符号化]]は、次のように行います [SRC[>>1]]。
[FIG(steps)[
= 値が 2[SUP[[VAR[N]]]]-1 [[未満]]なら、
== [VAR[N]] ビットで表現したものを、[[接頭辞]]として出力します。
= それ以外なら、
== [VAR[N]] 個の 1 のビットの列を、[[接頭辞]]として出力します。
== 値から 2[SUP[[VAR[N]]]]-1 を引きます。
== 値が 128 [[以上]]である間、繰り返し実行します。
=== 値を128で割った[[余り]]を下位7ビットで符号化し、
[[MSB]] を1とした[[オクテット]]を出力します。
=== 値を128で割ります。
== 値を符号化した[[オクテット]]を出力します。
]FIG]

[53] [[整数]]の[[復号]]は、次のように行います [SRC[>>1]]。
[FIG(steps)[
= [VAR[結果]]を、[[接頭辞]] [VAR[N]] ビットを復号した値に設定します。
= [VAR[結果]]が 2[SUP[[VAR[N]]]]-1 なら、
== [VAR[M]] を、0に設定します。
== 次の処理を実行します。
=== [VAR[B]] を、次の[[オクテット]]に設定します。
=== [VAR[結果]]に、 [VAR[B]] の下位7ビットの値に 2[SUP[[VAR[M]]]] を掛けた値を足します。
=== [VAR[M]] に、7 を足します。
=== [VAR[B]] の [[MSB]] が 1 なら、繰り返します。そうでなければ、次に進みます。
= [VAR[結果]]を返します。
]FIG]

[54] 本方式の[[整数]]は無限に大きな値を表現できますし、
無駄に多くの[[オクテット]]を消費したり、[[桁溢れ]]させたりするかもしれません。
実装は (使われる文脈毎に適当な) 値やオクテット長の制限を超えたら、
復号エラーとしなければ[['''なりません''']]。 [SRC[>>1]]

* 復号エラー

[130] [[HTTP/2]] [[ヘッダーブロック]]の[[復号エラー]]は、[[接続エラー]]
[CODE[[[COMPRESSION_ERROR]]]] としなければ[['''なりません''']] [SRC[>>73]]。

[91] [[誤り符号]] [DFN[[CODE[[[COMPRESSION_ERROR]]]]]] ([CODE[[[0x9]]]])
は、[[接続]]の[[ヘッダー]]圧縮文脈を維持できないことを示します [SRC[>>90]]。

[103] [[ヘッダー圧縮]]は、無駄な処理をさせるために濫用できます。
[[エンドポイント]]は利用状況を監視して制限する[['''べきです''']]。
[[接続エラー]] [CODE[[[ENHANCE_YOUR_CALM]]]] としても構いません。 [SRC[>>104]]

;; [136] [[ヘッダーリスト]]も参照。

[142] [[Chrome]] は仕様通りのようです。 [[Firefox]] は[[ストリームエラー]]
[CODE[[[PROTOCOL_ERROR]]]] とするようです。 [TIME[2015-10-03T15:16:58.500Z]]

[143] [[Chrome]] も [[Firefox]] も、 [CODE[[[HEADERS]]]] の復号エラーを検出するより前に次の
[CODE[[[CONTINUATION]]]] のエラーを検査するように見えます。 [TIME[2015-10-03T15:17:59.100Z]]

* セキュリティー

[85] [[CRIME攻撃]]のように ([[暗号化]]されていても) 長さから内容を推定することを難しくするため、
[[HPACK]] では個々の文字レベルではなく、文字列全体を表に入れて参照する形を採っています。
しかしこれで完全に攻撃を防げるわけではありませんから、注意が必要なことには変わりありません。
[SRC[>>1]]

[88] 機密データと攻撃者が何らかの形で注入可能なデータが同じ表に基づき[[圧縮]]されると、
攻撃が可能となります。同一の[[要求]]や[[応答]]に含まれる[[ヘッダー]]の場合はもちろん、
[[HTTP/2]] では同一の[[接続]]で同じ表を使いますから、
同じ[[接続]]を共有する他の[[要求]]や[[応答]]でさえあれば、
攻撃できてしまうかもしれません。

[EG[
[89] 例えば、次のような状況が存在し得ます。
[FIG(list)[
- [90] [[中間器]]が複数の[[クライアント]]からの[[要求]]を同じ[[起源サーバー]]への単一の[[接続]]で送信する状況 [SRC[>>1]]
- [91] [[中間器]]が複数の[[起源サーバー]]からの[[応答]]を同じ[[クライアント]]への単一の[[接続]]で送信する状況 [SRC[>>1]]
- [92] [[Webブラウザー]]が複数の[[起源]]からの[[要求]]を同じ[[起源サーバー]]への単一の[[接続]]で送信する状況 [SRC[>>1]]
]FIG]
]EG]

[93] 複数の出自を持つ[[ヘッダー]]を圧縮する場合は、[[動的表]]の項目を出自ごとに分け、
他の項目は使わないようにするのが理想的です。 [SRC[>>1]]

[94] 圧縮性能の向上のため、一部は共通にしても構いません [SRC[>>1]]。

[EG[
[95] 例えば [[Webブラウザー]]は [CODE(HTTP)@en[[[Accept-Encoding]]]] の値を共通にできます
[SRC[>>1]]。[[Webブラウザー]]が [CODE(HTTP)@en[[[Accept-Encoding]]]] [[ヘッダー]]に指定する値はどの[[起源]]でも同じでしょうから、
おそらく問題とならないはずです。
]EG]

[96] 同じ[[ヘッダー]]に多数の異なる値を指定して推定を試みているらしき状況なら、
それ以後その[[ヘッダー]]で[[動的表]]を用いないようにしても良さそうです。
(ただ単に[[動的表]]から削除するだけでは、別の方法で簡単に再追加されてしまうかもしれませんから、
不十分です。) 値が短い場合ほど、すぐに[[動的表]]を使わないようにするべきかもしれません。 [SRC[>>1]]

[97] また、繊細なヘッダーは決して索引付けしないことにする (常に文字列リテラルで表現する)
ことにもできます。 [SRC[>>1]]
[FIG(list)[
- [98] 短い値は攻撃に弱いので、索引付けしないことにするといいかもしれません。
- [99] [CODE(HTTP)@en[[[Cookie]]]] や [CODE(HTTP)@en[[[Authorization]]]]
のように繊細な情報を含むヘッダーは、索引付けしないことにするといいかもしれません。
]FIG]

[100] 逆に、分かったとしてもあまり価値が無いヘッダーは、索引付けした方がいいかもしれません。
[SRC[>>1]]

[EG[
[101] 例えば [CODE(HTTP)@en[[[User-Agent]]]] はどの[[サーバー]]にも同じ値で送信するのが普通なので、
その値を特定しようとする企てにはさして意味がありませんから、索引付けしても良さそうです。[SRC[>>1]]
]EG]

[112] [[HTTP/2]] [[ヘッダーリスト]]の機密データと攻撃者が制御できるデータを (同じ圧縮辞書で)
一緒に[[圧縮]]しては[['''なりません''']]。データの出所が不明なら、
[[圧縮]]しては[['''なりません''']]。 [SRC[>>111]]

* 歴史

[154] [CITE@en[1025071 – Don't index :path field in hpack]]
([TIME[2015-10-10 22:40:16 +09:00]] 版)
<https://bugzilla.mozilla.org/show_bug.cgi?id=1025071>

[155] [CITE[Issue 524041 - chromium - Reject line-folding in HPACK - An open-source project to help move the web forward. - Google Project Hosting]]
([TIME[2015-10-10 22:43:04 +09:00]] 版)
<https://code.google.com/p/chromium/issues/detail?id=524041>

[FIG(amazon)[
HTTP/2
]FIG]