Skip to content

Commit 9a13cff

Browse files
Support dynamic namespaces
1 parent a93d05a commit 9a13cff

File tree

3 files changed

+148
-14
lines changed

3 files changed

+148
-14
lines changed

lib/client.js

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -59,26 +59,40 @@ Client.prototype.setup = function(){
5959
*/
6060

6161
Client.prototype.connect = function(name){
62+
var self = this;
6263
debug('connecting to namespace %s', name);
63-
if (!this.server.nsps[name]) {
64-
this.packet({ type: parser.ERROR, nsp: name, data : 'Invalid namespace'}, false, false, true);
65-
return;
64+
65+
function connectNamespace() {
66+
var nsp = self.server.of(name);
67+
if ('/' != name && !self.nsps['/']) {
68+
self.connectBuffer.push(name);
69+
return;
70+
}
71+
72+
var socket = nsp.add(self, function(){
73+
self.sockets.push(socket);
74+
self.nsps[nsp.name] = socket;
75+
76+
if ('/' == nsp.name && self.connectBuffer.length > 0) {
77+
self.connectBuffer.forEach(self.connect, self);
78+
self.connectBuffer = [];
79+
}
80+
});
6681
}
67-
var nsp = this.server.of(name);
68-
if ('/' != name && !this.nsps['/']) {
69-
this.connectBuffer.push(name);
82+
83+
if (self.server.nsps[name]) {
84+
// Namespace already created, connect
85+
connectNamespace();
7086
return;
7187
}
7288

73-
var self = this;
74-
var socket = nsp.add(this, function(){
75-
self.sockets.push(socket);
76-
self.nsps[nsp.name] = socket;
77-
78-
if ('/' == nsp.name && self.connectBuffer.length > 0) {
79-
self.connectBuffer.forEach(self.connect, self);
80-
self.connectBuffer = [];
89+
self.server.checkNamespace(name, function(allow) {
90+
if (allow) {
91+
connectNamespace();
92+
return
8193
}
94+
95+
self.packet({ type: parser.ERROR, nsp: name, data : 'Invalid namespace'}, false, false, true);
8296
});
8397
};
8498

lib/index.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ function Server(srv, opts){
4343
}
4444
opts = opts || {};
4545
this.nsps = {};
46+
this.nspValidators = [];
4647
this.path(opts.path || '/socket.io');
4748
this.serveClient(false !== opts.serveClient);
4849
this.adapter(opts.adapter || Adapter);
@@ -137,6 +138,53 @@ Server.prototype.set = function(key, val){
137138
return this;
138139
};
139140

141+
/**
142+
* Sets up server middleware to validate incoming namespaces not already created on the server.
143+
*
144+
* @return {Server} self
145+
* @api public
146+
*/
147+
148+
Server.prototype.useNamespaceValidator = function(fn){
149+
this.nspValidators.push(fn);
150+
return this;
151+
};
152+
153+
/**
154+
* Executes the middleware for an incoming namespace not already created on the server.
155+
*
156+
* @param name of incomming namespace
157+
* @param {Function} last fn call in the middleware
158+
* @api private
159+
*/
160+
161+
Server.prototype.checkNamespace = function(name, fn){
162+
var fns = this.nspValidators.slice(0);
163+
if (!fns.length) return fn(false);
164+
165+
var namespaceAllowed = false; // Deny unknown namespaces by default
166+
167+
function run(i){
168+
fns[i](name, function(err, allow){
169+
// upon error, short-circuit
170+
if (err) return fn(false);
171+
172+
// if one piece of middleware explicitly denies namespace, short-circuit
173+
if (allow === false) return fn(false);
174+
175+
namespaceAllowed = namespaceAllowed || allow === true;
176+
177+
// if no middleware left, summon callback
178+
if (!fns[i + 1]) return fn(namespaceAllowed);
179+
180+
// go on to next
181+
run(i + 1);
182+
});
183+
}
184+
185+
run(0);
186+
};
187+
140188
/**
141189
* Sets the client serving path.
142190
*

test/socket.io.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,78 @@ describe('socket.io', function(){
730730
});
731731
}
732732
});
733+
734+
it('should allow connections to dynamic namespaces', function(done){
735+
var srv = http();
736+
var sio = io(srv);
737+
srv.listen(function(){
738+
var namespace = '/dynamic';
739+
var dynamic = client(srv,namespace);
740+
sio.useNamespaceValidator(function(nsp, next) {
741+
expect(nsp).to.be(namespace);
742+
next(null, true);
743+
});
744+
dynamic.on('error', function(err) {
745+
expect().fail();
746+
});
747+
dynamic.on('connect', function() {
748+
expect(sio.nsps[namespace]).to.be.a(Namespace);
749+
expect(sio.nsps[namespace].sockets.length).to.be(1);
750+
done();
751+
});
752+
});
753+
});
754+
755+
it('should not allow connections to dynamic namespaces if not supported', function(done){
756+
var srv = http();
757+
var sio = io(srv);
758+
srv.listen(function(){
759+
var namespace = '/dynamic';
760+
sio.useNamespaceValidator(function(nsp, next) {
761+
expect(nsp).to.be(namespace);
762+
next(null, false);
763+
});
764+
sio.on('connect', function(socket) {
765+
if (socket.nsp.name === namespace) {
766+
expect().fail();
767+
}
768+
});
769+
770+
var dynamic = client(srv,namespace);
771+
dynamic.on('connect', function(){
772+
expect().fail();
773+
});
774+
dynamic.on('error', function(err) {
775+
expect(err).to.be("Invalid namespace");
776+
done();
777+
});
778+
});
779+
});
780+
it('should not allow connections to dynamic namespaces if there is an error', function(done){
781+
var srv = http();
782+
var sio = io(srv);
783+
srv.listen(function(){
784+
var namespace = '/dynamic';
785+
sio.useNamespaceValidator(function(nsp, next) {
786+
expect(nsp).to.be(namespace);
787+
next(new Error(), true);
788+
});
789+
sio.on('connect', function(socket) {
790+
if (socket.nsp.name === namespace) {
791+
expect().fail();
792+
}
793+
});
794+
795+
var dynamic = client(srv,namespace);
796+
dynamic.on('connect', function(){
797+
expect().fail();
798+
});
799+
dynamic.on('error', function(err) {
800+
expect(err).to.be("Invalid namespace");
801+
done();
802+
});
803+
});
804+
});
733805
});
734806

735807
describe('socket', function(){

0 commit comments

Comments
 (0)