Skip to content

Commit

Permalink
fix: fix race condition in dynamic namespaces
Browse files Browse the repository at this point in the history
Using an async operation with `io.use()` could lead to the creation of
several instances of a same namespace, each of them overriding the
previous one.

Example:

```js
io.use(async (nsp, auth, next) => {
  await anOperationThatTakesSomeTime();
  next();
});
```

Related: #4136

Backported from 9d86397
  • Loading branch information
darrachequesne committed Jun 26, 2022
1 parent 22d4bdf commit 05e1278
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 5 deletions.
1 change: 0 additions & 1 deletion lib/client.js
Expand Up @@ -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);
Expand Down
14 changes: 10 additions & 4 deletions lib/index.js
Expand Up @@ -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);
});
};

Expand Down
36 changes: 36 additions & 0 deletions test/socket.io.js
Expand Up @@ -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);
});
});
});
});

Expand Down

0 comments on commit 05e1278

Please sign in to comment.