broadcast-operator.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.RemoteSocket = exports.BroadcastOperator = void 0;
  4. const socket_1 = require("./socket");
  5. const socket_io_parser_1 = require("socket.io-parser");
  6. class BroadcastOperator {
  7. constructor(adapter, rooms = new Set(), exceptRooms = new Set(), flags = {}) {
  8. this.adapter = adapter;
  9. this.rooms = rooms;
  10. this.exceptRooms = exceptRooms;
  11. this.flags = flags;
  12. }
  13. /**
  14. * Targets a room when emitting.
  15. *
  16. * @param room
  17. * @return a new BroadcastOperator instance
  18. * @public
  19. */
  20. to(room) {
  21. const rooms = new Set(this.rooms);
  22. if (Array.isArray(room)) {
  23. room.forEach((r) => rooms.add(r));
  24. }
  25. else {
  26. rooms.add(room);
  27. }
  28. return new BroadcastOperator(this.adapter, rooms, this.exceptRooms, this.flags);
  29. }
  30. /**
  31. * Targets a room when emitting.
  32. *
  33. * @param room
  34. * @return a new BroadcastOperator instance
  35. * @public
  36. */
  37. in(room) {
  38. return this.to(room);
  39. }
  40. /**
  41. * Excludes a room when emitting.
  42. *
  43. * @param room
  44. * @return a new BroadcastOperator instance
  45. * @public
  46. */
  47. except(room) {
  48. const exceptRooms = new Set(this.exceptRooms);
  49. if (Array.isArray(room)) {
  50. room.forEach((r) => exceptRooms.add(r));
  51. }
  52. else {
  53. exceptRooms.add(room);
  54. }
  55. return new BroadcastOperator(this.adapter, this.rooms, exceptRooms, this.flags);
  56. }
  57. /**
  58. * Sets the compress flag.
  59. *
  60. * @param compress - if `true`, compresses the sending data
  61. * @return a new BroadcastOperator instance
  62. * @public
  63. */
  64. compress(compress) {
  65. const flags = Object.assign({}, this.flags, { compress });
  66. return new BroadcastOperator(this.adapter, this.rooms, this.exceptRooms, flags);
  67. }
  68. /**
  69. * Sets a modifier for a subsequent event emission that the event data may be lost if the client is not ready to
  70. * receive messages (because of network slowness or other issues, or because they’re connected through long polling
  71. * and is in the middle of a request-response cycle).
  72. *
  73. * @return a new BroadcastOperator instance
  74. * @public
  75. */
  76. get volatile() {
  77. const flags = Object.assign({}, this.flags, { volatile: true });
  78. return new BroadcastOperator(this.adapter, this.rooms, this.exceptRooms, flags);
  79. }
  80. /**
  81. * Sets a modifier for a subsequent event emission that the event data will only be broadcast to the current node.
  82. *
  83. * @return a new BroadcastOperator instance
  84. * @public
  85. */
  86. get local() {
  87. const flags = Object.assign({}, this.flags, { local: true });
  88. return new BroadcastOperator(this.adapter, this.rooms, this.exceptRooms, flags);
  89. }
  90. /**
  91. * Adds a timeout in milliseconds for the next operation
  92. *
  93. * <pre><code>
  94. *
  95. * io.timeout(1000).emit("some-event", (err, responses) => {
  96. * // ...
  97. * });
  98. *
  99. * </pre></code>
  100. *
  101. * @param timeout
  102. */
  103. timeout(timeout) {
  104. const flags = Object.assign({}, this.flags, { timeout });
  105. return new BroadcastOperator(this.adapter, this.rooms, this.exceptRooms, flags);
  106. }
  107. /**
  108. * Emits to all clients.
  109. *
  110. * @return Always true
  111. * @public
  112. */
  113. emit(ev, ...args) {
  114. if (socket_1.RESERVED_EVENTS.has(ev)) {
  115. throw new Error(`"${ev}" is a reserved event name`);
  116. }
  117. // set up packet object
  118. const data = [ev, ...args];
  119. const packet = {
  120. type: socket_io_parser_1.PacketType.EVENT,
  121. data: data,
  122. };
  123. const withAck = typeof data[data.length - 1] === "function";
  124. if (!withAck) {
  125. this.adapter.broadcast(packet, {
  126. rooms: this.rooms,
  127. except: this.exceptRooms,
  128. flags: this.flags,
  129. });
  130. return true;
  131. }
  132. const ack = data.pop();
  133. let timedOut = false;
  134. let responses = [];
  135. const timer = setTimeout(() => {
  136. timedOut = true;
  137. ack.apply(this, [new Error("operation has timed out"), responses]);
  138. }, this.flags.timeout);
  139. let expectedServerCount = -1;
  140. let actualServerCount = 0;
  141. let expectedClientCount = 0;
  142. const checkCompleteness = () => {
  143. if (!timedOut &&
  144. expectedServerCount === actualServerCount &&
  145. responses.length === expectedClientCount) {
  146. clearTimeout(timer);
  147. ack.apply(this, [null, responses]);
  148. }
  149. };
  150. this.adapter.broadcastWithAck(packet, {
  151. rooms: this.rooms,
  152. except: this.exceptRooms,
  153. flags: this.flags,
  154. }, (clientCount) => {
  155. // each Socket.IO server in the cluster sends the number of clients that were notified
  156. expectedClientCount += clientCount;
  157. actualServerCount++;
  158. checkCompleteness();
  159. }, (clientResponse) => {
  160. // each client sends an acknowledgement
  161. responses.push(clientResponse);
  162. checkCompleteness();
  163. });
  164. this.adapter.serverCount().then((serverCount) => {
  165. expectedServerCount = serverCount;
  166. checkCompleteness();
  167. });
  168. return true;
  169. }
  170. /**
  171. * Gets a list of clients.
  172. *
  173. * @public
  174. */
  175. allSockets() {
  176. if (!this.adapter) {
  177. throw new Error("No adapter for this namespace, are you trying to get the list of clients of a dynamic namespace?");
  178. }
  179. return this.adapter.sockets(this.rooms);
  180. }
  181. /**
  182. * Returns the matching socket instances
  183. *
  184. * @public
  185. */
  186. fetchSockets() {
  187. return this.adapter
  188. .fetchSockets({
  189. rooms: this.rooms,
  190. except: this.exceptRooms,
  191. flags: this.flags,
  192. })
  193. .then((sockets) => {
  194. return sockets.map((socket) => {
  195. if (socket instanceof socket_1.Socket) {
  196. // FIXME the TypeScript compiler complains about missing private properties
  197. return socket;
  198. }
  199. else {
  200. return new RemoteSocket(this.adapter, socket);
  201. }
  202. });
  203. });
  204. }
  205. /**
  206. * Makes the matching socket instances join the specified rooms
  207. *
  208. * @param room
  209. * @public
  210. */
  211. socketsJoin(room) {
  212. this.adapter.addSockets({
  213. rooms: this.rooms,
  214. except: this.exceptRooms,
  215. flags: this.flags,
  216. }, Array.isArray(room) ? room : [room]);
  217. }
  218. /**
  219. * Makes the matching socket instances leave the specified rooms
  220. *
  221. * @param room
  222. * @public
  223. */
  224. socketsLeave(room) {
  225. this.adapter.delSockets({
  226. rooms: this.rooms,
  227. except: this.exceptRooms,
  228. flags: this.flags,
  229. }, Array.isArray(room) ? room : [room]);
  230. }
  231. /**
  232. * Makes the matching socket instances disconnect
  233. *
  234. * @param close - whether to close the underlying connection
  235. * @public
  236. */
  237. disconnectSockets(close = false) {
  238. this.adapter.disconnectSockets({
  239. rooms: this.rooms,
  240. except: this.exceptRooms,
  241. flags: this.flags,
  242. }, close);
  243. }
  244. }
  245. exports.BroadcastOperator = BroadcastOperator;
  246. /**
  247. * Expose of subset of the attributes and methods of the Socket class
  248. */
  249. class RemoteSocket {
  250. constructor(adapter, details) {
  251. this.id = details.id;
  252. this.handshake = details.handshake;
  253. this.rooms = new Set(details.rooms);
  254. this.data = details.data;
  255. this.operator = new BroadcastOperator(adapter, new Set([this.id]));
  256. }
  257. emit(ev, ...args) {
  258. return this.operator.emit(ev, ...args);
  259. }
  260. /**
  261. * Joins a room.
  262. *
  263. * @param {String|Array} room - room or array of rooms
  264. * @public
  265. */
  266. join(room) {
  267. return this.operator.socketsJoin(room);
  268. }
  269. /**
  270. * Leaves a room.
  271. *
  272. * @param {String} room
  273. * @public
  274. */
  275. leave(room) {
  276. return this.operator.socketsLeave(room);
  277. }
  278. /**
  279. * Disconnects this client.
  280. *
  281. * @param {Boolean} close - if `true`, closes the underlying connection
  282. * @return {Socket} self
  283. *
  284. * @public
  285. */
  286. disconnect(close = false) {
  287. this.operator.disconnectSockets(close);
  288. return this;
  289. }
  290. }
  291. exports.RemoteSocket = RemoteSocket;