製品開発グループintdashチームの呉羽です。 今回は標準化が進められているWebTransportの紹介と、実際にブラウザでの動作検証を行います。
本記事の参考資料として、Webの標準化団体W3C(World Wide Web Consortium)が公開しているWebTransport Explainerを用いています。
WebTransportとは何か
WebTransportとは、ブラウザとサーバー間での利用を目的とした新しい双方向通信プロトコルです。ではなぜ今さら双方向通信プロトコルなのでしょうか?
WebTransport標準化の目的
まず現状の課題をお伝えします。Web上の双方向通信プロトコルとしてWebSocketが存在しますが、WebSocketは単一のTCPコネクション上で動作します。ゆえにTCPによるメッセージの到達保証と順序保証が提供されますが、それらが不要なユースケースに対してはオーバスペックです。
そこでWebTransportでは、到達保証・順序保証のあるWebSocketのような通信方法に加え、それらの保証が不要な通信方法の提供を目的としています。
WebTransport ExplainerにはWebTransportの目的として以下の3つが設定されています。
- Provide a way to communicate with servers with low latency, including support for unreliable and unordered communication.
- Provide an API that can be used for many use cases and network protocols, including both reliable and unreliable, ordered and unordered, client-server and p2p, data and media.
- Ensure the same security properties as WebSockets (use of TLS, server-controlled origin policy)
先に述べた通信方法の提供に加え、従来のWebSocketと同じセキュリティ水準が必要です。
WebTransportを実現する技術
WebTransportの実装として、HTTP/3を利用した仕様が策定されています。https://www.ietf.org/archive/id/draft-ietf-webtrans-http3-02.html
HTTP/3はQUICのコネクション上で動作するため、QUIC Datagram[RFC9221]による到達保証が不要な通信を提供できます。またQUICのストリームという抽象化された通信経路により、ストリーム間での順序保証が不要な通信を実現できます。
以下に、HTTP/3でのWebTransportセッションの確立・メッセージのやり取りを要約します。
- HTTP/3の
:protocol
ヘッダーにwebtransport
を指定し、拡張CONNECTメソッドでサーバーにリクエストを送る。 - 1に用いたQUICのストリームIDを、WebTransportのセッションIDとする。
- 信頼性が必要な通信をする場合は、QUICのストリームを開き、セッションIDとともに送信する。
- 信頼性が不要な通信をする場合は、DatagramのメッセージをセッションIDとともに送信する。
このように実装のほどんどはQUICの機能を利用するため、WebTransportの実装にはそれほど手は掛からなさそうです。
WebRTCという代替案
WebSocket以外にもWebRTCという双方向通信プロトコルが存在します。web.devが公開している記事では、以下のようにWebRTCと比較しています。
- WebRTCはサーバー側でICE, DTLS, SCTPといった珍しいプロトコルの実装が必要だが、WebTransportはQUICやHTTP/3のみでよい(ただしWebTransportは実装方法による)。
- WebRTCはWeb Worker内で動かないが、WebTransportは動く。
- WebRTCよりもWebTransportはモダンなAPIデザインである。
しかし、既にWebRTCで満足のいくアプリケーションが実装されている場合、WebTransportに変更する大きなメリットは無いそうです。
WebTransportをブラウザ上で動かしてみる
それでは実際にWebTransportを実装し、ブラウザ上で動かしてみます。今回は弊社のサーバーサイドチームが主に用いているGo言語で実装します。
Go言語では既にquic-goと、それを用いたwebtransport-goというライブラリが存在しています。しかしWebTransportに関する仕様は未だに不安定であるため、すべての機能は実装されていません。例えばHTTP上でDatagramを扱う仕様(draft-ietf-masque-h3-datagram)は過去に互換性のない変更が起きており、それが影響してかquic-goにも実装されていません。
よって、安定していそうな箇所やWebTransportに依らない箇所はquic-goへPullRequestを出し、他の箇所は自己実装して進めます。
実装したGoのWebTransportライブラリ: github.com/hareku/webtransport-go
ブラウザ側のコードは、GoogleChromeが公開しているサンプルコードを一部修正して利用します。
修正したコード: github.com/hareku/samples/tree/aptpod-webtransport/webtransport
それでは、WebTransportサーバーとクライアント側のサーバーを起動してみます。動作にはGo v1.18とNode.js v14.18.0を用います。これらのバージョンが異なる場合、動作しない場合があります。
$ git clone https://github.com/hareku/webtransport-go.git -b aptpod-webtransport $ cd webtransport-go/cmd/echo $ go run . cert.pem key.pem # localhostの自己証明書を用意して指定 $ git clone https://github.com/hareku/samples.git -b aptpod-webtransport $ cd samples/webtransport $ ./launch.sh
簡素な検証にはなりますが、全ての通信方法で「Hello」というメッセージを送信し、同一メッセージが返ってくることを確認します。
データグラム
Sent datagram: Hello Datagram received: Hello
片方向ストリーム
Sent a unidirectional stream with data: Hello New incoming unidirectional stream #1 Received data on stream #1: Hello Stream #1 closed
双方向ストリーム
Opened bidirectional stream #2 with data: Hello Received data on stream #2: Hello Stream #2 closed
ブラウザ上のイベントログを見ると、同一メッセージが返ってくることを確認できました。
まとめ
以上がWebTransportの紹介と動作検証でした。WebTransportはまだRFCとして公開はされていませんが、WebSocketの次世代である双方向通信プロトコルとして弊社は期待しています。