QUICのヘッダ圧縮QPACKとは

20170209追記背景と基本的なコンセプトは変わりませんが、新しくでた draft02ではこの記事と多少細部が異なっております。
https://tools.ietf.org/html/draft-bishop-quic-http-and-qpack-02


20170806 別のヘッダ圧縮方式QCRAMについて別途記事を書きました
asnokaze.hatenablog.com


QUICのヘッダ圧縮であるQPACKについて


現在のQUICの策定中仕様の一つである「Hypertext Transfer Protocol (HTTP) over QUIC」では、QUICでもHTTPヘッダの圧縮にHPACKを使用することになっていますが、新しくQUIC用のヘッダ圧縮方式として「HTTP over QUIC - Mapping and Header Compression」という仕様でQPACKなるものが提案されています。

背景 QUICとHPACK

HTTP/2ではヘッダ圧縮方式としてHPACKと呼ばれる方式を利用します。HPACKはRFC 7541「HPACK: Header Compression for HTTP/2」で標準化されています。


HPACKはヘッダ名とヘッダ値をハフマン符号化するほか、静的テーブル・動的テーブル呼ばれる辞書を用いてHTTPヘッダを表現します。特に、一つのコネクションで一度使用したHTTPヘッダは動的テーブルに追加され、次のHTTPリクエスト・レスポンスではindex(1バイト)でそのヘッダ名・ヘッダ値を表現することができます。


QUICでもこのHPACKを使用するのですが、困ったことがあります。前提として、QUICはUDPを使用することで、HTTP2 over TCPの時にあったTCPレイヤのヘッドオブラインブロッキングを克服しています。


どういうことかというと、TCPではパケットの欠損や順番の入れ替わりがあるとそれらが解決出来るまで、すでに届いてるパケット(HTTPリクエスト・HTTPレスポンス)を処理することができません。欠損しているパケットの後続すべてが待たされてしまいます。


QUICではUDPを使用しており、一つのパケットロスなどにより他の処理がブロックされることはないようになっております。QUICが持つストリーム毎にパケットロスの回復や順番の並び替えを行うため、一つのストリームでのパケットロスは他のストリームに影響しません。


しかし、HPACKの動的ヘッダテーブルはテーブルへの挿入・エビクションが発生するため結局は順番通りにしか処理できないという欠点があります。つまり、結局パケットロスにより後続の処理はブロクッされてしまいます。


そこでHPACKが持つヘッドオブラインブロッキングを解消するために、QPACKが提案されているということになります。

QPACK

QPACKでは上記のような、HPACKが持つヘッドオブラインブロッキング問題を解決するために、動的テーブルの扱いに大きな変更が入っています。HPACKでは、動的テーブルへの追加する際にインデックス番号がずれていく他、追加・エビクションの際のインデックス番号は暗黙的でした。


QPACKの動的テーブルは下記のような特徴を持っています。

  • 追加する際はインデックス番号を明示する(静的テーブルの一番大きいインデックス番号より大きいこと)
  • エビクションは発生しない(テーブル上限を超えるとFatal error)
  • すでに使用されているインデックス番号は、明示的に削除するまで変更できない
  • 明示的に削除するDeletionを使用する
  • そのインデックスを参照するフレームをすべて処理し終わったら、QPACK-ACKフレームを送信して削除できるようになる


このようにして、一度追加されたインデックスはお互いにもう使用しないことが確認でき削除されるまで変更されないようになっています。


もちろんQUICのパケットは入れ替わるため、インデックスへ追加するパケットとインデックスを参照するパケットの順番が入れ替わることがあります。その時はインデックスへ追加するパケットが到着するまで待つことになります。


これを避けるために、QPACKは同じインデックス番号に同じ値を追加する命令は何度でも行って良いことになっており、複数のHTTPリクエストにおいてインデックスの追加命令を行うことで、待つパケットを減らすことができます(ただし、圧縮効率は落ちます)。また、部分的なHeader Block Fragmentsが受信できた際は処理をすすめることができ、この部分にテーブルへの追加がアレば実行して良いことになっています。


削除もお互いに合意できて(QPACK-ACKを受け取って)やっと削除ができます。つまり、そのインデックスを参照するパケットをすべて処理し終わったタイミングです。テーブルサイズの上限まで使用してると、なかなか合意が得られず削除できないこともあるので、仕様では上限になる前に送信側が削除を試みることを推奨してます。


その他仕様的な話

上記での説明と重複もあるが、以下のことについて書かれている

  • 静的テーブルや、文字列リテラル表現、動的ヘッダテーブルを更新しないヘッダ表現は変更はない
  • Literal Header Field with Incremental Indexingがなくなり、Literal Header Field with Indexingが新しく定義され、明示的にインデックス番号を指定するようになる
  • Dynamic Table Size Updateがなくなり、明示的に削除を行うDeletionになる
  • テーブルサイズ管理
  • 動的テーブルの伸長・縮小についての変更
  • この仕様によりQUICのHEADERSフレーム・PUSH_PROMISEフレームのシーケンス番号が不要になるので削除
  • お互いに削除出来ることが確認できるまでの4つの段階の定義
  • Performance Considerations, Security Considerations

翻訳

翻訳というのには気が引けるが


一応ざっと目を通したので、QPACKの翻訳をひっそり置いておく。
(読み返したら微妙だったので、後日直します....ます....)


https://github.com/flano-yuki/my-quic-spec-translation/blob/master/draft-bishop-quic-http-and-qpack/draft-bishop-quic-http-and-qpack-01.md