4. 传输编码
传输编码 (transfer coding) 名称用于表示一种编码变换: 为了确保在网络上 "安全传输", 这种变换已经被、可以被、或者可能需要对载荷主体施加. 它与内容编码的不同之处在于: 传输编码是消息的属性, 而不是正在传输的表示的属性.
transfer-coding = "chunked" ; Section 4.1
/ "compress" ; Section 4.2.1
/ "deflate" ; Section 4.2.2
/ "gzip" ; Section 4.2.3
/ transfer-extension
transfer-extension = token *( OWS ";" OWS transfer-parameter )
参数的形式是一个名称, 或者一个 name=value 对.
transfer-parameter = token BWS "=" BWS ( token / quoted-string )
所有传输编码名称都不区分大小写, 并且应当按照第 8.4 节的定义在 HTTP Transfer Coding 注册表中登记. 它们用于 TE (第 4.3 节) 和 Transfer-Encoding (第 3.3.1 节) 头部字段.
4.1. 分块传输编码
分块传输编码 (chunked transfer coding) 把载荷主体包装起来, 以便把它作为一系列块 (chunk) 来传输, 每块都有自己的大小指示符, 之后跟一个可选的、包含头部字段的尾部 (trailer). 分块编码使得大小未知的内容流能够作为一序列以长度界定的缓冲区来传输, 从而让发送方能够保持连接持久性, 也让接收方知道何时已收到完整消息.
chunked-body = *chunk
last-chunk
trailer-part
CRLF
chunk = chunk-size [ chunk-ext ] CRLF
chunk-data CRLF
chunk-size = 1*HEXDIG
last-chunk = 1*("0") [ chunk-ext ] CRLF
chunk-data = 1*OCTET ; a sequence of chunk-size octets
chunk-size 字段是一串十六进制数字, 以八位字节为单位指明 chunk-data 的大小. 当收到一个 chunk-size 为零的块时, 分块传输编码即告完成; 其后可能跟有一个尾部, 并最终以一个空行终止.
接收方 MUST 能够解析并解码分块传输编码.
4.1.1. 块扩展
分块编码允许每个块在 chunk-size 之后紧接着包含零个或多个块扩展 (chunk extension), 用于提供每块元数据 (例如签名或散列)、消息中途的控制信息, 或者对消息主体大小的随机化.
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
chunk-ext-name = token
chunk-ext-val = token / quoted-string
分块编码是特定于每条连接的, 并且在任何上层应用有机会检查这些扩展之前, 它们很可能已被每个接收方 (包括中间方) 移除或重新编码. 因此, 块扩展的使用通常仅限于专门的 HTTP 服务, 例如 "长轮询" (此时客户端和服务器可以对块扩展的使用有共同预期), 或者用于端到端安全连接中的填充.
接收方 MUST 忽略无法识别的块扩展. 服务器应当把请求中收到的块扩展总长度限制在与其所提供服务相称的合理范围内, 就像它对消息其他部分施加长度限制和超时一样; 如果超过该量, 则应生成相应的 4xx (Client Error) 响应.
4.1.2. 分块尾部分
尾部 (trailer) 允许发送方在分块消息的末尾包含额外字段, 以提供可能在消息主体发送期间动态生成的元数据, 例如消息完整性校验、数字签名或后处理状态. 尾部字段与头部字段相同, 区别只在于它们是在分块尾部中发送, 而不是在消息的头部部分中发送.
trailer-part = *( header-field CRLF )
发送方 MUST NOT 生成这样的尾部: 其中包含对消息分帧必需 (例如 Transfer-Encoding 和 Content-Length)、对路由必需 (例如 Host)、作为请求修饰符 (例如 [RFC7231] 第 5 节中的控制和条件)、用于认证 (例如见 [RFC7235] 和 [RFC6265])、作为响应控制数据 (例如见 [RFC7231] 第 7.1 节), 或者用于判定如何处理载荷 (例如 Content-Encoding、Content-Type、Content-Range 和 Trailer) 的字段.
当收到包含非空尾部的分块消息时, 接收方 MAY 把这些字段 (除上文中禁止的那些之外) 当作已被追加到消息头部部分来处理. 接收方 MUST 忽略 (或者视为错误) 那些禁止在尾部中发送的字段, 因为把它们当作存在于头部部分来处理可能绕过外部安全检查.
除非请求中包含表明可接受 "trailers" 的 TE 头部字段 (如第 4.3 节所述), 否则服务器 SHOULD NOT 生成它认为用户代理需要接收的尾部字段. 在没有包含 "trailers" 的 TE 时, 服务器应当假定这些尾部字段可能在通往用户代理的路径上被静默丢弃. 该要求允许中间方把已解除分块的消息转发给 HTTP/1.0 接收方, 而无需缓冲整个响应.
4.1.3. 分块解码
解码分块传输编码的过程可以用伪代码表示如下:
length := 0
read chunk-size, chunk-ext (if any), and CRLF
while (chunk-size > 0) {
read chunk-data and CRLF
append chunk-data to decoded-body
length := length + chunk-size
read chunk-size, chunk-ext (if any), and CRLF
}
read trailer field
while (trailer field is not empty) {
if (trailer field is allowed to be sent in a trailer) {
append trailer field to existing header fields
}
read trailer-field
}
Content-Length := length
Remove "chunked" from Transfer-Encoding
Remove Trailer from existing header fields
4.2. 压缩编码
下面定义的编码可用于压缩消息的载荷.
4.2.1. Compress 编码
"compress" 编码是一种自适应 Lempel-Ziv-Welch (LZW) 编码 [Welch], 通常由 UNIX 文件压缩程序 "compress" 产生. 接收方 SHOULD 把 "x-compress" 视为与 "compress" 等价.
4.2.2. Deflate 编码
"deflate" 编码是一种 "zlib" 数据格式 [RFC1950], 其中包含一个 "deflate" 压缩数据流 [RFC1951], 后者使用 Lempel-Ziv (LZ77) 压缩算法与 Huffman 编码的组合.
注意: 有些不符合规范的实现发送的 "deflate" 压缩数据不带 zlib 包装.
4.2.3. Gzip 编码
"gzip" 编码是一种带 32 位循环冗余校验 (Cyclic Redundancy Check, CRC) 的 LZ77 编码, 通常由 gzip 文件压缩程序产生 [RFC1952]. 接收方 SHOULD 把 "x-gzip" 视为与 "gzip" 等价.
4.3. TE
请求中的 "TE" 头部字段表明客户端愿意在响应中接受分块之外的哪些传输编码, 以及客户端是否愿意接受分块传输编码中的尾部字段.
TE 字段值由一个以逗号分隔的传输编码名称列表和/或关键词 "trailers" 组成, 其中每个传输编码名称都允许带有可选参数 (如第 4 节所述). 客户端 MUST NOT 在 TE 中发送分块传输编码的名称; 对 HTTP/1.1 接收方而言, 分块始终是可接受的.
TE = #t-codings
t-codings = "trailers" / ( transfer-coding [ t-ranking ] )
t-ranking = OWS ";" OWS "q=" rank
rank = ( "0" [ "." 0*3DIGIT ] )
/ ( "1" [ "." 0*3("0") ] )
下面是三个 TE 用法示例.
TE: deflate
TE:
TE: trailers, deflate;q=0.5
关键词 "trailers" 的出现表明: 客户端代表自己以及任何下游客户端, 愿意接受分块传输编码中的尾部字段 (如第 4.1.2 节所定义). 对于来自中间方的请求, 这意味着二者之一: (a) 所有下游客户端都愿意接受被转发响应中的尾部字段; 或者 (b) 中间方将代表下游接收方尝试缓冲该响应. 请注意, HTTP/1.1 没有定义任何限制分块响应大小的手段, 以使中间方能够确保缓冲整个响应.
当多种传输编码都可接受时, 客户端 MAY 使用不区分大小写的 "q" 参数按偏好对编码排序 (类似于内容协商字段中使用的 qvalue, [RFC7231] 第 5.3.1 节). 排序值是一个 0 到 1 之间的实数, 其中 0.001 表示最不偏好, 1 表示最偏好; 值为 0 表示 "不可接受".
如果 TE 字段值为空, 或者不存在 TE 字段, 则唯一可接受的传输编码就是分块. 不带任何传输编码的消息始终是可接受的.
由于 TE 头部字段只适用于直接相连的那条连接, TE 的发送方 MUST 同时在 Connection 头部字段 (第 6.1 节) 中发送 "TE" 连接选项, 以防止 TE 字段被不支持其语义的中间方转发.
4.4. Trailer
当消息包含以分块传输编码编码的消息主体, 且发送方希望在消息末尾以尾部字段的形式发送元数据时, 发送方 SHOULD 在消息主体之前生成一个 Trailer 头部字段, 以表明尾部中将出现哪些字段. 这使接收方能够在开始处理主体之前为接收这些元数据做好准备; 当消息正在以流的方式传输, 且接收方希望即时确认完整性校验时, 这很有用.
Trailer = 1#field-name