socket.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838
  1. import { PacketType } from "socket.io-parser";
  2. import { on } from "./on.js";
  3. import { Emitter, } from "@socket.io/component-emitter";
  4. /**
  5. * Internal events.
  6. * These events can't be emitted by the user.
  7. */
  8. const RESERVED_EVENTS = Object.freeze({
  9. connect: 1,
  10. connect_error: 1,
  11. disconnect: 1,
  12. disconnecting: 1,
  13. // EventEmitter reserved events: https://nodejs.org/api/events.html#events_event_newlistener
  14. newListener: 1,
  15. removeListener: 1,
  16. });
  17. /**
  18. * A Socket is the fundamental class for interacting with the server.
  19. *
  20. * A Socket belongs to a certain Namespace (by default /) and uses an underlying {@link Manager} to communicate.
  21. *
  22. * @example
  23. * const socket = io();
  24. *
  25. * socket.on("connect", () => {
  26. * console.log("connected");
  27. * });
  28. *
  29. * // send an event to the server
  30. * socket.emit("foo", "bar");
  31. *
  32. * socket.on("foobar", () => {
  33. * // an event was received from the server
  34. * });
  35. *
  36. * // upon disconnection
  37. * socket.on("disconnect", (reason) => {
  38. * console.log(`disconnected due to ${reason}`);
  39. * });
  40. */
  41. export class Socket extends Emitter {
  42. /**
  43. * `Socket` constructor.
  44. */
  45. constructor(io, nsp, opts) {
  46. super();
  47. /**
  48. * Whether the socket is currently connected to the server.
  49. *
  50. * @example
  51. * const socket = io();
  52. *
  53. * socket.on("connect", () => {
  54. * console.log(socket.connected); // true
  55. * });
  56. *
  57. * socket.on("disconnect", () => {
  58. * console.log(socket.connected); // false
  59. * });
  60. */
  61. this.connected = false;
  62. /**
  63. * Whether the connection state was recovered after a temporary disconnection. In that case, any missed packets will
  64. * be transmitted by the server.
  65. */
  66. this.recovered = false;
  67. /**
  68. * Buffer for packets received before the CONNECT packet
  69. */
  70. this.receiveBuffer = [];
  71. /**
  72. * Buffer for packets that will be sent once the socket is connected
  73. */
  74. this.sendBuffer = [];
  75. /**
  76. * The queue of packets to be sent with retry in case of failure.
  77. *
  78. * Packets are sent one by one, each waiting for the server acknowledgement, in order to guarantee the delivery order.
  79. * @private
  80. */
  81. this._queue = [];
  82. /**
  83. * A sequence to generate the ID of the {@link QueuedPacket}.
  84. * @private
  85. */
  86. this._queueSeq = 0;
  87. this.ids = 0;
  88. this.acks = {};
  89. this.flags = {};
  90. this.io = io;
  91. this.nsp = nsp;
  92. if (opts && opts.auth) {
  93. this.auth = opts.auth;
  94. }
  95. this._opts = Object.assign({}, opts);
  96. if (this.io._autoConnect)
  97. this.open();
  98. }
  99. /**
  100. * Whether the socket is currently disconnected
  101. *
  102. * @example
  103. * const socket = io();
  104. *
  105. * socket.on("connect", () => {
  106. * console.log(socket.disconnected); // false
  107. * });
  108. *
  109. * socket.on("disconnect", () => {
  110. * console.log(socket.disconnected); // true
  111. * });
  112. */
  113. get disconnected() {
  114. return !this.connected;
  115. }
  116. /**
  117. * Subscribe to open, close and packet events
  118. *
  119. * @private
  120. */
  121. subEvents() {
  122. if (this.subs)
  123. return;
  124. const io = this.io;
  125. this.subs = [
  126. on(io, "open", this.onopen.bind(this)),
  127. on(io, "packet", this.onpacket.bind(this)),
  128. on(io, "error", this.onerror.bind(this)),
  129. on(io, "close", this.onclose.bind(this)),
  130. ];
  131. }
  132. /**
  133. * Whether the Socket will try to reconnect when its Manager connects or reconnects.
  134. *
  135. * @example
  136. * const socket = io();
  137. *
  138. * console.log(socket.active); // true
  139. *
  140. * socket.on("disconnect", (reason) => {
  141. * if (reason === "io server disconnect") {
  142. * // the disconnection was initiated by the server, you need to manually reconnect
  143. * console.log(socket.active); // false
  144. * }
  145. * // else the socket will automatically try to reconnect
  146. * console.log(socket.active); // true
  147. * });
  148. */
  149. get active() {
  150. return !!this.subs;
  151. }
  152. /**
  153. * "Opens" the socket.
  154. *
  155. * @example
  156. * const socket = io({
  157. * autoConnect: false
  158. * });
  159. *
  160. * socket.connect();
  161. */
  162. connect() {
  163. if (this.connected)
  164. return this;
  165. this.subEvents();
  166. if (!this.io["_reconnecting"])
  167. this.io.open(); // ensure open
  168. if ("open" === this.io._readyState)
  169. this.onopen();
  170. return this;
  171. }
  172. /**
  173. * Alias for {@link connect()}.
  174. */
  175. open() {
  176. return this.connect();
  177. }
  178. /**
  179. * Sends a `message` event.
  180. *
  181. * This method mimics the WebSocket.send() method.
  182. *
  183. * @see https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send
  184. *
  185. * @example
  186. * socket.send("hello");
  187. *
  188. * // this is equivalent to
  189. * socket.emit("message", "hello");
  190. *
  191. * @return self
  192. */
  193. send(...args) {
  194. args.unshift("message");
  195. this.emit.apply(this, args);
  196. return this;
  197. }
  198. /**
  199. * Override `emit`.
  200. * If the event is in `events`, it's emitted normally.
  201. *
  202. * @example
  203. * socket.emit("hello", "world");
  204. *
  205. * // all serializable datastructures are supported (no need to call JSON.stringify)
  206. * socket.emit("hello", 1, "2", { 3: ["4"], 5: Uint8Array.from([6]) });
  207. *
  208. * // with an acknowledgement from the server
  209. * socket.emit("hello", "world", (val) => {
  210. * // ...
  211. * });
  212. *
  213. * @return self
  214. */
  215. emit(ev, ...args) {
  216. if (RESERVED_EVENTS.hasOwnProperty(ev)) {
  217. throw new Error('"' + ev.toString() + '" is a reserved event name');
  218. }
  219. args.unshift(ev);
  220. if (this._opts.retries && !this.flags.fromQueue && !this.flags.volatile) {
  221. this._addToQueue(args);
  222. return this;
  223. }
  224. const packet = {
  225. type: PacketType.EVENT,
  226. data: args,
  227. };
  228. packet.options = {};
  229. packet.options.compress = this.flags.compress !== false;
  230. // event ack callback
  231. if ("function" === typeof args[args.length - 1]) {
  232. const id = this.ids++;
  233. const ack = args.pop();
  234. this._registerAckCallback(id, ack);
  235. packet.id = id;
  236. }
  237. const isTransportWritable = this.io.engine &&
  238. this.io.engine.transport &&
  239. this.io.engine.transport.writable;
  240. const discardPacket = this.flags.volatile && (!isTransportWritable || !this.connected);
  241. if (discardPacket) {
  242. }
  243. else if (this.connected) {
  244. this.notifyOutgoingListeners(packet);
  245. this.packet(packet);
  246. }
  247. else {
  248. this.sendBuffer.push(packet);
  249. }
  250. this.flags = {};
  251. return this;
  252. }
  253. /**
  254. * @private
  255. */
  256. _registerAckCallback(id, ack) {
  257. var _a;
  258. const timeout = (_a = this.flags.timeout) !== null && _a !== void 0 ? _a : this._opts.ackTimeout;
  259. if (timeout === undefined) {
  260. this.acks[id] = ack;
  261. return;
  262. }
  263. // @ts-ignore
  264. const timer = this.io.setTimeoutFn(() => {
  265. delete this.acks[id];
  266. for (let i = 0; i < this.sendBuffer.length; i++) {
  267. if (this.sendBuffer[i].id === id) {
  268. this.sendBuffer.splice(i, 1);
  269. }
  270. }
  271. ack.call(this, new Error("operation has timed out"));
  272. }, timeout);
  273. this.acks[id] = (...args) => {
  274. // @ts-ignore
  275. this.io.clearTimeoutFn(timer);
  276. ack.apply(this, [null, ...args]);
  277. };
  278. }
  279. /**
  280. * Emits an event and waits for an acknowledgement
  281. *
  282. * @example
  283. * // without timeout
  284. * const response = await socket.emitWithAck("hello", "world");
  285. *
  286. * // with a specific timeout
  287. * try {
  288. * const response = await socket.timeout(1000).emitWithAck("hello", "world");
  289. * } catch (err) {
  290. * // the server did not acknowledge the event in the given delay
  291. * }
  292. *
  293. * @return a Promise that will be fulfilled when the server acknowledges the event
  294. */
  295. emitWithAck(ev, ...args) {
  296. // the timeout flag is optional
  297. const withErr = this.flags.timeout !== undefined || this._opts.ackTimeout !== undefined;
  298. return new Promise((resolve, reject) => {
  299. args.push((arg1, arg2) => {
  300. if (withErr) {
  301. return arg1 ? reject(arg1) : resolve(arg2);
  302. }
  303. else {
  304. return resolve(arg1);
  305. }
  306. });
  307. this.emit(ev, ...args);
  308. });
  309. }
  310. /**
  311. * Add the packet to the queue.
  312. * @param args
  313. * @private
  314. */
  315. _addToQueue(args) {
  316. let ack;
  317. if (typeof args[args.length - 1] === "function") {
  318. ack = args.pop();
  319. }
  320. const packet = {
  321. id: this._queueSeq++,
  322. tryCount: 0,
  323. pending: false,
  324. args,
  325. flags: Object.assign({ fromQueue: true }, this.flags),
  326. };
  327. args.push((err, ...responseArgs) => {
  328. if (packet !== this._queue[0]) {
  329. // the packet has already been acknowledged
  330. return;
  331. }
  332. const hasError = err !== null;
  333. if (hasError) {
  334. if (packet.tryCount > this._opts.retries) {
  335. this._queue.shift();
  336. if (ack) {
  337. ack(err);
  338. }
  339. }
  340. }
  341. else {
  342. this._queue.shift();
  343. if (ack) {
  344. ack(null, ...responseArgs);
  345. }
  346. }
  347. packet.pending = false;
  348. return this._drainQueue();
  349. });
  350. this._queue.push(packet);
  351. this._drainQueue();
  352. }
  353. /**
  354. * Send the first packet of the queue, and wait for an acknowledgement from the server.
  355. * @param force - whether to resend a packet that has not been acknowledged yet
  356. *
  357. * @private
  358. */
  359. _drainQueue(force = false) {
  360. if (!this.connected || this._queue.length === 0) {
  361. return;
  362. }
  363. const packet = this._queue[0];
  364. if (packet.pending && !force) {
  365. return;
  366. }
  367. packet.pending = true;
  368. packet.tryCount++;
  369. this.flags = packet.flags;
  370. this.emit.apply(this, packet.args);
  371. }
  372. /**
  373. * Sends a packet.
  374. *
  375. * @param packet
  376. * @private
  377. */
  378. packet(packet) {
  379. packet.nsp = this.nsp;
  380. this.io._packet(packet);
  381. }
  382. /**
  383. * Called upon engine `open`.
  384. *
  385. * @private
  386. */
  387. onopen() {
  388. if (typeof this.auth == "function") {
  389. this.auth((data) => {
  390. this._sendConnectPacket(data);
  391. });
  392. }
  393. else {
  394. this._sendConnectPacket(this.auth);
  395. }
  396. }
  397. /**
  398. * Sends a CONNECT packet to initiate the Socket.IO session.
  399. *
  400. * @param data
  401. * @private
  402. */
  403. _sendConnectPacket(data) {
  404. this.packet({
  405. type: PacketType.CONNECT,
  406. data: this._pid
  407. ? Object.assign({ pid: this._pid, offset: this._lastOffset }, data)
  408. : data,
  409. });
  410. }
  411. /**
  412. * Called upon engine or manager `error`.
  413. *
  414. * @param err
  415. * @private
  416. */
  417. onerror(err) {
  418. if (!this.connected) {
  419. this.emitReserved("connect_error", err);
  420. }
  421. }
  422. /**
  423. * Called upon engine `close`.
  424. *
  425. * @param reason
  426. * @param description
  427. * @private
  428. */
  429. onclose(reason, description) {
  430. this.connected = false;
  431. delete this.id;
  432. this.emitReserved("disconnect", reason, description);
  433. }
  434. /**
  435. * Called with socket packet.
  436. *
  437. * @param packet
  438. * @private
  439. */
  440. onpacket(packet) {
  441. const sameNamespace = packet.nsp === this.nsp;
  442. if (!sameNamespace)
  443. return;
  444. switch (packet.type) {
  445. case PacketType.CONNECT:
  446. if (packet.data && packet.data.sid) {
  447. this.onconnect(packet.data.sid, packet.data.pid);
  448. }
  449. else {
  450. this.emitReserved("connect_error", new Error("It seems you are trying to reach a Socket.IO server in v2.x with a v3.x client, but they are not compatible (more information here: https://socket.io/docs/v3/migrating-from-2-x-to-3-0/)"));
  451. }
  452. break;
  453. case PacketType.EVENT:
  454. case PacketType.BINARY_EVENT:
  455. this.onevent(packet);
  456. break;
  457. case PacketType.ACK:
  458. case PacketType.BINARY_ACK:
  459. this.onack(packet);
  460. break;
  461. case PacketType.DISCONNECT:
  462. this.ondisconnect();
  463. break;
  464. case PacketType.CONNECT_ERROR:
  465. this.destroy();
  466. const err = new Error(packet.data.message);
  467. // @ts-ignore
  468. err.data = packet.data.data;
  469. this.emitReserved("connect_error", err);
  470. break;
  471. }
  472. }
  473. /**
  474. * Called upon a server event.
  475. *
  476. * @param packet
  477. * @private
  478. */
  479. onevent(packet) {
  480. const args = packet.data || [];
  481. if (null != packet.id) {
  482. args.push(this.ack(packet.id));
  483. }
  484. if (this.connected) {
  485. this.emitEvent(args);
  486. }
  487. else {
  488. this.receiveBuffer.push(Object.freeze(args));
  489. }
  490. }
  491. emitEvent(args) {
  492. if (this._anyListeners && this._anyListeners.length) {
  493. const listeners = this._anyListeners.slice();
  494. for (const listener of listeners) {
  495. listener.apply(this, args);
  496. }
  497. }
  498. super.emit.apply(this, args);
  499. if (this._pid && args.length && typeof args[args.length - 1] === "string") {
  500. this._lastOffset = args[args.length - 1];
  501. }
  502. }
  503. /**
  504. * Produces an ack callback to emit with an event.
  505. *
  506. * @private
  507. */
  508. ack(id) {
  509. const self = this;
  510. let sent = false;
  511. return function (...args) {
  512. // prevent double callbacks
  513. if (sent)
  514. return;
  515. sent = true;
  516. self.packet({
  517. type: PacketType.ACK,
  518. id: id,
  519. data: args,
  520. });
  521. };
  522. }
  523. /**
  524. * Called upon a server acknowlegement.
  525. *
  526. * @param packet
  527. * @private
  528. */
  529. onack(packet) {
  530. const ack = this.acks[packet.id];
  531. if ("function" === typeof ack) {
  532. ack.apply(this, packet.data);
  533. delete this.acks[packet.id];
  534. }
  535. else {
  536. }
  537. }
  538. /**
  539. * Called upon server connect.
  540. *
  541. * @private
  542. */
  543. onconnect(id, pid) {
  544. this.id = id;
  545. this.recovered = pid && this._pid === pid;
  546. this._pid = pid; // defined only if connection state recovery is enabled
  547. this.connected = true;
  548. this.emitBuffered();
  549. this.emitReserved("connect");
  550. this._drainQueue(true);
  551. }
  552. /**
  553. * Emit buffered events (received and emitted).
  554. *
  555. * @private
  556. */
  557. emitBuffered() {
  558. this.receiveBuffer.forEach((args) => this.emitEvent(args));
  559. this.receiveBuffer = [];
  560. this.sendBuffer.forEach((packet) => {
  561. this.notifyOutgoingListeners(packet);
  562. this.packet(packet);
  563. });
  564. this.sendBuffer = [];
  565. }
  566. /**
  567. * Called upon server disconnect.
  568. *
  569. * @private
  570. */
  571. ondisconnect() {
  572. this.destroy();
  573. this.onclose("io server disconnect");
  574. }
  575. /**
  576. * Called upon forced client/server side disconnections,
  577. * this method ensures the manager stops tracking us and
  578. * that reconnections don't get triggered for this.
  579. *
  580. * @private
  581. */
  582. destroy() {
  583. if (this.subs) {
  584. // clean subscriptions to avoid reconnections
  585. this.subs.forEach((subDestroy) => subDestroy());
  586. this.subs = undefined;
  587. }
  588. this.io["_destroy"](this);
  589. }
  590. /**
  591. * Disconnects the socket manually. In that case, the socket will not try to reconnect.
  592. *
  593. * If this is the last active Socket instance of the {@link Manager}, the low-level connection will be closed.
  594. *
  595. * @example
  596. * const socket = io();
  597. *
  598. * socket.on("disconnect", (reason) => {
  599. * // console.log(reason); prints "io client disconnect"
  600. * });
  601. *
  602. * socket.disconnect();
  603. *
  604. * @return self
  605. */
  606. disconnect() {
  607. if (this.connected) {
  608. this.packet({ type: PacketType.DISCONNECT });
  609. }
  610. // remove socket from pool
  611. this.destroy();
  612. if (this.connected) {
  613. // fire events
  614. this.onclose("io client disconnect");
  615. }
  616. return this;
  617. }
  618. /**
  619. * Alias for {@link disconnect()}.
  620. *
  621. * @return self
  622. */
  623. close() {
  624. return this.disconnect();
  625. }
  626. /**
  627. * Sets the compress flag.
  628. *
  629. * @example
  630. * socket.compress(false).emit("hello");
  631. *
  632. * @param compress - if `true`, compresses the sending data
  633. * @return self
  634. */
  635. compress(compress) {
  636. this.flags.compress = compress;
  637. return this;
  638. }
  639. /**
  640. * Sets a modifier for a subsequent event emission that the event message will be dropped when this socket is not
  641. * ready to send messages.
  642. *
  643. * @example
  644. * socket.volatile.emit("hello"); // the server may or may not receive it
  645. *
  646. * @returns self
  647. */
  648. get volatile() {
  649. this.flags.volatile = true;
  650. return this;
  651. }
  652. /**
  653. * Sets a modifier for a subsequent event emission that the callback will be called with an error when the
  654. * given number of milliseconds have elapsed without an acknowledgement from the server:
  655. *
  656. * @example
  657. * socket.timeout(5000).emit("my-event", (err) => {
  658. * if (err) {
  659. * // the server did not acknowledge the event in the given delay
  660. * }
  661. * });
  662. *
  663. * @returns self
  664. */
  665. timeout(timeout) {
  666. this.flags.timeout = timeout;
  667. return this;
  668. }
  669. /**
  670. * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the
  671. * callback.
  672. *
  673. * @example
  674. * socket.onAny((event, ...args) => {
  675. * console.log(`got ${event}`);
  676. * });
  677. *
  678. * @param listener
  679. */
  680. onAny(listener) {
  681. this._anyListeners = this._anyListeners || [];
  682. this._anyListeners.push(listener);
  683. return this;
  684. }
  685. /**
  686. * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the
  687. * callback. The listener is added to the beginning of the listeners array.
  688. *
  689. * @example
  690. * socket.prependAny((event, ...args) => {
  691. * console.log(`got event ${event}`);
  692. * });
  693. *
  694. * @param listener
  695. */
  696. prependAny(listener) {
  697. this._anyListeners = this._anyListeners || [];
  698. this._anyListeners.unshift(listener);
  699. return this;
  700. }
  701. /**
  702. * Removes the listener that will be fired when any event is emitted.
  703. *
  704. * @example
  705. * const catchAllListener = (event, ...args) => {
  706. * console.log(`got event ${event}`);
  707. * }
  708. *
  709. * socket.onAny(catchAllListener);
  710. *
  711. * // remove a specific listener
  712. * socket.offAny(catchAllListener);
  713. *
  714. * // or remove all listeners
  715. * socket.offAny();
  716. *
  717. * @param listener
  718. */
  719. offAny(listener) {
  720. if (!this._anyListeners) {
  721. return this;
  722. }
  723. if (listener) {
  724. const listeners = this._anyListeners;
  725. for (let i = 0; i < listeners.length; i++) {
  726. if (listener === listeners[i]) {
  727. listeners.splice(i, 1);
  728. return this;
  729. }
  730. }
  731. }
  732. else {
  733. this._anyListeners = [];
  734. }
  735. return this;
  736. }
  737. /**
  738. * Returns an array of listeners that are listening for any event that is specified. This array can be manipulated,
  739. * e.g. to remove listeners.
  740. */
  741. listenersAny() {
  742. return this._anyListeners || [];
  743. }
  744. /**
  745. * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the
  746. * callback.
  747. *
  748. * Note: acknowledgements sent to the server are not included.
  749. *
  750. * @example
  751. * socket.onAnyOutgoing((event, ...args) => {
  752. * console.log(`sent event ${event}`);
  753. * });
  754. *
  755. * @param listener
  756. */
  757. onAnyOutgoing(listener) {
  758. this._anyOutgoingListeners = this._anyOutgoingListeners || [];
  759. this._anyOutgoingListeners.push(listener);
  760. return this;
  761. }
  762. /**
  763. * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the
  764. * callback. The listener is added to the beginning of the listeners array.
  765. *
  766. * Note: acknowledgements sent to the server are not included.
  767. *
  768. * @example
  769. * socket.prependAnyOutgoing((event, ...args) => {
  770. * console.log(`sent event ${event}`);
  771. * });
  772. *
  773. * @param listener
  774. */
  775. prependAnyOutgoing(listener) {
  776. this._anyOutgoingListeners = this._anyOutgoingListeners || [];
  777. this._anyOutgoingListeners.unshift(listener);
  778. return this;
  779. }
  780. /**
  781. * Removes the listener that will be fired when any event is emitted.
  782. *
  783. * @example
  784. * const catchAllListener = (event, ...args) => {
  785. * console.log(`sent event ${event}`);
  786. * }
  787. *
  788. * socket.onAnyOutgoing(catchAllListener);
  789. *
  790. * // remove a specific listener
  791. * socket.offAnyOutgoing(catchAllListener);
  792. *
  793. * // or remove all listeners
  794. * socket.offAnyOutgoing();
  795. *
  796. * @param [listener] - the catch-all listener (optional)
  797. */
  798. offAnyOutgoing(listener) {
  799. if (!this._anyOutgoingListeners) {
  800. return this;
  801. }
  802. if (listener) {
  803. const listeners = this._anyOutgoingListeners;
  804. for (let i = 0; i < listeners.length; i++) {
  805. if (listener === listeners[i]) {
  806. listeners.splice(i, 1);
  807. return this;
  808. }
  809. }
  810. }
  811. else {
  812. this._anyOutgoingListeners = [];
  813. }
  814. return this;
  815. }
  816. /**
  817. * Returns an array of listeners that are listening for any event that is specified. This array can be manipulated,
  818. * e.g. to remove listeners.
  819. */
  820. listenersAnyOutgoing() {
  821. return this._anyOutgoingListeners || [];
  822. }
  823. /**
  824. * Notify the listeners for each packet sent
  825. *
  826. * @param packet
  827. *
  828. * @private
  829. */
  830. notifyOutgoingListeners(packet) {
  831. if (this._anyOutgoingListeners && this._anyOutgoingListeners.length) {
  832. const listeners = this._anyOutgoingListeners.slice();
  833. for (const listener of listeners) {
  834. listener.apply(this, packet.data);
  835. }
  836. }
  837. }
  838. }