index.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. import { EventEmitter } from "events";
  2. export class Adapter extends EventEmitter {
  3. /**
  4. * In-memory adapter constructor.
  5. *
  6. * @param {Namespace} nsp
  7. */
  8. constructor(nsp) {
  9. super();
  10. this.nsp = nsp;
  11. this.rooms = new Map();
  12. this.sids = new Map();
  13. this.encoder = nsp.server.encoder;
  14. }
  15. /**
  16. * To be overridden
  17. */
  18. init() { }
  19. /**
  20. * To be overridden
  21. */
  22. close() { }
  23. /**
  24. * Adds a socket to a list of room.
  25. *
  26. * @param {SocketId} id the socket id
  27. * @param {Set<Room>} rooms a set of rooms
  28. * @public
  29. */
  30. addAll(id, rooms) {
  31. if (!this.sids.has(id)) {
  32. this.sids.set(id, new Set());
  33. }
  34. for (const room of rooms) {
  35. this.sids.get(id).add(room);
  36. if (!this.rooms.has(room)) {
  37. this.rooms.set(room, new Set());
  38. this.emit("create-room", room);
  39. }
  40. if (!this.rooms.get(room).has(id)) {
  41. this.rooms.get(room).add(id);
  42. this.emit("join-room", room, id);
  43. }
  44. }
  45. }
  46. /**
  47. * Removes a socket from a room.
  48. *
  49. * @param {SocketId} id the socket id
  50. * @param {Room} room the room name
  51. */
  52. del(id, room) {
  53. if (this.sids.has(id)) {
  54. this.sids.get(id).delete(room);
  55. }
  56. this._del(room, id);
  57. }
  58. _del(room, id) {
  59. const _room = this.rooms.get(room);
  60. if (_room != null) {
  61. const deleted = _room.delete(id);
  62. if (deleted) {
  63. this.emit("leave-room", room, id);
  64. }
  65. if (_room.size === 0 && this.rooms.delete(room)) {
  66. this.emit("delete-room", room);
  67. }
  68. }
  69. }
  70. /**
  71. * Removes a socket from all rooms it's joined.
  72. *
  73. * @param {SocketId} id the socket id
  74. */
  75. delAll(id) {
  76. if (!this.sids.has(id)) {
  77. return;
  78. }
  79. for (const room of this.sids.get(id)) {
  80. this._del(room, id);
  81. }
  82. this.sids.delete(id);
  83. }
  84. /**
  85. * Broadcasts a packet.
  86. *
  87. * Options:
  88. * - `flags` {Object} flags for this packet
  89. * - `except` {Array} sids that should be excluded
  90. * - `rooms` {Array} list of rooms to broadcast to
  91. *
  92. * @param {Object} packet the packet object
  93. * @param {Object} opts the options
  94. * @public
  95. */
  96. broadcast(packet, opts) {
  97. const flags = opts.flags || {};
  98. const basePacketOpts = {
  99. preEncoded: true,
  100. volatile: flags.volatile,
  101. compress: flags.compress
  102. };
  103. packet.nsp = this.nsp.name;
  104. const encodedPackets = this.encoder.encode(packet);
  105. const packetOpts = encodedPackets.map(encodedPacket => {
  106. if (typeof encodedPacket === "string") {
  107. return {
  108. ...basePacketOpts,
  109. wsPreEncoded: "4" + encodedPacket // "4" being the "message" packet type in Engine.IO
  110. };
  111. }
  112. else {
  113. return basePacketOpts;
  114. }
  115. });
  116. this.apply(opts, socket => {
  117. for (let i = 0; i < encodedPackets.length; i++) {
  118. socket.client.writeToEngine(encodedPackets[i], packetOpts[i]);
  119. }
  120. });
  121. }
  122. /**
  123. * Gets a list of sockets by sid.
  124. *
  125. * @param {Set<Room>} rooms the explicit set of rooms to check.
  126. */
  127. sockets(rooms) {
  128. const sids = new Set();
  129. this.apply({ rooms }, socket => {
  130. sids.add(socket.id);
  131. });
  132. return Promise.resolve(sids);
  133. }
  134. /**
  135. * Gets the list of rooms a given socket has joined.
  136. *
  137. * @param {SocketId} id the socket id
  138. */
  139. socketRooms(id) {
  140. return this.sids.get(id);
  141. }
  142. /**
  143. * Returns the matching socket instances
  144. *
  145. * @param opts - the filters to apply
  146. */
  147. fetchSockets(opts) {
  148. const sockets = [];
  149. this.apply(opts, socket => {
  150. sockets.push(socket);
  151. });
  152. return Promise.resolve(sockets);
  153. }
  154. /**
  155. * Makes the matching socket instances join the specified rooms
  156. *
  157. * @param opts - the filters to apply
  158. * @param rooms - the rooms to join
  159. */
  160. addSockets(opts, rooms) {
  161. this.apply(opts, socket => {
  162. socket.join(rooms);
  163. });
  164. }
  165. /**
  166. * Makes the matching socket instances leave the specified rooms
  167. *
  168. * @param opts - the filters to apply
  169. * @param rooms - the rooms to leave
  170. */
  171. delSockets(opts, rooms) {
  172. this.apply(opts, socket => {
  173. rooms.forEach(room => socket.leave(room));
  174. });
  175. }
  176. /**
  177. * Makes the matching socket instances disconnect
  178. *
  179. * @param opts - the filters to apply
  180. * @param close - whether to close the underlying connection
  181. */
  182. disconnectSockets(opts, close) {
  183. this.apply(opts, socket => {
  184. socket.disconnect(close);
  185. });
  186. }
  187. apply(opts, callback) {
  188. const rooms = opts.rooms;
  189. const except = this.computeExceptSids(opts.except);
  190. if (rooms.size) {
  191. const ids = new Set();
  192. for (const room of rooms) {
  193. if (!this.rooms.has(room))
  194. continue;
  195. for (const id of this.rooms.get(room)) {
  196. if (ids.has(id) || except.has(id))
  197. continue;
  198. const socket = this.nsp.sockets.get(id);
  199. if (socket) {
  200. callback(socket);
  201. ids.add(id);
  202. }
  203. }
  204. }
  205. }
  206. else {
  207. for (const [id] of this.sids) {
  208. if (except.has(id))
  209. continue;
  210. const socket = this.nsp.sockets.get(id);
  211. if (socket)
  212. callback(socket);
  213. }
  214. }
  215. }
  216. computeExceptSids(exceptRooms) {
  217. const exceptSids = new Set();
  218. if (exceptRooms && exceptRooms.size > 0) {
  219. for (const room of exceptRooms) {
  220. if (this.rooms.has(room)) {
  221. this.rooms.get(room).forEach(sid => exceptSids.add(sid));
  222. }
  223. }
  224. }
  225. return exceptSids;
  226. }
  227. /**
  228. * Send a packet to the other Socket.IO servers in the cluster
  229. * @param packet - an array of arguments, which may include an acknowledgement callback at the end
  230. */
  231. serverSideEmit(packet) {
  232. throw new Error("this adapter does not support the serverSideEmit() functionality");
  233. }
  234. }