RFC 9297 - HTTP Datagrams and the Capsule Protocol (HTTP 数据报和封装协议)
发布时间: 2022年8月
状态: Standards Track (标准跟踪)
作者: D. Schinazi (Google LLC), L. Pardue (Cloudflare)
Abstract (摘要)
This document describes HTTP Datagrams, a convention for conveying multiplexed, potentially unreliable datagrams inside an HTTP connection.
本文档描述了 HTTP Datagrams (HTTP 数据报), 这是一种在 HTTP 连接内传递多路复用, 可能不可靠的数据报的约定。
In HTTP/3, HTTP Datagrams can be sent unreliably using the QUIC DATAGRAM extension. When the QUIC DATAGRAM frame is unavailable or undesirable, HTTP Datagrams can be sent using the Capsule Protocol, which is a more general convention for conveying data in HTTP connections.
在 HTTP/3 中, HTTP Datagrams 可以使用 QUIC DATAGRAM extension (QUIC DATAGRAM 扩展) 不可靠地发送。当 QUIC DATAGRAM frame (QUIC DATAGRAM 帧) 不可用或不希望使用时, HTTP Datagrams 可以使用 Capsule Protocol (封装协议) 发送, 这是一种在 HTTP 连接中传递数据的更通用的约定。
HTTP Datagrams and the Capsule Protocol are intended for use by HTTP extensions, not applications.
HTTP Datagrams 和 Capsule Protocol 旨在供 HTTP extensions (HTTP 扩展) 使用, 而非应用程序。
Table of Contents (目录)
- 1. Introduction (简介)
- 1.1 Conventions and Definitions (约定和定义)
- 2. HTTP Datagrams (HTTP 数据报)
- 2.1 HTTP/3 Datagrams (HTTP/3 数据报)
- 2.1.1 The SETTINGS_H3_DATAGRAM HTTP/3 Setting (SETTINGS_H3_DATAGRAM HTTP/3 设置)
- 2.2 HTTP Datagrams Using Capsules (使用封装的 HTTP 数据报)
- 2.1 HTTP/3 Datagrams (HTTP/3 数据报)
- 3. Capsules (封装)
- 3.1 HTTP Data Streams (HTTP 数据流)
- 3.2 The Capsule Protocol (封装协议)
- 3.3 Error Handling (错误处理)
- 3.4 The Capsule-Protocol Header Field (Capsule-Protocol 头字段)
- 3.5 The DATAGRAM Capsule (DATAGRAM 封装)
- 4. Security Considerations (安全考虑)
- 5. IANA Considerations (IANA 考虑)
- 5.1 HTTP/3 Setting (HTTP/3 设置)
- 5.2 HTTP/3 Error Code (HTTP/3 错误代码)
- 5.3 HTTP Header Field Name (HTTP 头字段名称)
- 5.4 Capsule Types (封装类型)
- 6. References (参考文献)
- 6.1 Normative References (规范性参考文献)
- 6.2 Informative References (信息性参考文献)
- Acknowledgments (致谢)
- Authors' Addresses (作者地址)
Related Resources (相关资源)
- Official RFC:
https://www.rfc-editor.org/rfc/rfc9297.txt - Datatracker:
https://datatracker.ietf.org/doc/html/rfc9297 - Errata:
https://www.rfc-editor.org/errata_search.php?rfc=9297
HTTP Datagrams概述
什么是HTTP Datagrams?
定义:
HTTP Datagrams = HTTP数据报
作用: 在HTTP连接中传输不可靠的数据报
特点:
- 可能丢失 (不可靠传输)
- 不保证顺序
- 低延迟
- 与HTTP请求关联
类比:
传统HTTP: 可靠的字节流 (像TCP)
HTTP Datagrams: 不可靠的数据报 (像UDP)
为什么需要HTTP Datagrams?
传统HTTP的局限:
❌ 所有数据必须可靠传输
❌ 存在队头阻塞 (Head-of-Line Blocking)
❌ 不适合实时应用
❌ 重传延迟高
HTTP Datagrams的优势:
✅ 低延迟 (无重传)
✅ 无队头阻塞
✅ 适合实时通信
✅ 适合容错性强的数据
应用场景:
✓ WebRTC数据通道
✓ 实时游戏
✓ VoIP/视频通话
✓ IoT实时数据
✓ 流媒体
✓ DNS over HTTP (DoH)
HTTP Datagrams vs 传统HTTP
特性 | 传统HTTP流 | HTTP Datagrams
--------------|----------------|------------------
传输方式 | 字节流 | 数据报
可靠性 | 可靠 (保证送达) | 不可靠 (可能丢失)
顺序 | 保证顺序 | 不保证顺序
延迟 | 较高 (重传) | 低 (无重传)
适用场景 | 文件传输、API | 实时通信
队头阻塞 | 存在 | 无
示例:
传统HTTP:
[请求] → [响应流: chunk1 → chunk2 → chunk3...]
HTTP Datagrams:
[请求] ⇄ [数据报1] [数据报3] [数据报2] (顺序可能不同)
[数据报4丢失]
核心概念详解
1. Datagram Flow (数据报流)
定义:
Datagram Flow = 与HTTP请求关联的数据报序列
特点:
- 每个HTTP请求可以有一个Datagram Flow
- Flow ID唯一标识
- 双向通信 (客户端 ⇄ 服务器)
Flow ID生成:
- 基于HTTP/3 Stream ID (或HTTP/2 Stream ID)
- Quarter Stream ID: Stream ID / 4
示例:
HTTP/3 Stream ID: 0 → Datagram Flow ID: 0
HTTP/3 Stream ID: 4 → Datagram Flow ID: 1
HTTP/3 Stream ID: 8 → Datagram Flow ID: 2
Flow生命周期:
1. 创建: HTTP请求发送时
Client → Server: HTTP GET /stream
设置: Datagram-Flow-Id: 1
2. 活跃: 数据报传输
Client ⇄ Server: Datagrams on Flow 1
3. 关闭: HTTP请求结束
- Stream关闭
- Flow ID释放
- 相关数据报丢弃
状态转换:
IDLE → ACTIVE → CLOSING → CLOSED
2. Capsule Protocol (封装协议)
什么是Capsule?
Capsule = 封装的协议消息
格式:
┌─────────────────────────────────┐
│ Capsule Type (变长整数) │
├─────────────────────────────────┤
│ Capsule Length (变长整数) │
├─────────────────────────────────┤
│ Capsule Value (Length字节) │
└─────────────────────────────────┘
特点:
- 类型-长度-值 (TLV) 结构
- 自描述
- 可扩展
- 与HTTP请求流混合传输
为什么需要Capsule?
问题: HTTP/2和HTTP/1.x没有原生数据报支持
解决方案:
1. 使用HTTP CONNECT建立隧道
2. 在隧道中传输Capsules
3. Capsule承载数据报
优势:
✓ 统一HTTP/2和HTTP/3的接口
✓ 可扩展的类型系统
✓ 兼容旧HTTP版本
✓ 支持多种上层协议
Capsule类型:
已定义类型:
- 0x00: DATAGRAM (数据报)
- 0x01-0xFF: 保留给其他协议使用
DATAGRAM Capsule格式:
┌─────────────────────────────┐
│ Type: 0x00 │
├─────────────────────────────┤
│ Length: N │
├─────────────────────────────┤
│ Context ID (变长整数) │
├─────────────────────────────┤
│ Datagram Payload (N-X字节) │
└─────────────────────────────┘
Context ID: 标识数据报上下文 (通常是Flow ID)
HTTP/3中的HTTP Datagrams
使用QUIC DATAGRAM扩展
前提条件:
1. QUIC必须支持DATAGRAM扩展 (RFC 9221)
2. HTTP/3必须协商H3_DATAGRAM设置
SETTINGS帧:
┌──────────────────────────────┐
│ Setting ID: H3_DATAGRAM (0x33)│
├──────────────────────────────┤
│ Value: 1 (启用) │
└──────────────────────────────┘
协商流程:
Client → Server: SETTINGS [H3_DATAGRAM=1]
Server → Client: SETTINGS [H3_DATAGRAM=1]
→ 双方确认支持HTTP Datagrams
发送数据报:
格式:
┌──────────────────────────────────┐
│ Quarter Stream ID (变长整数) │
├──────────────────────────────────┤
│ HTTP Datagram Payload │
└──────────────────────────────────┘
Quarter Stream ID = Stream ID / 4
示例:
HTTP请求在Stream 4上:
Quarter Stream ID = 4 / 4 = 1
发送数据报:
[0x01] [Payload: "Hello"]
↑
Quarter Stream ID
接收数据报:
处理流程:
1. 接收QUIC DATAGRAM帧
2. 解析Quarter Stream ID
3. 查找对应的HTTP Stream
4. 将Payload交给上层应用
错误处理:
- Unknown Quarter Stream ID → 丢弃数据报
- Stream已关闭 → 丢弃数据报
- 解析错误 → 关闭连接 (H3_DATAGRAM_ERROR)
示例代码 (Node.js概念)
// HTTP/3 Datagrams示例 (概念代码)
const http3 = require('http3');
// 服务器
const server = http3.createServer({
enableDatagrams: true // 启用HTTP Datagrams
});
server.on('request', (req, res) => {
if (req.headers[':method'] === 'CONNECT' &&
req.headers[':protocol'] === 'connect-udp') {
// 接受CONNECT-UDP请求
res.writeHead(200);
// 接收数据报
req.on('datagram', (data) => {
console.log('Received datagram:', data);
// 回显数据报
req.sendDatagram(data);
});
}
});
// 客户端
const client = http3.connect('https://example.com');
const req = client.request({
':method': 'CONNECT',
':protocol': 'connect-udp',
':authority': 'target.example',
':path': '/.well-known/masque/udp/192.0.2.6/443/'
});
req.on('response', (headers) => {
if (headers[':status'] === '200') {
// 发送数据报
req.sendDatagram(Buffer.from('Hello'));
// 接收数据报
req.on('datagram', (data) => {
console.log('Received:', data.toString());
});
}
});
HTTP/2和HTTP/1.x中的HTTP Datagrams
使用Extended CONNECT
协商流程:
1. HTTP/2: 协商Extended CONNECT (RFC 8441)
SETTINGS_ENABLE_CONNECT_PROTOCOL: 1
2. 建立CONNECT隧道:
CONNECT example.com:443 HTTP/1.1
Host: example.com
Connection: Upgrade
Upgrade: connect-udp
Capsule-Protocol: ?1
3. 服务器响应:
HTTP/1.1 200 OK
Capsule-Protocol: ?1
4. 隧道建立成功
通过Capsule传输:
在CONNECT隧道中:
发送数据报:
┌─────────────────────────────┐
│ Capsule Type: 0x00 (DATAGRAM)│
├─────────────────────────────┤
│ Length: N │
├─────────────────────────────┤
│ Context ID: 0 │
├─────────────────────────────┤
│ Payload: "Hello" │
└─────────────────────────────┘
接收:
1. 读取Capsule头部
2. 解析Type和Length
3. 读取Capsule Value
4. 提取Payload交给应用
HTTP/2示例
// HTTP/2 with Capsules (概念代码)
const http2 = require('http2');
// 客户端
const client = http2.connect('https://example.com');
const req = client.request({
':method': 'CONNECT',
':protocol': 'connect-udp',
':authority': 'target.example',
':path': '/',
'capsule-protocol': '?1'
});
req.on('response', (headers) => {
if (headers[':status'] === '200' &&
headers['capsule-protocol'] === '?1') {
// 发送DATAGRAM Capsule
function sendDatagram(payload) {
const contextId = encodeVarint(0);
const type = encodeVarint(0x00); // DATAGRAM
const length = encodeVarint(contextId.length + payload.length);
req.write(Buffer.concat([type, length, contextId, payload]));
}
sendDatagram(Buffer.from('Hello'));
// 接收Capsules
req.on('data', (chunk) => {
const capsule = parseCapsule(chunk);
if (capsule.type === 0x00) { // DATAGRAM
console.log('Received datagram:', capsule.payload);
}
});
}
});
应用场景详解
1. WebRTC数据通道 (CONNECT-UDP)
场景描述:
目标: 通过HTTP代理建立UDP连接
协议: CONNECT-UDP (RFC 9298)
流程:
Client → Proxy: CONNECT-UDP target.example:5000
Proxy → Server: UDP packets
Server → Proxy: UDP packets
Proxy → Client: HTTP Datagrams
用途:
✓ 防火墙穿透
✓ NAT遍历
✓ 企业代理场景
示例:
// CONNECT-UDP示例
const req = client.request({
':method': 'CONNECT',
':protocol': 'connect-udp',
':authority': 'proxy.example.com',
':path': '/.well-known/masque/udp/192.0.2.6/5000/'
});
req.on('response', (headers) => {
if (headers[':status'] === '200') {
// 发送UDP数据 (作为HTTP Datagram)
req.sendDatagram(udpPacket);
// 接收UDP响应
req.on('datagram', (data) => {
// 处理UDP响应
handleUdpResponse(data);
});
}
});
2. DNS over HTTP (DoH)
使用Datagrams加速:
传统DoH (RFC 8484):
- POST /dns-query
- 响应完整DNS报文
- 可靠传输 (较慢)
DoH with Datagrams:
- CONNECT /dns-query
- 使用HTTP Datagrams
- 不可靠传输 (更快)
- 适合无状态查询
性能对比:
传统DoH: RTT + TCP/TLS握手 + HTTP响应
Datagram DoH: RTT (单次往返)
3. 实时游戏
低延迟状态同步:
场景: 多人在线游戏
要求:
- 低延迟 (<50ms)
- 容忍丢包 (位置更新)
- 高频率 (60 FPS)
使用HTTP Datagrams:
Client ⇄ Server: 位置更新 (每帧)
{
playerId: "123",
x: 100,
y: 200,
timestamp: 1234567890
}
优势:
✓ 低延迟 (无重传)
✓ 旧数据自动丢弃
✓ 最新状态优先
代码示例:
// 游戏客户端
function sendPlayerPosition(x, y) {
const update = JSON.stringify({
type: 'position',
playerId: localPlayerId,
x, y,
timestamp: Date.now()
});
gameRequest.sendDatagram(Buffer.from(update));
}
// 每帧发送
setInterval(() => {
sendPlayerPosition(player.x, player.y);
}, 1000 / 60); // 60 FPS
// 接收更新
gameRequest.on('datagram', (data) => {
const update = JSON.parse(data.toString());
// 更新其他玩家位置
if (update.type === 'position' && update.playerId !== localPlayerId) {
updateRemotePlayer(update.playerId, update.x, update.y);
}
});
4. 视频直播
低延迟分块传输:
传统HLS/DASH:
- 大分片 (2-10秒)
- 高延迟
- 可靠传输
使用Datagrams:
- 小分片 (<100ms)
- 低延迟 (<1秒)
- 允许丢帧
流程:
编码器 → [关键帧] → HTTP Datagram → 播放器
[P帧] → HTTP Datagram → 播放器
[P帧丢失] ✗
[关键帧] → HTTP Datagram → 播放器
策略:
- 关键帧: 可靠传输 (重传)
- P帧: 不可靠传输 (丢弃)
- 播放器: 丢帧后等待下一个关键帧
安全考虑
1. 隐私保护
问题: HTTP Datagrams可能泄露信息
对策:
✓ 使用TLS/QUIC加密
✓ 填充 (Padding) 隐藏大小
✓ 流量混淆
示例:
// 添加随机填充
function sendPaddedDatagram(payload) {
const padding = crypto.randomBytes(
Math.floor(Math.random() * 100)
);
const padded = Buffer.concat([payload, padding]);
req.sendDatagram(padded);
}
2. 拒绝服务 (DoS) 防护
风险:
- 大量小数据报
- Flow ID耗尽
- 缓冲区溢出
防护措施:
✓ 限制Flow数量
✓ 速率限制 (Rate Limiting)
✓ 数据报大小限制
✓ 超时清理
实现:
const flowLimits = {
maxFlows: 100,
maxDatagramsPerSecond: 1000,
maxDatagramSize: 1200
};
function checkLimits(flowId, datagram) {
if (activeFlows.size >= flowLimits.maxFlows) {
throw new Error('Too many flows');
}
if (datagram.length > flowLimits.maxDatagramSize) {
throw new Error('Datagram too large');
}
// 速率限制
const rate = flowRates.get(flowId) || 0;
if (rate > flowLimits.maxDatagramsPerSecond) {
throw new Error('Rate limit exceeded');
}
}
3. 中间件兼容性
问题: 某些中间件可能干扰
示例:
- 代理: 可能阻止CONNECT
- 防火墙: 可能过滤DATAGRAM帧
- CDN: 可能不支持HTTP/3
解决方案:
✓ 优雅降级
✓ 回退到传统HTTP
✓ 探测支持情况
代码:
async function connectWithFallback(url) {
try {
// 尝试HTTP/3 with Datagrams
return await connectHttp3Datagrams(url);
} catch (err) {
// 降级到HTTP/2 with Capsules
try {
return await connectHttp2Capsules(url);
} catch (err2) {
// 最后降级到传统HTTP
return await connectHttp1(url);
}
}
}
性能优化
1. 缓冲策略
问题: 频繁小数据报效率低
优化: 批量发送
- 累积多个数据报
- 达到阈值后发送
- 设置最大延迟
实现:
class DatagramBatcher {
constructor(maxBatchSize = 10, maxDelayMs = 10) {
this.queue = [];
this.maxBatchSize = maxBatchSize;
this.maxDelayMs = maxDelayMs;
this.timer = null;
}
send(datagram) {
this.queue.push(datagram);
if (this.queue.length >= this.maxBatchSize) {
this.flush();
} else if (!this.timer) {
this.timer = setTimeout(() => this.flush(), this.maxDelayMs);
}
}
flush() {
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
if (this.queue.length > 0) {
const batch = this.queue;
this.queue = [];
// 发送批量数据报
for (const dg of batch) {
req.sendDatagram(dg);
}
}
}
}
2. Flow管理
最佳实践:
✓ 复用Flow ID (避免创建过多)
✓ 及时关闭不用的Flow
✓ 监控Flow状态
实现:
class FlowManager {
constructor() {
this.flows = new Map();
}
getOrCreate(streamId) {
const flowId = Math.floor(streamId / 4);
if (!this.flows.has(flowId)) {
const flow = {
id: flowId,
created: Date.now(),
lastUsed: Date.now(),
datagrams: 0
};
this.flows.set(flowId, flow);
}
const flow = this.flows.get(flowId);
flow.lastUsed = Date.now();
flow.datagrams++;
return flow;
}
cleanup(maxIdleMs = 60000) {
const now = Date.now();
for (const [flowId, flow] of this.flows) {
if (now - flow.lastUsed > maxIdleMs) {
this.flows.delete(flowId);
console.log(`Cleaned up flow ${flowId}`);
}
}
}
}
实战:构建WebRTC Proxy
完整示例:
const http3 = require('http3');
const dgram = require('dgram');
// HTTP/3 Proxy服务器
class WebRTCProxy {
constructor(port = 443) {
this.server = http3.createServer({
enableDatagrams: true
});
this.udpSockets = new Map();
this.server.on('request', (req, res) => {
if (req.headers[':method'] === 'CONNECT' &&
req.headers[':protocol'] === 'connect-udp') {
this.handleConnectUdp(req, res);
}
});
this.server.listen(port);
}
handleConnectUdp(req, res) {
// 解析目标地址
const path = req.headers[':path'];
const match = path.match(/\/udp\/([^/]+)\/(\d+)/);
if (!match) {
res.writeHead(400);
res.end();
return;
}
const [, targetHost, targetPort] = match;
// 创建UDP socket
const udpSocket = dgram.createSocket('udp4');
const socketId = `${targetHost}:${targetPort}`;
this.udpSockets.set(socketId, udpSocket);
// HTTP请求关闭时清理
req.on('close', () => {
udpSocket.close();
this.udpSockets.delete(socketId);
});
// 接收HTTP Datagram → 转发UDP
req.on('datagram', (data) => {
udpSocket.send(data, targetPort, targetHost);
});
// 接收UDP → 转发HTTP Datagram
udpSocket.on('message', (msg) => {
req.sendDatagram(msg);
});
// 响应200表示接受CONNECT
res.writeHead(200);
}
}
// 使用
const proxy = new WebRTCProxy(8443);
console.log('WebRTC Proxy running on port 8443');
参考文献
核心RFC:
- [RFC 9297] HTTP Datagrams and the Capsule Protocol ← 本文档
- [RFC 9221] An Unreliable Datagram Extension to QUIC
- [RFC 9298] Proxying UDP in HTTP
- [RFC 8441] Bootstrapping WebSockets with HTTP/2
相关标准:
- [RFC 9000] QUIC: A UDP-Based Multiplexed and Secure Transport
- [RFC 9114] HTTP/3
- [RFC 7540] Hypertext Transfer Protocol Version 2 (HTTP/2)
总结: HTTP Datagrams为HTTP引入了不可靠传输能力,填补了实时通信的空白。通过在HTTP/3中使用QUIC DATAGRAM扩展,或在HTTP/2中使用Capsule Protocol,应用可以在保持HTTP语义的同时享受低延迟传输的优势。这对WebRTC、实时游戏、IoT等场景具有重要意义。