Tomcat竟然有bug,这我能信?
Tomcat竟然有bug,这我能信?
背景介绍
为了解决分布式链路跟踪的问题,我们引入了实现Opentracing的Jaeger。然后我们为SpringBoot框架写了一个starter,让用户实现近零转换,访问整个链接。
因为公司有一个包装Springboot的内部框架,所以我们的starter是基于最新框架中使用的springboot版本开发的。因此,业务系统在接入时需要升级框架,然后引入我们的starter无缝接入整个链接。
故障描述
然后有一个业务系统按照步骤升级框架,引入starter连接到整个链路系统,功能测试压力测试已经通过。结果,我们自信地上线了。结果,在线nginx报告了大量httt系统400错误。
故障排查
故障发生后,业务系统的研发人员检查了所有日志,包括elk和机器上的日志,没有发现明显的错误日志。这是。
经过几次挣扎,我仍然没有在在线日志中找到任何线索。这更绝望了。更奇怪的是,测试环境正常,这很奇怪。
然后我们想知道之前的压力测试是否还不够。让我们在压力测试环境中进行压力测试,看看它是否会再次出现。然后,就在业务系统进行压力测试之前,然后迅速找到操作和维护来建立一个压力测试环境。结果,在施工完成后,400个错误被复制了。
然后运维学生就各种折腾,然后神奇地在nginx的location下加一行配置。
proxy_set_headerHOST$host
然后开始检查这个配置是什么意思。
该配置的主要内容是在nginn转发htp请求时添加实际的host请求头。如果http请求是http:////coc.co/hello,nginx将hts请求头(host:abc.com)转发给后台服务。对于ngin,如果没有proooass_sususususususususususususususususuthosthost。
然后我们在压测环境中尝试修改之前的版本,发现很正常。
nginx的配置大致如下:
总结当前现象:
nginx配置prsst_hearndx有hosthost版本,修改前的版本正常,修改后的本报400错误。在nginxyetheatheadprost之后。两个版本都是正常的。
我们修改了什么?
升级Sperter
然后我们试着去掉全链路starter的引用,发现还是400个错误。然后回到springboot版本,发现是正常的。
综上所述,由于SpringringBoot版本的升级和http头部变化引起的,因此可以大胆猜测这个问题是由tomcat版本的升级引起的。
tomcat版本从8.5.11升级到8.5.31
故障本地复现故障本地复现
从前面的分析可以看出,没有配置preadproy_sexthory_sexthost知时,upstream的名称在转发host请求时默认为host头部的内容。
也就是说,新版tomcat在接收host为sc_java(带下划线)的http请求中报告了400个错误。
让我们重现这个错误:
以下是8083和8084两个使用新版本tomcat的本地后台服务。
nginx配置如下。关键是upstream带下划线。
然后用postman要求nginx复制400个错误。
调整nginx配置,主要修改upstream无下划线。
然后要求,发现是正常的。
故障修复方案
退款tocc版本的成本在线修改nginx配置,配置poooossshead_head_head_head_heatetetetetetetetetetetetetetetetam。
根因分析
虽然我们知道故障的原因和如何修复故障。但我就是不知道为什么新版本的tomcat会出现这个问题。带着这个问题,我们组的同事在springboot项目的issue中搜索了以下400个问题,发现确实有相关的issue。
https:/github.com/spring-projects/spring-boot/issues/132366
虽然看起来问题是一样的,都是400问题,但具体原因是不一样的。issue是ddoainnainnainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainainain
但即使我使用最新版本的8.5.xtomcat,使用带有下划线的hosthttp请求tomcat,我仍然会报告400个错误。
也就是说,tomcat认为http请求有下划线,有问题。
那为什么之前版本的tomcat正常呢?带着这个问题,我们来分析一下tomcat的源代码。
由于之前没有看过tomcat的源代码,很难分析哪一行代码有问题,所以我查看了tomcat的相关bug
https:/bz.apache.org/bugzila/show_bug
以下是bug中的错误stack。
发现对应的代码改动如下:
ctso.com/com/20196/14/3744/3744f222b259c55c5e4a790cf1c9ccc9cf4c9d45.p3g“dorder=“0”wiuito“hitle=“hitle=”“heahetle=“heahetle=hethetortortordrorordrordrostle=”
在这里我们也知道处理Host头部的类就是这个Httparser类。
然后我在这个时候下了toca8.5.31th8.11代码,比较了httparser和abstractprocesor类。
对比结果如下:
发现AbstractbstractProcesor类中有一种parsehost方法,然后主要的分析方法是host.parse(valueMB);
在这里,我们已经知道为什么8.5.11版的tomcat正常,主要是因为8.5.11版的tomcat没有验证host的头部,而这种验证是在8.5.31版的tomcat中添加的。
让我们来看看tomcat源代码的提交记录:
#
根因之根因
那为什么tomcat增加了这个host的验证,不允许使用带下划线的host呢?事实上,这是标准的。单击此链接
https:/www.ietf.org/rfc/rfc1034
经验教训
嗯,我们在这里知道,事实上,tomcat遵循RFC1-1034的标准,因此tomcat的处理是正确的。然而,tomcat在处理其他合法的host时存在历史上的漏洞,但下划线的处理一直是正确的。
因此,以后配置ustream时,不能使用带下划线的名称,最好在locatin中添加pocatinx_seteadp后添加pocatinx_seteadphostreamphostream