@gleamkit/engine.io 6.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,332 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Polling = void 0;
4
+ const transport_1 = require("../transport");
5
+ const zlib_1 = require("zlib");
6
+ const accepts = require("accepts");
7
+ const debug_1 = require("debug");
8
+ const debug = (0, debug_1.default)("engine:polling");
9
+ const compressionMethods = {
10
+ gzip: zlib_1.createGzip,
11
+ deflate: zlib_1.createDeflate,
12
+ };
13
+ class Polling extends transport_1.Transport {
14
+ /**
15
+ * HTTP polling constructor.
16
+ */
17
+ constructor(req) {
18
+ super(req);
19
+ this.closeTimeout = 30 * 1000;
20
+ }
21
+ /**
22
+ * Transport name
23
+ */
24
+ get name() {
25
+ return "polling";
26
+ }
27
+ /**
28
+ * Overrides onRequest.
29
+ *
30
+ * @param {EngineRequest} req
31
+ * @package
32
+ */
33
+ onRequest(req) {
34
+ const res = req.res;
35
+ // remove the reference to the ServerResponse object (as the first request of the session is kept in memory by default)
36
+ req.res = null;
37
+ if ("GET" === req.method) {
38
+ this.onPollRequest(req, res);
39
+ }
40
+ else if ("POST" === req.method) {
41
+ this.onDataRequest(req, res);
42
+ }
43
+ else {
44
+ res.writeHead(500);
45
+ res.end();
46
+ }
47
+ }
48
+ /**
49
+ * The client sends a request awaiting for us to send data.
50
+ *
51
+ * @private
52
+ */
53
+ onPollRequest(req, res) {
54
+ if (this.req) {
55
+ debug("request overlap");
56
+ // assert: this.res, '.req and .res should be (un)set together'
57
+ this.onError("overlap from client");
58
+ res.writeHead(400);
59
+ res.end();
60
+ return;
61
+ }
62
+ debug("setting request");
63
+ this.req = req;
64
+ this.res = res;
65
+ const onClose = () => {
66
+ this.onError("poll connection closed prematurely");
67
+ };
68
+ const cleanup = () => {
69
+ req.removeListener("close", onClose);
70
+ this.req = this.res = null;
71
+ };
72
+ req.cleanup = cleanup;
73
+ req.on("close", onClose);
74
+ this.writable = true;
75
+ this.emit("ready");
76
+ // if we're still writable but had a pending close, trigger an empty send
77
+ if (this.writable && this.shouldClose) {
78
+ debug("triggering empty send to append close packet");
79
+ this.send([{ type: "noop" }]);
80
+ }
81
+ }
82
+ /**
83
+ * The client sends a request with data.
84
+ *
85
+ * @private
86
+ */
87
+ onDataRequest(req, res) {
88
+ if (this.dataReq) {
89
+ // assert: this.dataRes, '.dataReq and .dataRes should be (un)set together'
90
+ this.onError("data request overlap from client");
91
+ res.writeHead(400);
92
+ res.end();
93
+ return;
94
+ }
95
+ const isBinary = "application/octet-stream" === req.headers["content-type"];
96
+ if (isBinary && this.protocol === 4) {
97
+ return this.onError("invalid content");
98
+ }
99
+ this.dataReq = req;
100
+ this.dataRes = res;
101
+ let chunks = isBinary ? Buffer.concat([]) : "";
102
+ const cleanup = () => {
103
+ req.removeListener("data", onData);
104
+ req.removeListener("end", onEnd);
105
+ req.removeListener("close", onClose);
106
+ this.dataReq = this.dataRes = chunks = null;
107
+ };
108
+ const onClose = () => {
109
+ cleanup();
110
+ this.onError("data request connection closed prematurely");
111
+ };
112
+ const onData = (data) => {
113
+ let contentLength;
114
+ if (isBinary) {
115
+ chunks = Buffer.concat([chunks, data]);
116
+ contentLength = chunks.length;
117
+ }
118
+ else {
119
+ chunks += data;
120
+ contentLength = Buffer.byteLength(chunks);
121
+ }
122
+ if (contentLength > this.maxHttpBufferSize) {
123
+ res.writeHead(413).end();
124
+ cleanup();
125
+ }
126
+ };
127
+ const onEnd = () => {
128
+ this.onData(chunks);
129
+ const headers = {
130
+ // text/html is required instead of text/plain to avoid an
131
+ // unwanted download dialog on certain user-agents (GH-43)
132
+ "Content-Type": "text/html",
133
+ "Content-Length": "2",
134
+ };
135
+ res.writeHead(200, this.headers(req, headers));
136
+ res.end("ok");
137
+ cleanup();
138
+ };
139
+ req.on("close", onClose);
140
+ if (!isBinary)
141
+ req.setEncoding("utf8");
142
+ req.on("data", onData);
143
+ req.on("end", onEnd);
144
+ }
145
+ /**
146
+ * Processes the incoming data payload.
147
+ *
148
+ * @param data - encoded payload
149
+ * @protected
150
+ */
151
+ onData(data) {
152
+ debug('received "%s"', data);
153
+ const callback = (packet) => {
154
+ if ("close" === packet.type) {
155
+ debug("got xhr close packet");
156
+ this.onClose();
157
+ return false;
158
+ }
159
+ this.onPacket(packet);
160
+ };
161
+ if (this.protocol === 3) {
162
+ this.parser.decodePayload(data, callback);
163
+ }
164
+ else {
165
+ this.parser.decodePayload(data).forEach(callback);
166
+ }
167
+ }
168
+ /**
169
+ * Overrides onClose.
170
+ *
171
+ * @private
172
+ */
173
+ onClose() {
174
+ if (this.writable) {
175
+ // close pending poll request
176
+ this.send([{ type: "noop" }]);
177
+ }
178
+ super.onClose();
179
+ }
180
+ send(packets) {
181
+ this.writable = false;
182
+ if (this.shouldClose) {
183
+ debug("appending close packet to payload");
184
+ packets.push({ type: "close" });
185
+ this.shouldClose();
186
+ this.shouldClose = null;
187
+ }
188
+ const doWrite = (data) => {
189
+ const compress = packets.some((packet) => {
190
+ return packet.options && packet.options.compress;
191
+ });
192
+ this.write(data, { compress });
193
+ };
194
+ if (this.protocol === 3) {
195
+ this.parser.encodePayload(packets, this.supportsBinary, doWrite);
196
+ }
197
+ else {
198
+ this.parser.encodePayload(packets, doWrite);
199
+ }
200
+ }
201
+ /**
202
+ * Writes data as response to poll request.
203
+ *
204
+ * @param {String} data
205
+ * @param {Object} options
206
+ * @private
207
+ */
208
+ write(data, options) {
209
+ debug('writing "%s"', data);
210
+ this.doWrite(data, options, () => {
211
+ this.req.cleanup();
212
+ this.emit("drain");
213
+ });
214
+ }
215
+ /**
216
+ * Performs the write.
217
+ *
218
+ * @protected
219
+ */
220
+ doWrite(data, options, callback) {
221
+ // explicit UTF-8 is required for pages not served under utf
222
+ const isString = typeof data === "string";
223
+ const contentType = isString
224
+ ? "text/plain; charset=UTF-8"
225
+ : "application/octet-stream";
226
+ const headers = {
227
+ "Content-Type": contentType,
228
+ };
229
+ const respond = (data) => {
230
+ headers["Content-Length"] =
231
+ "string" === typeof data ? Buffer.byteLength(data) : data.length;
232
+ this.res.writeHead(200, this.headers(this.req, headers));
233
+ this.res.end(data);
234
+ callback();
235
+ };
236
+ if (!this.httpCompression || !options.compress) {
237
+ respond(data);
238
+ return;
239
+ }
240
+ const len = isString ? Buffer.byteLength(data) : data.length;
241
+ if (len < this.httpCompression.threshold) {
242
+ respond(data);
243
+ return;
244
+ }
245
+ const encoding = accepts(this.req).encodings(["gzip", "deflate"]);
246
+ if (!encoding) {
247
+ respond(data);
248
+ return;
249
+ }
250
+ this.compress(data, encoding, (err, data) => {
251
+ if (err) {
252
+ this.res.writeHead(500);
253
+ this.res.end();
254
+ callback(err);
255
+ return;
256
+ }
257
+ headers["Content-Encoding"] = encoding;
258
+ respond(data);
259
+ });
260
+ }
261
+ /**
262
+ * Compresses data.
263
+ *
264
+ * @private
265
+ */
266
+ compress(data, encoding, callback) {
267
+ debug("compressing");
268
+ const buffers = [];
269
+ let nread = 0;
270
+ compressionMethods[encoding](this.httpCompression)
271
+ .on("error", callback)
272
+ .on("data", function (chunk) {
273
+ buffers.push(chunk);
274
+ nread += chunk.length;
275
+ })
276
+ .on("end", function () {
277
+ callback(null, Buffer.concat(buffers, nread));
278
+ })
279
+ .end(data);
280
+ }
281
+ /**
282
+ * Closes the transport.
283
+ *
284
+ * @private
285
+ */
286
+ doClose(fn) {
287
+ debug("closing");
288
+ let closeTimeoutTimer;
289
+ if (this.dataReq) {
290
+ debug("aborting ongoing data request");
291
+ this.dataReq.destroy();
292
+ }
293
+ const onClose = () => {
294
+ clearTimeout(closeTimeoutTimer);
295
+ fn();
296
+ this.onClose();
297
+ };
298
+ if (this.writable) {
299
+ debug("transport writable - closing right away");
300
+ this.send([{ type: "close" }]);
301
+ onClose();
302
+ }
303
+ else if (this.discarded) {
304
+ debug("transport discarded - closing right away");
305
+ onClose();
306
+ }
307
+ else {
308
+ debug("transport not writable - buffering orderly close");
309
+ this.shouldClose = onClose;
310
+ closeTimeoutTimer = setTimeout(onClose, this.closeTimeout);
311
+ }
312
+ }
313
+ /**
314
+ * Returns headers for a response.
315
+ *
316
+ * @param {http.IncomingMessage} req
317
+ * @param {Object} headers - extra headers
318
+ * @private
319
+ */
320
+ headers(req, headers = {}) {
321
+ // prevent XSS warnings on IE
322
+ // https://github.com/LearnBoost/socket.io/pull/1333
323
+ const ua = req.headers["user-agent"];
324
+ if (ua && (~ua.indexOf(";MSIE") || ~ua.indexOf("Trident/"))) {
325
+ headers["X-XSS-Protection"] = "0";
326
+ }
327
+ headers["cache-control"] = "no-store";
328
+ this.emit("headers", headers, req);
329
+ return headers;
330
+ }
331
+ }
332
+ exports.Polling = Polling;
@@ -0,0 +1,32 @@
1
+ import { EngineRequest, Transport } from "../transport";
2
+ import type { Packet } from "engine.io-parser";
3
+ export declare class WebSocket extends Transport {
4
+ protected perMessageDeflate: any;
5
+ private socket;
6
+ /**
7
+ * WebSocket transport
8
+ *
9
+ * @param {EngineRequest} req
10
+ */
11
+ constructor(req: EngineRequest);
12
+ /**
13
+ * Transport name
14
+ */
15
+ get name(): string;
16
+ /**
17
+ * Advertise upgrade support.
18
+ */
19
+ get handlesUpgrades(): boolean;
20
+ send(packets: Packet[]): void;
21
+ /**
22
+ * Whether the encoding of the WebSocket frame can be skipped.
23
+ * @param packet
24
+ * @private
25
+ */
26
+ private _canSendPreEncodedFrame;
27
+ private _doSend;
28
+ private _doSendLast;
29
+ private _onSent;
30
+ private _onSentLast;
31
+ doClose(fn?: () => void): void;
32
+ }
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WebSocket = void 0;
4
+ const transport_1 = require("../transport");
5
+ const debug_1 = require("debug");
6
+ const debug = (0, debug_1.default)("engine:ws");
7
+ class WebSocket extends transport_1.Transport {
8
+ /**
9
+ * WebSocket transport
10
+ *
11
+ * @param {EngineRequest} req
12
+ */
13
+ constructor(req) {
14
+ super(req);
15
+ this._doSend = (data) => {
16
+ this.socket.send(data, this._onSent);
17
+ };
18
+ this._doSendLast = (data) => {
19
+ this.socket.send(data, this._onSentLast);
20
+ };
21
+ this._onSent = (err) => {
22
+ if (err) {
23
+ this.onError("write error", err.stack);
24
+ }
25
+ };
26
+ this._onSentLast = (err) => {
27
+ if (err) {
28
+ this.onError("write error", err.stack);
29
+ }
30
+ else {
31
+ this.emit("drain");
32
+ this.writable = true;
33
+ this.emit("ready");
34
+ }
35
+ };
36
+ this.socket = req.websocket;
37
+ this.socket.on("message", (data, isBinary) => {
38
+ const message = isBinary ? data : data.toString();
39
+ debug('received "%s"', message);
40
+ super.onData(message);
41
+ });
42
+ this.socket.once("close", this.onClose.bind(this));
43
+ this.socket.on("error", this.onError.bind(this));
44
+ this.writable = true;
45
+ this.perMessageDeflate = null;
46
+ }
47
+ /**
48
+ * Transport name
49
+ */
50
+ get name() {
51
+ return "websocket";
52
+ }
53
+ /**
54
+ * Advertise upgrade support.
55
+ */
56
+ get handlesUpgrades() {
57
+ return true;
58
+ }
59
+ send(packets) {
60
+ this.writable = false;
61
+ for (let i = 0; i < packets.length; i++) {
62
+ const packet = packets[i];
63
+ const isLast = i + 1 === packets.length;
64
+ if (this._canSendPreEncodedFrame(packet)) {
65
+ // the WebSocket frame was computed with WebSocket.Sender.frame()
66
+ // see https://github.com/websockets/ws/issues/617#issuecomment-283002469
67
+ this.socket._sender.sendFrame(
68
+ // @ts-ignore
69
+ packet.options.wsPreEncodedFrame, isLast ? this._onSentLast : this._onSent);
70
+ }
71
+ else {
72
+ this.parser.encodePacket(packet, this.supportsBinary, isLast ? this._doSendLast : this._doSend);
73
+ }
74
+ }
75
+ }
76
+ /**
77
+ * Whether the encoding of the WebSocket frame can be skipped.
78
+ * @param packet
79
+ * @private
80
+ */
81
+ _canSendPreEncodedFrame(packet) {
82
+ var _a, _b, _c;
83
+ return (!this.perMessageDeflate &&
84
+ typeof ((_b = (_a = this.socket) === null || _a === void 0 ? void 0 : _a._sender) === null || _b === void 0 ? void 0 : _b.sendFrame) === "function" &&
85
+ // @ts-ignore
86
+ ((_c = packet.options) === null || _c === void 0 ? void 0 : _c.wsPreEncodedFrame) !== undefined);
87
+ }
88
+ doClose(fn) {
89
+ debug("closing");
90
+ this.socket.close();
91
+ fn && fn();
92
+ }
93
+ }
94
+ exports.WebSocket = WebSocket;
@@ -0,0 +1,12 @@
1
+ import { Transport } from "../transport";
2
+ /**
3
+ * Reference: https://developer.mozilla.org/en-US/docs/Web/API/WebTransport_API
4
+ */
5
+ export declare class WebTransport extends Transport {
6
+ private readonly session;
7
+ private readonly writer;
8
+ constructor(session: any, stream: any, reader: any);
9
+ get name(): string;
10
+ send(packets: any): Promise<void>;
11
+ doClose(fn: any): void;
12
+ }
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WebTransport = void 0;
4
+ const transport_1 = require("../transport");
5
+ const debug_1 = require("debug");
6
+ const engine_io_parser_1 = require("engine.io-parser");
7
+ const debug = (0, debug_1.default)("engine:webtransport");
8
+ /**
9
+ * Reference: https://developer.mozilla.org/en-US/docs/Web/API/WebTransport_API
10
+ */
11
+ class WebTransport extends transport_1.Transport {
12
+ constructor(session, stream, reader) {
13
+ super({ _query: { EIO: "4" } });
14
+ this.session = session;
15
+ const transformStream = (0, engine_io_parser_1.createPacketEncoderStream)();
16
+ transformStream.readable.pipeTo(stream.writable).catch(() => {
17
+ debug("the stream was closed");
18
+ });
19
+ this.writer = transformStream.writable.getWriter();
20
+ (async () => {
21
+ try {
22
+ while (true) {
23
+ const { value, done } = await reader.read();
24
+ if (done) {
25
+ debug("session is closed");
26
+ break;
27
+ }
28
+ debug("received chunk: %o", value);
29
+ this.onPacket(value);
30
+ }
31
+ }
32
+ catch (e) {
33
+ debug("error while reading: %s", e.message);
34
+ }
35
+ })();
36
+ session.closed.then(() => this.onClose());
37
+ this.writable = true;
38
+ }
39
+ get name() {
40
+ return "webtransport";
41
+ }
42
+ async send(packets) {
43
+ this.writable = false;
44
+ try {
45
+ for (let i = 0; i < packets.length; i++) {
46
+ const packet = packets[i];
47
+ await this.writer.write(packet);
48
+ }
49
+ }
50
+ catch (e) {
51
+ debug("error while writing: %s", e.message);
52
+ }
53
+ this.emit("drain");
54
+ this.writable = true;
55
+ this.emit("ready");
56
+ }
57
+ doClose(fn) {
58
+ debug("closing WebTransport session");
59
+ this.session.close();
60
+ fn && fn();
61
+ }
62
+ }
63
+ exports.WebTransport = WebTransport;
@@ -0,0 +1,7 @@
1
+ import { Polling } from "./polling";
2
+ import { WebSocket } from "./websocket";
3
+ declare const _default: {
4
+ polling: typeof Polling;
5
+ websocket: typeof WebSocket;
6
+ };
7
+ export default _default;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const polling_1 = require("./polling");
4
+ const websocket_1 = require("./websocket");
5
+ exports.default = {
6
+ polling: polling_1.Polling,
7
+ websocket: websocket_1.WebSocket,
8
+ };
@@ -0,0 +1,99 @@
1
+ import { Transport } from "../transport";
2
+ export declare class Polling extends Transport {
3
+ maxHttpBufferSize: number;
4
+ httpCompression: any;
5
+ private req;
6
+ private res;
7
+ private dataReq;
8
+ private dataRes;
9
+ private shouldClose;
10
+ private readonly closeTimeout;
11
+ /**
12
+ * HTTP polling constructor.
13
+ */
14
+ constructor(req: any);
15
+ /**
16
+ * Transport name
17
+ */
18
+ get name(): string;
19
+ /**
20
+ * Overrides onRequest.
21
+ *
22
+ * @param req
23
+ *
24
+ * @private
25
+ */
26
+ onRequest(req: any): void;
27
+ /**
28
+ * The client sends a request awaiting for us to send data.
29
+ *
30
+ * @private
31
+ */
32
+ onPollRequest(req: any, res: any): void;
33
+ /**
34
+ * The client sends a request with data.
35
+ *
36
+ * @private
37
+ */
38
+ onDataRequest(req: any, res: any): void;
39
+ /**
40
+ * Cleanup request.
41
+ *
42
+ * @private
43
+ */
44
+ private onDataRequestCleanup;
45
+ /**
46
+ * Processes the incoming data payload.
47
+ *
48
+ * @param {String} encoded payload
49
+ * @private
50
+ */
51
+ onData(data: any): void;
52
+ /**
53
+ * Overrides onClose.
54
+ *
55
+ * @private
56
+ */
57
+ onClose(): void;
58
+ /**
59
+ * Writes a packet payload.
60
+ *
61
+ * @param {Object} packet
62
+ * @private
63
+ */
64
+ send(packets: any): void;
65
+ /**
66
+ * Writes data as response to poll request.
67
+ *
68
+ * @param {String} data
69
+ * @param {Object} options
70
+ * @private
71
+ */
72
+ write(data: any, options: any): void;
73
+ /**
74
+ * Performs the write.
75
+ *
76
+ * @private
77
+ */
78
+ doWrite(data: any, options: any, callback: any): void;
79
+ /**
80
+ * Compresses data.
81
+ *
82
+ * @private
83
+ */
84
+ compress(data: any, encoding: any, callback: any): void;
85
+ /**
86
+ * Closes the transport.
87
+ *
88
+ * @private
89
+ */
90
+ doClose(fn: any): void;
91
+ /**
92
+ * Returns headers for a response.
93
+ *
94
+ * @param req - request
95
+ * @param {Object} extra headers
96
+ * @private
97
+ */
98
+ headers(req: any, headers: any): any;
99
+ }