Go 1.21ではcrypto/tlsパッケージでQUIC関連の更新が少しありましたが、QUICそのものは入りませんでした。QUICとかHTTP/3周りをどうするかはいろいろ議論があり、次のことが決定しています。
- 最終的にはnet/quicが作られる
- ただし、APIの安定化のために、まずは準標準パッケージとして golang.org/x/net/quicを作っていく
github.com/quic-go/quic-go
という実装はあるが、それをそのまま取り込むことはしない
ということで、もう少ししばらくかかりそうです。
といっても、HTTP/3のリクエストをエンドのアプリケーションサーバーが直接受けることはおそらく稀で、CDNとか、最前面にたつエンドポイントがHTTP/2やHTTP/3をしゃべって、その裏はHTTP/1.1(非TLS)が多いと思いますし、より固くしようとしてもmTLSでHTTP/2じゃないですかね。もともとHTTP/3の強みはエラー率の高い回線でも速度が落ちにくいことなので、回線品質の安定したデータセンターとかクラウド内部はHTTP/3にしてもうまみがあまりないといえるので、そんな悲観的になることもないかな、と思います。
とはいえ、HTTP/3に触ってみたい方もいるかと思うので試してみました。
上記の議論の中でも出てきた github.com/quic-go/quic-go
は、このパッケージの作者のMarten SeemannはIETFのQUICワーキンググループの初期からのメンバーでもあり、このパッケージ自身もIETFの他のQUICエージェントとの相互接続テストを行っており、品質はばっちりだと思われます。なのでこれを試してみます。
quic-goでのHTTPサーバー
Goの標準ライブラリのサーバー実装と似たようなAPIでサーバーをたてられます。ハンドラ周りはnet/httpのhttp.Handler
インタフェースそのものなので、いろんなフレームワークをそのまま上で動かせます。
package main |
Goを書いたことがある人にはおなじみですね? HTTP/2と同じく、TLS必須なので証明書を作成する必要があります。こちらは参考情報がいろいろあるので、お好きな文献などを参考に作ってみてください。Real World HTTPでも紹介しています。
localhost
のホスト名で証明書を作ったので、localhost:8443
でアクセスするとバッチリ表示されます。Safariでアクセスしてみると、すぐに表示されて案外簡単? と思いつつ、ここから深くなってきます。
quic-goの内部ではサーバーが2つ起動する
http3パッケージにはListenAndServeQUIC()
という関数もあります。どう違うのでしょうか? 最初はこちらで実装してみたのですが動かず、上記のListenAndServe()
にしたら動きました。
こちらの方は内部的には、こんな感じのコードと同じような動きになります。よくよくみると、HTTP/2のサーバーも起動していますね。Alt-Svc
フィールドを付与しています。
QUICはUDPですが、現在のブラウザはまずTCPでサーバーアクセスしに行きます。しかし、HTTP/3のみのサーバーがたっていても、そこにはTCPのサーバーはいません。そのため、HTTP/2のサーバーを裏でたてて、そのレスポンスの Alt-Svc
フィールドを返し「こちらでHTTP/3のサーバーがいるよ」とブラウザをHTTP/3の方に誘導しているというわけです。
package main |
プロトコル選択
HTTP/3ではいくつかの箇所でプロトコル選択をさせるようになっています。
- DNSのHTTPSレコード
- TLSのハンドシェイクのALPN(Application-Layer Protocol Negotiation)
- 上記で紹介したAlt-Svcフィールド
上から順番に実行されます。DNSであれば、最初のサーバーアクセスの前に情報を得ることができます。TLSのハンドシェイクはTCP/UDPを決めた後に行えるため、最初のTCPのアクセスの空撃ちは必要です。Alt-Svcフィールドのタイミングはさらに遅く、一度HTTP/2のサーバーがリクエストを処理するまで、ブラウザはHTTP/3のサーバーの存在を知ることはできません。
個人的には103 Early HintsでAlt-Svcフィールドを返せば僅かばかりAlt-Svcをレスポンスで返すよりも効率的かな、と思いましたが、103 Early Hintsはサンプル見てもプリロードの用途しか見ませんね。
CoreDNSをたててみた
さて、上記のHTTP/3サーバーですが、Safariからはばっちり3の方につながるのですが、ChromeとFirefoxは2ばかり。たまに開発者ツールを開いてリロードしたタイミングだけ3でつながったりといまいち安定しません。そんな中、Twitter(X)で、まえかわさんという方から、HTTPSレコードでばっちりいけるというお話を伺い、ちょっと試してみることにしました。
allt-svcに加えてHTTPSレコードをつけたらchromeは確実に3でfirefoxは半々ぐらいの確率になりますね
— まえかわ (@kiwithe2027) September 17, 2023
自分も壊れてるのかと思いました
ちなみに、現在出ているSoftware Design 2023年10月号がたまたまHTTP/3特集でしたが、HTTPSレコードはCDN上の設定で付与していました。
今回あつかったCoreDNSは、CNCFの傘下にいるプロジェクトで、Kubernetesでもよく使われています。もっとも、その理由がetcdでエントリーの管理ができて、そちらの情報を元に情報を返せるため管理が楽、というところがあるのだと思いますが、今回はetcdは使いません。
こちらをみながらmacOSでやってみた例になりますが、サービスの起動部分以外はポータブルなはずです。hnakamurさんがWindowsでサービス化するラッパーを作られているのでこちらに変えればWindowsでも動くかと思います。
# インストール |
http3.test
というホスト名だけ特別扱いしてHTTPSレコードをつけてあげたいので、/opt/homebrew/etc/coredns/Corefile
ファイルを次のようにしました
. { |
上記の設定からインポートされる自分のホーム以下に追加の設定ファイル(/Users/shibu/.config/coredns/test)をおきます。こんな感じにしてみました。
$ORIGIN test. |
# 再起動 |
最後にネットワーク設定でDNSに127.0.0.1を追加してあげれば完了です。
これでSafariでアクセスしてみると、無事に https://http3.test
というURLでアクセスできました。HTTPSレコードはプロトコル以外にもポートを設定できるのでポート番号を省略できるようになります。いいですね。
しかし、実はChromeとFirefox、EdgeはこのDNSサーバーを見に行ってくれませんでした。昔はDNS設定があったと思いますが、いまはDNS over HTTPSのみです。ChromeでHTTP/3に繋ぎたくてCoreDNSを入れてみたのですが、ここはうまく行っていません。DNS over HTTPSをCoreDNSで建ててみてもそこにアクセスしてくれなかったり、設定を拒否されたり。ここはぼちぼちやっていこうと思います。dnsmasqを入れた時はアクセスはしてくれたものの、HTTPSレコードの追加が分からずCoreDNSでやりましたが、別の方法も試そうかと。
まとめ
というわけで、HTTP/3のサーバーを起動してブラウザでアクセスしてみました。Safariからはうまくつながりました。しかし、実際に試す時間の90%はDNS周りを操作する時間だったりして、ちょっと敷居が高い気がしました。
将来的にはもう開発体験がちょっと改善されたらいいな、と思いました。