socket.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  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 = exports.RESERVED_EVENTS = void 0;
  7. const events_1 = require("events");
  8. const socket_io_parser_1 = require("socket.io-parser");
  9. const url = require("url");
  10. const debug_1 = __importDefault(require("debug"));
  11. const base64id_1 = __importDefault(require("base64id"));
  12. const debug = debug_1.default("socket.io:socket");
  13. exports.RESERVED_EVENTS = new Set([
  14. "connect",
  15. "connect_error",
  16. "disconnect",
  17. "disconnecting",
  18. // EventEmitter reserved events: https://nodejs.org/api/events.html#events_event_newlistener
  19. "newListener",
  20. "removeListener"
  21. ]);
  22. class Socket extends events_1.EventEmitter {
  23. /**
  24. * Interface to a `Client` for a given `Namespace`.
  25. *
  26. * @param {Namespace} nsp
  27. * @param {Client} client
  28. * @param {Object} auth
  29. * @package
  30. */
  31. constructor(nsp, client, auth) {
  32. super();
  33. this.nsp = nsp;
  34. this.client = client;
  35. this.acks = new Map();
  36. this.fns = [];
  37. this.flags = {};
  38. this._rooms = new Set();
  39. this.server = nsp.server;
  40. this.adapter = this.nsp.adapter;
  41. this.id = base64id_1.default.generateId(); // don't reuse the Engine.IO id because it's sensitive information
  42. this.connected = true;
  43. this.disconnected = false;
  44. this.handshake = this.buildHandshake(auth);
  45. }
  46. /**
  47. * Builds the `handshake` BC object
  48. *
  49. * @private
  50. */
  51. buildHandshake(auth) {
  52. return {
  53. headers: this.request.headers,
  54. time: new Date() + "",
  55. address: this.conn.remoteAddress,
  56. xdomain: !!this.request.headers.origin,
  57. // @ts-ignore
  58. secure: !!this.request.connection.encrypted,
  59. issued: +new Date(),
  60. url: this.request.url,
  61. query: url.parse(this.request.url, true).query,
  62. auth
  63. };
  64. }
  65. /**
  66. * Emits to this client.
  67. *
  68. * @return {Boolean} Always true
  69. * @public
  70. */
  71. emit(ev, ...args) {
  72. if (exports.RESERVED_EVENTS.has(ev)) {
  73. throw new Error(`"${ev}" is a reserved event name`);
  74. }
  75. args.unshift(ev);
  76. const packet = {
  77. type: socket_io_parser_1.PacketType.EVENT,
  78. data: args
  79. };
  80. // access last argument to see if it's an ACK callback
  81. if (typeof args[args.length - 1] === "function") {
  82. if (this._rooms.size || this.flags.broadcast) {
  83. throw new Error("Callbacks are not supported when broadcasting");
  84. }
  85. debug("emitting packet with ack id %d", this.nsp._ids);
  86. this.acks.set(this.nsp._ids, args.pop());
  87. packet.id = this.nsp._ids++;
  88. }
  89. const rooms = new Set(this._rooms);
  90. const flags = Object.assign({}, this.flags);
  91. // reset flags
  92. this._rooms.clear();
  93. this.flags = {};
  94. if (rooms.size || flags.broadcast) {
  95. this.adapter.broadcast(packet, {
  96. except: new Set([this.id]),
  97. rooms: rooms,
  98. flags: flags
  99. });
  100. }
  101. else {
  102. // dispatch packet
  103. this.packet(packet, flags);
  104. }
  105. return true;
  106. }
  107. /**
  108. * Targets a room when broadcasting.
  109. *
  110. * @param {String} name
  111. * @return {Socket} self
  112. * @public
  113. */
  114. to(name) {
  115. this._rooms.add(name);
  116. return this;
  117. }
  118. /**
  119. * Targets a room when broadcasting.
  120. *
  121. * @param {String} name
  122. * @return {Socket} self
  123. * @public
  124. */
  125. in(name) {
  126. this._rooms.add(name);
  127. return this;
  128. }
  129. /**
  130. * Sends a `message` event.
  131. *
  132. * @return {Socket} self
  133. * @public
  134. */
  135. send(...args) {
  136. args.unshift("message");
  137. this.emit.apply(this, args);
  138. return this;
  139. }
  140. /**
  141. * Sends a `message` event.
  142. *
  143. * @return {Socket} self
  144. * @public
  145. */
  146. write(...args) {
  147. args.unshift("message");
  148. this.emit.apply(this, args);
  149. return this;
  150. }
  151. /**
  152. * Writes a packet.
  153. *
  154. * @param {Object} packet - packet object
  155. * @param {Object} opts - options
  156. * @private
  157. */
  158. packet(packet, opts = {}) {
  159. packet.nsp = this.nsp.name;
  160. opts.compress = false !== opts.compress;
  161. this.client._packet(packet, opts);
  162. }
  163. /**
  164. * Joins a room.
  165. *
  166. * @param {String|Array} rooms - room or array of rooms
  167. * @return a Promise or nothing, depending on the adapter
  168. * @public
  169. */
  170. join(rooms) {
  171. debug("join room %s", rooms);
  172. return this.adapter.addAll(this.id, new Set(Array.isArray(rooms) ? rooms : [rooms]));
  173. }
  174. /**
  175. * Leaves a room.
  176. *
  177. * @param {String} room
  178. * @return a Promise or nothing, depending on the adapter
  179. * @public
  180. */
  181. leave(room) {
  182. debug("leave room %s", room);
  183. return this.adapter.del(this.id, room);
  184. }
  185. /**
  186. * Leave all rooms.
  187. *
  188. * @private
  189. */
  190. leaveAll() {
  191. this.adapter.delAll(this.id);
  192. }
  193. /**
  194. * Called by `Namespace` upon successful
  195. * middleware execution (ie: authorization).
  196. * Socket is added to namespace array before
  197. * call to join, so adapters can access it.
  198. *
  199. * @private
  200. */
  201. _onconnect() {
  202. debug("socket connected - writing packet");
  203. this.join(this.id);
  204. this.packet({ type: socket_io_parser_1.PacketType.CONNECT, data: { sid: this.id } });
  205. }
  206. /**
  207. * Called with each packet. Called by `Client`.
  208. *
  209. * @param {Object} packet
  210. * @private
  211. */
  212. _onpacket(packet) {
  213. debug("got packet %j", packet);
  214. switch (packet.type) {
  215. case socket_io_parser_1.PacketType.EVENT:
  216. this.onevent(packet);
  217. break;
  218. case socket_io_parser_1.PacketType.BINARY_EVENT:
  219. this.onevent(packet);
  220. break;
  221. case socket_io_parser_1.PacketType.ACK:
  222. this.onack(packet);
  223. break;
  224. case socket_io_parser_1.PacketType.BINARY_ACK:
  225. this.onack(packet);
  226. break;
  227. case socket_io_parser_1.PacketType.DISCONNECT:
  228. this.ondisconnect();
  229. break;
  230. case socket_io_parser_1.PacketType.CONNECT_ERROR:
  231. this._onerror(new Error(packet.data));
  232. }
  233. }
  234. /**
  235. * Called upon event packet.
  236. *
  237. * @param {Object} packet - packet object
  238. * @private
  239. */
  240. onevent(packet) {
  241. const args = packet.data || [];
  242. debug("emitting event %j", args);
  243. if (null != packet.id) {
  244. debug("attaching ack callback to event");
  245. args.push(this.ack(packet.id));
  246. }
  247. if (this._anyListeners && this._anyListeners.length) {
  248. const listeners = this._anyListeners.slice();
  249. for (const listener of listeners) {
  250. listener.apply(this, args);
  251. }
  252. }
  253. super.emit.apply(this, args);
  254. }
  255. /**
  256. * Produces an ack callback to emit with an event.
  257. *
  258. * @param {Number} id - packet id
  259. * @private
  260. */
  261. ack(id) {
  262. const self = this;
  263. let sent = false;
  264. return function () {
  265. // prevent double callbacks
  266. if (sent)
  267. return;
  268. const args = Array.prototype.slice.call(arguments);
  269. debug("sending ack %j", args);
  270. self.packet({
  271. id: id,
  272. type: socket_io_parser_1.PacketType.ACK,
  273. data: args
  274. });
  275. sent = true;
  276. };
  277. }
  278. /**
  279. * Called upon ack packet.
  280. *
  281. * @private
  282. */
  283. onack(packet) {
  284. const ack = this.acks.get(packet.id);
  285. if ("function" == typeof ack) {
  286. debug("calling ack %s with %j", packet.id, packet.data);
  287. ack.apply(this, packet.data);
  288. this.acks.delete(packet.id);
  289. }
  290. else {
  291. debug("bad ack %s", packet.id);
  292. }
  293. }
  294. /**
  295. * Called upon client disconnect packet.
  296. *
  297. * @private
  298. */
  299. ondisconnect() {
  300. debug("got disconnect packet");
  301. this._onclose("client namespace disconnect");
  302. }
  303. /**
  304. * Handles a client error.
  305. *
  306. * @private
  307. */
  308. _onerror(err) {
  309. if (this.listeners("error").length) {
  310. super.emit("error", err);
  311. }
  312. else {
  313. console.error("Missing error handler on `socket`.");
  314. console.error(err.stack);
  315. }
  316. }
  317. /**
  318. * Called upon closing. Called by `Client`.
  319. *
  320. * @param {String} reason
  321. * @throw {Error} optional error object
  322. *
  323. * @private
  324. */
  325. _onclose(reason) {
  326. if (!this.connected)
  327. return this;
  328. debug("closing socket - reason %s", reason);
  329. super.emit("disconnecting", reason);
  330. this.leaveAll();
  331. this.nsp._remove(this);
  332. this.client._remove(this);
  333. this.connected = false;
  334. this.disconnected = true;
  335. super.emit("disconnect", reason);
  336. }
  337. /**
  338. * Produces an `error` packet.
  339. *
  340. * @param {Object} err - error object
  341. *
  342. * @private
  343. */
  344. _error(err) {
  345. this.packet({ type: socket_io_parser_1.PacketType.CONNECT_ERROR, data: err });
  346. }
  347. /**
  348. * Disconnects this client.
  349. *
  350. * @param {Boolean} close - if `true`, closes the underlying connection
  351. * @return {Socket} self
  352. *
  353. * @public
  354. */
  355. disconnect(close = false) {
  356. if (!this.connected)
  357. return this;
  358. if (close) {
  359. this.client._disconnect();
  360. }
  361. else {
  362. this.packet({ type: socket_io_parser_1.PacketType.DISCONNECT });
  363. this._onclose("server namespace disconnect");
  364. }
  365. return this;
  366. }
  367. /**
  368. * Sets the compress flag.
  369. *
  370. * @param {Boolean} compress - if `true`, compresses the sending data
  371. * @return {Socket} self
  372. * @public
  373. */
  374. compress(compress) {
  375. this.flags.compress = compress;
  376. return this;
  377. }
  378. /**
  379. * Sets a modifier for a subsequent event emission that the event data may be lost if the client is not ready to
  380. * receive messages (because of network slowness or other issues, or because they’re connected through long polling
  381. * and is in the middle of a request-response cycle).
  382. *
  383. * @return {Socket} self
  384. * @public
  385. */
  386. get volatile() {
  387. this.flags.volatile = true;
  388. return this;
  389. }
  390. /**
  391. * Sets a modifier for a subsequent event emission that the event data will only be broadcast to every sockets but the
  392. * sender.
  393. *
  394. * @return {Socket} self
  395. * @public
  396. */
  397. get broadcast() {
  398. this.flags.broadcast = true;
  399. return this;
  400. }
  401. /**
  402. * Sets a modifier for a subsequent event emission that the event data will only be broadcast to the current node.
  403. *
  404. * @return {Socket} self
  405. * @public
  406. */
  407. get local() {
  408. this.flags.local = true;
  409. return this;
  410. }
  411. /**
  412. * A reference to the request that originated the underlying Engine.IO Socket.
  413. *
  414. * @public
  415. */
  416. get request() {
  417. return this.client.request;
  418. }
  419. /**
  420. * A reference to the underlying Client transport connection (Engine.IO Socket object).
  421. *
  422. * @public
  423. */
  424. get conn() {
  425. return this.client.conn;
  426. }
  427. /**
  428. * @public
  429. */
  430. get rooms() {
  431. return this.adapter.socketRooms(this.id) || new Set();
  432. }
  433. /**
  434. * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the
  435. * callback.
  436. *
  437. * @param listener
  438. * @public
  439. */
  440. onAny(listener) {
  441. this._anyListeners = this._anyListeners || [];
  442. this._anyListeners.push(listener);
  443. return this;
  444. }
  445. /**
  446. * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the
  447. * callback. The listener is added to the beginning of the listeners array.
  448. *
  449. * @param listener
  450. * @public
  451. */
  452. prependAny(listener) {
  453. this._anyListeners = this._anyListeners || [];
  454. this._anyListeners.unshift(listener);
  455. return this;
  456. }
  457. /**
  458. * Removes the listener that will be fired when any event is emitted.
  459. *
  460. * @param listener
  461. * @public
  462. */
  463. offAny(listener) {
  464. if (!this._anyListeners) {
  465. return this;
  466. }
  467. if (listener) {
  468. const listeners = this._anyListeners;
  469. for (let i = 0; i < listeners.length; i++) {
  470. if (listener === listeners[i]) {
  471. listeners.splice(i, 1);
  472. return this;
  473. }
  474. }
  475. }
  476. else {
  477. this._anyListeners = [];
  478. }
  479. return this;
  480. }
  481. /**
  482. * Returns an array of listeners that are listening for any event that is specified. This array can be manipulated,
  483. * e.g. to remove listeners.
  484. *
  485. * @public
  486. */
  487. listenersAny() {
  488. return this._anyListeners || [];
  489. }
  490. }
  491. exports.Socket = Socket;