最近需要用到Nginx反向代理Websocket协议,虽然Nginx配置起来了就两行配置但还是有必要来了解下它的原理。
HTTP Upgrade
HTTP/1.1协议提出的一种协议升级机制,允许将一个已经建立的HTTP连接升级到其它的协议。当客户端试图升级到新的协议时,需要发一个带有额外两个header的请求:
1
2
|
Connection: Upgrade
Protocol: protocol-name ["/" protocol-version]
|
Connection: Upgrade
表示我想升级协议,Protocol
指出我想升级成的协议和版本,如果服务端同意升级需要以101
状态码响应客户端的请求。
1
2
3
|
HTTP/1.1 101 Switching Protocols
Connection: upgrade
Upgrade: HTTP/2.0
|
到次就可以用新协议通信了。
Websocket建连过程
抓包看看websocket连接建立过程,请求头:
1
2
3
4
5
6
|
Connection: Upgrade
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Key: fL1y0DQDYrqGCwWPWIkDOw==
Sec-WebSocket-Protocol: vite-hmr
Sec-WebSocket-Version: 13
Upgrade: websocket
|
响应头
1
2
3
4
|
Connection: Upgrade
Sec-WebSocket-Accept: jWe7MVRbWtE+T/o2ZfoCecA3J7A=
Sec-WebSocket-Protocol: vite-hmr
Upgrade: websocket
|
Nginx配置Upgrade
Nginx 从1.3.13开始支持Upgrade协议升级机制,看看官方给的示例。
1
2
3
4
5
6
7
8
9
10
11
|
This allows to proxy WebSockets by using configuration like this:
location /chat/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
Connection upgrade is allowed as long as it was requested by a client
via the Upgrade request header.
|
Nginx Upgrade机制原理
为了跟进一步了解Nginx Upgrade机制的原理,翻了1.3.13版本的commit记录。
1
2
3
4
5
6
7
|
if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS) {
u->keepalive = 0;
if (r->headers_in.upgrade) {
u->upgrade = 1;
}
}
|
1
2
3
4
|
if (u->upgrade) {
ngx_http_upstream_upgrade(r, u);
return;
}
|
根据上游服务器是否返回101
状态码判断是否要upgreade,如果是则会执行ngx_http_upstream_upgrade
函数
1
2
3
4
5
6
7
8
9
10
11
|
ngx_http_upstream_upgrade(ngx_http_request_t *r, ngx_http_upstream_t *u)
{
// ...
u->read_event_handler = ngx_http_upstream_upgraded_read_upstream;
u->write_event_handler = ngx_http_upstream_upgraded_write_upstream;
r->read_event_handler = ngx_http_upstream_upgraded_read_downstream;
r->write_event_handler = ngx_http_upstream_upgraded_write_downstream;
// ...
}
|
ngx_http_upstream_upgrade
函数主要是配置upgrade时的网络请写io回调,这几个回调最终都会调用ngx_http_upstream_process_upgraded
函数,只不过是数据流向不同而已。
ngx_http_upstream_process_upgraded
函数代码比较长就不贴了,主要作用就是纯socket数据读写,也就是TCP层的数据转发,而非正常http反向代理。
总结
根据RFC的定义,Nginx在通过上游响应的101状态码判断是否需要执行upgrade,需要就走tcp数据包转发,否则就走正常的http代理,这也解释了为什么websocket一旦建连后就不能在增加header。
参考
http://nginx.org/en/CHANGES
https://github.com/nginx/nginx/commit/08a73b4aadebd9405ac52ec1f6eef5ca1fe8c11a
https://blog.csdn.net/chenhanzhun/article/details/43524135