使用HTTP/1.1協(xié)議中的protocol_switch機(jī)制,可以將客戶端與服務(wù)器的連接協(xié)議由HTTP/1.1升級為WebSocket。
HTTP協(xié)議中的 hop-by-hop 頭部
- Connection
- Keep-Alive
- Proxy-Authenticate
- Proxy-Authorization
- TE
- Trailers
- Transfer-Encoding
- Upgrade
只對單個傳輸級連接有效,不存儲于緩存,可以由代理服務(wù)器轉(zhuǎn)發(fā)。
HTTP method 之 CONNECT
CONNECT https://www.baidu.com HTTP/1.1
客戶端告訴代理服務(wù)器,替我訪問https://www.baidu.com,并將結(jié)果返回給我。
因為Upgrade是一個hop-by-hop頭部,所以不能直接從客戶端傳遞給被代理的服務(wù)器。使用正向代理時,客戶端可以CONNECT方法回避這個問題。對于反向代理卻回避不了,因為客戶端并不知道代理服務(wù)器的存在,更不知道請求需要在代理服務(wù)器上特殊處理。
版本1.3.13之后,nginx實現(xiàn)了一種特殊操作模式:如果被代理的服務(wù)器返回帶有101狀態(tài)碼的響應(yīng),并且客戶端通過請求中的Upgrade請求protocol_switch,則允許在客戶機(jī)和被代理的服務(wù)器之間設(shè)置隧道。
正如上面所說,作為hop-by-hop頭部,Upgrade和Connection不能從客戶端直接傳遞給被代理的服務(wù)器。為了讓被代理的服務(wù)器可以接收到這兩個頭部,知道客戶端想要切換到WebSocket協(xié)議,必須由代理服務(wù)器顯示地轉(zhuǎn)發(fā)。
location /chat/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
在一個更復(fù)雜的例子中,向代理服務(wù)器發(fā)出的請求中Connection頭字段的值取決于客戶端請求報頭中是否存在Upgrade字段:
http {
# 若http_upgrade存在,Connection值為upgrade,升級協(xié)議
# 若http_upgrade不存在,Connection值為close,關(guān)閉連接
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
...
location /chat/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
默認(rèn)情況下,如果被代理的服務(wù)器在60s內(nèi)未傳輸任何數(shù)據(jù),連接就會被關(guān)閉。這個時間可由proxy_read_timeout指令修改?;蛘?,可以配置被代理的服務(wù)器定期發(fā)送 WebSocket ping幀 來重置超時并檢查連接是否仍然有效。