既存のHTTPサーバーをHTTP/3化する手法を使ってPiping ServerをHTTP/3対応する
実現したいこと
既に立ち上がっているHTTPサーバーをHTTP/3対応する。
大きな流れ
大きな流れは、あらかじめ立っているHTTP/1.1のlocalhost:80をリバースプロキシを使ってlocalhost:8443でHTTP/3を受け付けられるようにすること。以下の図の通り必要に応じてHTTP/2やHTTP/1.1にフォールバックさせる。
https://gyazo.com/59296c26ed2f6288e9a9dac0972d5b39
ゴールは以下のようにHTTP/3で通信できたことを表すh3がDevToolsのネットワークで確認できること。
https://gyazo.com/d2804b5ca342e0b85f04565bec5a8fef
nghttpx
localhost:80
code:bash
# Dockerを使う方法
docker run --rm -p 80:80 nwtgck/piping-server:v1.12.3 --http-port=80
code:bash
# ワンバイナリを使う方法
sudo ./piping-server-pkg-linuxstatic-x64/piping-server --http-port=80
方法
docker runの方法は以下に記載されている。
以下がHTTP/3対応できた実際のコマンド。このコマンドが全て。Dockerイメージのnwtgck/nghttp2-for-http3は上記のDockerfileをビルドしたもの。nwtgck/nghttp2-for-http3は公開していて誰でもdocker pull可能。 追記: nwtgck/nghttp2()としてnghttp2の更新に追従してメンテナンスできるイメージを作成した。 code:bash
docker run --rm -it --net=host --privileged \
-v /etc/letsencrypt:/etc/letsencrypt:ro nwtgck/nghttp2-for-http3 \
nghttpx \
--npn-list=h3,h2,h2-16,h2-14,http/1.1 \
--add-response-header='alt-svc: h3=":8443"; ma=3600, h3-29=":8443"; ma=3600'\
/etc/letsencrypt/live/ex.nwtgck.org/privkey.pem /etc/letsencrypt/live/ex.nwtgck.org/fullchain.pem \
-f '*,8443' -f '*,8443;quic' -b localhost,80 \
--rlimit-memlock 524288
Dockerのオプションについて
公式READMEにも書かれている通り--privilegedは必要。省くと「Failed to load bpf object file: Operation not permitted」とエラーが出て終了する。
nghttpxのオプションについて
--npn-list=...はALPNの指定。なるべくクライアントがHTTP/3を話すようににalt-svcヘッダを指定している。HTTP/3をにするということは必然的にHTTPSが必要。今回はLet's Encryptを利用して/etc/letsencrypt/live/ex.nwtgck.org/privkey.pemと/etc/letsencrypt/live/ex.nwtgck.org/fullchain.pemを作成している。 -f '*,8443' -f '*,8443;quic' -b localhost,80の;quicをつけることでHTTP/3対応できる。-fはリバースプロキシとしてのfrontendの-bはbackendを示している。 HTTP/3通信の確認
初回アクセス時もHTTP/3でアクセスするためにCloudflareのDNSに以下のようにHTTPSレコードを入れている。 https://gyazo.com/7ab4df3a3ef49d0576154eb3f1f5317c
以下はFirefoxを使って初回のアクセスでHTTP/3ができている様子。具体的にはFirefoxで新しいプロファイルを作ってDoHの設定とNetworkタブのProtocolの表示をONにした後に初回のアクセスをした様子。 https://gyazo.com/f1299ed70f5183a9e0c713bf1cfe1f2b
次はChrome。キャッシュを避けるためゲストモードで開く。
https://gyazo.com/527855fc8901cd51df0e63fe2fa7b48a
最初だけHTTP/2になるのはFirefoxはHTTPSレコードに対応しているが、Chromeはまだ対応していないことが原因だと思われる。もう一度ロードすると両方ともHTTP/3になる。 https://gyazo.com/62a7ec104f1fb3f720b9c32a55baa963
長い間(N時間ぐらい)放置してリロードすると初回のアクセスがHTTP/2に戻ったりする。 エピソード: ngtcp2にバージョンが付く前の頃(2021年11月)はChromeだとh2になったり何度かリロードしたらたまにh3になったり不安定なこともあった。これがngtcp2の問題なのかChromeなのかは不明だった。ここ数ヶ月でたまにこのページの内容を検証して安定感を感じているので記述することにした。 なぜnghttpxなのか
Piping Serverの力を最大限発揮するにはストリーミングできる必要がある。HTTPのリクエストとレスポンスの両方でストリーミングされる。HTTP/1.1以降であればストリーミングはHTTPが普通に備えていて良い機能だと思う。むしろHTTP/2以降はプロトコルがネイティブでストリームを搭載してTransfer-Encoding: chunkedを廃止している(HTTP/2では「Transfer-Encoding: chunked」を使ってはいけない)。だが現存するHTTPサーバーはコンテンツの配信でよく使われるためかバッファによりスムーズさが失われたり、長時間データが流れ続ける耐性がなかったりするものが多かった。デフォルトでnghttpxはバックエンドのHTTPサーバーを尊重しスムーズなストリーミングを実現できている。 SSH over HTTP/3
以下はPiping Serverを使ってポートフォワーディングしたSSHをWebブラウザ上のSSHクライアントで利用しているデモ。これはスムーズなnghttpxを使うことでHTTP/3上でのストリーミングが実現できていることを示している。 https://gyazo.com/9dd14f66ed1528cd62d42728e3562696
ネットワークを見るとGETのリクエストである/bbbへの接続のプロトコルがh3になっていることがわかる。/aaaの方はPOSTのリクエストでChromeの仕様でpendingになっているためか表示はないが通信できている。
HTTP/3化したPiping Serverへの期待
速度の面以外のHTTP/3への期待は、QUICのコネクションマイグレーション。TCPの時よりも切断されず強力な接続が維持できるのではないかと期待している。コネクションマイグレーションにより、TCPではできなかったクライアント側のIPアドレスやポートの変更後も接続が維持できる。サーバーの設定によってはしばらくの間オフラインになってもコネクションを維持できるのではないかなと想像したりしている。長時間のストリーミングを安定的に維持するためにもコネクションマイグレーションに期待している。 おまけ: Docker Compose
code:docker-compose.yml
version: '3.7'
services:
http3:
depends_on:
- piping
image: nwtgck/nghttp2-for-http3
privileged: true
init: true
restart: always
ports:
- 8443:8443
- 8443:8443/udp
command: |
nghttpx --npn-list=h3,h2,h2-16,h2-14,http/1.1 --add-response-header='alt-svc: h3=":8443"; ma=3600, h3-29=":8443"; ma=3600' /etc/letsencrypt/live/ex.nwtgck.org/privkey.pem /etc/letsencrypt/live/ex.nwtgck.org/fullchain.pem -f '*,8443' -f '*,8443;quic' -b piping,8080 --rlimit-memlock 524288
volumes:
- /etc/letsencrypt:/etc/letsencrypt:ro
piping:
image: nwtgck/piping-server:v1.12.3
restart: always
expose:
- "8080"
command: --http-port=8080
(version: '3.1'にするならinit: trueを消せば可能)
以下で起動。本当に実際に試す場合は/etc/letsencrypt/live/ex.nwtgck.orgを適切に変更すれば良い
code:bash
docker-compose up