Skip to content

Commit

Permalink
feat: add support for dynamic namespaces (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
darrachequesne committed Mar 8, 2018
1 parent b77dabd commit 7a39fb4
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 8 deletions.
33 changes: 28 additions & 5 deletions lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,40 @@ Client.prototype.setup = function(){
* Connects a client to a namespace.
*
* @param {String} name namespace
* @param {Object} query the query parameters
* @api private
*/

Client.prototype.connect = function(name, query){
debug('connecting to namespace %s', name);
var nsp = this.server.nsps[name];
if (!nsp) {
this.packet({ type: parser.ERROR, nsp: name, data : 'Invalid namespace'});
return;
if (this.server.nsps[name]) {
debug('connecting to namespace %s', name);
return this.doConnect(name, query);
}

this.server.checkNamespace(name, query, (dynamicNsp) => {
if (dynamicNsp) {
debug('creating dynamic namespace %s', name);
this.server.emit('newNamespace', this.server.of(name));
this.doConnect(dynamicNsp.name, query);
this.doConnect(name, query);
} else {
debug('creation of namespace %s was denied', name);
this.packet({ type: parser.ERROR, nsp: name, data: 'Invalid namespace' });
}
});
};

/**
* Connects a client to a namespace.
*
* @param {String} name namespace
* @param {String} query the query parameters
* @api private
*/

Client.prototype.doConnect = function(name, query){
var nsp = this.server.of(name);

if ('/' != name && !this.nsps['/']) {
this.connectBuffer.push(name);
return;
Expand Down
48 changes: 47 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ function Server(srv, opts){
}
opts = opts || {};
this.nsps = {};
this.dynamicNsps = new Map();
this.path(opts.path || '/socket.io');
this.serveClient(false !== opts.serveClient);
this.parser = opts.parser || parser;
Expand Down Expand Up @@ -157,6 +158,37 @@ Server.prototype.set = function(key, val){
return this;
};

/**
* Executes the middleware for an incoming namespace not already created on the server.
*
* @param {String} name name of incoming namespace
* @param {Object} query the query parameters
* @param {Function} fn callback
* @api private
*/

Server.prototype.checkNamespace = function(name, query, fn){
if (this.dynamicNsps.size === 0) return fn(false);

const keysIterator = this.dynamicNsps.keys();

const run = () => {
let nextFn = keysIterator.next();
if (nextFn.done) {
return fn(false);
}
nextFn.value(name, query, (err, allow) => {
if (err || !allow) {
run();
} else {
fn(this.dynamicNsps.get(nextFn.value));
}
});
};

run();
};

/**
* Sets the client serving path.
*
Expand Down Expand Up @@ -402,12 +434,26 @@ Server.prototype.onconnection = function(conn){
/**
* Looks up a namespace.
*
* @param {String} name nsp name
* @param {String|RegExp|Function} name nsp name
* @param {Function} [fn] optional, nsp `connection` ev handler
* @api public
*/

Server.prototype.of = function(name, fn){
if (typeof name === 'function' || name instanceof RegExp) {
const nspName = '/_' + this.dynamicNsps.size;
debug('initializing dynamic namespace %s', nspName);
const dynamicNsp = new Namespace(this, nspName);
if (typeof name === 'function') {
this.dynamicNsps.set(name, dynamicNsp);
} else {
this.dynamicNsps.set((nsp, conn, next) => next(null, name.test(nsp)), dynamicNsp);
}
this.nsps[nspName] = dynamicNsp;
if (fn) dynamicNsp.on('connect', fn);
return dynamicNsp;
}

if (String(name)[0] !== '/') name = '/' + name;

var nsp = this.nsps[name];
Expand Down
5 changes: 3 additions & 2 deletions lib/namespace.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ module.exports = exports = Namespace;
exports.events = [
'connect', // for symmetry with client
'connection',
'newListener'
'newListener',
'newNamespace'
];

/**
Expand All @@ -45,7 +46,7 @@ var emit = Emitter.prototype.emit;
* Namespace constructor.
*
* @param {Server} server instance
* @param {Socket} name
* @param {String} name
* @api private
*/

Expand Down
26 changes: 26 additions & 0 deletions test/socket.io.js
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,32 @@ describe('socket.io', function(){
});
});
});

describe('dynamic namespaces', function () {
it('should allow connections to dynamic namespaces with regex', function(done){
const srv = http();
const sio = io(srv);
let count = 0;
srv.listen(function(){
const socket = client(srv, '/dynamic-101');
sio.of(/^\/dynamic-\d+$/).on('connect', () => {
if (++count === 3) done();
});
sio.on('newNamespace', (nsp) => {
expect(nsp.name).to.be('/dynamic-101');
nsp.on('connect', (socket) => {
if (++count === 3) done();
});
});
socket.on('error', function(err) {
expect().fail();
});
socket.on('connect', () => {
if (++count === 3) done();
})
});
});
});
});

describe('socket', function(){
Expand Down

0 comments on commit 7a39fb4

Please sign in to comment.