记录一次对获取body理解有误导致的bug
需求场景
我需要对http请求的body
体做flter处理,以及拿到request的body做验签,之后http的request body又会参与二次或者多次的使用,例如透明传递给backend做转发,流量拷贝等.
名词解释
backend: 这个backend是我需要将流量(http 请求)复制给后端server,然后拿到后端server的结果返回给前端,整个过程你可以理解为是gateway
或者类似nginx的porxy
所以这里的backend指的是: 后端server或者后端server的一些特征属性组成的一个实例.
bug复现
前端同学反馈给我,说backend
提供了一个post请求的接口,直接访问backend的接口是200的status,且没有respose body,但是在网关层访问返回500.
查找bug思路第一步:
第一步,赶紧找log,所以log内容如下:
net/http: HTTP/1.x transport connection broken: http: ContentLength=30 with Body length 0
从日志看来是body的内容指定了content-length
,但是是空body对应一个非0值的content-length
查找bug思路第二步->误入歧途:
这里我的第一脑补是backend的结果是空导致的,是不是backend返回给我了一个空body的情况下content-length是非0?
所以我就开始直接构造请求到backend
结果是:backend 返回没有问题, 到此我开始了阅读roundtrip
源代码的漫长之路,可是整个过程很复杂,涉及到异步,涉及到http 协议的底层.
查找bug思路的第三步->回归现象:
无奈,源码阅读之后并无头绪,所以重新回归现象,仔细分析
其实log中可以看出来,说明以及很明确了,一定是body内容和content-length头不一致导致的,所以这里反思之后觉得是不是请求body的问题?
一旦想到这一点,我便去构造不同的body来验证length长度,果然length长度随着请求的body不同而不同
查找bug思路第四步->初见端倪:
一定是body被某一层的代码读走了,而roundtrip依然要使用,所以开始了各个flter层的查看,最后找到罪魁祸首:
bodyString, _ := ctx.GetRawData()
果然body被拿走,而没有回写,导致roundtrip拿不到body了
解决bug:
找到原因后,解bug不足一分钟
思路:回写request body到ctx中
bodyString, _ := ctx.GetRawData()
ctx.Request.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(bodyString)))
总结:
当你读io的时候。读取时,它会把它读取完。一旦你阅读它,内容就消失了。你不能再读一遍。
io.reader 接近与水龙头:你可以得到水,但一旦它出来了,它就出来了。