Run npm install (#43)
All checks were successful
Lint / pre-commit Linting (push) Successful in 1m18s

Reviewed-on: #43
This commit was merged in pull request #43.
This commit is contained in:
2025-12-16 01:07:55 +01:00
parent 9510650dd1
commit 74d74b73ad
614 changed files with 6056 additions and 159231 deletions

274
node_modules/ws/lib/receiver.js generated vendored
View File

@@ -1,11 +1,16 @@
'use strict';
const stream = require('stream');
const { Writable } = require('stream');
const PerMessageDeflate = require('./permessage-deflate');
const bufferUtil = require('./buffer-util');
const validation = require('./validation');
const constants = require('./constants');
const {
BINARY_TYPES,
EMPTY_BUFFER,
kStatusCode,
kWebSocket
} = require('./constants');
const { concat, toArrayBuffer, unmask } = require('./buffer-util');
const { isValidStatusCode, isValidUTF8 } = require('./validation');
const GET_INFO = 0;
const GET_PAYLOAD_LENGTH_16 = 1;
@@ -17,22 +22,25 @@ const INFLATING = 5;
/**
* HyBi Receiver implementation.
*
* @extends stream.Writable
* @extends Writable
*/
class Receiver extends stream.Writable {
class Receiver extends Writable {
/**
* Creates a Receiver instance.
*
* @param {String} binaryType The type for binary data
* @param {Object} extensions An object containing the negotiated extensions
* @param {Number} maxPayload The maximum allowed message length
* @param {String} [binaryType=nodebuffer] The type for binary data
* @param {Object} [extensions] An object containing the negotiated extensions
* @param {Boolean} [isServer=false] Specifies whether to operate in client or
* server mode
* @param {Number} [maxPayload=0] The maximum allowed message length
*/
constructor (binaryType, extensions, maxPayload) {
constructor(binaryType, extensions, isServer, maxPayload) {
super();
this._binaryType = binaryType || constants.BINARY_TYPES[0];
this[constants.kWebSocket] = undefined;
this._binaryType = binaryType || BINARY_TYPES[0];
this[kWebSocket] = undefined;
this._extensions = extensions || {};
this._isServer = !!isServer;
this._maxPayload = maxPayload | 0;
this._bufferedBytes = 0;
@@ -60,9 +68,10 @@ class Receiver extends stream.Writable {
* @param {Buffer} chunk The chunk of data to write
* @param {String} encoding The character encoding of `chunk`
* @param {Function} cb Callback
* @private
*/
_write (chunk, encoding, cb) {
if (this._opcode === 0x08) return cb();
_write(chunk, encoding, cb) {
if (this._opcode === 0x08 && this._state == GET_INFO) return cb();
this._bufferedBytes += chunk.length;
this._buffers.push(chunk);
@@ -76,7 +85,7 @@ class Receiver extends stream.Writable {
* @return {Buffer} The consumed bytes
* @private
*/
consume (n) {
consume(n) {
this._bufferedBytes -= n;
if (n === this._buffers[0].length) return this._buffers.shift();
@@ -91,11 +100,12 @@ class Receiver extends stream.Writable {
do {
const buf = this._buffers[0];
const offset = dst.length - n;
if (n >= buf.length) {
this._buffers.shift().copy(dst, dst.length - n);
dst.set(this._buffers.shift(), offset);
} else {
buf.copy(dst, dst.length - n, 0, n);
dst.set(new Uint8Array(buf.buffer, buf.byteOffset, n), offset);
this._buffers[0] = buf.slice(n);
}
@@ -111,8 +121,8 @@ class Receiver extends stream.Writable {
* @param {Function} cb Callback
* @private
*/
startLoop (cb) {
var err;
startLoop(cb) {
let err;
this._loop = true;
do {
@@ -132,7 +142,8 @@ class Receiver extends stream.Writable {
case GET_DATA:
err = this.getData(cb);
break;
default: // `INFLATING`
default:
// `INFLATING`
this._loop = false;
return;
}
@@ -147,7 +158,7 @@ class Receiver extends stream.Writable {
* @return {(RangeError|undefined)} A possible error
* @private
*/
getInfo () {
getInfo() {
if (this._bufferedBytes < 2) {
this._loop = false;
return;
@@ -157,14 +168,26 @@ class Receiver extends stream.Writable {
if ((buf[0] & 0x30) !== 0x00) {
this._loop = false;
return error(RangeError, 'RSV2 and RSV3 must be clear', true, 1002);
return error(
RangeError,
'RSV2 and RSV3 must be clear',
true,
1002,
'WS_ERR_UNEXPECTED_RSV_2_3'
);
}
const compressed = (buf[0] & 0x40) === 0x40;
if (compressed && !this._extensions[PerMessageDeflate.extensionName]) {
this._loop = false;
return error(RangeError, 'RSV1 must be clear', true, 1002);
return error(
RangeError,
'RSV1 must be clear',
true,
1002,
'WS_ERR_UNEXPECTED_RSV_1'
);
}
this._fin = (buf[0] & 0x80) === 0x80;
@@ -174,31 +197,61 @@ class Receiver extends stream.Writable {
if (this._opcode === 0x00) {
if (compressed) {
this._loop = false;
return error(RangeError, 'RSV1 must be clear', true, 1002);
return error(
RangeError,
'RSV1 must be clear',
true,
1002,
'WS_ERR_UNEXPECTED_RSV_1'
);
}
if (!this._fragmented) {
this._loop = false;
return error(RangeError, 'invalid opcode 0', true, 1002);
return error(
RangeError,
'invalid opcode 0',
true,
1002,
'WS_ERR_INVALID_OPCODE'
);
}
this._opcode = this._fragmented;
} else if (this._opcode === 0x01 || this._opcode === 0x02) {
if (this._fragmented) {
this._loop = false;
return error(RangeError, `invalid opcode ${this._opcode}`, true, 1002);
return error(
RangeError,
`invalid opcode ${this._opcode}`,
true,
1002,
'WS_ERR_INVALID_OPCODE'
);
}
this._compressed = compressed;
} else if (this._opcode > 0x07 && this._opcode < 0x0b) {
if (!this._fin) {
this._loop = false;
return error(RangeError, 'FIN must be set', true, 1002);
return error(
RangeError,
'FIN must be set',
true,
1002,
'WS_ERR_EXPECTED_FIN'
);
}
if (compressed) {
this._loop = false;
return error(RangeError, 'RSV1 must be clear', true, 1002);
return error(
RangeError,
'RSV1 must be clear',
true,
1002,
'WS_ERR_UNEXPECTED_RSV_1'
);
}
if (this._payloadLength > 0x7d) {
@@ -207,17 +260,46 @@ class Receiver extends stream.Writable {
RangeError,
`invalid payload length ${this._payloadLength}`,
true,
1002
1002,
'WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH'
);
}
} else {
this._loop = false;
return error(RangeError, `invalid opcode ${this._opcode}`, true, 1002);
return error(
RangeError,
`invalid opcode ${this._opcode}`,
true,
1002,
'WS_ERR_INVALID_OPCODE'
);
}
if (!this._fin && !this._fragmented) this._fragmented = this._opcode;
this._masked = (buf[1] & 0x80) === 0x80;
if (this._isServer) {
if (!this._masked) {
this._loop = false;
return error(
RangeError,
'MASK must be set',
true,
1002,
'WS_ERR_EXPECTED_MASK'
);
}
} else if (this._masked) {
this._loop = false;
return error(
RangeError,
'MASK must be clear',
true,
1002,
'WS_ERR_UNEXPECTED_MASK'
);
}
if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16;
else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64;
else return this.haveLength();
@@ -229,7 +311,7 @@ class Receiver extends stream.Writable {
* @return {(RangeError|undefined)} A possible error
* @private
*/
getPayloadLength16 () {
getPayloadLength16() {
if (this._bufferedBytes < 2) {
this._loop = false;
return;
@@ -245,7 +327,7 @@ class Receiver extends stream.Writable {
* @return {(RangeError|undefined)} A possible error
* @private
*/
getPayloadLength64 () {
getPayloadLength64() {
if (this._bufferedBytes < 8) {
this._loop = false;
return;
@@ -264,7 +346,8 @@ class Receiver extends stream.Writable {
RangeError,
'Unsupported WebSocket frame: payload length > 2^53 - 1',
false,
1009
1009,
'WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH'
);
}
@@ -278,12 +361,18 @@ class Receiver extends stream.Writable {
* @return {(RangeError|undefined)} A possible error
* @private
*/
haveLength () {
haveLength() {
if (this._payloadLength && this._opcode < 0x08) {
this._totalPayloadLength += this._payloadLength;
if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) {
this._loop = false;
return error(RangeError, 'Max payload size exceeded', false, 1009);
return error(
RangeError,
'Max payload size exceeded',
false,
1009,
'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'
);
}
}
@@ -296,7 +385,7 @@ class Receiver extends stream.Writable {
*
* @private
*/
getMask () {
getMask() {
if (this._bufferedBytes < 4) {
this._loop = false;
return;
@@ -313,8 +402,8 @@ class Receiver extends stream.Writable {
* @return {(Error|RangeError|undefined)} A possible error
* @private
*/
getData (cb) {
var data = constants.EMPTY_BUFFER;
getData(cb) {
let data = EMPTY_BUFFER;
if (this._payloadLength) {
if (this._bufferedBytes < this._payloadLength) {
@@ -323,7 +412,7 @@ class Receiver extends stream.Writable {
}
data = this.consume(this._payloadLength);
if (this._masked) bufferUtil.unmask(data, this._mask);
if (this._masked) unmask(data, this._mask);
}
if (this._opcode > 0x07) return this.controlMessage(data);
@@ -353,7 +442,7 @@ class Receiver extends stream.Writable {
* @param {Function} cb Callback
* @private
*/
decompress (data, cb) {
decompress(data, cb) {
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
perMessageDeflate.decompress(data, this._fin, (err, buf) => {
@@ -362,7 +451,15 @@ class Receiver extends stream.Writable {
if (buf.length) {
this._messageLength += buf.length;
if (this._messageLength > this._maxPayload && this._maxPayload > 0) {
return cb(error(RangeError, 'Max payload size exceeded', false, 1009));
return cb(
error(
RangeError,
'Max payload size exceeded',
false,
1009,
'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'
)
);
}
this._fragments.push(buf);
@@ -381,7 +478,7 @@ class Receiver extends stream.Writable {
* @return {(Error|undefined)} A possible error
* @private
*/
dataMessage () {
dataMessage() {
if (this._fin) {
const messageLength = this._messageLength;
const fragments = this._fragments;
@@ -392,23 +489,29 @@ class Receiver extends stream.Writable {
this._fragments = [];
if (this._opcode === 2) {
var data;
let data;
if (this._binaryType === 'nodebuffer') {
data = toBuffer(fragments, messageLength);
data = concat(fragments, messageLength);
} else if (this._binaryType === 'arraybuffer') {
data = toArrayBuffer(toBuffer(fragments, messageLength));
data = toArrayBuffer(concat(fragments, messageLength));
} else {
data = fragments;
}
this.emit('message', data);
} else {
const buf = toBuffer(fragments, messageLength);
const buf = concat(fragments, messageLength);
if (!validation.isValidUTF8(buf)) {
if (!isValidUTF8(buf)) {
this._loop = false;
return error(Error, 'invalid UTF-8 sequence', true, 1007);
return error(
Error,
'invalid UTF-8 sequence',
true,
1007,
'WS_ERR_INVALID_UTF8'
);
}
this.emit('message', buf.toString());
@@ -425,7 +528,7 @@ class Receiver extends stream.Writable {
* @return {(Error|RangeError|undefined)} A possible error
* @private
*/
controlMessage (data) {
controlMessage(data) {
if (this._opcode === 0x08) {
this._loop = false;
@@ -433,30 +536,47 @@ class Receiver extends stream.Writable {
this.emit('conclude', 1005, '');
this.end();
} else if (data.length === 1) {
return error(RangeError, 'invalid payload length 1', true, 1002);
return error(
RangeError,
'invalid payload length 1',
true,
1002,
'WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH'
);
} else {
const code = data.readUInt16BE(0);
if (!validation.isValidStatusCode(code)) {
return error(RangeError, `invalid status code ${code}`, true, 1002);
if (!isValidStatusCode(code)) {
return error(
RangeError,
`invalid status code ${code}`,
true,
1002,
'WS_ERR_INVALID_CLOSE_CODE'
);
}
const buf = data.slice(2);
if (!validation.isValidUTF8(buf)) {
return error(Error, 'invalid UTF-8 sequence', true, 1007);
if (!isValidUTF8(buf)) {
return error(
Error,
'invalid UTF-8 sequence',
true,
1007,
'WS_ERR_INVALID_UTF8'
);
}
this.emit('conclude', code, buf.toString());
this.end();
}
return;
} else if (this._opcode === 0x09) {
this.emit('ping', data);
} else {
this.emit('pong', data);
}
if (this._opcode === 0x09) this.emit('ping', data);
else this.emit('pong', data);
this._state = GET_INFO;
}
}
@@ -466,48 +586,22 @@ module.exports = Receiver;
/**
* Builds an error object.
*
* @param {(Error|RangeError)} ErrorCtor The error constructor
* @param {function(new:Error|RangeError)} ErrorCtor The error constructor
* @param {String} message The error message
* @param {Boolean} prefix Specifies whether or not to add a default prefix to
* `message`
* @param {Number} statusCode The status code
* @param {String} errorCode The exposed error code
* @return {(Error|RangeError)} The error
* @private
*/
function error (ErrorCtor, message, prefix, statusCode) {
function error(ErrorCtor, message, prefix, statusCode, errorCode) {
const err = new ErrorCtor(
prefix ? `Invalid WebSocket frame: ${message}` : message
);
Error.captureStackTrace(err, error);
err[constants.kStatusCode] = statusCode;
err.code = errorCode;
err[kStatusCode] = statusCode;
return err;
}
/**
* Makes a buffer from a list of fragments.
*
* @param {Buffer[]} fragments The list of fragments composing the message
* @param {Number} messageLength The length of the message
* @return {Buffer}
* @private
*/
function toBuffer (fragments, messageLength) {
if (fragments.length === 1) return fragments[0];
if (fragments.length > 1) return bufferUtil.concat(fragments, messageLength);
return constants.EMPTY_BUFFER;
}
/**
* Converts a buffer to an `ArrayBuffer`.
*
* @param {Buffer} The buffer to convert
* @return {ArrayBuffer} Converted buffer
*/
function toArrayBuffer (buf) {
if (buf.byteOffset === 0 && buf.byteLength === buf.buffer.byteLength) {
return buf.buffer;
}
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
}