diff options
Diffstat (limited to 'src/js/out/modules/thirdparty/ws.js')
-rw-r--r-- | src/js/out/modules/thirdparty/ws.js | 681 |
1 files changed, 681 insertions, 0 deletions
diff --git a/src/js/out/modules/thirdparty/ws.js b/src/js/out/modules/thirdparty/ws.js new file mode 100644 index 000000000..dd616d86a --- /dev/null +++ b/src/js/out/modules/thirdparty/ws.js @@ -0,0 +1,681 @@ +var EventEmitter = import.meta.require("node:events"); +var http = import.meta.require("node:http"); +var emitWarning = function(type, message) { + if (emittedWarnings.has(type)) + return; + emittedWarnings.add(type), console.warn("[bun] Warning:", message); +}, subprotocolParse = function(header) { + const protocols = new Set; + let start = -1, end = -1, i = 0; + for (i;i < header.length; i++) { + const code = header.charCodeAt(i); + if (end === -1 && wsTokenChars[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`); + return protocols.add(protocol), protocols; +}, wsEmitClose = function(server) { + server._state = CLOSED, server.emit("close"); +}, abortHandshake = function(response, code, message, headers) { + message = message || http.STATUS_CODES[code], headers = { + Connection: "close", + "Content-Type": "text/html", + "Content-Length": Buffer.byteLength(message), + ...headers + }, response.writeHead(code, headers), response.write(message), response.end(); +}, abortHandshakeOrEmitwsClientError = function(server, req, response, socket, code, message) { + if (server.listenerCount("wsClientError")) { + const err = new Error(message); + Error.captureStackTrace(err, abortHandshakeOrEmitwsClientError), server.emit("wsClientError", err, socket, req); + } else + abortHandshake(response, code, message); +}, kBunInternals = Symbol.for("::bunternal::"), readyStates = ["CONNECTING", "OPEN", "CLOSING", "CLOSED"], encoder = new TextEncoder, emittedWarnings = new Set; + +class BunWebSocket extends EventEmitter { + static CONNECTING = 0; + static OPEN = 1; + static CLOSING = 2; + static CLOSED = 3; + #ws; + #paused = !1; + #fragments = !1; + #binaryType = "nodebuffer"; + readyState = BunWebSocket.CONNECTING; + constructor(url, protocols, options) { + super(); + let ws = this.#ws = new WebSocket(url, protocols); + ws.binaryType = "nodebuffer", ws.addEventListener("open", () => { + this.readyState = BunWebSocket.OPEN, this.emit("open"); + }), ws.addEventListener("error", (err) => { + this.readyState = BunWebSocket.CLOSED, this.emit("error", err); + }), ws.addEventListener("close", (ev) => { + this.readyState = BunWebSocket.CLOSED, this.emit("close", ev.code, ev.reason); + }), ws.addEventListener("message", (ev) => { + const isBinary = typeof ev.data !== "string"; + if (isBinary) + this.emit("message", this.#fragments ? [ev.data] : ev.data, isBinary); + else { + var encoded = encoder.encode(ev.data); + if (this.#binaryType !== "arraybuffer") + encoded = Buffer.from(encoded.buffer, encoded.byteOffset, encoded.byteLength); + this.emit("message", this.#fragments ? [encoded] : encoded, isBinary); + } + }); + } + on(event, listener) { + if (event === "unexpected-response" || event === "upgrade" || event === "ping" || event === "pong" || event === "redirect") + emitWarning(event, "ws.WebSocket '" + event + "' event is not implemented in bun"); + return super.on(event, listener); + } + send(data, opts, cb) { + this.#ws.send(data, opts?.compress), typeof cb === "function" && cb(); + } + close(code, reason) { + this.#ws.close(code, reason); + } + get binaryType() { + return this.#binaryType; + } + set binaryType(value) { + if (value) + this.#ws.binaryType = value; + } + set binaryType(value) { + if (value === "nodebuffer" || value === "arraybuffer") + this.#ws.binaryType = this.#binaryType = value, this.#fragments = !1; + else if (value === "fragments") + this.#ws.binaryType = "nodebuffer", this.#binaryType = "fragments", this.#fragments = !0; + } + get protocol() { + return this.#ws.protocol; + } + get extensions() { + return this.#ws.extensions; + } + addEventListener(type, listener, options) { + this.#ws.addEventListener(type, listener, options); + } + removeEventListener(type, listener) { + this.#ws.removeEventListener(type, listener); + } + get onopen() { + return this.#ws.onopen; + } + set onopen(value) { + this.#ws.onopen = value; + } + get onerror() { + return this.#ws.onerror; + } + set onerror(value) { + this.#ws.onerror = value; + } + get onclose() { + return this.#ws.onclose; + } + set onclose(value) { + this.#ws.onclose = value; + } + get onmessage() { + return this.#ws.onmessage; + } + set onmessage(value) { + this.#ws.onmessage = value; + } + get bufferedAmount() { + return this.#ws.bufferedAmount; + } + get isPaused() { + return this.#paused; + } + ping(data, mask, cb) { + if (this.readyState === BunWebSocket.CONNECTING) + throw new Error("WebSocket is not open: readyState 0 (CONNECTING)"); + if (typeof data === "function") + cb = data, data = mask = void 0; + else if (typeof mask === "function") + cb = mask, mask = void 0; + if (typeof data === "number") + data = data.toString(); + emitWarning("ping()", "ws.WebSocket.ping() is not implemented in bun"), typeof cb === "function" && cb(); + } + pong(data, mask, cb) { + if (this.readyState === BunWebSocket.CONNECTING) + throw new Error("WebSocket is not open: readyState 0 (CONNECTING)"); + if (typeof data === "function") + cb = data, data = mask = void 0; + else if (typeof mask === "function") + cb = mask, mask = void 0; + if (typeof data === "number") + data = data.toString(); + emitWarning("pong()", "ws.WebSocket.pong() is not implemented in bun"), typeof cb === "function" && cb(); + } + pause() { + if (this.readyState === WebSocket.CONNECTING || this.readyState === WebSocket.CLOSED) + return; + this.#paused = !0, emitWarning("pause()", "ws.WebSocket.pause() is not implemented in bun"); + } + resume() { + if (this.readyState === WebSocket.CONNECTING || this.readyState === WebSocket.CLOSED) + return; + this.#paused = !1, emitWarning("resume()", "ws.WebSocket.resume() is not implemented in bun"); + } +} +BunWebSocket.WebSocket = BunWebSocket; +var wsKeyRegex = /^[+/0-9A-Za-z]{22}==$/, wsTokenChars = [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 0, + 1, + 0 +], RUNNING = 0, CLOSING = 1, CLOSED = 2; + +class BunWebSocketMocked extends EventEmitter { + #ws; + #state; + #enquedMessages = []; + #url; + #protocol; + #extensions; + #bufferedAmount = 0; + #binaryType = "arraybuffer"; + #onclose; + #onerror; + #onmessage; + #onopen; + constructor(url, protocol, extensions, binaryType) { + super(); + if (this.#ws = null, this.#state = 0, this.#url = url, this.#bufferedAmount = 0, binaryType = binaryType || "arraybuffer", binaryType !== "nodebuffer" && binaryType !== "blob" && binaryType !== "arraybuffer") + throw new TypeError("binaryType must be either 'blob', 'arraybuffer' or 'nodebuffer'"); + this.#binaryType = binaryType, this.#protocol = protocol, this.#extensions = extensions; + const message = this.#message.bind(this), open = this.#open.bind(this), close = this.#close.bind(this), drain = this.#drain.bind(this); + this[kBunInternals] = { + message, + open, + close, + drain + }; + } + #message(ws, message) { + if (this.#ws = ws, typeof message === "string") + if (this.#binaryType === "arraybuffer") + message = encoder.encode(message).buffer; + else if (this.#binaryType === "blob") + message = new Blob([message], { type: "text/plain" }); + else + message = Buffer.from(message); + else if (this.#binaryType !== "nodebuffer") { + if (this.#binaryType === "arraybuffer") + message = new Uint8Array(message); + else if (this.#binaryType === "blob") + message = new Blob([message]); + } + this.emit("message", message); + } + #open(ws) { + this.#ws = ws, this.#state = 1, this.emit("open", this), this.#drain(ws); + } + #close(ws, code, reason) { + this.#state = 3, this.#ws = null, this.emit("close", code, reason); + } + #drain(ws) { + const chunk = this.#enquedMessages[0]; + if (chunk) { + const [data, compress, cb] = chunk; + if (ws.send(data, compress) == -1) + return; + typeof cb === "function" && cb(), this.#bufferedAmount -= chunk.length, this.#enquedMessages.shift(); + } + } + send(data, opts, cb) { + if (this.#state === 1) { + const compress = opts?.compress; + if (this.#ws.send(data, compress) == -1) { + this.#enquedMessages.push([data, compress, cb]), this.#bufferedAmount += data.length; + return; + } + typeof cb === "function" && cb(); + } else if (this.#state === 0) + this.#enquedMessages.push([data, opts?.compress, cb]), this.#bufferedAmount += data.length; + } + close(code, reason) { + if (this.#state === 1) + this.#state = 2, this.#ws.close(code, reason); + } + get binaryType() { + return this.#binaryType; + } + set binaryType(type) { + if (type !== "nodebuffer" && type !== "blob" && type !== "arraybuffer") + throw new TypeError("binaryType must be either 'blob', 'arraybuffer' or 'nodebuffer'"); + this.#binaryType = type; + } + get readyState() { + return readyStates[this.#state]; + } + get url() { + return this.#url; + } + get protocol() { + return this.#protocol; + } + get extensions() { + return this.#extensions; + } + get bufferedAmount() { + return this.#bufferedAmount ?? 0; + } + setSocket(socket, head, options) { + throw new Error("Not implemented"); + } + set onclose(cb) { + if (this.#onclose) + this.removeListener("close", this.#onclose); + this.on("close", cb), this.#onclose = cb; + } + set onerror(cb) { + if (this.#onerror) + this.removeListener("error", this.#onerror); + this.on("error", cb), this.#onerror = cb; + } + set onmessage(cb) { + if (this.#onmessage) + this.removeListener("message", this.#onmessage); + this.on("message", cb), this.#onmessage = cb; + } + set onopen(cb) { + if (this.#onopen) + this.removeListener("open", this.#onopen); + this.on("open", cb), this.#onopen = cb; + } + get onclose() { + return this.#onclose; + } + get onerror() { + return this.#onerror; + } + get onmessage() { + return this.#onmessage; + } + get onopen() { + return this.#onopen; + } +} + +class Server extends EventEmitter { + _server; + options; + clients; + _shouldEmitClose; + _state; + _removeListeners; + constructor(options, callback) { + super(); + if (options = { + maxPayload: 104857600, + skipUTF8Validation: !1, + perMessageDeflate: !1, + handleProtocols: null, + clientTracking: !0, + verifyClient: null, + noServer: !1, + backlog: null, + server: null, + host: null, + path: null, + port: null, + ...options + }, 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"), emitListening = this.emit.bind(this, "listening"), emitError = this.emit.bind(this, "error"), doUpgrade = (req, socket, head) => { + this.handleUpgrade(req, socket, head, emitConnection); + }; + this._server.on("listening", emitListening), this._server.on("error", emitError), this._server.on("upgrade", doUpgrade), this._removeListeners = () => { + this._server.removeListener("upgrade", doUpgrade), this._server.removeListener("listening", emitListening), this._server.removeListener("error", emitError); + }; + } + if (options.perMessageDeflate === !0) + options.perMessageDeflate = {}; + if (options.clientTracking) + this.clients = new Set, this._shouldEmitClose = !1; + this.options = options, this._state = RUNNING; + } + address() { + if (this.options.noServer) + throw new Error('The server is operating in "noServer" mode'); + if (!this._server) + return null; + return this._server.address(); + } + close(cb) { + if (this._state === CLOSED) { + if (cb) + this.once("close", () => { + cb(new Error("The server is not running")); + }); + process.nextTick((server) => { + server._state = CLOSED, server.emit("close"); + }, this); + return; + } + if (cb) + this.once("close", cb); + if (this._state === CLOSING) + return; + if (this._state = CLOSING, 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((server) => { + server._state = CLOSED, server.emit("close"); + }, this); + else + this._shouldEmitClose = !0; + else + process.nextTick((server) => { + server._state = CLOSED, server.emit("close"); + }, this); + } else { + const server = this._server; + this._removeListeners(), this._removeListeners = this._server = null, server.close(() => { + this._state = CLOSED, this.emit("close"); + }); + } + } + shouldHandle(req) { + if (this.options.path) { + const index = req.url.indexOf("?"); + if ((index !== -1 ? req.url.slice(0, index) : req.url) !== this.options.path) + return !1; + } + return !0; + } + completeUpgrade(extensions, key, protocols, request, socket, head, cb) { + const [server, response, req] = socket[kBunInternals]; + if (this._state > RUNNING) + return abortHandshake(response, 503); + let protocol = ""; + if (protocols.size) + protocol = this.options.handleProtocols ? this.options.handleProtocols(protocols, request) : protocols.values().next().value; + const ws = new BunWebSocketMocked(request.url, protocol, extensions, "nodebuffer"), headers = ["HTTP/1.1 101 Switching Protocols", "Upgrade: websocket", "Connection: Upgrade"]; + if (this.emit("headers", headers, request), server.upgrade(req, { + data: ws[kBunInternals] + })) { + if (response._reply(void 0), this.clients) + this.clients.add(ws), ws.on("close", () => { + if (this.clients.delete(ws), this._shouldEmitClose && !this.clients.size) + process.nextTick(wsEmitClose, this); + }); + cb(ws, request); + } else + abortHandshake(response, 500); + } + handleUpgrade(req, socket, head, cb) { + const [_, response] = socket[kBunInternals], key = req.headers["sec-websocket-key"], version = +req.headers["sec-websocket-version"]; + if (req.method !== "GET") { + abortHandshakeOrEmitwsClientError(this, req, response, socket, 405, "Invalid HTTP method"); + return; + } + if (req.headers.upgrade.toLowerCase() !== "websocket") { + abortHandshakeOrEmitwsClientError(this, req, response, socket, 400, "Invalid Upgrade header"); + return; + } + if (!key || !wsKeyRegex.test(key)) { + abortHandshakeOrEmitwsClientError(this, req, response, socket, 400, "Missing or invalid Sec-WebSocket-Key header"); + return; + } + if (version !== 8 && version !== 13) { + abortHandshakeOrEmitwsClientError(this, req, response, socket, 400, "Missing or invalid Sec-WebSocket-Version header"); + return; + } + if (!this.shouldHandle(req)) { + abortHandshake(response, 400); + return; + } + const secWebSocketProtocol = req.headers["sec-websocket-protocol"]; + let protocols = new Set; + if (secWebSocketProtocol !== void 0) + try { + protocols = subprotocolParse(secWebSocketProtocol); + } catch (err) { + abortHandshakeOrEmitwsClientError(this, req, response, socket, 400, "Invalid Sec-WebSocket-Protocol header"); + return; + } + const extensions = {}; + 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(response, code || 401, message, headers); + this.completeUpgrade(extensions, key, protocols, req, socket, head, cb); + }); + return; + } + if (!this.options.verifyClient(info)) + return abortHandshake(response, 401); + } + this.completeUpgrade(extensions, key, protocols, req, socket, head, cb); + } +} +BunWebSocket.WebSocketServer = Server; +BunWebSocket.Server = Server; +Object.defineProperty(BunWebSocket, "CONNECTING", { + enumerable: !0, + value: readyStates.indexOf("CONNECTING") +}); +Object.defineProperty(BunWebSocket.prototype, "CONNECTING", { + enumerable: !0, + value: readyStates.indexOf("CONNECTING") +}); +Object.defineProperty(BunWebSocket, "OPEN", { + enumerable: !0, + value: readyStates.indexOf("OPEN") +}); +Object.defineProperty(BunWebSocket.prototype, "OPEN", { + enumerable: !0, + value: readyStates.indexOf("OPEN") +}); +Object.defineProperty(BunWebSocket, "CLOSING", { + enumerable: !0, + value: readyStates.indexOf("CLOSING") +}); +Object.defineProperty(BunWebSocket.prototype, "CLOSING", { + enumerable: !0, + value: readyStates.indexOf("CLOSING") +}); +Object.defineProperty(BunWebSocket, "CLOSED", { + enumerable: !0, + value: readyStates.indexOf("CLOSED") +}); +Object.defineProperty(BunWebSocket.prototype, "CLOSED", { + enumerable: !0, + value: readyStates.indexOf("CLOSED") +}); + +class Sender { + constructor() { + throw new Error("Not supported yet in Bun"); + } +} +BunWebSocket.Sender = Sender; + +class Receiver { + constructor() { + throw new Error("Not supported yet in Bun"); + } +} +BunWebSocket.Receiver = Receiver; +var createWebSocketStream = (ws) => { + throw new Error("Not supported yet in Bun"); +}; +BunWebSocket.createWebSocketStream = createWebSocketStream; +BunWebSocket[Symbol.for("CommonJS")] = 0; +var ws_default = BunWebSocket; +export { + ws_default as default, + createWebSocketStream, + Server as WebSocketServer, + BunWebSocket as WebSocket, + Server, + Sender, + Receiver +}; |