-
Notifications
You must be signed in to change notification settings - Fork 9
HTTP2.0 最速実装法
http2 を実装するということは。
- hpack を実装する
- http2 client を実装する
- http2 server を実装する
ということです。
これらを全てキチンとやると結構たいへんなので、 「なるべく手を抜いて」簡単に実装することで、最速で実装する方法について記します。
(このドキュメントは、 https://gist.github.com/tatsuhiro-t/7397929 を元にする) HPACK は以下の要素を使って、ヘッダをなるべく小さく圧縮します。
- Huffman Coding
- Header Field Representation
これらを組み合わせる方法はいくつかあります。 それぞれで一番楽をし、それを naive と言いいます。(http://mew.org/~kazu/material/2014-hpack.pdf P11)
Huffman Coding は、HTTP2 仕様に定義される静的な Huffman Table を用いてヘッダーフィールドの文字列をハフマン符号化します。送信側がハフマン符号化をするかしないかを決めることができるので、実装する必要はありません。
一度送信したヘッダをテーブルに保存することで、再利用するために使用します。 ヘッダを全て String Literal without Indexing でエンコードして送信しテーブルに保存しないようにすると、テーブルを実装する必要はありません。
HPACK では naive 実装、つまり Huffman を使わず String Literal without Indexing でエンコーディングすることで、かなり楽に実装できます。
しかし、これらは自分がエンコードに使っていなかったとしても、接続した先が使用していた場合は、それをデコードするために対応する必要がでてしまいます。
そこで、接続テストをする場合は同じく naive に対応したものを使いましょう。まずはそこからです。
サーバは色々大変なので、クライアントから先に実装することを勧めます。 実際の流れは、 [https://jxck.io/labs/http2cat/] ここを見ながらやりましょう。 テストは、 naive で実装されているサーバから小さいデータを GET するところまでやりましょう。
HTTP2 は現在以下のハンドシェイク方法があります。
- https で NPN を用いる
- https で ALPN を用いる
- http で Upgrade を用いる
- http で Atl-Svc を用いる
- http で事前知識を用いる
HTTP2 においては、本来 NPN は非公式であり ALPN が正式採用になる予定ですが、この原稿執筆時点では ALPN の普及が進んでいないため、多くの HTTP2 実装では SPDY の流れを受けて引き続き NPN を利用および提供しています。 これらは、相手が HTTP2 に対応しているかを調べる方法なので、もし相手が対応していることが予めわかっていれば、いきなり HTTP2 の通信をしてもいいとされています。
よって、面倒なことは省いて、事前知識を用いていきなり Magic Octet を送るところから始めましょう。
Magic Octet とは, 24 バイトのバイナリデータで 16 進表記では以下のようになります(文字列表現: "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
):
505249202a20485454502f322e300d0a0d0a534d0d0a0d0a
もちろん、相手も Magic Octet を最初に受け取って HTTP2 通信を開始する方法に対応している必要があります。
- SETTINGS を最初に送り、 ACK を受け取ります。(StreamId=0)
- SETTINGS を受け取り、同じく ACK を返します。(StreamID=0)
- HEADERS を使って GET を送ります。(StreamId=1)
- flags は 0x5(END_STREAM | END_HEADERS)を立てます。
- ペイロードは
Header Block Fragment
のみ。中身はHPACK(naive)に合わせます。
- HEADERS と DATA を使ってレスポンスを受け取ります。(StreamId=1)
- HEADERS は HPACK でフル圧縮してくる可能性があります。最悪読み飛ばしましょう。
- 受信する HEADERS または DATA の END_STREAM フラグを見た後, GOAWAY を最後に送って終わり。(StreamId=0)
ということで、他のフレームは無視して、最小の GET を成功させましょう。 受け取った SETTIGNS は無視してかまいません。
テストは、 naive で実装されたクライアントからの GET に小さいデータをレスポンスするところからやりましょう。
いきなり magic octet が来る前提で作ります。
- SETTINGS を最初受け取り、返します。(StreamId=0)
- HEADERS を受け取ります。(StreamId=1)
- HPACK でフル圧縮してくる可能性があります。最悪読み飛ばしましょう。
- HEADERS と DATA を使ってレスポンスを送ります。(StreamId=1)
- HEADERS の flags は 0x4(END_HEADERS)を立てます。
- HEADERS のペイロードは
Header Block Fragment
のみ。中身はHPACK(naive)に合わせます。 - DATA の flags は 0x1(END_STREAM) を立てます。
- 受信する HEADERS の END_STREAM フラグを見た後, GOAWAY を最後に受け取って終わり。(StreamId=0)
SETTINGS は受け取ったら無視して構いません。
- HPACK に Huffman を実装
- HPACK で HeaderTable, StaticTable 対応
- ALPN 対応
- Uprade 対応
- Alt-Svc 対応
- Stream 実装
- Flow Control 実装
- PING
- PUSH PROMISE
- etc, etc, etc