UNPKG

7.59 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.Client = void 0;
4const socket_io_parser_1 = require("socket.io-parser");
5const debugModule = require("debug");
6const url = require("url");
7const debug = debugModule("socket.io:client");
8class 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 debug("dynamic namespace %s was created", dynamicNspName);
74 this.doConnect(name, auth);
75 }
76 else {
77 debug("creation of namespace %s was denied", name);
78 this._packet({
79 type: socket_io_parser_1.PacketType.CONNECT_ERROR,
80 nsp: name,
81 data: {
82 message: "Invalid namespace",
83 },
84 });
85 }
86 });
87 }
88 /**
89 * Connects a client to a namespace.
90 *
91 * @param name - the namespace
92 * @param {Object} auth - the auth parameters
93 *
94 * @private
95 */
96 doConnect(name, auth) {
97 const nsp = this.server.of(name);
98 const socket = nsp._add(this, auth, () => {
99 this.sockets.set(socket.id, socket);
100 this.nsps.set(nsp.name, socket);
101 if (this.connectTimeout) {
102 clearTimeout(this.connectTimeout);
103 this.connectTimeout = undefined;
104 }
105 });
106 }
107 /**
108 * Disconnects from all namespaces and closes transport.
109 *
110 * @private
111 */
112 _disconnect() {
113 for (const socket of this.sockets.values()) {
114 socket.disconnect();
115 }
116 this.sockets.clear();
117 this.close();
118 }
119 /**
120 * Removes a socket. Called by each `Socket`.
121 *
122 * @private
123 */
124 _remove(socket) {
125 if (this.sockets.has(socket.id)) {
126 const nsp = this.sockets.get(socket.id).nsp.name;
127 this.sockets.delete(socket.id);
128 this.nsps.delete(nsp);
129 }
130 else {
131 debug("ignoring remove for %s", socket.id);
132 }
133 }
134 /**
135 * Closes the underlying connection.
136 *
137 * @private
138 */
139 close() {
140 if ("open" === this.conn.readyState) {
141 debug("forcing transport close");
142 this.conn.close();
143 this.onclose("forced server close");
144 }
145 }
146 /**
147 * Writes a packet to the transport.
148 *
149 * @param {Object} packet object
150 * @param {Object} opts
151 * @private
152 */
153 _packet(packet, opts = {}) {
154 if (this.conn.readyState !== "open") {
155 debug("ignoring packet write %j", packet);
156 return;
157 }
158 const encodedPackets = opts.preEncoded
159 ? packet // previous versions of the adapter incorrectly used socket.packet() instead of writeToEngine()
160 : this.encoder.encode(packet);
161 this.writeToEngine(encodedPackets, opts);
162 }
163 writeToEngine(encodedPackets, opts) {
164 if (opts.volatile && !this.conn.transport.writable) {
165 debug("volatile packet is discarded since the transport is not currently writable");
166 return;
167 }
168 const packets = Array.isArray(encodedPackets)
169 ? encodedPackets
170 : [encodedPackets];
171 for (const encodedPacket of packets) {
172 this.conn.write(encodedPacket, opts);
173 }
174 }
175 /**
176 * Called with incoming transport data.
177 *
178 * @private
179 */
180 ondata(data) {
181 // try/catch is needed for protocol violations (GH-1880)
182 try {
183 this.decoder.add(data);
184 }
185 catch (e) {
186 this.onerror(e);
187 }
188 }
189 /**
190 * Called when parser fully decodes a packet.
191 *
192 * @private
193 */
194 ondecoded(packet) {
195 if (socket_io_parser_1.PacketType.CONNECT === packet.type) {
196 if (this.conn.protocol === 3) {
197 const parsed = url.parse(packet.nsp, true);
198 this.connect(parsed.pathname, parsed.query);
199 }
200 else {
201 this.connect(packet.nsp, packet.data);
202 }
203 }
204 else {
205 const socket = this.nsps.get(packet.nsp);
206 if (socket) {
207 process.nextTick(function () {
208 socket._onpacket(packet);
209 });
210 }
211 else {
212 debug("no socket for namespace %s", packet.nsp);
213 }
214 }
215 }
216 /**
217 * Handles an error.
218 *
219 * @param {Object} err object
220 * @private
221 */
222 onerror(err) {
223 for (const socket of this.sockets.values()) {
224 socket._onerror(err);
225 }
226 this.conn.close();
227 }
228 /**
229 * Called upon transport close.
230 *
231 * @param reason
232 * @private
233 */
234 onclose(reason) {
235 debug("client close with reason %s", reason);
236 // ignore a potential subsequent `close` event
237 this.destroy();
238 // `nsps` and `sockets` are cleaned up seamlessly
239 for (const socket of this.sockets.values()) {
240 socket._onclose(reason);
241 }
242 this.sockets.clear();
243 this.decoder.destroy(); // clean up decoder
244 }
245 /**
246 * Cleans up event listeners.
247 * @private
248 */
249 destroy() {
250 this.conn.removeListener("data", this.ondata);
251 this.conn.removeListener("error", this.onerror);
252 this.conn.removeListener("close", this.onclose);
253 // @ts-ignore
254 this.decoder.removeListener("decoded", this.ondecoded);
255 if (this.connectTimeout) {
256 clearTimeout(this.connectTimeout);
257 this.connectTimeout = undefined;
258 }
259 }
260}
261exports.Client = Client;