Use both http and https for socket.io Use both http and https for socket.io express express

Use both http and https for socket.io


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:

  1. 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'))
  2. create your http & https server
  3. create a new instance of ioServer
  4. bind your http and https servers (.listen)
  5. attach http&https server instances to the io instance. (.listen is an alias for .attach)
  6. setup io events.

And the client (jade syntax but you get the idea):

  1. include socket.io script tag
  2. call io and capture reference
  3. 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.


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.


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.