From 1f8bb8a0ece6eae2a57c529448267daf7549c300 Mon Sep 17 00:00:00 2001 From: Dylan Lingelbach Date: Fri, 19 Dec 2014 13:22:41 -0600 Subject: [PATCH 1/6] Support dynamic namespaces --- lib/client.js | 47 ++++++++++++++++++++++++++++------------------ lib/index.js | 48 +++++++++++++++++++++++++++++++++++++++++++++++ test/socket.io.js | 28 +++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 18 deletions(-) diff --git a/lib/client.js b/lib/client.js index 440c75edda..e1d4378ef0 100644 --- a/lib/client.js +++ b/lib/client.js @@ -59,27 +59,38 @@ Client.prototype.setup = function(){ */ Client.prototype.connect = function(name){ - debug('connecting to namespace %s', name); - if (!this.server.nsps[name]) { - this.packet({ type: parser.ERROR, nsp: name, data : 'Invalid namespace'}); - return; - } - var nsp = this.server.of(name); - if ('/' != name && !this.nsps['/']) { - this.connectBuffer.push(name); - return; - } - var self = this; - var socket = nsp.add(this, function(){ - self.sockets.push(socket); - self.nsps[nsp.name] = socket; + debug('connecting to namespace %s', name); - if ('/' == nsp.name && self.connectBuffer.length > 0) { - self.connectBuffer.forEach(self.connect, self); - self.connectBuffer = []; + function connectNamespace() { + var nsp = self.server.of(name); + if ('/' != name && !self.nsps['/']) { + self.connectBuffer.push(name); + return; } - }); + + var socket = nsp.add(self, function(){ + self.sockets.push(socket); + self.nsps[nsp.name] = socket; + + if ('/' == nsp.name && self.connectBuffer.length > 0) { + self.connectBuffer.forEach(self.connect, self); + self.connectBuffer = []; + } + }); + } + + if (!self.server.nsps[name]) { + self.server.checkNamespace(name, function(allow) { + if (allow) { + connectNamespace(); + } else { + self.packet({ type: parser.ERROR, nsp: name, data : 'Invalid namespace'}); + } + }); + } else { + connectNamespace(); + } }; /** diff --git a/lib/index.js b/lib/index.js index 92170c3938..a6060fdb02 100644 --- a/lib/index.js +++ b/lib/index.js @@ -43,6 +43,7 @@ function Server(srv, opts){ } opts = opts || {}; this.nsps = {}; + this.fns = []; this.path(opts.path || '/socket.io'); this.serveClient(false !== opts.serveClient); this.adapter(opts.adapter || Adapter); @@ -134,6 +135,53 @@ Server.prototype.set = function(key, val){ return this; }; +/** + * Sets up server middleware to check incoming namespaces. + * + * @return {Server} self + * @api public + */ + +Server.prototype.useNamespace = function(fn){ + this.fns.push(fn); + return this; +}; + +/** + * Executes the middleware for an incoming connection. + * + * @param name of incomming namespace + * @param {Function} last fn call in the middleware + * @api private + */ + +Server.prototype.checkNamespace = function(name, fn){ + var fns = this.fns.slice(0); + if (!fns.length) return fn(false); + + var namespaceAllowed = false; // Deny unknown namespaces by default + + function run(i){ + fns[i](name, function(err, allow){ + // upon error, short-circuit + if (err) return fn(false); + + // if one piece of middleware explicitly denies namespace, short-circuit + if (allow === false) return fn(false); + + namespaceAllowed = namespaceAllowed || allow === true; + + // if no middleware left, summon callback + if (!fns[i + 1]) return fn(namespaceAllowed); + + // go on to next + run(i + 1); + }); + } + + run(0); +}; + /** * Sets the client serving path. * diff --git a/test/socket.io.js b/test/socket.io.js index 8e66564ab2..c4c16cde18 100644 --- a/test/socket.io.js +++ b/test/socket.io.js @@ -571,6 +571,34 @@ describe('socket.io', function(){ }); }); }); + + it('should allow connections to dynamic namespaces', function(done){ + var srv = http(); + var sio = io(srv); + srv.listen(function(){ + var namespace = '/dynamic'; + var dynamic = client(srv,namespace); + sio.useNamespace(function(nsp, next) { + expect(nsp).to.be(namespace); + next(null, true); + }); + dynamic.on('error', function(err) { + expect(err).to.be(null); + done(); + }); + var total = 2; + dynamic.on('connect', function() { + --total || done(); + }); + sio.on('connect', function(socket){ + if (socket.nsp.name === '/') return; + + expect(socket).to.be.a(Socket); + expect(socket.nsp.name).to.be(namespace); + --total || done(); + }); + }); + }); }); describe('socket', function(){ From 1b77c27f7bb81d2aebfacb6caa9f404a1b46ea5d Mon Sep 17 00:00:00 2001 From: Dylan Lingelbach Date: Fri, 19 Dec 2014 15:27:08 -0600 Subject: [PATCH 2/6] Fix test --- test/socket.io.js | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/test/socket.io.js b/test/socket.io.js index c4c16cde18..284e51b951 100644 --- a/test/socket.io.js +++ b/test/socket.io.js @@ -586,17 +586,7 @@ describe('socket.io', function(){ expect(err).to.be(null); done(); }); - var total = 2; - dynamic.on('connect', function() { - --total || done(); - }); - sio.on('connect', function(socket){ - if (socket.nsp.name === '/') return; - - expect(socket).to.be.a(Socket); - expect(socket.nsp.name).to.be(namespace); - --total || done(); - }); + dynamic.on('connect', done); }); }); }); From cc7ce79251a432f440850d176a756b9bba8673b5 Mon Sep 17 00:00:00 2001 From: Dylan Lingelbach Date: Fri, 19 Dec 2014 15:32:20 -0600 Subject: [PATCH 3/6] Ensure server sends connect message --- test/socket.io.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/socket.io.js b/test/socket.io.js index 284e51b951..3a9b31accb 100644 --- a/test/socket.io.js +++ b/test/socket.io.js @@ -584,7 +584,15 @@ describe('socket.io', function(){ }); dynamic.on('error', function(err) { expect(err).to.be(null); - done(); + var dsio = io.of('/dynamic'); + + dsio.on('connect', function(socket) { + + expect(socket).to.be.a(Socket); + expect(socket.nsp.name).to.be(namespace); + + done(); + }); }); dynamic.on('connect', done); }); From de5cbdb833cdde2389daa11e04af0d2fb90cf826 Mon Sep 17 00:00:00 2001 From: Dylan Lingelbach Date: Wed, 4 Feb 2015 12:33:18 -0600 Subject: [PATCH 4/6] Improve tests --- test/socket.io.js | 64 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/test/socket.io.js b/test/socket.io.js index 3a9b31accb..43b49599a6 100644 --- a/test/socket.io.js +++ b/test/socket.io.js @@ -583,18 +583,64 @@ describe('socket.io', function(){ next(null, true); }); dynamic.on('error', function(err) { - expect(err).to.be(null); - var dsio = io.of('/dynamic'); + expect().fail(); + }); + dynamic.on('connect', function() { + expect(sio.nsps[namespace]).to.be.a(Namespace); + expect(sio.nsps[namespace].sockets.length).to.be(1); + done(); + }); + }); + }); - dsio.on('connect', function(socket) { + it('should not allow connections to dynamic namespaces if not supported', function(done){ + var srv = http(); + var sio = io(srv); + srv.listen(function(){ + var namespace = '/dynamic'; + sio.useNamespace(function(nsp, next) { + expect(nsp).to.be(namespace); + next(null, false); + }); + sio.on('connect', function(socket) { + if (socket.nsp.name === namespace) { + expect().fail(); + } + }); - expect(socket).to.be.a(Socket); - expect(socket.nsp.name).to.be(namespace); - - done(); - }); + var dynamic = client(srv,namespace); + dynamic.on('connect', function(){ + expect().fail(); + }); + dynamic.on('error', function(err) { + expect(err).to.be("Invalid namespace"); + done(); + }); + }); + }); + it('should not allow connections to dynamic namespaces if there is an error', function(done){ + var srv = http(); + var sio = io(srv); + srv.listen(function(){ + var namespace = '/dynamic'; + sio.useNamespace(function(nsp, next) { + expect(nsp).to.be(namespace); + next(new Error(), true); + }); + sio.on('connect', function(socket) { + if (socket.nsp.name === namespace) { + expect().fail(); + } + }); + + var dynamic = client(srv,namespace); + dynamic.on('connect', function(){ + expect().fail(); + }); + dynamic.on('error', function(err) { + expect(err).to.be("Invalid namespace"); + done(); }); - dynamic.on('connect', done); }); }); }); From 3bf7be73a04bafd70909e4647741d5f39b6f90fc Mon Sep 17 00:00:00 2001 From: Dylan Lingelbach Date: Wed, 4 Feb 2015 13:40:40 -0600 Subject: [PATCH 5/6] Style cleanup --- lib/client.js | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/client.js b/lib/client.js index e1d4378ef0..ac170703de 100644 --- a/lib/client.js +++ b/lib/client.js @@ -80,17 +80,20 @@ Client.prototype.connect = function(name){ }); } - if (!self.server.nsps[name]) { - self.server.checkNamespace(name, function(allow) { - if (allow) { - connectNamespace(); - } else { - self.packet({ type: parser.ERROR, nsp: name, data : 'Invalid namespace'}); - } - }); - } else { + if (self.server.nsps[name]) { + // Namespace already created, connect connectNamespace(); + return; } + + self.server.checkNamespace(name, function(allow) { + if (allow) { + connectNamespace(); + return + } + + self.packet({ type: parser.ERROR, nsp: name, data : 'Invalid namespace'}); + }); }; /** From dd72676c251066e797b19f1ecd2ff57f063dd924 Mon Sep 17 00:00:00 2001 From: Dylan Lingelbach Date: Wed, 4 Feb 2015 13:43:21 -0600 Subject: [PATCH 6/6] Make name more descriptive --- lib/index.js | 12 ++++++------ test/socket.io.js | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/index.js b/lib/index.js index a6060fdb02..d78e01b430 100644 --- a/lib/index.js +++ b/lib/index.js @@ -43,7 +43,7 @@ function Server(srv, opts){ } opts = opts || {}; this.nsps = {}; - this.fns = []; + this.nspValidators = []; this.path(opts.path || '/socket.io'); this.serveClient(false !== opts.serveClient); this.adapter(opts.adapter || Adapter); @@ -136,19 +136,19 @@ Server.prototype.set = function(key, val){ }; /** - * Sets up server middleware to check incoming namespaces. + * Sets up server middleware to validate incoming namespaces not already created on the server. * * @return {Server} self * @api public */ -Server.prototype.useNamespace = function(fn){ - this.fns.push(fn); +Server.prototype.useNamespaceValidator = function(fn){ + this.nspValidators.push(fn); return this; }; /** - * Executes the middleware for an incoming connection. + * Executes the middleware for an incoming namespace not already created on the server. * * @param name of incomming namespace * @param {Function} last fn call in the middleware @@ -156,7 +156,7 @@ Server.prototype.useNamespace = function(fn){ */ Server.prototype.checkNamespace = function(name, fn){ - var fns = this.fns.slice(0); + var fns = this.nspValidators.slice(0); if (!fns.length) return fn(false); var namespaceAllowed = false; // Deny unknown namespaces by default diff --git a/test/socket.io.js b/test/socket.io.js index 43b49599a6..990aca20e9 100644 --- a/test/socket.io.js +++ b/test/socket.io.js @@ -578,7 +578,7 @@ describe('socket.io', function(){ srv.listen(function(){ var namespace = '/dynamic'; var dynamic = client(srv,namespace); - sio.useNamespace(function(nsp, next) { + sio.useNamespaceValidator(function(nsp, next) { expect(nsp).to.be(namespace); next(null, true); }); @@ -598,7 +598,7 @@ describe('socket.io', function(){ var sio = io(srv); srv.listen(function(){ var namespace = '/dynamic'; - sio.useNamespace(function(nsp, next) { + sio.useNamespaceValidator(function(nsp, next) { expect(nsp).to.be(namespace); next(null, false); }); @@ -623,7 +623,7 @@ describe('socket.io', function(){ var sio = io(srv); srv.listen(function(){ var namespace = '/dynamic'; - sio.useNamespace(function(nsp, next) { + sio.useNamespaceValidator(function(nsp, next) { expect(nsp).to.be(namespace); next(new Error(), true); });