使用wireshark抓包分析瀏覽器無法建立WebSocket連線的問題(server為Alchemy WebSockets元件)

桃子夭夭發表於2015-04-22

工作時使用了Websocket技術,在使用的過程中發現,瀏覽器(Chrome)升級後可能會導致Websocket不可用,更換瀏覽器後可以正常使用。

近日偶爾一次在本地除錯,發現使用相同版本的Chrome瀏覽器,不可連線線上伺服器的WS服務,但是可以連線本地的WS服務。 此時初步懷疑是伺服器在某種特殊情況下會觸發無法連線的問題。

 

使用Wireshark抓包

Filter:    ip.dst==serverIP or (ip.dst==本地IP and ip.src==serverIP)

一.檢視可以正常連線線上服務的瀏覽器的網路請求(搜狗高速核)

可以看到WebSocket連線建立的步驟:

1、先建立TCP連線,1~3條為tcp連線的三次握手

2、發出一個http請求,Header內容如下

GET /write?agentId=255 HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: 10.134.71.235:2015
Origin: http://10.134.71.235
Pragma: no-cache
Cache-Control: no-cache
Sec-WebSocket-Key: NddL4PEqgeUKIon0p+IHwQ==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits, x-webkit-deflate-frame
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36 SE 2.X MetaSr 1.0
Cookie: ASP.NET_SessionId=kdparak1ecjplo4erozul2yl; _un=zouchengzhuo@sogou-inc.com; id=77.NRe6bXSRddXY6INl1HMkRAdn7L4yIt4wcTGYu43q9r4; un=zouchengzhuo@sogou-inc.com; pw=70467311a7ed8f62b58f8f1d65cdb408
websocket連線http請求header內容

伺服器返回  

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: LDedYtTLpS6J7EygF4awEchi+D4=
websocket連線http請求返回

 

3.連線建立成功,使用WebSocket協議收發訊息

其中[FIN][MASKED] 為瀏覽器給server發訊息

[FIN]為server給瀏覽器發訊息,瀏覽器收到後發一個TCP的 [ACK] 包確認

 

正常的網路請求知道了,接下來伺服器不變,更換瀏覽器

 

二、檢視無法正常連線線上伺服器的網路請求(Chrome)

 

可以看到,HTTP請求發出去後,沒有收到101的回覆,伺服器直接發起了TCP連線斷開的流程。

懷疑是HTTP請求的內容不對。檢視請求header

GET /write?agentId=255 HTTP/1.1
Host: 10.134.71.235:2015
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://10.134.71.235
Sec-WebSocket-Version: 13
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Cookie: ASP.NET_SessionId=1rfwnghibfq2jvvjrlbflvl0; _un=zouchengzhuo@sogou-inc.com; id=77.NRe6bXSRddXY6INl1HMkRAdn7L4yIt4wcTGYu43q9r4; un=zouchengzhuo@sogou-inc.com; pw=IsQky+6I5U5zM89pna9UNNirBD9v74G5799FdJvrK78aLTw0mvq5icQJpNlCweeHTl646j88InE03ayWm4PpcA==
Sec-WebSocket-Key: kcEwLRS2BowYzsoYxGCQNw==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
View Code

和搜狗的包對比,好像沒有任何問題,接下來瀏覽器不變,更換伺服器

三、檢視可以正常連線的本地伺服器的網路請求(Chrome)

連線建立過程正常,就不用截圖了,主要關注HTTP請求header裡邊的內容

GET /write?agentId=255 HTTP/1.1
Host: 10.129.157.168:2015
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin:http://localhost:8317
Sec-WebSocket-Version: 13
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Sec-WebSocket-Key: ldwAY7BvJ6c0Gt9Xbh/R/Q==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
View Code

分析原因

分析發現,這個header裡邊沒有cookie的資料,推測有可能是http請求裡邊的cookie導致連線斷開。

chrome

這裡就發現了第一個奇怪的問題,Chrome為什麼有的時候會傳送cookie,有的時候不會呢?   

經過多次除錯發現:

1.關閉瀏覽器並重啟的第一次,使用m.venus.sogou-inc.com訪問,是可以正常使用的

2.無論何時,通過IP直接訪問伺服器都無法連線

3.只要用IP訪問過,用域名訪問也無法正常連線了

分析Cookie

用域名訪問時的Cookie

用IP訪問時的Cookie

公司對m.venus.sogou-inc.com的解析是經過了代理伺服器的,目前OP的nginx還不能轉發ws協議的請求

所以瀏覽器建立Websocket的方式 是  new WebSocket('ws://ip:port') 而不是 new WebSocket('ws://hostname:port')

重啟瀏覽器,用域名訪問的時候,header中

Host: 10.134.71.235:2015
Origin: http://m.venus.sogou-inc.com

這是一個跨域的http請求,所以請求中預設是不會帶上cookie的,chrome對在ws協議中的http請求中,顯然也是應用的這個預設設定。

用IP訪問的時候

Host: 10.134.71.235:2015
Origin: http://10.134.71.235

這是一個同域的請求,所以會帶上cookie。 第二個問題得到了解釋

一旦用ip訪問過,在此ip下就種下cookie了,而即使是在域名訪問的情況下,ws協議發出的http請求的host也是ip:port,所以也會傳送ip下的cookie。

可以看到cookies的生命週期都是session級別的,所以重啟瀏覽器後再用域名訪問是可以的

第一、三個問題得到了解釋

 

但是,搜狗瀏覽器的請求裡邊也包含了Cookie,為何搜狗卻可以正常連線伺服器呢?

搜狗高速核

 

用搜狗高速核多次試驗,發現:

1.搜狗高速核的cookie資料中沒有傳送sessionid

2.搜狗瀏覽器不管任何時候,都會傳送cookie

 

用域名訪問的cookie:

用IP訪問的cookie:

 

通過對比cookie的值發現,傳送的cookie是ip域下的cookie。

那麼這裡有兩個沒辦法解釋的問題

1.為什麼帶有httponly屬性的cookie,搜狗瀏覽器在傳送請求的時候不會發出去

2.為何關掉所有搜狗瀏覽器的程式後,生命週期為session的cookie沒有被幹掉

這是不是搜狗瀏覽器的兩個BUG呢?

 

到此時基本可以確定,就是因為cookie中帶了sessionid導致伺服器主動斷開連線。

 

雲平臺的Websocket伺服器是用Alchemy Websockets開發的。按照正常的邏輯,WebSocket伺服器中不應該用session去判斷使用者身份,因為它和Http不屬於同一個會話。

現在懷疑是因為Alchemy WebSockets元件的BUG導致此問題。

除錯Alchemy WebSockets原始碼

首先在本地IIS中釋出一個雲平臺伺服器,然後在VS中開啟一個除錯的服務,通過IP登入一下本地的雲平臺,然後通過localhost:除錯埠 訪問除錯的服務,以模擬ws連線的http請求中帶上cookie的情況。

從連線開始階段打上斷點單步除錯,到Handshake.cs類的時候發現一個方法:

 

        public bool IsValid()
        {
            return (
                       (Host != null) &&
                       (Key != null) &&
                       (Int32.Parse(Version) >= 8)
                   );
        }
View Code

 

在正常情況下此方法返回true,請求中帶上cookie且含有sessionId後返回false。原因是key==null。

抓包得到的header:

GET /write?agentId=261 HTTP/1.1
Host: 10.129.157.168:2015
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://localhost:8317
Sec-WebSocket-Version: 13
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Cookie: ASP.NET_SessionId=5qmgzihkxtvntxirmekbm2tv; _un=zouchengzhuo@sogou-inc.com; id=77.NRe6bXSRddXY6INl1HMkRAdn7L4yIt4wcTGYu43q9r4; un=zouchengzhuo@sogou-inc.com; pw=RGPCwqEawjg9JXHVZ/rLzM4Ac1+nDHlL2y1kKYp6PVLkZ5o/Oj5/OsP8t8vsg3D+djE3x0Q7zH/7ggw2Jme63A==
Sec-WebSocket-Key: TLjHvbrhmKqEK3sNPu7bnA==
Sec-WebSocket-Extensions: permessage-deflate; client_max_windo

 

可以看到Sec-Websocket-Key 是存在的。

除錯進入分析處理header的類裡邊,發現收到的header字串為

GET /write?agentId=261 HTTP/1.1
Host: 10.129.157.168:2015
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://localhost:8317
Sec-WebSocket-Version: 13
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Cookie: ASP.NET_SessionId=5qmgzihkxtvntxirmekbm2tv; _un=zouchengzhuo@sogou-inc.com; id=77.NRe6bXSRddXY6INl1HMkRAdn7L4yIt4wcTGYu43q9r4; un=
View Code

 

發現header少了一段。繼續分析這個元件的程式碼,發現

 

Handler.cs 中:

TCPServer中:

預設只讀取了512位元組的資料,把這裡改為一個足夠大的大小來測試一下,就沒問題了。

至此問題的原因找到了,Alchemy WebSockets在處理ws連線第二步——傳送http請求的時候,對http頭的解析方法有問題,導致丟掉了關鍵性的資料,瀏覽器中生成的 Ses-Websocket-Key,以至於伺服器認為這是個非法的連線請求,給幹掉了。

將處理header的地方修改為讀取所有資料再處理,就能解決這個問題。

 

但是除錯過程中還是留下了兩個疑問:

1.為什麼帶有httponly屬性的cookie,搜狗瀏覽器在傳送請求的時候不會發出去

2.為何關掉所有搜狗瀏覽器的程式後,生命週期為session的cookie沒有被幹掉

相關文章