一次線(xiàn)上websocket返回400問(wèn)題排查的實(shí)戰(zhàn)記錄
現(xiàn)象
生產(chǎn)環(huán)境websocket無(wú)法正常連接,服務(wù)端返回400 bad request,開(kāi)發(fā)及測(cè)試環(huán)境均正常。

抓包排查
src:nginx服務(wù)器 172.16.177.193
dst:imp應(yīng)用服務(wù)器 172.16.177.218

問(wèn)題定位
觀察到header中的host值帶有下劃線(xiàn),在一些中間件(如kafka、hadoop)中,對(duì)host中的特殊字符也有限制。由此猜測(cè)是header問(wèn)題。
經(jīng)排查,此header來(lái)自nginx的upstream

解決方案1
修改nginx的upstream配置,去除下劃線(xiàn)
解決方案2
既然upstream中用了下劃線(xiàn),為何普通的http請(qǐng)求正常,而websocket則返回400呢?
再看正常的http請(qǐng)求的抓包
src: nginx服務(wù)器 172.31.47.151
dst: imp應(yīng)用服務(wù)器 172.31.47.153

可見(jiàn),header中的host,被轉(zhuǎn)發(fā)到了目標(biāo)服務(wù)器,而此host并沒(méi)有下劃線(xiàn),正常請(qǐng)求。
所以,如果不修改nginx中的upstream下劃線(xiàn)的配置,其實(shí)還可以在server中的websocket 對(duì)應(yīng)的location中,添加參數(shù),以便轉(zhuǎn)發(fā)原始請(qǐng)求的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;

配置后的抓包如下

請(qǐng)求正常
原因探討
帶有下劃線(xiàn)的host,為何會(huì)被服務(wù)端返回400?從上述排查過(guò)程可知,應(yīng)是tomcat返回的。
這一切要從一個(gè)RFC協(xié)議規(guī)范說(shuō)起
RFC-1034是一個(gè)關(guān)于DNS及域名基礎(chǔ)的標(biāo)準(zhǔn)。在「3.5 Preferred name syntax」中,有提及主機(jī)名的格式規(guī)范:由字母開(kāi)頭,字母或數(shù)字結(jié)尾,中間包含字母、數(shù)字或橫桿

可見(jiàn),RFC-1034標(biāo)準(zhǔn)中下劃線(xiàn)并不被允許。
而Tomcat在一次8.x的升級(jí)中遵循該標(biāo)準(zhǔn)對(duì)host做了此校驗(yàn),詳見(jiàn)鏈接
org.apache.coyote.AbstractProcessor#parseHost
org.apache.tomcat.util.http.parser.Host#parse(java.io.Reader)

總結(jié)
在我們?nèi)粘E渲胔ost參數(shù)時(shí),都盡量避免使用下劃線(xiàn)。包括在/etc/hosts下進(jìn)行的配置,有時(shí)在集群環(huán)境中為了方便管理,會(huì)配置host,此時(shí)也應(yīng)注意避免使用下劃線(xiàn)。因?yàn)橹虚g件可能也遵循了RTC規(guī)范。
延伸閱讀
如果大家閱讀了上述規(guī)范,可能會(huì)有疑問(wèn):為何網(wǎng)易163可以是數(shù)字開(kāi)頭?大家可以先去了解下域名的解析過(guò)程。
此處科普幾個(gè)基本概念。
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庫(kù)中的唯一標(biāo)識(shí),label是域名中以「.」分隔的單元,hostname是對(duì)于一個(gè)地址的特殊域名映射。
在1989年的RFC-1101中的「3.1 Network name syntax」定義了網(wǎng)絡(luò)名詞的DNS編碼規(guī)范。允許數(shù)字開(kāi)頭,只要不與十進(jìn)制八位字節(jié)形式的ip地址沖突。
在1997年的RFC-2181中「11 Name syntax」對(duì)域名語(yǔ)法作了澄清,下劃線(xiàn)不應(yīng)被DNS服務(wù)所拒絕。
而在近年的一次CA/B論壇里,眾多大廠發(fā)起投票,禁止在域名中使用下劃線(xiàn),否則SSL證書(shū)將不能正常申請(qǐng)使用。
有興趣的朋友可以嘗試搭建一個(gè)網(wǎng)站并作域名映射??梢源_定的是,做域名映射時(shí),因?yàn)槭莌ostname,A和MX記錄是不能帶下劃線(xiàn)的。CNAME記錄由于針對(duì)的是域名,是否支持則取決于你的DNS服務(wù)商了。
到此這篇關(guān)于一次線(xiàn)上websocket返回400問(wèn)題排查的文章就介紹到這了,更多相關(guān)websocket返回400問(wèn)題內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring學(xué)習(xí)之開(kāi)發(fā)環(huán)境搭建的詳細(xì)步驟
本篇文章主要介紹了Spring學(xué)習(xí)之開(kāi)發(fā)環(huán)境搭建的詳細(xì)步驟,具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07
說(shuō)說(shuō)Java異步調(diào)用的幾種方式
本文主要介紹了說(shuō)說(shuō)Java異步調(diào)用的幾種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
Java中自動(dòng)裝箱、拆箱引起的耗時(shí)詳解
這篇文章主要給大家介紹了關(guān)于Java中自動(dòng)裝箱、拆箱引起的耗時(shí)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
SpringBoot配置MySQL5.7與MySQL8.0的異同點(diǎn)詳解
MySQL 是 Java 開(kāi)發(fā)中最常用的數(shù)據(jù)庫(kù)之一,而 Spring Boot 提供了便捷的配置方式,隨著 MySQL 8.0 的普及,許多開(kāi)發(fā)者需要從 MySQL 5.7 升級(jí)到 8.0,在實(shí)際開(kāi)發(fā)中,二者的配置方式既有相似之處,也有一些需要特別注意的不同點(diǎn),所以本文給大家詳細(xì)介紹了它們的異同點(diǎn)2024-12-12
springboot 配置日志 打印不出來(lái)sql的解決方法
這篇文章主要介紹了springboot 配置日志 打印不出來(lái)sql的解決方法,幫助大家更好的理解和使用springboot框架,感興趣的朋友可以了解下2020-11-11
Java8內(nèi)存模型PermGen Metaspace實(shí)例解析
這篇文章主要介紹了Java8內(nèi)存模型PermGen Metaspace實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
Java實(shí)現(xiàn)超市會(huì)員管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)超市會(huì)員管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03

