5

HTTP2 下的 Transfer-Encoding: chunked

 1 year ago
source link: https://blog.p2hp.com/archives/9747
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.
neoserver,ios ssh client

在 HTTP 中传输数据有一个 chunked 的方式, 又称“分块传输”。在响应报文里用头字段Transfer-Encoding: chunked 来表示。意思是报文里的 body 部分不是一次性发过来的,而是分成了许多的块(chunk)逐个发送。而 HTTP2.0 协议作为 HTTP协议的升级,自然是对chunked模式做支持?不然!

HTTP2 是没有 chunked 的!

分块传输也可以用于“流式数据”,例如由数据库动态生成的表单页面,这种情况下 body 数据的长度是未知的,无法在头字段“Content-Length”里给出确切的长度,所以也只能用 chunked 方式分块发送。

chunked 的编码规则

  • 每个分块包含两个部分,长度头和数据块;
  • 长度头是以 CRLF(回车换行,即rn)结尾的一行明文,用 16 进制数字表示长度;
  • 数据块紧跟在长度头后,最后也用 CRLF 结尾,但数据不包含 CRLF;
  • 最后用一个长度为 0 的块表示结束,即“0rnrn”
http2_chunked.webp

HTTP2 下的分块传输

先说结论,HTTP2 是不支持 HTTP1 语义下的 chunked 模式的。因为H2的 Data 帧是纯天然的Chunked模式。

这也是最容易出bug的地方,一些实现不完全的HTTP2开源库经常在这里出问题。因为我们线上都是客户端请求基本都是 chunked 模式,升级到 HTTP2 之后,经常访问一些三方链接访问卡死,最终 debug 后的原因发现出在服务端对 HTTP2 chunked  的支持上。

最常见的反向代理实现 Nginx 就是最容易有这种 bug的,很多企业维护的Nginx经常不更新,而低版本的Nginx在HTTP2上的这个bug就被我们遇到过。

具体现象是,客户端使用chunked模式上传,但是服务端开启了 HTTP2,自然客户端也就升级到 HTTP2 。但是请求总是卡主,服务端无响应。最终定位到如果 DATA帧不携带内容,只携带一个 End Stream 标志,服务端无法识别流式传输结束。我拿 www.bing.com 做了对比,其他网站都是正常的。

http2_chunked_bug.png

其实 RFC 7540 早有规定,HTTP2 的传输是不支持 Transfer-Encoding: chunked 的。

http2_chunked_rfc.png

一般的网络库(如 Cronet、OKHTTP )底层都给我们做了兼容,如果上层使用 chunked 模式传输,而实际使用的是 HTTP2 ,网络库会帮我们自动隐藏掉 Transfer-Encoding: chunked 这个 Header 。

使用 HTTP2 发送 chunked

这里我们以 Golang 的 HTTP2 官方的客户端库做一个测试.

通过导入 SSLKEYLOG 从 Wireshark 抓包可以看到,每个 Data Frame 大小为 8000 。

http2_chunked_wireshark.png

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK