2013-06-24 65 views
11

我想使socket.io工作在httphttps連接,但似乎與我的配置,它只能在其中之一工作。同時使用http和https爲socket.io

使用下面的配置選項,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上.listen.connect功能https的選項服務器和客戶端分別我有相反的結果。 Socket.io可以通過http訪問,而不是通過https訪問。

這怎麼可能實現呢?我主要需要它,因爲它是關於一個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 

回答

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實例。

一個不錯的選擇是將代理SSL處理委託給stunnel,並忘記代碼中的SSL。

+0

來自'http'的客戶端能夠與連接到'https'的某個人進行通信嗎? 關於'stunnel'我試過了,也是'http-proxy',但是我無法讓它們正常工作。你能提供一個例子嗎? –

+0

抱歉,延遲。是的,stunnel負責SSL並透明地解密連接到https端口(443)的客戶端,以便它們顯示爲正在連接以表示http端口(80)。 – MrTorture

+0

這裏有一個工作簡單的stunnel配置,在443上偵聽並將所有傳入流量轉發到同一主機的端口80的要點:https://gist.github.com/mbenedettini/5911415 – MrTorture

0

我猜你可能會因爲交叉來源請求而得到錯誤。協議中的更改被認爲是域中的更改。因此,對於通過http服務器訪問的頁面https服務器(連接到它的websocket服務器)可能會導致安全錯誤。有關如何在express中啓用CORS的示例here

您還應該將標頭中的*更改爲http://serverAddress , https://serverAddress。允許所有網站不是一個好主意,請將其用於測試。

如果您在httphttps服務器之間嘗試AJAX,情況也是如此。請張貼錯誤,只是爲了確保它。

+0

謝謝,我添加了錯誤我接收。我試過啓用CORS,然後我明白,我包括socket.io在客戶端'head'的方式應該改變,所以請求總是對htpps而不是相對於url。它並沒有完全工作,但我得到了一些其他的錯誤,如'NETWORK_ERR:XMLHttpRequest異常101',它必須是因爲我使用Websockets,如果不可用,XHR輪詢。 –

+0

在您添加的錯誤之後出現此錯誤,或者您添加的錯誤被此錯誤取代。 – user568109

+0

這個錯誤取代了最初的。 –

4

可以說,HTTP和HTTPS的node.js服務器對象應該具有監聽任意數量的端口和接口的能力,可以使用SSL和不使用SSL,但目前似乎沒有實現。 (除了server.listen(port)之外,我還可以讓一臺服務器通過向server.listen(handle,[callback])接口傳遞沒有請求偵聽器的第二個服務器作爲「handle」參數來監聽兩個端口。 [主機名],[積壓],[回調]),但它並沒有夾雜SSL /非SSL服務器上運行。)

已經提到的安全通道解決方法當然是一個可行的選擇,但如果不希望安裝單獨的軟件(以避免非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具有相同的效果。換句話說,它將避免需要兩個獨立的socket.io服務器實例,同時也使node.js「https」模塊冗餘。

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但不叫它的監聽方法(假設http和https a已經需要)。相反,只要保留參考。 (VAR IO服務器=需要( 'socket.io'))
  2. 創建HTTP & HTTPS服務器
  3. 創建IO服務器
  4. 的新實例
  5. 綁定你的HTTP和HTTPS服務器(。聽)
  6. 連接HTTP & https服務器實例添加到io實例。 (.listen是.attach的別名)
  7. 設置io事件。

而且客戶端(玉語法,但你的想法):

  1. 包括socket.io script標籤
  2. 呼叫IO和捕獲參考
  3. 設置你的事件處理程序

在客戶端上,您不需要調用io.connect()。此外,我不確定你的選擇。它看起來像你有一個錯字(,,),我無法在1.0文檔中找到任何對secure:true的引用。