一次线上websocket返回400问题排查的实战记录
目录
- 现象
- 抓包排查
- 问题定位
- 解决方案1
- 解决方案2
- 原因探讨
- 总结
现象
生产环境websocket无法正常连接,服务端返回400 bad request,开发及测试环境均正常。
抓包排查
src:nginx服务器 172.16.177.193
dst:imp应用服务器 172.16.177.218
问题定位
观察到header中的host值带有下划线,在一些中间件(如kafka、hadoop)中,对host中的特殊字符也有限制。由此猜测是header问题。
经排查,此header来自nginx的upstream
解决方案1
修改nginx的upstream配置,去除下划线
解决方案2
既然upstream中用了下划线,为何普通的http请求正常,而websocket则返回400呢?
再看正常的http请求的抓包
src: nginx服务器 172.31.47.151
dst: imp应用服务器 172.31.47.153
可见,header中的host,被转发到了目标服务器,而此host并没有下划线,正常请求。
所以,如果不修改nginx中的upstream下划线的配置,其实还可以在server中的websocket 对应的location中,添加参数,以便转发原始请求的host
proxy_set_header Host $host; proxy_set_header X-real-ip $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
配置后的抓包如下
请求正常
原因探讨
带有下划线的host,为何会被服务端返回400?从上述排查过程可知,应是tomcat返回的。
这一切要从一个RFC协议规范说起
RFC-1034是一个关于DNS及域名基础的标准。在「3.5 Preferred name syntax」中,有提及主机名的格式规范:由字母开头,字母或数字结尾,中间包含字母、数字或横杆
可见,RFC-1034标准中下划线并不被允许。
而Tomcat在一次8.x的升级中遵循该标准对host做了此校验,详见链接
org.apache.coyote.AbstractProcessor#parseHost
org.apache.tomcat.util.http.parser.Host#parse(java.io.Reader)
总结
在我们日常配置host参数时,都尽量避免使用下划线。包括在/etc/hosts下进行的配置,有时在集群环境中为了方便管理,会配置host,此时也应注意避免使用下划线。因为中间件可能也遵循了RTC规范。
延伸阅读
如果大家阅读了上述规范,可能会有疑问:为何网易163可以是数字开头?大家可以先去了解下域名的解析过程。
此处科普几个基本概念。
domain nameis the identifier of a resource in a DNS databaselabelis the part of a domain name in between dotshostnameis a special type of domain name which identifies Internet hosts
域名是在dns库中的唯一标识,label是域名中以「.」分隔的单元,hostname是对于一个地址的特殊域名映射。
在1989年的RFC-1101中的「3.1 Network name syntax」定义了网络名词的DNS编码规范。允许数字开头,只要不与十进制八位字节形式的ip地址冲突。
在1997年的RFC-2181中「11 Name syntax」对域名语法作了澄清,下划线不应被DNS服务所拒绝。
而在近年的一次CA/B论坛里,众多大厂发起投票,禁止在域名中使用下划线,否则SSL证书将不能正常申请使用。
有兴趣的朋友可以尝试搭建一个网站并作域名映射。可以确定的是,做域名映射时,因为是hostname,A和MX记录是不能带下划线的。CNAME记录由于针对的是域名,是否支持则取决于你的DNS服务商了。
到此这篇关于一次线上websocket返回400问题排查的文章就介绍到这了,更多相关websocket返回400问题内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!