ChromeのHTTP/3(ドラフト版)対応

Chrome CanaryがHTTP/3のドラフト版に対応していたので、簡単に見てみる
(登場するバージョンについての説明は、後述する。)

動作確認

デフォルトでは喋ってくれない。chrome canaryの起動オプションに下記を追加する。

--enable-quic --quic-version=h3-23

(起動コマンドがわからない場合は、chrome://version/ を開くと確認できる)

HTTP/3 draft-23に対応しているページを開きます。
https:///quic.aiortc.org:4433

ページは正しく表示され、デベロッパーツール上はhttp/2+quic/99と表示されますが、HTTP/3 draft-23で通信は行われています。
f:id:ASnoKaze:20190921230316p:plain

Wireshark で、QUICトランスポートのバージョンを見てみるとちゃんとIETF QUICのバージョンを示す 0xFF00000017 (draft-23) で通信していることを確認できます。
f:id:ASnoKaze:20190921230528p:plain

バージョンについては、順に説明していく。

gQUIC, iQUICのバージョン

もともと、"QUIC"はGoogleが考案・実装していたプロトコルですが、標準化するに当たりIETFに持ち込まれました。そして、IETFではQUICはトランスポートプロトコルとして設計され、その上にHTTP/3がのっかる形となりました。(Google QUICはHTTPレイヤも含むプロトコルでした)

この過程で、もともとのGoogle QUICとIETF QUICは別物となり、それぞれ互換性の無いプロトコルとなりました。Google版のQUICをgQUIC, IETF版のQUICをiQUICと呼び分けることもあります。

もちろん、HTTP/3ではiQUICを前提にしているため、HTTP/3に対応するためにはiQUICに対応する必要があります。

Chromeでは標準仕様であるiQUICへの移行を段階的に進めており、HTTP/3 draft-23 が実験的に喋れる状況となっています。

識別子とalt-svc

iQUIC

HTTP/3では、サーバがHTTP/3に対応している旨、alt-svcレスポンスヘッダを用いて通知します。

下記例では、HTTP/3の仕様 draft-23バージョンを 4433ポートで提供していることを示しています。このレスポンスヘッダを受け取ったクライアントは、自身がそのバージョンに対応していれば、そこに繋がきに行きます。

alt-svc: h3-23=":4433"; ma=3600

IETF版、QUICではHTTP/3の識別子として、 h3- という形で通知を行います。もし、RFCとして正式なものが公開されれば、はなくなり、h3という識別子を用いることになります。
(ここでは、HTTP/3のバージョンにのみ言及しており、トランスポートとしてのQUICについては別途コネクション時にネゴシエーションされる点に注意。QUICトランスポートのハンドシェイクでは draft版は 0xff0000 を使用します。)

gQUIC

gQUICでも同様にalt-svcを使用しますが、バージョン体系が異なっています

Googleのサーバは下記のようなalt-svcを返します。gQUICは、HTTPレイヤも含むプロトコルですので識別子としてquicを使用しています。vという値を用いて、対応バージョンとして、46,43,39に対応していることを示しています。

alt-svc: quic=":443"; ma=2592000; v="46,43,39"

識別子も、バージョン体系もiQUICとgQUICは異なっています。

先述の通り、gQUICはiQUICへの移行を目指しております。GoogleのQUICライブラリに、その様子が伺えます
https://quiche.googlesource.com/quiche/+/refs/heads/master/quic/core/quic_versions.h

  QUIC_VERSION_46 = 46,  // Use IETF draft-17 header format with demultiplexing bit.
  QUIC_VERSION_47 = 47,  // Allow variable-length QUIC connection IDs.
  QUIC_VERSION_48 = 48,  // Use CRYPTO frames for the handshake.
  QUIC_VERSION_99 = 99,  // Dumping ground for IETF QUIC changes which are not yet ready for production.

gQUICでも暗号経路を確立するために暗号ハンドシェイクを行いますが、iQUICと同様のTLS1.3を用いたハンドシェイクの場合は、iQUICのバージョン同様 0xff0000を使うことがわかります(そうでない場合は、QUIC 99となる。)
https://quiche.googlesource.com/quiche.git/+/c8d9e40cd42e73b643e36300c807865b2ec8787d/quic/core/quic_versions.cc#115

    case QUIC_VERSION_99:
      if (parsed_version.handshake_protocol == PROTOCOL_TLS1_3) {
        return MakeVersionLabel(0xff, 0x00, 0x00, kQuicIetfDraftVersion);
      }
      return MakeVersionLabel(proto, '0', '9', '9');

ということで、実装的にはIETF QUIC版も対応していそうということがわかります。