一次使用自定义 Http Header 引发的血案
HttpClient Http Header 自定义 nginx 不转发
起因
最近在整理我们产品的 OpenAPI Demo (Python、C#、Java),为使各语言 Demo 表现一致,使用同样的测试数据和同样的请求封装方式。
在 Python、C# 都特别顺利写完后,Java 遇到问题了:其中有一个接口返回 HTTP 400 错误,而其他接口都正常
环境
- JDK 1.8
- JAVA 调用接口使用 Apache HttpClient 4.2.1
排查
疑因1
由于 HTTP 400 Bad Request 很明显的是客户请求不满足服务端的要求,是客户端的问题,所以最先怀疑是客户端参数没传。
经过再三确认后,确认参数传递没有问题。
疑因2
排除疑因1后,有点陷入死局,不知如何下手。最后,有可能是请求通过 nginx 转发时,nginx 破坏了请求,导致应用服务器报 400.
于是,登录服务器,查看 Nginx 日志,发现请求没有被转发,如下图:
与正常的的请求相比,如下图:
未被转发的请求缺少了 Http Header : Host,也就是说错误的请求的 Host Header 被客户端丢弃,通过 Wireshark 抓包也确认是客户端的请求中没有 Host Header
疑因3
通过排除疑因2的过程,明确了问题出在请求调用方,而请求方式都是统一的封装接口,那么最有可能的原因就是请求的参数不一致导致。
于是,逐个去掉该请求的参数,发现其中一个 key 为 “productName”, value 为“测试修改产品名称” 的参数导致。将 value 改为其它值进行测试,发现没有问题,于是逐个去掉 value 的汉字,发现只要去掉“名”字就可测试通过。
这就奇了怪了,为什么有“名”字就测试不通过呢,又为什么其它语言能测试通过?!!!
思考。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
然后,对比有“名”字和无“名”字两次 wireshark 的抓包。
哇哇哇,为什么有“名”字的 Header 有换行
然后,将“名”字转换成 Unicode ,其值为 \u540d
ASCII 转换
然后对照 ASCCI 码表发现,发现 0D 表示“回车键”
ASCCI 码表
至此就清楚了,请求封装的代码将请求数据也放到了 Header ,而恰巧“名”字导致了 Header 头不合规范,从而导致 Host Header 丢失。而对比其它语言的请求封装类,并没有将请求数据放到 Header 中,这也解释了其它语言为什么可以测试通过。
经查 OpenAPI 的协议文档,并没有要求将请求数据放到 Header,而当时为什么这么做了,也无从考查。。。。。。
总结
虽然这次问题解决了,但却花费了半天的时间,在排查问题的时候,还是要再细心一些,多抓包并仔细分析,多对比正常情况与异常情况的不同,可能会事半功倍。
谨记!!!