socket.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977
  1. "use strict";
  2. var __importDefault = (this && this.__importDefault) || function (mod) {
  3. return (mod && mod.__esModule) ? mod : { "default": mod };
  4. };
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. exports.Socket = void 0;
  7. const socket_io_parser_1 = require("socket.io-parser");
  8. const debug_1 = __importDefault(require("debug"));
  9. const typed_events_1 = require("./typed-events");
  10. const base64id_1 = __importDefault(require("base64id"));
  11. const broadcast_operator_1 = require("./broadcast-operator");
  12. const socket_types_1 = require("./socket-types");
  13. const debug = (0, debug_1.default)("socket.io:socket");
  14. const RECOVERABLE_DISCONNECT_REASONS = new Set([
  15. "transport error",
  16. "transport close",
  17. "forced close",
  18. "ping timeout",
  19. "server shutting down",
  20. "forced server close",
  21. ]);
  22. function noop() { }
  23. /**
  24. * This is the main object for interacting with a client.
  25. *
  26. * A Socket belongs to a given {@link Namespace} and uses an underlying {@link Client} to communicate.
  27. *
  28. * Within each {@link Namespace}, you can also define arbitrary channels (called "rooms") that the {@link Socket} can
  29. * join and leave. That provides a convenient way to broadcast to a group of socket instances.
  30. *
  31. * @example
  32. * io.on("connection", (socket) => {
  33. * console.log(`socket ${socket.id} connected`);
  34. *
  35. * // send an event to the client
  36. * socket.emit("foo", "bar");
  37. *
  38. * socket.on("foobar", () => {
  39. * // an event was received from the client
  40. * });
  41. *
  42. * // join the room named "room1"
  43. * socket.join("room1");
  44. *
  45. * // broadcast to everyone in the room named "room1"
  46. * io.to("room1").emit("hello");
  47. *
  48. * // upon disconnection
  49. * socket.on("disconnect", (reason) => {
  50. * console.log(`socket ${socket.id} disconnected due to ${reason}`);
  51. * });
  52. * });
  53. */
  54. class Socket extends typed_events_1.StrictEventEmitter {
  55. /**
  56. * Interface to a `Client` for a given `Namespace`.
  57. *
  58. * @param {Namespace} nsp
  59. * @param {Client} client
  60. * @param {Object} auth
  61. * @package
  62. */
  63. constructor(nsp, client, auth, previousSession) {
  64. super();
  65. this.nsp = nsp;
  66. this.client = client;
  67. /**
  68. * Whether the connection state was recovered after a temporary disconnection. In that case, any missed packets will
  69. * be transmitted to the client, the data attribute and the rooms will be restored.
  70. */
  71. this.recovered = false;
  72. /**
  73. * Additional information that can be attached to the Socket instance and which will be used in the
  74. * {@link Server.fetchSockets()} method.
  75. */
  76. this.data = {};
  77. /**
  78. * Whether the socket is currently connected or not.
  79. *
  80. * @example
  81. * io.use((socket, next) => {
  82. * console.log(socket.connected); // false
  83. * next();
  84. * });
  85. *
  86. * io.on("connection", (socket) => {
  87. * console.log(socket.connected); // true
  88. * });
  89. */
  90. this.connected = false;
  91. this.acks = new Map();
  92. this.fns = [];
  93. this.flags = {};
  94. this.server = nsp.server;
  95. this.adapter = this.nsp.adapter;
  96. if (previousSession) {
  97. this.id = previousSession.sid;
  98. this.pid = previousSession.pid;
  99. previousSession.rooms.forEach((room) => this.join(room));
  100. this.data = previousSession.data;
  101. previousSession.missedPackets.forEach((packet) => {
  102. this.packet({
  103. type: socket_io_parser_1.PacketType.EVENT,
  104. data: packet,
  105. });
  106. });
  107. this.recovered = true;
  108. }
  109. else {
  110. if (client.conn.protocol === 3) {
  111. // @ts-ignore
  112. this.id = nsp.name !== "/" ? nsp.name + "#" + client.id : client.id;
  113. }
  114. else {
  115. this.id = base64id_1.default.generateId(); // don't reuse the Engine.IO id because it's sensitive information
  116. }
  117. if (this.server._opts.connectionStateRecovery) {
  118. this.pid = base64id_1.default.generateId();
  119. }
  120. }
  121. this.handshake = this.buildHandshake(auth);
  122. // prevents crash when the socket receives an "error" event without listener
  123. this.on("error", noop);
  124. }
  125. /**
  126. * Builds the `handshake` BC object
  127. *
  128. * @private
  129. */
  130. buildHandshake(auth) {
  131. var _a, _b, _c, _d;
  132. return {
  133. headers: ((_a = this.request) === null || _a === void 0 ? void 0 : _a.headers) || {},
  134. time: new Date() + "",
  135. address: this.conn.remoteAddress,
  136. xdomain: !!((_b = this.request) === null || _b === void 0 ? void 0 : _b.headers.origin),
  137. // @ts-ignore
  138. secure: !this.request || !!this.request.connection.encrypted,
  139. issued: +new Date(),
  140. url: (_c = this.request) === null || _c === void 0 ? void 0 : _c.url,
  141. // @ts-ignore
  142. query: ((_d = this.request) === null || _d === void 0 ? void 0 : _d._query) || {},
  143. auth,
  144. };
  145. }
  146. /**
  147. * Emits to this client.
  148. *
  149. * @example
  150. * io.on("connection", (socket) => {
  151. * socket.emit("hello", "world");
  152. *
  153. * // all serializable datastructures are supported (no need to call JSON.stringify)
  154. * socket.emit("hello", 1, "2", { 3: ["4"], 5: Buffer.from([6]) });
  155. *
  156. * // with an acknowledgement from the client
  157. * socket.emit("hello", "world", (val) => {
  158. * // ...
  159. * });
  160. * });
  161. *
  162. * @return Always returns `true`.
  163. */
  164. emit(ev, ...args) {
  165. if (socket_types_1.RESERVED_EVENTS.has(ev)) {
  166. throw new Error(`"${String(ev)}" is a reserved event name`);
  167. }
  168. const data = [ev, ...args];
  169. const packet = {
  170. type: socket_io_parser_1.PacketType.EVENT,
  171. data: data,
  172. };
  173. // access last argument to see if it's an ACK callback
  174. if (typeof data[data.length - 1] === "function") {
  175. const id = this.nsp._ids++;
  176. debug("emitting packet with ack id %d", id);
  177. this.registerAckCallback(id, data.pop());
  178. packet.id = id;
  179. }
  180. const flags = Object.assign({}, this.flags);
  181. this.flags = {};
  182. // @ts-ignore
  183. if (this.nsp.server.opts.connectionStateRecovery) {
  184. // this ensures the packet is stored and can be transmitted upon reconnection
  185. this.adapter.broadcast(packet, {
  186. rooms: new Set([this.id]),
  187. except: new Set(),
  188. flags,
  189. });
  190. }
  191. else {
  192. this.notifyOutgoingListeners(packet);
  193. this.packet(packet, flags);
  194. }
  195. return true;
  196. }
  197. /**
  198. * Emits an event and waits for an acknowledgement
  199. *
  200. * @example
  201. * io.on("connection", async (socket) => {
  202. * // without timeout
  203. * const response = await socket.emitWithAck("hello", "world");
  204. *
  205. * // with a specific timeout
  206. * try {
  207. * const response = await socket.timeout(1000).emitWithAck("hello", "world");
  208. * } catch (err) {
  209. * // the client did not acknowledge the event in the given delay
  210. * }
  211. * });
  212. *
  213. * @return a Promise that will be fulfilled when the client acknowledges the event
  214. */
  215. emitWithAck(ev, ...args) {
  216. // the timeout flag is optional
  217. const withErr = this.flags.timeout !== undefined;
  218. return new Promise((resolve, reject) => {
  219. args.push((arg1, arg2) => {
  220. if (withErr) {
  221. return arg1 ? reject(arg1) : resolve(arg2);
  222. }
  223. else {
  224. return resolve(arg1);
  225. }
  226. });
  227. this.emit(ev, ...args);
  228. });
  229. }
  230. /**
  231. * @private
  232. */
  233. registerAckCallback(id, ack) {
  234. const timeout = this.flags.timeout;
  235. if (timeout === undefined) {
  236. this.acks.set(id, ack);
  237. return;
  238. }
  239. const timer = setTimeout(() => {
  240. debug("event with ack id %d has timed out after %d ms", id, timeout);
  241. this.acks.delete(id);
  242. ack.call(this, new Error("operation has timed out"));
  243. }, timeout);
  244. this.acks.set(id, (...args) => {
  245. clearTimeout(timer);
  246. ack.apply(this, [null, ...args]);
  247. });
  248. }
  249. /**
  250. * Targets a room when broadcasting.
  251. *
  252. * @example
  253. * io.on("connection", (socket) => {
  254. * // the “foo” event will be broadcast to all connected clients in the “room-101” room, except this socket
  255. * socket.to("room-101").emit("foo", "bar");
  256. *
  257. * // the code above is equivalent to:
  258. * io.to("room-101").except(socket.id).emit("foo", "bar");
  259. *
  260. * // with an array of rooms (a client will be notified at most once)
  261. * socket.to(["room-101", "room-102"]).emit("foo", "bar");
  262. *
  263. * // with multiple chained calls
  264. * socket.to("room-101").to("room-102").emit("foo", "bar");
  265. * });
  266. *
  267. * @param room - a room, or an array of rooms
  268. * @return a new {@link BroadcastOperator} instance for chaining
  269. */
  270. to(room) {
  271. return this.newBroadcastOperator().to(room);
  272. }
  273. /**
  274. * Targets a room when broadcasting. Similar to `to()`, but might feel clearer in some cases:
  275. *
  276. * @example
  277. * io.on("connection", (socket) => {
  278. * // disconnect all clients in the "room-101" room, except this socket
  279. * socket.in("room-101").disconnectSockets();
  280. * });
  281. *
  282. * @param room - a room, or an array of rooms
  283. * @return a new {@link BroadcastOperator} instance for chaining
  284. */
  285. in(room) {
  286. return this.newBroadcastOperator().in(room);
  287. }
  288. /**
  289. * Excludes a room when broadcasting.
  290. *
  291. * @example
  292. * io.on("connection", (socket) => {
  293. * // the "foo" event will be broadcast to all connected clients, except the ones that are in the "room-101" room
  294. * // and this socket
  295. * socket.except("room-101").emit("foo", "bar");
  296. *
  297. * // with an array of rooms
  298. * socket.except(["room-101", "room-102"]).emit("foo", "bar");
  299. *
  300. * // with multiple chained calls
  301. * socket.except("room-101").except("room-102").emit("foo", "bar");
  302. * });
  303. *
  304. * @param room - a room, or an array of rooms
  305. * @return a new {@link BroadcastOperator} instance for chaining
  306. */
  307. except(room) {
  308. return this.newBroadcastOperator().except(room);
  309. }
  310. /**
  311. * Sends a `message` event.
  312. *
  313. * This method mimics the WebSocket.send() method.
  314. *
  315. * @see https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send
  316. *
  317. * @example
  318. * io.on("connection", (socket) => {
  319. * socket.send("hello");
  320. *
  321. * // this is equivalent to
  322. * socket.emit("message", "hello");
  323. * });
  324. *
  325. * @return self
  326. */
  327. send(...args) {
  328. this.emit("message", ...args);
  329. return this;
  330. }
  331. /**
  332. * Sends a `message` event. Alias of {@link send}.
  333. *
  334. * @return self
  335. */
  336. write(...args) {
  337. this.emit("message", ...args);
  338. return this;
  339. }
  340. /**
  341. * Writes a packet.
  342. *
  343. * @param {Object} packet - packet object
  344. * @param {Object} opts - options
  345. * @private
  346. */
  347. packet(packet, opts = {}) {
  348. packet.nsp = this.nsp.name;
  349. opts.compress = false !== opts.compress;
  350. this.client._packet(packet, opts);
  351. }
  352. /**
  353. * Joins a room.
  354. *
  355. * @example
  356. * io.on("connection", (socket) => {
  357. * // join a single room
  358. * socket.join("room1");
  359. *
  360. * // join multiple rooms
  361. * socket.join(["room1", "room2"]);
  362. * });
  363. *
  364. * @param {String|Array} rooms - room or array of rooms
  365. * @return a Promise or nothing, depending on the adapter
  366. */
  367. join(rooms) {
  368. debug("join room %s", rooms);
  369. return this.adapter.addAll(this.id, new Set(Array.isArray(rooms) ? rooms : [rooms]));
  370. }
  371. /**
  372. * Leaves a room.
  373. *
  374. * @example
  375. * io.on("connection", (socket) => {
  376. * // leave a single room
  377. * socket.leave("room1");
  378. *
  379. * // leave multiple rooms
  380. * socket.leave("room1").leave("room2");
  381. * });
  382. *
  383. * @param {String} room
  384. * @return a Promise or nothing, depending on the adapter
  385. */
  386. leave(room) {
  387. debug("leave room %s", room);
  388. return this.adapter.del(this.id, room);
  389. }
  390. /**
  391. * Leave all rooms.
  392. *
  393. * @private
  394. */
  395. leaveAll() {
  396. this.adapter.delAll(this.id);
  397. }
  398. /**
  399. * Called by `Namespace` upon successful
  400. * middleware execution (ie: authorization).
  401. * Socket is added to namespace array before
  402. * call to join, so adapters can access it.
  403. *
  404. * @private
  405. */
  406. _onconnect() {
  407. debug("socket connected - writing packet");
  408. this.connected = true;
  409. this.join(this.id);
  410. if (this.conn.protocol === 3) {
  411. this.packet({ type: socket_io_parser_1.PacketType.CONNECT });
  412. }
  413. else {
  414. this.packet({
  415. type: socket_io_parser_1.PacketType.CONNECT,
  416. data: { sid: this.id, pid: this.pid },
  417. });
  418. }
  419. }
  420. /**
  421. * Called with each packet. Called by `Client`.
  422. *
  423. * @param {Object} packet
  424. * @private
  425. */
  426. _onpacket(packet) {
  427. debug("got packet %j", packet);
  428. switch (packet.type) {
  429. case socket_io_parser_1.PacketType.EVENT:
  430. this.onevent(packet);
  431. break;
  432. case socket_io_parser_1.PacketType.BINARY_EVENT:
  433. this.onevent(packet);
  434. break;
  435. case socket_io_parser_1.PacketType.ACK:
  436. this.onack(packet);
  437. break;
  438. case socket_io_parser_1.PacketType.BINARY_ACK:
  439. this.onack(packet);
  440. break;
  441. case socket_io_parser_1.PacketType.DISCONNECT:
  442. this.ondisconnect();
  443. break;
  444. }
  445. }
  446. /**
  447. * Called upon event packet.
  448. *
  449. * @param {Packet} packet - packet object
  450. * @private
  451. */
  452. onevent(packet) {
  453. const args = packet.data || [];
  454. debug("emitting event %j", args);
  455. if (null != packet.id) {
  456. debug("attaching ack callback to event");
  457. args.push(this.ack(packet.id));
  458. }
  459. if (this._anyListeners && this._anyListeners.length) {
  460. const listeners = this._anyListeners.slice();
  461. for (const listener of listeners) {
  462. listener.apply(this, args);
  463. }
  464. }
  465. this.dispatch(args);
  466. }
  467. /**
  468. * Produces an ack callback to emit with an event.
  469. *
  470. * @param {Number} id - packet id
  471. * @private
  472. */
  473. ack(id) {
  474. const self = this;
  475. let sent = false;
  476. return function () {
  477. // prevent double callbacks
  478. if (sent)
  479. return;
  480. const args = Array.prototype.slice.call(arguments);
  481. debug("sending ack %j", args);
  482. self.packet({
  483. id: id,
  484. type: socket_io_parser_1.PacketType.ACK,
  485. data: args,
  486. });
  487. sent = true;
  488. };
  489. }
  490. /**
  491. * Called upon ack packet.
  492. *
  493. * @private
  494. */
  495. onack(packet) {
  496. const ack = this.acks.get(packet.id);
  497. if ("function" == typeof ack) {
  498. debug("calling ack %s with %j", packet.id, packet.data);
  499. ack.apply(this, packet.data);
  500. this.acks.delete(packet.id);
  501. }
  502. else {
  503. debug("bad ack %s", packet.id);
  504. }
  505. }
  506. /**
  507. * Called upon client disconnect packet.
  508. *
  509. * @private
  510. */
  511. ondisconnect() {
  512. debug("got disconnect packet");
  513. this._onclose("client namespace disconnect");
  514. }
  515. /**
  516. * Handles a client error.
  517. *
  518. * @private
  519. */
  520. _onerror(err) {
  521. // FIXME the meaning of the "error" event is overloaded:
  522. // - it can be sent by the client (`socket.emit("error")`)
  523. // - it can be emitted when the connection encounters an error (an invalid packet for example)
  524. // - it can be emitted when a packet is rejected in a middleware (`socket.use()`)
  525. this.emitReserved("error", err);
  526. }
  527. /**
  528. * Called upon closing. Called by `Client`.
  529. *
  530. * @param {String} reason
  531. * @param description
  532. * @throw {Error} optional error object
  533. *
  534. * @private
  535. */
  536. _onclose(reason, description) {
  537. if (!this.connected)
  538. return this;
  539. debug("closing socket - reason %s", reason);
  540. this.emitReserved("disconnecting", reason, description);
  541. if (this.server._opts.connectionStateRecovery &&
  542. RECOVERABLE_DISCONNECT_REASONS.has(reason)) {
  543. debug("connection state recovery is enabled for sid %s", this.id);
  544. this.adapter.persistSession({
  545. sid: this.id,
  546. pid: this.pid,
  547. rooms: [...this.rooms],
  548. data: this.data,
  549. });
  550. }
  551. this._cleanup();
  552. this.client._remove(this);
  553. this.connected = false;
  554. this.emitReserved("disconnect", reason, description);
  555. return;
  556. }
  557. /**
  558. * Makes the socket leave all the rooms it was part of and prevents it from joining any other room
  559. *
  560. * @private
  561. */
  562. _cleanup() {
  563. this.leaveAll();
  564. this.nsp._remove(this);
  565. this.join = noop;
  566. }
  567. /**
  568. * Produces an `error` packet.
  569. *
  570. * @param {Object} err - error object
  571. *
  572. * @private
  573. */
  574. _error(err) {
  575. this.packet({ type: socket_io_parser_1.PacketType.CONNECT_ERROR, data: err });
  576. }
  577. /**
  578. * Disconnects this client.
  579. *
  580. * @example
  581. * io.on("connection", (socket) => {
  582. * // disconnect this socket (the connection might be kept alive for other namespaces)
  583. * socket.disconnect();
  584. *
  585. * // disconnect this socket and close the underlying connection
  586. * socket.disconnect(true);
  587. * })
  588. *
  589. * @param {Boolean} close - if `true`, closes the underlying connection
  590. * @return self
  591. */
  592. disconnect(close = false) {
  593. if (!this.connected)
  594. return this;
  595. if (close) {
  596. this.client._disconnect();
  597. }
  598. else {
  599. this.packet({ type: socket_io_parser_1.PacketType.DISCONNECT });
  600. this._onclose("server namespace disconnect");
  601. }
  602. return this;
  603. }
  604. /**
  605. * Sets the compress flag.
  606. *
  607. * @example
  608. * io.on("connection", (socket) => {
  609. * socket.compress(false).emit("hello");
  610. * });
  611. *
  612. * @param {Boolean} compress - if `true`, compresses the sending data
  613. * @return {Socket} self
  614. */
  615. compress(compress) {
  616. this.flags.compress = compress;
  617. return this;
  618. }
  619. /**
  620. * Sets a modifier for a subsequent event emission that the event data may be lost if the client is not ready to
  621. * receive messages (because of network slowness or other issues, or because they’re connected through long polling
  622. * and is in the middle of a request-response cycle).
  623. *
  624. * @example
  625. * io.on("connection", (socket) => {
  626. * socket.volatile.emit("hello"); // the client may or may not receive it
  627. * });
  628. *
  629. * @return {Socket} self
  630. */
  631. get volatile() {
  632. this.flags.volatile = true;
  633. return this;
  634. }
  635. /**
  636. * Sets a modifier for a subsequent event emission that the event data will only be broadcast to every sockets but the
  637. * sender.
  638. *
  639. * @example
  640. * io.on("connection", (socket) => {
  641. * // the “foo” event will be broadcast to all connected clients, except this socket
  642. * socket.broadcast.emit("foo", "bar");
  643. * });
  644. *
  645. * @return a new {@link BroadcastOperator} instance for chaining
  646. */
  647. get broadcast() {
  648. return this.newBroadcastOperator();
  649. }
  650. /**
  651. * Sets a modifier for a subsequent event emission that the event data will only be broadcast to the current node.
  652. *
  653. * @example
  654. * io.on("connection", (socket) => {
  655. * // the “foo” event will be broadcast to all connected clients on this node, except this socket
  656. * socket.local.emit("foo", "bar");
  657. * });
  658. *
  659. * @return a new {@link BroadcastOperator} instance for chaining
  660. */
  661. get local() {
  662. return this.newBroadcastOperator().local;
  663. }
  664. /**
  665. * Sets a modifier for a subsequent event emission that the callback will be called with an error when the
  666. * given number of milliseconds have elapsed without an acknowledgement from the client:
  667. *
  668. * @example
  669. * io.on("connection", (socket) => {
  670. * socket.timeout(5000).emit("my-event", (err) => {
  671. * if (err) {
  672. * // the client did not acknowledge the event in the given delay
  673. * }
  674. * });
  675. * });
  676. *
  677. * @returns self
  678. */
  679. timeout(timeout) {
  680. this.flags.timeout = timeout;
  681. return this;
  682. }
  683. /**
  684. * Dispatch incoming event to socket listeners.
  685. *
  686. * @param {Array} event - event that will get emitted
  687. * @private
  688. */
  689. dispatch(event) {
  690. debug("dispatching an event %j", event);
  691. this.run(event, (err) => {
  692. process.nextTick(() => {
  693. if (err) {
  694. return this._onerror(err);
  695. }
  696. if (this.connected) {
  697. super.emitUntyped.apply(this, event);
  698. }
  699. else {
  700. debug("ignore packet received after disconnection");
  701. }
  702. });
  703. });
  704. }
  705. /**
  706. * Sets up socket middleware.
  707. *
  708. * @example
  709. * io.on("connection", (socket) => {
  710. * socket.use(([event, ...args], next) => {
  711. * if (isUnauthorized(event)) {
  712. * return next(new Error("unauthorized event"));
  713. * }
  714. * // do not forget to call next
  715. * next();
  716. * });
  717. *
  718. * socket.on("error", (err) => {
  719. * if (err && err.message === "unauthorized event") {
  720. * socket.disconnect();
  721. * }
  722. * });
  723. * });
  724. *
  725. * @param {Function} fn - middleware function (event, next)
  726. * @return {Socket} self
  727. */
  728. use(fn) {
  729. this.fns.push(fn);
  730. return this;
  731. }
  732. /**
  733. * Executes the middleware for an incoming event.
  734. *
  735. * @param {Array} event - event that will get emitted
  736. * @param {Function} fn - last fn call in the middleware
  737. * @private
  738. */
  739. run(event, fn) {
  740. if (!this.fns.length)
  741. return fn();
  742. const fns = this.fns.slice(0);
  743. function run(i) {
  744. fns[i](event, (err) => {
  745. // upon error, short-circuit
  746. if (err)
  747. return fn(err);
  748. // if no middleware left, summon callback
  749. if (!fns[i + 1])
  750. return fn();
  751. // go on to next
  752. run(i + 1);
  753. });
  754. }
  755. run(0);
  756. }
  757. /**
  758. * Whether the socket is currently disconnected
  759. */
  760. get disconnected() {
  761. return !this.connected;
  762. }
  763. /**
  764. * A reference to the request that originated the underlying Engine.IO Socket.
  765. */
  766. get request() {
  767. return this.client.request;
  768. }
  769. /**
  770. * A reference to the underlying Client transport connection (Engine.IO Socket object).
  771. *
  772. * @example
  773. * io.on("connection", (socket) => {
  774. * console.log(socket.conn.transport.name); // prints "polling" or "websocket"
  775. *
  776. * socket.conn.once("upgrade", () => {
  777. * console.log(socket.conn.transport.name); // prints "websocket"
  778. * });
  779. * });
  780. */
  781. get conn() {
  782. return this.client.conn;
  783. }
  784. /**
  785. * Returns the rooms the socket is currently in.
  786. *
  787. * @example
  788. * io.on("connection", (socket) => {
  789. * console.log(socket.rooms); // Set { <socket.id> }
  790. *
  791. * socket.join("room1");
  792. *
  793. * console.log(socket.rooms); // Set { <socket.id>, "room1" }
  794. * });
  795. */
  796. get rooms() {
  797. return this.adapter.socketRooms(this.id) || new Set();
  798. }
  799. /**
  800. * Adds a listener that will be fired when any event is received. The event name is passed as the first argument to
  801. * the callback.
  802. *
  803. * @example
  804. * io.on("connection", (socket) => {
  805. * socket.onAny((event, ...args) => {
  806. * console.log(`got event ${event}`);
  807. * });
  808. * });
  809. *
  810. * @param listener
  811. */
  812. onAny(listener) {
  813. this._anyListeners = this._anyListeners || [];
  814. this._anyListeners.push(listener);
  815. return this;
  816. }
  817. /**
  818. * Adds a listener that will be fired when any event is received. The event name is passed as the first argument to
  819. * the callback. The listener is added to the beginning of the listeners array.
  820. *
  821. * @param listener
  822. */
  823. prependAny(listener) {
  824. this._anyListeners = this._anyListeners || [];
  825. this._anyListeners.unshift(listener);
  826. return this;
  827. }
  828. /**
  829. * Removes the listener that will be fired when any event is received.
  830. *
  831. * @example
  832. * io.on("connection", (socket) => {
  833. * const catchAllListener = (event, ...args) => {
  834. * console.log(`got event ${event}`);
  835. * }
  836. *
  837. * socket.onAny(catchAllListener);
  838. *
  839. * // remove a specific listener
  840. * socket.offAny(catchAllListener);
  841. *
  842. * // or remove all listeners
  843. * socket.offAny();
  844. * });
  845. *
  846. * @param listener
  847. */
  848. offAny(listener) {
  849. if (!this._anyListeners) {
  850. return this;
  851. }
  852. if (listener) {
  853. const listeners = this._anyListeners;
  854. for (let i = 0; i < listeners.length; i++) {
  855. if (listener === listeners[i]) {
  856. listeners.splice(i, 1);
  857. return this;
  858. }
  859. }
  860. }
  861. else {
  862. this._anyListeners = [];
  863. }
  864. return this;
  865. }
  866. /**
  867. * Returns an array of listeners that are listening for any event that is specified. This array can be manipulated,
  868. * e.g. to remove listeners.
  869. */
  870. listenersAny() {
  871. return this._anyListeners || [];
  872. }
  873. /**
  874. * Adds a listener that will be fired when any event is sent. The event name is passed as the first argument to
  875. * the callback.
  876. *
  877. * Note: acknowledgements sent to the client are not included.
  878. *
  879. * @example
  880. * io.on("connection", (socket) => {
  881. * socket.onAnyOutgoing((event, ...args) => {
  882. * console.log(`sent event ${event}`);
  883. * });
  884. * });
  885. *
  886. * @param listener
  887. */
  888. onAnyOutgoing(listener) {
  889. this._anyOutgoingListeners = this._anyOutgoingListeners || [];
  890. this._anyOutgoingListeners.push(listener);
  891. return this;
  892. }
  893. /**
  894. * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the
  895. * callback. The listener is added to the beginning of the listeners array.
  896. *
  897. * @example
  898. * io.on("connection", (socket) => {
  899. * socket.prependAnyOutgoing((event, ...args) => {
  900. * console.log(`sent event ${event}`);
  901. * });
  902. * });
  903. *
  904. * @param listener
  905. */
  906. prependAnyOutgoing(listener) {
  907. this._anyOutgoingListeners = this._anyOutgoingListeners || [];
  908. this._anyOutgoingListeners.unshift(listener);
  909. return this;
  910. }
  911. /**
  912. * Removes the listener that will be fired when any event is sent.
  913. *
  914. * @example
  915. * io.on("connection", (socket) => {
  916. * const catchAllListener = (event, ...args) => {
  917. * console.log(`sent event ${event}`);
  918. * }
  919. *
  920. * socket.onAnyOutgoing(catchAllListener);
  921. *
  922. * // remove a specific listener
  923. * socket.offAnyOutgoing(catchAllListener);
  924. *
  925. * // or remove all listeners
  926. * socket.offAnyOutgoing();
  927. * });
  928. *
  929. * @param listener - the catch-all listener
  930. */
  931. offAnyOutgoing(listener) {
  932. if (!this._anyOutgoingListeners) {
  933. return this;
  934. }
  935. if (listener) {
  936. const listeners = this._anyOutgoingListeners;
  937. for (let i = 0; i < listeners.length; i++) {
  938. if (listener === listeners[i]) {
  939. listeners.splice(i, 1);
  940. return this;
  941. }
  942. }
  943. }
  944. else {
  945. this._anyOutgoingListeners = [];
  946. }
  947. return this;
  948. }
  949. /**
  950. * Returns an array of listeners that are listening for any event that is specified. This array can be manipulated,
  951. * e.g. to remove listeners.
  952. */
  953. listenersAnyOutgoing() {
  954. return this._anyOutgoingListeners || [];
  955. }
  956. /**
  957. * Notify the listeners for each packet sent (emit or broadcast)
  958. *
  959. * @param packet
  960. *
  961. * @private
  962. */
  963. notifyOutgoingListeners(packet) {
  964. if (this._anyOutgoingListeners && this._anyOutgoingListeners.length) {
  965. const listeners = this._anyOutgoingListeners.slice();
  966. for (const listener of listeners) {
  967. listener.apply(this, packet.data);
  968. }
  969. }
  970. }
  971. newBroadcastOperator() {
  972. const flags = Object.assign({}, this.flags);
  973. this.flags = {};
  974. return new broadcast_operator_1.BroadcastOperator(this.adapter, new Set(), new Set([this.id]), flags);
  975. }
  976. }
  977. exports.Socket = Socket;