HTTP协议字段Content-Encoding、Transfer-Encoding

2,462 阅读4分钟

1、Content-Encoding

Accept-Encoding 和 Content-Encoding 是 HTTP中用来对「采用何种编码格式传输正文」进行协定的一对头部字段。它的工作原理是这样:向浏览器发送请求时,通过 Accept-Encoding 带上自己支持的内容编码格式列表;服务端从中挑选一种用来对正文进行编码,并通过 Content-Encoding 响应头指明选定的格式;浏览器拿到响应正文后,依据 Content-Encoding 进行解压。当然,服务端也可以返回未压缩的正文,但这种情况不允许返回 Content-Encoding。这个过程就是 HTTP 的内容编码机制。

内容编码目的是优化传输内容大小,通俗地讲就是进行压缩。一般经过 gzip 压缩过的文本响应,只有原始大小的 1/4。对于文本类响应是否开启了内容压缩,是我们做性能优化时首先要检查的重要项目;而对于 JPG / PNG 这类本身已经高度压缩过的二进制文件,不推荐开启内容压缩,效果微乎其微还浪费 CPU。

内容编码针对的只是传输正文。在 HTTP/1 中,头部始终是以 ASCII 文本传输,没有经过任何压缩。这个问题在 HTTP/2 中得以解决,HTTP/2 头部压缩技术。

内容编码使用特别广泛,理解起来也很简单,随手打开一个网页抓包看下请求响应就能明白。唯一要注意的是不要把它与 HTTP 中的另外一个概念:传输编码(Transfer-Encoding)搞混即可。

2、Transfer-Encoding

Transfer-Encoding 则是用来改变报文格式,它不但不会减少实体内容传输大小,甚至还会使传输变大。

2.1、HTTP长连接

HTTP 运行在 TCP 连接之上,自然也有着跟 TCP 一样的三次握手、慢启动等特性,为了尽可能的提高 HTTP 性能,使用持久连接就显得尤为重要了。HTTP/1.0 的持久连接机制是后来才引入的,通过 Connection: keep-alive 这个头部来实现,服务端和客户端都可以使用它告诉对方在发送完数据之后不需要断开 TCP 连接,以备后用。HTTP/1.1 则规定所有连接都必须是持久的,除非显式地在头部加上 Connection: close。浏览器重用已经打开的空闲持久连接,可以避开缓慢的三次握手,还可以避免遇上 TCP 慢启动的拥塞适应阶段。

2.2、Content-Length

对于非持久连接,浏览器可以通过连接是否关闭来界定请求或响应实体的边界;而对于持久连接,这种方法显然不奏效。在一次长连接请求时,尽管已经发送完所有数据,但浏览器并不知道这一点,它无法得知这个打开的连接上是否还会有新数据进来,只能傻傻地等了。浏览器可以通过 Content-Length 的长度信息,判断出响应实体已结束。通常如果 Content-Length 比实际长度短,会造成内容被截断;如果比实体内容长,会造成 pending。由于 Content-Length 字段必须真实反映实体长度,但实际应用中,有些时候实体长度并没那么好获得,例如实体来自于网络文件,或者由动态语言生成。这时候要想准确获取长度,只能开一个足够大的 buffer,等内容全部生成好再计算。但这样做一方面需要更大的内存开销,另一方面也会让客户端等更久。

2.3 分块编码

我们需要一个新的机制:不依赖头部的长度信息,也能知道实体的边界——分块编码(Transfer-Encoding: chunked)。

分块传输编码(Chunked transfer encoding)是超文本传输协议(HTTP)中的一种数据传输机制,允许HTTP由网页服务器发送给客户端的数据可以分成多个部分。分块传输编码只在HTTP协议1.1版本(HTTP/1.1)中提供。

数据分解成一系列数据块,并以一个或多个块发送,这样服务器可以发送数据而不需要预先知道发送内容的总大小。

在头部加入 Transfer-Encoding: chunked 之后,就代表这个报文采用了分块编码。这时,报文中的实体需要改为用一系列分块来传输。
每个分块包含十六进制的长度值和数据,长度值独占一行,长度不包括它结尾的 CRLF(\r\n),也不包括分块数据结尾的 CRLF。
最后一个分块长度值必须为 0,对应的分块数据没有内容,表示实体结束。
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked

25\r\n
This is the data in the first chunk\r\n

1C\r\n
and this is the second one\r\n

3\r\n
con\r\n

8\r\n
sequence\r\n

0\r\n
\r\n

Content-Encoding 和 Transfer-Encoding 二者经常会结合来用,其实就是针对 Transfer-Encoding 的分块再进行 Content-Encoding压缩。

参考

HTTP 协议中的 Content-Encoding

HTTP 协议中的 Transfer-Encoding