Skip to content

Commit 1c9827e

Browse files
feat: add some utility methods
This commit adds the following methods: - fetchSockets: return the matching socket instances - addSockets: make the matching socket instances join the specified rooms - delSockets: make the matching socket instances leave the specified rooms - disconnectSockets: disconnect the matching socket instances Those methods will then be exposed by the Socket.IO server: ```js // clear room io.socketsLeave("room1"); // disconnect all sockets in room io.in("room2").disconnectSockets(); // fetch socket instances in room io.in("room3").fetchSockets(); ``` This feature will also be extended in the Redis adapter to handle multiple Socket.IO servers.
1 parent 985bb41 commit 1c9827e

File tree

2 files changed

+137
-52
lines changed

2 files changed

+137
-52
lines changed

lib/index.ts

+93-49
Original file line numberDiff line numberDiff line change
@@ -125,53 +125,19 @@ export class Adapter extends EventEmitter {
125125
* @public
126126
*/
127127
public broadcast(packet: any, opts: BroadcastOptions): void {
128-
const rooms = opts.rooms;
129128
const flags = opts.flags || {};
130129
const packetOpts = {
131130
preEncoded: true,
132131
volatile: flags.volatile,
133132
compress: flags.compress
134133
};
135-
const ids = new Set();
136-
let except = opts.except || new Set();
137134

138135
packet.nsp = this.nsp.name;
139136
const encodedPackets = this.encoder.encode(packet);
140137

141-
// Allow ids in `except` to be room ids.
142-
if (except.size > 0) {
143-
const exclude = except;
144-
except = new Set(except);
145-
for (const id of exclude) {
146-
if (!this.rooms.has(id)) continue;
147-
for (const sid of this.rooms.get(id)) {
148-
if (sid !== id) {
149-
except.add(sid);
150-
}
151-
}
152-
}
153-
}
154-
155-
if (rooms.size) {
156-
for (const room of rooms) {
157-
if (!this.rooms.has(room)) continue;
158-
159-
for (const id of this.rooms.get(room)) {
160-
if (ids.has(id) || except.has(id)) continue;
161-
const socket = this.nsp.sockets.get(id);
162-
if (socket) {
163-
socket.packet(encodedPackets, packetOpts);
164-
ids.add(id);
165-
}
166-
}
167-
}
168-
} else {
169-
for (const [id] of this.sids) {
170-
if (except.has(id)) continue;
171-
const socket = this.nsp.sockets.get(id);
172-
if (socket) socket.packet(encodedPackets, packetOpts);
173-
}
174-
}
138+
this.apply(opts, socket => {
139+
socket.packet(encodedPackets, packetOpts);
140+
});
175141
}
176142

177143
/**
@@ -182,31 +148,109 @@ export class Adapter extends EventEmitter {
182148
public sockets(rooms: Set<Room>): Promise<Set<SocketId>> {
183149
const sids = new Set<SocketId>();
184150

151+
this.apply({ rooms }, socket => {
152+
sids.add(socket.id);
153+
});
154+
155+
return Promise.resolve(sids);
156+
}
157+
158+
/**
159+
* Gets the list of rooms a given socket has joined.
160+
*
161+
* @param {SocketId} id the socket id
162+
*/
163+
public socketRooms(id: SocketId): Set<Room> | undefined {
164+
return this.sids.get(id);
165+
}
166+
167+
/**
168+
* Returns the matching socket instances
169+
*
170+
* @param opts - the filters to apply
171+
*/
172+
public fetchSockets(opts: BroadcastOptions): Promise<any[]> {
173+
const sockets = [];
174+
175+
this.apply(opts, socket => {
176+
sockets.push(socket);
177+
});
178+
179+
return Promise.resolve(sockets);
180+
}
181+
182+
/**
183+
* Makes the matching socket instances join the specified rooms
184+
*
185+
* @param opts - the filters to apply
186+
* @param rooms - the rooms to join
187+
*/
188+
public addSockets(opts: BroadcastOptions, rooms: Room[]): void {
189+
this.apply(opts, socket => {
190+
socket.join(rooms);
191+
});
192+
}
193+
194+
/**
195+
* Makes the matching socket instances leave the specified rooms
196+
*
197+
* @param opts - the filters to apply
198+
* @param rooms - the rooms to leave
199+
*/
200+
public delSockets(opts: BroadcastOptions, rooms: Room[]): void {
201+
this.apply(opts, socket => {
202+
rooms.forEach(room => socket.leave(room));
203+
});
204+
}
205+
206+
/**
207+
* Makes the matching socket instances disconnect
208+
*
209+
* @param opts - the filters to apply
210+
* @param close - whether to close the underlying connection
211+
*/
212+
public disconnectSockets(opts: BroadcastOptions, close: boolean): void {
213+
this.apply(opts, socket => {
214+
socket.disconnect(close);
215+
});
216+
}
217+
218+
private apply(opts: BroadcastOptions, callback: (socket) => void): void {
219+
const rooms = opts.rooms;
220+
const except = this.computeExceptSids(opts.except);
221+
185222
if (rooms.size) {
223+
const ids = new Set();
186224
for (const room of rooms) {
187225
if (!this.rooms.has(room)) continue;
188226

189227
for (const id of this.rooms.get(room)) {
190-
if (this.nsp.sockets.has(id)) {
191-
sids.add(id);
228+
if (ids.has(id) || except.has(id)) continue;
229+
const socket = this.nsp.sockets.get(id);
230+
if (socket) {
231+
callback(socket);
232+
ids.add(id);
192233
}
193234
}
194235
}
195236
} else {
196237
for (const [id] of this.sids) {
197-
if (this.nsp.sockets.has(id)) sids.add(id);
238+
if (except.has(id)) continue;
239+
const socket = this.nsp.sockets.get(id);
240+
if (socket) callback(socket);
198241
}
199242
}
200-
201-
return Promise.resolve(sids);
202243
}
203244

204-
/**
205-
* Gets the list of rooms a given socket has joined.
206-
*
207-
* @param {SocketId} id the socket id
208-
*/
209-
public socketRooms(id: SocketId): Set<Room> | undefined {
210-
return this.sids.get(id);
245+
private computeExceptSids(exceptRooms?: Set<Room>) {
246+
const exceptSids = new Set();
247+
if (exceptRooms && exceptRooms.size > 0) {
248+
for (const room of exceptRooms) {
249+
if (this.rooms.has(room)) {
250+
this.rooms.get(room).forEach(sid => exceptSids.add(sid));
251+
}
252+
}
253+
}
254+
return exceptSids;
211255
}
212256
}

test/index.js

+44-3
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ describe("socket.io-adapter", () => {
3030
const adapter = new Adapter({
3131
server: { encoder: null },
3232
sockets: new Map([
33-
["s1", true],
34-
["s2", true],
35-
["s3", true]
33+
["s1", { id: "s1" }],
34+
["s2", { id: "s2" }],
35+
["s3", { id: "s3" }]
3636
])
3737
});
3838
adapter.addAll("s1", new Set(["r1", "r2"]));
@@ -124,6 +124,47 @@ describe("socket.io-adapter", () => {
124124
expect(ids).to.eql(["s3"]);
125125
});
126126

127+
describe("utility methods", () => {
128+
let adapter;
129+
130+
before(() => {
131+
adapter = new Adapter({
132+
server: { encoder: null },
133+
sockets: new Map([
134+
["s1", { id: "s1" }],
135+
["s2", { id: "s2" }],
136+
["s3", { id: "s3" }]
137+
])
138+
});
139+
});
140+
141+
describe("fetchSockets", () => {
142+
it("returns the matching socket instances", async () => {
143+
adapter.addAll("s1", new Set(["s1"]));
144+
adapter.addAll("s2", new Set(["s2"]));
145+
adapter.addAll("s3", new Set(["s3"]));
146+
const matchingSockets = await adapter.fetchSockets({
147+
rooms: new Set()
148+
});
149+
expect(matchingSockets).to.be.an(Array);
150+
expect(matchingSockets.length).to.be(3);
151+
});
152+
153+
it("returns the matching socket instances within room", async () => {
154+
adapter.addAll("s1", new Set(["r1", "r2"]));
155+
adapter.addAll("s2", new Set(["r1"]));
156+
adapter.addAll("s3", new Set(["r2"]));
157+
const matchingSockets = await adapter.fetchSockets({
158+
rooms: new Set(["r1"]),
159+
except: new Set(["r2"])
160+
});
161+
expect(matchingSockets).to.be.an(Array);
162+
expect(matchingSockets.length).to.be(1);
163+
expect(matchingSockets[0].id).to.be("s2");
164+
});
165+
});
166+
});
167+
127168
describe("events", () => {
128169
it("should emit a 'create-room' event", done => {
129170
const adapter = new Adapter({ server: { encoder: null } });

0 commit comments

Comments
 (0)