Issues with Socket.IO (>= 1.0) and Bouncy - node.js

There seems to be some incompatibilities between Socket.IO and Bouncy : in Chrome and Firefox (not in Edge), my browser tabs hang when I open more than 5.
I know it's caused by the NPM module "Bouncy" because if I remove the code that uses it, everything works fine and there's no limit to how many browser tabs I can open. The other culprit is the new version of Socket.IO (>= 1.0, which I just updated to), because when I downgrade back to v0.9.16 the problem goes away as well.
Here's some relevant server code :
var port = 8502;
var bouncy = require('bouncy');
var socketio = require('socket.io');
var express = require("express");
var http = require('http');
var app = express();
var server = http.createServer(app);
var io = socketio.listen(server);
server.listen(port);
var serverBouncer = bouncy(function(req, res, bounce) {
var path = req.url;
var url = req.headers.host;
if (typeof url !== "string") {
res.send(500);
res.end();
return;
}
var urlArray = url.split('.');
var bouncePort = port;
if (!isNaN(urlArray[0]))
bouncePort = parseInt(urlArray[0]);
else if (String(urlArray[0]).toLowerCase() === "www" && !isNaN(urlArray[1]))
bouncePort = parseInt(urlArray[1]);
bounce(bouncePort);
});
serverBouncer.listen(80);
io.on("connection", function(socket) {
//Some events...
});
Here's some relevant client code :
var client = io.connect();
I have been stuck trying to solve this problem for the past 3 days, any help is immensely appreciated.
Update:
Here's what the network tab of chrome dev tools looks like :
Update 2:
I changed the config of socket.io (server side) to force websocket :
io.set('transports', ['websocket', 'polling']);
Same for the client side :
var client = io({transports: ['websocket', 'polling']});
But now the events I emit don't reach the server. Here's what the network tab looks like :
Update 3:
Versions of the main components :
NodeJS v6.2.2
Socket.IO v1.4.8
Bouncy v3.2.2
Cors v2.7.1
Express v4.14.0
Npm v3.10.5

I managed to replace the bouncy module with nginx. See my other question for the solution.
nginx : redirect to port according to domain prefix (dynamically)

Related

Connecting Flash to Node via websockets - Crossdomain.xml issue

I am trying to set up a simple websocket server that communicates between a Flash AS3 client and a Node.js backend. The following code is when both the client and server are hosted locally. The code also functions properly when the swf is hosted locally connected to the server hosted on heroku. The socket only fails when both the swf and the server are hosted online.
Node.js Code (Server)
var WebSocketServer = require("ws").Server
var net = require('net');
var http = require("http")
var express = require("express")
var app = express()
var crossdomain = require('crossdomain')
var port = process.env.PORT || 5000;
app.use(express.static(__dirname + "/"))
var xml = crossdomain({ domain: '*' });
app.all('/crossdomain.xml', function (req, res, next) {
res.writeHead(200, { 'Content-Type': 'application/xml; charset=utf-8' });
res.write(xml);
//res.write(new Buffer([0x00]))
res.end();
});
var server1 = http.createServer(app)
server1.listen(port)
console.log("http server listening on " + port)
var wss = new WebSocketServer({server: server1})
wss.on("connection", function(ws) {
console.log("Connected");
ws.on("close", function() {
console.log("websocket connection close")
})
})
Crossdomain file - example.com/crossdomain.xml
<cross-domain-policy>
<allow-http-request-headers-from domain="*" headers="*"/>
<site-control permitted-cross-domain-policies="all"/>
<allow-access-from domain="*" secure="false"/>
</cross-domain-policy>
AS3 Code (Client) - I'm Using (https://github.com/theturtle32/AS3WebSocket)
import com.worlize.websocket.*;
Security.loadPolicyFile("http://example.com/crossdomain.xml");
var websocket:WebSocket = new WebSocket("ws://example.com", "*", "echo-protocol");
websocket.addEventListener(WebSocketEvent.OPEN, handleWebSocketOpen);
websocket.connect();
var con = false;
function handleWebSocketOpen(event:WebSocketEvent):void {
trace(event);
}
When the client is accessed from the server a request for the crossdomain.xml does occur and it gets returned successfully to the client, but at that point nothing happens anymore. I've been trying to solve this for ages with no luck. I did find that when you return the crossdomain file you need to end it with a 0x00 for it to be properly read, but I haven't found a way to make to that work. I've tried adding res.write(new Buffer([0x00])) to the response from the crossdomain request as well as several other modifications to no avail. These all seem to stop the xml file from being valid anyway.
I would greatly appreciate if anyone could help in getting this to work, I've been struggling with it for an extremely long time and can't quite seem to get this last step working.

Issues with Socket.IO with Chrome and Firefox

Another update:
The problem occurs when running on localhost as well. Since I figured out the problem comes from the proxy server, here's its code :
var serverBouncer = bouncy(function(req, res, bounce) {
var path = req.url;
var url = req.headers.host;
if (typeof url !== "string")
return;
var urlArray = url.split('.');
var bouncePort = port;
if (!isNaN(urlArray[0]))
bouncePort = parseInt(urlArray[0]);
else if (String(urlArray[0]).toLowerCase() === "www" && !isNaN(urlArray[1]))
bouncePort = parseInt(urlArray[1]);
bounce(bouncePort);
});
serverBouncer.listen(80);
Update:
I found where the problem came from!!! But I still need to find the solution... There seems to be issues with using newer versions of Socket.io (>= 1.0) with a proxy server (bouncy, in my case).
I recently updated Socket.IO from v0.9.16 to v1.4.5, as well as adding Express to the mix. However, now I cannot open multiple (number seems to vary) tabs in Chrome and Firefox without experiencing strange issues (Edge is the only one to work well). It either hangs, or partially loads html and other resources before it hangs.
After waiting, I often get the error :
Failed to load resource: the server responded with a status of 400 (Bad Request)
When I close one of the tab that's been hanging, it unblocks the other tabs that were also hanging.
The issues were not present before going through with the changes listed above.
I've been doing research for 2 full days and just now decided to post this, as I know it's very vague and I'm probably not providing enough information. As much as I'd like to, it would take a very long time to remember and list everything I tried during that time.
Using Windows 10 with Chrome v51.0.2704.103, Firefox v43.0.1. The server (CentOS) is using node v6.2.2 with mainly the following modules :
express#4.14.0
npm#3.9.5
socket.io#1.4.5
Here's some relevant server code :
var port = 8502;
var socketio = require('socket.io');
var express = require("express");
var http = require('http');
var app = express();
var server = http.createServer(app);
var io = socketio.listen(server);
server.listen(port);
app.get('/', function(req, res, next) {
//Returning index.html
});
io.on("connection", function(socket) {
//Some events...
});
Here's a bit of the client code :
var client = io.connect();
client.on('connect', function() {
//Some events
})
your binding before the server is listening, try something like this
var app = express();
server = app.listen(PORT, function () {
console.log('Example app listening on port ' + PORT + '!');
});
io.listen(server);
I managed to replace the bouncy module with nginx. See my other question for the solution.
nginx : redirect to port according to domain prefix (dynamically)

Socket.io doesn't correctly attach to http server in express application

I'm pretty new to NodeJS and trying to get Socket.IO running with an Express application. I've tried to stick to the docs and tutorials as close as possible, but in the end, socket.io.js cannot be found on client side.
The server is being started as follows:
var app = require('../app');
app.set('port', process.env.PORT || 3000);
var server = app.listen(app.get('port'), function() { debug('...') } );
Routing/serving pages:
var express = require('express');
var router = express.Router();
router.get('/', function (req, res) {
res.render('index', { title: 'Express' });
});
Including socket.io:
var express = require('express');
var http = require('http').Server(express);
var io = require('socket.io')(http);
And in the HTML file, for clients:
<script src="/socket.io/socket.io.js" type="text/javascript">
But when loading the page, this JS-file is never found. I suspect that including socket.io via that previously created http server is somehow wrong... Or am I missing something else?
Update
I just found out that the snipped that includes socket.io is executed before the server is started (first snippet). They're in different files and the server-start one (named www.js) is configured as "start" script in package.json, the other one (websockets.js) required indirectly by that.
If I put
var io = require('socket.io')(server);
at the end of www.js, everything works just fine. But is there a way to do it from websockets.js, which is loaded before, e.g. by a server start callback function?
Try specifying the full path, like this
<script src="http://localhost:8000/socket.io/socket.io.js"></script>
And connect to your server from client side:
var socket = io.connect('http://localhost:8000/');
also check if socket.io module is available in your node_modules directory.

Use both http and https for socket.io

I am trying to make socket.io work both on http and https connections, but it seems that with my current configuration it can work only on one of them.
With the below config options it can access my application through https, but when trying to access it through 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, e.g. it can access through http and not through https.
How is it possible to achieve this? I need it mostly because it is regarding a Facebook app, so it must provide both http and https connection options according to Facebook's rules.
Edit: In case it helps about the problem, the error I am receiving is this:
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
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.
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.
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.
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.

Socket.io is not served in /socket.io/socket.io.js

i've read the answers here about my problem in node.js, but my problem persists. With socket.io in the server, everything looks ok, but in client side, chrome says:
Failed to load resource: the server responded with a status of 404 (Not Found)
localhost:3382/socket.io/socket.io.js
var express = require("express");
var http = require("http");
var _io = require("socket.io");
var PORT = 3382;
var app = express();
app.listen(PORT, function(){
console.log("Listening from "+PORT);
});
var server = http.createServer(app);
var io = _io.listen(server);
With jade i have:
script(src='/js/jquery.js')
script(src='/js/chat.js')
script(src="socket.io/socket.io.js")
I have the folder js in a folder static, it works ok. I don't know if it's because the versions (node -v show v0.8.16), how do i see versions of express and socket.io?
Read through http://expressjs.com/api.html#app.listen, note how app.listen is defined.
In short: replace app.listen with server.listen.

Resources