socket.io - 如何让自定义节点服务器上的 socket.io 接受 CORS 请求?
问题描述
socket.io
我在服务器上有一个 CORS 错误:
Access to XMLHttpRequest at 'http://dev.learnintouch.com:9001/socket.io/?EIO=3&transport=polling&t=NbAVesU' from origin 'http://dev.learnintouch.com:83' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
我确实使用了以下cors
属性:
module.exports.io = socketio(httpsServer, {
cors: {
origin: 'http://dev.learnintouch.com:83'
}
});
我还尝试了该*
属性:
module.exports.io = socketio(httpsServer, {
cors: {
origin: '*',
methods: [
'GET',
'POST'
],
allowedHeaders: [],
credentials: true
}
});
但错误是完全相同的。
这是日志必须说的:
The NodeJS HTTP server [port: 9001] is listening...
{
redis: { hostname: 'redis', port: 6379 },
socketio: { port: 9001, sslport: 9002 },
ssl: {
path: '/usr/local/learnintouch/letsencrypt/',
key: 'current-privkey.pem',
certificate: 'current-cert.pem',
chain: 'current-fullchain.pem'
}
}
The virtual host DOESN'T have an SSL private key
Configuring the server for HTTP
The HTTP server is used by the healthcheck even if the socket is served on the HTTPS server
Server {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
_nsps: Map {
'/' => Namespace {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
sockets: Map {},
_fns: [],
_ids: 0,
server: [Circular],
name: '/',
adapter: [Adapter],
[Symbol(kCapture)]: false
}
},
parentNsps: Map {},
_path: '/socket.io',
clientPathRegex: /^\/socket\.io\/socket\.io(\.min|\.msgpack\.min)?\.js(\.map)?$/,
_connectTimeout: 45000,
_serveClient: true,
_parser: {
protocol: 5,
PacketType: {
'0': 'CONNECT',
'1': 'DISCONNECT',
'2': 'EVENT',
'3': 'ACK',
'4': 'CONNECT_ERROR',
'5': 'BINARY_EVENT',
'6': 'BINARY_ACK',
CONNECT: 0,
DISCONNECT: 1,
EVENT: 2,
ACK: 3,
CONNECT_ERROR: 4,
BINARY_EVENT: 5,
BINARY_ACK: 6
},
Encoder: [Function: Encoder],
Decoder: [Function: Decoder]
},
encoder: Encoder {},
_adapter: [Function: Adapter],
sockets: Namespace {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
sockets: Map {},
_fns: [],
_ids: 0,
server: [Circular],
name: '/',
adapter: Adapter {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
nsp: [Circular],
rooms: Map {},
sids: Map {},
encoder: Encoder {},
[Symbol(kCapture)]: false
},
[Symbol(kCapture)]: false
},
opts: { cors: { origin: 'http://dev.learnintouch.com:83' } },
[Symbol(kCapture)]: false
}
我的socket.io.min.js
版本是:
/*!
* Socket.IO v4.0.1
* (c) 2014-2021 Guillermo Rauch
* Released under the MIT License.
*/
我npm
安装cors
并添加了一个require
,但我不确定是否需要。
有关其他信息,服务器实现:
var http = require('http');
var https = require('https');
var cors = require('cors');
var connect = require('connect');
var cookie = require('cookie');
var path = require('path');
var fs = require('fs');
var redis = require('redis');
var ioredis = require('socket.io-redis');
var socketio = require('socket.io');
var utils = require('./utils.js');
var config = require('./config');
var sslKey = '';
var sslCertificate = '';
var sslChain = '';
if (fs.existsSync(config.ssl.path + config.ssl.key)) {
sslKey = fs.readFileSync(path.resolve(config.ssl.path + config.ssl.key));
sslCertificate = fs.readFileSync(path.resolve(config.ssl.path + config.ssl.certificate));
sslChain = fs.readFileSync(path.resolve(config.ssl.path + config.ssl.chain));
console.log("The virtual host HAS an SSL private key");
} else {
console.log("The virtual host DOESN'T have an SSL private key");
}
console.log("Configuring the server for HTTP");
console.log("The HTTP server is used by the healthcheck even if the socket is served on the HTTPS server");
var httpServer = http.createServer(utils.httpHandler);
httpServer.listen(config.socketio.port, function() {
console.log('The NodeJS HTTP server [port: ' + config.socketio.port + '] is listening...');
});
if (sslKey) {
console.log("Configuring the server for HTTPS");
var options = {
key: sslKey,
cert: sslCertificate,
ca: sslChain,
requestCert: false,
rejectUnauthorized: false
};
var httpsServer = https.createServer(options, utils.httpHandler);
httpsServer.listen(config.socketio.sslport, function() {
console.log('The NodeJS HTTPS server [port: ' + config.socketio.sslport + '] is listening...');
});
}
module.exports.io = socketio(httpsServer, {
cors: {
origin: '*',
methods: [
'GET',
'POST'
],
allowedHeaders: [],
credentials: true
}
});
console.log(module.exports.io);
module.exports.io.adapter(ioredis({ host: config.redis.hostname, port: config.redis.port }));
var redisClient = redis.createClient(config.redis.port, config.redis.hostname);
module.exports.io.use(function (socket, handler) {
if (socket.request.headers.cookie) {
socket.request.cookies = cookie.parse(decodeURIComponent(socket.request.headers.cookie));
socket.request.sessionID = socket.request.cookies['PHPSESSID'];
socket.request.socketSessionId = socket.request.cookies['socketSessionId'];
console.log("Authorization attempt with sessionID: " + socket.request.sessionID + " and socketSessionId: " + socket.request.socketSessionId);
redisClient.get("PHPREDIS_SESSION:" + socket.request.sessionID, function (error, reply) {
if (error) {
console.log("The redis client had an error: " + error);
return handler(new Error('The connection was refused because the redis client had an error.'));
} else if (!reply) {
console.log('The connection was refused because the redis client did not find the sessionID.');
return handler(new Error('The connection was refused because the redis client did not find the sessionID.'));
} else {
var redisSocketSessionId = utils.getRedisValue(reply, "socketSessionId");
if ('undefined' == typeof socket.request.socketSessionId || redisSocketSessionId != socket.request.socketSessionId) {
console.log('The connection was refused because the socketSessionId was invalid.');
return handler(new Error('The connection was refused because the socketSessionId was invalid.'));
} else {
console.log('The connection was granted.');
handler();
}
}
});
} else {
console.log('The connection was refused because no cookie was transmitted.');
return handler(new Error('The connection was refused because no cookie was transmitted.'));
}
});
更新:utils.js
文件:
var formidable = require('formidable');
Array.prototype.contains = function(k, callback) {
var self = this;
return (function check(i) {
if (i >= self.length) {
return callback(false);
}
if (self[i] === k) {
return callback(true);
}
return process.nextTick(check.bind(null, i+1));
}(0));
};
module.exports.isEmpty = function(obj) {
for(var prop in obj) {
if(obj.hasOwnProperty(prop))
return false;
}
return true;
}
module.exports.getRedisValue = function(data, name) {
var redisBits = data.split(";");
for (var i in redisBits) {
if (redisBits.hasOwnProperty(i)) {
if (redisBits[i].substring(0, name.length) == name) {
var value = redisBits[i].split("|")[1].split(":")[2].replace("\"", "").replace("\"", "");
return(value);
}
}
}
};
// Handle http requests sent to the Node.js server
module.exports.httpHandler = function(req, res) {
switch(req.url) {
case '/ping':
if (req.method == 'GET') {
// console.log("Received a [200] " + req.method + " to " + req.url);
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('');
}
break;
case '/push':
if (req.method == 'POST') {
// console.log("Received a [200] " + req.method + " to " + req.url);
form = new formidable.IncomingForm();
form.parse(req, function(e, fields, files) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('');
httpHandleServerPostRequest(fields);
});
}
break;
default:
send404(res);
};
};
send404 = function(res) {
res.writeHead(404);
res.write('404');
res.end();
};
解决方案
我在我的 HTTP 服务器处理程序中添加了标头,它解决了这个问题:
// Handle http requests sent to the Node.js server
module.exports.httpHandler = function(req, res, next) {
// Allow CORS
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Headers", "X-Requested-With");
switch(req.url) {
case '/ping':
if (req.method == 'GET') {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('');
}
break;
case '/push':
if (req.method == 'POST') {
form = new formidable.IncomingForm();
form.parse(req, function(e, fields, files) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('');
httpHandleServerPostRequest(fields);
});
}
break;
default:
};
};
推荐阅读
- javascript - Typescript 中有 `{ [key: string]: any } ` 的简写吗?
- html - 使用 Bootstrap v4.5.0 复制使用 Bootstrap v3.3.6 制作的导航栏
- angular - Angular RxJS Observable Service 在没有 Map 语句的情况下不返回响应
- docker - 刚刚在我的 Windows 10 Pro 上安装了 Docker 对我的版本有疑问
- macos - macOS 手册中的“第 4 节中的手册条目”?
- html - 通过 css 访问特定的 html 标签
- python - 将参数传递给另一个脚本,然后返回计算值
- c - 检查命令行中的字符串
- python - 为什么 Json 解析会破坏 While 循环?
- python - Dash 中的热重载不会自动更新