Commit 0177561b authored by Medicean's avatar Medicean Committed by Medicean

Upgrade(ThirdParty): upgrade ws to 6.2.2

parent 7bb309f8
{
"name": "async-limiter",
"version": "1.0.1",
"_args": [
[
"async-limiter@~1.0.0",
"/Users/m/workspace/antSword/node_modules/ws"
]
],
"_from": "async-limiter@>=1.0.0 <1.1.0",
"_hasShrinkwrap": false,
"_id": "async-limiter@1.0.1",
"_inCache": true,
"_installable": true,
"_location": "/async-limiter",
"_nodeVersion": "10.16.0",
"_npmOperationalInternal": {
"host": "s3://npm-registry-packages",
"tmp": "tmp/async-limiter_1.0.1_1564760633070_0.6974331182093105"
},
"_npmUser": {
"email": "samuel.trace.reed@gmail.com",
"name": "strml"
},
"_npmVersion": "6.9.0",
"_phantomChildren": {},
"_requested": {
"name": "async-limiter",
"raw": "async-limiter@~1.0.0",
"rawSpec": "~1.0.0",
"scope": null,
"spec": ">=1.0.0 <1.1.0",
"type": "range"
},
"_requiredBy": [
"/ws"
],
"_resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
"_shasum": "dd379e94f0db8310b08291f9d64c3209766617fd",
"_shrinkwrap": null,
"_spec": "async-limiter@~1.0.0",
"_where": "/Users/m/workspace/antSword/node_modules/ws",
"author": {
"name": "Samuel Reed"
},
"bugs": {
"url": "https://github.com/strml/async-limiter/issues"
},
"dependencies": {},
"description": "asynchronous function queue with adjustable concurrency",
"devDependencies": {
"coveralls": "^3.0.3",
"eslint": "^5.16.0",
"eslint-plugin-mocha": "^5.3.0",
"intelli-espower-loader": "^1.0.1",
"mocha": "^6.1.4",
"nyc": "^14.1.1",
"power-assert": "^1.6.1"
},
"directories": {},
"dist": {
"fileCount": 7,
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
"npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdRFo5CRA9TVsSAnZWagAAE8EP/AoamQTvsA8uUcSUKc4L\nL7rKbbH4m5Cv1Z7qeBXLV3KJHI+dhn/mKU2hOpnXHgks5Az4ELlOX9O1vo9j\nLYtN8ZMGEkMIx+k7OcVexaXLcK9ALliEMNoNy4cIVc+exBS4eKFPmaEx5DmD\nNf+eCG6jkA9WY/kYSmFnus7C0B7d2PMdmtBZKdzWya9PAB5BYEoz3/GYhJZG\nEFYHmWKtMDB6LMSZ0FSXwABV6QXWn5kk3fXaPX1NtMHLw+QCT/sWt+0cOnIE\nak2s8WOry7Fsx5wXQmKbd8854LC+yVT1f7RR7eBhKAlTk74nwfNDr84UBJIr\n+0G0RdgISOzLghtRFu3SqYKynXTjdlycZG9vvcHW9oPGI2ZiC2cHuiqc4+K7\ndYX1HGQICjflTmb+RR0vGNXiy3v6YBWgpItdeziPO2K+0uN6SJr1BidQ8oKI\nd49psu/xNvMhdwOo19+/Bt7n7nT4uzej8K7uQO81BJC0ITeNfaC/z9M/4VOg\nFuixwvvzfs+/RABxzXKZqOMVlAnAb4U/PBcliklyUBeZ62PDkqnBxdrOekf5\nacstUU3K5bAaBV8taKHEa1+tqYUjVEcaolDDKgmO0dxD9FlKAMlhck9ildO7\nnjODiNgcSMUlMmHGUZCEvjSt1YptntzC0DHwxWUjszaR4p0Iz0c0AyOYGH7T\nRewy\r\n=MPQY\r\n-----END PGP SIGNATURE-----\r\n",
"shasum": "dd379e94f0db8310b08291f9d64c3209766617fd",
"signatures": [
{
"keyid": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA",
"sig": "MEUCIQCYVwf0HXqe0bWbc7zYVM/9lbWUqBw9ET6oj9oL/MIGvQIgagFO41z8Ssm5K6bLiDP0KO4AMvSxGKLjZzN2hdLQgpM="
}
],
"tarball": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
"unpackedSize": 6900
},
"gitHead": "f3bb66f26e69a5747a6483e32c775a02632020ee",
"homepage": "https://github.com/strml/async-limiter#readme",
"keywords": [
"throttle",
"async",
......@@ -12,24 +82,26 @@
"concurrency",
"concurrent"
],
"dependencies": {},
"devDependencies": {
"coveralls": "^3.0.3",
"eslint": "^5.16.0",
"eslint-plugin-mocha": "^5.3.0",
"intelli-espower-loader": "^1.0.1",
"mocha": "^6.1.4",
"nyc": "^14.1.1",
"power-assert": "^1.6.1"
"license": "MIT",
"maintainers": [
{
"email": "samuel.trace.reed@gmail.com",
"name": "strml"
}
],
"name": "async-limiter",
"optionalDependencies": {},
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/strml/async-limiter.git"
},
"scripts": {
"test": "mocha --require intelli-espower-loader test/",
"travis": "npm run lint && npm run test",
"coverage": "nyc npm test && nyc report --reporter=text-lcov | coveralls",
"example": "node example",
"lint": "eslint ."
"lint": "eslint .",
"test": "mocha --require intelli-espower-loader test/",
"travis": "npm run lint && npm run test"
},
"repository": "https://github.com/strml/async-limiter.git",
"author": "Samuel Reed <samuel.trace.reed@gmail.com",
"license": "MIT"
"version": "1.0.1"
}
# ws: a Node.js WebSocket library
[![Version npm](https://img.shields.io/npm/v/ws.svg)](https://www.npmjs.com/package/ws)
[![Linux Build](https://img.shields.io/travis/websockets/ws/master.svg)](https://travis-ci.org/websockets/ws)
[![Windows Build](https://ci.appveyor.com/api/projects/status/github/websockets/ws?branch=master&svg=true)](https://ci.appveyor.com/project/lpinca/ws)
[![Coverage Status](https://img.shields.io/coveralls/websockets/ws/master.svg)](https://coveralls.io/r/websockets/ws?branch=master)
[![Version npm](https://img.shields.io/npm/v/ws.svg?logo=npm)](https://www.npmjs.com/package/ws)
[![Linux Build](https://img.shields.io/travis/websockets/ws/master.svg?logo=travis)](https://travis-ci.org/websockets/ws)
[![Windows Build](https://img.shields.io/appveyor/ci/lpinca/ws/master.svg?logo=appveyor)](https://ci.appveyor.com/project/lpinca/ws)
[![Coverage Status](https://img.shields.io/coveralls/websockets/ws/master.svg)](https://coveralls.io/github/websockets/ws)
ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and
server implementation.
......
'use strict';
const { EMPTY_BUFFER } = require('./constants');
/**
* Merges an array of buffers into a new buffer.
*
......@@ -9,6 +11,9 @@
* @public
*/
function concat(list, totalLength) {
if (list.length === 0) return EMPTY_BUFFER;
if (list.length === 1) return list[0];
const target = Buffer.allocUnsafe(totalLength);
var offset = 0;
......@@ -52,21 +57,88 @@ function _unmask(buffer, mask) {
}
}
/**
* 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;
var buf;
if (data instanceof ArrayBuffer) {
buf = Buffer.from(data);
} else if (ArrayBuffer.isView(data)) {
buf = viewToBuffer(data);
} else {
buf = Buffer.from(data);
toBuffer.readOnly = false;
}
return buf;
}
/**
* 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;
}
try {
const bufferUtil = require('bufferutil');
const bu = bufferUtil.BufferUtil || bufferUtil;
module.exports = {
concat,
mask(source, mask, output, offset, length) {
if (length < 48) _mask(source, mask, output, offset, length);
else bu.mask(source, mask, output, offset, length);
},
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
};
}
'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;
......@@ -19,7 +24,7 @@ const INFLATING = 5;
*
* @extends stream.Writable
*/
class Receiver extends stream.Writable {
class Receiver extends Writable {
/**
* Creates a Receiver instance.
*
......@@ -30,8 +35,8 @@ class Receiver extends stream.Writable {
constructor(binaryType, extensions, 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._maxPayload = maxPayload | 0;
......@@ -315,7 +320,7 @@ class Receiver extends stream.Writable {
* @private
*/
getData(cb) {
var data = constants.EMPTY_BUFFER;
var data = EMPTY_BUFFER;
if (this._payloadLength) {
if (this._bufferedBytes < this._payloadLength) {
......@@ -324,7 +329,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);
......@@ -398,18 +403,18 @@ class Receiver extends stream.Writable {
var 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);
}
......@@ -440,13 +445,13 @@ class Receiver extends stream.Writable {
} else {
const code = data.readUInt16BE(0);
if (!validation.isValidStatusCode(code)) {
if (!isValidStatusCode(code)) {
return error(RangeError, `invalid status code ${code}`, true, 1002);
}
const buf = data.slice(2);
if (!validation.isValidUTF8(buf)) {
if (!isValidUTF8(buf)) {
return error(Error, 'invalid UTF-8 sequence', true, 1007);
}
......@@ -482,34 +487,6 @@ function error(ErrorCtor, message, prefix, statusCode) {
);
Error.captureStackTrace(err, error);
err[constants.kStatusCode] = statusCode;
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} buf The buffer to convert
* @return {ArrayBuffer} Converted buffer
*/
function toArrayBuffer(buf) {
if (buf.byteLength === buf.buffer.byteLength) {
return buf.buffer;
}
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
}
'use strict';
const crypto = require('crypto');
const { randomBytes } = 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');
/**
* HyBi Sender implementation.
......@@ -43,7 +43,7 @@ class Sender {
* @public
*/
static frame(data, options) {
const merge = data.length < 1024 || (options.mask && options.readOnly);
const merge = options.mask && options.readOnly;
var offset = options.mask ? 6 : 2;
var payloadLength = data.length;
......@@ -60,6 +60,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,30 +69,22 @@ class Sender {
target.writeUInt32BE(data.length, 6);
}
if (!options.mask) {
target[1] = payloadLength;
if (merge) {
data.copy(target, offset);
return [target];
}
return [target, data];
}
if (!options.mask) return [target, data];
const mask = crypto.randomBytes(4);
const mask = 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];
}
......@@ -107,11 +101,8 @@ class Sender {
var 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);
......@@ -159,23 +150,12 @@ class Sender {
* @public
*/
ping(data, mask, cb) {
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;
}
}
const buf = toBuffer(data);
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);
}
}
......@@ -210,23 +190,12 @@ class Sender {
* @public
*/
pong(data, mask, cb) {
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;
}
}
const buf = toBuffer(data);
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);
}
}
......@@ -265,27 +234,15 @@ class Sender {
* @public
*/
send(data, options, cb) {
const buf = toBuffer(data);
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
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;
}
}
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
if (this._firstFragment) {
this._firstFragment = false;
if (rsv1 && perMessageDeflate) {
rsv1 = data.length >= perMessageDeflate._threshold;
rsv1 = buf.length >= perMessageDeflate._threshold;
}
this._compress = rsv1;
} else {
......@@ -301,22 +258,22 @@ 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, {
Sender.frame(buf, {
fin: options.fin,
rsv1: false,
opcode,
mask: options.mask,
readOnly
readOnly: toBuffer.readOnly
}),
cb
);
......@@ -388,8 +345,10 @@ class Sender {
*/
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);
}
......@@ -397,20 +356,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;
}
......@@ -6,8 +6,10 @@ const http = require('http');
const PerMessageDeflate = require('./permessage-deflate');
const extension = require('./extension');
const constants = require('./constants');
const WebSocket = require('./websocket');
const { GUID } = require('./constants');
const keyRegex = /^[+/0-9A-Za-z]{22}==$/;
/**
* Class representing a WebSocket server.
......@@ -19,16 +21,20 @@ class WebSocketServer extends EventEmitter {
* Create a `WebSocketServer` instance.
*
* @param {Object} options Configuration options
* @param {Number} options.backlog The maximum length of the queue of pending
* connections
* @param {Boolean} options.clientTracking Specifies whether or not to track
* clients
* @param {Function} options.handleProtocols An hook to handle protocols
* @param {String} options.host The hostname where to bind the server
* @param {Number} options.maxPayload The maximum allowed message size
* @param {Boolean} options.noServer Enable no server mode
* @param {String} options.path Accept only connections matching this path
* @param {(Boolean|Object)} options.perMessageDeflate Enable/disable
* permessage-deflate
* @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
*/
constructor(options, callback) {
......@@ -176,13 +182,18 @@ class WebSocketServer extends EventEmitter {
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 version = +req.headers['sec-websocket-version'];
const extensions = {};
if (
req.method !== 'GET' ||
req.headers.upgrade.toLowerCase() !== 'websocket' ||
!req.headers['sec-websocket-key'] ||
!key ||
!keyRegex.test(key) ||
(version !== 8 && version !== 13) ||
!this.shouldHandle(req)
) {
......@@ -225,7 +236,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;
}
......@@ -233,12 +244,13 @@ 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
......@@ -246,29 +258,29 @@ class WebSocketServer extends EventEmitter {
* @param {Function} cb Callback
* @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
const digest = crypto
.createHash('sha1')
.update(req.headers['sec-websocket-key'] + constants.GUID, 'binary')
.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'];
if (protocol) {
protocol = protocol.trim().split(/ *, */);
protocol = protocol.split(',').map(trim);
//
// Optionally call external protocol selection handler.
......@@ -387,3 +399,15 @@ function abortHandshake(socket, code, message, headers) {
socket.removeListener('error', socketOnError);
socket.destroy();
}
/**
* Remove whitespace characters from both ends of a string.
*
* @param {String} str The string
* @return {String} A new string representing `str` stripped of whitespace
* characters from both its beginning and end
* @private
*/
function trim(str) {
return str.trim();
}
......@@ -11,14 +11,20 @@ const url = require('url');
const PerMessageDeflate = require('./permessage-deflate');
const EventTarget = require('./event-target');
const extension = require('./extension');
const constants = require('./constants');
const Receiver = require('./receiver');
const Sender = require('./sender');
const {
BINARY_TYPES,
EMPTY_BUFFER,
GUID,
kStatusCode,
kWebSocket,
NOOP
} = require('./constants');
const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
const kWebSocket = constants.kWebSocket;
const protocolVersions = [8, 13];
const closeTimeout = 30 * 1000; // Allow 30 seconds to terminate the connection cleanly.
const closeTimeout = 30 * 1000;
/**
* Class representing a WebSocket.
......@@ -39,19 +45,21 @@ class WebSocket extends EventEmitter {
this.readyState = WebSocket.CONNECTING;
this.protocol = '';
this._binaryType = constants.BINARY_TYPES[0];
this._binaryType = BINARY_TYPES[0];
this._closeFrameReceived = false;
this._closeFrameSent = false;
this._closeMessage = '';
this._closeTimer = null;
this._closeCode = 1006;
this._extensions = {};
this._isServer = true;
this._receiver = null;
this._sender = null;
this._socket = null;
if (address !== null) {
this._isServer = false;
this._redirects = 0;
if (Array.isArray(protocols)) {
protocols = protocols.join(', ');
} else if (typeof protocols === 'object' && protocols !== null) {
......@@ -59,7 +67,9 @@ class WebSocket extends EventEmitter {
protocols = undefined;
}
initAsClient.call(this, address, protocols, options);
initAsClient(this, address, protocols, options);
} else {
this._isServer = true;
}
}
......@@ -77,8 +87,9 @@ class WebSocket extends EventEmitter {
}
/**
* This deviates from the WHATWG interface since ws doesn't support the required
* default "blob" type (instead we define a custom "nodebuffer" type).
* This deviates from the WHATWG interface since ws doesn't support the
* required default "blob" type (instead we define a custom "nodebuffer"
* type).
*
* @type {String}
*/
......@@ -87,7 +98,7 @@ class WebSocket extends EventEmitter {
}
set binaryType(type) {
if (!constants.BINARY_TYPES.includes(type)) return;
if (!BINARY_TYPES.includes(type)) return;
this._binaryType = type;
......@@ -220,20 +231,16 @@ class WebSocket extends EventEmitter {
if (err) return;
this._closeFrameSent = true;
if (this._socket.writable) {
if (this._closeFrameReceived) this._socket.end();
//
// Ensure that the connection is closed even if the closing handshake
// fails.
//
this._closeTimer = setTimeout(
this._socket.destroy.bind(this._socket),
closeTimeout
);
}
if (this._closeFrameReceived) this._socket.end();
});
//
// Specify a timeout for the closing handshake to complete.
//
this._closeTimer = setTimeout(
this._socket.destroy.bind(this._socket),
closeTimeout
);
}
/**
......@@ -265,7 +272,7 @@ class WebSocket extends EventEmitter {
if (typeof data === 'number') data = data.toString();
if (mask === undefined) mask = !this._isServer;
this._sender.ping(data || constants.EMPTY_BUFFER, mask, cb);
this._sender.ping(data || EMPTY_BUFFER, mask, cb);
}
/**
......@@ -297,7 +304,7 @@ class WebSocket extends EventEmitter {
if (typeof data === 'number') data = data.toString();
if (mask === undefined) mask = !this._isServer;
this._sender.pong(data || constants.EMPTY_BUFFER, mask, cb);
this._sender.pong(data || EMPTY_BUFFER, mask, cb);
}
/**
......@@ -344,7 +351,7 @@ class WebSocket extends EventEmitter {
opts.compress = false;
}
this._sender.send(data || constants.EMPTY_BUFFER, opts, cb);
this._sender.send(data || EMPTY_BUFFER, opts, cb);
}
/**
......@@ -417,22 +424,31 @@ module.exports = WebSocket;
/**
* Initialize a WebSocket client.
*
* @param {WebSocket} websocket The client to initialize
* @param {(String|url.Url|url.URL)} address The URL to which to connect
* @param {String} protocols The subprotocols
* @param {Object} options Connection options
* @param {(Boolean|Object)} options.perMessageDeflate Enable/disable permessage-deflate
* @param {Number} options.handshakeTimeout Timeout in milliseconds for the handshake request
* @param {Number} options.protocolVersion Value of the `Sec-WebSocket-Version` header
* @param {String} options.origin Value of the `Origin` or `Sec-WebSocket-Origin` header
* @param {(Boolean|Object)} options.perMessageDeflate Enable/disable
* permessage-deflate
* @param {Number} options.handshakeTimeout Timeout in milliseconds for the
* handshake request
* @param {Number} options.protocolVersion Value of the `Sec-WebSocket-Version`
* header
* @param {String} options.origin Value of the `Origin` or
* `Sec-WebSocket-Origin` header
* @param {Number} options.maxPayload The maximum allowed message size
* @param {Boolean} options.followRedirects Whether or not to follow redirects
* @param {Number} options.maxRedirects The maximum number of redirects allowed
* @private
*/
function initAsClient(address, protocols, options) {
options = Object.assign(
function initAsClient(websocket, address, protocols, options) {
const opts = Object.assign(
{
protocolVersion: protocolVersions[1],
maxPayload: 100 * 1024 * 1024,
perMessageDeflate: true,
maxPayload: 100 * 1024 * 1024
followRedirects: false,
maxRedirects: 10
},
options,
{
......@@ -449,136 +465,159 @@ function initAsClient(address, protocols, options) {
}
);
if (!protocolVersions.includes(options.protocolVersion)) {
if (!protocolVersions.includes(opts.protocolVersion)) {
throw new RangeError(
`Unsupported protocol version: ${options.protocolVersion} ` +
`Unsupported protocol version: ${opts.protocolVersion} ` +
`(supported versions: ${protocolVersions.join(', ')})`
);
}
this._isServer = false;
var parsedUrl;
if (typeof address === 'object' && address.href !== undefined) {
parsedUrl = address;
this.url = address.href;
websocket.url = address.href;
} else {
//
// The WHATWG URL constructor is not available on Node.js < 6.13.0
//
parsedUrl = url.URL ? new url.URL(address) : url.parse(address);
this.url = address;
websocket.url = address;
}
const isUnixSocket = parsedUrl.protocol === 'ws+unix:';
if (!parsedUrl.host && (!isUnixSocket || !parsedUrl.pathname)) {
throw new Error(`Invalid URL: ${this.url}`);
throw new Error(`Invalid URL: ${websocket.url}`);
}
const isSecure =
parsedUrl.protocol === 'wss:' || parsedUrl.protocol === 'https:';
const defaultPort = isSecure ? 443 : 80;
const key = crypto.randomBytes(16).toString('base64');
const httpObj = isSecure ? https : http;
const get = isSecure ? https.get : http.get;
const path = parsedUrl.search
? `${parsedUrl.pathname || '/'}${parsedUrl.search}`
: parsedUrl.pathname || '/';
var perMessageDeflate;
options.createConnection = isSecure ? tlsConnect : netConnect;
options.defaultPort = options.defaultPort || defaultPort;
options.port = parsedUrl.port || defaultPort;
options.host = parsedUrl.hostname.startsWith('[')
opts.createConnection = isSecure ? tlsConnect : netConnect;
opts.defaultPort = opts.defaultPort || defaultPort;
opts.port = parsedUrl.port || defaultPort;
opts.host = parsedUrl.hostname.startsWith('[')
? parsedUrl.hostname.slice(1, -1)
: parsedUrl.hostname;
options.headers = Object.assign(
opts.headers = Object.assign(
{
'Sec-WebSocket-Version': options.protocolVersion,
'Sec-WebSocket-Version': opts.protocolVersion,
'Sec-WebSocket-Key': key,
Connection: 'Upgrade',
Upgrade: 'websocket'
},
options.headers
opts.headers
);
options.path = path;
options.timeout = options.handshakeTimeout;
opts.path = path;
opts.timeout = opts.handshakeTimeout;
if (options.perMessageDeflate) {
if (opts.perMessageDeflate) {
perMessageDeflate = new PerMessageDeflate(
options.perMessageDeflate !== true ? options.perMessageDeflate : {},
opts.perMessageDeflate !== true ? opts.perMessageDeflate : {},
false,
options.maxPayload
opts.maxPayload
);
options.headers['Sec-WebSocket-Extensions'] = extension.format({
opts.headers['Sec-WebSocket-Extensions'] = extension.format({
[PerMessageDeflate.extensionName]: perMessageDeflate.offer()
});
}
if (protocols) {
options.headers['Sec-WebSocket-Protocol'] = protocols;
opts.headers['Sec-WebSocket-Protocol'] = protocols;
}
if (options.origin) {
if (options.protocolVersion < 13) {
options.headers['Sec-WebSocket-Origin'] = options.origin;
if (opts.origin) {
if (opts.protocolVersion < 13) {
opts.headers['Sec-WebSocket-Origin'] = opts.origin;
} else {
options.headers.Origin = options.origin;
opts.headers.Origin = opts.origin;
}
}
if (parsedUrl.auth) {
options.auth = parsedUrl.auth;
opts.auth = parsedUrl.auth;
} else if (parsedUrl.username || parsedUrl.password) {
options.auth = `${parsedUrl.username}:${parsedUrl.password}`;
opts.auth = `${parsedUrl.username}:${parsedUrl.password}`;
}
if (isUnixSocket) {
const parts = path.split(':');
options.socketPath = parts[0];
options.path = parts[1];
opts.socketPath = parts[0];
opts.path = parts[1];
}
var req = (this._req = httpObj.get(options));
var req = (websocket._req = get(opts));
if (options.handshakeTimeout) {
if (opts.timeout) {
req.on('timeout', () => {
abortHandshake(this, req, 'Opening handshake has timed out');
abortHandshake(websocket, req, 'Opening handshake has timed out');
});
}
req.on('error', (err) => {
if (this._req.aborted) return;
if (websocket._req.aborted) return;
req = this._req = null;
this.readyState = WebSocket.CLOSING;
this.emit('error', err);
this.emitClose();
req = websocket._req = null;
websocket.readyState = WebSocket.CLOSING;
websocket.emit('error', err);
websocket.emitClose();
});
req.on('response', (res) => {
if (this.emit('unexpected-response', req, res)) return;
const location = res.headers.location;
const statusCode = res.statusCode;
if (
location &&
opts.followRedirects &&
statusCode >= 300 &&
statusCode < 400
) {
if (++websocket._redirects > opts.maxRedirects) {
abortHandshake(websocket, req, 'Maximum redirects exceeded');
return;
}
req.abort();
const addr = url.URL
? new url.URL(location, address)
: url.resolve(address, location);
abortHandshake(this, req, `Unexpected server response: ${res.statusCode}`);
initAsClient(websocket, addr, protocols, options);
} else if (!websocket.emit('unexpected-response', req, res)) {
abortHandshake(
websocket,
req,
`Unexpected server response: ${res.statusCode}`
);
}
});
req.on('upgrade', (res, socket, head) => {
this.emit('upgrade', res);
websocket.emit('upgrade', res);
//
// The user may have closed the connection from a listener of the `upgrade`
// event.
//
if (this.readyState !== WebSocket.CONNECTING) return;
if (websocket.readyState !== WebSocket.CONNECTING) return;
req = this._req = null;
req = websocket._req = null;
const digest = crypto
.createHash('sha1')
.update(key + constants.GUID, 'binary')
.update(key + GUID)
.digest('base64');
if (res.headers['sec-websocket-accept'] !== digest) {
abortHandshake(this, socket, 'Invalid Sec-WebSocket-Accept header');
abortHandshake(websocket, socket, 'Invalid Sec-WebSocket-Accept header');
return;
}
......@@ -595,11 +634,11 @@ function initAsClient(address, protocols, options) {
}
if (protError) {
abortHandshake(this, socket, protError);
abortHandshake(websocket, socket, protError);
return;
}
if (serverProt) this.protocol = serverProt;
if (serverProt) websocket.protocol = serverProt;
if (perMessageDeflate) {
try {
......@@ -609,15 +648,21 @@ function initAsClient(address, protocols, options) {
if (extensions[PerMessageDeflate.extensionName]) {
perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]);
this._extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
websocket._extensions[
PerMessageDeflate.extensionName
] = perMessageDeflate;
}
} catch (err) {
abortHandshake(this, socket, 'Invalid Sec-WebSocket-Extensions header');
abortHandshake(
websocket,
socket,
'Invalid Sec-WebSocket-Extensions header'
);
return;
}
}
this.setSocket(socket, head, options.maxPayload);
websocket.setSocket(socket, head, opts.maxPayload);
});
}
......@@ -720,7 +765,7 @@ function receiverOnError(err) {
websocket._socket.removeListener('data', socketOnData);
websocket.readyState = WebSocket.CLOSING;
websocket._closeCode = err[constants.kStatusCode];
websocket._closeCode = err[kStatusCode];
websocket.emit('error', err);
websocket._socket.destroy();
}
......@@ -753,7 +798,7 @@ function receiverOnMessage(data) {
function receiverOnPing(data) {
const websocket = this[kWebSocket];
websocket.pong(data, !websocket._isServer, constants.NOOP);
websocket.pong(data, !websocket._isServer, NOOP);
websocket.emit('ping', data);
}
......@@ -843,10 +888,8 @@ function socketOnError() {
const websocket = this[kWebSocket];
this.removeListener('error', socketOnError);
this.on('error', constants.NOOP);
this.on('error', NOOP);
if (websocket) {
websocket.readyState = WebSocket.CLOSING;
this.destroy();
}
websocket.readyState = WebSocket.CLOSING;
this.destroy();
}
{
"name": "ws",
"version": "6.1.4",
"_args": [
[
"ws@6.2.2",
"/Users/m/workspace/antSword"
]
],
"_from": "ws@6.2.2",
"_hasShrinkwrap": false,
"_id": "ws@6.2.2",
"_inCache": true,
"_installable": true,
"_location": "/ws",
"_nodeVersion": "16.2.0",
"_npmOperationalInternal": {
"host": "s3://npm-registry-packages",
"tmp": "tmp/ws_6.2.2_1622572492507_0.791522012984119"
},
"_npmUser": {
"email": "luigipinca@gmail.com",
"name": "lpinca"
},
"_npmVersion": "7.13.0",
"_phantomChildren": {},
"_requested": {
"name": "ws",
"raw": "ws@6.2.2",
"rawSpec": "6.2.2",
"scope": null,
"spec": "6.2.2",
"type": "version"
},
"_requiredBy": [
"#USER"
],
"_resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
"_shasum": "dd5cdbd57a9979916097652d78f1cc5faea0c32e",
"_shrinkwrap": null,
"_spec": "ws@6.2.2",
"_where": "/Users/m/workspace/antSword",
"author": {
"email": "einaros@gmail.com",
"name": "Einar Otto Stangvik",
"url": "http://2x.io"
},
"browser": "browser.js",
"bugs": {
"url": "https://github.com/websockets/ws/issues"
},
"dependencies": {
"async-limiter": "~1.0.0"
},
"description": "Simple to use, blazing fast and thoroughly tested websocket client and server for Node.js",
"devDependencies": {
"benchmark": "~2.1.4",
"bufferutil": "~4.0.0",
"coveralls": "~3.0.3",
"eslint": "~5.15.0",
"eslint-config-prettier": "~4.1.0",
"eslint-plugin-prettier": "~3.0.0",
"mocha": "~6.0.0",
"nyc": "~13.3.0",
"prettier": "~1.16.1",
"utf-8-validate": "~5.0.0"
},
"directories": {},
"dist": {
"fileCount": 15,
"integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
"npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgtn3NCRA9TVsSAnZWagAAxFsP/jSEBOvCrZ5dtS/nj0ua\nqsCbU/Y1wqG/M0edJS6b9hM2SgKdmHPH9byqgrBs6SlEBSi8v0H0XEhvUjd0\nW3SNXps8SA0j7O1v6Sbb9YbJm0RU14Cq0gOngxJ/uad6XOOwCSsQqV9FfA9R\nnzx9QBXnJVks+q/LS3qDJ2XbvBDD4nA+YiIPJHu9nry21Z+lC48lsMQinLVZ\n4pAvCHAYNzgUg0J6B0qQQ+wc+i9Ml/3CRUmZMJ32h/yx2zzdwQ5zR5PDl3xb\nKhROfEE2kybahk7bYMIwBCVNPhe+WeoOKBJlu7V/Mzx3x/RnsSLUu6pF2alI\nvNfb4cS9B0ik9p8OdC9ULauDVKwHusfoxNjtmDFqDmxgh0zEnCQidhaWep0k\nwg30GmEkx3lzGWgjOJ8/1svW0TFVUUojGvQQc0qmgM+9Rn2tqcWkdV+xNm6A\nymtt9ZDpPPFBPmtipqbcRcLU7JuLM9PUz6xqRBCYyerBc1aqO0/oIevFuC/F\nifB8ZNPVvVwUJq2gz0KTkrnqpa1GDvaP4abFihpuXeodhN9QEF1YvCItdoAp\n4kWSOpVVpLfmJAxEgtHhkCKwfYJEP8lf7mjS+ilnkeP0cKO6Aq/N693fnH1W\nWE12+K2HXSmpWzoyKiFyU7psS9afANp4QbQsVh/mhQKSGRyIl4wmU1emJT9b\nv8eQ\r\n=1LXy\r\n-----END PGP SIGNATURE-----\r\n",
"shasum": "dd5cdbd57a9979916097652d78f1cc5faea0c32e",
"signatures": [
{
"keyid": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA",
"sig": "MEYCIQDTi3OXyBRVRRDbt1DkzzQvNJvHj01yrckBeI61CKkihAIhAJ20N24tK5ntCd8n0EL5JZEQVtwt2nYggGUWmIgr9pVY"
}
],
"tarball": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
"unpackedSize": 101735
},
"gitHead": "9bdb58070d64c33a9beeac7c732aac0f4e7e18b7",
"homepage": "https://github.com/websockets/ws",
"keywords": [
"HyBi",
"Push",
......@@ -10,36 +88,37 @@
"WebSockets",
"real-time"
],
"homepage": "https://github.com/websockets/ws",
"bugs": "https://github.com/websockets/ws/issues",
"repository": "websockets/ws",
"author": "Einar Otto Stangvik <einaros@gmail.com> (http://2x.io)",
"license": "MIT",
"main": "index.js",
"browser": "browser.js",
"files": [
"browser.js",
"index.js",
"lib/*.js"
"maintainers": [
{
"email": "einaros@gmail.com",
"name": "einaros"
},
{
"email": "npm@3rd-Eden.com",
"name": "v1"
},
{
"email": "luigipinca@gmail.com",
"name": "lpinca"
},
{
"email": "npmjs@3rd-Eden.com",
"name": "3rdeden"
}
],
"name": "ws",
"optionalDependencies": {},
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/websockets/ws.git"
},
"scripts": {
"test": "npm run lint && nyc --reporter=html --reporter=text mocha test/*.test.js",
"integration": "npm run lint && mocha test/*.integration.js",
"lint": "eslint . --ignore-path .gitignore && prettylint '**/*.{json,md}' --ignore-path .gitignore"
},
"dependencies": {
"async-limiter": "~1.0.0"
"lint": "eslint --ignore-path .gitignore . && prettier --check --ignore-path .gitignore \"**/*.{json,md,yml}\"",
"test": "npm run lint && nyc --reporter=html --reporter=text mocha test/*.test.js"
},
"devDependencies": {
"benchmark": "~2.1.4",
"bufferutil": "~4.0.0",
"eslint": "~5.14.0",
"eslint-config-prettier": "~4.0.0",
"eslint-plugin-prettier": "~3.0.0",
"mocha": "~5.2.0",
"nyc": "~13.3.0",
"prettier": "~1.16.1",
"prettylint": "~1.0.0",
"utf-8-validate": "~5.0.0"
}
"version": "6.2.2"
}
......@@ -17,7 +17,7 @@
"superagent-proxy": "3.0.0",
"tar": "4.4.18",
"through": "2.3.8",
"ws": "6.1.4",
"ws": "6.2.2",
"mime": "2.6.0",
"xml2js": "0.4.23"
},
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment