-
Notifications
You must be signed in to change notification settings - Fork 10.1k
Description
You want to:
- report a bug
Current behaviour
If .use() is used to add middleware to a Socket.io server, and the middleware contains asynchronous code, then a connecting client may receive a connect event from the server before the server triggers its own connect event. This can cause some race conditions where, for example, the client emits an event in its connect handler, but the server hasn't bound any event handlers yet.
Steps to reproduce
Create a file test.js with:
// Create a new socket.io server, websocket-only.
var server = require('http').createServer();
var io = require('socket.io')(server, {wsEngine: process.argv[3] || 'uws', transports: ['websocket']});
// Define a delay to use in the middleware.
var DELAY_MS = parseInt(process.argv[2] || 50);
// Add some middleware.
io.use(function(socket, next){
// If DELAY_MS is set to zero, return synchronously.
if (DELAY_MS === 0) {return next();}
// Otherwise do a couple of setTimeouts to run the event loop for a bit.
setTimeout(function() {
setTimeout(next, DELAY_MS);
}, DELAY_MS);
});
// When a socket connects to the server, add an 'event' handler to the socket.
io.on('connect', function(client){
console.log('Server got socket connection!');
client.on('event', function(data, cb){
console.log('Server received event.');
process.exit(0);
});
});
// Start the server.
server.listen(3000);
// Connect a client socket using socket.io-client, websocket-only.
var socket = require('socket.io-client')('http://localhost:3000', {transports: ['websocket']});
// When the client socket gets the "connect" event from the server, send an "event" message.
socket.on('connect', function(){
console.log('Client got socket connection! Sending event...');
socket.emit('event');
});and run with node test.js to see the undesirable behavior. Run with node test.js 0 to see the expected behavior.
Expected behaviour
Ideally, the server should receive its connect event before the client, so the console logs would be:
Server got socket connection!
Client got socket connection! Sending event...
Server received event.
That's what you get if you run node test.js 0, which causes the middleware to return synchronously.
Using the default settings, node test.js instead yields:
Client got socket connection! Sending event...
Server got socket connection!
and hangs.
Stranger yet, if you do node test.js 1 ws, which causes the middleware to run a couple of setTimeouts with 1 ms delays, using the older ws WebSocket engine, you sometimes get:
Client got socket connection! Sending event...
Server got socket connection!
Server received event.
because the packet being sent from the client doesn't arrive until after the server connect handler runs. This effect is more pronounced with the ws WebSocket engine than with uws. Run the tests with DEBUG=* to see all the gory details.
In any case, it seems like the connect event should never be sent to the client before the connect event handler on the server is called!
Setup
- OS: OS X 10.11.1
- browser: n/a
- socket.io version: 1.7.2
- socket.io-client version: 1.7.2