これは、http2 Advent Calendar 2016の5日目の記事です。
20161208 今回はtoy serverを使用しましたが、103 EarlyHintsを送信できるNginxモジュールを書きました
103 EarlyHintsを送信するNginxモジュール書いた
103 Early Hints
103 Early Hintsは、@kazuho氏によって提案されている仕様です。すでに Individual-Draftが提出されています。
https://tools.ietf.org/html/draft-kazuho-early-hints-status-code-00
IETF97でも仕様についての議論が行われており、その際のスライドを見ると分かりやすいかと思います
https://www.ietf.org/proceedings/97/slides/slides-97-httpbis-sessb-early-hints-00.pdf
簡単に言うと、HTTPレスポンスは一般的にコンテンツの生成が終わってからステータスコードが決定します。そのため、特定のHTTPレスポンスヘッダをまず返したい!ということは出来ません。
そこで、informationalステータスコードである100番代の "103" を使用することでHTTPレスポンスヘッダをコンテンツの生成が終わる前にクライアントに通知することが出来ます。一般的に馴染みのない 100番代のステータスコードですが、少々特殊でこのHTTPレスポンスのみ連続で送信する事ができます。但し、正しく実装されていないクライアント・サーバもあるので注意が必要であり、仕様としては議論が続くところかと思います。
IETF97の発表資料の書かれている通り、ユースケースとしてLinkヘッダでPreloadを先行してレスポンスすることで、クライアントは早いタイミングでそのリソースを取得しようとします。
また、プロキシがバックエンドWebサーバより103を受け取った場合、クライアントとHTTP/2で通信していれば サーバプッシュを使用しリソースをプッシュすることもできます。
すでに、h2oやnghttp2で対応されているようです
Apache mod_http2 で試す
mod_http2(nghttp2)でざっと試す
- Ubuntu 16.04
- Apache2.4 (Revision 1772437)
- nghttp2 (commit 85ba33c08f46)
- Openssl 1.0.2g
特殊なことはないが、nghttp2をインストールしてから、Apache2.4をsvnからチェックアウトしビルドする。その後、http2の有効化およびリバースプロキシの設定を入れる。
# https://github.com/nghttp2/nghttp2 にそって、インストールしておく sudo apt-get install subversion build-essential autoconf libxml2 libxml2-dev libtool libtool-bin libpcre3-dev svn checkout http://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x httpd-2.4.x cd ./httpd-2.4.x/ ./buildconf ./configure --enable-http2 --with-libxml2 make sudo make install
proxy
ざっと、ココらへんの設定を入れる。(フロントはhttp2)
#h2の設定 Protocols h2 http/1.1 ProtocolsHonorOrder On #H2EarlyHints on #proxy ProxyStatus On ProxyPreserveHost On ProxyPass / balancer://test <Proxy balancer://test> BalancerMember http://127.0.0.1:8080 loadfactor=10 </Proxy>
バックエンド
status 103を返すバックエンドサーバを簡易的に準備。
Linkヘッダでpreloadを指定します。nghttp2は103のpreloadヘッダを解釈し、HTTP/2 Server Pushをしてくれます。
vagrant@vagrant:~$ cat ./res HTTP/1.1 103 Link: </hoge.css>;rel=preload HTTP/1.1 200 OK Date: Sun, 04 Dec 2016 00:00:00 GMT helloworld vagrant@vagrant:~$ while true; do ( cat ./res ) | nc -l 8080 >/dev/null; [ $? != 0 ] && break; done &
試してみる
vagrant@vagrant:~$ nghttp https://localhost -vn --no-dep|lv |grep -i frame ... [ 0.011] send HEADERS frame <length=33, flags=0x05, stream_id=1> [ 0.013] recv SETTINGS frame <length=0, flags=0x01, stream_id=0> [ 0.015] recv PUSH_PROMISE frame <length=53, flags=0x04, stream_id=1> [ 0.015] recv HEADERS frame <length=57, flags=0x04, stream_id=1> [ 0.016] recv DATA frame <length=14, flags=0x01, stream_id=1> [ 0.016] recv HEADERS frame <length=55, flags=0x04, stream_id=2> [ 0.016] recv DATA frame <length=528, flags=0x01, stream_id=2> [ 0.016] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
ちゃんと、103のLinkヘッダを読んで、Proxy側からPUSH_PROMISEが送信されていることが確認できる。
(今回のtoy serverは103と200を同時に返しているの意味は薄いが)
また、mod_http2の設定で「H2EarlyHints」をon にするとクライアント側に103が伝達される
vagrant@vagrant:~$ nghttp https://localhost -vn --no-dep|lv |grep -i -e frame -e status ... [ 0.013] send HEADERS frame <length=33, flags=0x05, stream_id=1> [ 0.014] recv SETTINGS frame <length=0, flags=0x01, stream_id=0> [ 0.017] recv PUSH_PROMISE frame <length=53, flags=0x04, stream_id=1> [ 0.017] recv (stream_id=1) :status: 103 [ 0.017] recv HEADERS frame <length=41, flags=0x04, stream_id=1> [ 0.017] recv (stream_id=1) :status: 200 [ 0.017] recv HEADERS frame <length=57, flags=0x04, stream_id=1> [ 0.017] recv DATA frame <length=14, flags=0x01, stream_id=1> ...