Socket
Socket
Sign inDemoInstall

ws

Package Overview
Dependencies
Maintainers
4
Versions
169
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ws - npm Package Compare versions

Comparing version 8.17.1 to 8.18.0

8

lib/constants.js
'use strict';
const BINARY_TYPES = ['nodebuffer', 'arraybuffer', 'fragments'];
const hasBlob = typeof Blob !== 'undefined';
if (hasBlob) BINARY_TYPES.push('blob');
module.exports = {
BINARY_TYPES: ['nodebuffer', 'arraybuffer', 'fragments'],
BINARY_TYPES,
EMPTY_BUFFER: Buffer.alloc(0),
GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
hasBlob,
kForOnEventAttribute: Symbol('kIsForOnEventAttribute'),

@@ -8,0 +14,0 @@ kListener: Symbol('kListener'),

@@ -562,2 +562,4 @@ 'use strict';

data = toArrayBuffer(concat(fragments, messageLength));
} else if (this._binaryType === 'blob') {
data = new Blob(fragments);
} else {

@@ -564,0 +566,0 @@ data = fragments;

195

lib/sender.js

@@ -9,4 +9,4 @@ /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex" }] */

const PerMessageDeflate = require('./permessage-deflate');
const { EMPTY_BUFFER } = require('./constants');
const { isValidStatusCode } = require('./validation');
const { EMPTY_BUFFER, kWebSocket, NOOP } = require('./constants');
const { isBlob, isValidStatusCode } = require('./validation');
const { mask: applyMask, toBuffer } = require('./buffer-util');

@@ -20,2 +20,6 @@

const DEFAULT = 0;
const DEFLATING = 1;
const GET_BLOB_DATA = 2;
/**

@@ -47,4 +51,6 @@ * HyBi Sender implementation.

this._bufferedBytes = 0;
this._deflating = false;
this._queue = [];
this._state = DEFAULT;
this.onerror = NOOP;
this[kWebSocket] = undefined;
}

@@ -216,3 +222,3 @@

if (this._deflating) {
if (this._state !== DEFAULT) {
this.enqueue([this.dispatch, buf, false, options, cb]);

@@ -239,2 +245,5 @@ } else {

readOnly = false;
} else if (isBlob(data)) {
byteLength = data.size;
readOnly = false;
} else {

@@ -261,3 +270,9 @@ data = toBuffer(data);

if (this._deflating) {
if (isBlob(data)) {
if (this._state !== DEFAULT) {
this.enqueue([this.getBlobData, data, false, options, cb]);
} else {
this.getBlobData(data, false, options, cb);
}
} else if (this._state !== DEFAULT) {
this.enqueue([this.dispatch, data, false, options, cb]);

@@ -284,2 +299,5 @@ } else {

readOnly = false;
} else if (isBlob(data)) {
byteLength = data.size;
readOnly = false;
} else {

@@ -306,3 +324,9 @@ data = toBuffer(data);

if (this._deflating) {
if (isBlob(data)) {
if (this._state !== DEFAULT) {
this.enqueue([this.getBlobData, data, false, options, cb]);
} else {
this.getBlobData(data, false, options, cb);
}
} else if (this._state !== DEFAULT) {
this.enqueue([this.dispatch, data, false, options, cb]);

@@ -341,2 +365,5 @@ } else {

readOnly = false;
} else if (isBlob(data)) {
byteLength = data.size;
readOnly = false;
} else {

@@ -369,33 +396,23 @@ data = toBuffer(data);

if (perMessageDeflate) {
const opts = {
[kByteLength]: byteLength,
fin: options.fin,
generateMask: this._generateMask,
mask: options.mask,
maskBuffer: this._maskBuffer,
opcode,
readOnly,
rsv1
};
const opts = {
[kByteLength]: byteLength,
fin: options.fin,
generateMask: this._generateMask,
mask: options.mask,
maskBuffer: this._maskBuffer,
opcode,
readOnly,
rsv1
};
if (this._deflating) {
this.enqueue([this.dispatch, data, this._compress, opts, cb]);
if (isBlob(data)) {
if (this._state !== DEFAULT) {
this.enqueue([this.getBlobData, data, this._compress, opts, cb]);
} else {
this.dispatch(data, this._compress, opts, cb);
this.getBlobData(data, this._compress, opts, cb);
}
} else if (this._state !== DEFAULT) {
this.enqueue([this.dispatch, data, this._compress, opts, cb]);
} else {
this.sendFrame(
Sender.frame(data, {
[kByteLength]: byteLength,
fin: options.fin,
generateMask: this._generateMask,
mask: options.mask,
maskBuffer: this._maskBuffer,
opcode,
readOnly,
rsv1: false
}),
cb
);
this.dispatch(data, this._compress, opts, cb);
}

@@ -405,2 +422,66 @@ }

/**
* Gets the contents of a blob as binary data.
*
* @param {Blob} blob The blob
* @param {Boolean} [compress=false] Specifies whether or not to compress
* the data
* @param {Object} options Options object
* @param {Boolean} [options.fin=false] Specifies whether or not to set the
* FIN bit
* @param {Function} [options.generateMask] The function used to generate the
* masking key
* @param {Boolean} [options.mask=false] Specifies whether or not to mask
* `data`
* @param {Buffer} [options.maskBuffer] The buffer used to store the masking
* key
* @param {Number} options.opcode The opcode
* @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
* modified
* @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
* RSV1 bit
* @param {Function} [cb] Callback
* @private
*/
getBlobData(blob, compress, options, cb) {
this._bufferedBytes += options[kByteLength];
this._state = GET_BLOB_DATA;
blob
.arrayBuffer()
.then((arrayBuffer) => {
if (this._socket.destroyed) {
const err = new Error(
'The socket was closed while the blob was being read'
);
//
// `callCallbacks` is called in the next tick to ensure that errors
// that might be thrown in the callbacks behave like errors thrown
// outside the promise chain.
//
process.nextTick(callCallbacks, this, err, cb);
return;
}
this._bufferedBytes -= options[kByteLength];
const data = toBuffer(arrayBuffer);
if (!compress) {
this._state = DEFAULT;
this.sendFrame(Sender.frame(data, options), cb);
this.dequeue();
} else {
this.dispatch(data, compress, options, cb);
}
})
.catch((err) => {
//
// `onError` is called in the next tick for the same reason that
// `callCallbacks` above is.
//
process.nextTick(onError, this, err, cb);
});
}
/**
* Dispatches a message.

@@ -437,3 +518,3 @@ *

this._bufferedBytes += options[kByteLength];
this._deflating = true;
this._state = DEFLATING;
perMessageDeflate.compress(data, options.fin, (_, buf) => {

@@ -445,11 +526,3 @@ if (this._socket.destroyed) {

if (typeof cb === 'function') cb(err);
for (let i = 0; i < this._queue.length; i++) {
const params = this._queue[i];
const callback = params[params.length - 1];
if (typeof callback === 'function') callback(err);
}
callCallbacks(this, err, cb);
return;

@@ -459,3 +532,3 @@ }

this._bufferedBytes -= options[kByteLength];
this._deflating = false;
this._state = DEFAULT;
options.readOnly = false;

@@ -473,3 +546,3 @@ this.sendFrame(Sender.frame(buf, options), cb);

dequeue() {
while (!this._deflating && this._queue.length) {
while (this._state === DEFAULT && this._queue.length) {
const params = this._queue.shift();

@@ -513,1 +586,33 @@

module.exports = Sender;
/**
* Calls queued callbacks with an error.
*
* @param {Sender} sender The `Sender` instance
* @param {Error} err The error to call the callbacks with
* @param {Function} [cb] The first callback
* @private
*/
function callCallbacks(sender, err, cb) {
if (typeof cb === 'function') cb(err);
for (let i = 0; i < sender._queue.length; i++) {
const params = sender._queue[i];
const callback = params[params.length - 1];
if (typeof callback === 'function') callback(err);
}
}
/**
* Handles a `Sender` error.
*
* @param {Sender} sender The `Sender` instance
* @param {Error} err The error
* @param {Function} [cb] The first pending callback
* @private
*/
function onError(sender, err, cb) {
callCallbacks(sender, err, cb);
sender.onerror(err);
}

@@ -5,2 +5,4 @@ 'use strict';

const { hasBlob } = require('./constants');
//

@@ -111,3 +113,23 @@ // Allowed token characters:

/**
* Determines whether a value is a `Blob`.
*
* @param {*} value The value to be tested
* @return {Boolean} `true` if `value` is a `Blob`, else `false`
* @private
*/
function isBlob(value) {
return (
hasBlob &&
typeof value === 'object' &&
typeof value.arrayBuffer === 'function' &&
typeof value.type === 'string' &&
typeof value.stream === 'function' &&
(value[Symbol.toStringTag] === 'Blob' ||
value[Symbol.toStringTag] === 'File')
);
}
module.exports = {
isBlob,
isValidStatusCode,

@@ -114,0 +136,0 @@ isValidUTF8: _isValidUTF8,

@@ -17,2 +17,4 @@ /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex|Readable$", "caughtErrors": "none" }] */

const Sender = require('./sender');
const { isBlob } = require('./validation');
const {

@@ -62,2 +64,3 @@ BINARY_TYPES,

this._closeTimer = null;
this._errorEmitted = false;
this._extensions = {};

@@ -95,5 +98,4 @@ this._paused = false;

/**
* This deviates from the WHATWG interface since ws doesn't support the
* required default "blob" type (instead we define a custom "nodebuffer"
* type).
* For historical reasons, the custom "nodebuffer" type is used by the default
* instead of "blob".
*

@@ -219,7 +221,10 @@ * @type {String}

this._sender = new Sender(socket, this._extensions, options.generateMask);
const sender = new Sender(socket, this._extensions, options.generateMask);
this._receiver = receiver;
this._sender = sender;
this._socket = socket;
receiver[kWebSocket] = this;
sender[kWebSocket] = this;
socket[kWebSocket] = this;

@@ -234,2 +239,4 @@

sender.onerror = senderOnError;
//

@@ -330,9 +337,3 @@ // These methods may not be available if `socket` is just a `Duplex`.

//
// Specify a timeout for the closing handshake to complete.
//
this._closeTimer = setTimeout(
this._socket.destroy.bind(this._socket),
closeTimeout
);
setCloseTimer(this);
}

@@ -1041,2 +1042,7 @@

websocket._readyState = WebSocket.CLOSING;
//
// The following assignment is practically useless and is done only for
// consistency.
//
websocket._errorEmitted = true;
websocket.emit('error', err);

@@ -1122,3 +1128,3 @@ websocket.emitClose();

if (data) {
const length = toBuffer(data).length;
const length = isBlob(data) ? data.size : toBuffer(data).length;

@@ -1199,3 +1205,6 @@ //

websocket.emit('error', err);
if (!websocket._errorEmitted) {
websocket._errorEmitted = true;
websocket.emit('error', err);
}
}

@@ -1257,2 +1266,43 @@

/**
* The `Sender` error event handler.
*
* @param {Error} The error
* @private
*/
function senderOnError(err) {
const websocket = this[kWebSocket];
if (websocket.readyState === WebSocket.CLOSED) return;
if (websocket.readyState === WebSocket.OPEN) {
websocket._readyState = WebSocket.CLOSING;
setCloseTimer(websocket);
}
//
// `socket.end()` is used instead of `socket.destroy()` to allow the other
// peer to finish sending queued data. There is no need to set a timer here
// because `CLOSING` means that it is already set or not needed.
//
this._socket.end();
if (!websocket._errorEmitted) {
websocket._errorEmitted = true;
websocket.emit('error', err);
}
}
/**
* Set a timer to destroy the underlying raw socket of a WebSocket.
*
* @param {WebSocket} websocket The WebSocket instance
* @private
*/
function setCloseTimer(websocket) {
websocket._closeTimer = setTimeout(
websocket._socket.destroy.bind(websocket._socket),
closeTimeout
);
}
/**
* The listener of the socket `'close'` event.

@@ -1259,0 +1309,0 @@ *

{
"name": "ws",
"version": "8.17.1",
"version": "8.18.0",
"description": "Simple to use, blazing fast and thoroughly tested websocket client and server for Node.js",

@@ -5,0 +5,0 @@ "keywords": [

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc