/** * This deviates from the WHATWG interface since ws doesn't support the * required default "blob" type (instead we define a custom "nodebuffer" * type). * * @type {String}
*/
get binaryType() { returnthis._binaryType;
}
set binaryType(type) { if (!BINARY_TYPES.includes(type)) return;
this._binaryType = type;
// // Allow to change `binaryType` on the fly. // if (this._receiver) this._receiver._binaryType = type;
}
/** * @type {Number}
*/
get bufferedAmount() { if (!this._socket) returnthis._bufferedAmount;
/** * @type {Boolean}
*/
get isPaused() { returnthis._paused;
}
/** * @type {Function}
*/ /* istanbul ignore next */
get onclose() { returnnull;
}
/** * @type {Function}
*/ /* istanbul ignore next */
get onerror() { returnnull;
}
/** * @type {Function}
*/ /* istanbul ignore next */
get onopen() { returnnull;
}
/** * @type {Function}
*/ /* istanbul ignore next */
get onmessage() { returnnull;
}
/** * @type {String}
*/
get protocol() { returnthis._protocol;
}
/** * @type {Number}
*/
get readyState() { returnthis._readyState;
}
/** * @type {String}
*/
get url() { returnthis._url;
}
/** * Set up the socket and the internal resources. * * @param {(net.Socket|tls.Socket)} socket The network socket between the * server and client * @param {Buffer} head The first packet of the upgraded stream * @param {Object} options Options object * @param {Function} [options.generateMask] The function used to generate the * masking key * @param {Number} [options.maxPayload=0] The maximum allowed message size * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or * not to skip UTF-8 validation for text and close messages * @private
*/
setSocket(socket, head, options) { const receiver = new Receiver({
binaryType: this.binaryType,
extensions: this._extensions,
isServer: this._isServer,
maxPayload: options.maxPayload,
skipUTF8Validation: options.skipUTF8Validation
});
/** * Start a closing handshake. * * +----------+ +-----------+ +----------+ * - - -|ws.close()|-->|close frame|-->|ws.close()|- - - * | +----------+ +-----------+ +----------+ | * +----------+ +-----------+ | * CLOSING |ws.close()|<--|close frame|<--+-----+ CLOSING * +----------+ +-----------+ | * | | | +---+ | * +------------------------+-->|fin| - - - - * | +---+ | +---+ * - - - - -|fin|<---------------------+ * +---+ * * @param {Number} [code] Status code explaining why the connection is closing * @param {(String|Buffer)} [data] The reason why the connection is * closing * @public
*/
close(code, data) { if (this.readyState === WebSocket.CLOSED) return; if (this.readyState === WebSocket.CONNECTING) { const msg = 'WebSocket was closed before the connection was established'; return abortHandshake(this, this._req, msg);
}
if (this.readyState === WebSocket.CLOSING) { if ( this._closeFrameSent &&
(this._closeFrameReceived || this._receiver._writableState.errorEmitted)
) { this._socket.end();
}
return;
}
this._readyState = WebSocket.CLOSING; this._sender.close(code, data, !this._isServer, (err) => { // // This error is handled by the `'error'` listener on the socket. We only // want to know if the close frame has been sent here. // if (err) return;
this._closeFrameSent = true;
if ( this._closeFrameReceived || this._receiver._writableState.errorEmitted
) { this._socket.end();
}
});
// // Specify a timeout for the closing handshake to complete. // this._closeTimer = setTimeout( this._socket.destroy.bind(this._socket),
closeTimeout
);
}
/** * Send a ping. * * @param {*} [data] The data to send * @param {Boolean} [mask] Indicates whether or not to mask `data` * @param {Function} [cb] Callback which is executed when the ping is sent * @public
*/
ping(data, mask, cb) { if (this.readyState === WebSocket.CONNECTING) { thrownew Error('WebSocket is not open: readyState 0 (CONNECTING)');
}
if (typeof data === 'function') {
cb = data;
data = mask = undefined;
} elseif (typeof mask === 'function') {
cb = mask;
mask = undefined;
}
if (typeof data === 'number') data = data.toString();
if (this.readyState !== WebSocket.OPEN) {
sendAfterClose(this, data, cb); return;
}
/** * Send a pong. * * @param {*} [data] The data to send * @param {Boolean} [mask] Indicates whether or not to mask `data` * @param {Function} [cb] Callback which is executed when the pong is sent * @public
*/
pong(data, mask, cb) { if (this.readyState === WebSocket.CONNECTING) { thrownew Error('WebSocket is not open: readyState 0 (CONNECTING)');
}
if (typeof data === 'function') {
cb = data;
data = mask = undefined;
} elseif (typeof mask === 'function') {
cb = mask;
mask = undefined;
}
if (typeof data === 'number') data = data.toString();
if (this.readyState !== WebSocket.OPEN) {
sendAfterClose(this, data, cb); return;
}
this._paused = false; if (!this._receiver._writableState.needDrain) this._socket.resume();
}
/** * Send a data message. * * @param {*} data The message to send * @param {Object} [options] Options object * @param {Boolean} [options.binary] Specifies whether `data` is binary or * text * @param {Boolean} [options.compress] Specifies whether or not to compress * `data` * @param {Boolean} [options.fin=true] Specifies whether the fragment is the * last one * @param {Boolean} [options.mask] Specifies whether or not to mask `data` * @param {Function} [cb] Callback which is executed when data is written out * @public
*/
send(data, options, cb) { if (this.readyState === WebSocket.CONNECTING) { thrownew Error('WebSocket is not open: readyState 0 (CONNECTING)');
}
/** * Forcibly close the connection. * * @public
*/
terminate() { if (this.readyState === WebSocket.CLOSED) return; if (this.readyState === WebSocket.CONNECTING) { const msg = 'WebSocket was closed before the connection was established'; return abortHandshake(this, this._req, msg);
}
if (this._socket) { this._readyState = WebSocket.CLOSING; this._socket.destroy();
}
}
}
/** * Initialize a WebSocket client. * * @param {WebSocket} websocket The client to initialize * @param {(String|URL)} address The URL to which to connect * @param {Array} protocols The subprotocols * @param {Object} [options] Connection options * @param {Boolean} [options.followRedirects=false] Whether or not to follow * redirects * @param {Function} [options.generateMask] The function used to generate the * masking key * @param {Number} [options.handshakeTimeout] Timeout in milliseconds for the * handshake request * @param {Number} [options.maxPayload=104857600] The maximum allowed message * size * @param {Number} [options.maxRedirects=10] The maximum number of redirects * allowed * @param {String} [options.origin] Value of the `Origin` or * `Sec-WebSocket-Origin` header * @param {(Boolean|Object)} [options.perMessageDeflate=true] Enable/disable * permessage-deflate * @param {Number} [options.protocolVersion=13] Value of the * `Sec-WebSocket-Version` header * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or * not to skip UTF-8 validation for text and close messages * @private
*/ function initAsClient(websocket, address, protocols, options) { const opts = {
protocolVersion: protocolVersions[1],
maxPayload: 100 * 1024 * 1024,
skipUTF8Validation: false,
perMessageDeflate: true,
followRedirects: false,
maxRedirects: 10,
...options,
createConnection: undefined,
socketPath: undefined,
hostname: undefined,
protocol: undefined,
timeout: undefined,
method: 'GET',
host: undefined,
path: undefined,
port: undefined
};
if (parsedUrl.protocol !== 'ws:' && !isSecure && !isUnixSocket) {
invalidURLMessage = 'The URL\'s protocol must be one of "ws:", "wss:", or "ws+unix:"';
} elseif (isUnixSocket && !parsedUrl.pathname) {
invalidURLMessage = "The URL's pathname is empty";
} elseif (parsedUrl.hash) {
invalidURLMessage = 'The URL contains a fragment identifier';
}
if (invalidURLMessage) { const err = new SyntaxError(invalidURLMessage);
// // Shallow copy the user provided options so that headers can be changed // without mutating the original object. //
options = { ...options, headers: {} };
if (!isSameHost || (websocket._originalSecure && !isSecure)) { // // Match curl 7.77.0 behavior and drop the following headers. These // headers are also dropped when following a redirect to a subdomain. // delete opts.headers.authorization; delete opts.headers.cookie;
if (!isSameHost) delete opts.headers.host;
opts.auth = undefined;
}
}
// // Match curl 7.77.0 behavior and make the first `Authorization` header win. // If the `Authorization` header is set, then there is nothing to do as it // will take precedence. // if (opts.auth && !options.headers.authorization) {
options.headers.authorization = 'Basic ' + Buffer.from(opts.auth).toString('base64');
}
req = websocket._req = request(opts);
if (websocket._redirects) { // // Unlike what is done for the `'upgrade'` event, no early exit is // triggered here if the user calls `websocket.close()` or // `websocket.terminate()` from a listener of the `'redirect'` event. This // is because the user can also call `request.destroy()` with an error // before calling `websocket.close()` or `websocket.terminate()` and this // would result in an error being emitted on the `request` object with no // `'error'` event listeners attached. //
websocket.emit('redirect', websocket.url, req);
}
} else {
req = websocket._req = request(opts);
}
if (opts.timeout) {
req.on('timeout', () => {
abortHandshake(websocket, req, 'Opening handshake has timed out');
});
}
// // The user may have closed the connection from a listener of the // `'upgrade'` event. // if (websocket.readyState !== WebSocket.CONNECTING) return;
const serverProt = res.headers['sec-websocket-protocol'];
let protError;
if (serverProt !== undefined) { if (!protocolSet.size) {
protError = 'Server sent a subprotocol but none was requested';
} elseif (!protocolSet.has(serverProt)) {
protError = 'Server sent an invalid subprotocol';
}
} elseif (protocolSet.size) {
protError = 'Server sent no subprotocol';
}
if (protError) {
abortHandshake(websocket, socket, protError); return;
}
if (secWebSocketExtensions !== undefined) { if (!perMessageDeflate) { const message = 'Server sent a Sec-WebSocket-Extensions header but no extension ' + 'was requested';
abortHandshake(websocket, socket, message); return;
}
/**
* Emit the `'error'` and `'close'` events.
*
* @param {WebSocket} websocket The WebSocket instance
* @param {Error} The error to emit
* @private
*/
function emitErrorAndClose(websocket, err) {
websocket._readyState = WebSocket.CLOSING;
websocket.emit('error', err);
websocket.emitClose();
}
/**
* Create a `net.Socket` and initiate a connection.
*
* @param {Object} options Connection options
* @return {net.Socket} The newly created socket used to start the connection
* @private
*/
function netConnect(options) {
options.path = options.socketPath;
return net.connect(options);
}
/**
* Create a `tls.TLSSocket` and initiate a connection.
*
* @param {Object} options Connection options
* @return {tls.TLSSocket} The newly created socket used to start the connection
* @private
*/
function tlsConnect(options) {
options.path = undefined;
/**
* Abort the handshake and emit an error.
*
* @param {WebSocket} websocket The WebSocket instance
* @param {(http.ClientRequest|net.Socket|tls.Socket)} stream The request to
* abort or the socket to destroy
* @param {String} message The error message
* @private
*/
function abortHandshake(websocket, stream, message) {
websocket._readyState = WebSocket.CLOSING;
const err = new Error(message);
Error.captureStackTrace(err, abortHandshake);
if (stream.setHeader) {
stream[kAborted] = true;
stream.abort();
if (stream.socket && !stream.socket.destroyed) {
//
// On Node.js >= 14.3.0 `request.abort()` does not destroy the socket if
// called after the request completed. See
// https://github.com/websockets/ws/issues/1869.
//
stream.socket.destroy();
}
/**
* Handle cases where the `ping()`, `pong()`, or `send()` methods are called
* when the `readyState` attribute is `CLOSING` or `CLOSED`.
*
* @param {WebSocket} websocket The WebSocket instance
* @param {*} [data] The data to send
* @param {Function} [cb] Callback
* @private
*/
function sendAfterClose(websocket, data, cb) {
if (data) {
const length = toBuffer(data).length;
//
// The `_bufferedAmount` property is used only when the peer is a client and
// the opening handshake fails. Under these circumstances, in fact, the
// `setSocket()` method is not called, so the `_socket` and `_sender`
// properties are set to `null`.
//
if (websocket._socket) websocket._sender._bufferedBytes += length;
else websocket._bufferedAmount += length;
}
if (cb) {
const err = new Error(
`WebSocket is not open: readyState ${websocket.readyState} ` +
`(${readyStates[websocket.readyState]})`
);
cb(err);
}
}
/**
* The listener of the `Receiver` `'conclude'` event.
*
* @param {Number} code The status code
* @param {Buffer} reason The reason for closing
* @private
*/
function receiverOnConclude(code, reason) {
const websocket = this[kWebSocket];
/**
* The listener of the `Receiver` `'finish'` event.
*
* @private
*/
function receiverOnFinish() {
this[kWebSocket].emitClose();
}
/**
* The listener of the `Receiver` `'message'` event.
*
* @param {Buffer|ArrayBuffer|Buffer[])} data The message
* @param {Boolean} isBinary Specifies whether the message is binary or not
* @private
*/
function receiverOnMessage(data, isBinary) {
this[kWebSocket].emit('message', data, isBinary);
}
/**
* The listener of the `Receiver` `'ping'` event.
*
* @param {Buffer} data The data included in the ping frame
* @private
*/
function receiverOnPing(data) {
const websocket = this[kWebSocket];
/**
* The listener of the `Receiver` `'pong'` event.
*
* @param {Buffer} data The data included in the pong frame
* @private
*/
function receiverOnPong(data) {
this[kWebSocket].emit('pong', data);
}
/**
* Resume a readable stream
*
* @param {Readable} stream The readable stream
* @private
*/
function resume(stream) {
stream.resume();
}
/**
* The listener of the `net.Socket` `'close'` event.
*
* @private
*/
function socketOnClose() {
const websocket = this[kWebSocket];
//
// The close frame might not have been received or the `'end'` event emitted,
// for example, if the socket was destroyed due to an error. Ensure that the
// `receiver` stream is closed after writing any remaining buffered data to
// it. If the readable side of the socket is in flowing mode then there is no
// buffered data as everything has been already written and `readable.read()`
// will return `null`. If instead, the socket is paused, any possible buffered
// data will be read as a single chunk.
//
if (
!this._readableState.endEmitted &&
!websocket._closeFrameReceived &&
!websocket._receiver._writableState.errorEmitted &&
(chunk = websocket._socket.read()) !== null
) {
websocket._receiver.write(chunk);
}
/**
* The listener of the `net.Socket` `'data'` event.
*
* @param {Buffer} chunk A chunk of data
* @private
*/
function socketOnData(chunk) {
if (!this[kWebSocket]._receiver.write(chunk)) {
this.pause();
}
}
/**
* The listener of the `net.Socket` `'end'` event.
*
* @private
*/
function socketOnEnd() {
const websocket = this[kWebSocket];
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.