http响应chunked格式分析
HTTP 响应 Chunked 格式分析
背景与原理
当服务器无法事先确定 HTTP 响应内容的具体大小(例如动态生成的内容)时,无法在响应头中预先写入 Content-Length。此时,服务器通常采用 Chunked 编码(分块传输编码)来实时生成消息长度。
在进行 Chunked 编码传输时,响应消息头中会包含 Transfer-Encoding 字段并定义为 chunked,表示内容将通过 Chunked 编码进行传输。
数据结构与格式
Chunked 编码由若干个 Chunk(数据块)串联而成,并由一个长度为 0 的 Chunk 标示结束。每个 Chunk 分为头部和正文两部分:
- 头部:指定下一段正文的字符总数(十六进制数字)和数量单位(通常省略)。
- 正文:指定长度的实际内容。
- 分隔:头部与正文之间用回车换行(CRLF,
\r\n)隔开。 - 结束:最后一个长度为 0 的 Chunk 之后可能包含称为 Footer(尾部)的内容,这是一些附加的 Header 信息(通常可以直接忽略)。
模拟数据结构如下:
[Chunk 大小][CRLF][Chunk 数据体][CRLF][Chunk 大小][CRLF][Chunk 数据体][CRLF][0][CRLF][Footer 内容(可选)][CRLF]注意:
chunk-size是以十六进制的 ASCII 码表示的。例如25(十六进制)对应的十进制长度为 37,表示随后的数据体长度为 37 字节。- 在跟踪
www.yahoo.com的返回数据时曾发现,chunk-size字段中有时会包含多余空格。这可能是为了固定长度(如 7 字节),不满则以空格(ASCII 0x20)补足。但这属于特定服务器的实现细节,并非标准强制要求。
解码流程
对 Chunked 编码进行解码的目的是将分块的 chunk-data 整合恢复成完整的报文体,同时计算此块体的总长度。
参考 RFC2616 附带的解码流程(伪代码):
length := 0 // 长度计数器置 0
read chunk-size, chunk-extension // 读取 chunk-size, chunk-extension
read CRLF // 和 CRLF
while (chunk-size > 0) { // 表明不是 last-chunk
read chunk-data and CRLF // 读 chunk-size 大小的 chunk-data, skip CRLF
append chunk-data to entity-body // 将此块 chunk-data 追加到 entity-body 后
read chunk-size and CRLF // 读取新 chunk 的 chunk-size 和 CRLF
}
read entity-header // entity-header 格式为 name:value CRLF,如果为空即只有 CRLF
while (entity-header not empty) { // 即,不是只有 CRLF 的空行
append entity-header to existing header fields
read entity-header
}
Content-Length := length // 将解码流程结束后计算得到的新报文体 length
// 作为 Content-Length 域的值写入报文中
Remove "chunked" from Transfer-Encoding // 同时从 Transfer-Encoding 域值中去除 chunked 标记示例分析
1. 编码后的响应 (Encoded Response)
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
25
This is the data in the first chunk
1A
and this is the second one
0
2. 原始字节十六进制视图 (Raw Bytes in Hex)
0000-000F 48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d HTTP/1.1 200 OK.
0010-001F 0a 43 6f 6e 74 65 6e 74 2d 54 79 70 65 3a 20 74 .Content-Type: t
0020-002F 65 78 74 2f 70 6c 61 69 6e 0d 0a 54 72 61 6e 73 ext/plain..Trans
0030-003F 66 65 72 2d 45 6e 63 6f 64 69 6e 67 3a 20 63 68 fer-Encoding: ch
0040-004F 75 6e 6b 65 64 0d 0a 0d 0a 32 35 0d 0a 54 68 69 unked....25..Thi
0050-005F 73 20 69 73 20 74 68 65 20 64 61 74 61 20 69 6e s is the data in
0060-006F 20 74 68 65 20 66 69 72 73 74 20 63 68 75 6e 6b the first chunk
0070-007F 0d 0a 0d 0a 31 41 0d 0a 61 6e 64 20 74 68 69 73 ....1A..and this
0080-008F 20 69 73 20 74 68 65 20 73 65 63 6f 6e 64 20 6f is the second o
0090-009F 6e 65 0d 0a 30 0d 0a 0d 0a ne..0....3. Java 代码模拟 (Java Code Simulation)
public static final byte[] CHUNKED_RESPONSE;
static {
StringBuilder sb = new StringBuilder();
sb.append("HTTP/1.1 200 OK\r\n");
sb.append("Content-Type: text/plain\r\n");
sb.append("Transfer-Encoding: chunked\r\n\r\n");
sb.append("25\r\n");
sb.append("This is the data in the first chunk\r\n"); // 37 bytes of payload
// (conveniently consisting of ASCII characters only)
sb.append("\r\n1A\r\n");
sb.append("and this is the second one"); // 26 bytes of payload
// (conveniently consisting of ASCII characters only)
sb.append("\r\n0\r\n\r\n");
CHUNKED_RESPONSE = sb.toString().getBytes(java.nio.charset.Charset.forName("US-ASCII"));
}4. 解码后的数据 (Decoded Data)
This is the data in the first chunk
and this is the second one说明
- 本文引用的解码流程基于 RFC2616,该协议已被 RFC7230 及后续的 RFC9112 取代,但 Chunked 编码的核心机制保持一致。
- 文中关于 Yahoo 服务器固定长度补空格的观察属于特定历史案例,实际开发中应以 RFC 标准为准,不应依赖此类非标准行为。
- 以上就是 HTTP 响应中 Chunked 编码方式的基本分析。
版权声明:本文为原创文章,版权归 戴老师的博客 所有,转载请联系博主获得授权。
本文地址:https://1diff.fun/archives/http-xiang-ying-chunked-ge-shi-fen-xi.html
如果对本文有什么问题或疑问都可以在评论区留言,我看到后会尽量解答。