29

用了这么久HTTP, 你是否了解Content-Length和Transfer-Encoding ?

 4 years ago
source link: https://www.tuicool.com/articles/7vEvEfJ
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

fmUri2E.jpg!web

九月 8, 2019 朴瑞卿 HTTP

目录

  • 用了这么久HTTP, 你是否了解Content-Length和Transfer-Encoding ?
      • Content-Length是如何工作的
      • Content-Length > 实际长度
      • Content-Length < 实际长度
    • 不确定Content-Length的值怎么办
    • 什么是Transfer-Encoding: chunked
      • Transfer-Encoding: chunked是如何工作的

用了这么久HTTP, 你是否了解Content-Length和Transfer-Encoding ?

由Content-Length导致的问题引发的一系列思考:

前段时间开发API网关, 使用postman调试时出现了超时的情况, 经排查确定是请求数据被处理后 Content-Length 与实际不一致导致的问题, 故有此文.

前言

Content-Length , HTTP消息长度, 用 十进制数字 表示的 八位字节的数目 . 一般情况下, 很多工作都被框架完成, 我们很少去关注这部分内容, 但少数情况下发生了 Content-Length 与实际消息长度不一致, 程序可能会发生比较奇怪的异常, 如:

  • 无响应直到超时.
  • 请求被截断, 而且下一个请求解析出现错乱.

什么是Content-Length

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

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

Content-Length是如何工作的

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

IF3EF3r.jpg!web

3A7B3mU.jpg!web

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

Content-Length > 实际长度

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

MZ7jqua.jpg!web

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

nuqQ3qB.jpg!web

QjQVJza.jpg!web

Content-Length < 实际长度

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

E3QnMvA.jpg!web

zaiYFvM.jpg!web

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

BJ3Ab2y.jpg!web

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

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

mEr2IfE.jpg!web

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

[版权声明]

本文发布于朴瑞卿的博客, 允许非商业用途转载, 但转载必须保留原作者朴瑞卿 及链接: https://blog.piaoruiqing.com .

如有授权方面的协商或合作, 请联系邮箱: [email protected] .

不确定Content-Length的值怎么办

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

什么是Transfer-Encoding: chunked

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

Transfer-Encoding: chunked是如何工作的

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

r63qmqU.jpg!web

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

eea26vJ.jpg!web

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

VVnQFji.jpg!web

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

结语

  • Content-Length 如果存在且生效, 必须是正确的, 否则会发生异常.(大于实际值会超时, 小于实际值会截断并可能导致后续的数据解析混乱)
  • 如果报文中包含 Transfer-Encoding: chunked 首部, 那么 Content-Length 将被忽略.

参考文献

© 2019,朴瑞卿.

[版权声明]

本文发布于朴瑞卿的博客, 允许非商业用途转载, 但转载必须保留原作者朴瑞卿 及链接: https://blog.piaoruiqing.com . 如有授权方面的协商或合作, 请联系邮箱: [email protected] .

欢迎关注公众号: (代码如诗) iYFFBbB.jpg!web

0

相关


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK