消息体长度
Transfer-Encoding、Content-Length 与连接关闭——七条成帧规则的判定优先级。
作者 David更新于 第 5 篇(共 22 篇)
消息体长度不由空行决定,而由下面这条七步判定链决定。RFC 9112 §6.3 规定接收方 MUST 按顺序求值,命中即停。
判定链
- 响应对应 HEAD 请求,或状态码为
1xx/204/304→ 无消息体。头里即使有Content-Length也不读体(该值描述的是「若是 GET 会返回多长」)。 - 响应对应 CONNECT 且为
2xx→ 无消息体;连接自此转为隧道,后续字节不再按 HTTP 解析。 - 存在
Transfer-Encoding:- 若最后一个编码是
chunked→ 按分块解码确定长度。 - 若
chunked不在最后(或不存在)→ 响应:读到连接关闭为止;请求:MUST 返回 400 并关闭连接(无法确定边界,走私风险)。
- 若最后一个编码是
- 存在多个
Content-Length,或单个含多个值且不一致 → MUST 拒绝(400 / 502)。 - 存在合法单一
Content-Length→ 该十进制值即长度。 - 请求 → 无消息体(请求不能靠连接关闭定界)。
- 响应 → 读到连接关闭为止。
Transfer-Encoding 优先于 Content-Length
两者同时出现时:Transfer-Encoding 生效,Content-Length MUST 被视为无效并移除。这是防走私的关键——但更安全的做法是直接拒绝该报文:
POST / HTTP/1.1
Host: x
Content-Length: 6
Transfer-Encoding: chunked
0
前端若按 Content-Length 读、后端按 chunked 读,两者对「报文在哪结束」产生分歧,攻击者可把后续字节注入成下一个请求(CL.TE / TE.CL 走私)。规范要求代理拒绝此类报文。
Content-Length
Content-Length = 1*DIGIT- 十进制,无正负号、无前导
+、不接受十六进制或前导0x。前导零语法上合法但不应生成。 - 单位是字节,且是编码后(应用
Content-Encoding之后)的长度。 - 值必须与实际体字节数完全一致:
- 实际体短于声明 → 接收方视为消息不完整,MUST 当作出错(连接不可复用)。
- 实际体长于声明 → 多出的字节被当作下一个报文的开头(走私)。
- 响应可在无体时仍带
Content-Length: 0;HEAD 与 304 的Content-Length是元信息,见判定链第 1 条。
chunked 传输编码
chunked-body = *chunk last-chunk trailer-section CRLF
chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
chunk-size = 1*HEXDIG
last-chunk = 1*("0") [ chunk-ext ] CRLF一个实例:
HTTP/1.1 200 OK
Transfer-Encoding: chunked
7
Mozilla
9
Developer
0
要点:
chunk-size是十六进制,大小写不敏感,不带0x。与十进制的Content-Length正好相反,是高频错误点。chunk-size不含 CRLF 与 chunk-ext 本身的长度。- 每个
chunk-data之后必须跟 CRLF。 - 长度为 0 的块表示结束,其后是可选的 trailer 区,再跟一个 CRLF 收尾。
- 解码后的字节流才是真正的消息体;
Content-Length若与之共存则无效。
chunk-ext
chunk-ext = *( BWS ";" BWS ext-name [ BWS "=" BWS ext-val ] )无标准化的扩展名,接收方 MUST 忽略不认识的扩展。实践中几乎不用,且部分实现对超长 chunk-ext 有拒绝策略(DoS 防护)。
编码栈的顺序
Transfer-Encoding = #transfer-coding
transfer-coding = token *( OWS ";" OWS transfer-parameter )已注册值:chunked、compress、deflate、gzip、trailers(后者仅用于 TE)。
- 列表顺序即应用顺序:
Transfer-Encoding: gzip, chunked表示先 gzip 再分块;接收方逆序解码。 chunkedMUST 是请求中的最后一个编码,且不得重复出现。- 客户端不得向 HTTP/1.0 接收方发送
Transfer-Encoding。 - HTTP/2 与 HTTP/3 中该字段被禁用(成帧由协议层负责)。
Transfer-Encoding vs Content-Encoding
高频混淆点,二者正交:
Transfer-Encoding | Content-Encoding | |
|---|---|---|
| 层次 | 传输层,逐跳 | 表示层,端到端 |
| 是否影响成帧 | 是 | 否 |
| 是否改变资源本身 | 否,接收即还原 | 是,ETag 针对编码后的表示 |
| 代理可否改动 | 可以(可重新分块) | 不可 |
| 典型值 | chunked | gzip、br、zstd |
Content-Length 计的是 Content-Encoding 之后、Transfer-Encoding 之前的长度。
TE 与 trailers
TE = #t-codings
t-codings = "trailers" / ( transfer-coding [ weight ] )客户端用 TE 声明自己能接受哪些传输编码;TE: trailers 表示愿意接收 trailer 区。TE 是逐跳字段,必须在 Connection 中列出:Connection: TE。
下一篇:连接管理——持久连接、Connection 字段与逐跳语义。