diff options
Diffstat (limited to 'alarm/node_modules/jsdom/lib/jsdom/living/websockets/WebSocket-impl.js')
-rw-r--r-- | alarm/node_modules/jsdom/lib/jsdom/living/websockets/WebSocket-impl.js | 328 |
1 files changed, 0 insertions, 328 deletions
diff --git a/alarm/node_modules/jsdom/lib/jsdom/living/websockets/WebSocket-impl.js b/alarm/node_modules/jsdom/lib/jsdom/living/websockets/WebSocket-impl.js deleted file mode 100644 index ff64ff0..0000000 --- a/alarm/node_modules/jsdom/lib/jsdom/living/websockets/WebSocket-impl.js +++ /dev/null @@ -1,328 +0,0 @@ -"use strict"; - -const nodeURL = require("url"); - -const DOMException = require("domexception/webidl2js-wrapper"); -const { parseURL, serializeURL, serializeURLOrigin } = require("whatwg-url"); -const WebSocket = require("ws"); - -const { setupForSimpleEventAccessors } = require("../helpers/create-event-accessor"); -const { fireAnEvent } = require("../helpers/events"); -const { isArrayBuffer } = require("../generated/utils"); -const { copyToArrayBufferInNewRealm } = require("../helpers/binary-data"); - -const EventTargetImpl = require("../events/EventTarget-impl").implementation; - -const idlUtils = require("../generated/utils"); -const Blob = require("../generated/Blob"); -const CloseEvent = require("../generated/CloseEvent"); -const MessageEvent = require("../generated/MessageEvent"); - -const CONNECTING = 0; -const OPEN = 1; -const CLOSING = 2; -const CLOSED = 3; - -const productions = { - // https://tools.ietf.org/html/rfc7230#section-3.2.6 - token: /^[!#$%&'*+\-.^_`|~\dA-Za-z]+$/ -}; - -const readyStateWSToDOM = []; -readyStateWSToDOM[WebSocket.CONNECTING] = CONNECTING; -readyStateWSToDOM[WebSocket.OPEN] = OPEN; -readyStateWSToDOM[WebSocket.CLOSING] = CLOSING; -readyStateWSToDOM[WebSocket.CLOSED] = CLOSED; - -// https://tools.ietf.org/html/rfc6455#section-4.3 -// See Sec-WebSocket-Protocol-Client, which is for the syntax of an entire header value. This function checks if a -// single header conforms to the rules. -function verifySecWebSocketProtocol(str) { - return productions.token.test(str); -} - -class PromiseQueues extends WeakMap { - get(window) { - const cur = super.get(window); - return cur !== undefined ? cur : Promise.resolve(); - } -} - -const openSockets = new WeakMap(); -const openingQueues = new PromiseQueues(); - -class WebSocketImpl extends EventTargetImpl { - constructor(globalObject, args, privateData) { - super(globalObject, args, privateData); - - this._ownerDocument = idlUtils.implForWrapper(globalObject._document); - - const url = args[0]; - let protocols = args[1] !== undefined ? args[1] : []; - - const urlRecord = parseURL(url); - if (urlRecord === null) { - throw DOMException.create(this._globalObject, [`The URL '${url}' is invalid.`, "SyntaxError"]); - } - if (urlRecord.scheme !== "ws" && urlRecord.scheme !== "wss") { - throw DOMException.create(this._globalObject, [ - `The URL's scheme must be either 'ws' or 'wss'. '${urlRecord.scheme}' is not allowed.`, - "SyntaxError" - ]); - } - if (urlRecord.fragment !== null) { - throw DOMException.create(this._globalObject, [ - `The URL contains a fragment identifier ('${urlRecord.fragment}'). Fragment identifiers ` + - "are not allowed in WebSocket URLs.", - "SyntaxError" - ]); - } - - if (typeof protocols === "string") { - protocols = [protocols]; - } - const protocolSet = new Set(); - for (const protocol of protocols) { - if (!verifySecWebSocketProtocol(protocol)) { - throw DOMException.create(this._globalObject, [`The subprotocol '${protocol}' is invalid.`, "SyntaxError"]); - } - const lowered = protocol.toLowerCase(); - if (protocolSet.has(lowered)) { - throw DOMException.create(this._globalObject, [ - `The subprotocol '${protocol}' is duplicated.`, - "SyntaxError" - ]); - } - protocolSet.add(lowered); - } - - this._urlRecord = urlRecord; - this.url = serializeURL(urlRecord); - const nodeParsedURL = nodeURL.parse(this.url); - this.extensions = ""; - - this.binaryType = "blob"; - - this._ws = null; - // Used when this._ws has not been initialized yet. - this._readyState = CONNECTING; - this._requiredToFail = false; - this.bufferedAmount = 0; - this._sendQueue = []; - - let openSocketsForWindow = openSockets.get(globalObject._globalProxy); - if (openSocketsForWindow === undefined) { - openSocketsForWindow = new Set(); - openSockets.set(globalObject._globalProxy, openSocketsForWindow); - } - openSocketsForWindow.add(this); - - openingQueues.set(this._ownerDocument, openingQueues.get(this._ownerDocument).then(() => new Promise(resolve => { - // close() called before _ws has been initialized. - if (this._requiredToFail) { - resolve(); - this._readyState = CLOSED; - this._onConnectionClosed(1006, ""); - return; - } - - this._ws = new WebSocket(this.url, protocols, { - headers: { - "user-agent": globalObject.navigator.userAgent, - "cookie": this._ownerDocument._cookieJar.getCookieStringSync(nodeParsedURL, { http: true }), - "origin": globalObject._origin - }, - rejectUnauthorized: this._ownerDocument._strictSSL - }); - this._ws.once("open", () => { - resolve(); - this._onConnectionEstablished(); - }); - this._ws.on("message", this._onMessageReceived.bind(this)); - this._ws.once("close", (...closeArgs) => { - resolve(); - this._onConnectionClosed(...closeArgs); - }); - this._ws.once("upgrade", ({ headers }) => { - if (Array.isArray(headers["set-cookie"])) { - for (const cookie of headers["set-cookie"]) { - this._ownerDocument._cookieJar.setCookieSync( - cookie, - nodeParsedURL, - { http: true, ignoreError: true } - ); - } - } else if (headers["set-cookie"] !== undefined) { - this._ownerDocument._cookieJar.setCookieSync( - headers["set-cookie"], - nodeParsedURL, - { http: true, ignoreError: true } - ); - } - }); - this._ws.once("error", () => { - // The exact error is passed into this callback, but it is ignored as we don't really care about it. - resolve(); - this._requiredToFail = true; - // Do not emit an error here, as that will be handled in _onConnectionClosed. ws always emits a close event - // after errors. - }); - }))); - } - - // https://html.spec.whatwg.org/multipage/web-sockets.html#make-disappear - _makeDisappear() { - this._eventListeners = Object.create(null); - this._close(1001); - } - - static cleanUpWindow(window) { - const openSocketsForWindow = openSockets.get(window._globalProxy); - if (openSocketsForWindow !== undefined) { - for (const ws of openSocketsForWindow) { - ws._makeDisappear(); - } - } - } - - // https://html.spec.whatwg.org/multipage/web-sockets.html#feedback-from-the-protocol - _onConnectionEstablished() { - // readyState is a getter. - if (this._ws.extensions !== null) { - // Right now, ws only supports one extension, permessage-deflate, without any parameters. This algorithm may need - // to be more sophiscated as more extenions are supported. - this.extensions = Object.keys(this._ws.extensions).join(", "); - } - // protocol is a getter. - fireAnEvent("open", this); - } - - _onMessageReceived(data) { - if (this.readyState !== OPEN) { - return; - } - let dataForEvent; - if (typeof data === "string") { - dataForEvent = data; - } else if (this.binaryType === "arraybuffer") { - if (isArrayBuffer(data)) { - dataForEvent = data; - } else if (Array.isArray(data)) { - dataForEvent = copyToArrayBufferInNewRealm(Buffer.concat(data), this._globalObject); - } else { - dataForEvent = copyToArrayBufferInNewRealm(data, this._globalObject); - } - } else { // this.binaryType === "blob" - if (!Array.isArray(data)) { - data = [data]; - } - dataForEvent = Blob.create(this._globalObject, [data, { type: "" }]); - } - fireAnEvent("message", this, MessageEvent, { - data: dataForEvent, - origin: serializeURLOrigin(this._urlRecord) - }); - } - - _onConnectionClosed(code, reason) { - const openSocketsForWindow = openSockets.get(this._ownerDocument._defaultView); - openSocketsForWindow.delete(this); - - const wasClean = !this._requiredToFail; - if (this._requiredToFail) { - fireAnEvent("error", this); - } - fireAnEvent("close", this, CloseEvent, { - wasClean, - code, - reason - }); - } - - get readyState() { - if (this._ws !== null) { - return readyStateWSToDOM[this._ws.readyState]; - } - return this._readyState; - } - - get protocol() { - if (this._ws === null) { - return ""; - } - return this._ws.protocol; - } - - close(code = undefined, reason = undefined) { - if (code !== undefined && code !== 1000 && !(code >= 3000 && code <= 4999)) { - throw DOMException.create(this._globalObject, [ - `The code must be either 1000, or between 3000 and 4999. ${code} is neither.`, - "InvalidAccessError" - ]); - } - if (reason !== undefined && Buffer.byteLength(reason, "utf8") > 123) { - throw DOMException.create(this._globalObject, [ - "The message must not be greater than 123 bytes.", - "SyntaxError" - ]); - } - this._close(code, reason); - } - - _close(code = undefined, reason = undefined) { - if (this.readyState === CONNECTING) { - this._requiredToFail = true; - if (this._ws !== null) { - this._ws.terminate(); - } else { - this._readyState = CLOSING; - } - } else if (this.readyState === OPEN) { - this._ws.close(code, reason); - } - } - - send(data) { - if (this.readyState === CONNECTING) { - throw DOMException.create(this._globalObject, ["Still in CONNECTING state.", "InvalidStateError"]); - } - if (this.readyState !== OPEN) { - return; - } - if (Blob.isImpl(data)) { - data = data._buffer; - } - let length; - if (typeof data === "string") { - length = Buffer.byteLength(data, "utf8"); - } else { - length = data.byteLength; - } - this.bufferedAmount += length; - this._sendQueue.push([data, length]); - this._scheduleSend(); - } - - _actuallySend() { - for (const [data, length] of this._sendQueue.splice(0)) { - this._ws.send(data, { binary: typeof data !== "string" }, () => { - this.bufferedAmount -= length; - }); - } - } - - _scheduleSend() { - if (this._dequeueScheduled) { - return; - } - this._dequeueScheduled = true; - process.nextTick(() => { - this._dequeueScheduled = false; - this._actuallySend(); - }); - } -} - -setupForSimpleEventAccessors(WebSocketImpl.prototype, ["open", "message", "error", "close"]); - -exports.implementation = WebSocketImpl; |