6. Message Body (消息体)
HTTP/1.1 消息的消息体 (message body) (如果有) 用于承载请求或响应的内容 (content) ([HTTP] 第 6.4 节)。消息体与内容相同,除非已应用传输编码,如第 6.1 节所述。
message-body = *OCTET
确定 HTTP/1.1 消息中是否存在消息体的规则对于请求和响应是不同的。
请求中消息体的存在由 Content-Length 或 Transfer-Encoding 头字段发出信号。请求消息帧与方法语义无关。
响应中消息体的存在(如第 6.3 节所述)取决于它所响应的请求方法和响应状态码。这对应于 HTTP 语义允许响应内容的时间 ([HTTP] 第 6.4.1 节)。
6.1. Transfer-Encoding (传输编码)
Transfer-Encoding 头字段列出了已应用(或将要应用)于内容以形成消息体的传输编码名称序列。传输编码在第 7 节中定义。
Transfer-Encoding = #transfer-coding
; defined in [HTTP], Section 10.1.4
Transfer-Encoding 类似于 MIME 的 Content-Transfer-Encoding 字段,后者旨在通过 7 位传输服务安全传输二进制数据 ([RFC2045] 第 6 节)。然而,对于 8 位清洁传输协议,安全传输有不同的关注点。在 HTTP 的情况下,Transfer-Encoding 主要用于准确界定动态生成的内容。它还用于区分仅在传输中应用的编码与所选表示的特征编码。
接收方必须 (MUST) 能够解析分块传输编码 (第 7.1 节),因为当内容大小事先未知时,它在消息帧中起着关键作用。发送方禁止 (MUST NOT) 对消息体多次应用分块传输编码(即不允许对已分块的消息进行分块)。如果将除分块之外的任何传输编码应用于请求的内容,发送方必须 (MUST) 将分块作为最终传输编码应用,以确保消息被正确地帧化。如果将除分块之外的任何传输编码应用于响应的内容,发送方必须 (MUST) 将分块作为最终传输编码应用,或者通过关闭连接来终止消息。
例如:
Transfer-Encoding: gzip, chunked
表示内容在形成消息体时已使用 gzip 编码进行压缩,然后使用分块编码进行分块。
与 Content-Encoding ([HTTP] 第 8.4.1 节) 不同,Transfer-Encoding 是消息的属性,而不是表示的属性。请求/响应链上的任何接收方可以 (MAY) 解码收到的传输编码或对消息体应用额外的传输编码,前提是对 Transfer-Encoding 字段值进行相应的更改。有关编码参数的额外信息可以由本规范未定义的其他头字段提供。
Transfer-Encoding 可以 (MAY) 在对 HEAD 请求的响应中或在对 GET 请求的 304 (Not Modified) 响应 ([HTTP] 第 15.4.5 节) 中发送(两者都不包含消息体),以指示如果请求是无条件 GET,源服务器将对消息体应用传输编码。但是,不需要此指示,因为响应链上的任何接收方(包括源服务器)可以在不需要时删除传输编码。
服务器禁止 (MUST NOT) 在状态码为 1xx (Informational) 或 204 (No Content) 的任何响应中发送 Transfer-Encoding 头字段。服务器禁止 (MUST NOT) 在对 CONNECT 请求的任何 2xx (Successful) 响应 ([HTTP] 第 9.3.6 节) 中发送 Transfer-Encoding 头字段。
收到它不理解的传输编码的请求消息的服务器应该 (SHOULD) 响应 501 (Not Implemented)。
Transfer-Encoding 在 HTTP/1.1 中添加。通常假设仅声明支持 HTTP/1.0 的实现不会理解如何处理传输编码的内容,并且使用 Transfer-Encoding 接收的 HTTP/1.0 消息可能在传输过程中未正确处理分块传输编码而被转发。
客户端禁止 (MUST NOT) 发送包含 Transfer-Encoding 的请求,除非它知道服务器将处理 HTTP/1.1 请求(或更高次要修订版本); 这种知识可能是特定用户配置的形式,或通过记住先前收到的响应的版本。服务器禁止 (MUST NOT) 发送包含 Transfer-Encoding 的响应,除非相应的请求指示 HTTP/1.1(或更高次要修订版本)。
Transfer-Encoding 的早期实现有时会为消息帧发送分块传输编码和用于进度条的估计 Content-Length 头字段。这就是为什么 Transfer-Encoding 被定义为覆盖 Content-Length,而不是它们相互不兼容。不幸的是,如果任何下游接收方未能根据本规范解析消息,特别是当下游接收方仅实现 HTTP/1.0 时,转发此类消息可能会导致有关请求走私 (第 11.2 节) 或响应拆分 (第 11.1 节) 攻击的漏洞。
服务器可以 (MAY) 拒绝同时包含 Content-Length 和 Transfer-Encoding 的请求,或仅根据 Transfer-Encoding 处理此类请求。无论如何,服务器必须 (MUST) 在响应此类请求后关闭连接,以避免潜在的攻击。
收到包含 Transfer-Encoding 头字段的 HTTP/1.0 消息的服务器或客户端必须 (MUST) 将该消息视为帧错误,即使存在 Content-Length,并在处理消息后关闭连接。消息发送方可能在缓冲区中保留了消息的一部分,该部分可能被进一步使用连接误解。
6.2. Content-Length (内容长度)
当消息没有 Transfer-Encoding 头字段时,Content-Length 头字段 ([HTTP] 第 8.6 节) 可以提供潜在内容的预期大小,作为十进制八位字节数。对于确实包含内容的消息,Content-Length 字段值提供了确定数据(和消息)在哪里结束所需的帧信息。对于不包含内容的消息,Content-Length 指示所选表示的大小 ([HTTP] 第 8.6 节)。
发送方禁止 (MUST NOT) 在包含 Transfer-Encoding 头字段的任何消息中发送 Content-Length 头字段。
注意: HTTP 对 Content-Length 用于消息帧的使用与该字段在 MIME 中的使用有显著不同,在 MIME 中,它是一个可选字段,仅在 "message/external-body" 媒体类型中使用。
6.3. Message Body Length (消息体长度)
消息体的长度由以下之一确定(按优先顺序):
-
对 HEAD 请求的任何响应以及具有 1xx (Informational)、204 (No Content) 或 304 (Not Modified) 状态码的任何响应始终由头字段后的第一个空行终止,无论消息中存在哪些头字段,因此不能包含消息体或尾部部分。
-
对 CONNECT 请求的任何 2xx (Successful) 响应意味着连接将在结束头字段的空行之后立即成为隧道。客户端必须 (MUST) 忽略在此类消息中收到的任何 Content-Length 或 Transfer-Encoding 头字段。
-
如果收到的消息同时具有 Transfer-Encoding 和 Content-Length 头字段,则 Transfer-Encoding 覆盖 Content-Length。此类消息可能表明尝试执行请求走私 (第 11.2 节) 或响应拆分 (第 11.1 节),应作为错误处理。选择转发消息的中介必须 (MUST) 首先删除收到的 Content-Length 字段,并在向下游转发消息之前处理 Transfer-Encoding(如下所述)。
-
如果存在 Transfer-Encoding 头字段且分块传输编码 (第 7.1 节) 是最终编码,则通过读取和解码分块数据直到传输编码指示数据完成来确定消息体长度。
如果响应中存在 Transfer-Encoding 头字段且分块传输编码不是最终编码,则通过读取连接直到服务器关闭来确定消息体长度。
如果请求中存在 Transfer-Encoding 头字段且分块传输编码不是最终编码,则无法可靠地确定消息体长度; 服务器必须 (MUST) 响应 400 (Bad Request) 状态码,然后关闭连接。
-
如果收到的消息没有 Transfer-Encoding 且具有无效的 Content-Length 头字段,则消息帧无效,接收方必须 (MUST) 将其视为不可恢复的错误,除非可以成功将字段值解析为逗号分隔列表 ([HTTP] 第 5.6.1 节),列表中的所有值都有效,并且列表中的所有值都相同(在这种情况下,使用该单个值作为 Content-Length 字段值处理消息)。如果不可恢复的错误在请求消息中,服务器必须 (MUST) 响应 400 (Bad Request) 状态码,然后关闭连接。如果它在代理收到的响应消息中,代理必须 (MUST) 关闭到服务器的连接,丢弃收到的响应,并向客户端发送 502 (Bad Gateway) 响应。如果它在用户代理收到的响应消息中,用户代理必须 (MUST) 关闭到服务器的连接并丢弃收到的响应。
-
如果存在有效的 Content-Length 头字段而没有 Transfer-Encoding,则其十进制值定义预期的消息体长度(以八位字节为单位)。如果发送方在收到指示的八位字节数之前关闭连接或接收方超时,接收方必须 (MUST) 将消息视为不完整并关闭连接。
-
如果这是请求消息且上述都不为真,则消息体长度为零(不存在消息体)。
-
否则,这是没有声明的消息体长度的响应消息,因此消息体长度由服务器关闭连接之前收到的八位字节数确定。
由于没有办法区分成功完成的、关闭界定的响应消息和被网络故障中断的部分接收消息,因此服务器应该 (SHOULD) 尽可能生成编码或长度界定的消息。关闭界定功能主要用于与 HTTP/1.0 的向后兼容性。
注意: 请求消息永远不会关闭界定,因为它们总是由长度或传输编码明确帧化,两者都不存在意味着请求在头部分之后立即结束。
服务器可以 (MAY) 通过响应 411 (Length Required) 来拒绝包含消息体但没有 Content-Length 的请求。
除非已应用除分块之外的传输编码,否则如果消息体长度事先已知,发送包含消息体的请求的客户端应该 (SHOULD) 使用有效的 Content-Length 头字段而不是分块传输编码,因为某些现有服务即使理解分块传输编码也会以 411 (Length Required) 状态码响应分块。这通常是因为此类服务是通过网关实现的,该网关需要在被调用之前提前知道内容长度,并且服务器无法或不愿意在处理之前缓冲整个请求。
发送包含消息体的请求的用户代理必须 (MUST) 发送有效的 Content-Length 头字段或使用分块传输编码。客户端禁止 (MUST NOT) 使用分块传输编码,除非它知道服务器将处理 HTTP/1.1(或更高)请求; 这种知识可以是特定用户配置的形式,或通过记住先前收到的响应的版本。
如果连接上最后一个请求的最终响应已完全接收且仍有额外数据要读取,用户代理可以 (MAY) 丢弃剩余数据或尝试确定该数据是否属于先前消息体的一部分,如果先前消息的 Content-Length 值不正确,可能就是这种情况。客户端禁止 (MUST NOT) 将此类额外数据作为单独的响应处理、缓存或转发,因为此类行为容易受到缓存中毒攻击。