之前兩次singnalr、 websocket實時推送相關:
??.NET WebSockets 核心原理初體驗[1]
??SignalR 從開發到生產部署避坑指南[2]
tag:瀏覽器--->nginx--> server
其中提到nginx默認不會為客戶端轉發Upgrade
、Connection
標頭, 因為為了讓被代理的后端服務器知道客戶端要升級協議,故要在nginx上顯式轉發標頭:
location?/realtime/?{proxy_pass?http://backend;proxy_http_version?1.1;proxy_set_header?Upgrade?$http_upgrade;proxy_set_header?Connection?"upgrade";
}
事情本該就就這么簡單, 但devops總會有各種奇怪的姿勢。
小動作引起的頭腦風暴
但是運維在給nginx配置的時候,給/
根路徑配置了webcoket協議升級標頭。
按照字面理解,導致所有的客戶端轉發請求都在要求切換到websocket協議,但是除了/chat路徑, 服務器其他http路徑并沒有做websocket協議的邏輯,其他http請求是不是都該報錯了。

是實際看,所有的請求(websocket、http)都沒有報錯,都按照指定預期返回。
刨一下
利用asp.netcore默認腳手架項目:
已知http://localhost:5000/WeatherForecast
是http請求,返回一大坨json數據;
在WeatherForecast
添加斷言日志:

模擬ops的錯配效果,我們給這個請求添加websocket協議升級標頭。
第一次:curl 'http://localhost:5000/WeatherForecast' -H 'Upgrade: websocket' -H 'Connection: Upgrade' --verbose
,正常返回大坨json數據。
日志記錄:
該請求是不是webcocket請求:False,headers:[Accept, */*], [Connection, Upgrade], [Host, localhost:5000], [User-Agent, curl/7.79.1], [Upgrade, websocket]
以上說明,服務端并不認為是websocket請求, 這也印證了ops雖然錯配,但對于常規的http請求沒造成影響。
那服務端到底是怎么認定websocket請求?
從服務端認定websocket請求的源碼[3]看

依次判斷;
??HttpMethod: GET
??Sec-WebSocket-Version標頭==13
??Connection標頭==Upgrade
??Upgrade標頭==websocket
??有效的Sec-WebSocket-Key標頭
這樣我們就明白了,雖然websocket協議基于http,添加了httpConnection
、Upgrade
標頭,但是瀏覽器實際會給我們帶上Sec-WebSocket-Key
[4]、Sec-WebSocket-Version
標頭,以向服務器證明這是一個有效的websocket握手。
于是我們可以使用?curl 'http://localhost:5000/WeatherForecast' -H 'Upgrade: websocket' -H 'Connection: Upgrade' -H 'Sec-WebSocket-Version: 13' -H 'Sec-webSocket-Key: eeZn6lg/rOu8QbKwltqHDA==' --verbose
?仿造客戶端websocket請求。
日志記錄:
該請求是不是webcocket請求:True,headers:[Accept, */*], [Connection, Upgrade], [Host, localhost:5000], [User-Agent, curl/7.79.1], [Upgrade, websocket], [Sec-WebSocket-Version, 13], [Sec-WebSocket-Key, eeZn6lg/rOu8QbKwltqHDA==]
對于這個websocket請求,服務端還是按照http代碼邏輯返回200ok和JSON數據,從這個層面上看,http協議是兼容websocket的。
要讓服務端真正按照websocket姿勢, 要使用HttpContext.WebSockets.AcceptWebSocketAsync()
告知客戶端開始切換協議[5],并在原tcp上發起全雙工通信。
前后對比, 困惑得解:雖然nginx為http請求轉發了Connection
、Upgrade
標頭, 但是服務器并不認可這是websocket升級協議,依舊當成帶了Connection和Upgrade特殊標頭的http協議,走原來的http業務處理邏輯是沒有問題的。
總結
1.?本文記錄了nginx在轉發websocket請求時要添加的配置
2. websocket以http協議為藍本,添加了特定的htp標頭來要求切換協議;為了與常規http區分,瀏覽器自動增加了Sec-websocket-key等標頭, 讓服務端認為這是一個有效的websocket請求。
引用鏈接
[1]
?.NET WebSockets 核心原理初體驗:?https://www.cnblogs.com/JulianHuang/p/14681331.html[2]
?SignalR 從開發到生產部署避坑指南:?https://www.cnblogs.com/JulianHuang/p/15434137.html[3]
?服務端認定websocket請求的源碼:?https://github.com/dotnet/aspnetcore/blob/main/src/Middleware/WebSockets/src/WebSocketMiddleware.cs#L219[4]
?Sec-WebSocket-Key
:?https://www.rfc-editor.org/rfc/rfc6455#section-11.3.1[5]
?開始切換協議:?https://github.com/dotnet/aspnetcore/blob/main/src/Middleware/WebSockets/src/WebSocketMiddleware.cs#L134