web-dev-qa-db-ja.com

socket.ioにはhttpとhttpsの両方を使用します

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

もちろん、サーバーとクライアントの.listen関数と.connect関数でそれぞれhttpsオプションを使用してhttpを切り替えると、逆の結果になります。 Socket.ioは、https経由ではなくhttp経由でアクセスできます。

どうすればこれを達成できますか?これは主にFacebookアプリに関するものであるため必要です。そのため、Facebookのルールに従ってhttpとhttpsの両方の接続オプションを提供する必要があります。

編集:それが問題について役立つ場合、私が受け取っているエラーは以下のとおりです

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 
13

問題は、サーバー側およびクライアントでに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;
  });

サーバーの要点は次のとおりです。

  1. socket.ioが必要ですが、まだlistenメソッドを呼び出さないでください(httpとhttpsがすでに必要であると想定しています)。代わりに、参照を保持してください。 (var ioServer = require( 'socket.io'))
  2. http&httpsサーバーを作成します
  3. ioServerの新しいインスタンスを作成します
  4. httpサーバーとhttpsサーバーをバインドします(.listen)
  5. http&httpsサーバーインスタンスをioインスタンスに接続します。 (.listenは.attachのエイリアスです)
  6. ioイベントを設定します。

そしてクライアント(翡翠の構文ですが、あなたは考えを理解します):

  1. socket.ioスクリプトタグを含める
  2. ioを呼び出し、参照をキャプチャします
  3. イベントハンドラーを設定する

クライアントでは、io.connect()を呼び出す必要はありません。さらに、そこにあるあなたの選択肢についてはよくわかりません。タイプミス(、、)があるようですが、1.0のドキュメントでsecure:trueへの参照が見つかりません。

17
a_arias

間違いなく、HTTPおよびHTTPSのnode.jsサーバーオブジェクトには、SSLの有無にかかわらず、任意の数のポートとインターフェイスでリッスンする機能を与える必要がありますが、これは現在実装されていないようです。 (server.listen(port 、[ホスト名]、[バックログ]、[コールバック])が、SSL /非SSLサーバーが混在している場合は機能しませんでした。)

stunnelすでに述べた回避策はもちろん実行可能なオプションですが、別のソフトウェアをインストールすることが望ましくない場合(node.js以外の依存関係を回避するため)、代わりにnode.jsで同じトンネリングをネイティブに実現できます(ポート80でHTTP、ポート443でHTTPSを想定)。

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を使用するのと同じ効果があります。つまり、node.jsの「https」モジュールを冗長化しながら、2つの別々のsocket.ioサーバーインスタンスの必要性を回避します。

6
Otto G

私は似たようなことをしましたが、2つの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インスタンスに対して、メッセージを2回挿入する必要があります。

良いオプションは、SSL処理を stunnel に委任し、コード内のSSLを忘れることです。

2
MrTorture

別のアプローチを使用して問題を解決し、暗号化されていないトランスポートのみをサポートするようにサーバーを構成し、httpsサポートに stunnel を使用しました。

stunnelのインストール方法については、これを確認できます post

次に、次の構成を使用しました。

#/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を使用して接続します。

1
Kuf

クロスオリジンリクエストがエラーを受け取る理由である可能性があると思います。プロトコルの変更は、ドメインの変更と見なされます。したがって、httpサーバーを介して提供されるページの場合、httpsサーバー(それに接続されているWebSocketサーバー)にアクセスすると、セキュリティエラーがスローされる可能性があります。エクスプレスでCORSを有効にする方法については、例を参照してください ここ

また、ヘッダーの*http://serverAddress , https://serverAddressに変更する必要があります。すべてのサイトを許可することはお勧めできません。テストに使用してください。

httpサーバーとhttpsサーバーの間でAJAXを実行しようとしている場合も、同じことが言えます。念のため、エラーを投稿してください。

0
user568109