diff --git a/lib/client.js b/lib/client.js index 32d179f97..b1f9a22a1 100644 --- a/lib/client.js +++ b/lib/client.js @@ -68,7 +68,6 @@ Client.prototype.connect = function(name, query){ this.server.checkNamespace(name, query, (dynamicNsp) => { if (dynamicNsp) { - debug('dynamic namespace %s was created', dynamicNsp.name); this.doConnect(name, query); } else { debug('creation of namespace %s was denied', name); diff --git a/lib/index.js b/lib/index.js index 5287e4ead..bf0d01e21 100644 --- a/lib/index.js +++ b/lib/index.js @@ -182,11 +182,17 @@ Server.prototype.checkNamespace = function(name, query, fn){ return fn(false); } nextFn.value(name, query, (err, allow) => { - if (err || !allow) { - run(); - } else { - fn(this.parentNsps.get(nextFn.value).createChild(name)); + if (err || !allow) { + return run(); } + if (this.nsps[name]) { + // the namespace was created in the meantime + debug("dynamic namespace %s already exists", name); + return fn(this.nsps[name]); + } + const namespace = this.parentNsps.get(nextFn.value).createChild(name); + debug("dynamic namespace %s was created", name); + fn(namespace); }); }; diff --git a/test/socket.io.js b/test/socket.io.js index b75f0c259..53ca2e656 100644 --- a/test/socket.io.js +++ b/test/socket.io.js @@ -945,6 +945,42 @@ describe('socket.io', function(){ }); }); }); + + it("should handle race conditions with dynamic namespaces (#4136)", (done) => { + const srv = http(); + const sio = io(srv); + const counters = { + connected: 0, + created: 0, + events: 0, + }; + const buffer = []; + srv.listen(() => { + const handler = () => { + if (++counters.events === 2) { + done(); + } + }; + + sio + .of((name, query, next) => { + buffer.push(next); + if (buffer.length === 2) { + buffer.forEach((next) => next(null, true)); + } + }) + .on("connection", (socket) => { + if (++counters.connected === 2) { + sio.of("/dynamic-101").emit("message"); + } + }); + + let one = client(srv, "/dynamic-101"); + let two = client(srv, "/dynamic-101"); + one.on("message", handler); + two.on("message", handler); + }); + }); }); });