[6a3a178] | 1 | "use strict";
|
---|
| 2 | Object.defineProperty(exports, "__esModule", { value: true });
|
---|
| 3 | exports.Client = void 0;
|
---|
| 4 | const socket_io_parser_1 = require("socket.io-parser");
|
---|
| 5 | const debugModule = require("debug");
|
---|
| 6 | const url = require("url");
|
---|
| 7 | const debug = debugModule("socket.io:client");
|
---|
| 8 | class Client {
|
---|
| 9 | /**
|
---|
| 10 | * Client constructor.
|
---|
| 11 | *
|
---|
| 12 | * @param server instance
|
---|
| 13 | * @param conn
|
---|
| 14 | * @package
|
---|
| 15 | */
|
---|
| 16 | constructor(server, conn) {
|
---|
| 17 | this.sockets = new Map();
|
---|
| 18 | this.nsps = new Map();
|
---|
| 19 | this.server = server;
|
---|
| 20 | this.conn = conn;
|
---|
| 21 | this.encoder = server.encoder;
|
---|
| 22 | this.decoder = new server._parser.Decoder();
|
---|
| 23 | this.id = conn.id;
|
---|
| 24 | this.setup();
|
---|
| 25 | }
|
---|
| 26 | /**
|
---|
| 27 | * @return the reference to the request that originated the Engine.IO connection
|
---|
| 28 | *
|
---|
| 29 | * @public
|
---|
| 30 | */
|
---|
| 31 | get request() {
|
---|
| 32 | return this.conn.request;
|
---|
| 33 | }
|
---|
| 34 | /**
|
---|
| 35 | * Sets up event listeners.
|
---|
| 36 | *
|
---|
| 37 | * @private
|
---|
| 38 | */
|
---|
| 39 | setup() {
|
---|
| 40 | this.onclose = this.onclose.bind(this);
|
---|
| 41 | this.ondata = this.ondata.bind(this);
|
---|
| 42 | this.onerror = this.onerror.bind(this);
|
---|
| 43 | this.ondecoded = this.ondecoded.bind(this);
|
---|
| 44 | // @ts-ignore
|
---|
| 45 | this.decoder.on("decoded", this.ondecoded);
|
---|
| 46 | this.conn.on("data", this.ondata);
|
---|
| 47 | this.conn.on("error", this.onerror);
|
---|
| 48 | this.conn.on("close", this.onclose);
|
---|
| 49 | this.connectTimeout = setTimeout(() => {
|
---|
| 50 | if (this.nsps.size === 0) {
|
---|
| 51 | debug("no namespace joined yet, close the client");
|
---|
| 52 | this.close();
|
---|
| 53 | }
|
---|
| 54 | else {
|
---|
| 55 | debug("the client has already joined a namespace, nothing to do");
|
---|
| 56 | }
|
---|
| 57 | }, this.server._connectTimeout);
|
---|
| 58 | }
|
---|
| 59 | /**
|
---|
| 60 | * Connects a client to a namespace.
|
---|
| 61 | *
|
---|
| 62 | * @param {String} name - the namespace
|
---|
| 63 | * @param {Object} auth - the auth parameters
|
---|
| 64 | * @private
|
---|
| 65 | */
|
---|
| 66 | connect(name, auth = {}) {
|
---|
| 67 | if (this.server._nsps.has(name)) {
|
---|
| 68 | debug("connecting to namespace %s", name);
|
---|
| 69 | return this.doConnect(name, auth);
|
---|
| 70 | }
|
---|
| 71 | this.server._checkNamespace(name, auth, (dynamicNspName) => {
|
---|
| 72 | if (dynamicNspName) {
|
---|
| 73 | this.doConnect(name, auth);
|
---|
| 74 | }
|
---|
| 75 | else {
|
---|
| 76 | debug("creation of namespace %s was denied", name);
|
---|
| 77 | this._packet({
|
---|
| 78 | type: socket_io_parser_1.PacketType.CONNECT_ERROR,
|
---|
| 79 | nsp: name,
|
---|
| 80 | data: {
|
---|
| 81 | message: "Invalid namespace",
|
---|
| 82 | },
|
---|
| 83 | });
|
---|
| 84 | }
|
---|
| 85 | });
|
---|
| 86 | }
|
---|
| 87 | /**
|
---|
| 88 | * Connects a client to a namespace.
|
---|
| 89 | *
|
---|
| 90 | * @param name - the namespace
|
---|
| 91 | * @param {Object} auth - the auth parameters
|
---|
| 92 | *
|
---|
| 93 | * @private
|
---|
| 94 | */
|
---|
| 95 | doConnect(name, auth) {
|
---|
| 96 | const nsp = this.server.of(name);
|
---|
| 97 | const socket = nsp._add(this, auth, () => {
|
---|
| 98 | this.sockets.set(socket.id, socket);
|
---|
| 99 | this.nsps.set(nsp.name, socket);
|
---|
| 100 | if (this.connectTimeout) {
|
---|
| 101 | clearTimeout(this.connectTimeout);
|
---|
| 102 | this.connectTimeout = undefined;
|
---|
| 103 | }
|
---|
| 104 | });
|
---|
| 105 | }
|
---|
| 106 | /**
|
---|
| 107 | * Disconnects from all namespaces and closes transport.
|
---|
| 108 | *
|
---|
| 109 | * @private
|
---|
| 110 | */
|
---|
| 111 | _disconnect() {
|
---|
| 112 | for (const socket of this.sockets.values()) {
|
---|
| 113 | socket.disconnect();
|
---|
| 114 | }
|
---|
| 115 | this.sockets.clear();
|
---|
| 116 | this.close();
|
---|
| 117 | }
|
---|
| 118 | /**
|
---|
| 119 | * Removes a socket. Called by each `Socket`.
|
---|
| 120 | *
|
---|
| 121 | * @private
|
---|
| 122 | */
|
---|
| 123 | _remove(socket) {
|
---|
| 124 | if (this.sockets.has(socket.id)) {
|
---|
| 125 | const nsp = this.sockets.get(socket.id).nsp.name;
|
---|
| 126 | this.sockets.delete(socket.id);
|
---|
| 127 | this.nsps.delete(nsp);
|
---|
| 128 | }
|
---|
| 129 | else {
|
---|
| 130 | debug("ignoring remove for %s", socket.id);
|
---|
| 131 | }
|
---|
| 132 | }
|
---|
| 133 | /**
|
---|
| 134 | * Closes the underlying connection.
|
---|
| 135 | *
|
---|
| 136 | * @private
|
---|
| 137 | */
|
---|
| 138 | close() {
|
---|
| 139 | if ("open" === this.conn.readyState) {
|
---|
| 140 | debug("forcing transport close");
|
---|
| 141 | this.conn.close();
|
---|
| 142 | this.onclose("forced server close");
|
---|
| 143 | }
|
---|
| 144 | }
|
---|
| 145 | /**
|
---|
| 146 | * Writes a packet to the transport.
|
---|
| 147 | *
|
---|
| 148 | * @param {Object} packet object
|
---|
| 149 | * @param {Object} opts
|
---|
| 150 | * @private
|
---|
| 151 | */
|
---|
[e29cc2e] | 152 | _packet(packet, opts = {}) {
|
---|
| 153 | if (this.conn.readyState !== "open") {
|
---|
| 154 | debug("ignoring packet write %j", packet);
|
---|
| 155 | return;
|
---|
[6a3a178] | 156 | }
|
---|
[e29cc2e] | 157 | const encodedPackets = opts.preEncoded
|
---|
| 158 | ? packet // previous versions of the adapter incorrectly used socket.packet() instead of writeToEngine()
|
---|
| 159 | : this.encoder.encode(packet);
|
---|
| 160 | this.writeToEngine(encodedPackets, opts);
|
---|
| 161 | }
|
---|
| 162 | writeToEngine(encodedPackets, opts) {
|
---|
| 163 | if (opts.volatile && !this.conn.transport.writable) {
|
---|
| 164 | debug("volatile packet is discarded since the transport is not currently writable");
|
---|
| 165 | return;
|
---|
[6a3a178] | 166 | }
|
---|
[e29cc2e] | 167 | const packets = Array.isArray(encodedPackets)
|
---|
| 168 | ? encodedPackets
|
---|
| 169 | : [encodedPackets];
|
---|
| 170 | for (const encodedPacket of packets) {
|
---|
| 171 | this.conn.write(encodedPacket, opts);
|
---|
[6a3a178] | 172 | }
|
---|
| 173 | }
|
---|
| 174 | /**
|
---|
| 175 | * Called with incoming transport data.
|
---|
| 176 | *
|
---|
| 177 | * @private
|
---|
| 178 | */
|
---|
| 179 | ondata(data) {
|
---|
| 180 | // try/catch is needed for protocol violations (GH-1880)
|
---|
| 181 | try {
|
---|
| 182 | this.decoder.add(data);
|
---|
| 183 | }
|
---|
| 184 | catch (e) {
|
---|
| 185 | this.onerror(e);
|
---|
| 186 | }
|
---|
| 187 | }
|
---|
| 188 | /**
|
---|
| 189 | * Called when parser fully decodes a packet.
|
---|
| 190 | *
|
---|
| 191 | * @private
|
---|
| 192 | */
|
---|
| 193 | ondecoded(packet) {
|
---|
| 194 | if (socket_io_parser_1.PacketType.CONNECT === packet.type) {
|
---|
| 195 | if (this.conn.protocol === 3) {
|
---|
| 196 | const parsed = url.parse(packet.nsp, true);
|
---|
| 197 | this.connect(parsed.pathname, parsed.query);
|
---|
| 198 | }
|
---|
| 199 | else {
|
---|
| 200 | this.connect(packet.nsp, packet.data);
|
---|
| 201 | }
|
---|
| 202 | }
|
---|
| 203 | else {
|
---|
| 204 | const socket = this.nsps.get(packet.nsp);
|
---|
| 205 | if (socket) {
|
---|
| 206 | process.nextTick(function () {
|
---|
| 207 | socket._onpacket(packet);
|
---|
| 208 | });
|
---|
| 209 | }
|
---|
| 210 | else {
|
---|
| 211 | debug("no socket for namespace %s", packet.nsp);
|
---|
| 212 | }
|
---|
| 213 | }
|
---|
| 214 | }
|
---|
| 215 | /**
|
---|
| 216 | * Handles an error.
|
---|
| 217 | *
|
---|
| 218 | * @param {Object} err object
|
---|
| 219 | * @private
|
---|
| 220 | */
|
---|
| 221 | onerror(err) {
|
---|
| 222 | for (const socket of this.sockets.values()) {
|
---|
| 223 | socket._onerror(err);
|
---|
| 224 | }
|
---|
| 225 | this.conn.close();
|
---|
| 226 | }
|
---|
| 227 | /**
|
---|
| 228 | * Called upon transport close.
|
---|
| 229 | *
|
---|
| 230 | * @param reason
|
---|
| 231 | * @private
|
---|
| 232 | */
|
---|
| 233 | onclose(reason) {
|
---|
| 234 | debug("client close with reason %s", reason);
|
---|
| 235 | // ignore a potential subsequent `close` event
|
---|
| 236 | this.destroy();
|
---|
| 237 | // `nsps` and `sockets` are cleaned up seamlessly
|
---|
| 238 | for (const socket of this.sockets.values()) {
|
---|
| 239 | socket._onclose(reason);
|
---|
| 240 | }
|
---|
| 241 | this.sockets.clear();
|
---|
| 242 | this.decoder.destroy(); // clean up decoder
|
---|
| 243 | }
|
---|
| 244 | /**
|
---|
| 245 | * Cleans up event listeners.
|
---|
| 246 | * @private
|
---|
| 247 | */
|
---|
| 248 | destroy() {
|
---|
| 249 | this.conn.removeListener("data", this.ondata);
|
---|
| 250 | this.conn.removeListener("error", this.onerror);
|
---|
| 251 | this.conn.removeListener("close", this.onclose);
|
---|
| 252 | // @ts-ignore
|
---|
| 253 | this.decoder.removeListener("decoded", this.ondecoded);
|
---|
| 254 | if (this.connectTimeout) {
|
---|
| 255 | clearTimeout(this.connectTimeout);
|
---|
| 256 | this.connectTimeout = undefined;
|
---|
| 257 | }
|
---|
| 258 | }
|
---|
| 259 | }
|
---|
| 260 | exports.Client = Client;
|
---|