一次使用自定義 Http Header 引發的血案
起因
最近在整理我們產品的 OpenAPI Demo (Python、C#、Java),為使各語言 Demo 表現一致,使用同樣的測試數據和同樣的請求封裝方式。
在 Python、C# 都特別順利寫完后,Java 遇到問題了:其中有一個接口返回 HTTP 400 錯誤,而其他接口都正常
環境
- JDK 1.8
- JAVA 調用接口使用 Apache HttpClient 4.2.1
排查
疑因1
由於 HTTP 400 Bad Request 很明顯的是客戶請求不滿足服務端的要求,是客戶端的問題,所以最先懷疑是客戶端參數沒傳。
經過再三確認后,確認參數傳遞沒有問題。
疑因2
排除疑因1后,有點陷入死局,不知如何下手。最后,有可能是請求通過 nginx 轉發時,nginx 破壞了請求,導致應用服務器報 400.
於是,登錄服務器,查看 Nginx 日志,發現請求沒有被轉發,如下圖:
與正常的的請求相比,如下圖:
未被轉發的請求缺少了 Http Header : Host,也就是說錯誤的請求的 Host Header 被客戶端丟棄,通過 Wireshark 抓包也確認是客戶端的請求中沒有 Host Header
疑因3
通過排除疑因2的過程,明確了問題出在請求調用方,而請求方式都是統一的封裝接口,那么最有可能的原因就是請求的參數不一致導致。
於是,逐個去掉該請求的參數,發現其中一個 key 為 “productName”, value 為“測試修改產品名稱” 的參數導致。將 value 改為其它值進行測試,發現沒有問題,於是逐個去掉 value 的漢字,發現只要去掉“名”字就可測試通過。
這就奇了怪了,為什么有“名”字就測試不通過呢,又為什么其它語言能測試通過?!!!
思考。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
然后,對比有“名”字和無“名”字兩次 wireshark 的抓包。
哇哇哇,為什么有“名”字的 Header 有換行
然后,將“名”字轉換成 Unicode ,其值為 \u540d

然后對照 ASCCI 碼表發現,發現 0D 表示“回車鍵”

至此就清楚了,請求封裝的代碼將請求數據也放到了 Header ,而恰巧“名”字導致了 Header 頭不合規范,從而導致 Host Header 丟失。而對比其它語言的請求封裝類,並沒有將請求數據放到 Header 中,這也解釋了其它語言為什么可以測試通過。
經查 OpenAPI 的協議文檔,並沒有要求將請求數據放到 Header,而當時為什么這么做了,也無從考查。。。。。。
總結
雖然這次問題解決了,但卻花費了半天的時間,在排查問題的時候,還是要再細心一些,多抓包並仔細分析,多對比正常情況與異常情況的不同,可能會事半功倍。
謹記!!!