ContentLength和TransferEncoding详解

什么是Content-Length?

Content-Length是HTTP消息长度,用十进制数字表示的八位字节的数字,是Headers中常见的一个字段。Content-Length应该是精确的,否则就会导致异常 ( HTTP1.0中这个字段可有可无)。

Content-Length字段指出报文中实体主体的字节大小。这个大小是包含了所有内容编码的。比如, 对文本文件进行了gzip压缩的话,Content-Length首部指的就是压缩后的大小而不是原始大小。

Content-Length是如何工作的

Content-Length使用十进制的数字表示了消息的长度,服务端/客户端通过它来得知后续要读取消息的长度。

如果这个长度不正确, 会发生如下情况:

Content-Length > 实际长度

如果Content-Length比实际的长度大,服务端/客户端读取到消息结尾后,会等待下一个字节,自然会无响应直到超时。

 同样地,在响应消息中Content-Length超过实际长度也是一样的效果:

Content-Length < 实际长度

如果这个长度小于实际长度,首次请求的消息会被截取,比如参数为param=piaoruiqing,Content-Length为10,那么这次请求的消息会被截取为: param=piao,如图所示:

 但,仅仅是如此吗,当然不,我们再来看看第二次请求会发生什么让人意外的事情,如图:

连续的两次请求,第一次消息被截断,而第二次没有发生预期的截断,而是服务端抛出了异常: Request method 'ruiqingPOST' not supported。刺不刺激?

那 ruiqingPOST是个什么神仙方法??? 此时,凭着多年开发(DEBUG)经验练就的敏感度,我们大致可以猜出,上一次请求被截取剩下的消息,在这次请求出现了。掏出wireshark来验证一下,如图:

 导致这种情况的原因就是开启了Connection:keep-alive,如果使用Connection:close,所产生的现象就是每一次的请求都被截断,但不会产生解析混乱(如将上一次剩下的消息拼接到后续的请求消息中)。

不确定Content-Length的值怎么办?

Content-Length首部指示出报文中实体主体的字节大小。但如在请求处理完成前无法获取消息长度,我们就无法明确指定Content-Length,此时应该使用Transfer-Encoding: chunked 

什么是Transfer-Encoding: chunked

数据以一系列分块的形式进行发送。Content-Length 头部在这种情况下不会被设置发送。在每一个分块的开头需要添加当前分块的长度,以十六进制的形式表示,后面紧跟着 \r\n,之后是分块本身,后面也是\r\n。终止块是一个常规的分块,不同之处在于其长度为0。

Transfer-Encoding: chunked是如何工作的

接下来我们用一个下载文件的例子,来探讨Transfer-Encoding: chunked是如何工作的。服务端代码如下:

 使用postman发起请求,wireshark抓包查看,如图:

 在wireshark中可以很清晰地看到chunked的数据,其结构大致是:返回的消息被分为多个数据块,每个数据块有两部分,长度 + 数据,这两部分都以CRLF(即\r\n)结尾。而终止块是一个特殊的数据块,其长度为0,如图:

 如此,即完成了分块编码。其主要应用于如下场景,即要传输大量的数据,但是在请求在没有被处理完之前响应的长度是无法获得的。例如,当需要用从数据库中查询获得的数据生成一个大的HTML表格、需要传输大量的图片等。

PS:

Content-Length如果需要存在且生效,那么他的值必须是正确的,否则会发生异常。(大于实际值会超时,小于实际值会截断并可能导致后续的数据解析混乱)

如果报文中包含Transfer-Encoding: chunked首部, 那么Content-Length将被忽略。

原文地址:https://www.cnblogs.com/yyxianren/p/15798145.html