123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491 |
- "use strict";
- var __importDefault = (this && this.__importDefault) || function (mod) {
- return (mod && mod.__esModule) ? mod : { "default": mod };
- };
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.Socket = exports.RESERVED_EVENTS = void 0;
- const events_1 = require("events");
- const socket_io_parser_1 = require("socket.io-parser");
- const url = require("url");
- const debug_1 = __importDefault(require("debug"));
- const base64id_1 = __importDefault(require("base64id"));
- const debug = debug_1.default("socket.io:socket");
- exports.RESERVED_EVENTS = new Set([
- "connect",
- "connect_error",
- "disconnect",
- "disconnecting",
- // EventEmitter reserved events: https://nodejs.org/api/events.html#events_event_newlistener
- "newListener",
- "removeListener"
- ]);
- class Socket extends events_1.EventEmitter {
- /**
- * Interface to a `Client` for a given `Namespace`.
- *
- * @param {Namespace} nsp
- * @param {Client} client
- * @param {Object} auth
- * @package
- */
- constructor(nsp, client, auth) {
- super();
- this.nsp = nsp;
- this.client = client;
- this.acks = new Map();
- this.fns = [];
- this.flags = {};
- this._rooms = new Set();
- this.server = nsp.server;
- this.adapter = this.nsp.adapter;
- this.id = base64id_1.default.generateId(); // don't reuse the Engine.IO id because it's sensitive information
- this.connected = true;
- this.disconnected = false;
- this.handshake = this.buildHandshake(auth);
- }
- /**
- * Builds the `handshake` BC object
- *
- * @private
- */
- buildHandshake(auth) {
- return {
- headers: this.request.headers,
- time: new Date() + "",
- address: this.conn.remoteAddress,
- xdomain: !!this.request.headers.origin,
- // @ts-ignore
- secure: !!this.request.connection.encrypted,
- issued: +new Date(),
- url: this.request.url,
- query: url.parse(this.request.url, true).query,
- auth
- };
- }
- /**
- * Emits to this client.
- *
- * @return {Boolean} Always true
- * @public
- */
- emit(ev, ...args) {
- if (exports.RESERVED_EVENTS.has(ev)) {
- throw new Error(`"${ev}" is a reserved event name`);
- }
- args.unshift(ev);
- const packet = {
- type: socket_io_parser_1.PacketType.EVENT,
- data: args
- };
- // access last argument to see if it's an ACK callback
- if (typeof args[args.length - 1] === "function") {
- if (this._rooms.size || this.flags.broadcast) {
- throw new Error("Callbacks are not supported when broadcasting");
- }
- debug("emitting packet with ack id %d", this.nsp._ids);
- this.acks.set(this.nsp._ids, args.pop());
- packet.id = this.nsp._ids++;
- }
- const rooms = new Set(this._rooms);
- const flags = Object.assign({}, this.flags);
- // reset flags
- this._rooms.clear();
- this.flags = {};
- if (rooms.size || flags.broadcast) {
- this.adapter.broadcast(packet, {
- except: new Set([this.id]),
- rooms: rooms,
- flags: flags
- });
- }
- else {
- // dispatch packet
- this.packet(packet, flags);
- }
- return true;
- }
- /**
- * Targets a room when broadcasting.
- *
- * @param {String} name
- * @return {Socket} self
- * @public
- */
- to(name) {
- this._rooms.add(name);
- return this;
- }
- /**
- * Targets a room when broadcasting.
- *
- * @param {String} name
- * @return {Socket} self
- * @public
- */
- in(name) {
- this._rooms.add(name);
- return this;
- }
- /**
- * Sends a `message` event.
- *
- * @return {Socket} self
- * @public
- */
- send(...args) {
- args.unshift("message");
- this.emit.apply(this, args);
- return this;
- }
- /**
- * Sends a `message` event.
- *
- * @return {Socket} self
- * @public
- */
- write(...args) {
- args.unshift("message");
- this.emit.apply(this, args);
- return this;
- }
- /**
- * Writes a packet.
- *
- * @param {Object} packet - packet object
- * @param {Object} opts - options
- * @private
- */
- packet(packet, opts = {}) {
- packet.nsp = this.nsp.name;
- opts.compress = false !== opts.compress;
- this.client._packet(packet, opts);
- }
- /**
- * Joins a room.
- *
- * @param {String|Array} rooms - room or array of rooms
- * @return a Promise or nothing, depending on the adapter
- * @public
- */
- join(rooms) {
- debug("join room %s", rooms);
- return this.adapter.addAll(this.id, new Set(Array.isArray(rooms) ? rooms : [rooms]));
- }
- /**
- * Leaves a room.
- *
- * @param {String} room
- * @return a Promise or nothing, depending on the adapter
- * @public
- */
- leave(room) {
- debug("leave room %s", room);
- return this.adapter.del(this.id, room);
- }
- /**
- * Leave all rooms.
- *
- * @private
- */
- leaveAll() {
- this.adapter.delAll(this.id);
- }
- /**
- * Called by `Namespace` upon successful
- * middleware execution (ie: authorization).
- * Socket is added to namespace array before
- * call to join, so adapters can access it.
- *
- * @private
- */
- _onconnect() {
- debug("socket connected - writing packet");
- this.join(this.id);
- this.packet({ type: socket_io_parser_1.PacketType.CONNECT, data: { sid: this.id } });
- }
- /**
- * Called with each packet. Called by `Client`.
- *
- * @param {Object} packet
- * @private
- */
- _onpacket(packet) {
- debug("got packet %j", packet);
- switch (packet.type) {
- case socket_io_parser_1.PacketType.EVENT:
- this.onevent(packet);
- break;
- case socket_io_parser_1.PacketType.BINARY_EVENT:
- this.onevent(packet);
- break;
- case socket_io_parser_1.PacketType.ACK:
- this.onack(packet);
- break;
- case socket_io_parser_1.PacketType.BINARY_ACK:
- this.onack(packet);
- break;
- case socket_io_parser_1.PacketType.DISCONNECT:
- this.ondisconnect();
- break;
- case socket_io_parser_1.PacketType.CONNECT_ERROR:
- this._onerror(new Error(packet.data));
- }
- }
- /**
- * Called upon event packet.
- *
- * @param {Object} packet - packet object
- * @private
- */
- onevent(packet) {
- const args = packet.data || [];
- debug("emitting event %j", args);
- if (null != packet.id) {
- debug("attaching ack callback to event");
- args.push(this.ack(packet.id));
- }
- if (this._anyListeners && this._anyListeners.length) {
- const listeners = this._anyListeners.slice();
- for (const listener of listeners) {
- listener.apply(this, args);
- }
- }
- super.emit.apply(this, args);
- }
- /**
- * Produces an ack callback to emit with an event.
- *
- * @param {Number} id - packet id
- * @private
- */
- ack(id) {
- const self = this;
- let sent = false;
- return function () {
- // prevent double callbacks
- if (sent)
- return;
- const args = Array.prototype.slice.call(arguments);
- debug("sending ack %j", args);
- self.packet({
- id: id,
- type: socket_io_parser_1.PacketType.ACK,
- data: args
- });
- sent = true;
- };
- }
- /**
- * Called upon ack packet.
- *
- * @private
- */
- onack(packet) {
- const ack = this.acks.get(packet.id);
- if ("function" == typeof ack) {
- debug("calling ack %s with %j", packet.id, packet.data);
- ack.apply(this, packet.data);
- this.acks.delete(packet.id);
- }
- else {
- debug("bad ack %s", packet.id);
- }
- }
- /**
- * Called upon client disconnect packet.
- *
- * @private
- */
- ondisconnect() {
- debug("got disconnect packet");
- this._onclose("client namespace disconnect");
- }
- /**
- * Handles a client error.
- *
- * @private
- */
- _onerror(err) {
- if (this.listeners("error").length) {
- super.emit("error", err);
- }
- else {
- console.error("Missing error handler on `socket`.");
- console.error(err.stack);
- }
- }
- /**
- * Called upon closing. Called by `Client`.
- *
- * @param {String} reason
- * @throw {Error} optional error object
- *
- * @private
- */
- _onclose(reason) {
- if (!this.connected)
- return this;
- debug("closing socket - reason %s", reason);
- super.emit("disconnecting", reason);
- this.leaveAll();
- this.nsp._remove(this);
- this.client._remove(this);
- this.connected = false;
- this.disconnected = true;
- super.emit("disconnect", reason);
- }
- /**
- * Produces an `error` packet.
- *
- * @param {Object} err - error object
- *
- * @private
- */
- _error(err) {
- this.packet({ type: socket_io_parser_1.PacketType.CONNECT_ERROR, data: err });
- }
- /**
- * Disconnects this client.
- *
- * @param {Boolean} close - if `true`, closes the underlying connection
- * @return {Socket} self
- *
- * @public
- */
- disconnect(close = false) {
- if (!this.connected)
- return this;
- if (close) {
- this.client._disconnect();
- }
- else {
- this.packet({ type: socket_io_parser_1.PacketType.DISCONNECT });
- this._onclose("server namespace disconnect");
- }
- return this;
- }
- /**
- * Sets the compress flag.
- *
- * @param {Boolean} compress - if `true`, compresses the sending data
- * @return {Socket} self
- * @public
- */
- compress(compress) {
- this.flags.compress = compress;
- return this;
- }
- /**
- * Sets a modifier for a subsequent event emission that the event data may be lost if the client is not ready to
- * receive messages (because of network slowness or other issues, or because they’re connected through long polling
- * and is in the middle of a request-response cycle).
- *
- * @return {Socket} self
- * @public
- */
- get volatile() {
- this.flags.volatile = true;
- return this;
- }
- /**
- * Sets a modifier for a subsequent event emission that the event data will only be broadcast to every sockets but the
- * sender.
- *
- * @return {Socket} self
- * @public
- */
- get broadcast() {
- this.flags.broadcast = true;
- return this;
- }
- /**
- * Sets a modifier for a subsequent event emission that the event data will only be broadcast to the current node.
- *
- * @return {Socket} self
- * @public
- */
- get local() {
- this.flags.local = true;
- return this;
- }
- /**
- * A reference to the request that originated the underlying Engine.IO Socket.
- *
- * @public
- */
- get request() {
- return this.client.request;
- }
- /**
- * A reference to the underlying Client transport connection (Engine.IO Socket object).
- *
- * @public
- */
- get conn() {
- return this.client.conn;
- }
- /**
- * @public
- */
- get rooms() {
- return this.adapter.socketRooms(this.id) || new Set();
- }
- /**
- * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the
- * callback.
- *
- * @param listener
- * @public
- */
- onAny(listener) {
- this._anyListeners = this._anyListeners || [];
- this._anyListeners.push(listener);
- return this;
- }
- /**
- * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the
- * callback. The listener is added to the beginning of the listeners array.
- *
- * @param listener
- * @public
- */
- prependAny(listener) {
- this._anyListeners = this._anyListeners || [];
- this._anyListeners.unshift(listener);
- return this;
- }
- /**
- * Removes the listener that will be fired when any event is emitted.
- *
- * @param listener
- * @public
- */
- offAny(listener) {
- if (!this._anyListeners) {
- return this;
- }
- if (listener) {
- const listeners = this._anyListeners;
- for (let i = 0; i < listeners.length; i++) {
- if (listener === listeners[i]) {
- listeners.splice(i, 1);
- return this;
- }
- }
- }
- else {
- this._anyListeners = [];
- }
- return this;
- }
- /**
- * Returns an array of listeners that are listening for any event that is specified. This array can be manipulated,
- * e.g. to remove listeners.
- *
- * @public
- */
- listenersAny() {
- return this._anyListeners || [];
- }
- }
- exports.Socket = Socket;
|