[机房]Tomcat竟然有bug,这我能信?

来源: 作者: 发布时间:2021-12-30 10:19:03

背景介绍

为了解决分布式链路跟踪的问题,我们引入了实现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