chunked编码格式引发的问题
1、问题
在适配某家的cas时,票据校验时一直失败,通常是以下几个问题引发的问题
1、网关与cas服务器的网络不通,导致票校验的请求发送不出去
2、cas服务器的票据校验接口,配置了域名,但是在nginx没有配置dns,导致域名无法解析,请求无法发送
3、配置了ip,但是cas服务端限制了host,禁用了ip访问
4、票据校验的请求有问题,service参数组装不正确
经过排查后,发现都不是上面的原因,因此抓了一个包,发现cas服务端是正常把响应返回回来的
2、代码分析
2.1、定位
既然cas服务端正常返回了响应,那就是网关侧有异常,代码打印状态码和body,发现状态码确实200,但是body是nil,也就是没有获取到body。我们采用了agentzh” Yichun“ 写的lua-resty-http模块(https://github.com/liseen/lua-resty-http)
1 |
|
其中接收响应body的代码是
1 |
|
所以我们重点分析receivebody
1 |
|
抓包查看,响应的编码格式是 Transfer-Encoding:chunked,因此确认是走到了上述代码解析chunked的逻辑,在关键代码打印日志后,发现这里返回了错误,读取body失败
1 |
|
那么为什么读取会失败?
2.2、什么是chunked类型的数据?
HTTP 中的 Chunked
传输编码是一种在不预先知道响应数据大小的情况下进行数据传输的方式。通常在 HTTP 响应中,服务器会在头部发送 Content-Length
字段,指定即将发送的数据的总长度。然而,有时候服务器在生成响应内容时并不知道最终的内容长度,比如当内容是动态生成的。这时候,服务器可以使用 Chunked
传输编码。
Chunked 传输编码的基本工作原理
在 Chunked
传输编码中,响应体被分割成一系列块(chunks),每个块可以有不同的大小。每个块由两部分组成:
- 块头部(Chunk Header): 该部分指定了块的大小(以16进制表示),后面紧跟着一个回车换行符(\r\n)。
- 数据块(Chunk Data): 紧跟在块头部之后的实际数据,数据块的长度由块头部指定。块数据后面也跟着一个回车换行符(\r\n)。
响应的最后一个块是一个特殊的块,它的大小为 0,表示数据传输的结束。
客户端解析 Chunked 数据
客户端接收到 Chunked
编码的响应时,会逐块解析数据,直到遇到大小为 0
的块。具体的解析步骤如下:
- 读取块头部,确定当前块的大小。
- 读取指定大小的数据块。
- 继续读取下一个块,重复步骤 1 和 2。
- 当遇到大小为
0
的块时,停止读取。
常规的内容传输
在通常情况下,当服务器要发送响应时,会先计算好整个响应体的大小,并在 HTTP 响应头的 Content-Length
字段中告知客户端。例如,服务器在发送一个 HTML 页面时,可能会先生成整个页面内容,并且计算出它的大小,然后在响应头中设置 Content-Length
,再将整个内容一次性发送给客户端。
这种方式的缺点是:
- 延迟高:服务器必须等待整个内容生成完毕,才能开始发送。这增加了初始延迟。
- 不适合动态生成的内容:如果内容是逐步生成的(例如,来自数据库的查询结果或通过流处理的内容),服务器需要等到所有内容都生成后才能计算总大小并发送。
Chunked
传输编码的优势
使用 Chunked
传输编码,服务器不需要预先知道响应内容的总大小。相反,它可以在生成内容的同时逐步将内容以一块块的形式发送给客户端。
2.3、分析
了解了原理,那我们来看下服务端返回的数据是否正常,使用tcp追踪流展示数据
十六进制:
数据很明显有问题,让我们回到代码,只有读到一个0,才会认为数据结束了,也就是已经接收到了完整的数据,但是现在不是一个0,是0000,从十六进制看也很明显,我们需要读取到 30 od oa才会认为数据结束,但是现在是30 30 30 30 od oa。因此客户端会一直读数据,而从请求的响应头connection:close 可以知道,这个连接在传输完数据后会关闭。因此当连接关闭时, local data, err, partial = chunk_header()最后会报错,err其实是一个close。
1 |
|
所以到这里,问题其实可以确认是cas返回的数据有问题
3、问题解决
3.1、本地验证
联系了cas的服务商,被告知这个cas是拿开源来用的,掌握程度一般,沟通下来比较困难🙄,刚好我对这个问题比较感兴趣,因此拿对应开源版本做一个验证
cas地址:https://github.com/apereo/cas/tree/5.3.x?tab=readme-ov-file
安装参考文档:https://www.cnblogs.com/hellxz/p/15740935.html
tomact:https://dlcdn.apache.org/tomcat/tomcat-9/v9.0.93/bin/
cas的war包:https://repo1.maven.org/maven2/org/apereo/cas/cas-server-webapp-tomcat/
简单运行后,成功对接我们的网关,那么抓包看一下返回的数据
数据格式非常正确!
3.2、最终的结论
那么现在有问题的场景,还是cas做了一定的改动?cas是用java实现的,这块对我有一定的研究成本,研究了下源码,最后追踪到了视图,但是没找到具体哪里可以设置传输编码,后面有时间再研究一下,还是先把问题还给服务商。