apache 后面的 Websocket - 意外的响应代码 500
Websocket behind apache - Unexpected response code 500
我有一个 NodeJS api 和一个使用 socket.io 的 websocket。 api 监听 http://localhost:8080 和 ws://localhost:8080.
服务器端(NodeJS,“socket.io”:“2”):
// SOCKET_ORIGINS="*"
let io = require('socket.io')(server, {
origins: "*",
})
// AUTH_SECRET="asecret"
io.use(socketioJwt.authorize({
secret: AUTH_SECRET,
handshake: true
}))
io.on('connection', (socket) => {
console.log('a user connected', socket.id, socket.decoded_token.user_id);
io.users.push({user_id:socket.decoded_token.user_id, sid:socket.id})
socket.on('disconnect', () => {
console.log('user disconnected');
});
});
客户端(Angular 10, "socket.io-client": "^2.2.0"):
let token = JSON.parse(localStorage.getItem('token'))
// connect
this.socket = socketIo.connect(this.globalServices.socketURL,{ transports: ['websocket'],
query: 'token='+ token});
// authentication handler
this.socket.on('connect', () => {
console.debug("socket connected", this.socket.id)
})
一切都在本地运行得很好。我可以毫无问题地同时请求 http 和 ws。
然后,我在服务器上部署 api(配置:Debian 8.11、Apache 2.4、npm 3.10.10)。
Apache 配置:
<VirtualHost *:443>
ProxyPreserveHost On
ServerName my-api.net
ServerAdmin admin@my-api.com
DocumentRoot /opt/my-api/api/versions/default/public
RewriteEngine on
RewriteCond %{HTTP:UPGRADE} ^websocket$ [NC]
RewriteCond %{HTTP:CONNECTION} Upgrade$ [NC]
RewriteRule /(.*)$ ws://127.0.0.1:3080/ [P,L]
ProxyRequests off
ProxyPass /socket.io/ ws://127.0.0.1:3080/socket.io/
ProxyPassReverse /socket.io/ ws://127.0.0.1:3080/socket.io/
ProxyPass / http://127.0.0.1:3080/ retry=0 timeout=5
ProxyPassReverse / http://127.0.0.1:3080/
SSLEngine On
KeepAlive On
SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
SSLHonorCipherOrder on
SSLOptions +StrictRequire
SSLCertificateFile /etc/letsencrypt/live/my-api.net/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/my-api.net/privkey.pem
ErrorLog /opt/my-api/logs/apache-my-api-error.log
CustomLog /opt/my-api/logs/apache-my-api-access.log combined
</VirtualHost>
api 在服务器上的端口 3080 上运行。
$ lsof -i -P -n | grep '3080'
node 50041 lgrenier 23u IPv6 124610904 0t0 TCP *:3080 (LISTEN)
此外,pm2状态正常:
┌─────┬───────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │
├─────┼───────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 4 │ my-api │ default │ 0.0.1 │ fork │ 50041 │ 83m │ 28 │ online │ 0.4% │ 70.2mb │ lgrenier │ disabled │
└─────┴───────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
api 开始没有任何错误。
我的所有 HTTPS 请求都有效,但 WSS 无效。他们在客户端失败并显示以下错误消息
main-es2015.ce216019692753660ec5.js:1 WebSocket connection to 'wss://my-api.net/socket.io/?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJleHBpcmVzSW4iOjI1OTIwMDB9.eFYbyoZ6a53Sf72iX5mjgRWrIij2tWYl_PQnOVlCKPY&EIO=3&transport=websocket' failed: Error during WebSocket handshake: Unexpected response code: 500
尽管有错误消息,但在 Apache 日志或 pm2 日志中没有任何我调用的痕迹。
我哪里错了?
在服务器端,cors 设置不正确:
错误的 CORS 设置
// SOCKET_ORIGINS="*"
let io = require('socket.io')(server, {
origins: "*",
})
正确的 CORS 设置
// SOCKET_ORIGINS="*"
let io = require('socket.io')(server, {
cors:{ origin: "*" }
})
就这么简单。
我有一个 NodeJS api 和一个使用 socket.io 的 websocket。 api 监听 http://localhost:8080 和 ws://localhost:8080.
服务器端(NodeJS,“socket.io”:“2”):
// SOCKET_ORIGINS="*"
let io = require('socket.io')(server, {
origins: "*",
})
// AUTH_SECRET="asecret"
io.use(socketioJwt.authorize({
secret: AUTH_SECRET,
handshake: true
}))
io.on('connection', (socket) => {
console.log('a user connected', socket.id, socket.decoded_token.user_id);
io.users.push({user_id:socket.decoded_token.user_id, sid:socket.id})
socket.on('disconnect', () => {
console.log('user disconnected');
});
});
客户端(Angular 10, "socket.io-client": "^2.2.0"):
let token = JSON.parse(localStorage.getItem('token'))
// connect
this.socket = socketIo.connect(this.globalServices.socketURL,{ transports: ['websocket'],
query: 'token='+ token});
// authentication handler
this.socket.on('connect', () => {
console.debug("socket connected", this.socket.id)
})
一切都在本地运行得很好。我可以毫无问题地同时请求 http 和 ws。 然后,我在服务器上部署 api(配置:Debian 8.11、Apache 2.4、npm 3.10.10)。 Apache 配置:
<VirtualHost *:443>
ProxyPreserveHost On
ServerName my-api.net
ServerAdmin admin@my-api.com
DocumentRoot /opt/my-api/api/versions/default/public
RewriteEngine on
RewriteCond %{HTTP:UPGRADE} ^websocket$ [NC]
RewriteCond %{HTTP:CONNECTION} Upgrade$ [NC]
RewriteRule /(.*)$ ws://127.0.0.1:3080/ [P,L]
ProxyRequests off
ProxyPass /socket.io/ ws://127.0.0.1:3080/socket.io/
ProxyPassReverse /socket.io/ ws://127.0.0.1:3080/socket.io/
ProxyPass / http://127.0.0.1:3080/ retry=0 timeout=5
ProxyPassReverse / http://127.0.0.1:3080/
SSLEngine On
KeepAlive On
SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
SSLHonorCipherOrder on
SSLOptions +StrictRequire
SSLCertificateFile /etc/letsencrypt/live/my-api.net/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/my-api.net/privkey.pem
ErrorLog /opt/my-api/logs/apache-my-api-error.log
CustomLog /opt/my-api/logs/apache-my-api-access.log combined
</VirtualHost>
api 在服务器上的端口 3080 上运行。
$ lsof -i -P -n | grep '3080'
node 50041 lgrenier 23u IPv6 124610904 0t0 TCP *:3080 (LISTEN)
此外,pm2状态正常:
┌─────┬───────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │
├─────┼───────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 4 │ my-api │ default │ 0.0.1 │ fork │ 50041 │ 83m │ 28 │ online │ 0.4% │ 70.2mb │ lgrenier │ disabled │
└─────┴───────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
api 开始没有任何错误。
我的所有 HTTPS 请求都有效,但 WSS 无效。他们在客户端失败并显示以下错误消息
main-es2015.ce216019692753660ec5.js:1 WebSocket connection to 'wss://my-api.net/socket.io/?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJleHBpcmVzSW4iOjI1OTIwMDB9.eFYbyoZ6a53Sf72iX5mjgRWrIij2tWYl_PQnOVlCKPY&EIO=3&transport=websocket' failed: Error during WebSocket handshake: Unexpected response code: 500
尽管有错误消息,但在 Apache 日志或 pm2 日志中没有任何我调用的痕迹。 我哪里错了?
在服务器端,cors 设置不正确:
错误的 CORS 设置
// SOCKET_ORIGINS="*"
let io = require('socket.io')(server, {
origins: "*",
})
正确的 CORS 设置
// SOCKET_ORIGINS="*"
let io = require('socket.io')(server, {
cors:{ origin: "*" }
})
就这么简单。