Dep update

This commit is contained in:
2025-04-08 22:00:41 +02:00
committed by Philipp Horstenkamp
parent f79e41cbfe
commit 78549e06b4
966 changed files with 43637 additions and 158248 deletions

85
node_modules/ws/lib/buffer-util.js generated vendored
View File

@ -1,5 +1,7 @@
'use strict';
const { EMPTY_BUFFER } = require('./constants');
/**
* Merges an array of buffers into a new buffer.
*
@ -8,16 +10,21 @@
* @return {Buffer} The resulting buffer
* @public
*/
function concat (list, totalLength) {
const target = Buffer.allocUnsafe(totalLength);
var offset = 0;
function concat(list, totalLength) {
if (list.length === 0) return EMPTY_BUFFER;
if (list.length === 1) return list[0];
for (var i = 0; i < list.length; i++) {
const target = Buffer.allocUnsafe(totalLength);
let offset = 0;
for (let i = 0; i < list.length; i++) {
const buf = list[i];
buf.copy(target, offset);
target.set(buf, offset);
offset += buf.length;
}
if (offset < totalLength) return target.slice(0, offset);
return target;
}
@ -31,8 +38,8 @@ function concat (list, totalLength) {
* @param {Number} length The number of bytes to mask.
* @public
*/
function _mask (source, mask, output, offset, length) {
for (var i = 0; i < length; i++) {
function _mask(source, mask, output, offset, length) {
for (let i = 0; i < length; i++) {
output[offset + i] = source[i] ^ mask[i & 3];
}
}
@ -44,29 +51,79 @@ function _mask (source, mask, output, offset, length) {
* @param {Buffer} mask The mask to use
* @public
*/
function _unmask (buffer, mask) {
function _unmask(buffer, mask) {
// Required until https://github.com/nodejs/node/issues/9006 is resolved.
const length = buffer.length;
for (var i = 0; i < length; i++) {
for (let i = 0; i < length; i++) {
buffer[i] ^= mask[i & 3];
}
}
/**
* Converts a buffer to an `ArrayBuffer`.
*
* @param {Buffer} buf The buffer to convert
* @return {ArrayBuffer} Converted buffer
* @public
*/
function toArrayBuffer(buf) {
if (buf.byteLength === buf.buffer.byteLength) {
return buf.buffer;
}
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
}
/**
* Converts `data` to a `Buffer`.
*
* @param {*} data The data to convert
* @return {Buffer} The buffer
* @throws {TypeError}
* @public
*/
function toBuffer(data) {
toBuffer.readOnly = true;
if (Buffer.isBuffer(data)) return data;
let buf;
if (data instanceof ArrayBuffer) {
buf = Buffer.from(data);
} else if (ArrayBuffer.isView(data)) {
buf = Buffer.from(data.buffer, data.byteOffset, data.byteLength);
} else {
buf = Buffer.from(data);
toBuffer.readOnly = false;
}
return buf;
}
try {
const bufferUtil = require('bufferutil');
const bu = bufferUtil.BufferUtil || bufferUtil;
module.exports = {
mask (source, mask, output, offset, length) {
concat,
mask(source, mask, output, offset, length) {
if (length < 48) _mask(source, mask, output, offset, length);
else bu.mask(source, mask, output, offset, length);
},
unmask (buffer, mask) {
toArrayBuffer,
toBuffer,
unmask(buffer, mask) {
if (buffer.length < 32) _unmask(buffer, mask);
else bu.unmask(buffer, mask);
},
concat
}
};
} catch (e) /* istanbul ignore next */ {
module.exports = { concat, mask: _mask, unmask: _unmask };
module.exports = {
concat,
mask: _mask,
toArrayBuffer,
toBuffer,
unmask: _unmask
};
}

78
node_modules/ws/lib/event-target.js generated vendored
View File

@ -10,9 +10,10 @@ class Event {
* Create a new `Event`.
*
* @param {String} type The name of the event
* @param {Object} target A reference to the target to which the event was dispatched
* @param {Object} target A reference to the target to which the event was
* dispatched
*/
constructor (type, target) {
constructor(type, target) {
this.target = target;
this.type = type;
}
@ -29,9 +30,10 @@ class MessageEvent extends Event {
* Create a new `MessageEvent`.
*
* @param {(String|Buffer|ArrayBuffer|Buffer[])} data The received data
* @param {WebSocket} target A reference to the target to which the event was dispatched
* @param {WebSocket} target A reference to the target to which the event was
* dispatched
*/
constructor (data, target) {
constructor(data, target) {
super('message', target);
this.data = data;
@ -48,11 +50,14 @@ class CloseEvent extends Event {
/**
* Create a new `CloseEvent`.
*
* @param {Number} code The status code explaining why the connection is being closed
* @param {String} reason A human-readable string explaining why the connection is closing
* @param {WebSocket} target A reference to the target to which the event was dispatched
* @param {Number} code The status code explaining why the connection is being
* closed
* @param {String} reason A human-readable string explaining why the
* connection is closing
* @param {WebSocket} target A reference to the target to which the event was
* dispatched
*/
constructor (code, reason, target) {
constructor(code, reason, target) {
super('close', target);
this.wasClean = target._closeFrameReceived && target._closeFrameSent;
@ -71,9 +76,10 @@ class OpenEvent extends Event {
/**
* Create a new `OpenEvent`.
*
* @param {WebSocket} target A reference to the target to which the event was dispatched
* @param {WebSocket} target A reference to the target to which the event was
* dispatched
*/
constructor (target) {
constructor(target) {
super('open', target);
}
}
@ -89,9 +95,10 @@ class ErrorEvent extends Event {
* Create a new `ErrorEvent`.
*
* @param {Object} error The error that generated this event
* @param {WebSocket} target A reference to the target to which the event was dispatched
* @param {WebSocket} target A reference to the target to which the event was
* dispatched
*/
constructor (error, target) {
constructor(error, target) {
super('error', target);
this.message = error.message;
@ -109,59 +116,66 @@ const EventTarget = {
/**
* Register an event listener.
*
* @param {String} method A string representing the event type to listen for
* @param {String} type A string representing the event type to listen for
* @param {Function} listener The listener to add
* @param {Object} [options] An options object specifies characteristics about
* the event listener
* @param {Boolean} [options.once=false] A `Boolean`` indicating that the
* listener should be invoked at most once after being added. If `true`,
* the listener would be automatically removed when invoked.
* @public
*/
addEventListener (method, listener) {
addEventListener(type, listener, options) {
if (typeof listener !== 'function') return;
function onMessage (data) {
function onMessage(data) {
listener.call(this, new MessageEvent(data, this));
}
function onClose (code, message) {
function onClose(code, message) {
listener.call(this, new CloseEvent(code, message, this));
}
function onError (error) {
function onError(error) {
listener.call(this, new ErrorEvent(error, this));
}
function onOpen () {
function onOpen() {
listener.call(this, new OpenEvent(this));
}
if (method === 'message') {
const method = options && options.once ? 'once' : 'on';
if (type === 'message') {
onMessage._listener = listener;
this.on(method, onMessage);
} else if (method === 'close') {
this[method](type, onMessage);
} else if (type === 'close') {
onClose._listener = listener;
this.on(method, onClose);
} else if (method === 'error') {
this[method](type, onClose);
} else if (type === 'error') {
onError._listener = listener;
this.on(method, onError);
} else if (method === 'open') {
this[method](type, onError);
} else if (type === 'open') {
onOpen._listener = listener;
this.on(method, onOpen);
this[method](type, onOpen);
} else {
this.on(method, listener);
this[method](type, listener);
}
},
/**
* Remove an event listener.
*
* @param {String} method A string representing the event type to remove
* @param {String} type A string representing the event type to remove
* @param {Function} listener The listener to remove
* @public
*/
removeEventListener (method, listener) {
const listeners = this.listeners(method);
removeEventListener(type, listener) {
const listeners = this.listeners(type);
for (var i = 0; i < listeners.length; i++) {
for (let i = 0; i < listeners.length; i++) {
if (listeners[i] === listener || listeners[i]._listener === listener) {
this.removeListener(method, listeners[i]);
this.removeListener(type, listeners[i]);
}
}
}

84
node_modules/ws/lib/extension.js generated vendored
View File

@ -11,6 +11,7 @@
// tokenChars[34] === 0 // '"'
// ...
//
// prettier-ignore
const tokenChars = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31
@ -32,9 +33,9 @@ const tokenChars = [
* parameter value
* @private
*/
function push (dest, name, elem) {
if (Object.prototype.hasOwnProperty.call(dest, name)) dest[name].push(elem);
else dest[name] = [elem];
function push(dest, name, elem) {
if (dest[name] === undefined) dest[name] = [elem];
else dest[name].push(elem);
}
/**
@ -44,29 +45,30 @@ function push (dest, name, elem) {
* @return {Object} The parsed object
* @public
*/
function parse (header) {
const offers = {};
function parse(header) {
const offers = Object.create(null);
if (header === undefined || header === '') return offers;
var params = {};
var mustUnescape = false;
var isEscaping = false;
var inQuotes = false;
var extensionName;
var paramName;
var start = -1;
var end = -1;
let params = Object.create(null);
let mustUnescape = false;
let isEscaping = false;
let inQuotes = false;
let extensionName;
let paramName;
let start = -1;
let end = -1;
let i = 0;
for (var i = 0; i < header.length; i++) {
for (; i < header.length; i++) {
const code = header.charCodeAt(i);
if (extensionName === undefined) {
if (end === -1 && tokenChars[code] === 1) {
if (start === -1) start = i;
} else if (code === 0x20/* ' ' */|| code === 0x09/* '\t' */) {
} else if (code === 0x20 /* ' ' */ || code === 0x09 /* '\t' */) {
if (end === -1 && start !== -1) end = i;
} else if (code === 0x3b/* ';' */ || code === 0x2c/* ',' */) {
} else if (code === 0x3b /* ';' */ || code === 0x2c /* ',' */) {
if (start === -1) {
throw new SyntaxError(`Unexpected character at index ${i}`);
}
@ -75,7 +77,7 @@ function parse (header) {
const name = header.slice(start, end);
if (code === 0x2c) {
push(offers, name, params);
params = {};
params = Object.create(null);
} else {
extensionName = name;
}
@ -98,12 +100,12 @@ function parse (header) {
push(params, header.slice(start, end), true);
if (code === 0x2c) {
push(offers, extensionName, params);
params = {};
params = Object.create(null);
extensionName = undefined;
}
start = end = -1;
} else if (code === 0x3d/* '=' */&& start !== -1 && end === -1) {
} else if (code === 0x3d /* '=' */ && start !== -1 && end === -1) {
paramName = header.slice(start, i);
start = end = -1;
} else {
@ -125,10 +127,10 @@ function parse (header) {
} else if (inQuotes) {
if (tokenChars[code] === 1) {
if (start === -1) start = i;
} else if (code === 0x22/* '"' */ && start !== -1) {
} else if (code === 0x22 /* '"' */ && start !== -1) {
inQuotes = false;
end = i;
} else if (code === 0x5c/* '\' */) {
} else if (code === 0x5c /* '\' */) {
isEscaping = true;
} else {
throw new SyntaxError(`Unexpected character at index ${i}`);
@ -145,7 +147,7 @@ function parse (header) {
}
if (end === -1) end = i;
var value = header.slice(start, end);
let value = header.slice(start, end);
if (mustUnescape) {
value = value.replace(/\\/g, '');
mustUnescape = false;
@ -153,7 +155,7 @@ function parse (header) {
push(params, paramName, value);
if (code === 0x2c) {
push(offers, extensionName, params);
params = {};
params = Object.create(null);
extensionName = undefined;
}
@ -172,7 +174,7 @@ function parse (header) {
if (end === -1) end = i;
const token = header.slice(start, end);
if (extensionName === undefined) {
push(offers, token, {});
push(offers, token, params);
} else {
if (paramName === undefined) {
push(params, token, true);
@ -194,18 +196,28 @@ function parse (header) {
* @return {String} A string representing the given object
* @public
*/
function format (extensions) {
return Object.keys(extensions).map((extension) => {
var configurations = extensions[extension];
if (!Array.isArray(configurations)) configurations = [configurations];
return configurations.map((params) => {
return [extension].concat(Object.keys(params).map((k) => {
var values = params[k];
if (!Array.isArray(values)) values = [values];
return values.map((v) => v === true ? k : `${k}=${v}`).join('; ');
})).join('; ');
}).join(', ');
}).join(', ');
function format(extensions) {
return Object.keys(extensions)
.map((extension) => {
let configurations = extensions[extension];
if (!Array.isArray(configurations)) configurations = [configurations];
return configurations
.map((params) => {
return [extension]
.concat(
Object.keys(params).map((k) => {
let values = params[k];
if (!Array.isArray(values)) values = [values];
return values
.map((v) => (v === true ? k : `${k}=${v}`))
.join('; ');
})
)
.join('; ');
})
.join(', ');
})
.join(', ');
}
module.exports = { format, parse };

55
node_modules/ws/lib/limiter.js generated vendored Normal file
View File

@ -0,0 +1,55 @@
'use strict';
const kDone = Symbol('kDone');
const kRun = Symbol('kRun');
/**
* A very simple job queue with adjustable concurrency. Adapted from
* https://github.com/STRML/async-limiter
*/
class Limiter {
/**
* Creates a new `Limiter`.
*
* @param {Number} [concurrency=Infinity] The maximum number of jobs allowed
* to run concurrently
*/
constructor(concurrency) {
this[kDone] = () => {
this.pending--;
this[kRun]();
};
this.concurrency = concurrency || Infinity;
this.jobs = [];
this.pending = 0;
}
/**
* Adds a job to the queue.
*
* @param {Function} job The job to run
* @public
*/
add(job) {
this.jobs.push(job);
this[kRun]();
}
/**
* Removes a job from the queue and runs it if possible.
*
* @private
*/
[kRun]() {
if (this.pending === this.concurrency) return;
if (this.jobs.length) {
const job = this.jobs.shift();
this.pending++;
job(this[kDone]);
}
}
}
module.exports = Limiter;

View File

@ -1,17 +1,13 @@
'use strict';
const Limiter = require('async-limiter');
const zlib = require('zlib');
const bufferUtil = require('./buffer-util');
const constants = require('./constants');
const Limiter = require('./limiter');
const { kStatusCode, NOOP } = require('./constants');
const TRAILER = Buffer.from([0x00, 0x00, 0xff, 0xff]);
const EMPTY_BLOCK = Buffer.from([0x00]);
const kPerMessageDeflate = Symbol('permessage-deflate');
const kWriteInProgress = Symbol('write-in-progress');
const kPendingClose = Symbol('pending-close');
const kTotalLength = Symbol('total-length');
const kCallback = Symbol('callback');
const kBuffers = Symbol('buffers');
@ -33,31 +29,32 @@ class PerMessageDeflate {
/**
* Creates a PerMessageDeflate instance.
*
* @param {Object} options Configuration options
* @param {Boolean} options.serverNoContextTakeover Request/accept disabling
* of server context takeover
* @param {Boolean} options.clientNoContextTakeover Advertise/acknowledge
* disabling of client context takeover
* @param {(Boolean|Number)} options.serverMaxWindowBits Request/confirm the
* @param {Object} [options] Configuration options
* @param {Boolean} [options.serverNoContextTakeover=false] Request/accept
* disabling of server context takeover
* @param {Boolean} [options.clientNoContextTakeover=false] Advertise/
* acknowledge disabling of client context takeover
* @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the
* use of a custom server window size
* @param {(Boolean|Number)} options.clientMaxWindowBits Advertise support
* @param {(Boolean|Number)} [options.clientMaxWindowBits] Advertise support
* for, or request, a custom client window size
* @param {Object} options.zlibDeflateOptions Options to pass to zlib on deflate
* @param {Object} options.zlibInflateOptions Options to pass to zlib on inflate
* @param {Number} options.threshold Size (in bytes) below which messages
* should not be compressed
* @param {Number} options.concurrencyLimit The number of concurrent calls to
* zlib
* @param {Boolean} isServer Create the instance in either server or client
* mode
* @param {Number} maxPayload The maximum allowed message length
* @param {Object} [options.zlibDeflateOptions] Options to pass to zlib on
* deflate
* @param {Object} [options.zlibInflateOptions] Options to pass to zlib on
* inflate
* @param {Number} [options.threshold=1024] Size (in bytes) below which
* messages should not be compressed
* @param {Number} [options.concurrencyLimit=10] The number of concurrent
* calls to zlib
* @param {Boolean} [isServer=false] Create the instance in either server or
* client mode
* @param {Number} [maxPayload=0] The maximum allowed message length
*/
constructor (options, isServer, maxPayload) {
constructor(options, isServer, maxPayload) {
this._maxPayload = maxPayload | 0;
this._options = options || {};
this._threshold = this._options.threshold !== undefined
? this._options.threshold
: 1024;
this._threshold =
this._options.threshold !== undefined ? this._options.threshold : 1024;
this._isServer = !!isServer;
this._deflate = null;
this._inflate = null;
@ -65,17 +62,18 @@ class PerMessageDeflate {
this.params = null;
if (!zlibLimiter) {
const concurrency = this._options.concurrencyLimit !== undefined
? this._options.concurrencyLimit
: 10;
zlibLimiter = new Limiter({ concurrency });
const concurrency =
this._options.concurrencyLimit !== undefined
? this._options.concurrencyLimit
: 10;
zlibLimiter = new Limiter(concurrency);
}
}
/**
* @type {String}
*/
static get extensionName () {
static get extensionName() {
return 'permessage-deflate';
}
@ -85,7 +83,7 @@ class PerMessageDeflate {
* @return {Object} Extension parameters
* @public
*/
offer () {
offer() {
const params = {};
if (this._options.serverNoContextTakeover) {
@ -113,7 +111,7 @@ class PerMessageDeflate {
* @return {Object} Accepted configuration
* @public
*/
accept (configurations) {
accept(configurations) {
configurations = this.normalizeParams(configurations);
this.params = this._isServer
@ -128,21 +126,24 @@ class PerMessageDeflate {
*
* @public
*/
cleanup () {
cleanup() {
if (this._inflate) {
if (this._inflate[kWriteInProgress]) {
this._inflate[kPendingClose] = true;
} else {
this._inflate.close();
this._inflate = null;
}
this._inflate.close();
this._inflate = null;
}
if (this._deflate) {
if (this._deflate[kWriteInProgress]) {
this._deflate[kPendingClose] = true;
} else {
this._deflate.close();
this._deflate = null;
const callback = this._deflate[kCallback];
this._deflate.close();
this._deflate = null;
if (callback) {
callback(
new Error(
'The deflate stream was closed while data was being processed'
)
);
}
}
}
@ -154,7 +155,7 @@ class PerMessageDeflate {
* @return {Object} Accepted configuration
* @private
*/
acceptAsServer (offers) {
acceptAsServer(offers) {
const opts = this._options;
const accepted = offers.find((params) => {
if (
@ -205,7 +206,7 @@ class PerMessageDeflate {
* @return {Object} Accepted configuration
* @private
*/
acceptAsClient (response) {
acceptAsClient(response) {
const params = response[0];
if (
@ -239,10 +240,10 @@ class PerMessageDeflate {
* @return {Array} The offers/response with normalized parameters
* @private
*/
normalizeParams (configurations) {
normalizeParams(configurations) {
configurations.forEach((params) => {
Object.keys(params).forEach((key) => {
var value = params[key];
let value = params[key];
if (value.length > 1) {
throw new Error(`Parameter "${key}" must have only a single value`);
@ -293,15 +294,15 @@ class PerMessageDeflate {
}
/**
* Decompress data. Concurrency limited by async-limiter.
* Decompress data. Concurrency limited.
*
* @param {Buffer} data Compressed data
* @param {Boolean} fin Specifies whether or not this is the last fragment
* @param {Function} callback Callback
* @public
*/
decompress (data, fin, callback) {
zlibLimiter.push((done) => {
decompress(data, fin, callback) {
zlibLimiter.add((done) => {
this._decompress(data, fin, (err, result) => {
done();
callback(err, result);
@ -310,15 +311,15 @@ class PerMessageDeflate {
}
/**
* Compress data. Concurrency limited by async-limiter.
* Compress data. Concurrency limited.
*
* @param {Buffer} data Data to compress
* @param {Boolean} fin Specifies whether or not this is the last fragment
* @param {Function} callback Callback
* @public
*/
compress (data, fin, callback) {
zlibLimiter.push((done) => {
compress(data, fin, callback) {
zlibLimiter.add((done) => {
this._compress(data, fin, (err, result) => {
done();
callback(err, result);
@ -334,18 +335,20 @@ class PerMessageDeflate {
* @param {Function} callback Callback
* @private
*/
_decompress (data, fin, callback) {
_decompress(data, fin, callback) {
const endpoint = this._isServer ? 'client' : 'server';
if (!this._inflate) {
const key = `${endpoint}_max_window_bits`;
const windowBits = typeof this.params[key] !== 'number'
? zlib.Z_DEFAULT_WINDOWBITS
: this.params[key];
const windowBits =
typeof this.params[key] !== 'number'
? zlib.Z_DEFAULT_WINDOWBITS
: this.params[key];
this._inflate = zlib.createInflateRaw(
Object.assign({}, this._options.zlibInflateOptions, { windowBits })
);
this._inflate = zlib.createInflateRaw({
...this._options.zlibInflateOptions,
windowBits
});
this._inflate[kPerMessageDeflate] = this;
this._inflate[kTotalLength] = 0;
this._inflate[kBuffers] = [];
@ -354,7 +357,6 @@ class PerMessageDeflate {
}
this._inflate[kCallback] = callback;
this._inflate[kWriteInProgress] = true;
this._inflate.write(data);
if (fin) this._inflate.write(TRAILER);
@ -374,16 +376,16 @@ class PerMessageDeflate {
this._inflate[kTotalLength]
);
if (
(fin && this.params[`${endpoint}_no_context_takeover`]) ||
this._inflate[kPendingClose]
) {
if (this._inflate._readableState.endEmitted) {
this._inflate.close();
this._inflate = null;
} else {
this._inflate[kWriteInProgress] = false;
this._inflate[kTotalLength] = 0;
this._inflate[kBuffers] = [];
if (fin && this.params[`${endpoint}_no_context_takeover`]) {
this._inflate.reset();
}
}
callback(null, data);
@ -398,64 +400,63 @@ class PerMessageDeflate {
* @param {Function} callback Callback
* @private
*/
_compress (data, fin, callback) {
if (!data || data.length === 0) {
process.nextTick(callback, null, EMPTY_BLOCK);
return;
}
_compress(data, fin, callback) {
const endpoint = this._isServer ? 'server' : 'client';
if (!this._deflate) {
const key = `${endpoint}_max_window_bits`;
const windowBits = typeof this.params[key] !== 'number'
? zlib.Z_DEFAULT_WINDOWBITS
: this.params[key];
const windowBits =
typeof this.params[key] !== 'number'
? zlib.Z_DEFAULT_WINDOWBITS
: this.params[key];
this._deflate = zlib.createDeflateRaw(
Object.assign(
// TODO deprecate memLevel/level and recommend zlibDeflateOptions instead
{
memLevel: this._options.memLevel,
level: this._options.level
},
this._options.zlibDeflateOptions,
{ windowBits }
)
);
this._deflate = zlib.createDeflateRaw({
...this._options.zlibDeflateOptions,
windowBits
});
this._deflate[kTotalLength] = 0;
this._deflate[kBuffers] = [];
//
// `zlib.DeflateRaw` emits an `'error'` event only when an attempt to use
// it is made after it has already been closed. This cannot happen here,
// so we only add a listener for the `'data'` event.
// An `'error'` event is emitted, only on Node.js < 10.0.0, if the
// `zlib.DeflateRaw` instance is closed while data is being processed.
// This can happen if `PerMessageDeflate#cleanup()` is called at the wrong
// time due to an abnormal WebSocket closure.
//
this._deflate.on('error', NOOP);
this._deflate.on('data', deflateOnData);
}
this._deflate[kWriteInProgress] = true;
this._deflate[kCallback] = callback;
this._deflate.write(data);
this._deflate.flush(zlib.Z_SYNC_FLUSH, () => {
var data = bufferUtil.concat(
if (!this._deflate) {
//
// The deflate stream was closed while data was being processed.
//
return;
}
let data = bufferUtil.concat(
this._deflate[kBuffers],
this._deflate[kTotalLength]
);
if (fin) data = data.slice(0, data.length - 4);
if (
(fin && this.params[`${endpoint}_no_context_takeover`]) ||
this._deflate[kPendingClose]
) {
this._deflate.close();
this._deflate = null;
} else {
this._deflate[kWriteInProgress] = false;
this._deflate[kTotalLength] = 0;
this._deflate[kBuffers] = [];
//
// Ensure that the callback will not be called again in
// `PerMessageDeflate#cleanup()`.
//
this._deflate[kCallback] = null;
this._deflate[kTotalLength] = 0;
this._deflate[kBuffers] = [];
if (fin && this.params[`${endpoint}_no_context_takeover`]) {
this._deflate.reset();
}
callback(null, data);
@ -471,7 +472,7 @@ module.exports = PerMessageDeflate;
* @param {Buffer} chunk A chunk of data
* @private
*/
function deflateOnData (chunk) {
function deflateOnData(chunk) {
this[kBuffers].push(chunk);
this[kTotalLength] += chunk.length;
}
@ -482,7 +483,7 @@ function deflateOnData (chunk) {
* @param {Buffer} chunk A chunk of data
* @private
*/
function inflateOnData (chunk) {
function inflateOnData(chunk) {
this[kTotalLength] += chunk.length;
if (
@ -494,7 +495,8 @@ function inflateOnData (chunk) {
}
this[kError] = new RangeError('Max payload size exceeded');
this[kError][constants.kStatusCode] = 1009;
this[kError].code = 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH';
this[kError][kStatusCode] = 1009;
this.removeListener('data', inflateOnData);
this.reset();
}
@ -505,12 +507,12 @@ function inflateOnData (chunk) {
* @param {Error} err The emitted error
* @private
*/
function inflateOnError (err) {
function inflateOnError(err) {
//
// There is no need to call `Zlib#close()` as the handle is automatically
// closed when an error is emitted.
//
this[kPerMessageDeflate]._inflate = null;
err[constants.kStatusCode] = 1007;
err[kStatusCode] = 1007;
this[kCallback](err);
}

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);
}

334
node_modules/ws/lib/sender.js generated vendored
View File

@ -1,11 +1,17 @@
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^net|tls$" }] */
'use strict';
const crypto = require('crypto');
const net = require('net');
const tls = require('tls');
const { randomFillSync } = require('crypto');
const PerMessageDeflate = require('./permessage-deflate');
const bufferUtil = require('./buffer-util');
const validation = require('./validation');
const constants = require('./constants');
const { EMPTY_BUFFER } = require('./constants');
const { isValidStatusCode } = require('./validation');
const { mask: applyMask, toBuffer } = require('./buffer-util');
const mask = Buffer.alloc(4);
/**
* HyBi Sender implementation.
@ -14,10 +20,10 @@ class Sender {
/**
* Creates a Sender instance.
*
* @param {net.Socket} socket The connection socket
* @param {Object} extensions An object containing the negotiated extensions
* @param {(net.Socket|tls.Socket)} socket The connection socket
* @param {Object} [extensions] An object containing the negotiated extensions
*/
constructor (socket, extensions) {
constructor(socket, extensions) {
this._extensions = extensions || {};
this._socket = socket;
@ -35,17 +41,21 @@ class Sender {
* @param {Buffer} data The data to frame
* @param {Object} options Options object
* @param {Number} options.opcode The opcode
* @param {Boolean} options.readOnly Specifies whether `data` can be modified
* @param {Boolean} options.fin Specifies whether or not to set the FIN bit
* @param {Boolean} options.mask Specifies whether or not to mask `data`
* @param {Boolean} options.rsv1 Specifies whether or not to set the RSV1 bit
* @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
* modified
* @param {Boolean} [options.fin=false] Specifies whether or not to set the
* FIN bit
* @param {Boolean} [options.mask=false] Specifies whether or not to mask
* `data`
* @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
* RSV1 bit
* @return {Buffer[]} The framed data as a list of `Buffer` instances
* @public
*/
static frame (data, options) {
const merge = data.length < 1024 || (options.mask && options.readOnly);
var offset = options.mask ? 6 : 2;
var payloadLength = data.length;
static frame(data, options) {
const merge = options.mask && options.readOnly;
let offset = options.mask ? 6 : 2;
let payloadLength = data.length;
if (data.length >= 65536) {
offset += 8;
@ -60,6 +70,8 @@ class Sender {
target[0] = options.fin ? options.opcode | 0x80 : options.opcode;
if (options.rsv1) target[0] |= 0x40;
target[1] = payloadLength;
if (payloadLength === 126) {
target.writeUInt16BE(data.length, 2);
} else if (payloadLength === 127) {
@ -67,54 +79,52 @@ class Sender {
target.writeUInt32BE(data.length, 6);
}
if (!options.mask) {
target[1] = payloadLength;
if (merge) {
data.copy(target, offset);
return [target];
}
if (!options.mask) return [target, data];
return [target, data];
}
randomFillSync(mask, 0, 4);
const mask = crypto.randomBytes(4);
target[1] = payloadLength | 0x80;
target[1] |= 0x80;
target[offset - 4] = mask[0];
target[offset - 3] = mask[1];
target[offset - 2] = mask[2];
target[offset - 1] = mask[3];
if (merge) {
bufferUtil.mask(data, mask, target, offset, data.length);
applyMask(data, mask, target, offset, data.length);
return [target];
}
bufferUtil.mask(data, mask, data, 0, data.length);
applyMask(data, mask, data, 0, data.length);
return [target, data];
}
/**
* Sends a close message to the other peer.
*
* @param {(Number|undefined)} code The status code component of the body
* @param {String} data The message component of the body
* @param {Boolean} mask Specifies whether or not to mask the message
* @param {Function} cb Callback
* @param {Number} [code] The status code component of the body
* @param {String} [data] The message component of the body
* @param {Boolean} [mask=false] Specifies whether or not to mask the message
* @param {Function} [cb] Callback
* @public
*/
close (code, data, mask, cb) {
var buf;
close(code, data, mask, cb) {
let buf;
if (code === undefined) {
buf = constants.EMPTY_BUFFER;
} else if (typeof code !== 'number' || !validation.isValidStatusCode(code)) {
buf = EMPTY_BUFFER;
} else if (typeof code !== 'number' || !isValidStatusCode(code)) {
throw new TypeError('First argument must be a valid error code number');
} else if (data === undefined || data === '') {
buf = Buffer.allocUnsafe(2);
buf.writeUInt16BE(code, 0);
} else {
buf = Buffer.allocUnsafe(2 + Buffer.byteLength(data));
const length = Buffer.byteLength(data);
if (length > 123) {
throw new RangeError('The message must not be greater than 123 bytes');
}
buf = Buffer.allocUnsafe(2 + length);
buf.writeUInt16BE(code, 0);
buf.write(data, 2);
}
@ -130,114 +140,109 @@ class Sender {
* Frames and sends a close message.
*
* @param {Buffer} data The message to send
* @param {Boolean} mask Specifies whether or not to mask `data`
* @param {Function} cb Callback
* @param {Boolean} [mask=false] Specifies whether or not to mask `data`
* @param {Function} [cb] Callback
* @private
*/
doClose (data, mask, cb) {
this.sendFrame(Sender.frame(data, {
fin: true,
rsv1: false,
opcode: 0x08,
mask,
readOnly: false
}), cb);
doClose(data, mask, cb) {
this.sendFrame(
Sender.frame(data, {
fin: true,
rsv1: false,
opcode: 0x08,
mask,
readOnly: false
}),
cb
);
}
/**
* Sends a ping message to the other peer.
*
* @param {*} data The message to send
* @param {Boolean} mask Specifies whether or not to mask `data`
* @param {Function} cb Callback
* @param {Boolean} [mask=false] Specifies whether or not to mask `data`
* @param {Function} [cb] Callback
* @public
*/
ping (data, mask, cb) {
var readOnly = true;
ping(data, mask, cb) {
const buf = toBuffer(data);
if (!Buffer.isBuffer(data)) {
if (data instanceof ArrayBuffer) {
data = Buffer.from(data);
} else if (ArrayBuffer.isView(data)) {
data = viewToBuffer(data);
} else {
data = Buffer.from(data);
readOnly = false;
}
if (buf.length > 125) {
throw new RangeError('The data size must not be greater than 125 bytes');
}
if (this._deflating) {
this.enqueue([this.doPing, data, mask, readOnly, cb]);
this.enqueue([this.doPing, buf, mask, toBuffer.readOnly, cb]);
} else {
this.doPing(data, mask, readOnly, cb);
this.doPing(buf, mask, toBuffer.readOnly, cb);
}
}
/**
* Frames and sends a ping message.
*
* @param {*} data The message to send
* @param {Boolean} mask Specifies whether or not to mask `data`
* @param {Boolean} readOnly Specifies whether `data` can be modified
* @param {Function} cb Callback
* @param {Buffer} data The message to send
* @param {Boolean} [mask=false] Specifies whether or not to mask `data`
* @param {Boolean} [readOnly=false] Specifies whether `data` can be modified
* @param {Function} [cb] Callback
* @private
*/
doPing (data, mask, readOnly, cb) {
this.sendFrame(Sender.frame(data, {
fin: true,
rsv1: false,
opcode: 0x09,
mask,
readOnly
}), cb);
doPing(data, mask, readOnly, cb) {
this.sendFrame(
Sender.frame(data, {
fin: true,
rsv1: false,
opcode: 0x09,
mask,
readOnly
}),
cb
);
}
/**
* Sends a pong message to the other peer.
*
* @param {*} data The message to send
* @param {Boolean} mask Specifies whether or not to mask `data`
* @param {Function} cb Callback
* @param {Boolean} [mask=false] Specifies whether or not to mask `data`
* @param {Function} [cb] Callback
* @public
*/
pong (data, mask, cb) {
var readOnly = true;
pong(data, mask, cb) {
const buf = toBuffer(data);
if (!Buffer.isBuffer(data)) {
if (data instanceof ArrayBuffer) {
data = Buffer.from(data);
} else if (ArrayBuffer.isView(data)) {
data = viewToBuffer(data);
} else {
data = Buffer.from(data);
readOnly = false;
}
if (buf.length > 125) {
throw new RangeError('The data size must not be greater than 125 bytes');
}
if (this._deflating) {
this.enqueue([this.doPong, data, mask, readOnly, cb]);
this.enqueue([this.doPong, buf, mask, toBuffer.readOnly, cb]);
} else {
this.doPong(data, mask, readOnly, cb);
this.doPong(buf, mask, toBuffer.readOnly, cb);
}
}
/**
* Frames and sends a pong message.
*
* @param {*} data The message to send
* @param {Boolean} mask Specifies whether or not to mask `data`
* @param {Boolean} readOnly Specifies whether `data` can be modified
* @param {Function} cb Callback
* @param {Buffer} data The message to send
* @param {Boolean} [mask=false] Specifies whether or not to mask `data`
* @param {Boolean} [readOnly=false] Specifies whether `data` can be modified
* @param {Function} [cb] Callback
* @private
*/
doPong (data, mask, readOnly, cb) {
this.sendFrame(Sender.frame(data, {
fin: true,
rsv1: false,
opcode: 0x0a,
mask,
readOnly
}), cb);
doPong(data, mask, readOnly, cb) {
this.sendFrame(
Sender.frame(data, {
fin: true,
rsv1: false,
opcode: 0x0a,
mask,
readOnly
}),
cb
);
}
/**
@ -245,35 +250,27 @@ class Sender {
*
* @param {*} data The message to send
* @param {Object} options Options object
* @param {Boolean} options.compress Specifies whether or not to compress `data`
* @param {Boolean} options.binary Specifies whether `data` is binary or text
* @param {Boolean} options.fin Specifies whether the fragment is the last one
* @param {Boolean} options.mask Specifies whether or not to mask `data`
* @param {Function} cb Callback
* @param {Boolean} [options.compress=false] Specifies whether or not to
* compress `data`
* @param {Boolean} [options.binary=false] Specifies whether `data` is binary
* or text
* @param {Boolean} [options.fin=false] Specifies whether the fragment is the
* last one
* @param {Boolean} [options.mask=false] Specifies whether or not to mask
* `data`
* @param {Function} [cb] Callback
* @public
*/
send (data, options, cb) {
var opcode = options.binary ? 2 : 1;
var rsv1 = options.compress;
var readOnly = true;
if (!Buffer.isBuffer(data)) {
if (data instanceof ArrayBuffer) {
data = Buffer.from(data);
} else if (ArrayBuffer.isView(data)) {
data = viewToBuffer(data);
} else {
data = Buffer.from(data);
readOnly = false;
}
}
send(data, options, cb) {
const buf = toBuffer(data);
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
let opcode = options.binary ? 2 : 1;
let rsv1 = options.compress;
if (this._firstFragment) {
this._firstFragment = false;
if (rsv1 && perMessageDeflate) {
rsv1 = data.length >= perMessageDeflate._threshold;
rsv1 = buf.length >= perMessageDeflate._threshold;
}
this._compress = rsv1;
} else {
@ -289,22 +286,25 @@ class Sender {
rsv1,
opcode,
mask: options.mask,
readOnly
readOnly: toBuffer.readOnly
};
if (this._deflating) {
this.enqueue([this.dispatch, data, this._compress, opts, cb]);
this.enqueue([this.dispatch, buf, this._compress, opts, cb]);
} else {
this.dispatch(data, this._compress, opts, cb);
this.dispatch(buf, this._compress, opts, cb);
}
} else {
this.sendFrame(Sender.frame(data, {
fin: options.fin,
rsv1: false,
opcode,
mask: options.mask,
readOnly
}), cb);
this.sendFrame(
Sender.frame(buf, {
fin: options.fin,
rsv1: false,
opcode,
mask: options.mask,
readOnly: toBuffer.readOnly
}),
cb
);
}
}
@ -312,17 +312,22 @@ class Sender {
* Dispatches a data message.
*
* @param {Buffer} data The message to send
* @param {Boolean} compress Specifies whether or not to compress `data`
* @param {Boolean} [compress=false] Specifies whether or not to compress
* `data`
* @param {Object} options Options object
* @param {Number} options.opcode The opcode
* @param {Boolean} options.readOnly Specifies whether `data` can be modified
* @param {Boolean} options.fin Specifies whether or not to set the FIN bit
* @param {Boolean} options.mask Specifies whether or not to mask `data`
* @param {Boolean} options.rsv1 Specifies whether or not to set the RSV1 bit
* @param {Function} cb Callback
* @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
* modified
* @param {Boolean} [options.fin=false] Specifies whether or not to set the
* FIN bit
* @param {Boolean} [options.mask=false] Specifies whether or not to mask
* `data`
* @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
* RSV1 bit
* @param {Function} [cb] Callback
* @private
*/
dispatch (data, compress, options, cb) {
dispatch(data, compress, options, cb) {
if (!compress) {
this.sendFrame(Sender.frame(data, options), cb);
return;
@ -330,11 +335,29 @@ class Sender {
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
this._bufferedBytes += data.length;
this._deflating = true;
perMessageDeflate.compress(data, options.fin, (_, buf) => {
if (this._socket.destroyed) {
const err = new Error(
'The socket was closed while data was being compressed'
);
if (typeof cb === 'function') cb(err);
for (let i = 0; i < this._queue.length; i++) {
const callback = this._queue[i][4];
if (typeof callback === 'function') callback(err);
}
return;
}
this._bufferedBytes -= data.length;
this._deflating = false;
options.readOnly = false;
this.sendFrame(Sender.frame(buf, options), cb);
this._deflating = false;
this.dequeue();
});
}
@ -344,12 +367,12 @@ class Sender {
*
* @private
*/
dequeue () {
dequeue() {
while (!this._deflating && this._queue.length) {
const params = this._queue.shift();
this._bufferedBytes -= params[1].length;
params[0].apply(this, params.slice(1));
Reflect.apply(params[0], this, params.slice(1));
}
}
@ -359,7 +382,7 @@ class Sender {
* @param {Array} params Send operation parameters.
* @private
*/
enqueue (params) {
enqueue(params) {
this._bufferedBytes += params[1].length;
this._queue.push(params);
}
@ -368,13 +391,15 @@ class Sender {
* Sends a frame.
*
* @param {Buffer[]} list The frame to send
* @param {Function} cb Callback
* @param {Function} [cb] Callback
* @private
*/
sendFrame (list, cb) {
sendFrame(list, cb) {
if (list.length === 2) {
this._socket.cork();
this._socket.write(list[0]);
this._socket.write(list[1], cb);
this._socket.uncork();
} else {
this._socket.write(list[0], cb);
}
@ -382,20 +407,3 @@ class Sender {
}
module.exports = Sender;
/**
* Converts an `ArrayBuffer` view into a buffer.
*
* @param {(DataView|TypedArray)} view The view to convert
* @return {Buffer} Converted view
* @private
*/
function viewToBuffer (view) {
const buf = Buffer.from(view.buffer);
if (view.byteLength !== view.buffer.byteLength) {
return buf.slice(view.byteOffset, view.byteOffset + view.byteLength);
}
return buf;
}

180
node_modules/ws/lib/stream.js generated vendored Normal file
View File

@ -0,0 +1,180 @@
'use strict';
const { Duplex } = require('stream');
/**
* Emits the `'close'` event on a stream.
*
* @param {Duplex} stream The stream.
* @private
*/
function emitClose(stream) {
stream.emit('close');
}
/**
* The listener of the `'end'` event.
*
* @private
*/
function duplexOnEnd() {
if (!this.destroyed && this._writableState.finished) {
this.destroy();
}
}
/**
* The listener of the `'error'` event.
*
* @param {Error} err The error
* @private
*/
function duplexOnError(err) {
this.removeListener('error', duplexOnError);
this.destroy();
if (this.listenerCount('error') === 0) {
// Do not suppress the throwing behavior.
this.emit('error', err);
}
}
/**
* Wraps a `WebSocket` in a duplex stream.
*
* @param {WebSocket} ws The `WebSocket` to wrap
* @param {Object} [options] The options for the `Duplex` constructor
* @return {Duplex} The duplex stream
* @public
*/
function createWebSocketStream(ws, options) {
let resumeOnReceiverDrain = true;
let terminateOnDestroy = true;
function receiverOnDrain() {
if (resumeOnReceiverDrain) ws._socket.resume();
}
if (ws.readyState === ws.CONNECTING) {
ws.once('open', function open() {
ws._receiver.removeAllListeners('drain');
ws._receiver.on('drain', receiverOnDrain);
});
} else {
ws._receiver.removeAllListeners('drain');
ws._receiver.on('drain', receiverOnDrain);
}
const duplex = new Duplex({
...options,
autoDestroy: false,
emitClose: false,
objectMode: false,
writableObjectMode: false
});
ws.on('message', function message(msg) {
if (!duplex.push(msg)) {
resumeOnReceiverDrain = false;
ws._socket.pause();
}
});
ws.once('error', function error(err) {
if (duplex.destroyed) return;
// Prevent `ws.terminate()` from being called by `duplex._destroy()`.
//
// - If the `'error'` event is emitted before the `'open'` event, then
// `ws.terminate()` is a noop as no socket is assigned.
// - Otherwise, the error is re-emitted by the listener of the `'error'`
// event of the `Receiver` object. The listener already closes the
// connection by calling `ws.close()`. This allows a close frame to be
// sent to the other peer. If `ws.terminate()` is called right after this,
// then the close frame might not be sent.
terminateOnDestroy = false;
duplex.destroy(err);
});
ws.once('close', function close() {
if (duplex.destroyed) return;
duplex.push(null);
});
duplex._destroy = function (err, callback) {
if (ws.readyState === ws.CLOSED) {
callback(err);
process.nextTick(emitClose, duplex);
return;
}
let called = false;
ws.once('error', function error(err) {
called = true;
callback(err);
});
ws.once('close', function close() {
if (!called) callback(err);
process.nextTick(emitClose, duplex);
});
if (terminateOnDestroy) ws.terminate();
};
duplex._final = function (callback) {
if (ws.readyState === ws.CONNECTING) {
ws.once('open', function open() {
duplex._final(callback);
});
return;
}
// If the value of the `_socket` property is `null` it means that `ws` is a
// client websocket and the handshake failed. In fact, when this happens, a
// socket is never assigned to the websocket. Wait for the `'error'` event
// that will be emitted by the websocket.
if (ws._socket === null) return;
if (ws._socket._writableState.finished) {
callback();
if (duplex._readableState.endEmitted) duplex.destroy();
} else {
ws._socket.once('finish', function finish() {
// `duplex` is not destroyed here because the `'end'` event will be
// emitted on `duplex` after this `'finish'` event. The EOF signaling
// `null` chunk is, in fact, pushed when the websocket emits `'close'`.
callback();
});
ws.close();
}
};
duplex._read = function () {
if (
(ws.readyState === ws.OPEN || ws.readyState === ws.CLOSING) &&
!resumeOnReceiverDrain
) {
resumeOnReceiverDrain = true;
if (!ws._receiver._writableState.needDrain) ws._socket.resume();
}
};
duplex._write = function (chunk, encoding, callback) {
if (ws.readyState === ws.CONNECTING) {
ws.once('open', function open() {
duplex._write(chunk, encoding, callback);
});
return;
}
ws.send(chunk, callback);
};
duplex.on('end', duplexOnEnd);
duplex.on('error', duplexOnError);
return duplex;
}
module.exports = createWebSocketStream;

101
node_modules/ws/lib/validation.js generated vendored
View File

@ -1,15 +1,5 @@
'use strict';
try {
const isValidUTF8 = require('utf-8-validate');
exports.isValidUTF8 = typeof isValidUTF8 === 'object'
? isValidUTF8.Validation.isValidUTF8 // utf-8-validate@<3.0.0
: isValidUTF8;
} catch (e) /* istanbul ignore next */ {
exports.isValidUTF8 = () => true;
}
/**
* Checks if a status code is allowed in a close frame.
*
@ -17,13 +7,98 @@ try {
* @return {Boolean} `true` if the status code is valid, else `false`
* @public
*/
exports.isValidStatusCode = (code) => {
function isValidStatusCode(code) {
return (
(code >= 1000 &&
code <= 1013 &&
code <= 1014 &&
code !== 1004 &&
code !== 1005 &&
code !== 1006) ||
(code >= 3000 && code <= 4999)
);
};
}
/**
* Checks if a given buffer contains only correct UTF-8.
* Ported from https://www.cl.cam.ac.uk/%7Emgk25/ucs/utf8_check.c by
* Markus Kuhn.
*
* @param {Buffer} buf The buffer to check
* @return {Boolean} `true` if `buf` contains only correct UTF-8, else `false`
* @public
*/
function _isValidUTF8(buf) {
const len = buf.length;
let i = 0;
while (i < len) {
if ((buf[i] & 0x80) === 0) {
// 0xxxxxxx
i++;
} else if ((buf[i] & 0xe0) === 0xc0) {
// 110xxxxx 10xxxxxx
if (
i + 1 === len ||
(buf[i + 1] & 0xc0) !== 0x80 ||
(buf[i] & 0xfe) === 0xc0 // Overlong
) {
return false;
}
i += 2;
} else if ((buf[i] & 0xf0) === 0xe0) {
// 1110xxxx 10xxxxxx 10xxxxxx
if (
i + 2 >= len ||
(buf[i + 1] & 0xc0) !== 0x80 ||
(buf[i + 2] & 0xc0) !== 0x80 ||
(buf[i] === 0xe0 && (buf[i + 1] & 0xe0) === 0x80) || // Overlong
(buf[i] === 0xed && (buf[i + 1] & 0xe0) === 0xa0) // Surrogate (U+D800 - U+DFFF)
) {
return false;
}
i += 3;
} else if ((buf[i] & 0xf8) === 0xf0) {
// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
if (
i + 3 >= len ||
(buf[i + 1] & 0xc0) !== 0x80 ||
(buf[i + 2] & 0xc0) !== 0x80 ||
(buf[i + 3] & 0xc0) !== 0x80 ||
(buf[i] === 0xf0 && (buf[i + 1] & 0xf0) === 0x80) || // Overlong
(buf[i] === 0xf4 && buf[i + 1] > 0x8f) ||
buf[i] > 0xf4 // > U+10FFFF
) {
return false;
}
i += 4;
} else {
return false;
}
}
return true;
}
try {
let isValidUTF8 = require('utf-8-validate');
/* istanbul ignore if */
if (typeof isValidUTF8 === 'object') {
isValidUTF8 = isValidUTF8.Validation.isValidUTF8; // utf-8-validate@<3.0.0
}
module.exports = {
isValidStatusCode,
isValidUTF8(buf) {
return buf.length < 150 ? _isValidUTF8(buf) : isValidUTF8(buf);
}
};
} catch (e) /* istanbul ignore next */ {
module.exports = {
isValidStatusCode,
isValidUTF8: _isValidUTF8
};
}

View File

@ -1,14 +1,24 @@
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^net|tls|https$" }] */
'use strict';
const EventEmitter = require('events');
const crypto = require('crypto');
const http = require('http');
const url = require('url');
const https = require('https');
const net = require('net');
const tls = require('tls');
const { createHash } = require('crypto');
const PerMessageDeflate = require('./permessage-deflate');
const extension = require('./extension');
const constants = require('./constants');
const WebSocket = require('./websocket');
const { format, parse } = require('./extension');
const { GUID, kWebSocket } = require('./constants');
const keyRegex = /^[+/0-9A-Za-z]{22}==$/;
const RUNNING = 0;
const CLOSING = 1;
const CLOSED = 2;
/**
* Class representing a WebSocket server.
@ -20,22 +30,28 @@ class WebSocketServer extends EventEmitter {
* Create a `WebSocketServer` instance.
*
* @param {Object} options Configuration options
* @param {String} options.host The hostname where to bind the server
* @param {Number} options.port The port where to bind the server
* @param {http.Server} options.server A pre-created HTTP/S server to use
* @param {Function} options.verifyClient An hook to reject connections
* @param {Function} options.handleProtocols An hook to handle protocols
* @param {String} options.path Accept only connections matching this path
* @param {Boolean} options.noServer Enable no server mode
* @param {Boolean} options.clientTracking Specifies whether or not to track clients
* @param {(Boolean|Object)} options.perMessageDeflate Enable/disable permessage-deflate
* @param {Number} options.maxPayload The maximum allowed message size
* @param {Function} callback A listener for the `listening` event
* @param {Number} [options.backlog=511] The maximum length of the queue of
* pending connections
* @param {Boolean} [options.clientTracking=true] Specifies whether or not to
* track clients
* @param {Function} [options.handleProtocols] A hook to handle protocols
* @param {String} [options.host] The hostname where to bind the server
* @param {Number} [options.maxPayload=104857600] The maximum allowed message
* size
* @param {Boolean} [options.noServer=false] Enable no server mode
* @param {String} [options.path] Accept only connections matching this path
* @param {(Boolean|Object)} [options.perMessageDeflate=false] Enable/disable
* permessage-deflate
* @param {Number} [options.port] The port where to bind the server
* @param {(http.Server|https.Server)} [options.server] A pre-created HTTP/S
* server to use
* @param {Function} [options.verifyClient] A hook to reject connections
* @param {Function} [callback] A listener for the `listening` event
*/
constructor (options, callback) {
constructor(options, callback) {
super();
options = Object.assign({
options = {
maxPayload: 100 * 1024 * 1024,
perMessageDeflate: false,
handleProtocols: null,
@ -46,12 +62,18 @@ class WebSocketServer extends EventEmitter {
server: null,
host: null,
path: null,
port: null
}, options);
port: null,
...options
};
if (options.port == null && !options.server && !options.noServer) {
if (
(options.port == null && !options.server && !options.noServer) ||
(options.port != null && (options.server || options.noServer)) ||
(options.server && options.noServer)
) {
throw new TypeError(
'One of the "port", "server", or "noServer" options must be specified'
'One and only one of the "port", "server", or "noServer" options ' +
'must be specified'
);
}
@ -65,19 +87,24 @@ class WebSocketServer extends EventEmitter {
});
res.end(body);
});
this._server.listen(options.port, options.host, options.backlog, callback);
this._server.listen(
options.port,
options.host,
options.backlog,
callback
);
} else if (options.server) {
this._server = options.server;
}
if (this._server) {
const emitConnection = this.emit.bind(this, 'connection');
this._removeListeners = addListeners(this._server, {
listening: this.emit.bind(this, 'listening'),
error: this.emit.bind(this, 'error'),
upgrade: (req, socket, head) => {
this.handleUpgrade(req, socket, head, (ws) => {
this.emit('connection', ws, req);
});
this.handleUpgrade(req, socket, head, emitConnection);
}
});
}
@ -85,6 +112,7 @@ class WebSocketServer extends EventEmitter {
if (options.perMessageDeflate === true) options.perMessageDeflate = {};
if (options.clientTracking) this.clients = new Set();
this.options = options;
this._state = RUNNING;
}
/**
@ -96,7 +124,7 @@ class WebSocketServer extends EventEmitter {
* @return {(Object|String|null)} The address of the server
* @public
*/
address () {
address() {
if (this.options.noServer) {
throw new Error('The server is operating in "noServer" mode');
}
@ -108,10 +136,20 @@ class WebSocketServer extends EventEmitter {
/**
* Close the server.
*
* @param {Function} cb Callback
* @param {Function} [cb] Callback
* @public
*/
close (cb) {
close(cb) {
if (cb) this.once('close', cb);
if (this._state === CLOSED) {
process.nextTick(emitClose, this);
return;
}
if (this._state === CLOSING) return;
this._state = CLOSING;
//
// Terminate all associated clients.
//
@ -128,10 +166,13 @@ class WebSocketServer extends EventEmitter {
//
// Close the http server if it was internally created.
//
if (this.options.port != null) return server.close(cb);
if (this.options.port != null) {
server.close(emitClose.bind(undefined, this));
return;
}
}
if (cb) cb();
process.nextTick(emitClose, this);
}
/**
@ -141,9 +182,12 @@ class WebSocketServer extends EventEmitter {
* @return {Boolean} `true` if the request is valid, else `false`
* @public
*/
shouldHandle (req) {
if (this.options.path && url.parse(req.url).pathname !== this.options.path) {
return false;
shouldHandle(req) {
if (this.options.path) {
const index = req.url.indexOf('?');
const pathname = index !== -1 ? req.url.slice(0, index) : req.url;
if (pathname !== this.options.path) return false;
}
return true;
@ -153,20 +197,30 @@ class WebSocketServer extends EventEmitter {
* Handle a HTTP Upgrade request.
*
* @param {http.IncomingMessage} req The request object
* @param {net.Socket} socket The network socket between the server and client
* @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 {Function} cb Callback
* @public
*/
handleUpgrade (req, socket, head, cb) {
handleUpgrade(req, socket, head, cb) {
socket.on('error', socketOnError);
const key =
req.headers['sec-websocket-key'] !== undefined
? req.headers['sec-websocket-key'].trim()
: false;
const upgrade = req.headers.upgrade;
const version = +req.headers['sec-websocket-version'];
const extensions = {};
if (
req.method !== 'GET' || req.headers.upgrade.toLowerCase() !== 'websocket' ||
!req.headers['sec-websocket-key'] || (version !== 8 && version !== 13) ||
req.method !== 'GET' ||
upgrade === undefined ||
upgrade.toLowerCase() !== 'websocket' ||
!key ||
!keyRegex.test(key) ||
(version !== 8 && version !== 13) ||
!this.shouldHandle(req)
) {
return abortHandshake(socket, 400);
@ -180,9 +234,7 @@ class WebSocketServer extends EventEmitter {
);
try {
const offers = extension.parse(
req.headers['sec-websocket-extensions']
);
const offers = parse(req.headers['sec-websocket-extensions']);
if (offers[PerMessageDeflate.extensionName]) {
perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]);
@ -198,8 +250,9 @@ class WebSocketServer extends EventEmitter {
//
if (this.options.verifyClient) {
const info = {
origin: req.headers[`${version === 8 ? 'sec-websocket-origin' : 'origin'}`],
secure: !!(req.connection.authorized || req.connection.encrypted),
origin:
req.headers[`${version === 8 ? 'sec-websocket-origin' : 'origin'}`],
secure: !!(req.socket.authorized || req.socket.encrypted),
req
};
@ -209,7 +262,7 @@ class WebSocketServer extends EventEmitter {
return abortHandshake(socket, code || 401, message, headers);
}
this.completeUpgrade(extensions, req, socket, head, cb);
this.completeUpgrade(key, extensions, req, socket, head, cb);
});
return;
}
@ -217,38 +270,50 @@ class WebSocketServer extends EventEmitter {
if (!this.options.verifyClient(info)) return abortHandshake(socket, 401);
}
this.completeUpgrade(extensions, req, socket, head, cb);
this.completeUpgrade(key, extensions, req, socket, head, cb);
}
/**
* Upgrade the connection to WebSocket.
*
* @param {String} key The value of the `Sec-WebSocket-Key` header
* @param {Object} extensions The accepted extensions
* @param {http.IncomingMessage} req The request object
* @param {net.Socket} socket The network socket between the server and client
* @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 {Function} cb Callback
* @throws {Error} If called more than once with the same socket
* @private
*/
completeUpgrade (extensions, req, socket, head, cb) {
completeUpgrade(key, extensions, req, socket, head, cb) {
//
// Destroy the socket if the client has already sent a FIN packet.
//
if (!socket.readable || !socket.writable) return socket.destroy();
const key = crypto.createHash('sha1')
.update(req.headers['sec-websocket-key'] + constants.GUID, 'binary')
if (socket[kWebSocket]) {
throw new Error(
'server.handleUpgrade() was called more than once with the same ' +
'socket, possibly due to a misconfiguration'
);
}
if (this._state > RUNNING) return abortHandshake(socket, 503);
const digest = createHash('sha1')
.update(key + GUID)
.digest('base64');
const headers = [
'HTTP/1.1 101 Switching Protocols',
'Upgrade: websocket',
'Connection: Upgrade',
`Sec-WebSocket-Accept: ${key}`
`Sec-WebSocket-Accept: ${digest}`
];
const ws = new WebSocket(null);
var protocol = req.headers['sec-websocket-protocol'];
let protocol = req.headers['sec-websocket-protocol'];
if (protocol) {
protocol = protocol.split(',').map(trim);
@ -264,13 +329,13 @@ class WebSocketServer extends EventEmitter {
if (protocol) {
headers.push(`Sec-WebSocket-Protocol: ${protocol}`);
ws.protocol = protocol;
ws._protocol = protocol;
}
}
if (extensions[PerMessageDeflate.extensionName]) {
const params = extensions[PerMessageDeflate.extensionName].params;
const value = extension.format({
const value = format({
[PerMessageDeflate.extensionName]: [params]
});
headers.push(`Sec-WebSocket-Extensions: ${value}`);
@ -292,7 +357,7 @@ class WebSocketServer extends EventEmitter {
ws.on('close', () => this.clients.delete(ws));
}
cb(ws);
cb(ws, req);
}
}
@ -304,51 +369,66 @@ module.exports = WebSocketServer;
*
* @param {EventEmitter} server The event emitter
* @param {Object.<String, Function>} map The listeners to add
* @return {Function} A function that will remove the added listeners when called
* @return {Function} A function that will remove the added listeners when
* called
* @private
*/
function addListeners (server, map) {
function addListeners(server, map) {
for (const event of Object.keys(map)) server.on(event, map[event]);
return function removeListeners () {
return function removeListeners() {
for (const event of Object.keys(map)) {
server.removeListener(event, map[event]);
}
};
}
/**
* Emit a `'close'` event on an `EventEmitter`.
*
* @param {EventEmitter} server The event emitter
* @private
*/
function emitClose(server) {
server._state = CLOSED;
server.emit('close');
}
/**
* Handle premature socket errors.
*
* @private
*/
function socketOnError () {
function socketOnError() {
this.destroy();
}
/**
* Close the connection when preconditions are not fulfilled.
*
* @param {net.Socket} socket The socket of the upgrade request
* @param {(net.Socket|tls.Socket)} socket The socket of the upgrade request
* @param {Number} code The HTTP response status code
* @param {String} [message] The HTTP response body
* @param {Object} [headers] Additional HTTP response headers
* @private
*/
function abortHandshake (socket, code, message, headers) {
function abortHandshake(socket, code, message, headers) {
if (socket.writable) {
message = message || http.STATUS_CODES[code];
headers = Object.assign({
'Connection': 'close',
'Content-type': 'text/html',
'Content-Length': Buffer.byteLength(message)
}, headers);
headers = {
Connection: 'close',
'Content-Type': 'text/html',
'Content-Length': Buffer.byteLength(message),
...headers
};
socket.write(
`HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\r\n` +
Object.keys(headers).map(h => `${h}: ${headers[h]}`).join('\r\n') +
'\r\n\r\n' +
message
Object.keys(headers)
.map((h) => `${h}: ${headers[h]}`)
.join('\r\n') +
'\r\n\r\n' +
message
);
}

901
node_modules/ws/lib/websocket.js generated vendored

File diff suppressed because it is too large Load Diff