记一次http pipeline异常
背景
服务依赖关系
storage Client{
storage OkHttp
storage Netty
}
storage Server{
storage Control
storage Tomcat
}
OkHttp->Netty
Netty.>Tomcat
Tomcat -> Control
现象
每天偶现5次左右API调用返回400现象, 查询Server端日志:
[DEBUG] [io.undertow.server.protocol.http.HttpReadListener sendBadRequestAndClose 284] - UT005014: Failed to parse request i in request-targetdRequestException: UT000165: Invalid character
at io.undertow.server.protocol.http.HttpRequestParser.handlePath(HttpRequestParser.java:382) ~[undertow-core-1.4.26.Final.jar!/:1.4.26.Final]
at io.undertow.server.protocol.http.HttpRequestParser.handle(HttpRequestParser.java:248) ~[undertow-core-1.4.26.Final.jar!/:1.4.26.Final]
at io.undertow.server.protocol.http.HttpReadListener.handleEventWithNoRunningRequest(HttpReadListener.java:187) [undertow-core-1.4.26.Final.jar!/:1.4.26.Final]
at io.undertow.server.protocol.http.HttpReadListener.handleEvent(HttpReadListener.java:136) [undertow-core-1.4.26.Final.jar!/:1.4.26.Final]
at io.undertow.server.protocol.http.HttpReadListener.handleEvent(HttpReadListener.java:59) [undertow-core-1.4.26.Final.jar!/:1.4.26.Final]
排查流程
根据日志异常提示,报文中存在非法字符,两方面排查:
- 服务端容器对HTTP协议解析有问题
- 客户端发过来的请求存在非法字符
首先尝试换容器,使用tomcat替换undertow, 线上运行测试,发现问题依然存在,开始抓包
抓包
抓包结果如下图,出现6次400请求:

使用tcp follow 查看一下报文,并没有发现异常情况:

具体查看一下报文:

查看上面报文发现请求中携带content-length
继续看看body中的内容:

发现content中的是下一个http 请求(请求B), 请求B的一部分被放到上一个请求中,导致Server端无法解析请求B,报出非法字符,返回400 请求
继续查看一下http follow情况,如下图, 请求B的一部分:

验证之前的分析,这个请求不是Pipeline,正常的pipeline格式如下:
echo -en "GET /index.html HTTP/1.1\r\nHost: example.com\r\n\r\nGET /other.html HTTP/1.1\r\nHost: example.com\r\n\r\n" | nc example.com 80
处理方法
- 升级OKHttp到4.0
- 不使用OKHttp ,使用HttpComponent