2022/08/25 追記
「RFC 9297: HTTP Datagrams and the Capsule Protocol」として標準化さました。この記事の内容は古いところがあるので注意して下さい。
「HTTP Datagrams and the Capsule Protocol」という提案仕様が出されている。長く議論されている仕様ではあるが、動きがあったので眺めておく
背景
トランスポートプロトコルであるQUICでは、パケットロスがあったとしてもアプリケーションプロトコルのデータは再送してピアに届けられます。
「RFC9221 An Unreliable Datagram Extension to QUIC」という仕様で定義される、QUIC DATAGRAMフレーム拡張では、信頼性のないアプリケーションデータの送受信が可能になります。DATAGRAMフレームのデータはパケットロスで失われても再送する必要はありません。
DATAGRAMフレームは受信したものから処理することができます、アプリケーションの要件によりデータの再送が必要な場合はアプリケーションレイヤで再送信を行うこともできます。
WebTransportやMASQUE(CONNECT-UDP)といった仕様では、このQUIC DATAGRAM拡張をHTTPで利用します。ただし、普通のQUIC DATAGRAMフレームはストリーム上で送信されません。HTTPでDATAGRAMを送受信するには、HTTPメッセージと関連付ける必要があります。それを定義するのが「HTTP Datagrams and the Capsule Protocol」です。
もともとHTTP/3を前提にしていましたが、途中にProxyが居るケースなどを想定しHTTP/1.1やHTTP/2も想定したCapsuleプロトコルとともに標準化が進められています。
なお、WebTransportやMASQUE(CONNECT-UDP)については過去に書いた記事を御覧ください
(HTTP DatagramはWebTransportなどで利用されます。通常のHTTPリクエスト・レスポンスで使う想定は今のところありません)
HTTP DatagramsとCapsuleプロトコル
HTTP/3, HTTP/2, HTTP/1の場合でそれぞれ、データの送り方が違います。
- HTTP/3では、QUIC DATAGRAMフレームを利用します
- HTTP/2では、拡張CONNECTでトンネルを確立し、CAPSULEプロトコルを使ってデータを送信します。(信頼性のない通信はできません)
- HTTP/1では、HTTP Upgradeの手順でトンネルを確立し、CAPSULEプロトコルを使ってデータを送信します。(信頼性のない通信はできません)
常にHTTP/3(QUIC)が使えるとは限りません、QUICがブロックされている環境では、HTTP/2やHTTP/1.1でDatagramメッセージを送れる必要があります。このときCAPSULEプロトコルという仕組みでは、TCPなHTTPでDatagramメッセージをやりとりできるようになります。
HTTP/3
HTTP/3では、QUIC DATAGRAMフレームに以下のデータを格納します
- Quarter Stream IDは、このDatagramが関連付けられれたHTTPリクエストのストリームIDが格納されます (クライアントが開始したストリームが前提なので4で割った数値で十分)
- HTTP Datagram Payloadは、アプリケーションのデータです
ご覧の通り、以前まであった、一つのストリームに複数のDatagaram フローを関連付ける仕組みはなくなりました (CONNECT-UDPといったより上位のプロトコルで定義してたりします)。
なおこの拡張を利用するには、H3_DATAGRAM SETTINGSパラメータが有効になっている必要があります。
HTTP/1.1とHTTP/2におけるコネクションの確率
DatagramメッセージをTCPなHTTPプロトコルで送信できるようにします。
HTTP/1.1の場合は Upgradeの手順をもって通信を一旦確立します。その後、そのコネクション上でデータをやり取りします
```
GET https://proxy.example.org/.well-known/masque/udp/192.0.2.42/443/ HTTP/1.1
Host: proxy.example.org
Connection: upgrade
Upgrade: connect-udp
```
HTTP/2の場合は拡張CONNECTの手順をもって通信を一旦確立します。その後、そのコネクション上でデータをやり取りします。
```
HEADERS
:method = CONNECT
:protocol = connect-udp
:scheme = https
:path = /.well-known/masque/udp/192.0.2.42/443/
:authority = proxy.example.org
```