現象
生產環境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 name is the identifier of a resource in a DNS database
- label is the part of a domain name in between dots
- hostname is 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服務商了。