Skip to content

Commit

Permalink
feat: allow to exclude specific rooms when broadcasting (#3789)
Browse files Browse the repository at this point in the history
New syntax:

```
io.except("room1").emit(...);
io.to("room1").except("room2").emit(...);

socket.broadcast.except("room1").emit(...);
socket.to("room1").except("room2").emit(...);
```

Related:

- #3629
- #3657
  • Loading branch information
sebamarynissen authored and darrachequesne committed Mar 1, 2021
1 parent 225ade0 commit 7de2e87
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 5 deletions.
12 changes: 12 additions & 0 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,18 @@ export class Server extends EventEmitter {
return this;
}

/**
* Excludes a room when emitting.
*
* @param name
* @return self
* @public
*/
public except(name: Room): Server {
this.sockets.except(name);
return this;
}

/**
* Sends a `message` event to all clients.
*
Expand Down
18 changes: 18 additions & 0 deletions lib/namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ export class Namespace extends EventEmitter {
/** @private */
_rooms: Set<Room> = new Set();

/** @private */
_except: Set<Room> = new Set();

/** @private */
_flags: any = {};

Expand Down Expand Up @@ -123,6 +126,18 @@ export class Namespace extends EventEmitter {
return this;
}

/**
* Excludes a room when emitting.
*
* @param name
* @return self
* @public
*/
public except(name: Room): Namespace {
this._except.add(name);
return this;
}

/**
* Adds a new client.
*
Expand Down Expand Up @@ -203,14 +218,17 @@ export class Namespace extends EventEmitter {

const rooms = new Set(this._rooms);
const flags = Object.assign({}, this._flags);
const except = new Set(this._except);

// reset flags
this._rooms.clear();
this._flags = {};
this._except.clear();

this.adapter.broadcast(packet, {
rooms: rooms,
flags: flags,
except: except,
});

return true;
Expand Down
17 changes: 16 additions & 1 deletion lib/socket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export class Socket extends EventEmitter {
> = [];
private flags: BroadcastFlags = {};
private _rooms: Set<Room> = new Set();
private _except: Set<Room> = new Set();
private _anyListeners?: Array<(...args: any[]) => void>;

/**
Expand Down Expand Up @@ -166,14 +167,16 @@ export class Socket extends EventEmitter {

const rooms = new Set(this._rooms);
const flags = Object.assign({}, this.flags);
const except = new Set(this._except);

// reset flags
this._rooms.clear();
this.flags = {};
this._except.clear();

if (rooms.size || flags.broadcast) {
this.adapter.broadcast(packet, {
except: new Set([this.id]),
except: new Set([this.id, ...except]),
rooms: rooms,
flags: flags,
});
Expand Down Expand Up @@ -208,6 +211,18 @@ export class Socket extends EventEmitter {
return this;
}

/**
* Excludes a room when broadcasting.
*
* @param name
* @return self
* @public
*/
public except(name: Room): Socket {
this._except.add(name);
return this;
}

/**
* Sends a `message` event.
*
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"base64id": "~2.0.0",
"debug": "~4.3.1",
"engine.io": "~4.1.0",
"socket.io-adapter": "~2.1.0",
"socket.io-adapter": "~2.2.0",
"socket.io-parser": "~4.0.3"
},
"devDependencies": {
Expand Down
117 changes: 117 additions & 0 deletions test/socket.io.ts
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,57 @@ describe("socket.io", () => {
});
});

it("should exclude a specific socket when emitting", (done) => {
const srv = createServer();
const sio = new Server(srv);

const nsp = sio.of("/nsp");

srv.listen(() => {
const socket1 = client(srv, "/nsp");
const socket2 = client(srv, "/nsp");

socket2.on("a", () => {
done(new Error("not"));
});
socket1.on("a", () => {
done();
});

socket2.on("connect", () => {
nsp.except(socket2.id).emit("a");
});
});
});

it("should exclude a specific room when emitting", (done) => {
const srv = createServer();
const sio = new Server(srv);

const nsp = sio.of("/nsp");

srv.listen(() => {
const socket1 = client(srv, "/nsp");
const socket2 = client(srv, "/nsp");

socket1.on("a", () => {
done();
});
socket2.on("a", () => {
done(new Error("not"));
});

nsp.on("connection", (socket) => {
socket.on("broadcast", () => {
socket.join("room1");
nsp.except("room1").emit("a");
});
});

socket2.emit("broadcast");
});
});

describe("dynamic namespaces", () => {
it("should allow connections to dynamic namespaces with a regex", (done) => {
const srv = createServer();
Expand Down Expand Up @@ -2222,6 +2273,72 @@ describe("socket.io", () => {
});
});
});

it("should exclude specific sockets when broadcasting", (done) => {
const srv = createServer();
const sio = new Server(srv);

srv.listen(() => {
const socket1 = client(srv, { multiplex: false });
const socket2 = client(srv, { multiplex: false });
const socket3 = client(srv, { multiplex: false });

socket2.on("a", () => {
done(new Error("not"));
});
socket3.on("a", () => {
done(new Error("not"));
});
socket1.on("a", () => {
done();
});

sio.on("connection", (socket) => {
socket.on("exclude", (id) => {
socket.broadcast.except(id).emit("a");
});
});

socket2.on("connect", () => {
socket3.emit("exclude", socket2.id);
});
});
});

it("should exclude a specific room when broadcasting", (done) => {
const srv = createServer();
const sio = new Server(srv);

srv.listen(() => {
const socket1 = client(srv, { multiplex: false });
const socket2 = client(srv, { multiplex: false });
const socket3 = client(srv, { multiplex: false });

socket2.on("a", () => {
done(new Error("not"));
});
socket3.on("a", () => {
done(new Error("not"));
});
socket1.on("a", () => {
done();
});

sio.on("connection", (socket) => {
socket.on("join", (room, cb) => {
socket.join(room);
cb();
});
socket.on("broadcast", () => {
socket.broadcast.except("room1").emit("a");
});
});

socket2.emit("join", "room1", () => {
socket3.emit("broadcast");
});
});
});
});

describe("middleware", () => {
Expand Down

0 comments on commit 7de2e87

Please sign in to comment.