Spaces:
Sleeping
Sleeping
| import require$$0 from "stream"; | |
| import require$$0$1 from "zlib"; | |
| import require$$0$2 from "buffer"; | |
| import require$$3 from "net"; | |
| import require$$4 from "tls"; | |
| import require$$5 from "crypto"; | |
| import require$$0$3 from "events"; | |
| import require$$1$1 from "https"; | |
| import require$$2 from "http"; | |
| import require$$7 from "url"; | |
| function getDefaultExportFromCjs(x) { | |
| return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x; | |
| } | |
| function getAugmentedNamespace(n) { | |
| if (n.__esModule) | |
| return n; | |
| var f = n.default; | |
| if (typeof f == "function") { | |
| var a = function a2() { | |
| if (this instanceof a2) { | |
| var args = [null]; | |
| args.push.apply(args, arguments); | |
| var Ctor = Function.bind.apply(f, args); | |
| return new Ctor(); | |
| } | |
| return f.apply(this, arguments); | |
| }; | |
| a.prototype = f.prototype; | |
| } else | |
| a = {}; | |
| Object.defineProperty(a, "__esModule", { value: true }); | |
| Object.keys(n).forEach(function(k) { | |
| var d = Object.getOwnPropertyDescriptor(n, k); | |
| Object.defineProperty(a, k, d.get ? d : { | |
| enumerable: true, | |
| get: function() { | |
| return n[k]; | |
| } | |
| }); | |
| }); | |
| return a; | |
| } | |
| const { Duplex } = require$$0; | |
| function emitClose$1(stream2) { | |
| stream2.emit("close"); | |
| } | |
| function duplexOnEnd() { | |
| if (!this.destroyed && this._writableState.finished) { | |
| this.destroy(); | |
| } | |
| } | |
| function duplexOnError(err) { | |
| this.removeListener("error", duplexOnError); | |
| this.destroy(); | |
| if (this.listenerCount("error") === 0) { | |
| this.emit("error", err); | |
| } | |
| } | |
| function createWebSocketStream(ws, options) { | |
| let terminateOnDestroy = true; | |
| const duplex = new Duplex({ | |
| ...options, | |
| autoDestroy: false, | |
| emitClose: false, | |
| objectMode: false, | |
| writableObjectMode: false | |
| }); | |
| ws.on("message", function message(msg, isBinary) { | |
| const data = !isBinary && duplex._readableState.objectMode ? msg.toString() : msg; | |
| if (!duplex.push(data)) | |
| ws.pause(); | |
| }); | |
| ws.once("error", function error2(err) { | |
| if (duplex.destroyed) | |
| return; | |
| terminateOnDestroy = false; | |
| duplex.destroy(err); | |
| }); | |
| ws.once("close", function close() { | |
| if (duplex.destroyed) | |
| return; | |
| duplex.push(null); | |
| }); | |
| duplex._destroy = function(err, callback) { | |
| if (ws.readyState === ws.CLOSED) { | |
| callback(err); | |
| process.nextTick(emitClose$1, duplex); | |
| return; | |
| } | |
| let called = false; | |
| ws.once("error", function error2(err2) { | |
| called = true; | |
| callback(err2); | |
| }); | |
| ws.once("close", function close() { | |
| if (!called) | |
| callback(err); | |
| process.nextTick(emitClose$1, duplex); | |
| }); | |
| if (terminateOnDestroy) | |
| ws.terminate(); | |
| }; | |
| duplex._final = function(callback) { | |
| if (ws.readyState === ws.CONNECTING) { | |
| ws.once("open", function open() { | |
| duplex._final(callback); | |
| }); | |
| return; | |
| } | |
| if (ws._socket === null) | |
| return; | |
| if (ws._socket._writableState.finished) { | |
| callback(); | |
| if (duplex._readableState.endEmitted) | |
| duplex.destroy(); | |
| } else { | |
| ws._socket.once("finish", function finish() { | |
| callback(); | |
| }); | |
| ws.close(); | |
| } | |
| }; | |
| duplex._read = function() { | |
| if (ws.isPaused) | |
| ws.resume(); | |
| }; | |
| duplex._write = function(chunk, encoding, callback) { | |
| if (ws.readyState === ws.CONNECTING) { | |
| ws.once("open", function open() { | |
| duplex._write(chunk, encoding, callback); | |
| }); | |
| return; | |
| } | |
| ws.send(chunk, callback); | |
| }; | |
| duplex.on("end", duplexOnEnd); | |
| duplex.on("error", duplexOnError); | |
| return duplex; | |
| } | |
| var stream = createWebSocketStream; | |
| const stream$1 = /* @__PURE__ */ getDefaultExportFromCjs(stream); | |
| var bufferUtil$1 = { exports: {} }; | |
| var constants = { | |
| BINARY_TYPES: ["nodebuffer", "arraybuffer", "fragments"], | |
| EMPTY_BUFFER: Buffer.alloc(0), | |
| GUID: "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", | |
| kForOnEventAttribute: Symbol("kIsForOnEventAttribute"), | |
| kListener: Symbol("kListener"), | |
| kStatusCode: Symbol("status-code"), | |
| kWebSocket: Symbol("websocket"), | |
| NOOP: () => { | |
| } | |
| }; | |
| var unmask$1; | |
| var mask; | |
| const { EMPTY_BUFFER: EMPTY_BUFFER$3 } = constants; | |
| const FastBuffer$2 = Buffer[Symbol.species]; | |
| function concat$1(list, totalLength) { | |
| if (list.length === 0) | |
| return EMPTY_BUFFER$3; | |
| if (list.length === 1) | |
| return list[0]; | |
| const target = Buffer.allocUnsafe(totalLength); | |
| let offset = 0; | |
| for (let i = 0; i < list.length; i++) { | |
| const buf = list[i]; | |
| target.set(buf, offset); | |
| offset += buf.length; | |
| } | |
| if (offset < totalLength) { | |
| return new FastBuffer$2(target.buffer, target.byteOffset, offset); | |
| } | |
| return target; | |
| } | |
| function _mask(source, mask2, output, offset, length) { | |
| for (let i = 0; i < length; i++) { | |
| output[offset + i] = source[i] ^ mask2[i & 3]; | |
| } | |
| } | |
| function _unmask(buffer, mask2) { | |
| for (let i = 0; i < buffer.length; i++) { | |
| buffer[i] ^= mask2[i & 3]; | |
| } | |
| } | |
| function toArrayBuffer$1(buf) { | |
| if (buf.length === buf.buffer.byteLength) { | |
| return buf.buffer; | |
| } | |
| return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.length); | |
| } | |
| function toBuffer$2(data) { | |
| toBuffer$2.readOnly = true; | |
| if (Buffer.isBuffer(data)) | |
| return data; | |
| let buf; | |
| if (data instanceof ArrayBuffer) { | |
| buf = new FastBuffer$2(data); | |
| } else if (ArrayBuffer.isView(data)) { | |
| buf = new FastBuffer$2(data.buffer, data.byteOffset, data.byteLength); | |
| } else { | |
| buf = Buffer.from(data); | |
| toBuffer$2.readOnly = false; | |
| } | |
| return buf; | |
| } | |
| bufferUtil$1.exports = { | |
| concat: concat$1, | |
| mask: _mask, | |
| toArrayBuffer: toArrayBuffer$1, | |
| toBuffer: toBuffer$2, | |
| unmask: _unmask | |
| }; | |
| if (!process.env.WS_NO_BUFFER_UTIL) { | |
| try { | |
| const bufferUtil2 = require("bufferutil"); | |
| mask = bufferUtil$1.exports.mask = function(source, mask2, output, offset, length) { | |
| if (length < 48) | |
| _mask(source, mask2, output, offset, length); | |
| else | |
| bufferUtil2.mask(source, mask2, output, offset, length); | |
| }; | |
| unmask$1 = bufferUtil$1.exports.unmask = function(buffer, mask2) { | |
| if (buffer.length < 32) | |
| _unmask(buffer, mask2); | |
| else | |
| bufferUtil2.unmask(buffer, mask2); | |
| }; | |
| } catch (e) { | |
| } | |
| } | |
| var bufferUtilExports = bufferUtil$1.exports; | |
| const kDone = Symbol("kDone"); | |
| const kRun = Symbol("kRun"); | |
| let Limiter$1 = class Limiter { | |
| /** | |
| * Creates a new `Limiter`. | |
| * | |
| * @param {Number} [concurrency=Infinity] The maximum number of jobs allowed | |
| * to run concurrently | |
| */ | |
| constructor(concurrency) { | |
| this[kDone] = () => { | |
| this.pending--; | |
| this[kRun](); | |
| }; | |
| this.concurrency = concurrency || Infinity; | |
| this.jobs = []; | |
| this.pending = 0; | |
| } | |
| /** | |
| * Adds a job to the queue. | |
| * | |
| * @param {Function} job The job to run | |
| * @public | |
| */ | |
| add(job) { | |
| this.jobs.push(job); | |
| this[kRun](); | |
| } | |
| /** | |
| * Removes a job from the queue and runs it if possible. | |
| * | |
| * @private | |
| */ | |
| [kRun]() { | |
| if (this.pending === this.concurrency) | |
| return; | |
| if (this.jobs.length) { | |
| const job = this.jobs.shift(); | |
| this.pending++; | |
| job(this[kDone]); | |
| } | |
| } | |
| }; | |
| var limiter = Limiter$1; | |
| const zlib = require$$0$1; | |
| const bufferUtil = bufferUtilExports; | |
| const Limiter2 = limiter; | |
| const { kStatusCode: kStatusCode$2 } = constants; | |
| const FastBuffer$1 = Buffer[Symbol.species]; | |
| const TRAILER = Buffer.from([0, 0, 255, 255]); | |
| const kPerMessageDeflate = Symbol("permessage-deflate"); | |
| const kTotalLength = Symbol("total-length"); | |
| const kCallback = Symbol("callback"); | |
| const kBuffers = Symbol("buffers"); | |
| const kError$1 = Symbol("error"); | |
| let zlibLimiter; | |
| let PerMessageDeflate$4 = class PerMessageDeflate { | |
| /** | |
| * Creates a PerMessageDeflate instance. | |
| * | |
| * @param {Object} [options] Configuration options | |
| * @param {(Boolean|Number)} [options.clientMaxWindowBits] Advertise support | |
| * for, or request, a custom client window size | |
| * @param {Boolean} [options.clientNoContextTakeover=false] Advertise/ | |
| * acknowledge disabling of client context takeover | |
| * @param {Number} [options.concurrencyLimit=10] The number of concurrent | |
| * calls to zlib | |
| * @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the | |
| * use of a custom server window size | |
| * @param {Boolean} [options.serverNoContextTakeover=false] Request/accept | |
| * disabling of server context takeover | |
| * @param {Number} [options.threshold=1024] Size (in bytes) below which | |
| * messages should not be compressed if context takeover is disabled | |
| * @param {Object} [options.zlibDeflateOptions] Options to pass to zlib on | |
| * deflate | |
| * @param {Object} [options.zlibInflateOptions] Options to pass to zlib on | |
| * inflate | |
| * @param {Boolean} [isServer=false] Create the instance in either server or | |
| * client mode | |
| * @param {Number} [maxPayload=0] The maximum allowed message length | |
| */ | |
| constructor(options, isServer, maxPayload) { | |
| this._maxPayload = maxPayload | 0; | |
| this._options = options || {}; | |
| this._threshold = this._options.threshold !== void 0 ? this._options.threshold : 1024; | |
| this._isServer = !!isServer; | |
| this._deflate = null; | |
| this._inflate = null; | |
| this.params = null; | |
| if (!zlibLimiter) { | |
| const concurrency = this._options.concurrencyLimit !== void 0 ? this._options.concurrencyLimit : 10; | |
| zlibLimiter = new Limiter2(concurrency); | |
| } | |
| } | |
| /** | |
| * @type {String} | |
| */ | |
| static get extensionName() { | |
| return "permessage-deflate"; | |
| } | |
| /** | |
| * Create an extension negotiation offer. | |
| * | |
| * @return {Object} Extension parameters | |
| * @public | |
| */ | |
| offer() { | |
| const params = {}; | |
| if (this._options.serverNoContextTakeover) { | |
| params.server_no_context_takeover = true; | |
| } | |
| if (this._options.clientNoContextTakeover) { | |
| params.client_no_context_takeover = true; | |
| } | |
| if (this._options.serverMaxWindowBits) { | |
| params.server_max_window_bits = this._options.serverMaxWindowBits; | |
| } | |
| if (this._options.clientMaxWindowBits) { | |
| params.client_max_window_bits = this._options.clientMaxWindowBits; | |
| } else if (this._options.clientMaxWindowBits == null) { | |
| params.client_max_window_bits = true; | |
| } | |
| return params; | |
| } | |
| /** | |
| * Accept an extension negotiation offer/response. | |
| * | |
| * @param {Array} configurations The extension negotiation offers/reponse | |
| * @return {Object} Accepted configuration | |
| * @public | |
| */ | |
| accept(configurations) { | |
| configurations = this.normalizeParams(configurations); | |
| this.params = this._isServer ? this.acceptAsServer(configurations) : this.acceptAsClient(configurations); | |
| return this.params; | |
| } | |
| /** | |
| * Releases all resources used by the extension. | |
| * | |
| * @public | |
| */ | |
| cleanup() { | |
| if (this._inflate) { | |
| this._inflate.close(); | |
| this._inflate = null; | |
| } | |
| if (this._deflate) { | |
| const callback = this._deflate[kCallback]; | |
| this._deflate.close(); | |
| this._deflate = null; | |
| if (callback) { | |
| callback( | |
| new Error( | |
| "The deflate stream was closed while data was being processed" | |
| ) | |
| ); | |
| } | |
| } | |
| } | |
| /** | |
| * Accept an extension negotiation offer. | |
| * | |
| * @param {Array} offers The extension negotiation offers | |
| * @return {Object} Accepted configuration | |
| * @private | |
| */ | |
| acceptAsServer(offers) { | |
| const opts = this._options; | |
| const accepted = offers.find((params) => { | |
| if (opts.serverNoContextTakeover === false && params.server_no_context_takeover || params.server_max_window_bits && (opts.serverMaxWindowBits === false || typeof opts.serverMaxWindowBits === "number" && opts.serverMaxWindowBits > params.server_max_window_bits) || typeof opts.clientMaxWindowBits === "number" && !params.client_max_window_bits) { | |
| return false; | |
| } | |
| return true; | |
| }); | |
| if (!accepted) { | |
| throw new Error("None of the extension offers can be accepted"); | |
| } | |
| if (opts.serverNoContextTakeover) { | |
| accepted.server_no_context_takeover = true; | |
| } | |
| if (opts.clientNoContextTakeover) { | |
| accepted.client_no_context_takeover = true; | |
| } | |
| if (typeof opts.serverMaxWindowBits === "number") { | |
| accepted.server_max_window_bits = opts.serverMaxWindowBits; | |
| } | |
| if (typeof opts.clientMaxWindowBits === "number") { | |
| accepted.client_max_window_bits = opts.clientMaxWindowBits; | |
| } else if (accepted.client_max_window_bits === true || opts.clientMaxWindowBits === false) { | |
| delete accepted.client_max_window_bits; | |
| } | |
| return accepted; | |
| } | |
| /** | |
| * Accept the extension negotiation response. | |
| * | |
| * @param {Array} response The extension negotiation response | |
| * @return {Object} Accepted configuration | |
| * @private | |
| */ | |
| acceptAsClient(response) { | |
| const params = response[0]; | |
| if (this._options.clientNoContextTakeover === false && params.client_no_context_takeover) { | |
| throw new Error('Unexpected parameter "client_no_context_takeover"'); | |
| } | |
| if (!params.client_max_window_bits) { | |
| if (typeof this._options.clientMaxWindowBits === "number") { | |
| params.client_max_window_bits = this._options.clientMaxWindowBits; | |
| } | |
| } else if (this._options.clientMaxWindowBits === false || typeof this._options.clientMaxWindowBits === "number" && params.client_max_window_bits > this._options.clientMaxWindowBits) { | |
| throw new Error( | |
| 'Unexpected or invalid parameter "client_max_window_bits"' | |
| ); | |
| } | |
| return params; | |
| } | |
| /** | |
| * Normalize parameters. | |
| * | |
| * @param {Array} configurations The extension negotiation offers/reponse | |
| * @return {Array} The offers/response with normalized parameters | |
| * @private | |
| */ | |
| normalizeParams(configurations) { | |
| configurations.forEach((params) => { | |
| Object.keys(params).forEach((key) => { | |
| let value = params[key]; | |
| if (value.length > 1) { | |
| throw new Error(`Parameter "${key}" must have only a single value`); | |
| } | |
| value = value[0]; | |
| if (key === "client_max_window_bits") { | |
| if (value !== true) { | |
| const num = +value; | |
| if (!Number.isInteger(num) || num < 8 || num > 15) { | |
| throw new TypeError( | |
| `Invalid value for parameter "${key}": ${value}` | |
| ); | |
| } | |
| value = num; | |
| } else if (!this._isServer) { | |
| throw new TypeError( | |
| `Invalid value for parameter "${key}": ${value}` | |
| ); | |
| } | |
| } else if (key === "server_max_window_bits") { | |
| const num = +value; | |
| if (!Number.isInteger(num) || num < 8 || num > 15) { | |
| throw new TypeError( | |
| `Invalid value for parameter "${key}": ${value}` | |
| ); | |
| } | |
| value = num; | |
| } else if (key === "client_no_context_takeover" || key === "server_no_context_takeover") { | |
| if (value !== true) { | |
| throw new TypeError( | |
| `Invalid value for parameter "${key}": ${value}` | |
| ); | |
| } | |
| } else { | |
| throw new Error(`Unknown parameter "${key}"`); | |
| } | |
| params[key] = value; | |
| }); | |
| }); | |
| return configurations; | |
| } | |
| /** | |
| * Decompress data. Concurrency limited. | |
| * | |
| * @param {Buffer} data Compressed data | |
| * @param {Boolean} fin Specifies whether or not this is the last fragment | |
| * @param {Function} callback Callback | |
| * @public | |
| */ | |
| decompress(data, fin, callback) { | |
| zlibLimiter.add((done) => { | |
| this._decompress(data, fin, (err, result) => { | |
| done(); | |
| callback(err, result); | |
| }); | |
| }); | |
| } | |
| /** | |
| * Compress data. Concurrency limited. | |
| * | |
| * @param {(Buffer|String)} data Data to compress | |
| * @param {Boolean} fin Specifies whether or not this is the last fragment | |
| * @param {Function} callback Callback | |
| * @public | |
| */ | |
| compress(data, fin, callback) { | |
| zlibLimiter.add((done) => { | |
| this._compress(data, fin, (err, result) => { | |
| done(); | |
| callback(err, result); | |
| }); | |
| }); | |
| } | |
| /** | |
| * Decompress data. | |
| * | |
| * @param {Buffer} data Compressed data | |
| * @param {Boolean} fin Specifies whether or not this is the last fragment | |
| * @param {Function} callback Callback | |
| * @private | |
| */ | |
| _decompress(data, fin, callback) { | |
| const endpoint = this._isServer ? "client" : "server"; | |
| if (!this._inflate) { | |
| const key = `${endpoint}_max_window_bits`; | |
| const windowBits = typeof this.params[key] !== "number" ? zlib.Z_DEFAULT_WINDOWBITS : this.params[key]; | |
| this._inflate = zlib.createInflateRaw({ | |
| ...this._options.zlibInflateOptions, | |
| windowBits | |
| }); | |
| this._inflate[kPerMessageDeflate] = this; | |
| this._inflate[kTotalLength] = 0; | |
| this._inflate[kBuffers] = []; | |
| this._inflate.on("error", inflateOnError); | |
| this._inflate.on("data", inflateOnData); | |
| } | |
| this._inflate[kCallback] = callback; | |
| this._inflate.write(data); | |
| if (fin) | |
| this._inflate.write(TRAILER); | |
| this._inflate.flush(() => { | |
| const err = this._inflate[kError$1]; | |
| if (err) { | |
| this._inflate.close(); | |
| this._inflate = null; | |
| callback(err); | |
| return; | |
| } | |
| const data2 = bufferUtil.concat( | |
| this._inflate[kBuffers], | |
| this._inflate[kTotalLength] | |
| ); | |
| if (this._inflate._readableState.endEmitted) { | |
| this._inflate.close(); | |
| this._inflate = null; | |
| } else { | |
| this._inflate[kTotalLength] = 0; | |
| this._inflate[kBuffers] = []; | |
| if (fin && this.params[`${endpoint}_no_context_takeover`]) { | |
| this._inflate.reset(); | |
| } | |
| } | |
| callback(null, data2); | |
| }); | |
| } | |
| /** | |
| * Compress data. | |
| * | |
| * @param {(Buffer|String)} data Data to compress | |
| * @param {Boolean} fin Specifies whether or not this is the last fragment | |
| * @param {Function} callback Callback | |
| * @private | |
| */ | |
| _compress(data, fin, callback) { | |
| const endpoint = this._isServer ? "server" : "client"; | |
| if (!this._deflate) { | |
| const key = `${endpoint}_max_window_bits`; | |
| const windowBits = typeof this.params[key] !== "number" ? zlib.Z_DEFAULT_WINDOWBITS : this.params[key]; | |
| this._deflate = zlib.createDeflateRaw({ | |
| ...this._options.zlibDeflateOptions, | |
| windowBits | |
| }); | |
| this._deflate[kTotalLength] = 0; | |
| this._deflate[kBuffers] = []; | |
| this._deflate.on("data", deflateOnData); | |
| } | |
| this._deflate[kCallback] = callback; | |
| this._deflate.write(data); | |
| this._deflate.flush(zlib.Z_SYNC_FLUSH, () => { | |
| if (!this._deflate) { | |
| return; | |
| } | |
| let data2 = bufferUtil.concat( | |
| this._deflate[kBuffers], | |
| this._deflate[kTotalLength] | |
| ); | |
| if (fin) { | |
| data2 = new FastBuffer$1(data2.buffer, data2.byteOffset, data2.length - 4); | |
| } | |
| this._deflate[kCallback] = null; | |
| this._deflate[kTotalLength] = 0; | |
| this._deflate[kBuffers] = []; | |
| if (fin && this.params[`${endpoint}_no_context_takeover`]) { | |
| this._deflate.reset(); | |
| } | |
| callback(null, data2); | |
| }); | |
| } | |
| }; | |
| var permessageDeflate = PerMessageDeflate$4; | |
| function deflateOnData(chunk) { | |
| this[kBuffers].push(chunk); | |
| this[kTotalLength] += chunk.length; | |
| } | |
| function inflateOnData(chunk) { | |
| this[kTotalLength] += chunk.length; | |
| if (this[kPerMessageDeflate]._maxPayload < 1 || this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload) { | |
| this[kBuffers].push(chunk); | |
| return; | |
| } | |
| this[kError$1] = new RangeError("Max payload size exceeded"); | |
| this[kError$1].code = "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH"; | |
| this[kError$1][kStatusCode$2] = 1009; | |
| this.removeListener("data", inflateOnData); | |
| this.reset(); | |
| } | |
| function inflateOnError(err) { | |
| this[kPerMessageDeflate]._inflate = null; | |
| err[kStatusCode$2] = 1007; | |
| this[kCallback](err); | |
| } | |
| var validation = { exports: {} }; | |
| const __viteOptionalPeerDep_utf8Validate_ws = {}; | |
| const __viteOptionalPeerDep_utf8Validate_ws$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ | |
| __proto__: null, | |
| default: __viteOptionalPeerDep_utf8Validate_ws | |
| }, Symbol.toStringTag, { value: "Module" })); | |
| const require$$1 = /* @__PURE__ */ getAugmentedNamespace(__viteOptionalPeerDep_utf8Validate_ws$1); | |
| var isValidUTF8_1; | |
| const { isUtf8 } = require$$0$2; | |
| const tokenChars$2 = [ | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| // 0 - 15 | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| // 16 - 31 | |
| 0, | |
| 1, | |
| 0, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 0, | |
| 0, | |
| 1, | |
| 1, | |
| 0, | |
| 1, | |
| 1, | |
| 0, | |
| // 32 - 47 | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| // 48 - 63 | |
| 0, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| // 64 - 79 | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 0, | |
| 0, | |
| 0, | |
| 1, | |
| 1, | |
| // 80 - 95 | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| // 96 - 111 | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 1, | |
| 0, | |
| 1, | |
| 0, | |
| 1, | |
| 0 | |
| // 112 - 127 | |
| ]; | |
| function isValidStatusCode$2(code) { | |
| return code >= 1e3 && code <= 1014 && code !== 1004 && code !== 1005 && code !== 1006 || code >= 3e3 && code <= 4999; | |
| } | |
| function _isValidUTF8(buf) { | |
| const len = buf.length; | |
| let i = 0; | |
| while (i < len) { | |
| if ((buf[i] & 128) === 0) { | |
| i++; | |
| } else if ((buf[i] & 224) === 192) { | |
| if (i + 1 === len || (buf[i + 1] & 192) !== 128 || (buf[i] & 254) === 192) { | |
| return false; | |
| } | |
| i += 2; | |
| } else if ((buf[i] & 240) === 224) { | |
| if (i + 2 >= len || (buf[i + 1] & 192) !== 128 || (buf[i + 2] & 192) !== 128 || buf[i] === 224 && (buf[i + 1] & 224) === 128 || // Overlong | |
| buf[i] === 237 && (buf[i + 1] & 224) === 160) { | |
| return false; | |
| } | |
| i += 3; | |
| } else if ((buf[i] & 248) === 240) { | |
| if (i + 3 >= len || (buf[i + 1] & 192) !== 128 || (buf[i + 2] & 192) !== 128 || (buf[i + 3] & 192) !== 128 || buf[i] === 240 && (buf[i + 1] & 240) === 128 || // Overlong | |
| buf[i] === 244 && buf[i + 1] > 143 || buf[i] > 244) { | |
| return false; | |
| } | |
| i += 4; | |
| } else { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| validation.exports = { | |
| isValidStatusCode: isValidStatusCode$2, | |
| isValidUTF8: _isValidUTF8, | |
| tokenChars: tokenChars$2 | |
| }; | |
| if (isUtf8) { | |
| isValidUTF8_1 = validation.exports.isValidUTF8 = function(buf) { | |
| return buf.length < 24 ? _isValidUTF8(buf) : isUtf8(buf); | |
| }; | |
| } else if (!process.env.WS_NO_UTF_8_VALIDATE) { | |
| try { | |
| const isValidUTF82 = require$$1; | |
| isValidUTF8_1 = validation.exports.isValidUTF8 = function(buf) { | |
| return buf.length < 32 ? _isValidUTF8(buf) : isValidUTF82(buf); | |
| }; | |
| } catch (e) { | |
| } | |
| } | |
| var validationExports = validation.exports; | |
| const { Writable } = require$$0; | |
| const PerMessageDeflate$3 = permessageDeflate; | |
| const { | |
| BINARY_TYPES: BINARY_TYPES$1, | |
| EMPTY_BUFFER: EMPTY_BUFFER$2, | |
| kStatusCode: kStatusCode$1, | |
| kWebSocket: kWebSocket$2 | |
| } = constants; | |
| const { concat, toArrayBuffer, unmask } = bufferUtilExports; | |
| const { isValidStatusCode: isValidStatusCode$1, isValidUTF8 } = validationExports; | |
| const FastBuffer = Buffer[Symbol.species]; | |
| const GET_INFO = 0; | |
| const GET_PAYLOAD_LENGTH_16 = 1; | |
| const GET_PAYLOAD_LENGTH_64 = 2; | |
| const GET_MASK = 3; | |
| const GET_DATA = 4; | |
| const INFLATING = 5; | |
| let Receiver$1 = class Receiver extends Writable { | |
| /** | |
| * Creates a Receiver instance. | |
| * | |
| * @param {Object} [options] Options object | |
| * @param {String} [options.binaryType=nodebuffer] The type for binary data | |
| * @param {Object} [options.extensions] An object containing the negotiated | |
| * extensions | |
| * @param {Boolean} [options.isServer=false] Specifies whether to operate in | |
| * client or server mode | |
| * @param {Number} [options.maxPayload=0] The maximum allowed message length | |
| * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or | |
| * not to skip UTF-8 validation for text and close messages | |
| */ | |
| constructor(options = {}) { | |
| super(); | |
| this._binaryType = options.binaryType || BINARY_TYPES$1[0]; | |
| this._extensions = options.extensions || {}; | |
| this._isServer = !!options.isServer; | |
| this._maxPayload = options.maxPayload | 0; | |
| this._skipUTF8Validation = !!options.skipUTF8Validation; | |
| this[kWebSocket$2] = void 0; | |
| this._bufferedBytes = 0; | |
| this._buffers = []; | |
| this._compressed = false; | |
| this._payloadLength = 0; | |
| this._mask = void 0; | |
| this._fragmented = 0; | |
| this._masked = false; | |
| this._fin = false; | |
| this._opcode = 0; | |
| this._totalPayloadLength = 0; | |
| this._messageLength = 0; | |
| this._fragments = []; | |
| this._state = GET_INFO; | |
| this._loop = false; | |
| } | |
| /** | |
| * Implements `Writable.prototype._write()`. | |
| * | |
| * @param {Buffer} chunk The chunk of data to write | |
| * @param {String} encoding The character encoding of `chunk` | |
| * @param {Function} cb Callback | |
| * @private | |
| */ | |
| _write(chunk, encoding, cb) { | |
| if (this._opcode === 8 && this._state == GET_INFO) | |
| return cb(); | |
| this._bufferedBytes += chunk.length; | |
| this._buffers.push(chunk); | |
| this.startLoop(cb); | |
| } | |
| /** | |
| * Consumes `n` bytes from the buffered data. | |
| * | |
| * @param {Number} n The number of bytes to consume | |
| * @return {Buffer} The consumed bytes | |
| * @private | |
| */ | |
| consume(n) { | |
| this._bufferedBytes -= n; | |
| if (n === this._buffers[0].length) | |
| return this._buffers.shift(); | |
| if (n < this._buffers[0].length) { | |
| const buf = this._buffers[0]; | |
| this._buffers[0] = new FastBuffer( | |
| buf.buffer, | |
| buf.byteOffset + n, | |
| buf.length - n | |
| ); | |
| return new FastBuffer(buf.buffer, buf.byteOffset, n); | |
| } | |
| const dst = Buffer.allocUnsafe(n); | |
| do { | |
| const buf = this._buffers[0]; | |
| const offset = dst.length - n; | |
| if (n >= buf.length) { | |
| dst.set(this._buffers.shift(), offset); | |
| } else { | |
| dst.set(new Uint8Array(buf.buffer, buf.byteOffset, n), offset); | |
| this._buffers[0] = new FastBuffer( | |
| buf.buffer, | |
| buf.byteOffset + n, | |
| buf.length - n | |
| ); | |
| } | |
| n -= buf.length; | |
| } while (n > 0); | |
| return dst; | |
| } | |
| /** | |
| * Starts the parsing loop. | |
| * | |
| * @param {Function} cb Callback | |
| * @private | |
| */ | |
| startLoop(cb) { | |
| let err; | |
| this._loop = true; | |
| do { | |
| switch (this._state) { | |
| case GET_INFO: | |
| err = this.getInfo(); | |
| break; | |
| case GET_PAYLOAD_LENGTH_16: | |
| err = this.getPayloadLength16(); | |
| break; | |
| case GET_PAYLOAD_LENGTH_64: | |
| err = this.getPayloadLength64(); | |
| break; | |
| case GET_MASK: | |
| this.getMask(); | |
| break; | |
| case GET_DATA: | |
| err = this.getData(cb); | |
| break; | |
| default: | |
| this._loop = false; | |
| return; | |
| } | |
| } while (this._loop); | |
| cb(err); | |
| } | |
| /** | |
| * Reads the first two bytes of a frame. | |
| * | |
| * @return {(RangeError|undefined)} A possible error | |
| * @private | |
| */ | |
| getInfo() { | |
| if (this._bufferedBytes < 2) { | |
| this._loop = false; | |
| return; | |
| } | |
| const buf = this.consume(2); | |
| if ((buf[0] & 48) !== 0) { | |
| this._loop = false; | |
| return error( | |
| RangeError, | |
| "RSV2 and RSV3 must be clear", | |
| true, | |
| 1002, | |
| "WS_ERR_UNEXPECTED_RSV_2_3" | |
| ); | |
| } | |
| const compressed = (buf[0] & 64) === 64; | |
| if (compressed && !this._extensions[PerMessageDeflate$3.extensionName]) { | |
| this._loop = false; | |
| return error( | |
| RangeError, | |
| "RSV1 must be clear", | |
| true, | |
| 1002, | |
| "WS_ERR_UNEXPECTED_RSV_1" | |
| ); | |
| } | |
| this._fin = (buf[0] & 128) === 128; | |
| this._opcode = buf[0] & 15; | |
| this._payloadLength = buf[1] & 127; | |
| if (this._opcode === 0) { | |
| if (compressed) { | |
| this._loop = false; | |
| return error( | |
| RangeError, | |
| "RSV1 must be clear", | |
| true, | |
| 1002, | |
| "WS_ERR_UNEXPECTED_RSV_1" | |
| ); | |
| } | |
| if (!this._fragmented) { | |
| this._loop = false; | |
| return error( | |
| RangeError, | |
| "invalid opcode 0", | |
| true, | |
| 1002, | |
| "WS_ERR_INVALID_OPCODE" | |
| ); | |
| } | |
| this._opcode = this._fragmented; | |
| } else if (this._opcode === 1 || this._opcode === 2) { | |
| if (this._fragmented) { | |
| this._loop = false; | |
| return error( | |
| RangeError, | |
| `invalid opcode ${this._opcode}`, | |
| true, | |
| 1002, | |
| "WS_ERR_INVALID_OPCODE" | |
| ); | |
| } | |
| this._compressed = compressed; | |
| } else if (this._opcode > 7 && this._opcode < 11) { | |
| if (!this._fin) { | |
| this._loop = false; | |
| return error( | |
| RangeError, | |
| "FIN must be set", | |
| true, | |
| 1002, | |
| "WS_ERR_EXPECTED_FIN" | |
| ); | |
| } | |
| if (compressed) { | |
| this._loop = false; | |
| return error( | |
| RangeError, | |
| "RSV1 must be clear", | |
| true, | |
| 1002, | |
| "WS_ERR_UNEXPECTED_RSV_1" | |
| ); | |
| } | |
| if (this._payloadLength > 125 || this._opcode === 8 && this._payloadLength === 1) { | |
| this._loop = false; | |
| return error( | |
| RangeError, | |
| `invalid payload length ${this._payloadLength}`, | |
| true, | |
| 1002, | |
| "WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH" | |
| ); | |
| } | |
| } else { | |
| this._loop = false; | |
| return error( | |
| RangeError, | |
| `invalid opcode ${this._opcode}`, | |
| true, | |
| 1002, | |
| "WS_ERR_INVALID_OPCODE" | |
| ); | |
| } | |
| if (!this._fin && !this._fragmented) | |
| this._fragmented = this._opcode; | |
| this._masked = (buf[1] & 128) === 128; | |
| if (this._isServer) { | |
| if (!this._masked) { | |
| this._loop = false; | |
| return error( | |
| RangeError, | |
| "MASK must be set", | |
| true, | |
| 1002, | |
| "WS_ERR_EXPECTED_MASK" | |
| ); | |
| } | |
| } else if (this._masked) { | |
| this._loop = false; | |
| return error( | |
| RangeError, | |
| "MASK must be clear", | |
| true, | |
| 1002, | |
| "WS_ERR_UNEXPECTED_MASK" | |
| ); | |
| } | |
| if (this._payloadLength === 126) | |
| this._state = GET_PAYLOAD_LENGTH_16; | |
| else if (this._payloadLength === 127) | |
| this._state = GET_PAYLOAD_LENGTH_64; | |
| else | |
| return this.haveLength(); | |
| } | |
| /** | |
| * Gets extended payload length (7+16). | |
| * | |
| * @return {(RangeError|undefined)} A possible error | |
| * @private | |
| */ | |
| getPayloadLength16() { | |
| if (this._bufferedBytes < 2) { | |
| this._loop = false; | |
| return; | |
| } | |
| this._payloadLength = this.consume(2).readUInt16BE(0); | |
| return this.haveLength(); | |
| } | |
| /** | |
| * Gets extended payload length (7+64). | |
| * | |
| * @return {(RangeError|undefined)} A possible error | |
| * @private | |
| */ | |
| getPayloadLength64() { | |
| if (this._bufferedBytes < 8) { | |
| this._loop = false; | |
| return; | |
| } | |
| const buf = this.consume(8); | |
| const num = buf.readUInt32BE(0); | |
| if (num > Math.pow(2, 53 - 32) - 1) { | |
| this._loop = false; | |
| return error( | |
| RangeError, | |
| "Unsupported WebSocket frame: payload length > 2^53 - 1", | |
| false, | |
| 1009, | |
| "WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH" | |
| ); | |
| } | |
| this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4); | |
| return this.haveLength(); | |
| } | |
| /** | |
| * Payload length has been read. | |
| * | |
| * @return {(RangeError|undefined)} A possible error | |
| * @private | |
| */ | |
| haveLength() { | |
| if (this._payloadLength && this._opcode < 8) { | |
| this._totalPayloadLength += this._payloadLength; | |
| if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) { | |
| this._loop = false; | |
| return error( | |
| RangeError, | |
| "Max payload size exceeded", | |
| false, | |
| 1009, | |
| "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH" | |
| ); | |
| } | |
| } | |
| if (this._masked) | |
| this._state = GET_MASK; | |
| else | |
| this._state = GET_DATA; | |
| } | |
| /** | |
| * Reads mask bytes. | |
| * | |
| * @private | |
| */ | |
| getMask() { | |
| if (this._bufferedBytes < 4) { | |
| this._loop = false; | |
| return; | |
| } | |
| this._mask = this.consume(4); | |
| this._state = GET_DATA; | |
| } | |
| /** | |
| * Reads data bytes. | |
| * | |
| * @param {Function} cb Callback | |
| * @return {(Error|RangeError|undefined)} A possible error | |
| * @private | |
| */ | |
| getData(cb) { | |
| let data = EMPTY_BUFFER$2; | |
| if (this._payloadLength) { | |
| if (this._bufferedBytes < this._payloadLength) { | |
| this._loop = false; | |
| return; | |
| } | |
| data = this.consume(this._payloadLength); | |
| if (this._masked && (this._mask[0] | this._mask[1] | this._mask[2] | this._mask[3]) !== 0) { | |
| unmask(data, this._mask); | |
| } | |
| } | |
| if (this._opcode > 7) | |
| return this.controlMessage(data); | |
| if (this._compressed) { | |
| this._state = INFLATING; | |
| this.decompress(data, cb); | |
| return; | |
| } | |
| if (data.length) { | |
| this._messageLength = this._totalPayloadLength; | |
| this._fragments.push(data); | |
| } | |
| return this.dataMessage(); | |
| } | |
| /** | |
| * Decompresses data. | |
| * | |
| * @param {Buffer} data Compressed data | |
| * @param {Function} cb Callback | |
| * @private | |
| */ | |
| decompress(data, cb) { | |
| const perMessageDeflate = this._extensions[PerMessageDeflate$3.extensionName]; | |
| perMessageDeflate.decompress(data, this._fin, (err, buf) => { | |
| if (err) | |
| return cb(err); | |
| if (buf.length) { | |
| this._messageLength += buf.length; | |
| if (this._messageLength > this._maxPayload && this._maxPayload > 0) { | |
| return cb( | |
| error( | |
| RangeError, | |
| "Max payload size exceeded", | |
| false, | |
| 1009, | |
| "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH" | |
| ) | |
| ); | |
| } | |
| this._fragments.push(buf); | |
| } | |
| const er = this.dataMessage(); | |
| if (er) | |
| return cb(er); | |
| this.startLoop(cb); | |
| }); | |
| } | |
| /** | |
| * Handles a data message. | |
| * | |
| * @return {(Error|undefined)} A possible error | |
| * @private | |
| */ | |
| dataMessage() { | |
| if (this._fin) { | |
| const messageLength = this._messageLength; | |
| const fragments = this._fragments; | |
| this._totalPayloadLength = 0; | |
| this._messageLength = 0; | |
| this._fragmented = 0; | |
| this._fragments = []; | |
| if (this._opcode === 2) { | |
| let data; | |
| if (this._binaryType === "nodebuffer") { | |
| data = concat(fragments, messageLength); | |
| } else if (this._binaryType === "arraybuffer") { | |
| data = toArrayBuffer(concat(fragments, messageLength)); | |
| } else { | |
| data = fragments; | |
| } | |
| this.emit("message", data, true); | |
| } else { | |
| const buf = concat(fragments, messageLength); | |
| if (!this._skipUTF8Validation && !isValidUTF8(buf)) { | |
| this._loop = false; | |
| return error( | |
| Error, | |
| "invalid UTF-8 sequence", | |
| true, | |
| 1007, | |
| "WS_ERR_INVALID_UTF8" | |
| ); | |
| } | |
| this.emit("message", buf, false); | |
| } | |
| } | |
| this._state = GET_INFO; | |
| } | |
| /** | |
| * Handles a control message. | |
| * | |
| * @param {Buffer} data Data to handle | |
| * @return {(Error|RangeError|undefined)} A possible error | |
| * @private | |
| */ | |
| controlMessage(data) { | |
| if (this._opcode === 8) { | |
| this._loop = false; | |
| if (data.length === 0) { | |
| this.emit("conclude", 1005, EMPTY_BUFFER$2); | |
| this.end(); | |
| } else { | |
| const code = data.readUInt16BE(0); | |
| if (!isValidStatusCode$1(code)) { | |
| return error( | |
| RangeError, | |
| `invalid status code ${code}`, | |
| true, | |
| 1002, | |
| "WS_ERR_INVALID_CLOSE_CODE" | |
| ); | |
| } | |
| const buf = new FastBuffer( | |
| data.buffer, | |
| data.byteOffset + 2, | |
| data.length - 2 | |
| ); | |
| if (!this._skipUTF8Validation && !isValidUTF8(buf)) { | |
| return error( | |
| Error, | |
| "invalid UTF-8 sequence", | |
| true, | |
| 1007, | |
| "WS_ERR_INVALID_UTF8" | |
| ); | |
| } | |
| this.emit("conclude", code, buf); | |
| this.end(); | |
| } | |
| } else if (this._opcode === 9) { | |
| this.emit("ping", data); | |
| } else { | |
| this.emit("pong", data); | |
| } | |
| this._state = GET_INFO; | |
| } | |
| }; | |
| var receiver = Receiver$1; | |
| function error(ErrorCtor, message, prefix, statusCode, errorCode) { | |
| const err = new ErrorCtor( | |
| prefix ? `Invalid WebSocket frame: ${message}` : message | |
| ); | |
| Error.captureStackTrace(err, error); | |
| err.code = errorCode; | |
| err[kStatusCode$1] = statusCode; | |
| return err; | |
| } | |
| const receiver$1 = /* @__PURE__ */ getDefaultExportFromCjs(receiver); | |
| const { randomFillSync } = require$$5; | |
| const PerMessageDeflate$2 = permessageDeflate; | |
| const { EMPTY_BUFFER: EMPTY_BUFFER$1 } = constants; | |
| const { isValidStatusCode } = validationExports; | |
| const { mask: applyMask, toBuffer: toBuffer$1 } = bufferUtilExports; | |
| const kByteLength = Symbol("kByteLength"); | |
| const maskBuffer = Buffer.alloc(4); | |
| let Sender$1 = class Sender { | |
| /** | |
| * Creates a Sender instance. | |
| * | |
| * @param {(net.Socket|tls.Socket)} socket The connection socket | |
| * @param {Object} [extensions] An object containing the negotiated extensions | |
| * @param {Function} [generateMask] The function used to generate the masking | |
| * key | |
| */ | |
| constructor(socket, extensions, generateMask) { | |
| this._extensions = extensions || {}; | |
| if (generateMask) { | |
| this._generateMask = generateMask; | |
| this._maskBuffer = Buffer.alloc(4); | |
| } | |
| this._socket = socket; | |
| this._firstFragment = true; | |
| this._compress = false; | |
| this._bufferedBytes = 0; | |
| this._deflating = false; | |
| this._queue = []; | |
| } | |
| /** | |
| * Frames a piece of data according to the HyBi WebSocket protocol. | |
| * | |
| * @param {(Buffer|String)} data The data to frame | |
| * @param {Object} options Options object | |
| * @param {Boolean} [options.fin=false] Specifies whether or not to set the | |
| * FIN bit | |
| * @param {Function} [options.generateMask] The function used to generate the | |
| * masking key | |
| * @param {Boolean} [options.mask=false] Specifies whether or not to mask | |
| * `data` | |
| * @param {Buffer} [options.maskBuffer] The buffer used to store the masking | |
| * key | |
| * @param {Number} options.opcode The opcode | |
| * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be | |
| * modified | |
| * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the | |
| * RSV1 bit | |
| * @return {(Buffer|String)[]} The framed data | |
| * @public | |
| */ | |
| static frame(data, options) { | |
| let mask2; | |
| let merge = false; | |
| let offset = 2; | |
| let skipMasking = false; | |
| if (options.mask) { | |
| mask2 = options.maskBuffer || maskBuffer; | |
| if (options.generateMask) { | |
| options.generateMask(mask2); | |
| } else { | |
| randomFillSync(mask2, 0, 4); | |
| } | |
| skipMasking = (mask2[0] | mask2[1] | mask2[2] | mask2[3]) === 0; | |
| offset = 6; | |
| } | |
| let dataLength; | |
| if (typeof data === "string") { | |
| if ((!options.mask || skipMasking) && options[kByteLength] !== void 0) { | |
| dataLength = options[kByteLength]; | |
| } else { | |
| data = Buffer.from(data); | |
| dataLength = data.length; | |
| } | |
| } else { | |
| dataLength = data.length; | |
| merge = options.mask && options.readOnly && !skipMasking; | |
| } | |
| let payloadLength = dataLength; | |
| if (dataLength >= 65536) { | |
| offset += 8; | |
| payloadLength = 127; | |
| } else if (dataLength > 125) { | |
| offset += 2; | |
| payloadLength = 126; | |
| } | |
| const target = Buffer.allocUnsafe(merge ? dataLength + offset : offset); | |
| target[0] = options.fin ? options.opcode | 128 : options.opcode; | |
| if (options.rsv1) | |
| target[0] |= 64; | |
| target[1] = payloadLength; | |
| if (payloadLength === 126) { | |
| target.writeUInt16BE(dataLength, 2); | |
| } else if (payloadLength === 127) { | |
| target[2] = target[3] = 0; | |
| target.writeUIntBE(dataLength, 4, 6); | |
| } | |
| if (!options.mask) | |
| return [target, data]; | |
| target[1] |= 128; | |
| target[offset - 4] = mask2[0]; | |
| target[offset - 3] = mask2[1]; | |
| target[offset - 2] = mask2[2]; | |
| target[offset - 1] = mask2[3]; | |
| if (skipMasking) | |
| return [target, data]; | |
| if (merge) { | |
| applyMask(data, mask2, target, offset, dataLength); | |
| return [target]; | |
| } | |
| applyMask(data, mask2, data, 0, dataLength); | |
| return [target, data]; | |
| } | |
| /** | |
| * Sends a close message to the other peer. | |
| * | |
| * @param {Number} [code] The status code component of the body | |
| * @param {(String|Buffer)} [data] The message component of the body | |
| * @param {Boolean} [mask=false] Specifies whether or not to mask the message | |
| * @param {Function} [cb] Callback | |
| * @public | |
| */ | |
| close(code, data, mask2, cb) { | |
| let buf; | |
| if (code === void 0) { | |
| buf = EMPTY_BUFFER$1; | |
| } else if (typeof code !== "number" || !isValidStatusCode(code)) { | |
| throw new TypeError("First argument must be a valid error code number"); | |
| } else if (data === void 0 || !data.length) { | |
| buf = Buffer.allocUnsafe(2); | |
| buf.writeUInt16BE(code, 0); | |
| } else { | |
| const length = Buffer.byteLength(data); | |
| if (length > 123) { | |
| throw new RangeError("The message must not be greater than 123 bytes"); | |
| } | |
| buf = Buffer.allocUnsafe(2 + length); | |
| buf.writeUInt16BE(code, 0); | |
| if (typeof data === "string") { | |
| buf.write(data, 2); | |
| } else { | |
| buf.set(data, 2); | |
| } | |
| } | |
| const options = { | |
| [kByteLength]: buf.length, | |
| fin: true, | |
| generateMask: this._generateMask, | |
| mask: mask2, | |
| maskBuffer: this._maskBuffer, | |
| opcode: 8, | |
| readOnly: false, | |
| rsv1: false | |
| }; | |
| if (this._deflating) { | |
| this.enqueue([this.dispatch, buf, false, options, cb]); | |
| } else { | |
| this.sendFrame(Sender.frame(buf, options), cb); | |
| } | |
| } | |
| /** | |
| * Sends a ping message to the other peer. | |
| * | |
| * @param {*} data The message to send | |
| * @param {Boolean} [mask=false] Specifies whether or not to mask `data` | |
| * @param {Function} [cb] Callback | |
| * @public | |
| */ | |
| ping(data, mask2, cb) { | |
| let byteLength; | |
| let readOnly; | |
| if (typeof data === "string") { | |
| byteLength = Buffer.byteLength(data); | |
| readOnly = false; | |
| } else { | |
| data = toBuffer$1(data); | |
| byteLength = data.length; | |
| readOnly = toBuffer$1.readOnly; | |
| } | |
| if (byteLength > 125) { | |
| throw new RangeError("The data size must not be greater than 125 bytes"); | |
| } | |
| const options = { | |
| [kByteLength]: byteLength, | |
| fin: true, | |
| generateMask: this._generateMask, | |
| mask: mask2, | |
| maskBuffer: this._maskBuffer, | |
| opcode: 9, | |
| readOnly, | |
| rsv1: false | |
| }; | |
| if (this._deflating) { | |
| this.enqueue([this.dispatch, data, false, options, cb]); | |
| } else { | |
| this.sendFrame(Sender.frame(data, options), cb); | |
| } | |
| } | |
| /** | |
| * Sends a pong message to the other peer. | |
| * | |
| * @param {*} data The message to send | |
| * @param {Boolean} [mask=false] Specifies whether or not to mask `data` | |
| * @param {Function} [cb] Callback | |
| * @public | |
| */ | |
| pong(data, mask2, cb) { | |
| let byteLength; | |
| let readOnly; | |
| if (typeof data === "string") { | |
| byteLength = Buffer.byteLength(data); | |
| readOnly = false; | |
| } else { | |
| data = toBuffer$1(data); | |
| byteLength = data.length; | |
| readOnly = toBuffer$1.readOnly; | |
| } | |
| if (byteLength > 125) { | |
| throw new RangeError("The data size must not be greater than 125 bytes"); | |
| } | |
| const options = { | |
| [kByteLength]: byteLength, | |
| fin: true, | |
| generateMask: this._generateMask, | |
| mask: mask2, | |
| maskBuffer: this._maskBuffer, | |
| opcode: 10, | |
| readOnly, | |
| rsv1: false | |
| }; | |
| if (this._deflating) { | |
| this.enqueue([this.dispatch, data, false, options, cb]); | |
| } else { | |
| this.sendFrame(Sender.frame(data, options), cb); | |
| } | |
| } | |
| /** | |
| * Sends a data message to the other peer. | |
| * | |
| * @param {*} data The message to send | |
| * @param {Object} options Options object | |
| * @param {Boolean} [options.binary=false] Specifies whether `data` is binary | |
| * or text | |
| * @param {Boolean} [options.compress=false] Specifies whether or not to | |
| * compress `data` | |
| * @param {Boolean} [options.fin=false] Specifies whether the fragment is the | |
| * last one | |
| * @param {Boolean} [options.mask=false] Specifies whether or not to mask | |
| * `data` | |
| * @param {Function} [cb] Callback | |
| * @public | |
| */ | |
| send(data, options, cb) { | |
| const perMessageDeflate = this._extensions[PerMessageDeflate$2.extensionName]; | |
| let opcode = options.binary ? 2 : 1; | |
| let rsv1 = options.compress; | |
| let byteLength; | |
| let readOnly; | |
| if (typeof data === "string") { | |
| byteLength = Buffer.byteLength(data); | |
| readOnly = false; | |
| } else { | |
| data = toBuffer$1(data); | |
| byteLength = data.length; | |
| readOnly = toBuffer$1.readOnly; | |
| } | |
| if (this._firstFragment) { | |
| this._firstFragment = false; | |
| if (rsv1 && perMessageDeflate && perMessageDeflate.params[perMessageDeflate._isServer ? "server_no_context_takeover" : "client_no_context_takeover"]) { | |
| rsv1 = byteLength >= perMessageDeflate._threshold; | |
| } | |
| this._compress = rsv1; | |
| } else { | |
| rsv1 = false; | |
| opcode = 0; | |
| } | |
| if (options.fin) | |
| this._firstFragment = true; | |
| if (perMessageDeflate) { | |
| const opts = { | |
| [kByteLength]: byteLength, | |
| fin: options.fin, | |
| generateMask: this._generateMask, | |
| mask: options.mask, | |
| maskBuffer: this._maskBuffer, | |
| opcode, | |
| readOnly, | |
| rsv1 | |
| }; | |
| if (this._deflating) { | |
| this.enqueue([this.dispatch, data, this._compress, opts, cb]); | |
| } else { | |
| this.dispatch(data, this._compress, opts, cb); | |
| } | |
| } else { | |
| this.sendFrame( | |
| Sender.frame(data, { | |
| [kByteLength]: byteLength, | |
| fin: options.fin, | |
| generateMask: this._generateMask, | |
| mask: options.mask, | |
| maskBuffer: this._maskBuffer, | |
| opcode, | |
| readOnly, | |
| rsv1: false | |
| }), | |
| cb | |
| ); | |
| } | |
| } | |
| /** | |
| * Dispatches a message. | |
| * | |
| * @param {(Buffer|String)} data The message to send | |
| * @param {Boolean} [compress=false] Specifies whether or not to compress | |
| * `data` | |
| * @param {Object} options Options object | |
| * @param {Boolean} [options.fin=false] Specifies whether or not to set the | |
| * FIN bit | |
| * @param {Function} [options.generateMask] The function used to generate the | |
| * masking key | |
| * @param {Boolean} [options.mask=false] Specifies whether or not to mask | |
| * `data` | |
| * @param {Buffer} [options.maskBuffer] The buffer used to store the masking | |
| * key | |
| * @param {Number} options.opcode The opcode | |
| * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be | |
| * modified | |
| * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the | |
| * RSV1 bit | |
| * @param {Function} [cb] Callback | |
| * @private | |
| */ | |
| dispatch(data, compress, options, cb) { | |
| if (!compress) { | |
| this.sendFrame(Sender.frame(data, options), cb); | |
| return; | |
| } | |
| const perMessageDeflate = this._extensions[PerMessageDeflate$2.extensionName]; | |
| this._bufferedBytes += options[kByteLength]; | |
| this._deflating = true; | |
| perMessageDeflate.compress(data, options.fin, (_, buf) => { | |
| if (this._socket.destroyed) { | |
| const err = new Error( | |
| "The socket was closed while data was being compressed" | |
| ); | |
| if (typeof cb === "function") | |
| cb(err); | |
| for (let i = 0; i < this._queue.length; i++) { | |
| const params = this._queue[i]; | |
| const callback = params[params.length - 1]; | |
| if (typeof callback === "function") | |
| callback(err); | |
| } | |
| return; | |
| } | |
| this._bufferedBytes -= options[kByteLength]; | |
| this._deflating = false; | |
| options.readOnly = false; | |
| this.sendFrame(Sender.frame(buf, options), cb); | |
| this.dequeue(); | |
| }); | |
| } | |
| /** | |
| * Executes queued send operations. | |
| * | |
| * @private | |
| */ | |
| dequeue() { | |
| while (!this._deflating && this._queue.length) { | |
| const params = this._queue.shift(); | |
| this._bufferedBytes -= params[3][kByteLength]; | |
| Reflect.apply(params[0], this, params.slice(1)); | |
| } | |
| } | |
| /** | |
| * Enqueues a send operation. | |
| * | |
| * @param {Array} params Send operation parameters. | |
| * @private | |
| */ | |
| enqueue(params) { | |
| this._bufferedBytes += params[3][kByteLength]; | |
| this._queue.push(params); | |
| } | |
| /** | |
| * Sends a frame. | |
| * | |
| * @param {Buffer[]} list The frame to send | |
| * @param {Function} [cb] Callback | |
| * @private | |
| */ | |
| sendFrame(list, cb) { | |
| if (list.length === 2) { | |
| this._socket.cork(); | |
| this._socket.write(list[0]); | |
| this._socket.write(list[1], cb); | |
| this._socket.uncork(); | |
| } else { | |
| this._socket.write(list[0], cb); | |
| } | |
| } | |
| }; | |
| var sender = Sender$1; | |
| const sender$1 = /* @__PURE__ */ getDefaultExportFromCjs(sender); | |
| const { kForOnEventAttribute: kForOnEventAttribute$1, kListener: kListener$1 } = constants; | |
| const kCode = Symbol("kCode"); | |
| const kData = Symbol("kData"); | |
| const kError = Symbol("kError"); | |
| const kMessage = Symbol("kMessage"); | |
| const kReason = Symbol("kReason"); | |
| const kTarget = Symbol("kTarget"); | |
| const kType = Symbol("kType"); | |
| const kWasClean = Symbol("kWasClean"); | |
| class Event { | |
| /** | |
| * Create a new `Event`. | |
| * | |
| * @param {String} type The name of the event | |
| * @throws {TypeError} If the `type` argument is not specified | |
| */ | |
| constructor(type) { | |
| this[kTarget] = null; | |
| this[kType] = type; | |
| } | |
| /** | |
| * @type {*} | |
| */ | |
| get target() { | |
| return this[kTarget]; | |
| } | |
| /** | |
| * @type {String} | |
| */ | |
| get type() { | |
| return this[kType]; | |
| } | |
| } | |
| Object.defineProperty(Event.prototype, "target", { enumerable: true }); | |
| Object.defineProperty(Event.prototype, "type", { enumerable: true }); | |
| class CloseEvent extends Event { | |
| /** | |
| * Create a new `CloseEvent`. | |
| * | |
| * @param {String} type The name of the event | |
| * @param {Object} [options] A dictionary object that allows for setting | |
| * attributes via object members of the same name | |
| * @param {Number} [options.code=0] The status code explaining why the | |
| * connection was closed | |
| * @param {String} [options.reason=''] A human-readable string explaining why | |
| * the connection was closed | |
| * @param {Boolean} [options.wasClean=false] Indicates whether or not the | |
| * connection was cleanly closed | |
| */ | |
| constructor(type, options = {}) { | |
| super(type); | |
| this[kCode] = options.code === void 0 ? 0 : options.code; | |
| this[kReason] = options.reason === void 0 ? "" : options.reason; | |
| this[kWasClean] = options.wasClean === void 0 ? false : options.wasClean; | |
| } | |
| /** | |
| * @type {Number} | |
| */ | |
| get code() { | |
| return this[kCode]; | |
| } | |
| /** | |
| * @type {String} | |
| */ | |
| get reason() { | |
| return this[kReason]; | |
| } | |
| /** | |
| * @type {Boolean} | |
| */ | |
| get wasClean() { | |
| return this[kWasClean]; | |
| } | |
| } | |
| Object.defineProperty(CloseEvent.prototype, "code", { enumerable: true }); | |
| Object.defineProperty(CloseEvent.prototype, "reason", { enumerable: true }); | |
| Object.defineProperty(CloseEvent.prototype, "wasClean", { enumerable: true }); | |
| class ErrorEvent extends Event { | |
| /** | |
| * Create a new `ErrorEvent`. | |
| * | |
| * @param {String} type The name of the event | |
| * @param {Object} [options] A dictionary object that allows for setting | |
| * attributes via object members of the same name | |
| * @param {*} [options.error=null] The error that generated this event | |
| * @param {String} [options.message=''] The error message | |
| */ | |
| constructor(type, options = {}) { | |
| super(type); | |
| this[kError] = options.error === void 0 ? null : options.error; | |
| this[kMessage] = options.message === void 0 ? "" : options.message; | |
| } | |
| /** | |
| * @type {*} | |
| */ | |
| get error() { | |
| return this[kError]; | |
| } | |
| /** | |
| * @type {String} | |
| */ | |
| get message() { | |
| return this[kMessage]; | |
| } | |
| } | |
| Object.defineProperty(ErrorEvent.prototype, "error", { enumerable: true }); | |
| Object.defineProperty(ErrorEvent.prototype, "message", { enumerable: true }); | |
| class MessageEvent extends Event { | |
| /** | |
| * Create a new `MessageEvent`. | |
| * | |
| * @param {String} type The name of the event | |
| * @param {Object} [options] A dictionary object that allows for setting | |
| * attributes via object members of the same name | |
| * @param {*} [options.data=null] The message content | |
| */ | |
| constructor(type, options = {}) { | |
| super(type); | |
| this[kData] = options.data === void 0 ? null : options.data; | |
| } | |
| /** | |
| * @type {*} | |
| */ | |
| get data() { | |
| return this[kData]; | |
| } | |
| } | |
| Object.defineProperty(MessageEvent.prototype, "data", { enumerable: true }); | |
| const EventTarget = { | |
| /** | |
| * Register an event listener. | |
| * | |
| * @param {String} type A string representing the event type to listen for | |
| * @param {(Function|Object)} handler The listener to add | |
| * @param {Object} [options] An options object specifies characteristics about | |
| * the event listener | |
| * @param {Boolean} [options.once=false] A `Boolean` indicating that the | |
| * listener should be invoked at most once after being added. If `true`, | |
| * the listener would be automatically removed when invoked. | |
| * @public | |
| */ | |
| addEventListener(type, handler, options = {}) { | |
| for (const listener of this.listeners(type)) { | |
| if (!options[kForOnEventAttribute$1] && listener[kListener$1] === handler && !listener[kForOnEventAttribute$1]) { | |
| return; | |
| } | |
| } | |
| let wrapper; | |
| if (type === "message") { | |
| wrapper = function onMessage(data, isBinary) { | |
| const event = new MessageEvent("message", { | |
| data: isBinary ? data : data.toString() | |
| }); | |
| event[kTarget] = this; | |
| callListener(handler, this, event); | |
| }; | |
| } else if (type === "close") { | |
| wrapper = function onClose(code, message) { | |
| const event = new CloseEvent("close", { | |
| code, | |
| reason: message.toString(), | |
| wasClean: this._closeFrameReceived && this._closeFrameSent | |
| }); | |
| event[kTarget] = this; | |
| callListener(handler, this, event); | |
| }; | |
| } else if (type === "error") { | |
| wrapper = function onError(error2) { | |
| const event = new ErrorEvent("error", { | |
| error: error2, | |
| message: error2.message | |
| }); | |
| event[kTarget] = this; | |
| callListener(handler, this, event); | |
| }; | |
| } else if (type === "open") { | |
| wrapper = function onOpen() { | |
| const event = new Event("open"); | |
| event[kTarget] = this; | |
| callListener(handler, this, event); | |
| }; | |
| } else { | |
| return; | |
| } | |
| wrapper[kForOnEventAttribute$1] = !!options[kForOnEventAttribute$1]; | |
| wrapper[kListener$1] = handler; | |
| if (options.once) { | |
| this.once(type, wrapper); | |
| } else { | |
| this.on(type, wrapper); | |
| } | |
| }, | |
| /** | |
| * Remove an event listener. | |
| * | |
| * @param {String} type A string representing the event type to remove | |
| * @param {(Function|Object)} handler The listener to remove | |
| * @public | |
| */ | |
| removeEventListener(type, handler) { | |
| for (const listener of this.listeners(type)) { | |
| if (listener[kListener$1] === handler && !listener[kForOnEventAttribute$1]) { | |
| this.removeListener(type, listener); | |
| break; | |
| } | |
| } | |
| } | |
| }; | |
| var eventTarget = { | |
| CloseEvent, | |
| ErrorEvent, | |
| Event, | |
| EventTarget, | |
| MessageEvent | |
| }; | |
| function callListener(listener, thisArg, event) { | |
| if (typeof listener === "object" && listener.handleEvent) { | |
| listener.handleEvent.call(listener, event); | |
| } else { | |
| listener.call(thisArg, event); | |
| } | |
| } | |
| const { tokenChars: tokenChars$1 } = validationExports; | |
| function push(dest, name, elem) { | |
| if (dest[name] === void 0) | |
| dest[name] = [elem]; | |
| else | |
| dest[name].push(elem); | |
| } | |
| function parse$2(header) { | |
| const offers = /* @__PURE__ */ Object.create(null); | |
| let params = /* @__PURE__ */ Object.create(null); | |
| let mustUnescape = false; | |
| let isEscaping = false; | |
| let inQuotes = false; | |
| let extensionName; | |
| let paramName; | |
| let start = -1; | |
| let code = -1; | |
| let end = -1; | |
| let i = 0; | |
| for (; i < header.length; i++) { | |
| code = header.charCodeAt(i); | |
| if (extensionName === void 0) { | |
| if (end === -1 && tokenChars$1[code] === 1) { | |
| if (start === -1) | |
| start = i; | |
| } else if (i !== 0 && (code === 32 || code === 9)) { | |
| if (end === -1 && start !== -1) | |
| end = i; | |
| } else if (code === 59 || code === 44) { | |
| if (start === -1) { | |
| throw new SyntaxError(`Unexpected character at index ${i}`); | |
| } | |
| if (end === -1) | |
| end = i; | |
| const name = header.slice(start, end); | |
| if (code === 44) { | |
| push(offers, name, params); | |
| params = /* @__PURE__ */ Object.create(null); | |
| } else { | |
| extensionName = name; | |
| } | |
| start = end = -1; | |
| } else { | |
| throw new SyntaxError(`Unexpected character at index ${i}`); | |
| } | |
| } else if (paramName === void 0) { | |
| if (end === -1 && tokenChars$1[code] === 1) { | |
| if (start === -1) | |
| start = i; | |
| } else if (code === 32 || code === 9) { | |
| if (end === -1 && start !== -1) | |
| end = i; | |
| } else if (code === 59 || code === 44) { | |
| if (start === -1) { | |
| throw new SyntaxError(`Unexpected character at index ${i}`); | |
| } | |
| if (end === -1) | |
| end = i; | |
| push(params, header.slice(start, end), true); | |
| if (code === 44) { | |
| push(offers, extensionName, params); | |
| params = /* @__PURE__ */ Object.create(null); | |
| extensionName = void 0; | |
| } | |
| start = end = -1; | |
| } else if (code === 61 && start !== -1 && end === -1) { | |
| paramName = header.slice(start, i); | |
| start = end = -1; | |
| } else { | |
| throw new SyntaxError(`Unexpected character at index ${i}`); | |
| } | |
| } else { | |
| if (isEscaping) { | |
| if (tokenChars$1[code] !== 1) { | |
| throw new SyntaxError(`Unexpected character at index ${i}`); | |
| } | |
| if (start === -1) | |
| start = i; | |
| else if (!mustUnescape) | |
| mustUnescape = true; | |
| isEscaping = false; | |
| } else if (inQuotes) { | |
| if (tokenChars$1[code] === 1) { | |
| if (start === -1) | |
| start = i; | |
| } else if (code === 34 && start !== -1) { | |
| inQuotes = false; | |
| end = i; | |
| } else if (code === 92) { | |
| isEscaping = true; | |
| } else { | |
| throw new SyntaxError(`Unexpected character at index ${i}`); | |
| } | |
| } else if (code === 34 && header.charCodeAt(i - 1) === 61) { | |
| inQuotes = true; | |
| } else if (end === -1 && tokenChars$1[code] === 1) { | |
| if (start === -1) | |
| start = i; | |
| } else if (start !== -1 && (code === 32 || code === 9)) { | |
| if (end === -1) | |
| end = i; | |
| } else if (code === 59 || code === 44) { | |
| if (start === -1) { | |
| throw new SyntaxError(`Unexpected character at index ${i}`); | |
| } | |
| if (end === -1) | |
| end = i; | |
| let value = header.slice(start, end); | |
| if (mustUnescape) { | |
| value = value.replace(/\\/g, ""); | |
| mustUnescape = false; | |
| } | |
| push(params, paramName, value); | |
| if (code === 44) { | |
| push(offers, extensionName, params); | |
| params = /* @__PURE__ */ Object.create(null); | |
| extensionName = void 0; | |
| } | |
| paramName = void 0; | |
| start = end = -1; | |
| } else { | |
| throw new SyntaxError(`Unexpected character at index ${i}`); | |
| } | |
| } | |
| } | |
| if (start === -1 || inQuotes || code === 32 || code === 9) { | |
| throw new SyntaxError("Unexpected end of input"); | |
| } | |
| if (end === -1) | |
| end = i; | |
| const token = header.slice(start, end); | |
| if (extensionName === void 0) { | |
| push(offers, token, params); | |
| } else { | |
| if (paramName === void 0) { | |
| push(params, token, true); | |
| } else if (mustUnescape) { | |
| push(params, paramName, token.replace(/\\/g, "")); | |
| } else { | |
| push(params, paramName, token); | |
| } | |
| push(offers, extensionName, params); | |
| } | |
| return offers; | |
| } | |
| function format$1(extensions) { | |
| return Object.keys(extensions).map((extension2) => { | |
| let configurations = extensions[extension2]; | |
| if (!Array.isArray(configurations)) | |
| configurations = [configurations]; | |
| return configurations.map((params) => { | |
| return [extension2].concat( | |
| Object.keys(params).map((k) => { | |
| let values = params[k]; | |
| if (!Array.isArray(values)) | |
| values = [values]; | |
| return values.map((v) => v === true ? k : `${k}=${v}`).join("; "); | |
| }) | |
| ).join("; "); | |
| }).join(", "); | |
| }).join(", "); | |
| } | |
| var extension$1 = { format: format$1, parse: parse$2 }; | |
| const EventEmitter$1 = require$$0$3; | |
| const https = require$$1$1; | |
| const http$1 = require$$2; | |
| const net = require$$3; | |
| const tls = require$$4; | |
| const { randomBytes, createHash: createHash$1 } = require$$5; | |
| const { URL } = require$$7; | |
| const PerMessageDeflate$1 = permessageDeflate; | |
| const Receiver2 = receiver; | |
| const Sender2 = sender; | |
| const { | |
| BINARY_TYPES, | |
| EMPTY_BUFFER, | |
| GUID: GUID$1, | |
| kForOnEventAttribute, | |
| kListener, | |
| kStatusCode, | |
| kWebSocket: kWebSocket$1, | |
| NOOP | |
| } = constants; | |
| const { | |
| EventTarget: { addEventListener, removeEventListener } | |
| } = eventTarget; | |
| const { format, parse: parse$1 } = extension$1; | |
| const { toBuffer } = bufferUtilExports; | |
| const closeTimeout = 30 * 1e3; | |
| const kAborted = Symbol("kAborted"); | |
| const protocolVersions = [8, 13]; | |
| const readyStates = ["CONNECTING", "OPEN", "CLOSING", "CLOSED"]; | |
| const subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/; | |
| let WebSocket$1 = class WebSocket extends EventEmitter$1 { | |
| /** | |
| * Create a new `WebSocket`. | |
| * | |
| * @param {(String|URL)} address The URL to which to connect | |
| * @param {(String|String[])} [protocols] The subprotocols | |
| * @param {Object} [options] Connection options | |
| */ | |
| constructor(address, protocols, options) { | |
| super(); | |
| this._binaryType = BINARY_TYPES[0]; | |
| this._closeCode = 1006; | |
| this._closeFrameReceived = false; | |
| this._closeFrameSent = false; | |
| this._closeMessage = EMPTY_BUFFER; | |
| this._closeTimer = null; | |
| this._extensions = {}; | |
| this._paused = false; | |
| this._protocol = ""; | |
| this._readyState = WebSocket.CONNECTING; | |
| this._receiver = null; | |
| this._sender = null; | |
| this._socket = null; | |
| if (address !== null) { | |
| this._bufferedAmount = 0; | |
| this._isServer = false; | |
| this._redirects = 0; | |
| if (protocols === void 0) { | |
| protocols = []; | |
| } else if (!Array.isArray(protocols)) { | |
| if (typeof protocols === "object" && protocols !== null) { | |
| options = protocols; | |
| protocols = []; | |
| } else { | |
| protocols = [protocols]; | |
| } | |
| } | |
| initAsClient(this, address, protocols, options); | |
| } else { | |
| this._isServer = true; | |
| } | |
| } | |
| /** | |
| * This deviates from the WHATWG interface since ws doesn't support the | |
| * required default "blob" type (instead we define a custom "nodebuffer" | |
| * type). | |
| * | |
| * @type {String} | |
| */ | |
| get binaryType() { | |
| return this._binaryType; | |
| } | |
| set binaryType(type) { | |
| if (!BINARY_TYPES.includes(type)) | |
| return; | |
| this._binaryType = type; | |
| if (this._receiver) | |
| this._receiver._binaryType = type; | |
| } | |
| /** | |
| * @type {Number} | |
| */ | |
| get bufferedAmount() { | |
| if (!this._socket) | |
| return this._bufferedAmount; | |
| return this._socket._writableState.length + this._sender._bufferedBytes; | |
| } | |
| /** | |
| * @type {String} | |
| */ | |
| get extensions() { | |
| return Object.keys(this._extensions).join(); | |
| } | |
| /** | |
| * @type {Boolean} | |
| */ | |
| get isPaused() { | |
| return this._paused; | |
| } | |
| /** | |
| * @type {Function} | |
| */ | |
| /* istanbul ignore next */ | |
| get onclose() { | |
| return null; | |
| } | |
| /** | |
| * @type {Function} | |
| */ | |
| /* istanbul ignore next */ | |
| get onerror() { | |
| return null; | |
| } | |
| /** | |
| * @type {Function} | |
| */ | |
| /* istanbul ignore next */ | |
| get onopen() { | |
| return null; | |
| } | |
| /** | |
| * @type {Function} | |
| */ | |
| /* istanbul ignore next */ | |
| get onmessage() { | |
| return null; | |
| } | |
| /** | |
| * @type {String} | |
| */ | |
| get protocol() { | |
| return this._protocol; | |
| } | |
| /** | |
| * @type {Number} | |
| */ | |
| get readyState() { | |
| return this._readyState; | |
| } | |
| /** | |
| * @type {String} | |
| */ | |
| get url() { | |
| return this._url; | |
| } | |
| /** | |
| * Set up the socket and the internal resources. | |
| * | |
| * @param {(net.Socket|tls.Socket)} socket The network socket between the | |
| * server and client | |
| * @param {Buffer} head The first packet of the upgraded stream | |
| * @param {Object} options Options object | |
| * @param {Function} [options.generateMask] The function used to generate the | |
| * masking key | |
| * @param {Number} [options.maxPayload=0] The maximum allowed message size | |
| * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or | |
| * not to skip UTF-8 validation for text and close messages | |
| * @private | |
| */ | |
| setSocket(socket, head, options) { | |
| const receiver2 = new Receiver2({ | |
| binaryType: this.binaryType, | |
| extensions: this._extensions, | |
| isServer: this._isServer, | |
| maxPayload: options.maxPayload, | |
| skipUTF8Validation: options.skipUTF8Validation | |
| }); | |
| this._sender = new Sender2(socket, this._extensions, options.generateMask); | |
| this._receiver = receiver2; | |
| this._socket = socket; | |
| receiver2[kWebSocket$1] = this; | |
| socket[kWebSocket$1] = this; | |
| receiver2.on("conclude", receiverOnConclude); | |
| receiver2.on("drain", receiverOnDrain); | |
| receiver2.on("error", receiverOnError); | |
| receiver2.on("message", receiverOnMessage); | |
| receiver2.on("ping", receiverOnPing); | |
| receiver2.on("pong", receiverOnPong); | |
| socket.setTimeout(0); | |
| socket.setNoDelay(); | |
| if (head.length > 0) | |
| socket.unshift(head); | |
| socket.on("close", socketOnClose); | |
| socket.on("data", socketOnData); | |
| socket.on("end", socketOnEnd); | |
| socket.on("error", socketOnError$1); | |
| this._readyState = WebSocket.OPEN; | |
| this.emit("open"); | |
| } | |
| /** | |
| * Emit the `'close'` event. | |
| * | |
| * @private | |
| */ | |
| emitClose() { | |
| if (!this._socket) { | |
| this._readyState = WebSocket.CLOSED; | |
| this.emit("close", this._closeCode, this._closeMessage); | |
| return; | |
| } | |
| if (this._extensions[PerMessageDeflate$1.extensionName]) { | |
| this._extensions[PerMessageDeflate$1.extensionName].cleanup(); | |
| } | |
| this._receiver.removeAllListeners(); | |
| this._readyState = WebSocket.CLOSED; | |
| this.emit("close", this._closeCode, this._closeMessage); | |
| } | |
| /** | |
| * Start a closing handshake. | |
| * | |
| * +----------+ +-----------+ +----------+ | |
| * - - -|ws.close()|-->|close frame|-->|ws.close()|- - - | |
| * | +----------+ +-----------+ +----------+ | | |
| * +----------+ +-----------+ | | |
| * CLOSING |ws.close()|<--|close frame|<--+-----+ CLOSING | |
| * +----------+ +-----------+ | | |
| * | | | +---+ | | |
| * +------------------------+-->|fin| - - - - | |
| * | +---+ | +---+ | |
| * - - - - -|fin|<---------------------+ | |
| * +---+ | |
| * | |
| * @param {Number} [code] Status code explaining why the connection is closing | |
| * @param {(String|Buffer)} [data] The reason why the connection is | |
| * closing | |
| * @public | |
| */ | |
| close(code, data) { | |
| if (this.readyState === WebSocket.CLOSED) | |
| return; | |
| if (this.readyState === WebSocket.CONNECTING) { | |
| const msg = "WebSocket was closed before the connection was established"; | |
| abortHandshake$1(this, this._req, msg); | |
| return; | |
| } | |
| if (this.readyState === WebSocket.CLOSING) { | |
| if (this._closeFrameSent && (this._closeFrameReceived || this._receiver._writableState.errorEmitted)) { | |
| this._socket.end(); | |
| } | |
| return; | |
| } | |
| this._readyState = WebSocket.CLOSING; | |
| this._sender.close(code, data, !this._isServer, (err) => { | |
| if (err) | |
| return; | |
| this._closeFrameSent = true; | |
| if (this._closeFrameReceived || this._receiver._writableState.errorEmitted) { | |
| this._socket.end(); | |
| } | |
| }); | |
| this._closeTimer = setTimeout( | |
| this._socket.destroy.bind(this._socket), | |
| closeTimeout | |
| ); | |
| } | |
| /** | |
| * Pause the socket. | |
| * | |
| * @public | |
| */ | |
| pause() { | |
| if (this.readyState === WebSocket.CONNECTING || this.readyState === WebSocket.CLOSED) { | |
| return; | |
| } | |
| this._paused = true; | |
| this._socket.pause(); | |
| } | |
| /** | |
| * Send a ping. | |
| * | |
| * @param {*} [data] The data to send | |
| * @param {Boolean} [mask] Indicates whether or not to mask `data` | |
| * @param {Function} [cb] Callback which is executed when the ping is sent | |
| * @public | |
| */ | |
| ping(data, mask2, cb) { | |
| if (this.readyState === WebSocket.CONNECTING) { | |
| throw new Error("WebSocket is not open: readyState 0 (CONNECTING)"); | |
| } | |
| if (typeof data === "function") { | |
| cb = data; | |
| data = mask2 = void 0; | |
| } else if (typeof mask2 === "function") { | |
| cb = mask2; | |
| mask2 = void 0; | |
| } | |
| if (typeof data === "number") | |
| data = data.toString(); | |
| if (this.readyState !== WebSocket.OPEN) { | |
| sendAfterClose(this, data, cb); | |
| return; | |
| } | |
| if (mask2 === void 0) | |
| mask2 = !this._isServer; | |
| this._sender.ping(data || EMPTY_BUFFER, mask2, cb); | |
| } | |
| /** | |
| * Send a pong. | |
| * | |
| * @param {*} [data] The data to send | |
| * @param {Boolean} [mask] Indicates whether or not to mask `data` | |
| * @param {Function} [cb] Callback which is executed when the pong is sent | |
| * @public | |
| */ | |
| pong(data, mask2, cb) { | |
| if (this.readyState === WebSocket.CONNECTING) { | |
| throw new Error("WebSocket is not open: readyState 0 (CONNECTING)"); | |
| } | |
| if (typeof data === "function") { | |
| cb = data; | |
| data = mask2 = void 0; | |
| } else if (typeof mask2 === "function") { | |
| cb = mask2; | |
| mask2 = void 0; | |
| } | |
| if (typeof data === "number") | |
| data = data.toString(); | |
| if (this.readyState !== WebSocket.OPEN) { | |
| sendAfterClose(this, data, cb); | |
| return; | |
| } | |
| if (mask2 === void 0) | |
| mask2 = !this._isServer; | |
| this._sender.pong(data || EMPTY_BUFFER, mask2, cb); | |
| } | |
| /** | |
| * Resume the socket. | |
| * | |
| * @public | |
| */ | |
| resume() { | |
| if (this.readyState === WebSocket.CONNECTING || this.readyState === WebSocket.CLOSED) { | |
| return; | |
| } | |
| this._paused = false; | |
| if (!this._receiver._writableState.needDrain) | |
| this._socket.resume(); | |
| } | |
| /** | |
| * Send a data message. | |
| * | |
| * @param {*} data The message to send | |
| * @param {Object} [options] Options object | |
| * @param {Boolean} [options.binary] Specifies whether `data` is binary or | |
| * text | |
| * @param {Boolean} [options.compress] Specifies whether or not to compress | |
| * `data` | |
| * @param {Boolean} [options.fin=true] Specifies whether the fragment is the | |
| * last one | |
| * @param {Boolean} [options.mask] Specifies whether or not to mask `data` | |
| * @param {Function} [cb] Callback which is executed when data is written out | |
| * @public | |
| */ | |
| send(data, options, cb) { | |
| if (this.readyState === WebSocket.CONNECTING) { | |
| throw new Error("WebSocket is not open: readyState 0 (CONNECTING)"); | |
| } | |
| if (typeof options === "function") { | |
| cb = options; | |
| options = {}; | |
| } | |
| if (typeof data === "number") | |
| data = data.toString(); | |
| if (this.readyState !== WebSocket.OPEN) { | |
| sendAfterClose(this, data, cb); | |
| return; | |
| } | |
| const opts = { | |
| binary: typeof data !== "string", | |
| mask: !this._isServer, | |
| compress: true, | |
| fin: true, | |
| ...options | |
| }; | |
| if (!this._extensions[PerMessageDeflate$1.extensionName]) { | |
| opts.compress = false; | |
| } | |
| this._sender.send(data || EMPTY_BUFFER, opts, cb); | |
| } | |
| /** | |
| * Forcibly close the connection. | |
| * | |
| * @public | |
| */ | |
| terminate() { | |
| if (this.readyState === WebSocket.CLOSED) | |
| return; | |
| if (this.readyState === WebSocket.CONNECTING) { | |
| const msg = "WebSocket was closed before the connection was established"; | |
| abortHandshake$1(this, this._req, msg); | |
| return; | |
| } | |
| if (this._socket) { | |
| this._readyState = WebSocket.CLOSING; | |
| this._socket.destroy(); | |
| } | |
| } | |
| }; | |
| Object.defineProperty(WebSocket$1, "CONNECTING", { | |
| enumerable: true, | |
| value: readyStates.indexOf("CONNECTING") | |
| }); | |
| Object.defineProperty(WebSocket$1.prototype, "CONNECTING", { | |
| enumerable: true, | |
| value: readyStates.indexOf("CONNECTING") | |
| }); | |
| Object.defineProperty(WebSocket$1, "OPEN", { | |
| enumerable: true, | |
| value: readyStates.indexOf("OPEN") | |
| }); | |
| Object.defineProperty(WebSocket$1.prototype, "OPEN", { | |
| enumerable: true, | |
| value: readyStates.indexOf("OPEN") | |
| }); | |
| Object.defineProperty(WebSocket$1, "CLOSING", { | |
| enumerable: true, | |
| value: readyStates.indexOf("CLOSING") | |
| }); | |
| Object.defineProperty(WebSocket$1.prototype, "CLOSING", { | |
| enumerable: true, | |
| value: readyStates.indexOf("CLOSING") | |
| }); | |
| Object.defineProperty(WebSocket$1, "CLOSED", { | |
| enumerable: true, | |
| value: readyStates.indexOf("CLOSED") | |
| }); | |
| Object.defineProperty(WebSocket$1.prototype, "CLOSED", { | |
| enumerable: true, | |
| value: readyStates.indexOf("CLOSED") | |
| }); | |
| [ | |
| "binaryType", | |
| "bufferedAmount", | |
| "extensions", | |
| "isPaused", | |
| "protocol", | |
| "readyState", | |
| "url" | |
| ].forEach((property) => { | |
| Object.defineProperty(WebSocket$1.prototype, property, { enumerable: true }); | |
| }); | |
| ["open", "error", "close", "message"].forEach((method) => { | |
| Object.defineProperty(WebSocket$1.prototype, `on${method}`, { | |
| enumerable: true, | |
| get() { | |
| for (const listener of this.listeners(method)) { | |
| if (listener[kForOnEventAttribute]) | |
| return listener[kListener]; | |
| } | |
| return null; | |
| }, | |
| set(handler) { | |
| for (const listener of this.listeners(method)) { | |
| if (listener[kForOnEventAttribute]) { | |
| this.removeListener(method, listener); | |
| break; | |
| } | |
| } | |
| if (typeof handler !== "function") | |
| return; | |
| this.addEventListener(method, handler, { | |
| [kForOnEventAttribute]: true | |
| }); | |
| } | |
| }); | |
| }); | |
| WebSocket$1.prototype.addEventListener = addEventListener; | |
| WebSocket$1.prototype.removeEventListener = removeEventListener; | |
| var websocket = WebSocket$1; | |
| function initAsClient(websocket2, address, protocols, options) { | |
| const opts = { | |
| protocolVersion: protocolVersions[1], | |
| maxPayload: 100 * 1024 * 1024, | |
| skipUTF8Validation: false, | |
| perMessageDeflate: true, | |
| followRedirects: false, | |
| maxRedirects: 10, | |
| ...options, | |
| createConnection: void 0, | |
| socketPath: void 0, | |
| hostname: void 0, | |
| protocol: void 0, | |
| timeout: void 0, | |
| method: "GET", | |
| host: void 0, | |
| path: void 0, | |
| port: void 0 | |
| }; | |
| if (!protocolVersions.includes(opts.protocolVersion)) { | |
| throw new RangeError( | |
| `Unsupported protocol version: ${opts.protocolVersion} (supported versions: ${protocolVersions.join(", ")})` | |
| ); | |
| } | |
| let parsedUrl; | |
| if (address instanceof URL) { | |
| parsedUrl = address; | |
| websocket2._url = address.href; | |
| } else { | |
| try { | |
| parsedUrl = new URL(address); | |
| } catch (e) { | |
| throw new SyntaxError(`Invalid URL: ${address}`); | |
| } | |
| websocket2._url = address; | |
| } | |
| const isSecure = parsedUrl.protocol === "wss:"; | |
| const isIpcUrl = parsedUrl.protocol === "ws+unix:"; | |
| let invalidUrlMessage; | |
| if (parsedUrl.protocol !== "ws:" && !isSecure && !isIpcUrl) { | |
| invalidUrlMessage = `The URL's protocol must be one of "ws:", "wss:", or "ws+unix:"`; | |
| } else if (isIpcUrl && !parsedUrl.pathname) { | |
| invalidUrlMessage = "The URL's pathname is empty"; | |
| } else if (parsedUrl.hash) { | |
| invalidUrlMessage = "The URL contains a fragment identifier"; | |
| } | |
| if (invalidUrlMessage) { | |
| const err = new SyntaxError(invalidUrlMessage); | |
| if (websocket2._redirects === 0) { | |
| throw err; | |
| } else { | |
| emitErrorAndClose(websocket2, err); | |
| return; | |
| } | |
| } | |
| const defaultPort = isSecure ? 443 : 80; | |
| const key = randomBytes(16).toString("base64"); | |
| const request = isSecure ? https.request : http$1.request; | |
| const protocolSet = /* @__PURE__ */ new Set(); | |
| let perMessageDeflate; | |
| opts.createConnection = isSecure ? tlsConnect : netConnect; | |
| opts.defaultPort = opts.defaultPort || defaultPort; | |
| opts.port = parsedUrl.port || defaultPort; | |
| opts.host = parsedUrl.hostname.startsWith("[") ? parsedUrl.hostname.slice(1, -1) : parsedUrl.hostname; | |
| opts.headers = { | |
| ...opts.headers, | |
| "Sec-WebSocket-Version": opts.protocolVersion, | |
| "Sec-WebSocket-Key": key, | |
| Connection: "Upgrade", | |
| Upgrade: "websocket" | |
| }; | |
| opts.path = parsedUrl.pathname + parsedUrl.search; | |
| opts.timeout = opts.handshakeTimeout; | |
| if (opts.perMessageDeflate) { | |
| perMessageDeflate = new PerMessageDeflate$1( | |
| opts.perMessageDeflate !== true ? opts.perMessageDeflate : {}, | |
| false, | |
| opts.maxPayload | |
| ); | |
| opts.headers["Sec-WebSocket-Extensions"] = format({ | |
| [PerMessageDeflate$1.extensionName]: perMessageDeflate.offer() | |
| }); | |
| } | |
| if (protocols.length) { | |
| for (const protocol of protocols) { | |
| if (typeof protocol !== "string" || !subprotocolRegex.test(protocol) || protocolSet.has(protocol)) { | |
| throw new SyntaxError( | |
| "An invalid or duplicated subprotocol was specified" | |
| ); | |
| } | |
| protocolSet.add(protocol); | |
| } | |
| opts.headers["Sec-WebSocket-Protocol"] = protocols.join(","); | |
| } | |
| if (opts.origin) { | |
| if (opts.protocolVersion < 13) { | |
| opts.headers["Sec-WebSocket-Origin"] = opts.origin; | |
| } else { | |
| opts.headers.Origin = opts.origin; | |
| } | |
| } | |
| if (parsedUrl.username || parsedUrl.password) { | |
| opts.auth = `${parsedUrl.username}:${parsedUrl.password}`; | |
| } | |
| if (isIpcUrl) { | |
| const parts = opts.path.split(":"); | |
| opts.socketPath = parts[0]; | |
| opts.path = parts[1]; | |
| } | |
| let req; | |
| if (opts.followRedirects) { | |
| if (websocket2._redirects === 0) { | |
| websocket2._originalIpc = isIpcUrl; | |
| websocket2._originalSecure = isSecure; | |
| websocket2._originalHostOrSocketPath = isIpcUrl ? opts.socketPath : parsedUrl.host; | |
| const headers = options && options.headers; | |
| options = { ...options, headers: {} }; | |
| if (headers) { | |
| for (const [key2, value] of Object.entries(headers)) { | |
| options.headers[key2.toLowerCase()] = value; | |
| } | |
| } | |
| } else if (websocket2.listenerCount("redirect") === 0) { | |
| const isSameHost = isIpcUrl ? websocket2._originalIpc ? opts.socketPath === websocket2._originalHostOrSocketPath : false : websocket2._originalIpc ? false : parsedUrl.host === websocket2._originalHostOrSocketPath; | |
| if (!isSameHost || websocket2._originalSecure && !isSecure) { | |
| delete opts.headers.authorization; | |
| delete opts.headers.cookie; | |
| if (!isSameHost) | |
| delete opts.headers.host; | |
| opts.auth = void 0; | |
| } | |
| } | |
| if (opts.auth && !options.headers.authorization) { | |
| options.headers.authorization = "Basic " + Buffer.from(opts.auth).toString("base64"); | |
| } | |
| req = websocket2._req = request(opts); | |
| if (websocket2._redirects) { | |
| websocket2.emit("redirect", websocket2.url, req); | |
| } | |
| } else { | |
| req = websocket2._req = request(opts); | |
| } | |
| if (opts.timeout) { | |
| req.on("timeout", () => { | |
| abortHandshake$1(websocket2, req, "Opening handshake has timed out"); | |
| }); | |
| } | |
| req.on("error", (err) => { | |
| if (req === null || req[kAborted]) | |
| return; | |
| req = websocket2._req = null; | |
| emitErrorAndClose(websocket2, err); | |
| }); | |
| req.on("response", (res) => { | |
| const location = res.headers.location; | |
| const statusCode = res.statusCode; | |
| if (location && opts.followRedirects && statusCode >= 300 && statusCode < 400) { | |
| if (++websocket2._redirects > opts.maxRedirects) { | |
| abortHandshake$1(websocket2, req, "Maximum redirects exceeded"); | |
| return; | |
| } | |
| req.abort(); | |
| let addr; | |
| try { | |
| addr = new URL(location, address); | |
| } catch (e) { | |
| const err = new SyntaxError(`Invalid URL: ${location}`); | |
| emitErrorAndClose(websocket2, err); | |
| return; | |
| } | |
| initAsClient(websocket2, addr, protocols, options); | |
| } else if (!websocket2.emit("unexpected-response", req, res)) { | |
| abortHandshake$1( | |
| websocket2, | |
| req, | |
| `Unexpected server response: ${res.statusCode}` | |
| ); | |
| } | |
| }); | |
| req.on("upgrade", (res, socket, head) => { | |
| websocket2.emit("upgrade", res); | |
| if (websocket2.readyState !== WebSocket$1.CONNECTING) | |
| return; | |
| req = websocket2._req = null; | |
| if (res.headers.upgrade.toLowerCase() !== "websocket") { | |
| abortHandshake$1(websocket2, socket, "Invalid Upgrade header"); | |
| return; | |
| } | |
| const digest = createHash$1("sha1").update(key + GUID$1).digest("base64"); | |
| if (res.headers["sec-websocket-accept"] !== digest) { | |
| abortHandshake$1(websocket2, socket, "Invalid Sec-WebSocket-Accept header"); | |
| return; | |
| } | |
| const serverProt = res.headers["sec-websocket-protocol"]; | |
| let protError; | |
| if (serverProt !== void 0) { | |
| if (!protocolSet.size) { | |
| protError = "Server sent a subprotocol but none was requested"; | |
| } else if (!protocolSet.has(serverProt)) { | |
| protError = "Server sent an invalid subprotocol"; | |
| } | |
| } else if (protocolSet.size) { | |
| protError = "Server sent no subprotocol"; | |
| } | |
| if (protError) { | |
| abortHandshake$1(websocket2, socket, protError); | |
| return; | |
| } | |
| if (serverProt) | |
| websocket2._protocol = serverProt; | |
| const secWebSocketExtensions = res.headers["sec-websocket-extensions"]; | |
| if (secWebSocketExtensions !== void 0) { | |
| if (!perMessageDeflate) { | |
| const message = "Server sent a Sec-WebSocket-Extensions header but no extension was requested"; | |
| abortHandshake$1(websocket2, socket, message); | |
| return; | |
| } | |
| let extensions; | |
| try { | |
| extensions = parse$1(secWebSocketExtensions); | |
| } catch (err) { | |
| const message = "Invalid Sec-WebSocket-Extensions header"; | |
| abortHandshake$1(websocket2, socket, message); | |
| return; | |
| } | |
| const extensionNames = Object.keys(extensions); | |
| if (extensionNames.length !== 1 || extensionNames[0] !== PerMessageDeflate$1.extensionName) { | |
| const message = "Server indicated an extension that was not requested"; | |
| abortHandshake$1(websocket2, socket, message); | |
| return; | |
| } | |
| try { | |
| perMessageDeflate.accept(extensions[PerMessageDeflate$1.extensionName]); | |
| } catch (err) { | |
| const message = "Invalid Sec-WebSocket-Extensions header"; | |
| abortHandshake$1(websocket2, socket, message); | |
| return; | |
| } | |
| websocket2._extensions[PerMessageDeflate$1.extensionName] = perMessageDeflate; | |
| } | |
| websocket2.setSocket(socket, head, { | |
| generateMask: opts.generateMask, | |
| maxPayload: opts.maxPayload, | |
| skipUTF8Validation: opts.skipUTF8Validation | |
| }); | |
| }); | |
| if (opts.finishRequest) { | |
| opts.finishRequest(req, websocket2); | |
| } else { | |
| req.end(); | |
| } | |
| } | |
| function emitErrorAndClose(websocket2, err) { | |
| websocket2._readyState = WebSocket$1.CLOSING; | |
| websocket2.emit("error", err); | |
| websocket2.emitClose(); | |
| } | |
| function netConnect(options) { | |
| options.path = options.socketPath; | |
| return net.connect(options); | |
| } | |
| function tlsConnect(options) { | |
| options.path = void 0; | |
| if (!options.servername && options.servername !== "") { | |
| options.servername = net.isIP(options.host) ? "" : options.host; | |
| } | |
| return tls.connect(options); | |
| } | |
| function abortHandshake$1(websocket2, stream2, message) { | |
| websocket2._readyState = WebSocket$1.CLOSING; | |
| const err = new Error(message); | |
| Error.captureStackTrace(err, abortHandshake$1); | |
| if (stream2.setHeader) { | |
| stream2[kAborted] = true; | |
| stream2.abort(); | |
| if (stream2.socket && !stream2.socket.destroyed) { | |
| stream2.socket.destroy(); | |
| } | |
| process.nextTick(emitErrorAndClose, websocket2, err); | |
| } else { | |
| stream2.destroy(err); | |
| stream2.once("error", websocket2.emit.bind(websocket2, "error")); | |
| stream2.once("close", websocket2.emitClose.bind(websocket2)); | |
| } | |
| } | |
| function sendAfterClose(websocket2, data, cb) { | |
| if (data) { | |
| const length = toBuffer(data).length; | |
| if (websocket2._socket) | |
| websocket2._sender._bufferedBytes += length; | |
| else | |
| websocket2._bufferedAmount += length; | |
| } | |
| if (cb) { | |
| const err = new Error( | |
| `WebSocket is not open: readyState ${websocket2.readyState} (${readyStates[websocket2.readyState]})` | |
| ); | |
| process.nextTick(cb, err); | |
| } | |
| } | |
| function receiverOnConclude(code, reason) { | |
| const websocket2 = this[kWebSocket$1]; | |
| websocket2._closeFrameReceived = true; | |
| websocket2._closeMessage = reason; | |
| websocket2._closeCode = code; | |
| if (websocket2._socket[kWebSocket$1] === void 0) | |
| return; | |
| websocket2._socket.removeListener("data", socketOnData); | |
| process.nextTick(resume, websocket2._socket); | |
| if (code === 1005) | |
| websocket2.close(); | |
| else | |
| websocket2.close(code, reason); | |
| } | |
| function receiverOnDrain() { | |
| const websocket2 = this[kWebSocket$1]; | |
| if (!websocket2.isPaused) | |
| websocket2._socket.resume(); | |
| } | |
| function receiverOnError(err) { | |
| const websocket2 = this[kWebSocket$1]; | |
| if (websocket2._socket[kWebSocket$1] !== void 0) { | |
| websocket2._socket.removeListener("data", socketOnData); | |
| process.nextTick(resume, websocket2._socket); | |
| websocket2.close(err[kStatusCode]); | |
| } | |
| websocket2.emit("error", err); | |
| } | |
| function receiverOnFinish() { | |
| this[kWebSocket$1].emitClose(); | |
| } | |
| function receiverOnMessage(data, isBinary) { | |
| this[kWebSocket$1].emit("message", data, isBinary); | |
| } | |
| function receiverOnPing(data) { | |
| const websocket2 = this[kWebSocket$1]; | |
| websocket2.pong(data, !websocket2._isServer, NOOP); | |
| websocket2.emit("ping", data); | |
| } | |
| function receiverOnPong(data) { | |
| this[kWebSocket$1].emit("pong", data); | |
| } | |
| function resume(stream2) { | |
| stream2.resume(); | |
| } | |
| function socketOnClose() { | |
| const websocket2 = this[kWebSocket$1]; | |
| this.removeListener("close", socketOnClose); | |
| this.removeListener("data", socketOnData); | |
| this.removeListener("end", socketOnEnd); | |
| websocket2._readyState = WebSocket$1.CLOSING; | |
| let chunk; | |
| if (!this._readableState.endEmitted && !websocket2._closeFrameReceived && !websocket2._receiver._writableState.errorEmitted && (chunk = websocket2._socket.read()) !== null) { | |
| websocket2._receiver.write(chunk); | |
| } | |
| websocket2._receiver.end(); | |
| this[kWebSocket$1] = void 0; | |
| clearTimeout(websocket2._closeTimer); | |
| if (websocket2._receiver._writableState.finished || websocket2._receiver._writableState.errorEmitted) { | |
| websocket2.emitClose(); | |
| } else { | |
| websocket2._receiver.on("error", receiverOnFinish); | |
| websocket2._receiver.on("finish", receiverOnFinish); | |
| } | |
| } | |
| function socketOnData(chunk) { | |
| if (!this[kWebSocket$1]._receiver.write(chunk)) { | |
| this.pause(); | |
| } | |
| } | |
| function socketOnEnd() { | |
| const websocket2 = this[kWebSocket$1]; | |
| websocket2._readyState = WebSocket$1.CLOSING; | |
| websocket2._receiver.end(); | |
| this.end(); | |
| } | |
| function socketOnError$1() { | |
| const websocket2 = this[kWebSocket$1]; | |
| this.removeListener("error", socketOnError$1); | |
| this.on("error", NOOP); | |
| if (websocket2) { | |
| websocket2._readyState = WebSocket$1.CLOSING; | |
| this.destroy(); | |
| } | |
| } | |
| const WebSocket$2 = /* @__PURE__ */ getDefaultExportFromCjs(websocket); | |
| const { tokenChars } = validationExports; | |
| function parse(header) { | |
| const protocols = /* @__PURE__ */ new Set(); | |
| let start = -1; | |
| let end = -1; | |
| let i = 0; | |
| for (i; i < header.length; i++) { | |
| const code = header.charCodeAt(i); | |
| if (end === -1 && tokenChars[code] === 1) { | |
| if (start === -1) | |
| start = i; | |
| } else if (i !== 0 && (code === 32 || code === 9)) { | |
| if (end === -1 && start !== -1) | |
| end = i; | |
| } else if (code === 44) { | |
| if (start === -1) { | |
| throw new SyntaxError(`Unexpected character at index ${i}`); | |
| } | |
| if (end === -1) | |
| end = i; | |
| const protocol2 = header.slice(start, end); | |
| if (protocols.has(protocol2)) { | |
| throw new SyntaxError(`The "${protocol2}" subprotocol is duplicated`); | |
| } | |
| protocols.add(protocol2); | |
| start = end = -1; | |
| } else { | |
| throw new SyntaxError(`Unexpected character at index ${i}`); | |
| } | |
| } | |
| if (start === -1 || end !== -1) { | |
| throw new SyntaxError("Unexpected end of input"); | |
| } | |
| const protocol = header.slice(start, i); | |
| if (protocols.has(protocol)) { | |
| throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`); | |
| } | |
| protocols.add(protocol); | |
| return protocols; | |
| } | |
| var subprotocol$1 = { parse }; | |
| const EventEmitter = require$$0$3; | |
| const http = require$$2; | |
| const { createHash } = require$$5; | |
| const extension = extension$1; | |
| const PerMessageDeflate2 = permessageDeflate; | |
| const subprotocol = subprotocol$1; | |
| const WebSocket2 = websocket; | |
| const { GUID, kWebSocket } = constants; | |
| const keyRegex = /^[+/0-9A-Za-z]{22}==$/; | |
| const RUNNING = 0; | |
| const CLOSING = 1; | |
| const CLOSED = 2; | |
| class WebSocketServer extends EventEmitter { | |
| /** | |
| * Create a `WebSocketServer` instance. | |
| * | |
| * @param {Object} options Configuration options | |
| * @param {Number} [options.backlog=511] The maximum length of the queue of | |
| * pending connections | |
| * @param {Boolean} [options.clientTracking=true] Specifies whether or not to | |
| * track clients | |
| * @param {Function} [options.handleProtocols] A hook to handle protocols | |
| * @param {String} [options.host] The hostname where to bind the server | |
| * @param {Number} [options.maxPayload=104857600] The maximum allowed message | |
| * size | |
| * @param {Boolean} [options.noServer=false] Enable no server mode | |
| * @param {String} [options.path] Accept only connections matching this path | |
| * @param {(Boolean|Object)} [options.perMessageDeflate=false] Enable/disable | |
| * permessage-deflate | |
| * @param {Number} [options.port] The port where to bind the server | |
| * @param {(http.Server|https.Server)} [options.server] A pre-created HTTP/S | |
| * server to use | |
| * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or | |
| * not to skip UTF-8 validation for text and close messages | |
| * @param {Function} [options.verifyClient] A hook to reject connections | |
| * @param {Function} [options.WebSocket=WebSocket] Specifies the `WebSocket` | |
| * class to use. It must be the `WebSocket` class or class that extends it | |
| * @param {Function} [callback] A listener for the `listening` event | |
| */ | |
| constructor(options, callback) { | |
| super(); | |
| options = { | |
| maxPayload: 100 * 1024 * 1024, | |
| skipUTF8Validation: false, | |
| perMessageDeflate: false, | |
| handleProtocols: null, | |
| clientTracking: true, | |
| verifyClient: null, | |
| noServer: false, | |
| backlog: null, | |
| // use default (511 as implemented in net.js) | |
| server: null, | |
| host: null, | |
| path: null, | |
| port: null, | |
| WebSocket: WebSocket2, | |
| ...options | |
| }; | |
| if (options.port == null && !options.server && !options.noServer || options.port != null && (options.server || options.noServer) || options.server && options.noServer) { | |
| throw new TypeError( | |
| 'One and only one of the "port", "server", or "noServer" options must be specified' | |
| ); | |
| } | |
| if (options.port != null) { | |
| this._server = http.createServer((req, res) => { | |
| const body = http.STATUS_CODES[426]; | |
| res.writeHead(426, { | |
| "Content-Length": body.length, | |
| "Content-Type": "text/plain" | |
| }); | |
| res.end(body); | |
| }); | |
| this._server.listen( | |
| options.port, | |
| options.host, | |
| options.backlog, | |
| callback | |
| ); | |
| } else if (options.server) { | |
| this._server = options.server; | |
| } | |
| if (this._server) { | |
| const emitConnection = this.emit.bind(this, "connection"); | |
| this._removeListeners = addListeners(this._server, { | |
| listening: this.emit.bind(this, "listening"), | |
| error: this.emit.bind(this, "error"), | |
| upgrade: (req, socket, head) => { | |
| this.handleUpgrade(req, socket, head, emitConnection); | |
| } | |
| }); | |
| } | |
| if (options.perMessageDeflate === true) | |
| options.perMessageDeflate = {}; | |
| if (options.clientTracking) { | |
| this.clients = /* @__PURE__ */ new Set(); | |
| this._shouldEmitClose = false; | |
| } | |
| this.options = options; | |
| this._state = RUNNING; | |
| } | |
| /** | |
| * Returns the bound address, the address family name, and port of the server | |
| * as reported by the operating system if listening on an IP socket. | |
| * If the server is listening on a pipe or UNIX domain socket, the name is | |
| * returned as a string. | |
| * | |
| * @return {(Object|String|null)} The address of the server | |
| * @public | |
| */ | |
| address() { | |
| if (this.options.noServer) { | |
| throw new Error('The server is operating in "noServer" mode'); | |
| } | |
| if (!this._server) | |
| return null; | |
| return this._server.address(); | |
| } | |
| /** | |
| * Stop the server from accepting new connections and emit the `'close'` event | |
| * when all existing connections are closed. | |
| * | |
| * @param {Function} [cb] A one-time listener for the `'close'` event | |
| * @public | |
| */ | |
| close(cb) { | |
| if (this._state === CLOSED) { | |
| if (cb) { | |
| this.once("close", () => { | |
| cb(new Error("The server is not running")); | |
| }); | |
| } | |
| process.nextTick(emitClose, this); | |
| return; | |
| } | |
| if (cb) | |
| this.once("close", cb); | |
| if (this._state === CLOSING) | |
| return; | |
| this._state = CLOSING; | |
| if (this.options.noServer || this.options.server) { | |
| if (this._server) { | |
| this._removeListeners(); | |
| this._removeListeners = this._server = null; | |
| } | |
| if (this.clients) { | |
| if (!this.clients.size) { | |
| process.nextTick(emitClose, this); | |
| } else { | |
| this._shouldEmitClose = true; | |
| } | |
| } else { | |
| process.nextTick(emitClose, this); | |
| } | |
| } else { | |
| const server = this._server; | |
| this._removeListeners(); | |
| this._removeListeners = this._server = null; | |
| server.close(() => { | |
| emitClose(this); | |
| }); | |
| } | |
| } | |
| /** | |
| * See if a given request should be handled by this server instance. | |
| * | |
| * @param {http.IncomingMessage} req Request object to inspect | |
| * @return {Boolean} `true` if the request is valid, else `false` | |
| * @public | |
| */ | |
| shouldHandle(req) { | |
| if (this.options.path) { | |
| const index = req.url.indexOf("?"); | |
| const pathname = index !== -1 ? req.url.slice(0, index) : req.url; | |
| if (pathname !== this.options.path) | |
| return false; | |
| } | |
| return true; | |
| } | |
| /** | |
| * Handle a HTTP Upgrade request. | |
| * | |
| * @param {http.IncomingMessage} req The request object | |
| * @param {(net.Socket|tls.Socket)} socket The network socket between the | |
| * server and client | |
| * @param {Buffer} head The first packet of the upgraded stream | |
| * @param {Function} cb Callback | |
| * @public | |
| */ | |
| handleUpgrade(req, socket, head, cb) { | |
| socket.on("error", socketOnError); | |
| const key = req.headers["sec-websocket-key"]; | |
| const version = +req.headers["sec-websocket-version"]; | |
| if (req.method !== "GET") { | |
| const message = "Invalid HTTP method"; | |
| abortHandshakeOrEmitwsClientError(this, req, socket, 405, message); | |
| return; | |
| } | |
| if (req.headers.upgrade.toLowerCase() !== "websocket") { | |
| const message = "Invalid Upgrade header"; | |
| abortHandshakeOrEmitwsClientError(this, req, socket, 400, message); | |
| return; | |
| } | |
| if (!key || !keyRegex.test(key)) { | |
| const message = "Missing or invalid Sec-WebSocket-Key header"; | |
| abortHandshakeOrEmitwsClientError(this, req, socket, 400, message); | |
| return; | |
| } | |
| if (version !== 8 && version !== 13) { | |
| const message = "Missing or invalid Sec-WebSocket-Version header"; | |
| abortHandshakeOrEmitwsClientError(this, req, socket, 400, message); | |
| return; | |
| } | |
| if (!this.shouldHandle(req)) { | |
| abortHandshake(socket, 400); | |
| return; | |
| } | |
| const secWebSocketProtocol = req.headers["sec-websocket-protocol"]; | |
| let protocols = /* @__PURE__ */ new Set(); | |
| if (secWebSocketProtocol !== void 0) { | |
| try { | |
| protocols = subprotocol.parse(secWebSocketProtocol); | |
| } catch (err) { | |
| const message = "Invalid Sec-WebSocket-Protocol header"; | |
| abortHandshakeOrEmitwsClientError(this, req, socket, 400, message); | |
| return; | |
| } | |
| } | |
| const secWebSocketExtensions = req.headers["sec-websocket-extensions"]; | |
| const extensions = {}; | |
| if (this.options.perMessageDeflate && secWebSocketExtensions !== void 0) { | |
| const perMessageDeflate = new PerMessageDeflate2( | |
| this.options.perMessageDeflate, | |
| true, | |
| this.options.maxPayload | |
| ); | |
| try { | |
| const offers = extension.parse(secWebSocketExtensions); | |
| if (offers[PerMessageDeflate2.extensionName]) { | |
| perMessageDeflate.accept(offers[PerMessageDeflate2.extensionName]); | |
| extensions[PerMessageDeflate2.extensionName] = perMessageDeflate; | |
| } | |
| } catch (err) { | |
| const message = "Invalid or unacceptable Sec-WebSocket-Extensions header"; | |
| abortHandshakeOrEmitwsClientError(this, req, socket, 400, message); | |
| return; | |
| } | |
| } | |
| if (this.options.verifyClient) { | |
| const info = { | |
| origin: req.headers[`${version === 8 ? "sec-websocket-origin" : "origin"}`], | |
| secure: !!(req.socket.authorized || req.socket.encrypted), | |
| req | |
| }; | |
| if (this.options.verifyClient.length === 2) { | |
| this.options.verifyClient(info, (verified, code, message, headers) => { | |
| if (!verified) { | |
| return abortHandshake(socket, code || 401, message, headers); | |
| } | |
| this.completeUpgrade( | |
| extensions, | |
| key, | |
| protocols, | |
| req, | |
| socket, | |
| head, | |
| cb | |
| ); | |
| }); | |
| return; | |
| } | |
| if (!this.options.verifyClient(info)) | |
| return abortHandshake(socket, 401); | |
| } | |
| this.completeUpgrade(extensions, key, protocols, req, socket, head, cb); | |
| } | |
| /** | |
| * Upgrade the connection to WebSocket. | |
| * | |
| * @param {Object} extensions The accepted extensions | |
| * @param {String} key The value of the `Sec-WebSocket-Key` header | |
| * @param {Set} protocols The subprotocols | |
| * @param {http.IncomingMessage} req The request object | |
| * @param {(net.Socket|tls.Socket)} socket The network socket between the | |
| * server and client | |
| * @param {Buffer} head The first packet of the upgraded stream | |
| * @param {Function} cb Callback | |
| * @throws {Error} If called more than once with the same socket | |
| * @private | |
| */ | |
| completeUpgrade(extensions, key, protocols, req, socket, head, cb) { | |
| if (!socket.readable || !socket.writable) | |
| return socket.destroy(); | |
| if (socket[kWebSocket]) { | |
| throw new Error( | |
| "server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration" | |
| ); | |
| } | |
| if (this._state > RUNNING) | |
| return abortHandshake(socket, 503); | |
| const digest = createHash("sha1").update(key + GUID).digest("base64"); | |
| const headers = [ | |
| "HTTP/1.1 101 Switching Protocols", | |
| "Upgrade: websocket", | |
| "Connection: Upgrade", | |
| `Sec-WebSocket-Accept: ${digest}` | |
| ]; | |
| const ws = new this.options.WebSocket(null); | |
| if (protocols.size) { | |
| const protocol = this.options.handleProtocols ? this.options.handleProtocols(protocols, req) : protocols.values().next().value; | |
| if (protocol) { | |
| headers.push(`Sec-WebSocket-Protocol: ${protocol}`); | |
| ws._protocol = protocol; | |
| } | |
| } | |
| if (extensions[PerMessageDeflate2.extensionName]) { | |
| const params = extensions[PerMessageDeflate2.extensionName].params; | |
| const value = extension.format({ | |
| [PerMessageDeflate2.extensionName]: [params] | |
| }); | |
| headers.push(`Sec-WebSocket-Extensions: ${value}`); | |
| ws._extensions = extensions; | |
| } | |
| this.emit("headers", headers, req); | |
| socket.write(headers.concat("\r\n").join("\r\n")); | |
| socket.removeListener("error", socketOnError); | |
| ws.setSocket(socket, head, { | |
| maxPayload: this.options.maxPayload, | |
| skipUTF8Validation: this.options.skipUTF8Validation | |
| }); | |
| if (this.clients) { | |
| this.clients.add(ws); | |
| ws.on("close", () => { | |
| this.clients.delete(ws); | |
| if (this._shouldEmitClose && !this.clients.size) { | |
| process.nextTick(emitClose, this); | |
| } | |
| }); | |
| } | |
| cb(ws, req); | |
| } | |
| } | |
| var websocketServer = WebSocketServer; | |
| function addListeners(server, map) { | |
| for (const event of Object.keys(map)) | |
| server.on(event, map[event]); | |
| return function removeListeners() { | |
| for (const event of Object.keys(map)) { | |
| server.removeListener(event, map[event]); | |
| } | |
| }; | |
| } | |
| function emitClose(server) { | |
| server._state = CLOSED; | |
| server.emit("close"); | |
| } | |
| function socketOnError() { | |
| this.destroy(); | |
| } | |
| function abortHandshake(socket, code, message, headers) { | |
| message = message || http.STATUS_CODES[code]; | |
| headers = { | |
| Connection: "close", | |
| "Content-Type": "text/html", | |
| "Content-Length": Buffer.byteLength(message), | |
| ...headers | |
| }; | |
| socket.once("finish", socket.destroy); | |
| socket.end( | |
| `HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\r | |
| ` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join("\r\n") + "\r\n\r\n" + message | |
| ); | |
| } | |
| function abortHandshakeOrEmitwsClientError(server, req, socket, code, message) { | |
| if (server.listenerCount("wsClientError")) { | |
| const err = new Error(message); | |
| Error.captureStackTrace(err, abortHandshakeOrEmitwsClientError); | |
| server.emit("wsClientError", err, socket, req); | |
| } else { | |
| abortHandshake(socket, code, message); | |
| } | |
| } | |
| const websocketServer$1 = /* @__PURE__ */ getDefaultExportFromCjs(websocketServer); | |
| export { | |
| receiver$1 as Receiver, | |
| sender$1 as Sender, | |
| WebSocket$2 as WebSocket, | |
| websocketServer$1 as WebSocketServer, | |
| stream$1 as createWebSocketStream, | |
| WebSocket$2 as default | |
| }; | |