diff --git a/LICENSE b/LICENSE index 81a927588c..b248ba1bc5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ (The MIT License) -Copyright (c) 2014 Automattic +Copyright (c) 2014-2015 Automattic Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/Readme.md b/Readme.md index 271ac43169..6bf1f09e0d 100644 --- a/Readme.md +++ b/Readme.md @@ -274,7 +274,7 @@ server.listen(3000); ### Socket#conn:Socket - A reference to the underyling `Client` transport connection (engine.io + A reference to the underlying `Client` transport connection (engine.io `Socket` object). ### Socket#request:Request diff --git a/lib/index.js b/lib/index.js index 92170c3938..64de3b880a 100644 --- a/lib/index.js +++ b/lib/index.js @@ -62,7 +62,7 @@ Server.prototype.checkRequest = function(req, fn) { var origin = req.headers.origin || req.headers.referer; // file:// URLs produce a null Origin which can't be authorized via echo-back - if ('null' == origin) origin = '*'; + if ('null' == origin || null == origin) origin = '*'; if (!!origin && typeof(this._origins) == 'function') return this._origins(origin, fn); if (this._origins.indexOf('*:*') !== -1) return fn(null, true); @@ -194,7 +194,7 @@ Server.prototype.origins = function(v){ Server.prototype.listen = Server.prototype.attach = function(srv, opts){ if ('function' == typeof srv) { - var msg = 'You are trying to attach socket.io to an express' + + var msg = 'You are trying to attach socket.io to an express ' + 'request handler function. Please pass a http.Server instance.'; throw new Error(msg); } diff --git a/lib/socket.js b/lib/socket.js index 4a3aa3d7a0..38440d1f42 100644 --- a/lib/socket.js +++ b/lib/socket.js @@ -242,7 +242,10 @@ Socket.prototype.leave = function(room, fn){ this.adapter.del(this.id, room, function(err){ if (err) return fn && fn(err); debug('left room %s', room); - self.rooms.splice(self.rooms.indexOf(room), 1); + var idx = self.rooms.indexOf(room); + if (idx >= 0) { + self.rooms.splice(idx, 1); + } fn && fn(null); }); return this; diff --git a/package.json b/package.json index f6a50e06ee..b962597bbf 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "socket.io-client": "Automattic/socket.io-client#6b97ec", "socket.io-adapter": "0.3.1", "has-binary-data": "0.1.3", - "debug": "0.7.4" + "debug": "2.1.0" }, "devDependencies": { "mocha": "1.16.2", diff --git a/test/socket.io.js b/test/socket.io.js index dc1e31c096..e6222d8096 100644 --- a/test/socket.io.js +++ b/test/socket.io.js @@ -302,6 +302,21 @@ describe('socket.io', function(){ done(); }); }); + + it('should allow request when origin defined as function and no origin is supplied', function(done) { + var sockets = io({ origins: function(origin,callback){ + if (origin == '*') { + return callback(null, true); + } + return callback(null, false); + } }).listen('54021'); + request.get('http://localhost:54021/socket.io/default/') + .query({ transport: 'polling' }) + .end(function (err, res) { + expect(res.status).to.be(200); + done(); + }); + }); }); describe('close', function(){ @@ -447,7 +462,7 @@ describe('socket.io', function(){ var c1 = client(srv, '/'); var c2 = client(srv, '/abc'); }); - + it('should be equivalent for "" and "/" on client', function(done){ var srv = http(); var sio = io(srv); @@ -456,7 +471,7 @@ describe('socket.io', function(){ }); var c1 = client(srv, ''); }); - + it('should work with `of` and many sockets', function(done){ var srv = http(); var sio = io(srv); @@ -800,6 +815,208 @@ describe('socket.io', function(){ }); }); + it('should not emit volatile event after regular event (polling)', function(done) { + var srv = http(); + var sio = io(srv, { transports: ['polling'] }); + + var counter = 0; + srv.listen(function(){ + sio.on('connection', function(s){ + s.emit('ev', 'data'); + s.volatile.emit('ev', 'data'); + }); + + var socket = client(srv, { transports: ['polling'] }); + socket.on('ev', function() { + counter++; + }); + }); + + setTimeout(function() { + expect(counter).to.be(1); + done(); + }, 200); + }); + + it('should not emit volatile event after regular event (ws)', function(done) { + var srv = http(); + var sio = io(srv, { transports: ['websocket'] }); + + var counter = 0; + srv.listen(function(){ + sio.on('connection', function(s){ + s.emit('ev', 'data'); + s.volatile.emit('ev', 'data'); + }); + + var socket = client(srv, { transports: ['websocket'] }); + socket.on('ev', function() { + counter++; + }); + }); + + setTimeout(function() { + expect(counter).to.be(1); + done(); + }, 200); + }); + + it('should emit volatile event (polling)', function(done) { + var srv = http(); + var sio = io(srv, { transports: ['polling'] }); + + var counter = 0; + srv.listen(function(){ + sio.on('connection', function(s){ + // Wait to make sure there are no packets being sent for opening the connection + setTimeout(function() { + s.volatile.emit('ev', 'data'); + }, 20); + }); + + var socket = client(srv, { transports: ['polling'] }); + socket.on('ev', function() { + counter++; + }); + }); + + setTimeout(function() { + expect(counter).to.be(1); + done(); + }, 200); + }); + + it('should emit volatile event (ws)', function(done) { + var srv = http(); + var sio = io(srv, { transports: ['websocket'] }); + + var counter = 0; + srv.listen(function(){ + sio.on('connection', function(s){ + // Wait to make sure there are no packets being sent for opening the connection + setTimeout(function() { + s.volatile.emit('ev', 'data'); + }, 20); + }); + + var socket = client(srv, { transports: ['websocket'] }); + socket.on('ev', function() { + counter++; + }); + }); + + setTimeout(function() { + expect(counter).to.be(1); + done(); + }, 200); + }); + + it('should emit only one consecutive volatile event (polling)', function(done) { + var srv = http(); + var sio = io(srv, { transports: ['polling'] }); + + var counter = 0; + srv.listen(function(){ + sio.on('connection', function(s){ + // Wait to make sure there are no packets being sent for opening the connection + setTimeout(function() { + s.volatile.emit('ev', 'data'); + s.volatile.emit('ev', 'data'); + }, 20); + }); + + var socket = client(srv, { transports: ['polling'] }); + socket.on('ev', function() { + counter++; + }); + }); + + setTimeout(function() { + expect(counter).to.be(1); + done(); + }, 200); + }); + + it('should emit only one consecutive volatile event (ws)', function(done) { + var srv = http(); + var sio = io(srv, { transports: ['websocket'] }); + + var counter = 0; + srv.listen(function(){ + sio.on('connection', function(s){ + // Wait to make sure there are no packets being sent for opening the connection + setTimeout(function() { + s.volatile.emit('ev', 'data'); + s.volatile.emit('ev', 'data'); + }, 20); + }); + + var socket = client(srv, { transports: ['websocket'] }); + socket.on('ev', function() { + counter++; + }); + }); + + setTimeout(function() { + expect(counter).to.be(1); + done(); + }, 200); + }); + + it('should emit regular events after trying a failed volatile event (polling)', function(done) { + var srv = http(); + var sio = io(srv, { transports: ['polling'] }); + + var counter = 0; + srv.listen(function(){ + sio.on('connection', function(s){ + // Wait to make sure there are no packets being sent for opening the connection + setTimeout(function() { + s.emit('ev', 'data'); + s.volatile.emit('ev', 'data'); + s.emit('ev', 'data'); + }, 20); + }); + + var socket = client(srv, { transports: ['polling'] }); + socket.on('ev', function() { + counter++; + }); + }); + + setTimeout(function() { + expect(counter).to.be(2); + done(); + }, 200); + }); + + it('should emit regular events after trying a failed volatile event (ws)', function(done) { + var srv = http(); + var sio = io(srv, { transports: ['websocket'] }); + + var counter = 0; + srv.listen(function(){ + sio.on('connection', function(s){ + // Wait to make sure there are no packets being sent for opening the connection + setTimeout(function() { + s.emit('ev', 'data'); + s.volatile.emit('ev', 'data'); + s.emit('ev', 'data'); + }, 20); + }); + + var socket = client(srv, { transports: ['websocket'] }); + socket.on('ev', function() { + counter++; + }); + }); + + setTimeout(function() { + expect(counter).to.be(2); + done(); + }, 200); + }); + it('should emit message events through `send`', function(done){ var srv = http(); var sio = io(srv); @@ -1086,6 +1303,32 @@ describe('socket.io', function(){ }); }); }); + + it('should be able to emit after server close and restart', function(done){ + var srv = http(); + var sio = io(srv); + + sio.on('connection', function(socket){ + socket.on('ev', function(data){ + expect(data).to.be('payload'); + done(); + }); + }); + + srv.listen(function(){ + var port = srv.address().port; + var clientSocket = client(srv, { reconnectionAttempts: 10, reconnectionDelay: 100 }); + clientSocket.once('connect', function(){ + srv.close(function(){ + srv.listen(port, function(){ + clientSocket.on('reconnect', function(){ + clientSocket.emit('ev', 'payload'); + }); + }); + }); + }); + }); + }); }); describe('messaging many', function(){ @@ -1385,6 +1628,30 @@ describe('socket.io', function(){ }); }); }); + + it('should properly cleanup left rooms', function(done){ + var srv = http(); + var sio = io(srv); + + srv.listen(function(){ + var socket = client(srv); + sio.on('connection', function(s){ + s.join('a', function(){ + expect(s.rooms).to.eql([s.id, 'a']); + s.join('b', function(){ + expect(s.rooms).to.eql([s.id, 'a', 'b']); + s.leave('unknown', function(){ + expect(s.rooms).to.eql([s.id, 'a', 'b']); + s.leaveAll(); + expect(s.rooms).to.eql([]); + done(); + }); + }); + }); + }); + }); + }); + }); describe('middleware', function(done){