Используйте как http, так и https для socket.io

Я пытаюсь заставить socket.io работать как на http, так и на https соединениях, но похоже, что с моей конфигурацией он может работать только на одном из них.

С помощью следующих опций конфигурации socket.io может получить доступ к моему приложению через https, но при попытке доступа через обычный http он не может подключиться, и я получаю ошибки.

    var app = express()
  , http= require('http').createServer(app)
  , https = require('https').createServer(options, app)
  , io = require('socket.io').listen(https, { log: false })

И позже у меня есть это

http.listen(80, serverAddress);
https.listen(443, serverAddress);

На стороне клиента у меня есть следующее:

<script src='/socket.io/socket.io.js'></script>

var socket = io.connect('https://<%= serverAddress %>', {secure: true, 'sync disconnect on unload' : true});

Конечно, если я переключу http с помощью параметров https в функциях .listen и .connect сервера и клиента соответственно, у меня есть обратные результаты. Socket.io может получить доступ через http, а не через https.

Как можно добиться этого? Мне нужно это в основном потому, что речь идет о приложении facebook, поэтому он должен предоставлять как http, так и https-соединения в соответствии с правилами facebook.

Изменить: в случае, если это помогает в проблеме, ошибка, которую я получаю, ниже

Failed to load resource: the server responded with a status of 404 (Not Found) http://DOMAIN/socket.io/socket.io.js

И из-за этого я получаю другие, такие как:

Uncaught ReferenceError: io is not defined 

Ответ 1

Я считаю, что проблема заключается в том, как настроить socket.io на стороне сервера и на клиенте.

Вот как я заставил его работать (только для вас).

Сервер:

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);
});

Клиент:

script(src='/socket.io/socket.io.js')
script.
  var socket = io();
  socket.on('update', function(update){
    document.getElementById('update').innerHTML = update;
  });

Вот ключевые моменты для сервера:

  • требуется socket.io, но еще не называть его методом прослушивания (предполагается, что HTTP и https уже требуются). Вместо этого просто держите ссылку. (var ioServer = require ('socket.io'))
  • создайте свой сервер http и https
  • создать новый экземпляр ioServer
  • свяжите ваши серверы http и https (.listen)
  • присоединяйте экземпляры HTTP и https к экземпляру io. (.listen - это псевдоним для .attach)
  • настроить события io.

И клиент (синтаксис нефрита, но вы получаете идею):

  • включить метку socket.io script
  • вызов io и запись
  • настроить обработчики событий.

На клиенте вам не нужно вызывать io.connect(). Кроме того, я не уверен в ваших вариантах. Похоже, у вас есть typo (,), и я не могу найти ссылку на secure: true в документации 1.0.

Ответ 2

Возможно, серверному объекту node.js для HTTP и HTTPS должна быть предоставлена ​​возможность прослушивать произвольное количество портов и интерфейсов с использованием и без SSL, но в настоящее время это не выполняется. (Я смог получить один сервер для прослушивания на двух портах, передав второй сервер, у которого не было приемника запросов в качестве аргумента "handle" для интерфейса server.listen(handle, [callback]), в дополнение к server.listen(порт, [hostname], [backlog], [callback]), но он не работал с серверами SSL/не SSL.)

Обходной путь stunnel, конечно же, является жизнеспособным вариантом, но если нежелательно устанавливать отдельную часть программного обеспечения (чтобы избежать зависимостей без node.js), то же самое туннелирование может быть выполнено изначально вместо node.js(при условии, что HTTP на порту 80 и HTTPS на порту 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);

Это будет иметь тот же эффект, что и при использовании stunnel. Другими словами, это позволит избежать необходимости в двух отдельных экземплярах сервера socket.io, а также сделать модуль node.js "https" избыточным.

Ответ 3

Я сделал что-то подобное и потребовал два экземпляра socket.io. Что-то вроде этого:

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);

Конечно, вам нужно дважды вводить сообщения: для обоих экземпляров socket.io.

Хорошим вариантом является делегирование SSL с помощью stunnel и забыть о SSL в вашем коде.

Ответ 4

Я предполагаю, что запросы Cross origin могут быть причиной того, что вы получаете ошибки. Изменение протокола считается изменением в домене. Таким образом, для страницы, обслуживаемой сервером http, доступ к серверу https (сервер websocket, прикрепленный к нему), может вызывать ошибки безопасности. Пример здесь о том, как включить CORS в экспресс.

Также вы должны изменить * в заголовке на http://serverAddress , https://serverAddress. Разрешить все сайты не рекомендуется, используйте их для тестирования.

То же самое верно, если вы пытаетесь использовать AJAX между серверами http и https. Пожалуйста, опубликуйте ошибки, просто чтобы убедиться в этом.

Ответ 5

Я решил проблемы с использованием другого подхода, я настроил сервер для поддержки только незашифрованного транспорта и использовал stunnel для https поддержка.

Для получения информации о том, как установить stunnel, вы можете проверить этот post.

Затем используется следующая конфигурация con:

#/etc/stunnel/stunnel.conf
cert = /var/www/node/ssl/encryption.pem
[node]
accept = 443
connect = 80

Наконец, для подключения клиентов я использовал следующее:

var socket = that.socket = io.connect('//'+server);

Это будет автоматически определить схему браузера и соответственно подключиться с помощью http/https.