当远程但不是本地时,Socket.io 在 nginx 代理后面回退到轮询(websocket 给出 400)

mko*_*lum 6 sockets nginx websocket node.js socket.io

我使用 nginx 1.4.6 作为 Django 应用程序和使用 socket.io 1.2.1 以及 socketio-jwt 2.3.5 的相关 NodeJS 应用程序的代理

\n\n

在本地,我使用 Vagrant 实例(ubuntu 14.04.1)进行编码,而在远程,我使用专用的 AWS EC2 实例——两者都使用相同的 Vagrantfile 和设置 shell 脚本进行配置,因此它们非常接近至相同。

\n\n

在本地运行时,我的客户端连接到 nginx 服务器,该服务器升级连接并将其传递给 Node.js 下的 socket.io 服务器实现。一切都运转良好。

\n\n

远程地,我记录第一个连接,transport=polling该连接还指示客户端升级到 websocket 传输,然后是第二个请求transport=websockets,然后是第三个请求transport=polling再次记录第三个连接。

\n\n

虽然应用程序仍然有效,但出于多种原因轮询 < websockets,我希望我能找到一种方法来对此进行排序。

\n\n

第二个请求得到答复400 Bad Request。运行我的 Node 应用程序DEBUG=* node index.js给出以下输出:

\n\n
engine intercepting request for path "/socket.io/" +24s\nengine handling "GET" http request "/socket.io/?token=eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Im1hcmNlbCIsIm9yaWdfaWF0IjoxNDE4MDU5ODg1LCJ1c2VyX2lkIjozLCJlbWFpbCI6IiIsImV4cCI6MTQxODE0NjI4NX0.2PE0TNRol9G4hby4OzQ-af2e0yjfgFAb-gQJF5tKWRwxWnFLv1NGp3Yo87UaqNaQceMW6KqzMIx2gLcRFnk09A&EIO=3&transport=polling&t=1418062322853-0" +0ms\nengine handshaking client "6YDFOjBfvZ9UyutVAAAB" +1ms\nengine:socket sending packet "open" ({"sid":"6YDFOjBfvZ9UyutVAAAB","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":60000}) +0ms\nengine:polling setting request +0ms\nengine:socket flushing buffer to transport +0ms\nengine:polling writing "  \xef\xbf\xbd0{"sid":"6YDFOjBfvZ9UyutVAAAB","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":60000}" +1ms\nengine:socket executing batch send callback +1ms\nsocket.io:server incoming connection with id 6YDFOjBfvZ9UyutVAAAB +1.7m\nsocket.io:client connecting to namespace / +1.7m\nsocket.io:namespace adding socket to nsp / +1.7m\nsocket.io:socket socket connected - writing packet +1.7m\nsocket.io:socket joining room 6YDFOjBfvZ9UyutVAAAB +0ms\nsocket.io:client writing packet {"type":0,"nsp":"/"} +79ms\nsocket.io-parser encoding packet {"type":0,"nsp":"/"} +1.7m\nsocket.io-parser encoded {"type":0,"nsp":"/"} as 0 +0ms\nengine:socket sending packet "message" (0) +79ms\nsocket id: "6YDFOjBfvZ9UyutVAAAB" connected to user: "marcel"\nsocket.io:socket joined room 6YDFOjBfvZ9UyutVAAAB +1ms\nengine intercepting request for path "/socket.io/" +121ms\nengine handling "GET" http request "/socket.io/?token=eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Im1hcmNlbCIsIm9yaWdfaWF0IjoxNDE4MDU5ODg1LCJ1c2VyX2lkIjozLCJlbWFpbCI6IiIsImV4cCI6MTQxODE0NjI4NX0.2PE0TNRol9G4hby4OzQ-af2e0yjfgFAb-gQJF5tKWRwxWnFLv1NGp3Yo87UaqNaQceMW6KqzMIx2gLcRFnk09A&EIO=3&transport=polling&t=1418062323048-1&sid=6YDFOjBfvZ9UyutVAAAB" +0ms\nengine setting new request for existing client +1ms\nengine:polling setting request +0ms\nengine:socket flushing buffer to transport +1ms\nengine:polling writing "\xef\xbf\xbd40" +38ms\nengine:socket executing batch send callback +1ms\nengine intercepting request for path "/socket.io/" +0ms\nengine handling "GET" http request "/socket.io/?token=eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Im1hcmNlbCIsIm9yaWdfaWF0IjoxNDE4MDU5ODg1LCJ1c2VyX2lkIjozLCJlbWFpbCI6IiIsImV4cCI6MTQxODE0NjI4NX0.2PE0TNRol9G4hby4OzQ-af2e0yjfgFAb-gQJF5tKWRwxWnFLv1NGp3Yo87UaqNaQceMW6KqzMIx2gLcRFnk09A&EIO=3&transport=websocket&sid=6YDFOjBfvZ9UyutVAAAB" +1ms\nengine bad request: unexpected transport without upgrade +0ms\nengine intercepting request for path "/socket.io/" +120ms\nengine handling "GET" http request "/socket.io/?token=eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Im1hcmNlbCIsIm9yaWdfaWF0IjoxNDE4MDU5ODg1LCJ1c2VyX2lkIjozLCJlbWFpbCI6IiIsImV4cCI6MTQxODE0NjI4NX0.2PE0TNRol9G4hby4OzQ-af2e0yjfgFAb-gQJF5tKWRwxWnFLv1NGp3Yo87UaqNaQceMW6KqzMIx2gLcRFnk09A&EIO=3&transport=polling&t=1418062323180-2&sid=6YDFOjBfvZ9UyutVAAAB" +1ms\nengine setting new request for existing client +0ms\nengine:polling setting request +0ms\n
Run Code Online (Sandbox Code Playgroud)\n\n

该问题似乎与线路有关engine bad request: unexpected transport without upgrade +0ms,但我不明白。nginx 配置肯定提到了升级,并且它可以在我的 vagrant 机器上运行。

\n\n

nginx配置的相关部分如下:

\n\n
server {\n    ...\n\n    location / {\n        ...\n    }\n\n    location /socket.io/ {\n        proxy_pass http://127.0.0.1:4000/socket.io/;\n        proxy_http_version 1.1;\n\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_set_header Connection "upgrade";\n        proxy_set_header Host $host;\n\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-NginX-Proxy true;\n\n        proxy_redirect off;\n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我能想到的本地和远程设置之间的唯一区别是,在本地,客户端在两个不同的端口上连接到本地主机(我代理 nginx 后面的 Node 应用程序,但 Web 应用程序通过 Gulp 运行),而远程节点应用程序位于不同的域上,位于端口 80 后面。

\n\n

非常感谢任何线索:)

\n

小智 5

我也遇到过同样的问题。在查看了一些 tcp 捕获后,看起来 nginx 甚至没有收到传入的 Upgrade 标头,因此它没有将其传递给 node.js。(我确实怀疑 cloudflare 这样做,但我不确定)。

我解决这个问题的方法确实很hacky,但是效果很好。基本上我修改了 nginx 配置来查找Sec-Websocket-Key标头,当它找到时,它将 Upgrade 标头设置为websocket,将 Connection 标头设置为upgrade。这有效。

配置示例:

map $http_sec_websocket_key $upgr {
    ""      "";           # If the Sec-Websocket-Key header is empty, send no upgrade header
    default "websocket";  # If the header is present, set Upgrade to "websocket"
}

map $http_sec_websocket_key $conn {
    ""      $http_connection;  # If no Sec-Websocket-Key header exists, set $conn to the incoming Connection header
    default "upgrade";         # Otherwise, set $conn to upgrade
}
Run Code Online (Sandbox Code Playgroud)

您必须在server {}块之前定义这 2 个地图块。

现在在您的 socket.io 位置添加以下两行:

proxy_set_header Upgrade $upgr;
proxy_set_header Connection $conn;
Run Code Online (Sandbox Code Playgroud)

这会将升级和连接标头设置为映射值。

我希望这也能解决您的问题。