From 239a2a82d0371867d37537f4a5df831aeb18653c Mon Sep 17 00:00:00 2001 From: Gah Tang Date: Fri, 26 Apr 2024 09:36:26 -0400 Subject: [PATCH] fix: fix broadcasting from a parent namespace (#5009) Following [1], emitting from a dynamic namespace to a room would throw this error: > node_modules/socket.io/dist/parent-namespace.js:88 > this.children.forEach((nsp) => { > ^ > > TypeError: Cannot read properties of undefined (reading 'forEach') > at ParentBroadcastAdapter.broadcast (node_modules/socket.io/dist/parent-namespace.js:88:23) > at BroadcastOperator.emit (node_modules/socket.io/dist/broadcast-operator.js:169:26) > at Socket. (server.js:60:33) > at Socket.emit (node:events:520:28) > at Socket.emitReserved (node_modules/socket.io/dist/typed-events.js:56:22) > at Socket._onclose (node_modules/socket.io/dist/socket.js:547:14) > at Client.onclose (node_modules/socket.io/dist/client.js:247:20) > at Socket.emit (node:events:532:35) > at Socket.onClose (node_modules/engine.io/build/socket.js:304:18) > at Object.onceWrapper (node:events:639:28) Previous output code: ```js class ParentNamespace extends namespace_1.Namespace { constructor(server) { super(server, "/_" + ParentNamespace.count++); this.children = new Set(); } _initAdapter() { this.adapter = new ParentBroadcastAdapter(this, this.children); } } ``` Here, `super()` calls `Namespace._initAdapter()`, but `this.children` is not defined yet, hence the problem. [1]: https://github.com/socketio/socket.io/commit/b9ce6a25d1f99368fe21fdc0308213fa1831e450 Related: https://github.com/socketio/socket.io/issues/4985 --- lib/parent-namespace.ts | 8 ++------ test/namespaces.ts | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/lib/parent-namespace.ts b/lib/parent-namespace.ts index 63e7ce56e2..5a158b74e8 100644 --- a/lib/parent-namespace.ts +++ b/lib/parent-namespace.ts @@ -48,7 +48,7 @@ export class ParentNamespace< * @private */ _initAdapter(): void { - this.adapter = new ParentBroadcastAdapter(this, this.children); + this.adapter = new ParentBroadcastAdapter(this); } public emit>( @@ -113,12 +113,8 @@ export class ParentNamespace< * @private file */ class ParentBroadcastAdapter extends Adapter { - constructor(parentNsp: any, private readonly children: Set) { - super(parentNsp); - } - broadcast(packet: any, opts: BroadcastOptions) { - this.children.forEach((nsp) => { + this.nsp.children.forEach((nsp) => { nsp.adapter.broadcast(packet, opts); }); } diff --git a/test/namespaces.ts b/test/namespaces.ts index ffe9602c30..621c589cef 100644 --- a/test/namespaces.ts +++ b/test/namespaces.ts @@ -525,6 +525,37 @@ describe("namespaces", () => { }); }); + it("should allow connections to dynamic namespaces with a regex and emit in a room", (done) => { + const io = new Server(0); + const socket = createClient(io, "/dynamic-101"); + const partialDone = createPartialDone(4, successFn(done, io, socket)); + + let dynamicNsp = io + .of(/^\/dynamic-\d+$/) + .on("connect", (socket) => { + expect(socket.nsp.name).to.be("/dynamic-101"); + socket.join("some-room"); + dynamicNsp.to("some-room").emit("hello", 4, "3", { 2: "1" }); + partialDone(); + }) + .use((socket, next) => { + next(); + partialDone(); + }); + socket.on("connect_error", (err) => { + expect().fail(); + }); + socket.on("connect", () => { + partialDone(); + }); + socket.on("hello", (a, b, c) => { + expect(a).to.eql(4); + expect(b).to.eql("3"); + expect(c).to.eql({ 2: "1" }); + partialDone(); + }); + }); + it("should allow connections to dynamic namespaces with a function", (done) => { const io = new Server(0); const socket = createClient(io, "/dynamic-101");