aboutsummaryrefslogtreecommitdiffstats
path: root/vanilla/node_modules/undici/lib/web/websocket
diff options
context:
space:
mode:
authorAdam Mathes <adam@adammathes.com>2026-02-14 14:46:37 -0800
committerAdam Mathes <adam@adammathes.com>2026-02-14 14:46:37 -0800
commitafa87af01c79a9baa539f2992d32154d2a4739bd (patch)
tree92c7416db734270a2fee1d72ee9cc119379ff8e1 /vanilla/node_modules/undici/lib/web/websocket
parent3b927e84d200402281f68181cd4253bc77e5528d (diff)
downloadneko-afa87af01c79a9baa539f2992d32154d2a4739bd.tar.gz
neko-afa87af01c79a9baa539f2992d32154d2a4739bd.tar.bz2
neko-afa87af01c79a9baa539f2992d32154d2a4739bd.zip
task: delete vanilla js prototype\n\n- Removed vanilla/ directory and web/dist/vanilla directory\n- Updated Makefile, Dockerfile, and CI workflow to remove vanilla references\n- Cleaned up web/web.go to remove vanilla embed and routes\n- Verified build and tests pass\n\nCloses NK-2tcnmq
Diffstat (limited to 'vanilla/node_modules/undici/lib/web/websocket')
-rw-r--r--vanilla/node_modules/undici/lib/web/websocket/connection.js329
-rw-r--r--vanilla/node_modules/undici/lib/web/websocket/constants.js126
-rw-r--r--vanilla/node_modules/undici/lib/web/websocket/events.js331
-rw-r--r--vanilla/node_modules/undici/lib/web/websocket/frame.js133
-rw-r--r--vanilla/node_modules/undici/lib/web/websocket/permessage-deflate.js70
-rw-r--r--vanilla/node_modules/undici/lib/web/websocket/receiver.js444
-rw-r--r--vanilla/node_modules/undici/lib/web/websocket/sender.js109
-rw-r--r--vanilla/node_modules/undici/lib/web/websocket/stream/websocketerror.js104
-rw-r--r--vanilla/node_modules/undici/lib/web/websocket/stream/websocketstream.js497
-rw-r--r--vanilla/node_modules/undici/lib/web/websocket/util.js339
-rw-r--r--vanilla/node_modules/undici/lib/web/websocket/websocket.js739
11 files changed, 0 insertions, 3221 deletions
diff --git a/vanilla/node_modules/undici/lib/web/websocket/connection.js b/vanilla/node_modules/undici/lib/web/websocket/connection.js
deleted file mode 100644
index 4ecc8a1..0000000
--- a/vanilla/node_modules/undici/lib/web/websocket/connection.js
+++ /dev/null
@@ -1,329 +0,0 @@
-'use strict'
-
-const { uid, states, sentCloseFrameState, emptyBuffer, opcodes } = require('./constants')
-const { parseExtensions, isClosed, isClosing, isEstablished, isConnecting, validateCloseCodeAndReason } = require('./util')
-const { makeRequest } = require('../fetch/request')
-const { fetching } = require('../fetch/index')
-const { Headers, getHeadersList } = require('../fetch/headers')
-const { getDecodeSplit } = require('../fetch/util')
-const { WebsocketFrameSend } = require('./frame')
-const assert = require('node:assert')
-const { runtimeFeatures } = require('../../util/runtime-features')
-
-const crypto = runtimeFeatures.has('crypto')
- ? require('node:crypto')
- : null
-
-let warningEmitted = false
-
-/**
- * @see https://websockets.spec.whatwg.org/#concept-websocket-establish
- * @param {URL} url
- * @param {string|string[]} protocols
- * @param {import('./websocket').Handler} handler
- * @param {Partial<import('../../../types/websocket').WebSocketInit>} options
- */
-function establishWebSocketConnection (url, protocols, client, handler, options) {
- // 1. Let requestURL be a copy of url, with its scheme set to "http", if url’s
- // scheme is "ws", and to "https" otherwise.
- const requestURL = url
-
- requestURL.protocol = url.protocol === 'ws:' ? 'http:' : 'https:'
-
- // 2. Let request be a new request, whose URL is requestURL, client is client,
- // service-workers mode is "none", referrer is "no-referrer", mode is
- // "websocket", credentials mode is "include", cache mode is "no-store" ,
- // redirect mode is "error", and use-URL-credentials flag is set.
- const request = makeRequest({
- urlList: [requestURL],
- client,
- serviceWorkers: 'none',
- referrer: 'no-referrer',
- mode: 'websocket',
- credentials: 'include',
- cache: 'no-store',
- redirect: 'error',
- useURLCredentials: true
- })
-
- // Note: undici extension, allow setting custom headers.
- if (options.headers) {
- const headersList = getHeadersList(new Headers(options.headers))
-
- request.headersList = headersList
- }
-
- // 3. Append (`Upgrade`, `websocket`) to request’s header list.
- // 4. Append (`Connection`, `Upgrade`) to request’s header list.
- // Note: both of these are handled by undici currently.
- // https://github.com/nodejs/undici/blob/68c269c4144c446f3f1220951338daef4a6b5ec4/lib/client.js#L1397
-
- // 5. Let keyValue be a nonce consisting of a randomly selected
- // 16-byte value that has been forgiving-base64-encoded and
- // isomorphic encoded.
- const keyValue = crypto.randomBytes(16).toString('base64')
-
- // 6. Append (`Sec-WebSocket-Key`, keyValue) to request’s
- // header list.
- request.headersList.append('sec-websocket-key', keyValue, true)
-
- // 7. Append (`Sec-WebSocket-Version`, `13`) to request’s
- // header list.
- request.headersList.append('sec-websocket-version', '13', true)
-
- // 8. For each protocol in protocols, combine
- // (`Sec-WebSocket-Protocol`, protocol) in request’s header
- // list.
- for (const protocol of protocols) {
- request.headersList.append('sec-websocket-protocol', protocol, true)
- }
-
- // 9. Let permessageDeflate be a user-agent defined
- // "permessage-deflate" extension header value.
- // https://github.com/mozilla/gecko-dev/blob/ce78234f5e653a5d3916813ff990f053510227bc/netwerk/protocol/websocket/WebSocketChannel.cpp#L2673
- const permessageDeflate = 'permessage-deflate; client_max_window_bits'
-
- // 10. Append (`Sec-WebSocket-Extensions`, permessageDeflate) to
- // request’s header list.
- request.headersList.append('sec-websocket-extensions', permessageDeflate, true)
-
- // 11. Fetch request with useParallelQueue set to true, and
- // processResponse given response being these steps:
- const controller = fetching({
- request,
- useParallelQueue: true,
- dispatcher: options.dispatcher,
- processResponse (response) {
- // 1. If response is a network error or its status is not 101,
- // fail the WebSocket connection.
- // if (response.type === 'error' || ((response.socket?.session != null && response.status !== 200) && response.status !== 101)) {
- if (response.type === 'error' || response.status !== 101) {
- // The presence of a session property on the socket indicates HTTP2
- // HTTP1
- if (response.socket?.session == null) {
- failWebsocketConnection(handler, 1002, 'Received network error or non-101 status code.', response.error)
- return
- }
-
- // HTTP2
- if (response.status !== 200) {
- failWebsocketConnection(handler, 1002, 'Received network error or non-200 status code.', response.error)
- return
- }
- }
-
- if (warningEmitted === false && response.socket?.session != null) {
- process.emitWarning('WebSocket over HTTP2 is experimental, and subject to change.', 'ExperimentalWarning')
- warningEmitted = true
- }
-
- // 2. If protocols is not the empty list and extracting header
- // list values given `Sec-WebSocket-Protocol` and response’s
- // header list results in null, failure, or the empty byte
- // sequence, then fail the WebSocket connection.
- if (protocols.length !== 0 && !response.headersList.get('Sec-WebSocket-Protocol')) {
- failWebsocketConnection(handler, 1002, 'Server did not respond with sent protocols.')
- return
- }
-
- // 3. Follow the requirements stated step 2 to step 6, inclusive,
- // of the last set of steps in section 4.1 of The WebSocket
- // Protocol to validate response. This either results in fail
- // the WebSocket connection or the WebSocket connection is
- // established.
-
- // 2. If the response lacks an |Upgrade| header field or the |Upgrade|
- // header field contains a value that is not an ASCII case-
- // insensitive match for the value "websocket", the client MUST
- // _Fail the WebSocket Connection_.
- // For H2, no upgrade header is expected.
- if (response.socket.session == null && response.headersList.get('Upgrade')?.toLowerCase() !== 'websocket') {
- failWebsocketConnection(handler, 1002, 'Server did not set Upgrade header to "websocket".')
- return
- }
-
- // 3. If the response lacks a |Connection| header field or the
- // |Connection| header field doesn't contain a token that is an
- // ASCII case-insensitive match for the value "Upgrade", the client
- // MUST _Fail the WebSocket Connection_.
- // For H2, no connection header is expected.
- if (response.socket.session == null && response.headersList.get('Connection')?.toLowerCase() !== 'upgrade') {
- failWebsocketConnection(handler, 1002, 'Server did not set Connection header to "upgrade".')
- return
- }
-
- // 4. If the response lacks a |Sec-WebSocket-Accept| header field or
- // the |Sec-WebSocket-Accept| contains a value other than the
- // base64-encoded SHA-1 of the concatenation of the |Sec-WebSocket-
- // Key| (as a string, not base64-decoded) with the string "258EAFA5-
- // E914-47DA-95CA-C5AB0DC85B11" but ignoring any leading and
- // trailing whitespace, the client MUST _Fail the WebSocket
- // Connection_.
- const secWSAccept = response.headersList.get('Sec-WebSocket-Accept')
- const digest = crypto.hash('sha1', keyValue + uid, 'base64')
- if (secWSAccept !== digest) {
- failWebsocketConnection(handler, 1002, 'Incorrect hash received in Sec-WebSocket-Accept header.')
- return
- }
-
- // 5. If the response includes a |Sec-WebSocket-Extensions| header
- // field and this header field indicates the use of an extension
- // that was not present in the client's handshake (the server has
- // indicated an extension not requested by the client), the client
- // MUST _Fail the WebSocket Connection_. (The parsing of this
- // header field to determine which extensions are requested is
- // discussed in Section 9.1.)
- const secExtension = response.headersList.get('Sec-WebSocket-Extensions')
- let extensions
-
- if (secExtension !== null) {
- extensions = parseExtensions(secExtension)
-
- if (!extensions.has('permessage-deflate')) {
- failWebsocketConnection(handler, 1002, 'Sec-WebSocket-Extensions header does not match.')
- return
- }
- }
-
- // 6. If the response includes a |Sec-WebSocket-Protocol| header field
- // and this header field indicates the use of a subprotocol that was
- // not present in the client's handshake (the server has indicated a
- // subprotocol not requested by the client), the client MUST _Fail
- // the WebSocket Connection_.
- const secProtocol = response.headersList.get('Sec-WebSocket-Protocol')
-
- if (secProtocol !== null) {
- const requestProtocols = getDecodeSplit('sec-websocket-protocol', request.headersList)
-
- // The client can request that the server use a specific subprotocol by
- // including the |Sec-WebSocket-Protocol| field in its handshake. If it
- // is specified, the server needs to include the same field and one of
- // the selected subprotocol values in its response for the connection to
- // be established.
- if (!requestProtocols.includes(secProtocol)) {
- failWebsocketConnection(handler, 1002, 'Protocol was not set in the opening handshake.')
- return
- }
- }
-
- response.socket.on('data', handler.onSocketData)
- response.socket.on('close', handler.onSocketClose)
- response.socket.on('error', handler.onSocketError)
-
- handler.wasEverConnected = true
- handler.onConnectionEstablished(response, extensions)
- }
- })
-
- return controller
-}
-
-/**
- * @see https://whatpr.org/websockets/48.html#close-the-websocket
- * @param {import('./websocket').Handler} object
- * @param {number} [code=null]
- * @param {string} [reason='']
- */
-function closeWebSocketConnection (object, code, reason, validate = false) {
- // 1. If code was not supplied, let code be null.
- code ??= null
-
- // 2. If reason was not supplied, let reason be the empty string.
- reason ??= ''
-
- // 3. Validate close code and reason with code and reason.
- if (validate) validateCloseCodeAndReason(code, reason)
-
- // 4. Run the first matching steps from the following list:
- // - If object’s ready state is CLOSING (2) or CLOSED (3)
- // - If the WebSocket connection is not yet established [WSP]
- // - If the WebSocket closing handshake has not yet been started [WSP]
- // - Otherwise
- if (isClosed(object.readyState) || isClosing(object.readyState)) {
- // Do nothing.
- } else if (!isEstablished(object.readyState)) {
- // Fail the WebSocket connection and set object’s ready state to CLOSING (2). [WSP]
- failWebsocketConnection(object)
- object.readyState = states.CLOSING
- } else if (!object.closeState.has(sentCloseFrameState.SENT) && !object.closeState.has(sentCloseFrameState.RECEIVED)) {
- // Upon either sending or receiving a Close control frame, it is said
- // that _The WebSocket Closing Handshake is Started_ and that the
- // WebSocket connection is in the CLOSING state.
-
- const frame = new WebsocketFrameSend()
-
- // If neither code nor reason is present, the WebSocket Close
- // message must not have a body.
-
- // If code is present, then the status code to use in the
- // WebSocket Close message must be the integer given by code.
- // If code is null and reason is the empty string, the WebSocket Close frame must not have a body.
- // If reason is non-empty but code is null, then set code to 1000 ("Normal Closure").
- if (reason.length !== 0 && code === null) {
- code = 1000
- }
-
- // If code is set, then the status code to use in the WebSocket Close frame must be the integer given by code.
- assert(code === null || Number.isInteger(code))
-
- if (code === null && reason.length === 0) {
- frame.frameData = emptyBuffer
- } else if (code !== null && reason === null) {
- frame.frameData = Buffer.allocUnsafe(2)
- frame.frameData.writeUInt16BE(code, 0)
- } else if (code !== null && reason !== null) {
- // If reason is also present, then reasonBytes must be
- // provided in the Close message after the status code.
- frame.frameData = Buffer.allocUnsafe(2 + Buffer.byteLength(reason))
- frame.frameData.writeUInt16BE(code, 0)
- // the body MAY contain UTF-8-encoded data with value /reason/
- frame.frameData.write(reason, 2, 'utf-8')
- } else {
- frame.frameData = emptyBuffer
- }
-
- object.socket.write(frame.createFrame(opcodes.CLOSE))
-
- object.closeState.add(sentCloseFrameState.SENT)
-
- // Upon either sending or receiving a Close control frame, it is said
- // that _The WebSocket Closing Handshake is Started_ and that the
- // WebSocket connection is in the CLOSING state.
- object.readyState = states.CLOSING
- } else {
- // Set object’s ready state to CLOSING (2).
- object.readyState = states.CLOSING
- }
-}
-
-/**
- * @param {import('./websocket').Handler} handler
- * @param {number} code
- * @param {string|undefined} reason
- * @param {unknown} cause
- * @returns {void}
- */
-function failWebsocketConnection (handler, code, reason, cause) {
- // If _The WebSocket Connection is Established_ prior to the point where
- // the endpoint is required to _Fail the WebSocket Connection_, the
- // endpoint SHOULD send a Close frame with an appropriate status code
- // (Section 7.4) before proceeding to _Close the WebSocket Connection_.
- if (isEstablished(handler.readyState)) {
- closeWebSocketConnection(handler, code, reason, false)
- }
-
- handler.controller.abort()
-
- if (isConnecting(handler.readyState)) {
- // If the connection was not established, we must still emit an 'error' and 'close' events
- handler.onSocketClose()
- } else if (handler.socket?.destroyed === false) {
- handler.socket.destroy()
- }
-}
-
-module.exports = {
- establishWebSocketConnection,
- failWebsocketConnection,
- closeWebSocketConnection
-}
diff --git a/vanilla/node_modules/undici/lib/web/websocket/constants.js b/vanilla/node_modules/undici/lib/web/websocket/constants.js
deleted file mode 100644
index e4e6990..0000000
--- a/vanilla/node_modules/undici/lib/web/websocket/constants.js
+++ /dev/null
@@ -1,126 +0,0 @@
-'use strict'
-
-/**
- * This is a Globally Unique Identifier unique used to validate that the
- * endpoint accepts websocket connections.
- * @see https://www.rfc-editor.org/rfc/rfc6455.html#section-1.3
- * @type {'258EAFA5-E914-47DA-95CA-C5AB0DC85B11'}
- */
-const uid = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
-
-/**
- * @type {PropertyDescriptor}
- */
-const staticPropertyDescriptors = {
- enumerable: true,
- writable: false,
- configurable: false
-}
-
-/**
- * The states of the WebSocket connection.
- *
- * @readonly
- * @enum
- * @property {0} CONNECTING
- * @property {1} OPEN
- * @property {2} CLOSING
- * @property {3} CLOSED
- */
-const states = {
- CONNECTING: 0,
- OPEN: 1,
- CLOSING: 2,
- CLOSED: 3
-}
-
-/**
- * @readonly
- * @enum
- * @property {0} NOT_SENT
- * @property {1} PROCESSING
- * @property {2} SENT
- */
-const sentCloseFrameState = {
- SENT: 1,
- RECEIVED: 2
-}
-
-/**
- * The WebSocket opcodes.
- *
- * @readonly
- * @enum
- * @property {0x0} CONTINUATION
- * @property {0x1} TEXT
- * @property {0x2} BINARY
- * @property {0x8} CLOSE
- * @property {0x9} PING
- * @property {0xA} PONG
- * @see https://datatracker.ietf.org/doc/html/rfc6455#section-5.2
- */
-const opcodes = {
- CONTINUATION: 0x0,
- TEXT: 0x1,
- BINARY: 0x2,
- CLOSE: 0x8,
- PING: 0x9,
- PONG: 0xA
-}
-
-/**
- * The maximum value for an unsigned 16-bit integer.
- *
- * @type {65535} 2 ** 16 - 1
- */
-const maxUnsigned16Bit = 65535
-
-/**
- * The states of the parser.
- *
- * @readonly
- * @enum
- * @property {0} INFO
- * @property {2} PAYLOADLENGTH_16
- * @property {3} PAYLOADLENGTH_64
- * @property {4} READ_DATA
- */
-const parserStates = {
- INFO: 0,
- PAYLOADLENGTH_16: 2,
- PAYLOADLENGTH_64: 3,
- READ_DATA: 4
-}
-
-/**
- * An empty buffer.
- *
- * @type {Buffer}
- */
-const emptyBuffer = Buffer.allocUnsafe(0)
-
-/**
- * @readonly
- * @property {1} text
- * @property {2} typedArray
- * @property {3} arrayBuffer
- * @property {4} blob
- */
-const sendHints = {
- text: 1,
- typedArray: 2,
- arrayBuffer: 3,
- blob: 4
-}
-
-module.exports = {
- uid,
- sentCloseFrameState,
- staticPropertyDescriptors,
- states,
- opcodes,
- maxUnsigned16Bit,
- parserStates,
- emptyBuffer,
- sendHints
-}
diff --git a/vanilla/node_modules/undici/lib/web/websocket/events.js b/vanilla/node_modules/undici/lib/web/websocket/events.js
deleted file mode 100644
index 7ac9566..0000000
--- a/vanilla/node_modules/undici/lib/web/websocket/events.js
+++ /dev/null
@@ -1,331 +0,0 @@
-'use strict'
-
-const { webidl } = require('../webidl')
-const { kEnumerableProperty } = require('../../core/util')
-const { kConstruct } = require('../../core/symbols')
-
-/**
- * @see https://html.spec.whatwg.org/multipage/comms.html#messageevent
- */
-class MessageEvent extends Event {
- #eventInit
-
- constructor (type, eventInitDict = {}) {
- if (type === kConstruct) {
- super(arguments[1], arguments[2])
- webidl.util.markAsUncloneable(this)
- return
- }
-
- const prefix = 'MessageEvent constructor'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- type = webidl.converters.DOMString(type, prefix, 'type')
- eventInitDict = webidl.converters.MessageEventInit(eventInitDict, prefix, 'eventInitDict')
-
- super(type, eventInitDict)
-
- this.#eventInit = eventInitDict
- webidl.util.markAsUncloneable(this)
- }
-
- get data () {
- webidl.brandCheck(this, MessageEvent)
-
- return this.#eventInit.data
- }
-
- get origin () {
- webidl.brandCheck(this, MessageEvent)
-
- return this.#eventInit.origin
- }
-
- get lastEventId () {
- webidl.brandCheck(this, MessageEvent)
-
- return this.#eventInit.lastEventId
- }
-
- get source () {
- webidl.brandCheck(this, MessageEvent)
-
- return this.#eventInit.source
- }
-
- get ports () {
- webidl.brandCheck(this, MessageEvent)
-
- if (!Object.isFrozen(this.#eventInit.ports)) {
- Object.freeze(this.#eventInit.ports)
- }
-
- return this.#eventInit.ports
- }
-
- initMessageEvent (
- type,
- bubbles = false,
- cancelable = false,
- data = null,
- origin = '',
- lastEventId = '',
- source = null,
- ports = []
- ) {
- webidl.brandCheck(this, MessageEvent)
-
- webidl.argumentLengthCheck(arguments, 1, 'MessageEvent.initMessageEvent')
-
- return new MessageEvent(type, {
- bubbles, cancelable, data, origin, lastEventId, source, ports
- })
- }
-
- static createFastMessageEvent (type, init) {
- const messageEvent = new MessageEvent(kConstruct, type, init)
- messageEvent.#eventInit = init
- messageEvent.#eventInit.data ??= null
- messageEvent.#eventInit.origin ??= ''
- messageEvent.#eventInit.lastEventId ??= ''
- messageEvent.#eventInit.source ??= null
- messageEvent.#eventInit.ports ??= []
- return messageEvent
- }
-}
-
-const { createFastMessageEvent } = MessageEvent
-delete MessageEvent.createFastMessageEvent
-
-/**
- * @see https://websockets.spec.whatwg.org/#the-closeevent-interface
- */
-class CloseEvent extends Event {
- #eventInit
-
- constructor (type, eventInitDict = {}) {
- const prefix = 'CloseEvent constructor'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- type = webidl.converters.DOMString(type, prefix, 'type')
- eventInitDict = webidl.converters.CloseEventInit(eventInitDict)
-
- super(type, eventInitDict)
-
- this.#eventInit = eventInitDict
- webidl.util.markAsUncloneable(this)
- }
-
- get wasClean () {
- webidl.brandCheck(this, CloseEvent)
-
- return this.#eventInit.wasClean
- }
-
- get code () {
- webidl.brandCheck(this, CloseEvent)
-
- return this.#eventInit.code
- }
-
- get reason () {
- webidl.brandCheck(this, CloseEvent)
-
- return this.#eventInit.reason
- }
-}
-
-// https://html.spec.whatwg.org/multipage/webappapis.html#the-errorevent-interface
-class ErrorEvent extends Event {
- #eventInit
-
- constructor (type, eventInitDict) {
- const prefix = 'ErrorEvent constructor'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- super(type, eventInitDict)
- webidl.util.markAsUncloneable(this)
-
- type = webidl.converters.DOMString(type, prefix, 'type')
- eventInitDict = webidl.converters.ErrorEventInit(eventInitDict ?? {})
-
- this.#eventInit = eventInitDict
- }
-
- get message () {
- webidl.brandCheck(this, ErrorEvent)
-
- return this.#eventInit.message
- }
-
- get filename () {
- webidl.brandCheck(this, ErrorEvent)
-
- return this.#eventInit.filename
- }
-
- get lineno () {
- webidl.brandCheck(this, ErrorEvent)
-
- return this.#eventInit.lineno
- }
-
- get colno () {
- webidl.brandCheck(this, ErrorEvent)
-
- return this.#eventInit.colno
- }
-
- get error () {
- webidl.brandCheck(this, ErrorEvent)
-
- return this.#eventInit.error
- }
-}
-
-Object.defineProperties(MessageEvent.prototype, {
- [Symbol.toStringTag]: {
- value: 'MessageEvent',
- configurable: true
- },
- data: kEnumerableProperty,
- origin: kEnumerableProperty,
- lastEventId: kEnumerableProperty,
- source: kEnumerableProperty,
- ports: kEnumerableProperty,
- initMessageEvent: kEnumerableProperty
-})
-
-Object.defineProperties(CloseEvent.prototype, {
- [Symbol.toStringTag]: {
- value: 'CloseEvent',
- configurable: true
- },
- reason: kEnumerableProperty,
- code: kEnumerableProperty,
- wasClean: kEnumerableProperty
-})
-
-Object.defineProperties(ErrorEvent.prototype, {
- [Symbol.toStringTag]: {
- value: 'ErrorEvent',
- configurable: true
- },
- message: kEnumerableProperty,
- filename: kEnumerableProperty,
- lineno: kEnumerableProperty,
- colno: kEnumerableProperty,
- error: kEnumerableProperty
-})
-
-webidl.converters.MessagePort = webidl.interfaceConverter(
- webidl.is.MessagePort,
- 'MessagePort'
-)
-
-webidl.converters['sequence<MessagePort>'] = webidl.sequenceConverter(
- webidl.converters.MessagePort
-)
-
-const eventInit = [
- {
- key: 'bubbles',
- converter: webidl.converters.boolean,
- defaultValue: () => false
- },
- {
- key: 'cancelable',
- converter: webidl.converters.boolean,
- defaultValue: () => false
- },
- {
- key: 'composed',
- converter: webidl.converters.boolean,
- defaultValue: () => false
- }
-]
-
-webidl.converters.MessageEventInit = webidl.dictionaryConverter([
- ...eventInit,
- {
- key: 'data',
- converter: webidl.converters.any,
- defaultValue: () => null
- },
- {
- key: 'origin',
- converter: webidl.converters.USVString,
- defaultValue: () => ''
- },
- {
- key: 'lastEventId',
- converter: webidl.converters.DOMString,
- defaultValue: () => ''
- },
- {
- key: 'source',
- // Node doesn't implement WindowProxy or ServiceWorker, so the only
- // valid value for source is a MessagePort.
- converter: webidl.nullableConverter(webidl.converters.MessagePort),
- defaultValue: () => null
- },
- {
- key: 'ports',
- converter: webidl.converters['sequence<MessagePort>'],
- defaultValue: () => []
- }
-])
-
-webidl.converters.CloseEventInit = webidl.dictionaryConverter([
- ...eventInit,
- {
- key: 'wasClean',
- converter: webidl.converters.boolean,
- defaultValue: () => false
- },
- {
- key: 'code',
- converter: webidl.converters['unsigned short'],
- defaultValue: () => 0
- },
- {
- key: 'reason',
- converter: webidl.converters.USVString,
- defaultValue: () => ''
- }
-])
-
-webidl.converters.ErrorEventInit = webidl.dictionaryConverter([
- ...eventInit,
- {
- key: 'message',
- converter: webidl.converters.DOMString,
- defaultValue: () => ''
- },
- {
- key: 'filename',
- converter: webidl.converters.USVString,
- defaultValue: () => ''
- },
- {
- key: 'lineno',
- converter: webidl.converters['unsigned long'],
- defaultValue: () => 0
- },
- {
- key: 'colno',
- converter: webidl.converters['unsigned long'],
- defaultValue: () => 0
- },
- {
- key: 'error',
- converter: webidl.converters.any
- }
-])
-
-module.exports = {
- MessageEvent,
- CloseEvent,
- ErrorEvent,
- createFastMessageEvent
-}
diff --git a/vanilla/node_modules/undici/lib/web/websocket/frame.js b/vanilla/node_modules/undici/lib/web/websocket/frame.js
deleted file mode 100644
index e397c87..0000000
--- a/vanilla/node_modules/undici/lib/web/websocket/frame.js
+++ /dev/null
@@ -1,133 +0,0 @@
-'use strict'
-
-const { runtimeFeatures } = require('../../util/runtime-features')
-const { maxUnsigned16Bit, opcodes } = require('./constants')
-
-const BUFFER_SIZE = 8 * 1024
-
-let buffer = null
-let bufIdx = BUFFER_SIZE
-
-const randomFillSync = runtimeFeatures.has('crypto')
- ? require('node:crypto').randomFillSync
- // not full compatibility, but minimum.
- : function randomFillSync (buffer, _offset, _size) {
- for (let i = 0; i < buffer.length; ++i) {
- buffer[i] = Math.random() * 255 | 0
- }
- return buffer
- }
-
-function generateMask () {
- if (bufIdx === BUFFER_SIZE) {
- bufIdx = 0
- randomFillSync((buffer ??= Buffer.allocUnsafeSlow(BUFFER_SIZE)), 0, BUFFER_SIZE)
- }
- return [buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++]]
-}
-
-class WebsocketFrameSend {
- /**
- * @param {Buffer|undefined} data
- */
- constructor (data) {
- this.frameData = data
- }
-
- createFrame (opcode) {
- const frameData = this.frameData
- const maskKey = generateMask()
- const bodyLength = frameData?.byteLength ?? 0
-
- /** @type {number} */
- let payloadLength = bodyLength // 0-125
- let offset = 6
-
- if (bodyLength > maxUnsigned16Bit) {
- offset += 8 // payload length is next 8 bytes
- payloadLength = 127
- } else if (bodyLength > 125) {
- offset += 2 // payload length is next 2 bytes
- payloadLength = 126
- }
-
- const buffer = Buffer.allocUnsafe(bodyLength + offset)
-
- // Clear first 2 bytes, everything else is overwritten
- buffer[0] = buffer[1] = 0
- buffer[0] |= 0x80 // FIN
- buffer[0] = (buffer[0] & 0xF0) + opcode // opcode
-
- /*! ws. MIT License. Einar Otto Stangvik <einaros@gmail.com> */
- buffer[offset - 4] = maskKey[0]
- buffer[offset - 3] = maskKey[1]
- buffer[offset - 2] = maskKey[2]
- buffer[offset - 1] = maskKey[3]
-
- buffer[1] = payloadLength
-
- if (payloadLength === 126) {
- buffer.writeUInt16BE(bodyLength, 2)
- } else if (payloadLength === 127) {
- // Clear extended payload length
- buffer[2] = buffer[3] = 0
- buffer.writeUIntBE(bodyLength, 4, 6)
- }
-
- buffer[1] |= 0x80 // MASK
-
- // mask body
- for (let i = 0; i < bodyLength; ++i) {
- buffer[offset + i] = frameData[i] ^ maskKey[i & 3]
- }
-
- return buffer
- }
-
- /**
- * @param {Uint8Array} buffer
- */
- static createFastTextFrame (buffer) {
- const maskKey = generateMask()
-
- const bodyLength = buffer.length
-
- // mask body
- for (let i = 0; i < bodyLength; ++i) {
- buffer[i] ^= maskKey[i & 3]
- }
-
- let payloadLength = bodyLength
- let offset = 6
-
- if (bodyLength > maxUnsigned16Bit) {
- offset += 8 // payload length is next 8 bytes
- payloadLength = 127
- } else if (bodyLength > 125) {
- offset += 2 // payload length is next 2 bytes
- payloadLength = 126
- }
- const head = Buffer.allocUnsafeSlow(offset)
-
- head[0] = 0x80 /* FIN */ | opcodes.TEXT /* opcode TEXT */
- head[1] = payloadLength | 0x80 /* MASK */
- head[offset - 4] = maskKey[0]
- head[offset - 3] = maskKey[1]
- head[offset - 2] = maskKey[2]
- head[offset - 1] = maskKey[3]
-
- if (payloadLength === 126) {
- head.writeUInt16BE(bodyLength, 2)
- } else if (payloadLength === 127) {
- head[2] = head[3] = 0
- head.writeUIntBE(bodyLength, 4, 6)
- }
-
- return [head, buffer]
- }
-}
-
-module.exports = {
- WebsocketFrameSend,
- generateMask // for benchmark
-}
diff --git a/vanilla/node_modules/undici/lib/web/websocket/permessage-deflate.js b/vanilla/node_modules/undici/lib/web/websocket/permessage-deflate.js
deleted file mode 100644
index 76cb366..0000000
--- a/vanilla/node_modules/undici/lib/web/websocket/permessage-deflate.js
+++ /dev/null
@@ -1,70 +0,0 @@
-'use strict'
-
-const { createInflateRaw, Z_DEFAULT_WINDOWBITS } = require('node:zlib')
-const { isValidClientWindowBits } = require('./util')
-
-const tail = Buffer.from([0x00, 0x00, 0xff, 0xff])
-const kBuffer = Symbol('kBuffer')
-const kLength = Symbol('kLength')
-
-class PerMessageDeflate {
- /** @type {import('node:zlib').InflateRaw} */
- #inflate
-
- #options = {}
-
- constructor (extensions) {
- this.#options.serverNoContextTakeover = extensions.has('server_no_context_takeover')
- this.#options.serverMaxWindowBits = extensions.get('server_max_window_bits')
- }
-
- decompress (chunk, fin, callback) {
- // An endpoint uses the following algorithm to decompress a message.
- // 1. Append 4 octets of 0x00 0x00 0xff 0xff to the tail end of the
- // payload of the message.
- // 2. Decompress the resulting data using DEFLATE.
-
- if (!this.#inflate) {
- let windowBits = Z_DEFAULT_WINDOWBITS
-
- if (this.#options.serverMaxWindowBits) { // empty values default to Z_DEFAULT_WINDOWBITS
- if (!isValidClientWindowBits(this.#options.serverMaxWindowBits)) {
- callback(new Error('Invalid server_max_window_bits'))
- return
- }
-
- windowBits = Number.parseInt(this.#options.serverMaxWindowBits)
- }
-
- this.#inflate = createInflateRaw({ windowBits })
- this.#inflate[kBuffer] = []
- this.#inflate[kLength] = 0
-
- this.#inflate.on('data', (data) => {
- this.#inflate[kBuffer].push(data)
- this.#inflate[kLength] += data.length
- })
-
- this.#inflate.on('error', (err) => {
- this.#inflate = null
- callback(err)
- })
- }
-
- this.#inflate.write(chunk)
- if (fin) {
- this.#inflate.write(tail)
- }
-
- this.#inflate.flush(() => {
- const full = Buffer.concat(this.#inflate[kBuffer], this.#inflate[kLength])
-
- this.#inflate[kBuffer].length = 0
- this.#inflate[kLength] = 0
-
- callback(null, full)
- })
- }
-}
-
-module.exports = { PerMessageDeflate }
diff --git a/vanilla/node_modules/undici/lib/web/websocket/receiver.js b/vanilla/node_modules/undici/lib/web/websocket/receiver.js
deleted file mode 100644
index ba0a5aa..0000000
--- a/vanilla/node_modules/undici/lib/web/websocket/receiver.js
+++ /dev/null
@@ -1,444 +0,0 @@
-'use strict'
-
-const { Writable } = require('node:stream')
-const assert = require('node:assert')
-const { parserStates, opcodes, states, emptyBuffer, sentCloseFrameState } = require('./constants')
-const {
- isValidStatusCode,
- isValidOpcode,
- websocketMessageReceived,
- utf8Decode,
- isControlFrame,
- isTextBinaryFrame,
- isContinuationFrame
-} = require('./util')
-const { failWebsocketConnection } = require('./connection')
-const { WebsocketFrameSend } = require('./frame')
-const { PerMessageDeflate } = require('./permessage-deflate')
-
-// This code was influenced by ws released under the MIT license.
-// Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
-// Copyright (c) 2013 Arnout Kazemier and contributors
-// Copyright (c) 2016 Luigi Pinca and contributors
-
-class ByteParser extends Writable {
- #buffers = []
- #fragmentsBytes = 0
- #byteOffset = 0
- #loop = false
-
- #state = parserStates.INFO
-
- #info = {}
- #fragments = []
-
- /** @type {Map<string, PerMessageDeflate>} */
- #extensions
-
- /** @type {import('./websocket').Handler} */
- #handler
-
- constructor (handler, extensions) {
- super()
-
- this.#handler = handler
- this.#extensions = extensions == null ? new Map() : extensions
-
- if (this.#extensions.has('permessage-deflate')) {
- this.#extensions.set('permessage-deflate', new PerMessageDeflate(extensions))
- }
- }
-
- /**
- * @param {Buffer} chunk
- * @param {() => void} callback
- */
- _write (chunk, _, callback) {
- this.#buffers.push(chunk)
- this.#byteOffset += chunk.length
- this.#loop = true
-
- this.run(callback)
- }
-
- /**
- * Runs whenever a new chunk is received.
- * Callback is called whenever there are no more chunks buffering,
- * or not enough bytes are buffered to parse.
- */
- run (callback) {
- while (this.#loop) {
- if (this.#state === parserStates.INFO) {
- // If there aren't enough bytes to parse the payload length, etc.
- if (this.#byteOffset < 2) {
- return callback()
- }
-
- const buffer = this.consume(2)
- const fin = (buffer[0] & 0x80) !== 0
- const opcode = buffer[0] & 0x0F
- const masked = (buffer[1] & 0x80) === 0x80
-
- const fragmented = !fin && opcode !== opcodes.CONTINUATION
- const payloadLength = buffer[1] & 0x7F
-
- const rsv1 = buffer[0] & 0x40
- const rsv2 = buffer[0] & 0x20
- const rsv3 = buffer[0] & 0x10
-
- if (!isValidOpcode(opcode)) {
- failWebsocketConnection(this.#handler, 1002, 'Invalid opcode received')
- return callback()
- }
-
- if (masked) {
- failWebsocketConnection(this.#handler, 1002, 'Frame cannot be masked')
- return callback()
- }
-
- // MUST be 0 unless an extension is negotiated that defines meanings
- // for non-zero values. If a nonzero value is received and none of
- // the negotiated extensions defines the meaning of such a nonzero
- // value, the receiving endpoint MUST _Fail the WebSocket
- // Connection_.
- // This document allocates the RSV1 bit of the WebSocket header for
- // PMCEs and calls the bit the "Per-Message Compressed" bit. On a
- // WebSocket connection where a PMCE is in use, this bit indicates
- // whether a message is compressed or not.
- if (rsv1 !== 0 && !this.#extensions.has('permessage-deflate')) {
- failWebsocketConnection(this.#handler, 1002, 'Expected RSV1 to be clear.')
- return
- }
-
- if (rsv2 !== 0 || rsv3 !== 0) {
- failWebsocketConnection(this.#handler, 1002, 'RSV1, RSV2, RSV3 must be clear')
- return
- }
-
- if (fragmented && !isTextBinaryFrame(opcode)) {
- // Only text and binary frames can be fragmented
- failWebsocketConnection(this.#handler, 1002, 'Invalid frame type was fragmented.')
- return
- }
-
- // If we are already parsing a text/binary frame and do not receive either
- // a continuation frame or close frame, fail the connection.
- if (isTextBinaryFrame(opcode) && this.#fragments.length > 0) {
- failWebsocketConnection(this.#handler, 1002, 'Expected continuation frame')
- return
- }
-
- if (this.#info.fragmented && fragmented) {
- // A fragmented frame can't be fragmented itself
- failWebsocketConnection(this.#handler, 1002, 'Fragmented frame exceeded 125 bytes.')
- return
- }
-
- // "All control frames MUST have a payload length of 125 bytes or less
- // and MUST NOT be fragmented."
- if ((payloadLength > 125 || fragmented) && isControlFrame(opcode)) {
- failWebsocketConnection(this.#handler, 1002, 'Control frame either too large or fragmented')
- return
- }
-
- if (isContinuationFrame(opcode) && this.#fragments.length === 0 && !this.#info.compressed) {
- failWebsocketConnection(this.#handler, 1002, 'Unexpected continuation frame')
- return
- }
-
- if (payloadLength <= 125) {
- this.#info.payloadLength = payloadLength
- this.#state = parserStates.READ_DATA
- } else if (payloadLength === 126) {
- this.#state = parserStates.PAYLOADLENGTH_16
- } else if (payloadLength === 127) {
- this.#state = parserStates.PAYLOADLENGTH_64
- }
-
- if (isTextBinaryFrame(opcode)) {
- this.#info.binaryType = opcode
- this.#info.compressed = rsv1 !== 0
- }
-
- this.#info.opcode = opcode
- this.#info.masked = masked
- this.#info.fin = fin
- this.#info.fragmented = fragmented
- } else if (this.#state === parserStates.PAYLOADLENGTH_16) {
- if (this.#byteOffset < 2) {
- return callback()
- }
-
- const buffer = this.consume(2)
-
- this.#info.payloadLength = buffer.readUInt16BE(0)
- this.#state = parserStates.READ_DATA
- } else if (this.#state === parserStates.PAYLOADLENGTH_64) {
- if (this.#byteOffset < 8) {
- return callback()
- }
-
- const buffer = this.consume(8)
- const upper = buffer.readUInt32BE(0)
-
- // 2^31 is the maximum bytes an arraybuffer can contain
- // on 32-bit systems. Although, on 64-bit systems, this is
- // 2^53-1 bytes.
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Invalid_array_length
- // https://source.chromium.org/chromium/chromium/src/+/main:v8/src/common/globals.h;drc=1946212ac0100668f14eb9e2843bdd846e510a1e;bpv=1;bpt=1;l=1275
- // https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/js-array-buffer.h;l=34;drc=1946212ac0100668f14eb9e2843bdd846e510a1e
- if (upper > 2 ** 31 - 1) {
- failWebsocketConnection(this.#handler, 1009, 'Received payload length > 2^31 bytes.')
- return
- }
-
- const lower = buffer.readUInt32BE(4)
-
- this.#info.payloadLength = (upper << 8) + lower
- this.#state = parserStates.READ_DATA
- } else if (this.#state === parserStates.READ_DATA) {
- if (this.#byteOffset < this.#info.payloadLength) {
- return callback()
- }
-
- const body = this.consume(this.#info.payloadLength)
-
- if (isControlFrame(this.#info.opcode)) {
- this.#loop = this.parseControlFrame(body)
- this.#state = parserStates.INFO
- } else {
- if (!this.#info.compressed) {
- this.writeFragments(body)
-
- // If the frame is not fragmented, a message has been received.
- // If the frame is fragmented, it will terminate with a fin bit set
- // and an opcode of 0 (continuation), therefore we handle that when
- // parsing continuation frames, not here.
- if (!this.#info.fragmented && this.#info.fin) {
- websocketMessageReceived(this.#handler, this.#info.binaryType, this.consumeFragments())
- }
-
- this.#state = parserStates.INFO
- } else {
- this.#extensions.get('permessage-deflate').decompress(body, this.#info.fin, (error, data) => {
- if (error) {
- failWebsocketConnection(this.#handler, 1007, error.message)
- return
- }
-
- this.writeFragments(data)
-
- if (!this.#info.fin) {
- this.#state = parserStates.INFO
- this.#loop = true
- this.run(callback)
- return
- }
-
- websocketMessageReceived(this.#handler, this.#info.binaryType, this.consumeFragments())
-
- this.#loop = true
- this.#state = parserStates.INFO
- this.run(callback)
- })
-
- this.#loop = false
- break
- }
- }
- }
- }
- }
-
- /**
- * Take n bytes from the buffered Buffers
- * @param {number} n
- * @returns {Buffer}
- */
- consume (n) {
- if (n > this.#byteOffset) {
- throw new Error('Called consume() before buffers satiated.')
- } else if (n === 0) {
- return emptyBuffer
- }
-
- this.#byteOffset -= n
-
- const first = this.#buffers[0]
-
- if (first.length > n) {
- // replace with remaining buffer
- this.#buffers[0] = first.subarray(n, first.length)
- return first.subarray(0, n)
- } else if (first.length === n) {
- // prefect match
- return this.#buffers.shift()
- } else {
- let offset = 0
- // If Buffer.allocUnsafe is used, extra copies will be made because the offset is non-zero.
- const buffer = Buffer.allocUnsafeSlow(n)
- while (offset !== n) {
- const next = this.#buffers[0]
- const length = next.length
-
- if (length + offset === n) {
- buffer.set(this.#buffers.shift(), offset)
- break
- } else if (length + offset > n) {
- buffer.set(next.subarray(0, n - offset), offset)
- this.#buffers[0] = next.subarray(n - offset)
- break
- } else {
- buffer.set(this.#buffers.shift(), offset)
- offset += length
- }
- }
-
- return buffer
- }
- }
-
- writeFragments (fragment) {
- this.#fragmentsBytes += fragment.length
- this.#fragments.push(fragment)
- }
-
- consumeFragments () {
- const fragments = this.#fragments
-
- if (fragments.length === 1) {
- // single fragment
- this.#fragmentsBytes = 0
- return fragments.shift()
- }
-
- let offset = 0
- // If Buffer.allocUnsafe is used, extra copies will be made because the offset is non-zero.
- const output = Buffer.allocUnsafeSlow(this.#fragmentsBytes)
-
- for (let i = 0; i < fragments.length; ++i) {
- const buffer = fragments[i]
- output.set(buffer, offset)
- offset += buffer.length
- }
-
- this.#fragments = []
- this.#fragmentsBytes = 0
-
- return output
- }
-
- parseCloseBody (data) {
- assert(data.length !== 1)
-
- // https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.5
- /** @type {number|undefined} */
- let code
-
- if (data.length >= 2) {
- // _The WebSocket Connection Close Code_ is
- // defined as the status code (Section 7.4) contained in the first Close
- // control frame received by the application
- code = data.readUInt16BE(0)
- }
-
- if (code !== undefined && !isValidStatusCode(code)) {
- return { code: 1002, reason: 'Invalid status code', error: true }
- }
-
- // https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.6
- /** @type {Buffer} */
- let reason = data.subarray(2)
-
- // Remove BOM
- if (reason[0] === 0xEF && reason[1] === 0xBB && reason[2] === 0xBF) {
- reason = reason.subarray(3)
- }
-
- try {
- reason = utf8Decode(reason)
- } catch {
- return { code: 1007, reason: 'Invalid UTF-8', error: true }
- }
-
- return { code, reason, error: false }
- }
-
- /**
- * Parses control frames.
- * @param {Buffer} body
- */
- parseControlFrame (body) {
- const { opcode, payloadLength } = this.#info
-
- if (opcode === opcodes.CLOSE) {
- if (payloadLength === 1) {
- failWebsocketConnection(this.#handler, 1002, 'Received close frame with a 1-byte body.')
- return false
- }
-
- this.#info.closeInfo = this.parseCloseBody(body)
-
- if (this.#info.closeInfo.error) {
- const { code, reason } = this.#info.closeInfo
-
- failWebsocketConnection(this.#handler, code, reason)
- return false
- }
-
- // Upon receiving such a frame, the other peer sends a
- // Close frame in response, if it hasn't already sent one.
- if (!this.#handler.closeState.has(sentCloseFrameState.SENT) && !this.#handler.closeState.has(sentCloseFrameState.RECEIVED)) {
- // If an endpoint receives a Close frame and did not previously send a
- // Close frame, the endpoint MUST send a Close frame in response. (When
- // sending a Close frame in response, the endpoint typically echos the
- // status code it received.)
- let body = emptyBuffer
- if (this.#info.closeInfo.code) {
- body = Buffer.allocUnsafe(2)
- body.writeUInt16BE(this.#info.closeInfo.code, 0)
- }
- const closeFrame = new WebsocketFrameSend(body)
-
- this.#handler.socket.write(closeFrame.createFrame(opcodes.CLOSE))
- this.#handler.closeState.add(sentCloseFrameState.SENT)
- }
-
- // Upon either sending or receiving a Close control frame, it is said
- // that _The WebSocket Closing Handshake is Started_ and that the
- // WebSocket connection is in the CLOSING state.
- this.#handler.readyState = states.CLOSING
- this.#handler.closeState.add(sentCloseFrameState.RECEIVED)
-
- return false
- } else if (opcode === opcodes.PING) {
- // Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in
- // response, unless it already received a Close frame.
- // A Pong frame sent in response to a Ping frame must have identical
- // "Application data"
-
- if (!this.#handler.closeState.has(sentCloseFrameState.RECEIVED)) {
- const frame = new WebsocketFrameSend(body)
-
- this.#handler.socket.write(frame.createFrame(opcodes.PONG))
-
- this.#handler.onPing(body)
- }
- } else if (opcode === opcodes.PONG) {
- // A Pong frame MAY be sent unsolicited. This serves as a
- // unidirectional heartbeat. A response to an unsolicited Pong frame is
- // not expected.
- this.#handler.onPong(body)
- }
-
- return true
- }
-
- get closingInfo () {
- return this.#info.closeInfo
- }
-}
-
-module.exports = {
- ByteParser
-}
diff --git a/vanilla/node_modules/undici/lib/web/websocket/sender.js b/vanilla/node_modules/undici/lib/web/websocket/sender.js
deleted file mode 100644
index c647bf6..0000000
--- a/vanilla/node_modules/undici/lib/web/websocket/sender.js
+++ /dev/null
@@ -1,109 +0,0 @@
-'use strict'
-
-const { WebsocketFrameSend } = require('./frame')
-const { opcodes, sendHints } = require('./constants')
-const FixedQueue = require('../../dispatcher/fixed-queue')
-
-/**
- * @typedef {object} SendQueueNode
- * @property {Promise<void> | null} promise
- * @property {((...args: any[]) => any)} callback
- * @property {Buffer | null} frame
- */
-
-class SendQueue {
- /**
- * @type {FixedQueue}
- */
- #queue = new FixedQueue()
-
- /**
- * @type {boolean}
- */
- #running = false
-
- /** @type {import('node:net').Socket} */
- #socket
-
- constructor (socket) {
- this.#socket = socket
- }
-
- add (item, cb, hint) {
- if (hint !== sendHints.blob) {
- if (!this.#running) {
- // TODO(@tsctx): support fast-path for string on running
- if (hint === sendHints.text) {
- // special fast-path for string
- const { 0: head, 1: body } = WebsocketFrameSend.createFastTextFrame(item)
- this.#socket.cork()
- this.#socket.write(head)
- this.#socket.write(body, cb)
- this.#socket.uncork()
- } else {
- // direct writing
- this.#socket.write(createFrame(item, hint), cb)
- }
- } else {
- /** @type {SendQueueNode} */
- const node = {
- promise: null,
- callback: cb,
- frame: createFrame(item, hint)
- }
- this.#queue.push(node)
- }
- return
- }
-
- /** @type {SendQueueNode} */
- const node = {
- promise: item.arrayBuffer().then((ab) => {
- node.promise = null
- node.frame = createFrame(ab, hint)
- }),
- callback: cb,
- frame: null
- }
-
- this.#queue.push(node)
-
- if (!this.#running) {
- this.#run()
- }
- }
-
- async #run () {
- this.#running = true
- const queue = this.#queue
- while (!queue.isEmpty()) {
- const node = queue.shift()
- // wait pending promise
- if (node.promise !== null) {
- await node.promise
- }
- // write
- this.#socket.write(node.frame, node.callback)
- // cleanup
- node.callback = node.frame = null
- }
- this.#running = false
- }
-}
-
-function createFrame (data, hint) {
- return new WebsocketFrameSend(toBuffer(data, hint)).createFrame(hint === sendHints.text ? opcodes.TEXT : opcodes.BINARY)
-}
-
-function toBuffer (data, hint) {
- switch (hint) {
- case sendHints.text:
- case sendHints.typedArray:
- return new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
- case sendHints.arrayBuffer:
- case sendHints.blob:
- return new Uint8Array(data)
- }
-}
-
-module.exports = { SendQueue }
diff --git a/vanilla/node_modules/undici/lib/web/websocket/stream/websocketerror.js b/vanilla/node_modules/undici/lib/web/websocket/stream/websocketerror.js
deleted file mode 100644
index a34c521..0000000
--- a/vanilla/node_modules/undici/lib/web/websocket/stream/websocketerror.js
+++ /dev/null
@@ -1,104 +0,0 @@
-'use strict'
-
-const { webidl } = require('../../webidl')
-const { validateCloseCodeAndReason } = require('../util')
-const { kConstruct } = require('../../../core/symbols')
-const { kEnumerableProperty } = require('../../../core/util')
-
-function createInheritableDOMException () {
- // https://github.com/nodejs/node/issues/59677
- class Test extends DOMException {
- get reason () {
- return ''
- }
- }
-
- if (new Test().reason !== undefined) {
- return DOMException
- }
-
- return new Proxy(DOMException, {
- construct (target, args, newTarget) {
- const instance = Reflect.construct(target, args, target)
- Object.setPrototypeOf(instance, newTarget.prototype)
- return instance
- }
- })
-}
-
-class WebSocketError extends createInheritableDOMException() {
- #closeCode
- #reason
-
- constructor (message = '', init = undefined) {
- message = webidl.converters.DOMString(message, 'WebSocketError', 'message')
-
- // 1. Set this 's name to " WebSocketError ".
- // 2. Set this 's message to message .
- super(message, 'WebSocketError')
-
- if (init === kConstruct) {
- return
- } else if (init !== null) {
- init = webidl.converters.WebSocketCloseInfo(init)
- }
-
- // 3. Let code be init [" closeCode "] if it exists , or null otherwise.
- let code = init.closeCode ?? null
-
- // 4. Let reason be init [" reason "] if it exists , or the empty string otherwise.
- const reason = init.reason ?? ''
-
- // 5. Validate close code and reason with code and reason .
- validateCloseCodeAndReason(code, reason)
-
- // 6. If reason is non-empty, but code is not set, then set code to 1000 ("Normal Closure").
- if (reason.length !== 0 && code === null) {
- code = 1000
- }
-
- // 7. Set this 's closeCode to code .
- this.#closeCode = code
-
- // 8. Set this 's reason to reason .
- this.#reason = reason
- }
-
- get closeCode () {
- return this.#closeCode
- }
-
- get reason () {
- return this.#reason
- }
-
- /**
- * @param {string} message
- * @param {number|null} code
- * @param {string} reason
- */
- static createUnvalidatedWebSocketError (message, code, reason) {
- const error = new WebSocketError(message, kConstruct)
- error.#closeCode = code
- error.#reason = reason
- return error
- }
-}
-
-const { createUnvalidatedWebSocketError } = WebSocketError
-delete WebSocketError.createUnvalidatedWebSocketError
-
-Object.defineProperties(WebSocketError.prototype, {
- closeCode: kEnumerableProperty,
- reason: kEnumerableProperty,
- [Symbol.toStringTag]: {
- value: 'WebSocketError',
- writable: false,
- enumerable: false,
- configurable: true
- }
-})
-
-webidl.is.WebSocketError = webidl.util.MakeTypeAssertion(WebSocketError)
-
-module.exports = { WebSocketError, createUnvalidatedWebSocketError }
diff --git a/vanilla/node_modules/undici/lib/web/websocket/stream/websocketstream.js b/vanilla/node_modules/undici/lib/web/websocket/stream/websocketstream.js
deleted file mode 100644
index ce3be84..0000000
--- a/vanilla/node_modules/undici/lib/web/websocket/stream/websocketstream.js
+++ /dev/null
@@ -1,497 +0,0 @@
-'use strict'
-
-const { createDeferredPromise } = require('../../../util/promise')
-const { environmentSettingsObject } = require('../../fetch/util')
-const { states, opcodes, sentCloseFrameState } = require('../constants')
-const { webidl } = require('../../webidl')
-const { getURLRecord, isValidSubprotocol, isEstablished, utf8Decode } = require('../util')
-const { establishWebSocketConnection, failWebsocketConnection, closeWebSocketConnection } = require('../connection')
-const { channels } = require('../../../core/diagnostics')
-const { WebsocketFrameSend } = require('../frame')
-const { ByteParser } = require('../receiver')
-const { WebSocketError, createUnvalidatedWebSocketError } = require('./websocketerror')
-const { kEnumerableProperty } = require('../../../core/util')
-const { utf8DecodeBytes } = require('../../../encoding')
-
-let emittedExperimentalWarning = false
-
-class WebSocketStream {
- // Each WebSocketStream object has an associated url , which is a URL record .
- /** @type {URL} */
- #url
-
- // Each WebSocketStream object has an associated opened promise , which is a promise.
- /** @type {import('../../../util/promise').DeferredPromise} */
- #openedPromise
-
- // Each WebSocketStream object has an associated closed promise , which is a promise.
- /** @type {import('../../../util/promise').DeferredPromise} */
- #closedPromise
-
- // Each WebSocketStream object has an associated readable stream , which is a ReadableStream .
- /** @type {ReadableStream} */
- #readableStream
- /** @type {ReadableStreamDefaultController} */
- #readableStreamController
-
- // Each WebSocketStream object has an associated writable stream , which is a WritableStream .
- /** @type {WritableStream} */
- #writableStream
-
- // Each WebSocketStream object has an associated boolean handshake aborted , which is initially false.
- #handshakeAborted = false
-
- /** @type {import('../websocket').Handler} */
- #handler = {
- // https://whatpr.org/websockets/48/7b748d3...d5570f3.html#feedback-to-websocket-stream-from-the-protocol
- onConnectionEstablished: (response, extensions) => this.#onConnectionEstablished(response, extensions),
- onMessage: (opcode, data) => this.#onMessage(opcode, data),
- onParserError: (err) => failWebsocketConnection(this.#handler, null, err.message),
- onParserDrain: () => this.#handler.socket.resume(),
- onSocketData: (chunk) => {
- if (!this.#parser.write(chunk)) {
- this.#handler.socket.pause()
- }
- },
- onSocketError: (err) => {
- this.#handler.readyState = states.CLOSING
-
- if (channels.socketError.hasSubscribers) {
- channels.socketError.publish(err)
- }
-
- this.#handler.socket.destroy()
- },
- onSocketClose: () => this.#onSocketClose(),
- onPing: () => {},
- onPong: () => {},
-
- readyState: states.CONNECTING,
- socket: null,
- closeState: new Set(),
- controller: null,
- wasEverConnected: false
- }
-
- /** @type {import('../receiver').ByteParser} */
- #parser
-
- constructor (url, options = undefined) {
- if (!emittedExperimentalWarning) {
- process.emitWarning('WebSocketStream is experimental! Expect it to change at any time.', {
- code: 'UNDICI-WSS'
- })
- emittedExperimentalWarning = true
- }
-
- webidl.argumentLengthCheck(arguments, 1, 'WebSocket')
-
- url = webidl.converters.USVString(url)
- if (options !== null) {
- options = webidl.converters.WebSocketStreamOptions(options)
- }
-
- // 1. Let baseURL be this 's relevant settings object 's API base URL .
- const baseURL = environmentSettingsObject.settingsObject.baseUrl
-
- // 2. Let urlRecord be the result of getting a URL record given url and baseURL .
- const urlRecord = getURLRecord(url, baseURL)
-
- // 3. Let protocols be options [" protocols "] if it exists , otherwise an empty sequence.
- const protocols = options.protocols
-
- // 4. If any of the values in protocols occur more than once or otherwise fail to match the requirements for elements that comprise the value of ` Sec-WebSocket-Protocol ` fields as defined by The WebSocket Protocol , then throw a " SyntaxError " DOMException . [WSP]
- if (protocols.length !== new Set(protocols.map(p => p.toLowerCase())).size) {
- throw new DOMException('Invalid Sec-WebSocket-Protocol value', 'SyntaxError')
- }
-
- if (protocols.length > 0 && !protocols.every(p => isValidSubprotocol(p))) {
- throw new DOMException('Invalid Sec-WebSocket-Protocol value', 'SyntaxError')
- }
-
- // 5. Set this 's url to urlRecord .
- this.#url = urlRecord.toString()
-
- // 6. Set this 's opened promise and closed promise to new promises.
- this.#openedPromise = createDeferredPromise()
- this.#closedPromise = createDeferredPromise()
-
- // 7. Apply backpressure to the WebSocket.
- // TODO
-
- // 8. If options [" signal "] exists ,
- if (options.signal != null) {
- // 8.1. Let signal be options [" signal "].
- const signal = options.signal
-
- // 8.2. If signal is aborted , then reject this 's opened promise and closed promise with signal ’s abort reason
- // and return.
- if (signal.aborted) {
- this.#openedPromise.reject(signal.reason)
- this.#closedPromise.reject(signal.reason)
- return
- }
-
- // 8.3. Add the following abort steps to signal :
- signal.addEventListener('abort', () => {
- // 8.3.1. If the WebSocket connection is not yet established : [WSP]
- if (!isEstablished(this.#handler.readyState)) {
- // 8.3.1.1. Fail the WebSocket connection .
- failWebsocketConnection(this.#handler)
-
- // Set this 's ready state to CLOSING .
- this.#handler.readyState = states.CLOSING
-
- // Reject this 's opened promise and closed promise with signal ’s abort reason .
- this.#openedPromise.reject(signal.reason)
- this.#closedPromise.reject(signal.reason)
-
- // Set this 's handshake aborted to true.
- this.#handshakeAborted = true
- }
- }, { once: true })
- }
-
- // 9. Let client be this 's relevant settings object .
- const client = environmentSettingsObject.settingsObject
-
- // 10. Run this step in parallel :
- // 10.1. Establish a WebSocket connection given urlRecord , protocols , and client . [FETCH]
- this.#handler.controller = establishWebSocketConnection(
- urlRecord,
- protocols,
- client,
- this.#handler,
- options
- )
- }
-
- // The url getter steps are to return this 's url , serialized .
- get url () {
- return this.#url.toString()
- }
-
- // The opened getter steps are to return this 's opened promise .
- get opened () {
- return this.#openedPromise.promise
- }
-
- // The closed getter steps are to return this 's closed promise .
- get closed () {
- return this.#closedPromise.promise
- }
-
- // The close( closeInfo ) method steps are:
- close (closeInfo = undefined) {
- if (closeInfo !== null) {
- closeInfo = webidl.converters.WebSocketCloseInfo(closeInfo)
- }
-
- // 1. Let code be closeInfo [" closeCode "] if present, or null otherwise.
- const code = closeInfo.closeCode ?? null
-
- // 2. Let reason be closeInfo [" reason "].
- const reason = closeInfo.reason
-
- // 3. Close the WebSocket with this , code , and reason .
- closeWebSocketConnection(this.#handler, code, reason, true)
- }
-
- #write (chunk) {
- // See /websockets/stream/tentative/write.any.html
- chunk = webidl.converters.WebSocketStreamWrite(chunk)
-
- // 1. Let promise be a new promise created in stream ’s relevant realm .
- const promise = createDeferredPromise()
-
- // 2. Let data be null.
- let data = null
-
- // 3. Let opcode be null.
- let opcode = null
-
- // 4. If chunk is a BufferSource ,
- if (webidl.is.BufferSource(chunk)) {
- // 4.1. Set data to a copy of the bytes given chunk .
- data = new Uint8Array(ArrayBuffer.isView(chunk) ? new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength) : chunk.slice())
-
- // 4.2. Set opcode to a binary frame opcode.
- opcode = opcodes.BINARY
- } else {
- // 5. Otherwise,
-
- // 5.1. Let string be the result of converting chunk to an IDL USVString .
- // If this throws an exception, return a promise rejected with the exception.
- let string
-
- try {
- string = webidl.converters.DOMString(chunk)
- } catch (e) {
- promise.reject(e)
- return promise.promise
- }
-
- // 5.2. Set data to the result of UTF-8 encoding string .
- data = new TextEncoder().encode(string)
-
- // 5.3. Set opcode to a text frame opcode.
- opcode = opcodes.TEXT
- }
-
- // 6. In parallel,
- // 6.1. Wait until there is sufficient buffer space in stream to send the message.
-
- // 6.2. If the closing handshake has not yet started , Send a WebSocket Message to stream comprised of data using opcode .
- if (!this.#handler.closeState.has(sentCloseFrameState.SENT) && !this.#handler.closeState.has(sentCloseFrameState.RECEIVED)) {
- const frame = new WebsocketFrameSend(data)
-
- this.#handler.socket.write(frame.createFrame(opcode), () => {
- promise.resolve(undefined)
- })
- }
-
- // 6.3. Queue a global task on the WebSocket task source given stream ’s relevant global object to resolve promise with undefined.
- return promise.promise
- }
-
- /** @type {import('../websocket').Handler['onConnectionEstablished']} */
- #onConnectionEstablished (response, parsedExtensions) {
- this.#handler.socket = response.socket
-
- const parser = new ByteParser(this.#handler, parsedExtensions)
- parser.on('drain', () => this.#handler.onParserDrain())
- parser.on('error', (err) => this.#handler.onParserError(err))
-
- this.#parser = parser
-
- // 1. Change stream ’s ready state to OPEN (1).
- this.#handler.readyState = states.OPEN
-
- // 2. Set stream ’s was ever connected to true.
- // This is done in the opening handshake.
-
- // 3. Let extensions be the extensions in use .
- const extensions = parsedExtensions ?? ''
-
- // 4. Let protocol be the subprotocol in use .
- const protocol = response.headersList.get('sec-websocket-protocol') ?? ''
-
- // 5. Let pullAlgorithm be an action that pulls bytes from stream .
- // 6. Let cancelAlgorithm be an action that cancels stream with reason , given reason .
- // 7. Let readable be a new ReadableStream .
- // 8. Set up readable with pullAlgorithm and cancelAlgorithm .
- const readable = new ReadableStream({
- start: (controller) => {
- this.#readableStreamController = controller
- },
- pull (controller) {
- let chunk
- while (controller.desiredSize > 0 && (chunk = response.socket.read()) !== null) {
- controller.enqueue(chunk)
- }
- },
- cancel: (reason) => this.#cancel(reason)
- })
-
- // 9. Let writeAlgorithm be an action that writes chunk to stream , given chunk .
- // 10. Let closeAlgorithm be an action that closes stream .
- // 11. Let abortAlgorithm be an action that aborts stream with reason , given reason .
- // 12. Let writable be a new WritableStream .
- // 13. Set up writable with writeAlgorithm , closeAlgorithm , and abortAlgorithm .
- const writable = new WritableStream({
- write: (chunk) => this.#write(chunk),
- close: () => closeWebSocketConnection(this.#handler, null, null),
- abort: (reason) => this.#closeUsingReason(reason)
- })
-
- // Set stream ’s readable stream to readable .
- this.#readableStream = readable
-
- // Set stream ’s writable stream to writable .
- this.#writableStream = writable
-
- // Resolve stream ’s opened promise with WebSocketOpenInfo «[ " extensions " → extensions , " protocol " → protocol , " readable " → readable , " writable " → writable ]».
- this.#openedPromise.resolve({
- extensions,
- protocol,
- readable,
- writable
- })
- }
-
- /** @type {import('../websocket').Handler['onMessage']} */
- #onMessage (type, data) {
- // 1. If stream’s ready state is not OPEN (1), then return.
- if (this.#handler.readyState !== states.OPEN) {
- return
- }
-
- // 2. Let chunk be determined by switching on type:
- // - type indicates that the data is Text
- // a new DOMString containing data
- // - type indicates that the data is Binary
- // a new Uint8Array object, created in the relevant Realm of the
- // WebSocketStream object, whose contents are data
- let chunk
-
- if (type === opcodes.TEXT) {
- try {
- chunk = utf8Decode(data)
- } catch {
- failWebsocketConnection(this.#handler, 'Received invalid UTF-8 in text frame.')
- return
- }
- } else if (type === opcodes.BINARY) {
- chunk = new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
- }
-
- // 3. Enqueue chunk into stream’s readable stream.
- this.#readableStreamController.enqueue(chunk)
-
- // 4. Apply backpressure to the WebSocket.
- }
-
- /** @type {import('../websocket').Handler['onSocketClose']} */
- #onSocketClose () {
- const wasClean =
- this.#handler.closeState.has(sentCloseFrameState.SENT) &&
- this.#handler.closeState.has(sentCloseFrameState.RECEIVED)
-
- // 1. Change the ready state to CLOSED (3).
- this.#handler.readyState = states.CLOSED
-
- // 2. If stream ’s handshake aborted is true, then return.
- if (this.#handshakeAborted) {
- return
- }
-
- // 3. If stream ’s was ever connected is false, then reject stream ’s opened promise with a new WebSocketError.
- if (!this.#handler.wasEverConnected) {
- this.#openedPromise.reject(new WebSocketError('Socket never opened'))
- }
-
- const result = this.#parser?.closingInfo
-
- // 4. Let code be the WebSocket connection close code .
- // https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.5
- // If this Close control frame contains no status code, _The WebSocket
- // Connection Close Code_ is considered to be 1005. If _The WebSocket
- // Connection is Closed_ and no Close control frame was received by the
- // endpoint (such as could occur if the underlying transport connection
- // is lost), _The WebSocket Connection Close Code_ is considered to be
- // 1006.
- let code = result?.code ?? 1005
-
- if (!this.#handler.closeState.has(sentCloseFrameState.SENT) && !this.#handler.closeState.has(sentCloseFrameState.RECEIVED)) {
- code = 1006
- }
-
- // 5. Let reason be the result of applying UTF-8 decode without BOM to the WebSocket connection close reason .
- const reason = result?.reason == null ? '' : utf8DecodeBytes(Buffer.from(result.reason))
-
- // 6. If the connection was closed cleanly ,
- if (wasClean) {
- // 6.1. Close stream ’s readable stream .
- this.#readableStreamController.close()
-
- // 6.2. Error stream ’s writable stream with an " InvalidStateError " DOMException indicating that a closed WebSocketStream cannot be written to.
- if (!this.#writableStream.locked) {
- this.#writableStream.abort(new DOMException('A closed WebSocketStream cannot be written to', 'InvalidStateError'))
- }
-
- // 6.3. Resolve stream ’s closed promise with WebSocketCloseInfo «[ " closeCode " → code , " reason " → reason ]».
- this.#closedPromise.resolve({
- closeCode: code,
- reason
- })
- } else {
- // 7. Otherwise,
-
- // 7.1. Let error be a new WebSocketError whose closeCode is code and reason is reason .
- const error = createUnvalidatedWebSocketError('unclean close', code, reason)
-
- // 7.2. Error stream ’s readable stream with error .
- this.#readableStreamController?.error(error)
-
- // 7.3. Error stream ’s writable stream with error .
- this.#writableStream?.abort(error)
-
- // 7.4. Reject stream ’s closed promise with error .
- this.#closedPromise.reject(error)
- }
- }
-
- #closeUsingReason (reason) {
- // 1. Let code be null.
- let code = null
-
- // 2. Let reasonString be the empty string.
- let reasonString = ''
-
- // 3. If reason implements WebSocketError ,
- if (webidl.is.WebSocketError(reason)) {
- // 3.1. Set code to reason ’s closeCode .
- code = reason.closeCode
-
- // 3.2. Set reasonString to reason ’s reason .
- reasonString = reason.reason
- }
-
- // 4. Close the WebSocket with stream , code , and reasonString . If this throws an exception,
- // discard code and reasonString and close the WebSocket with stream .
- closeWebSocketConnection(this.#handler, code, reasonString)
- }
-
- // To cancel a WebSocketStream stream given reason , close using reason giving stream and reason .
- #cancel (reason) {
- this.#closeUsingReason(reason)
- }
-}
-
-Object.defineProperties(WebSocketStream.prototype, {
- url: kEnumerableProperty,
- opened: kEnumerableProperty,
- closed: kEnumerableProperty,
- close: kEnumerableProperty,
- [Symbol.toStringTag]: {
- value: 'WebSocketStream',
- writable: false,
- enumerable: false,
- configurable: true
- }
-})
-
-webidl.converters.WebSocketStreamOptions = webidl.dictionaryConverter([
- {
- key: 'protocols',
- converter: webidl.sequenceConverter(webidl.converters.USVString),
- defaultValue: () => []
- },
- {
- key: 'signal',
- converter: webidl.nullableConverter(webidl.converters.AbortSignal),
- defaultValue: () => null
- }
-])
-
-webidl.converters.WebSocketCloseInfo = webidl.dictionaryConverter([
- {
- key: 'closeCode',
- converter: (V) => webidl.converters['unsigned short'](V, webidl.attributes.EnforceRange)
- },
- {
- key: 'reason',
- converter: webidl.converters.USVString,
- defaultValue: () => ''
- }
-])
-
-webidl.converters.WebSocketStreamWrite = function (V) {
- if (typeof V === 'string') {
- return webidl.converters.USVString(V)
- }
-
- return webidl.converters.BufferSource(V)
-}
-
-module.exports = { WebSocketStream }
diff --git a/vanilla/node_modules/undici/lib/web/websocket/util.js b/vanilla/node_modules/undici/lib/web/websocket/util.js
deleted file mode 100644
index eaa5e7a..0000000
--- a/vanilla/node_modules/undici/lib/web/websocket/util.js
+++ /dev/null
@@ -1,339 +0,0 @@
-'use strict'
-
-const { states, opcodes } = require('./constants')
-const { isUtf8 } = require('node:buffer')
-const { removeHTTPWhitespace } = require('../fetch/data-url')
-const { collectASequenceOfCodePointsFast } = require('../infra')
-
-/**
- * @param {number} readyState
- * @returns {boolean}
- */
-function isConnecting (readyState) {
- // If the WebSocket connection is not yet established, and the connection
- // is not yet closed, then the WebSocket connection is in the CONNECTING state.
- return readyState === states.CONNECTING
-}
-
-/**
- * @param {number} readyState
- * @returns {boolean}
- */
-function isEstablished (readyState) {
- // If the server's response is validated as provided for above, it is
- // said that _The WebSocket Connection is Established_ and that the
- // WebSocket Connection is in the OPEN state.
- return readyState === states.OPEN
-}
-
-/**
- * @param {number} readyState
- * @returns {boolean}
- */
-function isClosing (readyState) {
- // Upon either sending or receiving a Close control frame, it is said
- // that _The WebSocket Closing Handshake is Started_ and that the
- // WebSocket connection is in the CLOSING state.
- return readyState === states.CLOSING
-}
-
-/**
- * @param {number} readyState
- * @returns {boolean}
- */
-function isClosed (readyState) {
- return readyState === states.CLOSED
-}
-
-/**
- * @see https://dom.spec.whatwg.org/#concept-event-fire
- * @param {string} e
- * @param {EventTarget} target
- * @param {(...args: ConstructorParameters<typeof Event>) => Event} eventFactory
- * @param {EventInit | undefined} eventInitDict
- * @returns {void}
- */
-function fireEvent (e, target, eventFactory = (type, init) => new Event(type, init), eventInitDict = {}) {
- // 1. If eventConstructor is not given, then let eventConstructor be Event.
-
- // 2. Let event be the result of creating an event given eventConstructor,
- // in the relevant realm of target.
- // 3. Initialize event’s type attribute to e.
- const event = eventFactory(e, eventInitDict)
-
- // 4. Initialize any other IDL attributes of event as described in the
- // invocation of this algorithm.
-
- // 5. Return the result of dispatching event at target, with legacy target
- // override flag set if set.
- target.dispatchEvent(event)
-}
-
-/**
- * @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol
- * @param {import('./websocket').Handler} handler
- * @param {number} type Opcode
- * @param {Buffer} data application data
- * @returns {void}
- */
-function websocketMessageReceived (handler, type, data) {
- handler.onMessage(type, data)
-}
-
-/**
- * @param {Buffer} buffer
- * @returns {ArrayBuffer}
- */
-function toArrayBuffer (buffer) {
- if (buffer.byteLength === buffer.buffer.byteLength) {
- return buffer.buffer
- }
- return new Uint8Array(buffer).buffer
-}
-
-/**
- * @see https://datatracker.ietf.org/doc/html/rfc6455
- * @see https://datatracker.ietf.org/doc/html/rfc2616
- * @see https://bugs.chromium.org/p/chromium/issues/detail?id=398407
- * @param {string} protocol
- * @returns {boolean}
- */
-function isValidSubprotocol (protocol) {
- // If present, this value indicates one
- // or more comma-separated subprotocol the client wishes to speak,
- // ordered by preference. The elements that comprise this value
- // MUST be non-empty strings with characters in the range U+0021 to
- // U+007E not including separator characters as defined in
- // [RFC2616] and MUST all be unique strings.
- if (protocol.length === 0) {
- return false
- }
-
- for (let i = 0; i < protocol.length; ++i) {
- const code = protocol.charCodeAt(i)
-
- if (
- code < 0x21 || // CTL, contains SP (0x20) and HT (0x09)
- code > 0x7E ||
- code === 0x22 || // "
- code === 0x28 || // (
- code === 0x29 || // )
- code === 0x2C || // ,
- code === 0x2F || // /
- code === 0x3A || // :
- code === 0x3B || // ;
- code === 0x3C || // <
- code === 0x3D || // =
- code === 0x3E || // >
- code === 0x3F || // ?
- code === 0x40 || // @
- code === 0x5B || // [
- code === 0x5C || // \
- code === 0x5D || // ]
- code === 0x7B || // {
- code === 0x7D // }
- ) {
- return false
- }
- }
-
- return true
-}
-
-/**
- * @see https://datatracker.ietf.org/doc/html/rfc6455#section-7-4
- * @param {number} code
- * @returns {boolean}
- */
-function isValidStatusCode (code) {
- if (code >= 1000 && code < 1015) {
- return (
- code !== 1004 && // reserved
- code !== 1005 && // "MUST NOT be set as a status code"
- code !== 1006 // "MUST NOT be set as a status code"
- )
- }
-
- return code >= 3000 && code <= 4999
-}
-
-/**
- * @see https://datatracker.ietf.org/doc/html/rfc6455#section-5.5
- * @param {number} opcode
- * @returns {boolean}
- */
-function isControlFrame (opcode) {
- return (
- opcode === opcodes.CLOSE ||
- opcode === opcodes.PING ||
- opcode === opcodes.PONG
- )
-}
-
-/**
- * @param {number} opcode
- * @returns {boolean}
- */
-function isContinuationFrame (opcode) {
- return opcode === opcodes.CONTINUATION
-}
-
-/**
- * @param {number} opcode
- * @returns {boolean}
- */
-function isTextBinaryFrame (opcode) {
- return opcode === opcodes.TEXT || opcode === opcodes.BINARY
-}
-
-/**
- *
- * @param {number} opcode
- * @returns {boolean}
- */
-function isValidOpcode (opcode) {
- return isTextBinaryFrame(opcode) || isContinuationFrame(opcode) || isControlFrame(opcode)
-}
-
-/**
- * Parses a Sec-WebSocket-Extensions header value.
- * @param {string} extensions
- * @returns {Map<string, string>}
- */
-// TODO(@Uzlopak, @KhafraDev): make compliant https://datatracker.ietf.org/doc/html/rfc6455#section-9.1
-function parseExtensions (extensions) {
- const position = { position: 0 }
- const extensionList = new Map()
-
- while (position.position < extensions.length) {
- const pair = collectASequenceOfCodePointsFast(';', extensions, position)
- const [name, value = ''] = pair.split('=', 2)
-
- extensionList.set(
- removeHTTPWhitespace(name, true, false),
- removeHTTPWhitespace(value, false, true)
- )
-
- position.position++
- }
-
- return extensionList
-}
-
-/**
- * @see https://www.rfc-editor.org/rfc/rfc7692#section-7.1.2.2
- * @description "client-max-window-bits = 1*DIGIT"
- * @param {string} value
- * @returns {boolean}
- */
-function isValidClientWindowBits (value) {
- for (let i = 0; i < value.length; i++) {
- const byte = value.charCodeAt(i)
-
- if (byte < 0x30 || byte > 0x39) {
- return false
- }
- }
-
- return true
-}
-
-/**
- * @see https://whatpr.org/websockets/48/7b748d3...d5570f3.html#get-a-url-record
- * @param {string} url
- * @param {string} [baseURL]
- */
-function getURLRecord (url, baseURL) {
- // 1. Let urlRecord be the result of applying the URL parser to url with baseURL .
- // 2. If urlRecord is failure, then throw a " SyntaxError " DOMException .
- let urlRecord
-
- try {
- urlRecord = new URL(url, baseURL)
- } catch (e) {
- throw new DOMException(e, 'SyntaxError')
- }
-
- // 3. If urlRecord ’s scheme is " http ", then set urlRecord ’s scheme to " ws ".
- // 4. Otherwise, if urlRecord ’s scheme is " https ", set urlRecord ’s scheme to " wss ".
- if (urlRecord.protocol === 'http:') {
- urlRecord.protocol = 'ws:'
- } else if (urlRecord.protocol === 'https:') {
- urlRecord.protocol = 'wss:'
- }
-
- // 5. If urlRecord ’s scheme is not " ws " or " wss ", then throw a " SyntaxError " DOMException .
- if (urlRecord.protocol !== 'ws:' && urlRecord.protocol !== 'wss:') {
- throw new DOMException('expected a ws: or wss: url', 'SyntaxError')
- }
-
- // If urlRecord ’s fragment is non-null, then throw a " SyntaxError " DOMException .
- if (urlRecord.hash.length || urlRecord.href.endsWith('#')) {
- throw new DOMException('hash', 'SyntaxError')
- }
-
- // Return urlRecord .
- return urlRecord
-}
-
-// https://whatpr.org/websockets/48.html#validate-close-code-and-reason
-function validateCloseCodeAndReason (code, reason) {
- // 1. If code is not null, but is neither an integer equal to
- // 1000 nor an integer in the range 3000 to 4999, inclusive,
- // throw an "InvalidAccessError" DOMException.
- if (code !== null) {
- if (code !== 1000 && (code < 3000 || code > 4999)) {
- throw new DOMException('invalid code', 'InvalidAccessError')
- }
- }
-
- // 2. If reason is not null, then:
- if (reason !== null) {
- // 2.1. Let reasonBytes be the result of UTF-8 encoding reason.
- // 2.2. If reasonBytes is longer than 123 bytes, then throw a
- // "SyntaxError" DOMException.
- const reasonBytesLength = Buffer.byteLength(reason)
-
- if (reasonBytesLength > 123) {
- throw new DOMException(`Reason must be less than 123 bytes; received ${reasonBytesLength}`, 'SyntaxError')
- }
- }
-}
-
-/**
- * Converts a Buffer to utf-8, even on platforms without icu.
- * @type {(buffer: Buffer) => string}
- */
-const utf8Decode = (() => {
- if (typeof process.versions.icu === 'string') {
- const fatalDecoder = new TextDecoder('utf-8', { fatal: true })
- return fatalDecoder.decode.bind(fatalDecoder)
- }
- return function (buffer) {
- if (isUtf8(buffer)) {
- return buffer.toString('utf-8')
- }
- throw new TypeError('Invalid utf-8 received.')
- }
-})()
-
-module.exports = {
- isConnecting,
- isEstablished,
- isClosing,
- isClosed,
- fireEvent,
- isValidSubprotocol,
- isValidStatusCode,
- websocketMessageReceived,
- utf8Decode,
- isControlFrame,
- isContinuationFrame,
- isTextBinaryFrame,
- isValidOpcode,
- parseExtensions,
- isValidClientWindowBits,
- toArrayBuffer,
- getURLRecord,
- validateCloseCodeAndReason
-}
diff --git a/vanilla/node_modules/undici/lib/web/websocket/websocket.js b/vanilla/node_modules/undici/lib/web/websocket/websocket.js
deleted file mode 100644
index 3314976..0000000
--- a/vanilla/node_modules/undici/lib/web/websocket/websocket.js
+++ /dev/null
@@ -1,739 +0,0 @@
-'use strict'
-
-const { isArrayBuffer } = require('node:util/types')
-const { webidl } = require('../webidl')
-const { URLSerializer } = require('../fetch/data-url')
-const { environmentSettingsObject } = require('../fetch/util')
-const { staticPropertyDescriptors, states, sentCloseFrameState, sendHints, opcodes } = require('./constants')
-const {
- isConnecting,
- isEstablished,
- isClosing,
- isClosed,
- isValidSubprotocol,
- fireEvent,
- utf8Decode,
- toArrayBuffer,
- getURLRecord
-} = require('./util')
-const { establishWebSocketConnection, closeWebSocketConnection, failWebsocketConnection } = require('./connection')
-const { ByteParser } = require('./receiver')
-const { kEnumerableProperty } = require('../../core/util')
-const { getGlobalDispatcher } = require('../../global')
-const { ErrorEvent, CloseEvent, createFastMessageEvent } = require('./events')
-const { SendQueue } = require('./sender')
-const { WebsocketFrameSend } = require('./frame')
-const { channels } = require('../../core/diagnostics')
-
-/**
- * @typedef {object} Handler
- * @property {(response: any, extensions?: string[]) => void} onConnectionEstablished
- * @property {(opcode: number, data: Buffer) => void} onMessage
- * @property {(error: Error) => void} onParserError
- * @property {() => void} onParserDrain
- * @property {(chunk: Buffer) => void} onSocketData
- * @property {(err: Error) => void} onSocketError
- * @property {() => void} onSocketClose
- * @property {(body: Buffer) => void} onPing
- * @property {(body: Buffer) => void} onPong
- *
- * @property {number} readyState
- * @property {import('stream').Duplex} socket
- * @property {Set<number>} closeState
- * @property {import('../fetch/index').Fetch} controller
- * @property {boolean} [wasEverConnected=false]
- */
-
-// https://websockets.spec.whatwg.org/#interface-definition
-class WebSocket extends EventTarget {
- #events = {
- open: null,
- error: null,
- close: null,
- message: null
- }
-
- #bufferedAmount = 0
- #protocol = ''
- #extensions = ''
-
- /** @type {SendQueue} */
- #sendQueue
-
- /** @type {Handler} */
- #handler = {
- onConnectionEstablished: (response, extensions) => this.#onConnectionEstablished(response, extensions),
- onMessage: (opcode, data) => this.#onMessage(opcode, data),
- onParserError: (err) => failWebsocketConnection(this.#handler, null, err.message),
- onParserDrain: () => this.#onParserDrain(),
- onSocketData: (chunk) => {
- if (!this.#parser.write(chunk)) {
- this.#handler.socket.pause()
- }
- },
- onSocketError: (err) => {
- this.#handler.readyState = states.CLOSING
-
- if (channels.socketError.hasSubscribers) {
- channels.socketError.publish(err)
- }
-
- this.#handler.socket.destroy()
- },
- onSocketClose: () => this.#onSocketClose(),
- onPing: (body) => {
- if (channels.ping.hasSubscribers) {
- channels.ping.publish({
- payload: body,
- websocket: this
- })
- }
- },
- onPong: (body) => {
- if (channels.pong.hasSubscribers) {
- channels.pong.publish({
- payload: body,
- websocket: this
- })
- }
- },
-
- readyState: states.CONNECTING,
- socket: null,
- closeState: new Set(),
- controller: null,
- wasEverConnected: false
- }
-
- #url
- #binaryType
- /** @type {import('./receiver').ByteParser} */
- #parser
-
- /**
- * @param {string} url
- * @param {string|string[]} protocols
- */
- constructor (url, protocols = []) {
- super()
-
- webidl.util.markAsUncloneable(this)
-
- const prefix = 'WebSocket constructor'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- const options = webidl.converters['DOMString or sequence<DOMString> or WebSocketInit'](protocols, prefix, 'options')
-
- url = webidl.converters.USVString(url)
- protocols = options.protocols
-
- // 1. Let baseURL be this's relevant settings object's API base URL.
- const baseURL = environmentSettingsObject.settingsObject.baseUrl
-
- // 2. Let urlRecord be the result of getting a URL record given url and baseURL.
- const urlRecord = getURLRecord(url, baseURL)
-
- // 3. If protocols is a string, set protocols to a sequence consisting
- // of just that string.
- if (typeof protocols === 'string') {
- protocols = [protocols]
- }
-
- // 4. If any of the values in protocols occur more than once or otherwise
- // fail to match the requirements for elements that comprise the value
- // of `Sec-WebSocket-Protocol` fields as defined by The WebSocket
- // protocol, then throw a "SyntaxError" DOMException.
- if (protocols.length !== new Set(protocols.map(p => p.toLowerCase())).size) {
- throw new DOMException('Invalid Sec-WebSocket-Protocol value', 'SyntaxError')
- }
-
- if (protocols.length > 0 && !protocols.every(p => isValidSubprotocol(p))) {
- throw new DOMException('Invalid Sec-WebSocket-Protocol value', 'SyntaxError')
- }
-
- // 5. Set this's url to urlRecord.
- this.#url = new URL(urlRecord.href)
-
- // 6. Let client be this's relevant settings object.
- const client = environmentSettingsObject.settingsObject
-
- // 7. Run this step in parallel:
- // 7.1. Establish a WebSocket connection given urlRecord, protocols,
- // and client.
- this.#handler.controller = establishWebSocketConnection(
- urlRecord,
- protocols,
- client,
- this.#handler,
- options
- )
-
- // Each WebSocket object has an associated ready state, which is a
- // number representing the state of the connection. Initially it must
- // be CONNECTING (0).
- this.#handler.readyState = WebSocket.CONNECTING
-
- // The extensions attribute must initially return the empty string.
-
- // The protocol attribute must initially return the empty string.
-
- // Each WebSocket object has an associated binary type, which is a
- // BinaryType. Initially it must be "blob".
- this.#binaryType = 'blob'
- }
-
- /**
- * @see https://websockets.spec.whatwg.org/#dom-websocket-close
- * @param {number|undefined} code
- * @param {string|undefined} reason
- */
- close (code = undefined, reason = undefined) {
- webidl.brandCheck(this, WebSocket)
-
- const prefix = 'WebSocket.close'
-
- if (code !== undefined) {
- code = webidl.converters['unsigned short'](code, prefix, 'code', webidl.attributes.Clamp)
- }
-
- if (reason !== undefined) {
- reason = webidl.converters.USVString(reason)
- }
-
- // 1. If code is the special value "missing", then set code to null.
- code ??= null
-
- // 2. If reason is the special value "missing", then set reason to the empty string.
- reason ??= ''
-
- // 3. Close the WebSocket with this, code, and reason.
- closeWebSocketConnection(this.#handler, code, reason, true)
- }
-
- /**
- * @see https://websockets.spec.whatwg.org/#dom-websocket-send
- * @param {NodeJS.TypedArray|ArrayBuffer|Blob|string} data
- */
- send (data) {
- webidl.brandCheck(this, WebSocket)
-
- const prefix = 'WebSocket.send'
- webidl.argumentLengthCheck(arguments, 1, prefix)
-
- data = webidl.converters.WebSocketSendData(data, prefix, 'data')
-
- // 1. If this's ready state is CONNECTING, then throw an
- // "InvalidStateError" DOMException.
- if (isConnecting(this.#handler.readyState)) {
- throw new DOMException('Sent before connected.', 'InvalidStateError')
- }
-
- // 2. Run the appropriate set of steps from the following list:
- // https://datatracker.ietf.org/doc/html/rfc6455#section-6.1
- // https://datatracker.ietf.org/doc/html/rfc6455#section-5.2
-
- if (!isEstablished(this.#handler.readyState) || isClosing(this.#handler.readyState)) {
- return
- }
-
- // If data is a string
- if (typeof data === 'string') {
- // If the WebSocket connection is established and the WebSocket
- // closing handshake has not yet started, then the user agent
- // must send a WebSocket Message comprised of the data argument
- // using a text frame opcode; if the data cannot be sent, e.g.
- // because it would need to be buffered but the buffer is full,
- // the user agent must flag the WebSocket as full and then close
- // the WebSocket connection. Any invocation of this method with a
- // string argument that does not throw an exception must increase
- // the bufferedAmount attribute by the number of bytes needed to
- // express the argument as UTF-8.
-
- const buffer = Buffer.from(data)
-
- this.#bufferedAmount += buffer.byteLength
- this.#sendQueue.add(buffer, () => {
- this.#bufferedAmount -= buffer.byteLength
- }, sendHints.text)
- } else if (isArrayBuffer(data)) {
- // If the WebSocket connection is established, and the WebSocket
- // closing handshake has not yet started, then the user agent must
- // send a WebSocket Message comprised of data using a binary frame
- // opcode; if the data cannot be sent, e.g. because it would need
- // to be buffered but the buffer is full, the user agent must flag
- // the WebSocket as full and then close the WebSocket connection.
- // The data to be sent is the data stored in the buffer described
- // by the ArrayBuffer object. Any invocation of this method with an
- // ArrayBuffer argument that does not throw an exception must
- // increase the bufferedAmount attribute by the length of the
- // ArrayBuffer in bytes.
-
- this.#bufferedAmount += data.byteLength
- this.#sendQueue.add(data, () => {
- this.#bufferedAmount -= data.byteLength
- }, sendHints.arrayBuffer)
- } else if (ArrayBuffer.isView(data)) {
- // If the WebSocket connection is established, and the WebSocket
- // closing handshake has not yet started, then the user agent must
- // send a WebSocket Message comprised of data using a binary frame
- // opcode; if the data cannot be sent, e.g. because it would need to
- // be buffered but the buffer is full, the user agent must flag the
- // WebSocket as full and then close the WebSocket connection. The
- // data to be sent is the data stored in the section of the buffer
- // described by the ArrayBuffer object that data references. Any
- // invocation of this method with this kind of argument that does
- // not throw an exception must increase the bufferedAmount attribute
- // by the length of data’s buffer in bytes.
-
- this.#bufferedAmount += data.byteLength
- this.#sendQueue.add(data, () => {
- this.#bufferedAmount -= data.byteLength
- }, sendHints.typedArray)
- } else if (webidl.is.Blob(data)) {
- // If the WebSocket connection is established, and the WebSocket
- // closing handshake has not yet started, then the user agent must
- // send a WebSocket Message comprised of data using a binary frame
- // opcode; if the data cannot be sent, e.g. because it would need to
- // be buffered but the buffer is full, the user agent must flag the
- // WebSocket as full and then close the WebSocket connection. The data
- // to be sent is the raw data represented by the Blob object. Any
- // invocation of this method with a Blob argument that does not throw
- // an exception must increase the bufferedAmount attribute by the size
- // of the Blob object’s raw data, in bytes.
-
- this.#bufferedAmount += data.size
- this.#sendQueue.add(data, () => {
- this.#bufferedAmount -= data.size
- }, sendHints.blob)
- }
- }
-
- get readyState () {
- webidl.brandCheck(this, WebSocket)
-
- // The readyState getter steps are to return this's ready state.
- return this.#handler.readyState
- }
-
- get bufferedAmount () {
- webidl.brandCheck(this, WebSocket)
-
- return this.#bufferedAmount
- }
-
- get url () {
- webidl.brandCheck(this, WebSocket)
-
- // The url getter steps are to return this's url, serialized.
- return URLSerializer(this.#url)
- }
-
- get extensions () {
- webidl.brandCheck(this, WebSocket)
-
- return this.#extensions
- }
-
- get protocol () {
- webidl.brandCheck(this, WebSocket)
-
- return this.#protocol
- }
-
- get onopen () {
- webidl.brandCheck(this, WebSocket)
-
- return this.#events.open
- }
-
- set onopen (fn) {
- webidl.brandCheck(this, WebSocket)
-
- if (this.#events.open) {
- this.removeEventListener('open', this.#events.open)
- }
-
- const listener = webidl.converters.EventHandlerNonNull(fn)
-
- if (listener !== null) {
- this.addEventListener('open', listener)
- this.#events.open = fn
- } else {
- this.#events.open = null
- }
- }
-
- get onerror () {
- webidl.brandCheck(this, WebSocket)
-
- return this.#events.error
- }
-
- set onerror (fn) {
- webidl.brandCheck(this, WebSocket)
-
- if (this.#events.error) {
- this.removeEventListener('error', this.#events.error)
- }
-
- const listener = webidl.converters.EventHandlerNonNull(fn)
-
- if (listener !== null) {
- this.addEventListener('error', listener)
- this.#events.error = fn
- } else {
- this.#events.error = null
- }
- }
-
- get onclose () {
- webidl.brandCheck(this, WebSocket)
-
- return this.#events.close
- }
-
- set onclose (fn) {
- webidl.brandCheck(this, WebSocket)
-
- if (this.#events.close) {
- this.removeEventListener('close', this.#events.close)
- }
-
- const listener = webidl.converters.EventHandlerNonNull(fn)
-
- if (listener !== null) {
- this.addEventListener('close', listener)
- this.#events.close = fn
- } else {
- this.#events.close = null
- }
- }
-
- get onmessage () {
- webidl.brandCheck(this, WebSocket)
-
- return this.#events.message
- }
-
- set onmessage (fn) {
- webidl.brandCheck(this, WebSocket)
-
- if (this.#events.message) {
- this.removeEventListener('message', this.#events.message)
- }
-
- const listener = webidl.converters.EventHandlerNonNull(fn)
-
- if (listener !== null) {
- this.addEventListener('message', listener)
- this.#events.message = fn
- } else {
- this.#events.message = null
- }
- }
-
- get binaryType () {
- webidl.brandCheck(this, WebSocket)
-
- return this.#binaryType
- }
-
- set binaryType (type) {
- webidl.brandCheck(this, WebSocket)
-
- if (type !== 'blob' && type !== 'arraybuffer') {
- this.#binaryType = 'blob'
- } else {
- this.#binaryType = type
- }
- }
-
- /**
- * @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol
- */
- #onConnectionEstablished (response, parsedExtensions) {
- // processResponse is called when the "response’s header list has been received and initialized."
- // once this happens, the connection is open
- this.#handler.socket = response.socket
-
- const parser = new ByteParser(this.#handler, parsedExtensions)
- parser.on('drain', () => this.#handler.onParserDrain())
- parser.on('error', (err) => this.#handler.onParserError(err))
-
- this.#parser = parser
- this.#sendQueue = new SendQueue(response.socket)
-
- // 1. Change the ready state to OPEN (1).
- this.#handler.readyState = states.OPEN
-
- // 2. Change the extensions attribute’s value to the extensions in use, if
- // it is not the null value.
- // https://datatracker.ietf.org/doc/html/rfc6455#section-9.1
- const extensions = response.headersList.get('sec-websocket-extensions')
-
- if (extensions !== null) {
- this.#extensions = extensions
- }
-
- // 3. Change the protocol attribute’s value to the subprotocol in use, if
- // it is not the null value.
- // https://datatracker.ietf.org/doc/html/rfc6455#section-1.9
- const protocol = response.headersList.get('sec-websocket-protocol')
-
- if (protocol !== null) {
- this.#protocol = protocol
- }
-
- // 4. Fire an event named open at the WebSocket object.
- fireEvent('open', this)
-
- if (channels.open.hasSubscribers) {
- // Convert headers to a plain object for the event
- const headers = response.headersList.entries
- channels.open.publish({
- address: response.socket.address(),
- protocol: this.#protocol,
- extensions: this.#extensions,
- websocket: this,
- handshakeResponse: {
- status: response.status,
- statusText: response.statusText,
- headers
- }
- })
- }
- }
-
- #onMessage (type, data) {
- // 1. If ready state is not OPEN (1), then return.
- if (this.#handler.readyState !== states.OPEN) {
- return
- }
-
- // 2. Let dataForEvent be determined by switching on type and binary type:
- let dataForEvent
-
- if (type === opcodes.TEXT) {
- // -> type indicates that the data is Text
- // a new DOMString containing data
- try {
- dataForEvent = utf8Decode(data)
- } catch {
- failWebsocketConnection(this.#handler, 1007, 'Received invalid UTF-8 in text frame.')
- return
- }
- } else if (type === opcodes.BINARY) {
- if (this.#binaryType === 'blob') {
- // -> type indicates that the data is Binary and binary type is "blob"
- // a new Blob object, created in the relevant Realm of the WebSocket
- // object, that represents data as its raw data
- dataForEvent = new Blob([data])
- } else {
- // -> type indicates that the data is Binary and binary type is "arraybuffer"
- // a new ArrayBuffer object, created in the relevant Realm of the
- // WebSocket object, whose contents are data
- dataForEvent = toArrayBuffer(data)
- }
- }
-
- // 3. Fire an event named message at the WebSocket object, using MessageEvent,
- // with the origin attribute initialized to the serialization of the WebSocket
- // object’s url's origin, and the data attribute initialized to dataForEvent.
- fireEvent('message', this, createFastMessageEvent, {
- origin: this.#url.origin,
- data: dataForEvent
- })
- }
-
- #onParserDrain () {
- this.#handler.socket.resume()
- }
-
- /**
- * @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol
- * @see https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.4
- */
- #onSocketClose () {
- // If the TCP connection was closed after the
- // WebSocket closing handshake was completed, the WebSocket connection
- // is said to have been closed _cleanly_.
- const wasClean =
- this.#handler.closeState.has(sentCloseFrameState.SENT) &&
- this.#handler.closeState.has(sentCloseFrameState.RECEIVED)
-
- let code = 1005
- let reason = ''
-
- const result = this.#parser?.closingInfo
-
- if (result && !result.error) {
- code = result.code ?? 1005
- reason = result.reason
- }
-
- // 1. Change the ready state to CLOSED (3).
- this.#handler.readyState = states.CLOSED
-
- // 2. If the user agent was required to fail the WebSocket
- // connection, or if the WebSocket connection was closed
- // after being flagged as full, fire an event named error
- // at the WebSocket object.
- if (!this.#handler.closeState.has(sentCloseFrameState.RECEIVED)) {
- // If _The WebSocket
- // Connection is Closed_ and no Close control frame was received by the
- // endpoint (such as could occur if the underlying transport connection
- // is lost), _The WebSocket Connection Close Code_ is considered to be
- // 1006.
- code = 1006
-
- fireEvent('error', this, (type, init) => new ErrorEvent(type, init), {
- error: new TypeError(reason)
- })
- }
-
- // 3. Fire an event named close at the WebSocket object,
- // using CloseEvent, with the wasClean attribute
- // initialized to true if the connection closed cleanly
- // and false otherwise, the code attribute initialized to
- // the WebSocket connection close code, and the reason
- // attribute initialized to the result of applying UTF-8
- // decode without BOM to the WebSocket connection close
- // reason.
- // TODO: process.nextTick
- fireEvent('close', this, (type, init) => new CloseEvent(type, init), {
- wasClean, code, reason
- })
-
- if (channels.close.hasSubscribers) {
- channels.close.publish({
- websocket: this,
- code,
- reason
- })
- }
- }
-
- /**
- * @param {WebSocket} ws
- * @param {Buffer|undefined} buffer
- */
- static ping (ws, buffer) {
- if (Buffer.isBuffer(buffer)) {
- if (buffer.length > 125) {
- throw new TypeError('A PING frame cannot have a body larger than 125 bytes.')
- }
- } else if (buffer !== undefined) {
- throw new TypeError('Expected buffer payload')
- }
-
- // An endpoint MAY send a Ping frame any time after the connection is
- // established and before the connection is closed.
- const readyState = ws.#handler.readyState
-
- if (isEstablished(readyState) && !isClosing(readyState) && !isClosed(readyState)) {
- const frame = new WebsocketFrameSend(buffer)
- ws.#handler.socket.write(frame.createFrame(opcodes.PING))
- }
- }
-}
-
-const { ping } = WebSocket
-Reflect.deleteProperty(WebSocket, 'ping')
-
-// https://websockets.spec.whatwg.org/#dom-websocket-connecting
-WebSocket.CONNECTING = WebSocket.prototype.CONNECTING = states.CONNECTING
-// https://websockets.spec.whatwg.org/#dom-websocket-open
-WebSocket.OPEN = WebSocket.prototype.OPEN = states.OPEN
-// https://websockets.spec.whatwg.org/#dom-websocket-closing
-WebSocket.CLOSING = WebSocket.prototype.CLOSING = states.CLOSING
-// https://websockets.spec.whatwg.org/#dom-websocket-closed
-WebSocket.CLOSED = WebSocket.prototype.CLOSED = states.CLOSED
-
-Object.defineProperties(WebSocket.prototype, {
- CONNECTING: staticPropertyDescriptors,
- OPEN: staticPropertyDescriptors,
- CLOSING: staticPropertyDescriptors,
- CLOSED: staticPropertyDescriptors,
- url: kEnumerableProperty,
- readyState: kEnumerableProperty,
- bufferedAmount: kEnumerableProperty,
- onopen: kEnumerableProperty,
- onerror: kEnumerableProperty,
- onclose: kEnumerableProperty,
- close: kEnumerableProperty,
- onmessage: kEnumerableProperty,
- binaryType: kEnumerableProperty,
- send: kEnumerableProperty,
- extensions: kEnumerableProperty,
- protocol: kEnumerableProperty,
- [Symbol.toStringTag]: {
- value: 'WebSocket',
- writable: false,
- enumerable: false,
- configurable: true
- }
-})
-
-Object.defineProperties(WebSocket, {
- CONNECTING: staticPropertyDescriptors,
- OPEN: staticPropertyDescriptors,
- CLOSING: staticPropertyDescriptors,
- CLOSED: staticPropertyDescriptors
-})
-
-webidl.converters['sequence<DOMString>'] = webidl.sequenceConverter(
- webidl.converters.DOMString
-)
-
-webidl.converters['DOMString or sequence<DOMString>'] = function (V, prefix, argument) {
- if (webidl.util.Type(V) === webidl.util.Types.OBJECT && Symbol.iterator in V) {
- return webidl.converters['sequence<DOMString>'](V)
- }
-
- return webidl.converters.DOMString(V, prefix, argument)
-}
-
-// This implements the proposal made in https://github.com/whatwg/websockets/issues/42
-webidl.converters.WebSocketInit = webidl.dictionaryConverter([
- {
- key: 'protocols',
- converter: webidl.converters['DOMString or sequence<DOMString>'],
- defaultValue: () => []
- },
- {
- key: 'dispatcher',
- converter: webidl.converters.any,
- defaultValue: () => getGlobalDispatcher()
- },
- {
- key: 'headers',
- converter: webidl.nullableConverter(webidl.converters.HeadersInit)
- }
-])
-
-webidl.converters['DOMString or sequence<DOMString> or WebSocketInit'] = function (V) {
- if (webidl.util.Type(V) === webidl.util.Types.OBJECT && !(Symbol.iterator in V)) {
- return webidl.converters.WebSocketInit(V)
- }
-
- return { protocols: webidl.converters['DOMString or sequence<DOMString>'](V) }
-}
-
-webidl.converters.WebSocketSendData = function (V) {
- if (webidl.util.Type(V) === webidl.util.Types.OBJECT) {
- if (webidl.is.Blob(V)) {
- return V
- }
-
- if (webidl.is.BufferSource(V)) {
- return V
- }
- }
-
- return webidl.converters.USVString(V)
-}
-
-module.exports = {
- WebSocket,
- ping
-}