要約

  • RFC 6455のWebSocketはHTTP/1.1のGET、Upgrade、101応答を使って接続を切り替えた。RFC 7540のHTTP/2は複数ストリームを共有するため、接続全体を切り替える101を使わない。
  • RFC 8441では、サーバーからSETTINGS_ENABLE_CONNECT_PROTOCOL = 1を受け取った後、クライアントが:protocol = websocket、:scheme、:pathを使う拡張CONNECTを新しいストリームに送れる。IANAでの設定番号は0x8、初期値は0だ。
  • WebSocketのデータは開かれた一本のストリームに流れ、同じ接続の他ストリームは動き続ける。旧方式のConnection、UpgradeとSec-WebSocket-Key/Acceptの処理は、この開始手順から外れる。

大きすぎた切り替えスイッチ

2011年のWebSocket規格はHTTP/1.1のリクエストから始まった。クライアントがUpgrade: websocketとConnection: Upgradeを載せたGETを送り、サーバーが101 Switching Protocolsで応じる。以後、そのTCP接続は双方向のWebSocket通信となる。一つのリクエストが一つの接続を使うなら、管を丸ごと引き渡す発想に無理はない。

HTTP/2は管を複数のストリームで共用する。ページが画像を受け取る間に、別の機能が長い対話を始められる。RFC 7540はHTTP/2で101を廃した。UpgradeやConnectionは接続全体の概念であり、一本のストリームに隣の通信まで変える権限はない。古い開き方が新しい共有構造に合わなくなった。

運用上の代償もあった。ページ用のHTTP/2接続がすでにあるのに、WebSocketには別の開始経路が要る。チャットが接続全体を奪えばほかの仕事が行き場を失い、旧式の手順に固執すれば共用の利点を活かせない。RFC 8441は単なる応答番号の置き換えではなく、機能が要求できる占有範囲を作り直した。

サーバーが小さな扉を開ける

サーバーはSETTINGS_ENABLE_CONNECT_PROTOCOL = 1で対応を通知する。IANAのHTTP/2設定登録表では番号0x8、初期値0。クライアントは値1を受信して初めて、新しいストリームに拡張CONNECTを使える。許可前に新しい疑似ヘッダーを送れば、対応しない相手には不正なリクエストとなる。設定は飾りの能力表示ではなく、リクエストの意味を変える合意だ。

許可を得たクライアントは新しいストリームに:method = CONNECTと:protocol = websocketを載せる。対象URIの:scheme、:path、:authorityも必要だ。紛らわしいのは:authorityの意味である。通常のCONNECTではトンネル先のホストを示すが、この拡張では対象URIのauthorityとして扱う。RFC 8441は、CONNECTという語だけを見てそのホストへ通常の別トンネルを作ってはならないと明記する。:protocolがこのストリームの中身を示す。

安全なWebSocket URIでは対象のschemeをhttpsとし、非暗号化のURIではhttpとする。残りの対象位置はWebSocket URIを引き継ぐ。HTTP/2の正方向プロキシを通る場合、まず従来のCONNECTでWebSocketサーバーへの経路を作り、その先で使うHTTPの版によって開始方法を選ぶ。源サーバー向けの拡張CONNECTを、どのプロキシにもそのまま差し込めるわけではない。

接続でなくストリームが双方向になる

拡張CONNECTは古いGETによる開始に代わる。ConnectionとUpgradeを送らず、HTTP/1.1のSec-WebSocket-KeyとSec-WebSocket-Acceptも処理しない。この経路では:protocolがその役目を引き受ける。一方、Origin、WebSocketの版、サブプロトコル、拡張を示すヘッダーはWebSocketの規則に従って使う。変わったのは入場手続きで、入った後のフレームは残る。

RFC 8441の例では、設定による許可の後に拡張CONNECTが送られ、サーバーは:status = 200を返す。その後、HTTP/2のDATAフレームが双方向にWebSocketデータを運ぶ。選ばれたストリームを、RFC 6455でいうTCP接続のように扱う。隣のストリームは通常のHTTP/2通信や別のトンネルを続けられる。優先順位、取消し、流量制御もHTTP/2に残り、END_STREAMとRST_STREAMがそのストリームの正常終了と例外を示す。

この変更で占有権の大きさが合った。チャットは長く続いても画像のリクエストを人質に取らない。サーバーは設定を出さずに新方式を拒める。クライアントは特殊なCONNECTを試す前に相手の対応を知る。旧101の意味を中間機器が推測する必要はない。

共用の管にほかの住人が残った

RFC 8441は規模を絞った設計だ。HTTP/2とWebSocketの統合をもっと大きく作り直す選択肢もあると認めながら、両者を一つの接続で並走させる問題に拡張CONNECTで答えた。2018年に変わったのは入場であり、WebSocketのフレームと安全上の多くの条件は続く。旧稿で扱ったマスキングはフレームの問題で、本稿の争点はそのフレームを運ぶ一本の流への許可だ。

四つの公式記録は仕組みと登録を示す。特定のサービスで使われたことを示すには、日付のあるクライアント・サーバー・プロキシの実装記録とSETTINGS、HEADERSの通信記録が要る。歴史上の変更は明確だ。HTTP/2は一つの会話に共有接続を渡さず、WebSocketには相手の同意を得て開く部屋を一本のストリームに与えた。