可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am trying to make socket.io work both on http
and https
connections, but it seems that with my configuration it can work only on one of them.
With the below config options socket.io can access my application through https
, but when trying to access through regular http
it cannot connect and i receive errors.
var app = express()
, http= require('http').createServer(app)
, https = require('https').createServer(options, app)
, io = require('socket.io').listen(https, { log: false })
And later i have this
http.listen(80, serverAddress);
https.listen(443, serverAddress);
On client side i have this:
<script src='/socket.io/socket.io.js'></script>
var socket = io.connect('https://<%= serverAddress %>', {secure: true, 'sync disconnect on unload' : true});
Of course if i switch the http with the https options on the .listen
and .connect
functions of the server and the client respectively i am having the reverse results. Socket.io can access through http and not through https.
How is it possible to achieve this? I need it mostly because it is about a facebook app, so it must provide both http and https connection options according to facebook rules.
Edit: In case it helps about the problem, the error i am receiving is the below
Failed to load resource: the server responded with a status of 404 (Not Found) http://DOMAIN/socket.io/socket.io.js
And because of this i get others such as:
Uncaught ReferenceError: io is not defined
回答1:
I believe the problem is in your way of setting up socket.io on the server side and on the client.
Here's how I made it work (just for you).
Server:
var debug = require('debug')('httpssetuid');
var app = require('../app');
var http = require('http');
var https = require('https');
var fs = require('fs');
var exec = require('child_process').exec;
var EventEmitter = require('events').EventEmitter;
var ioServer = require('socket.io');
var startupItems = [];
startupItems.httpServerReady = false;
startupItems.httpsServerReady = false;
var ee = new EventEmitter();
ee.on('ready', function(arg) {
startupItems[arg] = true;
if (startupItems.httpServerReady && startupItems.httpsServerReady) {
var id = exec('id -u ' + process.env.SUDO_UID, function(error, stdout, stderr) {
if(error || stderr) throw new Error(error || stderr);
var uid = parseInt(stdout);
process.setuid(uid);
console.log('de-escalated privileges. now running as %d', uid);
setInterval(function cb(){
var rnd = Math.random();
console.log('emitting update: %d', rnd);
io.emit('update', rnd);
}, 5000);
});
};
});
app.set('http_port', process.env.PORT || 80);
app.set('https_port', process.env.HTTPS_PORT || 443);
var httpServer = http.createServer(app);
var opts = {
pfx: fs.readFileSync('httpssetuid.pfx')
};
var httpsServer = https.createServer(opts, app);
var io = new ioServer();
httpServer.listen(app.get('http_port'), function(){
console.log('httpServer listening on port %d', app.get('http_port'));
ee.emit('ready', 'httpServerReady');
});
httpsServer.listen(app.get('https_port'), function(){
console.log('httpsServer listening on port %d', app.get('https_port'));
ee.emit('ready', 'httpsServerReady');
});
io.attach(httpServer);
io.attach(httpsServer);
io.on('connection', function(socket){
console.log('socket connected: %s', socket.id);
});
Client:
script(src='/socket.io/socket.io.js')
script.
var socket = io();
socket.on('update', function(update){
document.getElementById('update').innerHTML = update;
});
Here are the key points for the server:
- require socket.io but don't call it's listen method yet (assuming http and https are already required). Instead, just keep the reference. (var ioServer = require('socket.io'))
- create your http & https server
- create a new instance of ioServer
- bind your http and https servers (.listen)
- attach http&https server instances to the io instance. (.listen is an alias for .attach)
- setup io events.
And the client (jade syntax but you get the idea):
- include socket.io script tag
- call io and capture reference
- setup your event handlers
On the client you don't need to call io.connect(). Furthermore, I'm not sure about your options there. It looks like you have a typo (, ,) and I can't find any reference to secure: true in the 1.0 documentation.
回答2:
Arguably, the node.js server object for HTTP and HTTPS ought to be given the capability to listen on an arbitrary number of ports and interfaces, with and without SSL, but this does not seem to currently be implemented. (I was able to get one server to listen on two ports by passing a second server that had no request listener as the "handle" argument to server.listen(handle, [callback]) interface, in addition to server.listen(port, [hostname], [backlog], [callback]), but it did not work with SSL/non-SSL servers mixed.)
The stunnel workaround already mentioned is of course a viable option, but if it is not desirable to install a separate piece of software (to avoid non-node.js dependencies), the same tunneling can be achieved natively in node.js instead (assuming HTTP on port 80 and HTTPS on port 443):
var fs = require('fs');
var net = require('net');
var tls = require('tls');
var sslOptions = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem')
};
tls.createServer(sslOptions, function (cleartextStream) {
var cleartextRequest = net.connect({
port: 80,
host: '127.0.0.1'
}, function () {
cleartextStream.pipe(cleartextRequest);
cleartextRequest.pipe(cleartextStream);
});
}).listen(443);
This will have the same effect as using stunnel. In other words, it will avoid the need for two separate socket.io server instances, while also making the node.js "https" module redundant.
回答3:
I have done something similar and it required two socket.io instances. Something like this:
var express = require('express');
var oneServer = express.createServer();
var anotherServer = express.createServer();
var io = require('socket.io');
var oneIo = io.listen(oneServer);
var anotherIo = io.listen(anotherServer);
Of course that you will need to inject messages twice: for both socket.io instances.
A good option is delegate SSL handling to stunnel and forget about SSL in your code.
回答4:
I am guessing Cross origin requests could be the reason why you are getting errors. Change in protocol is considered change in domain. So for page served via http
server accessing https
server (websocket server attached to it) may throw security errors. See an example here on how to enable CORS in express.
Also you should change *
in the header to http://serverAddress , https://serverAddress
. Allowing all sites is not a good idea, use it for testing.
The same is true if you are trying to AJAX between your http
and https
servers. Please post the errors, just to be sure about it.
回答5:
I solved the problems using a different approach, I configured the server to support only unencrypted transport, and used stunnel
for the https support.
For information on how to install stunnel
you can check this post.
Then, used the following con configuration:
#/etc/stunnel/stunnel.conf
cert = /var/www/node/ssl/encryption.pem
[node]
accept = 443
connect = 80
Finally, I used the following to connect the clients:
var socket = that.socket = io.connect('//'+server);
This will auto detect the browser scheme and connect using http/https accordingly.