1
0
mirror of https://github.com/musix-org/musix-oss synced 2025-06-16 12:36:01 +00:00
This commit is contained in:
MatteZ02
2019-05-30 12:06:47 +03:00
parent cbdffcf19c
commit 5eb0264906
2502 changed files with 360854 additions and 0 deletions

21
node_modules/ws/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

389
node_modules/ws/README.md generated vendored Normal file
View File

@ -0,0 +1,389 @@
# 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)
ws is a simple to use, blazing fast, and thoroughly tested WebSocket client
and server implementation.
Passes the quite extensive Autobahn test suite: [server][server-report],
[client][client-report].
**Note**: This module does not work in the browser. The client in the docs is a
reference to a back end with the role of a client in the WebSocket
communication. Browser clients must use the native
[`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) object.
To make the same code work seamlessly on Node.js and the browser, you can use
one of the many wrappers available on npm, like
[isomorphic-ws](https://github.com/heineiuo/isomorphic-ws).
## Table of Contents
* [Protocol support](#protocol-support)
* [Installing](#installing)
+ [Opt-in for performance and spec compliance](#opt-in-for-performance-and-spec-compliance)
* [API docs](#api-docs)
* [WebSocket compression](#websocket-compression)
* [Usage examples](#usage-examples)
+ [Sending and receiving text data](#sending-and-receiving-text-data)
+ [Sending binary data](#sending-binary-data)
+ [Server example](#server-example)
+ [Broadcast example](#broadcast-example)
+ [ExpressJS example](#expressjs-example)
+ [echo.websocket.org demo](#echowebsocketorg-demo)
+ [Other examples](#other-examples)
* [Error handling best practices](#error-handling-best-practices)
* [FAQ](#faq)
+ [How to get the IP address of the client?](#how-to-get-the-ip-address-of-the-client)
+ [How to detect and close broken connections?](#how-to-detect-and-close-broken-connections)
+ [How to connect via a proxy?](#how-to-connect-via-a-proxy)
* [Changelog](#changelog)
* [License](#license)
## Protocol support
* **HyBi drafts 07-12** (Use the option `protocolVersion: 8`)
* **HyBi drafts 13-17** (Current default, alternatively option `protocolVersion: 13`)
## Installing
```
npm install --save ws
```
### Opt-in for performance and spec compliance
There are 2 optional modules that can be installed along side with the ws
module. These modules are binary addons which improve certain operations.
Prebuilt binaries are available for the most popular platforms so you don't
necessarily need to have a C++ compiler installed on your machine.
- `npm install --save-optional bufferutil`: Allows to efficiently perform
operations such as masking and unmasking the data payload of the WebSocket
frames.
- `npm install --save-optional utf-8-validate`: Allows to efficiently check
if a message contains valid UTF-8 as required by the spec.
## API docs
See [`/doc/ws.md`](./doc/ws.md) for Node.js-like docs for the ws classes.
## WebSocket compression
ws supports the [permessage-deflate extension][permessage-deflate] which
enables the client and server to negotiate a compression algorithm and its
parameters, and then selectively apply it to the data payloads of each
WebSocket message.
The extension is disabled by default on the server and enabled by default on
the client. It adds a significant overhead in terms of performance and memory
consumption so we suggest to enable it only if it is really needed.
Note that Node.js has a variety of issues with high-performance compression,
where increased concurrency, especially on Linux, can lead to
[catastrophic memory fragmentation][node-zlib-bug] and slow performance.
If you intend to use permessage-deflate in production, it is worthwhile to set
up a test representative of your workload and ensure Node.js/zlib will handle
it with acceptable performance and memory usage.
Tuning of permessage-deflate can be done via the options defined below. You can
also use `zlibDeflateOptions` and `zlibInflateOptions`, which is passed directly
into the creation of [raw deflate/inflate streams][node-zlib-deflaterawdocs].
See [the docs][ws-server-options] for more options.
```js
const WebSocket = require('ws');
const wss = new WebSocket.Server({
port: 8080,
perMessageDeflate: {
zlibDeflateOptions: { // See zlib defaults.
chunkSize: 1024,
memLevel: 7,
level: 3,
},
zlibInflateOptions: {
chunkSize: 10 * 1024
},
// Other options settable:
clientNoContextTakeover: true, // Defaults to negotiated value.
serverNoContextTakeover: true, // Defaults to negotiated value.
clientMaxWindowBits: 10, // Defaults to negotiated value.
serverMaxWindowBits: 10, // Defaults to negotiated value.
// Below options specified as default values.
concurrencyLimit: 10, // Limits zlib concurrency for perf.
threshold: 1024, // Size (in bytes) below which messages
// should not be compressed.
}
});
```
The client will only use the extension if it is supported and enabled on the
server. To always disable the extension on the client set the
`perMessageDeflate` option to `false`.
```js
const WebSocket = require('ws');
const ws = new WebSocket('ws://www.host.com/path', {
perMessageDeflate: false
});
```
## Usage examples
### Sending and receiving text data
```js
const WebSocket = require('ws');
const ws = new WebSocket('ws://www.host.com/path');
ws.on('open', function open() {
ws.send('something');
});
ws.on('message', function incoming(data) {
console.log(data);
});
```
### Sending binary data
```js
const WebSocket = require('ws');
const ws = new WebSocket('ws://www.host.com/path');
ws.on('open', function open() {
const array = new Float32Array(5);
for (var i = 0; i < array.length; ++i) {
array[i] = i / 2;
}
ws.send(array);
});
```
### Server example
```js
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});
ws.send('something');
});
```
### Broadcast example
```js
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
// Broadcast to all.
wss.broadcast = function broadcast(data) {
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
};
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(data) {
// Broadcast to everyone else.
wss.clients.forEach(function each(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
});
});
```
### ExpressJS example
```js
const express = require('express');
const http = require('http');
const url = require('url');
const WebSocket = require('ws');
const app = express();
app.use(function (req, res) {
res.send({ msg: "hello" });
});
const server = http.createServer(app);
const wss = new WebSocket.Server({ server });
wss.on('connection', function connection(ws, req) {
const location = url.parse(req.url, true);
// You might use location.query.access_token to authenticate or share sessions
// or req.headers.cookie (see http://stackoverflow.com/a/16395220/151312)
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});
ws.send('something');
});
server.listen(8080, function listening() {
console.log('Listening on %d', server.address().port);
});
```
### echo.websocket.org demo
```js
const WebSocket = require('ws');
const ws = new WebSocket('wss://echo.websocket.org/', {
origin: 'https://websocket.org'
});
ws.on('open', function open() {
console.log('connected');
ws.send(Date.now());
});
ws.on('close', function close() {
console.log('disconnected');
});
ws.on('message', function incoming(data) {
console.log(`Roundtrip time: ${Date.now() - data} ms`);
setTimeout(function timeout() {
ws.send(Date.now());
}, 500);
});
```
### Other examples
For a full example with a browser client communicating with a ws server, see the
examples folder.
Otherwise, see the test cases.
## Error handling best practices
```js
// If the WebSocket is closed before the following send is attempted
ws.send('something');
// Errors (both immediate and async write errors) can be detected in an optional
// callback. The callback is also the only way of being notified that data has
// actually been sent.
ws.send('something', function ack(error) {
// If error is not defined, the send has been completed, otherwise the error
// object will indicate what failed.
});
// Immediate errors can also be handled with `try...catch`, but **note** that
// since sends are inherently asynchronous, socket write failures will *not* be
// captured when this technique is used.
try { ws.send('something'); }
catch (e) { /* handle error */ }
```
## FAQ
### How to get the IP address of the client?
The remote IP address can be obtained from the raw socket.
```js
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws, req) {
const ip = req.connection.remoteAddress;
});
```
When the server runs behind a proxy like NGINX, the de-facto standard is to use
the `X-Forwarded-For` header.
```js
wss.on('connection', function connection(ws, req) {
const ip = req.headers['x-forwarded-for'];
});
```
### How to detect and close broken connections?
Sometimes the link between the server and the client can be interrupted in a
way that keeps both the server and the client unaware of the broken state of the
connection (e.g. when pulling the cord).
In these cases ping messages can be used as a means to verify that the remote
endpoint is still responsive.
```js
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
function noop() {}
function heartbeat() {
this.isAlive = true;
}
wss.on('connection', function connection(ws) {
ws.isAlive = true;
ws.on('pong', heartbeat);
});
const interval = setInterval(function ping() {
wss.clients.forEach(function each(ws) {
if (ws.isAlive === false) return ws.terminate();
ws.isAlive = false;
ws.ping(noop);
});
}, 30000);
```
Pong messages are automatically sent in response to ping messages as required
by the spec.
### How to connect via a proxy?
Use a custom `http.Agent` implementation like [https-proxy-agent][] or
[socks-proxy-agent][].
## Changelog
We're using the GitHub [releases][changelog] for changelog entries.
## License
[MIT](LICENSE)
[https-proxy-agent]: https://github.com/TooTallNate/node-https-proxy-agent
[socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent
[client-report]: http://websockets.github.io/ws/autobahn/clients/
[server-report]: http://websockets.github.io/ws/autobahn/servers/
[permessage-deflate]: https://tools.ietf.org/html/rfc7692
[changelog]: https://github.com/websockets/ws/releases
[node-zlib-bug]: https://github.com/nodejs/node/issues/8871
[node-zlib-deflaterawdocs]: https://nodejs.org/api/zlib.html#zlib_zlib_createdeflateraw_options
[ws-server-options]: https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback

9
node_modules/ws/index.js generated vendored Normal file
View File

@ -0,0 +1,9 @@
'use strict';
const WebSocket = require('./lib/websocket');
WebSocket.Server = require('./lib/websocket-server');
WebSocket.Receiver = require('./lib/receiver');
WebSocket.Sender = require('./lib/sender');
module.exports = WebSocket;

BIN
node_modules/ws/lib/.DS_Store generated vendored Normal file

Binary file not shown.

65
node_modules/ws/lib/buffer-util.js generated vendored Normal file
View File

@ -0,0 +1,65 @@
'use strict';
const safeBuffer = require('safe-buffer');
const Buffer = safeBuffer.Buffer;
/**
* Merges an array of buffers into a new buffer.
*
* @param {Buffer[]} list The array of buffers to concat
* @param {Number} totalLength The total length of buffers in the list
* @return {Buffer} The resulting buffer
* @public
*/
const concat = (list, totalLength) => {
const target = Buffer.allocUnsafe(totalLength);
var offset = 0;
for (var i = 0; i < list.length; i++) {
const buf = list[i];
buf.copy(target, offset);
offset += buf.length;
}
return target;
};
try {
const bufferUtil = require('bufferutil');
module.exports = Object.assign({ concat }, bufferUtil.BufferUtil || bufferUtil);
} catch (e) /* istanbul ignore next */ {
/**
* Masks a buffer using the given mask.
*
* @param {Buffer} source The buffer to mask
* @param {Buffer} mask The mask to use
* @param {Buffer} output The buffer where to store the result
* @param {Number} offset The offset at which to start writing
* @param {Number} length The number of bytes to mask.
* @public
*/
const mask = (source, mask, output, offset, length) => {
for (var i = 0; i < length; i++) {
output[offset + i] = source[i] ^ mask[i & 3];
}
};
/**
* Unmasks a buffer using the given mask.
*
* @param {Buffer} buffer The buffer to unmask
* @param {Buffer} mask The mask to use
* @public
*/
const 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++) {
buffer[i] ^= mask[i & 3];
}
};
module.exports = { concat, mask, unmask };
}

10
node_modules/ws/lib/constants.js generated vendored Normal file
View File

@ -0,0 +1,10 @@
'use strict';
const safeBuffer = require('safe-buffer');
const Buffer = safeBuffer.Buffer;
exports.BINARY_TYPES = ['nodebuffer', 'arraybuffer', 'fragments'];
exports.GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
exports.EMPTY_BUFFER = Buffer.alloc(0);
exports.NOOP = () => {};

170
node_modules/ws/lib/event-target.js generated vendored Normal file
View File

@ -0,0 +1,170 @@
'use strict';
/**
* Class representing an event.
*
* @private
*/
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
*/
constructor (type, target) {
this.target = target;
this.type = type;
}
}
/**
* Class representing a message event.
*
* @extends Event
* @private
*/
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
*/
constructor (data, target) {
super('message', target);
this.data = data;
}
}
/**
* Class representing a close event.
*
* @extends Event
* @private
*/
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
*/
constructor (code, reason, target) {
super('close', target);
this.wasClean = target._closeFrameReceived && target._closeFrameSent;
this.reason = reason;
this.code = code;
}
}
/**
* Class representing an open event.
*
* @extends Event
* @private
*/
class OpenEvent extends Event {
/**
* Create a new `OpenEvent`.
*
* @param {WebSocket} target A reference to the target to which the event was dispatched
*/
constructor (target) {
super('open', target);
}
}
/**
* Class representing an error event.
*
* @extends Event
* @private
*/
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
*/
constructor (error, target) {
super('error', target);
this.message = error.message;
this.error = error;
}
}
/**
* This provides methods for emulating the `EventTarget` interface. It's not
* meant to be used directly.
*
* @mixin
*/
const EventTarget = {
/**
* Register an event listener.
*
* @param {String} method A string representing the event type to listen for
* @param {Function} listener The listener to add
* @public
*/
addEventListener (method, listener) {
if (typeof listener !== 'function') return;
function onMessage (data) {
listener.call(this, new MessageEvent(data, this));
}
function onClose (code, message) {
listener.call(this, new CloseEvent(code, message, this));
}
function onError (error) {
listener.call(this, new ErrorEvent(error, this));
}
function onOpen () {
listener.call(this, new OpenEvent(this));
}
if (method === 'message') {
onMessage._listener = listener;
this.on(method, onMessage);
} else if (method === 'close') {
onClose._listener = listener;
this.on(method, onClose);
} else if (method === 'error') {
onError._listener = listener;
this.on(method, onError);
} else if (method === 'open') {
onOpen._listener = listener;
this.on(method, onOpen);
} else {
this.on(method, listener);
}
},
/**
* Remove an event listener.
*
* @param {String} method A string representing the event type to remove
* @param {Function} listener The listener to remove
* @public
*/
removeEventListener (method, listener) {
const listeners = this.listeners(method);
for (var i = 0; i < listeners.length; i++) {
if (listeners[i] === listener || listeners[i]._listener === listener) {
this.removeListener(method, listeners[i]);
}
}
}
};
module.exports = EventTarget;

211
node_modules/ws/lib/extension.js generated vendored Normal file
View File

@ -0,0 +1,211 @@
'use strict';
//
// Allowed token characters:
//
// '!', '#', '$', '%', '&', ''', '*', '+', '-',
// '.', 0-9, A-Z, '^', '_', '`', a-z, '|', '~'
//
// tokenChars[32] === 0 // ' '
// tokenChars[33] === 1 // '!'
// tokenChars[34] === 0 // '"'
// ...
//
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
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32 - 47
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80 - 95
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 // 112 - 127
];
/**
* Adds an offer to the map of extension offers or a parameter to the map of
* parameters.
*
* @param {Object} dest The map of extension offers or parameters
* @param {String} name The extension or parameter name
* @param {(Object|Boolean|String)} elem The extension parameters or the
* parameter value
* @private
*/
function push (dest, name, elem) {
if (Object.prototype.hasOwnProperty.call(dest, name)) dest[name].push(elem);
else dest[name] = [elem];
}
/**
* Parses the `Sec-WebSocket-Extensions` header into an object.
*
* @param {String} header The field value of the header
* @return {Object} The parsed object
* @public
*/
function parse (header) {
const offers = {};
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;
for (var i = 0; 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' */) {
if (end === -1 && start !== -1) end = i;
} else if (code === 0x3b/* ';' */ || code === 0x2c/* ',' */) {
if (start === -1) {
throw new SyntaxError(`Unexpected character at index ${i}`);
}
if (end === -1) end = i;
const name = header.slice(start, end);
if (code === 0x2c) {
push(offers, name, params);
params = {};
} else {
extensionName = name;
}
start = end = -1;
} else {
throw new SyntaxError(`Unexpected character at index ${i}`);
}
} else if (paramName === undefined) {
if (end === -1 && tokenChars[code] === 1) {
if (start === -1) start = i;
} else if (code === 0x20 || code === 0x09) {
if (end === -1 && start !== -1) end = i;
} else if (code === 0x3b || code === 0x2c) {
if (start === -1) {
throw new SyntaxError(`Unexpected character at index ${i}`);
}
if (end === -1) end = i;
push(params, header.slice(start, end), true);
if (code === 0x2c) {
push(offers, extensionName, params);
params = {};
extensionName = undefined;
}
start = end = -1;
} else if (code === 0x3d/* '=' */&& start !== -1 && end === -1) {
paramName = header.slice(start, i);
start = end = -1;
} else {
throw new SyntaxError(`Unexpected character at index ${i}`);
}
} else {
//
// The value of a quoted-string after unescaping must conform to the
// token ABNF, so only token characters are valid.
// Ref: https://tools.ietf.org/html/rfc6455#section-9.1
//
if (isEscaping) {
if (tokenChars[code] !== 1) {
throw new SyntaxError(`Unexpected character at index ${i}`);
}
if (start === -1) start = i;
else if (!mustUnescape) mustUnescape = true;
isEscaping = false;
} else if (inQuotes) {
if (tokenChars[code] === 1) {
if (start === -1) start = i;
} else if (code === 0x22/* '"' */ && start !== -1) {
inQuotes = false;
end = i;
} else if (code === 0x5c/* '\' */) {
isEscaping = true;
} else {
throw new SyntaxError(`Unexpected character at index ${i}`);
}
} else if (code === 0x22 && header.charCodeAt(i - 1) === 0x3d) {
inQuotes = true;
} else if (end === -1 && tokenChars[code] === 1) {
if (start === -1) start = i;
} else if (start !== -1 && (code === 0x20 || code === 0x09)) {
if (end === -1) end = i;
} else if (code === 0x3b || code === 0x2c) {
if (start === -1) {
throw new SyntaxError(`Unexpected character at index ${i}`);
}
if (end === -1) end = i;
var value = header.slice(start, end);
if (mustUnescape) {
value = value.replace(/\\/g, '');
mustUnescape = false;
}
push(params, paramName, value);
if (code === 0x2c) {
push(offers, extensionName, params);
params = {};
extensionName = undefined;
}
paramName = undefined;
start = end = -1;
} else {
throw new SyntaxError(`Unexpected character at index ${i}`);
}
}
}
if (start === -1 || inQuotes) {
throw new SyntaxError('Unexpected end of input');
}
if (end === -1) end = i;
const token = header.slice(start, end);
if (extensionName === undefined) {
push(offers, token, {});
} else {
if (paramName === undefined) {
push(params, token, true);
} else if (mustUnescape) {
push(params, paramName, token.replace(/\\/g, ''));
} else {
push(params, paramName, token);
}
push(offers, extensionName, params);
}
return offers;
}
/**
* Builds the `Sec-WebSocket-Extensions` header field value.
*
* @param {Object} extensions The map of extensions and parameters to format
* @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(', ');
}
module.exports = { format, parse };

521
node_modules/ws/lib/permessage-deflate.js generated vendored Normal file
View File

@ -0,0 +1,521 @@
'use strict';
const safeBuffer = require('safe-buffer');
const Limiter = require('async-limiter');
const zlib = require('zlib');
const bufferUtil = require('./buffer-util');
const Buffer = safeBuffer.Buffer;
const TRAILER = Buffer.from([0x00, 0x00, 0xff, 0xff]);
const EMPTY_BLOCK = Buffer.from([0x00]);
const kWriteInProgress = Symbol('write-in-progress');
const kPendingClose = Symbol('pending-close');
const kTotalLength = Symbol('total-length');
const kCallback = Symbol('callback');
const kBuffers = Symbol('buffers');
const kError = Symbol('error');
const kOwner = Symbol('owner');
//
// We limit zlib concurrency, which prevents severe memory fragmentation
// as documented in https://github.com/nodejs/node/issues/8871#issuecomment-250915913
// and https://github.com/websockets/ws/issues/1202
//
// Intentionally global; it's the global thread pool that's an issue.
//
let zlibLimiter;
/**
* permessage-deflate implementation.
*/
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
* use of a custom server window size
* @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
*/
constructor (options, isServer, maxPayload) {
this._maxPayload = maxPayload | 0;
this._options = options || {};
this._threshold = this._options.threshold !== undefined
? this._options.threshold
: 1024;
this._isServer = !!isServer;
this._deflate = null;
this._inflate = null;
this.params = null;
if (!zlibLimiter) {
const concurrency = this._options.concurrencyLimit !== undefined
? this._options.concurrencyLimit
: 10;
zlibLimiter = new Limiter({ concurrency });
}
}
/**
* @type {String}
*/
static get extensionName () {
return 'permessage-deflate';
}
/**
* Create an extension negotiation offer.
*
* @return {Object} Extension parameters
* @public
*/
offer () {
const params = {};
if (this._options.serverNoContextTakeover) {
params.server_no_context_takeover = true;
}
if (this._options.clientNoContextTakeover) {
params.client_no_context_takeover = true;
}
if (this._options.serverMaxWindowBits) {
params.server_max_window_bits = this._options.serverMaxWindowBits;
}
if (this._options.clientMaxWindowBits) {
params.client_max_window_bits = this._options.clientMaxWindowBits;
} else if (this._options.clientMaxWindowBits == null) {
params.client_max_window_bits = true;
}
return params;
}
/**
* Accept an extension negotiation offer/response.
*
* @param {Array} configurations The extension negotiation offers/reponse
* @return {Object} Accepted configuration
* @public
*/
accept (configurations) {
configurations = this.normalizeParams(configurations);
this.params = this._isServer
? this.acceptAsServer(configurations)
: this.acceptAsClient(configurations);
return this.params;
}
/**
* Releases all resources used by the extension.
*
* @public
*/
cleanup () {
if (this._inflate) {
if (this._inflate[kWriteInProgress]) {
this._inflate[kPendingClose] = true;
} else {
this._inflate.close();
this._inflate = null;
}
}
if (this._deflate) {
if (this._deflate[kWriteInProgress]) {
this._deflate[kPendingClose] = true;
} else {
this._deflate.close();
this._deflate = null;
}
}
}
/**
* Accept an extension negotiation offer.
*
* @param {Array} offers The extension negotiation offers
* @return {Object} Accepted configuration
* @private
*/
acceptAsServer (offers) {
const opts = this._options;
const accepted = offers.find((params) => {
if (
(opts.serverNoContextTakeover === false &&
params.server_no_context_takeover) ||
(params.server_max_window_bits &&
(opts.serverMaxWindowBits === false ||
(typeof opts.serverMaxWindowBits === 'number' &&
opts.serverMaxWindowBits > params.server_max_window_bits))) ||
(typeof opts.clientMaxWindowBits === 'number' &&
!params.client_max_window_bits)
) {
return false;
}
return true;
});
if (!accepted) {
throw new Error('None of the extension offers can be accepted');
}
if (opts.serverNoContextTakeover) {
accepted.server_no_context_takeover = true;
}
if (opts.clientNoContextTakeover) {
accepted.client_no_context_takeover = true;
}
if (typeof opts.serverMaxWindowBits === 'number') {
accepted.server_max_window_bits = opts.serverMaxWindowBits;
}
if (typeof opts.clientMaxWindowBits === 'number') {
accepted.client_max_window_bits = opts.clientMaxWindowBits;
} else if (
accepted.client_max_window_bits === true ||
opts.clientMaxWindowBits === false
) {
delete accepted.client_max_window_bits;
}
return accepted;
}
/**
* Accept the extension negotiation response.
*
* @param {Array} response The extension negotiation response
* @return {Object} Accepted configuration
* @private
*/
acceptAsClient (response) {
const params = response[0];
if (
this._options.clientNoContextTakeover === false &&
params.client_no_context_takeover
) {
throw new Error('Unexpected parameter "client_no_context_takeover"');
}
if (!params.client_max_window_bits) {
if (typeof this._options.clientMaxWindowBits === 'number') {
params.client_max_window_bits = this._options.clientMaxWindowBits;
}
} else if (
this._options.clientMaxWindowBits === false ||
(typeof this._options.clientMaxWindowBits === 'number' &&
params.client_max_window_bits > this._options.clientMaxWindowBits)
) {
throw new Error(
'Unexpected or invalid parameter "client_max_window_bits"'
);
}
return params;
}
/**
* Normalize parameters.
*
* @param {Array} configurations The extension negotiation offers/reponse
* @return {Array} The offers/response with normalized parameters
* @private
*/
normalizeParams (configurations) {
configurations.forEach((params) => {
Object.keys(params).forEach((key) => {
var value = params[key];
if (value.length > 1) {
throw new Error(`Parameter "${key}" must have only a single value`);
}
value = value[0];
if (key === 'client_max_window_bits') {
if (value !== true) {
const num = +value;
if (!Number.isInteger(num) || num < 8 || num > 15) {
throw new TypeError(
`Invalid value for parameter "${key}": ${value}`
);
}
value = num;
} else if (!this._isServer) {
throw new TypeError(
`Invalid value for parameter "${key}": ${value}`
);
}
} else if (key === 'server_max_window_bits') {
const num = +value;
if (!Number.isInteger(num) || num < 8 || num > 15) {
throw new TypeError(
`Invalid value for parameter "${key}": ${value}`
);
}
value = num;
} else if (
key === 'client_no_context_takeover' ||
key === 'server_no_context_takeover'
) {
if (value !== true) {
throw new TypeError(
`Invalid value for parameter "${key}": ${value}`
);
}
} else {
throw new Error(`Unknown parameter "${key}"`);
}
params[key] = value;
});
});
return configurations;
}
/**
* Decompress data. Concurrency limited by async-limiter.
*
* @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) => {
this._decompress(data, fin, (err, result) => {
done();
callback(err, result);
});
});
}
/**
* Compress data. Concurrency limited by async-limiter.
*
* @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) => {
this._compress(data, fin, (err, result) => {
done();
callback(err, result);
});
});
}
/**
* Decompress data.
*
* @param {Buffer} data Compressed data
* @param {Boolean} fin Specifies whether or not this is the last fragment
* @param {Function} callback Callback
* @private
*/
_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];
this._inflate = zlib.createInflateRaw(
Object.assign(
{},
this._options.zlibInflateOptions,
{ windowBits }
)
);
this._inflate[kTotalLength] = 0;
this._inflate[kBuffers] = [];
this._inflate[kOwner] = this;
this._inflate.on('error', inflateOnError);
this._inflate.on('data', inflateOnData);
}
this._inflate[kCallback] = callback;
this._inflate[kWriteInProgress] = true;
this._inflate.write(data);
if (fin) this._inflate.write(TRAILER);
this._inflate.flush(() => {
const err = this._inflate[kError];
if (err) {
this._inflate.close();
this._inflate = null;
callback(err);
return;
}
const data = bufferUtil.concat(
this._inflate[kBuffers],
this._inflate[kTotalLength]
);
if (
(fin && this.params[`${endpoint}_no_context_takeover`]) ||
this._inflate[kPendingClose]
) {
this._inflate.close();
this._inflate = null;
} else {
this._inflate[kWriteInProgress] = false;
this._inflate[kTotalLength] = 0;
this._inflate[kBuffers] = [];
}
callback(null, data);
});
}
/**
* Compress data.
*
* @param {Buffer} data Data to compress
* @param {Boolean} fin Specifies whether or not this is the last fragment
* @param {Function} callback Callback
* @private
*/
_compress (data, fin, callback) {
if (!data || data.length === 0) {
process.nextTick(callback, null, EMPTY_BLOCK);
return;
}
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];
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[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.
//
this._deflate.on('data', deflateOnData);
}
this._deflate[kWriteInProgress] = true;
this._deflate.write(data);
this._deflate.flush(zlib.Z_SYNC_FLUSH, () => {
var 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] = [];
}
callback(null, data);
});
}
}
module.exports = PerMessageDeflate;
/**
* The listener of the `zlib.DeflateRaw` stream `'data'` event.
*
* @param {Buffer} chunk A chunk of data
* @private
*/
function deflateOnData (chunk) {
this[kBuffers].push(chunk);
this[kTotalLength] += chunk.length;
}
/**
* The listener of the `zlib.InflateRaw` stream `'data'` event.
*
* @param {Buffer} chunk A chunk of data
* @private
*/
function inflateOnData (chunk) {
this[kTotalLength] += chunk.length;
if (
this[kOwner]._maxPayload < 1 ||
this[kTotalLength] <= this[kOwner]._maxPayload
) {
this[kBuffers].push(chunk);
return;
}
this[kError] = new RangeError('Max payload size exceeded');
this[kError].closeCode = 1009;
this.removeListener('data', inflateOnData);
this.reset();
}
/**
* The listener of the `zlib.InflateRaw` stream `'error'` event.
*
* @param {Error} err The emitted error
* @private
*/
function inflateOnError (err) {
//
// There is no need to call `Zlib#close()` as the handle is automatically
// closed when an error is emitted.
//
this[kOwner]._inflate = null;
this[kCallback](err);
}

589
node_modules/ws/lib/receiver.js generated vendored Normal file
View File

@ -0,0 +1,589 @@
'use strict';
const safeBuffer = require('safe-buffer');
const PerMessageDeflate = require('./permessage-deflate');
const bufferUtil = require('./buffer-util');
const validation = require('./validation');
const constants = require('./constants');
const Buffer = safeBuffer.Buffer;
const GET_INFO = 0;
const GET_PAYLOAD_LENGTH_16 = 1;
const GET_PAYLOAD_LENGTH_64 = 2;
const GET_MASK = 3;
const GET_DATA = 4;
const INFLATING = 5;
/**
* HyBi Receiver implementation.
*/
class Receiver {
/**
* Creates a Receiver instance.
*
* @param {Object} extensions An object containing the negotiated extensions
* @param {Number} maxPayload The maximum allowed message length
* @param {String} binaryType The type for binary data
*/
constructor (extensions, maxPayload, binaryType) {
this._binaryType = binaryType || constants.BINARY_TYPES[0];
this._extensions = extensions || {};
this._maxPayload = maxPayload | 0;
this._bufferedBytes = 0;
this._buffers = [];
this._compressed = false;
this._payloadLength = 0;
this._fragmented = 0;
this._masked = false;
this._fin = false;
this._mask = null;
this._opcode = 0;
this._totalPayloadLength = 0;
this._messageLength = 0;
this._fragments = [];
this._cleanupCallback = null;
this._isCleaningUp = false;
this._hadError = false;
this._loop = false;
this.add = this.add.bind(this);
this.onmessage = null;
this.onclose = null;
this.onerror = null;
this.onping = null;
this.onpong = null;
this._state = GET_INFO;
}
/**
* Consumes `n` bytes from the buffered data, calls `cleanup` if necessary.
*
* @param {Number} n The number of bytes to consume
* @return {(Buffer|null)} The consumed bytes or `null` if `n` bytes are not
* available
* @private
*/
consume (n) {
if (this._bufferedBytes < n) {
this._loop = false;
if (this._isCleaningUp) this.cleanup(this._cleanupCallback);
return null;
}
this._bufferedBytes -= n;
if (n === this._buffers[0].length) return this._buffers.shift();
if (n < this._buffers[0].length) {
const buf = this._buffers[0];
this._buffers[0] = buf.slice(n);
return buf.slice(0, n);
}
const dst = Buffer.allocUnsafe(n);
do {
const buf = this._buffers[0];
if (n >= buf.length) {
this._buffers.shift().copy(dst, dst.length - n);
} else {
buf.copy(dst, dst.length - n, 0, n);
this._buffers[0] = buf.slice(n);
}
n -= buf.length;
} while (n > 0);
return dst;
}
/**
* Adds new data to the parser.
*
* @param {Buffer} chunk A chunk of data
* @public
*/
add (chunk) {
this._bufferedBytes += chunk.length;
this._buffers.push(chunk);
this.startLoop();
}
/**
* Starts the parsing loop.
*
* @private
*/
startLoop () {
this._loop = true;
do {
switch (this._state) {
case GET_INFO:
this.getInfo();
break;
case GET_PAYLOAD_LENGTH_16:
this.getPayloadLength16();
break;
case GET_PAYLOAD_LENGTH_64:
this.getPayloadLength64();
break;
case GET_MASK:
this.getMask();
break;
case GET_DATA:
this.getData();
break;
default: // `INFLATING`
this._loop = false;
}
} while (this._loop);
}
/**
* Reads the first two bytes of a frame.
*
* @private
*/
getInfo () {
const buf = this.consume(2);
if (buf === null) return;
if ((buf[0] & 0x30) !== 0x00) {
this.error(
new RangeError('Invalid WebSocket frame: RSV2 and RSV3 must be clear'),
1002
);
return;
}
const compressed = (buf[0] & 0x40) === 0x40;
if (compressed && !this._extensions[PerMessageDeflate.extensionName]) {
this.error(
new RangeError('Invalid WebSocket frame: RSV1 must be clear'),
1002
);
return;
}
this._fin = (buf[0] & 0x80) === 0x80;
this._opcode = buf[0] & 0x0f;
this._payloadLength = buf[1] & 0x7f;
if (this._opcode === 0x00) {
if (compressed) {
this.error(
new RangeError('Invalid WebSocket frame: RSV1 must be clear'),
1002
);
return;
}
if (!this._fragmented) {
this.error(
new RangeError('Invalid WebSocket frame: invalid opcode 0'),
1002
);
return;
} else {
this._opcode = this._fragmented;
}
} else if (this._opcode === 0x01 || this._opcode === 0x02) {
if (this._fragmented) {
this.error(
new RangeError(
`Invalid WebSocket frame: invalid opcode ${this._opcode}`
),
1002
);
return;
}
this._compressed = compressed;
} else if (this._opcode > 0x07 && this._opcode < 0x0b) {
if (!this._fin) {
this.error(
new RangeError('Invalid WebSocket frame: FIN must be set'),
1002
);
return;
}
if (compressed) {
this.error(
new RangeError('Invalid WebSocket frame: RSV1 must be clear'),
1002
);
return;
}
if (this._payloadLength > 0x7d) {
this.error(
new RangeError(
`Invalid WebSocket frame: invalid payload length ` +
`${this._payloadLength}`
),
1002
);
return;
}
} else {
this.error(
new RangeError(
`Invalid WebSocket frame: invalid opcode ${this._opcode}`
),
1002
);
return;
}
if (!this._fin && !this._fragmented) this._fragmented = this._opcode;
this._masked = (buf[1] & 0x80) === 0x80;
if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16;
else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64;
else this.haveLength();
}
/**
* Gets extended payload length (7+16).
*
* @private
*/
getPayloadLength16 () {
const buf = this.consume(2);
if (buf === null) return;
this._payloadLength = buf.readUInt16BE(0, true);
this.haveLength();
}
/**
* Gets extended payload length (7+64).
*
* @private
*/
getPayloadLength64 () {
const buf = this.consume(8);
if (buf === null) return;
const num = buf.readUInt32BE(0, true);
//
// The maximum safe integer in JavaScript is 2^53 - 1. An error is returned
// if payload length is greater than this number.
//
if (num > Math.pow(2, 53 - 32) - 1) {
this.error(
new RangeError(
'Unsupported WebSocket frame: payload length > 2^53 - 1'
),
1009
);
return;
}
this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4, true);
this.haveLength();
}
/**
* Payload length has been read.
*
* @private
*/
haveLength () {
if (this._opcode < 0x08 && this.maxPayloadExceeded(this._payloadLength)) {
return;
}
if (this._masked) this._state = GET_MASK;
else this._state = GET_DATA;
}
/**
* Reads mask bytes.
*
* @private
*/
getMask () {
this._mask = this.consume(4);
if (this._mask === null) return;
this._state = GET_DATA;
}
/**
* Reads data bytes.
*
* @private
*/
getData () {
var data = constants.EMPTY_BUFFER;
if (this._payloadLength) {
data = this.consume(this._payloadLength);
if (data === null) return;
if (this._masked) bufferUtil.unmask(data, this._mask);
}
if (this._opcode > 0x07) {
this.controlMessage(data);
} else if (this._compressed) {
this._state = INFLATING;
this.decompress(data);
} else if (this.pushFragment(data)) {
this.dataMessage();
}
}
/**
* Decompresses data.
*
* @param {Buffer} data Compressed data
* @private
*/
decompress (data) {
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
perMessageDeflate.decompress(data, this._fin, (err, buf) => {
if (err) {
this.error(err, err.closeCode === 1009 ? 1009 : 1007);
return;
}
if (this.pushFragment(buf)) this.dataMessage();
this.startLoop();
});
}
/**
* Handles a data message.
*
* @private
*/
dataMessage () {
if (this._fin) {
const messageLength = this._messageLength;
const fragments = this._fragments;
this._totalPayloadLength = 0;
this._messageLength = 0;
this._fragmented = 0;
this._fragments = [];
if (this._opcode === 2) {
var data;
if (this._binaryType === 'nodebuffer') {
data = toBuffer(fragments, messageLength);
} else if (this._binaryType === 'arraybuffer') {
data = toArrayBuffer(toBuffer(fragments, messageLength));
} else {
data = fragments;
}
this.onmessage(data);
} else {
const buf = toBuffer(fragments, messageLength);
if (!validation.isValidUTF8(buf)) {
this.error(
new Error('Invalid WebSocket frame: invalid UTF-8 sequence'),
1007
);
return;
}
this.onmessage(buf.toString());
}
}
this._state = GET_INFO;
}
/**
* Handles a control message.
*
* @param {Buffer} data Data to handle
* @private
*/
controlMessage (data) {
if (this._opcode === 0x08) {
if (data.length === 0) {
this._loop = false;
this.onclose(1005, '');
this.cleanup(this._cleanupCallback);
} else if (data.length === 1) {
this.error(
new RangeError('Invalid WebSocket frame: invalid payload length 1'),
1002
);
} else {
const code = data.readUInt16BE(0, true);
if (!validation.isValidStatusCode(code)) {
this.error(
new RangeError(
`Invalid WebSocket frame: invalid status code ${code}`
),
1002
);
return;
}
const buf = data.slice(2);
if (!validation.isValidUTF8(buf)) {
this.error(
new Error('Invalid WebSocket frame: invalid UTF-8 sequence'),
1007
);
return;
}
this._loop = false;
this.onclose(code, buf.toString());
this.cleanup(this._cleanupCallback);
}
return;
}
if (this._opcode === 0x09) this.onping(data);
else this.onpong(data);
this._state = GET_INFO;
}
/**
* Handles an error.
*
* @param {Error} err The error
* @param {Number} code Close code
* @private
*/
error (err, code) {
this._hadError = true;
this._loop = false;
this.onerror(err, code);
this.cleanup(this._cleanupCallback);
}
/**
* Checks payload size, disconnects socket when it exceeds `maxPayload`.
*
* @param {Number} length Payload length
* @private
*/
maxPayloadExceeded (length) {
if (length === 0 || this._maxPayload < 1) return false;
const fullLength = this._totalPayloadLength + length;
if (fullLength <= this._maxPayload) {
this._totalPayloadLength = fullLength;
return false;
}
this.error(new RangeError('Max payload size exceeded'), 1009);
return true;
}
/**
* Appends a fragment in the fragments array after checking that the sum of
* fragment lengths does not exceed `maxPayload`.
*
* @param {Buffer} fragment The fragment to add
* @return {Boolean} `true` if `maxPayload` is not exceeded, else `false`
* @private
*/
pushFragment (fragment) {
if (fragment.length === 0) return true;
const totalLength = this._messageLength + fragment.length;
if (this._maxPayload < 1 || totalLength <= this._maxPayload) {
this._messageLength = totalLength;
this._fragments.push(fragment);
return true;
}
this.error(new RangeError('Max payload size exceeded'), 1009);
return false;
}
/**
* Releases resources used by the receiver.
*
* @param {Function} cb Callback
* @public
*/
cleanup (cb) {
if (this._extensions === null) {
if (cb) cb();
return;
}
if (!this._hadError && (this._loop || this._state === INFLATING)) {
this._cleanupCallback = cb;
this._isCleaningUp = true;
return;
}
this._extensions = null;
this._fragments = null;
this._buffers = null;
this._mask = null;
this._cleanupCallback = null;
this.onmessage = null;
this.onclose = null;
this.onerror = null;
this.onping = null;
this.onpong = null;
if (cb) cb();
}
}
module.exports = Receiver;
/**
* 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);
}

404
node_modules/ws/lib/sender.js generated vendored Normal file
View File

@ -0,0 +1,404 @@
'use strict';
const safeBuffer = require('safe-buffer');
const crypto = require('crypto');
const PerMessageDeflate = require('./permessage-deflate');
const bufferUtil = require('./buffer-util');
const validation = require('./validation');
const constants = require('./constants');
const Buffer = safeBuffer.Buffer;
/**
* HyBi Sender implementation.
*/
class Sender {
/**
* Creates a Sender instance.
*
* @param {net.Socket} socket The connection socket
* @param {Object} extensions An object containing the negotiated extensions
*/
constructor (socket, extensions) {
this._extensions = extensions || {};
this._socket = socket;
this._firstFragment = true;
this._compress = false;
this._bufferedBytes = 0;
this._deflating = false;
this._queue = [];
}
/**
* Frames a piece of data according to the HyBi WebSocket protocol.
*
* @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
* @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;
if (data.length >= 65536) {
offset += 8;
payloadLength = 127;
} else if (data.length > 125) {
offset += 2;
payloadLength = 126;
}
const target = Buffer.allocUnsafe(merge ? data.length + offset : offset);
target[0] = options.fin ? options.opcode | 0x80 : options.opcode;
if (options.rsv1) target[0] |= 0x40;
if (payloadLength === 126) {
target.writeUInt16BE(data.length, 2, true);
} else if (payloadLength === 127) {
target.writeUInt32BE(0, 2, true);
target.writeUInt32BE(data.length, 6, true);
}
if (!options.mask) {
target[1] = payloadLength;
if (merge) {
data.copy(target, offset);
return [target];
}
return [target, data];
}
const mask = crypto.randomBytes(4);
target[1] = payloadLength | 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);
return [target];
}
bufferUtil.mask(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
* @public
*/
close (code, data, mask, cb) {
var buf;
if (code === undefined) {
buf = constants.EMPTY_BUFFER;
} else if (typeof code !== 'number' || !validation.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, true);
} else {
buf = Buffer.allocUnsafe(2 + Buffer.byteLength(data));
buf.writeUInt16BE(code, 0, true);
buf.write(data, 2);
}
if (this._deflating) {
this.enqueue([this.doClose, buf, mask, cb]);
} else {
this.doClose(buf, mask, cb);
}
}
/**
* 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
* @private
*/
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
* @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;
}
}
if (this._deflating) {
this.enqueue([this.doPing, data, mask, readOnly, cb]);
} else {
this.doPing(data, mask, 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
* @private
*/
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
* @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;
}
}
if (this._deflating) {
this.enqueue([this.doPong, data, mask, readOnly, cb]);
} else {
this.doPong(data, mask, 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
* @private
*/
doPong (data, mask, readOnly, cb) {
this.sendFrame(Sender.frame(data, {
fin: true,
rsv1: false,
opcode: 0x0a,
mask,
readOnly
}), cb);
}
/**
* Sends a data message to the other peer.
*
* @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
* @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;
}
}
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
if (this._firstFragment) {
this._firstFragment = false;
if (rsv1 && perMessageDeflate) {
rsv1 = data.length >= perMessageDeflate._threshold;
}
this._compress = rsv1;
} else {
rsv1 = false;
opcode = 0;
}
if (options.fin) this._firstFragment = true;
if (perMessageDeflate) {
const opts = {
fin: options.fin,
rsv1,
opcode,
mask: options.mask,
readOnly
};
if (this._deflating) {
this.enqueue([this.dispatch, data, this._compress, opts, cb]);
} else {
this.dispatch(data, this._compress, opts, cb);
}
} else {
this.sendFrame(Sender.frame(data, {
fin: options.fin,
rsv1: false,
opcode,
mask: options.mask,
readOnly
}), cb);
}
}
/**
* Dispatches a data message.
*
* @param {Buffer} data The message to send
* @param {Boolean} compress 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
* @private
*/
dispatch (data, compress, options, cb) {
if (!compress) {
this.sendFrame(Sender.frame(data, options), cb);
return;
}
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
this._deflating = true;
perMessageDeflate.compress(data, options.fin, (_, buf) => {
options.readOnly = false;
this.sendFrame(Sender.frame(buf, options), cb);
this._deflating = false;
this.dequeue();
});
}
/**
* Executes queued send operations.
*
* @private
*/
dequeue () {
while (!this._deflating && this._queue.length) {
const params = this._queue.shift();
this._bufferedBytes -= params[1].length;
params[0].apply(this, params.slice(1));
}
}
/**
* Enqueues a send operation.
*
* @param {Array} params Send operation parameters.
* @private
*/
enqueue (params) {
this._bufferedBytes += params[1].length;
this._queue.push(params);
}
/**
* Sends a frame.
*
* @param {Buffer[]} list The frame to send
* @param {Function} cb Callback
* @private
*/
sendFrame (list, cb) {
if (list.length === 2) {
this._socket.write(list[0]);
this._socket.write(list[1], cb);
} else {
this._socket.write(list[0], cb);
}
}
}
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;
}

29
node_modules/ws/lib/validation.js generated vendored Normal file
View File

@ -0,0 +1,29 @@
'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.
*
* @param {Number} code The status code
* @return {Boolean} `true` if the status code is valid, else `false`
* @public
*/
exports.isValidStatusCode = (code) => {
return (
(code >= 1000 &&
code <= 1013 &&
code !== 1004 &&
code !== 1005 &&
code !== 1006) ||
(code >= 3000 && code <= 4999)
);
};

351
node_modules/ws/lib/websocket-server.js generated vendored Normal file
View File

@ -0,0 +1,351 @@
'use strict';
const safeBuffer = require('safe-buffer');
const EventEmitter = require('events');
const crypto = require('crypto');
const http = require('http');
const url = require('url');
const PerMessageDeflate = require('./permessage-deflate');
const extension = require('./extension');
const constants = require('./constants');
const WebSocket = require('./websocket');
const Buffer = safeBuffer.Buffer;
/**
* Class representing a WebSocket server.
*
* @extends EventEmitter
*/
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
*/
constructor (options, callback) {
super();
options = Object.assign({
maxPayload: 100 * 1024 * 1024,
perMessageDeflate: false,
handleProtocols: null,
clientTracking: true,
verifyClient: null,
noServer: false,
backlog: null, // use default (511 as implemented in net.js)
server: null,
host: null,
path: null,
port: null
}, options);
if (options.port == null && !options.server && !options.noServer) {
throw new TypeError(
'One of the "port", "server", or "noServer" options must be specified'
);
}
if (options.port != null) {
this._server = http.createServer((req, res) => {
const body = http.STATUS_CODES[426];
res.writeHead(426, {
'Content-Length': body.length,
'Content-Type': 'text/plain'
});
res.end(body);
});
this._server.listen(options.port, options.host, options.backlog, callback);
} else if (options.server) {
this._server = options.server;
}
if (this._server) {
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);
});
}
});
}
if (options.perMessageDeflate === true) options.perMessageDeflate = {};
if (options.clientTracking) this.clients = new Set();
this.options = options;
}
/**
* Returns the bound address, the address family name, and port of the server
* as reported by the operating system if listening on an IP socket.
* If the server is listening on a pipe or UNIX domain socket, the name is
* returned as a string.
*
* @return {(Object|String|null)} The address of the server
* @public
*/
address () {
if (this.options.noServer) {
throw new Error('The server is operating in "noServer" mode');
}
if (!this._server) return null;
return this._server.address();
}
/**
* Close the server.
*
* @param {Function} cb Callback
* @public
*/
close (cb) {
//
// Terminate all associated clients.
//
if (this.clients) {
for (const client of this.clients) client.terminate();
}
const server = this._server;
if (server) {
this._removeListeners();
this._removeListeners = this._server = null;
//
// Close the http server if it was internally created.
//
if (this.options.port != null) return server.close(cb);
}
if (cb) cb();
}
/**
* See if a given request should be handled by this server instance.
*
* @param {http.IncomingMessage} req Request object to inspect
* @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;
}
return true;
}
/**
* 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 {Buffer} head The first packet of the upgraded stream
* @param {Function} cb Callback
* @public
*/
handleUpgrade (req, socket, head, cb) {
socket.on('error', socketOnError);
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) ||
!this.shouldHandle(req)
) {
return abortConnection(socket, 400);
}
if (this.options.perMessageDeflate) {
const perMessageDeflate = new PerMessageDeflate(
this.options.perMessageDeflate,
true,
this.options.maxPayload
);
try {
const offers = extension.parse(
req.headers['sec-websocket-extensions']
);
if (offers[PerMessageDeflate.extensionName]) {
perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]);
extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
}
} catch (err) {
return abortConnection(socket, 400);
}
}
var protocol = (req.headers['sec-websocket-protocol'] || '').split(/, */);
//
// Optionally call external protocol selection handler.
//
if (this.options.handleProtocols) {
protocol = this.options.handleProtocols(protocol, req);
if (protocol === false) return abortConnection(socket, 401);
} else {
protocol = protocol[0];
}
//
// Optionally call external client verification handler.
//
if (this.options.verifyClient) {
const info = {
origin: req.headers[`${version === 8 ? 'sec-websocket-origin' : 'origin'}`],
secure: !!(req.connection.authorized || req.connection.encrypted),
req
};
if (this.options.verifyClient.length === 2) {
this.options.verifyClient(info, (verified, code, message) => {
if (!verified) return abortConnection(socket, code || 401, message);
this.completeUpgrade(protocol, extensions, req, socket, head, cb);
});
return;
}
if (!this.options.verifyClient(info)) return abortConnection(socket, 401);
}
this.completeUpgrade(protocol, extensions, req, socket, head, cb);
}
/**
* Upgrade the connection to WebSocket.
*
* @param {String} protocol The chosen subprotocol
* @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 {Buffer} head The first packet of the upgraded stream
* @param {Function} cb Callback
* @private
*/
completeUpgrade (protocol, 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')
.digest('base64');
const headers = [
'HTTP/1.1 101 Switching Protocols',
'Upgrade: websocket',
'Connection: Upgrade',
`Sec-WebSocket-Accept: ${key}`
];
const ws = new WebSocket(null);
if (protocol) {
headers.push(`Sec-WebSocket-Protocol: ${protocol}`);
ws.protocol = protocol;
}
if (extensions[PerMessageDeflate.extensionName]) {
const params = extensions[PerMessageDeflate.extensionName].params;
const value = extension.format({
[PerMessageDeflate.extensionName]: [params]
});
headers.push(`Sec-WebSocket-Extensions: ${value}`);
ws._extensions = extensions;
}
//
// Allow external modification/inspection of handshake headers.
//
this.emit('headers', headers, req);
socket.write(headers.concat('\r\n').join('\r\n'));
socket.removeListener('error', socketOnError);
ws.setSocket(socket, head, this.options.maxPayload);
if (this.clients) {
this.clients.add(ws);
ws.on('close', () => this.clients.delete(ws));
}
cb(ws);
}
}
module.exports = WebSocketServer;
/**
* Add event listeners on an `EventEmitter` using a map of <event, listener>
* pairs.
*
* @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
* @private
*/
function addListeners (server, map) {
for (const event of Object.keys(map)) server.on(event, map[event]);
return function removeListeners () {
for (const event of Object.keys(map)) {
server.removeListener(event, map[event]);
}
};
}
/**
* Handle premature socket errors.
*
* @private
*/
function socketOnError () {
this.destroy();
}
/**
* Close the connection when preconditions are not fulfilled.
*
* @param {net.Socket} socket The socket of the upgrade request
* @param {Number} code The HTTP response status code
* @param {String} [message] The HTTP response body
* @private
*/
function abortConnection (socket, code, message) {
if (socket.writable) {
message = message || http.STATUS_CODES[code];
socket.write(
`HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\r\n` +
'Connection: close\r\n' +
'Content-type: text/html\r\n' +
`Content-Length: ${Buffer.byteLength(message)}\r\n` +
'\r\n' +
message
);
}
socket.removeListener('error', socketOnError);
socket.destroy();
}

705
node_modules/ws/lib/websocket.js generated vendored Normal file
View File

@ -0,0 +1,705 @@
'use strict';
const EventEmitter = require('events');
const crypto = require('crypto');
const https = require('https');
const http = require('http');
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 readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
const protocolVersions = [8, 13];
const closeTimeout = 30 * 1000; // Allow 30 seconds to terminate the connection cleanly.
/**
* Class representing a WebSocket.
*
* @extends EventEmitter
*/
class WebSocket extends EventEmitter {
/**
* Create a new `WebSocket`.
*
* @param {String} address The URL to which to connect
* @param {(String|String[])} protocols The subprotocols
* @param {Object} options Connection options
*/
constructor (address, protocols, options) {
super();
this.readyState = WebSocket.CONNECTING;
this.protocol = '';
this._binaryType = constants.BINARY_TYPES[0];
this._finalize = this.finalize.bind(this);
this._closeFrameReceived = false;
this._closeFrameSent = false;
this._closeMessage = '';
this._closeTimer = null;
this._finalized = false;
this._closeCode = 1006;
this._extensions = {};
this._isServer = true;
this._receiver = null;
this._sender = null;
this._socket = null;
this._error = null;
if (address !== null) {
if (!protocols) {
protocols = [];
} else if (typeof protocols === 'string') {
protocols = [protocols];
} else if (!Array.isArray(protocols)) {
options = protocols;
protocols = [];
}
initAsClient.call(this, address, protocols, options);
}
}
get CONNECTING () { return WebSocket.CONNECTING; }
get CLOSING () { return WebSocket.CLOSING; }
get CLOSED () { return WebSocket.CLOSED; }
get OPEN () { return WebSocket.OPEN; }
/**
* This deviates from the WHATWG interface since ws doesn't support the required
* default "blob" type (instead we define a custom "nodebuffer" type).
*
* @type {String}
*/
get binaryType () {
return this._binaryType;
}
set binaryType (type) {
if (constants.BINARY_TYPES.indexOf(type) < 0) return;
this._binaryType = type;
//
// Allow to change `binaryType` on the fly.
//
if (this._receiver) this._receiver._binaryType = type;
}
/**
* @type {Number}
*/
get bufferedAmount () {
if (!this._socket) return 0;
//
// `socket.bufferSize` is `undefined` if the socket is closed.
//
return (this._socket.bufferSize || 0) + this._sender._bufferedBytes;
}
/**
* @type {String}
*/
get extensions () {
return Object.keys(this._extensions).join();
}
/**
* Set up the socket and the internal resources.
*
* @param {net.Socket} socket The network socket between the server and client
* @param {Buffer} head The first packet of the upgraded stream
* @param {Number} maxPayload The maximum allowed message size
* @private
*/
setSocket (socket, head, maxPayload) {
socket.setTimeout(0);
socket.setNoDelay();
socket.on('close', this._finalize);
socket.on('error', this._finalize);
socket.on('end', this._finalize);
this._receiver = new Receiver(this._extensions, maxPayload, this.binaryType);
this._sender = new Sender(socket, this._extensions);
this._socket = socket;
if (head.length > 0) socket.unshift(head);
socket.on('data', this._receiver.add);
this._receiver.onmessage = (data) => this.emit('message', data);
this._receiver.onping = (data) => {
this.pong(data, !this._isServer, constants.NOOP);
this.emit('ping', data);
};
this._receiver.onpong = (data) => this.emit('pong', data);
this._receiver.onclose = (code, reason) => {
//
// Discard any additional data that is received on the socket.
//
this._socket.removeListener('data', this._receiver.add);
this._closeFrameReceived = true;
this._closeMessage = reason;
this._closeCode = code;
if (code === 1005) this.close();
else this.close(code, reason);
};
this._receiver.onerror = (error, code) => {
if (this._error) return;
this._closeCode = code;
if (!this._finalized) this.finalize(error);
else this.emit('error', error);
};
this.readyState = WebSocket.OPEN;
this.emit('open');
}
/**
* Clean up internal resources and emit the `'close'` event.
*
* @param {(Boolean|Error)} error Indicates whether or not an error occurred
* @private
*/
finalize (error) {
if (this._finalized) return;
this.readyState = WebSocket.CLOSING;
this._finalized = true;
if (!this._socket) {
//
// `error` is always an `Error` instance in this case.
//
this.emit('error', error);
this.readyState = WebSocket.CLOSED;
this.emit('close', this._closeCode, this._closeMessage);
return;
}
clearTimeout(this._closeTimer);
this._socket.removeListener('data', this._receiver.add);
this._socket.removeListener('close', this._finalize);
this._socket.removeListener('error', this._finalize);
this._socket.removeListener('end', this._finalize);
this._socket.on('error', constants.NOOP);
if (error) {
if (error !== true) this._error = error;
this._socket.destroy();
} else {
this._socket.end();
}
this._receiver.cleanup(() => {
const err = this._error;
if (err) {
this._error = null;
this.emit('error', err);
}
this.readyState = WebSocket.CLOSED;
if (this._extensions[PerMessageDeflate.extensionName]) {
this._extensions[PerMessageDeflate.extensionName].cleanup();
}
this.emit('close', this._closeCode, this._closeMessage);
});
}
/**
* Start a closing handshake.
*
* +----------+ +-----------+ +----------+
* + - - -|ws.close()|---->|close frame|-->|ws.close()|- - - -
* +----------+ +-----------+ +----------+ |
* | +----------+ +-----------+ |
* |ws.close()|<----|close frame|<--------+ |
* +----------+ +-----------+ |
* CLOSING | +---+ | CLOSING
* | +---|fin|<------------+
* | | | +---+ |
* | | +---+ +-------------+
* | +----------+-->|fin|----->|ws.finalize()| - - +
* | +---+ +-------------+
* | +-------------+ |
* - - -|ws.finalize()|<--+
* +-------------+
*
* @param {Number} code Status code explaining why the connection is closing
* @param {String} data A string explaining why the connection is closing
* @public
*/
close (code, data) {
if (this.readyState === WebSocket.CLOSED) return;
if (this.readyState === WebSocket.CONNECTING) {
this._req.abort();
this.finalize(
new Error('WebSocket was closed before the connection was established')
);
return;
}
if (this.readyState === WebSocket.CLOSING) {
if (this._closeFrameSent && this._closeFrameReceived) this._socket.end();
return;
}
this.readyState = WebSocket.CLOSING;
this._sender.close(code, data, !this._isServer, (err) => {
//
// This error is handled by the `'error'` listener on the socket. We only
// want to know if the close frame has been sent here.
//
if (err) return;
this._closeFrameSent = true;
if (!this._finalized) {
if (this._closeFrameReceived) this._socket.end();
//
// Ensure that the connection is cleaned up even when the closing
// handshake fails.
//
this._closeTimer = setTimeout(this._finalize, closeTimeout, true);
}
});
}
/**
* Send a ping.
*
* @param {*} data The data to send
* @param {Boolean} mask Indicates whether or not to mask `data`
* @param {Function} cb Callback which is executed when the ping is sent
* @public
*/
ping (data, mask, cb) {
if (typeof data === 'function') {
cb = data;
data = mask = undefined;
} else if (typeof mask === 'function') {
cb = mask;
mask = undefined;
}
if (this.readyState !== WebSocket.OPEN) {
const err = new Error(
`WebSocket is not open: readyState ${this.readyState} ` +
`(${readyStates[this.readyState]})`
);
if (cb) return cb(err);
throw err;
}
if (typeof data === 'number') data = data.toString();
if (mask === undefined) mask = !this._isServer;
this._sender.ping(data || constants.EMPTY_BUFFER, mask, cb);
}
/**
* Send a pong.
*
* @param {*} data The data to send
* @param {Boolean} mask Indicates whether or not to mask `data`
* @param {Function} cb Callback which is executed when the pong is sent
* @public
*/
pong (data, mask, cb) {
if (typeof data === 'function') {
cb = data;
data = mask = undefined;
} else if (typeof mask === 'function') {
cb = mask;
mask = undefined;
}
if (this.readyState !== WebSocket.OPEN) {
const err = new Error(
`WebSocket is not open: readyState ${this.readyState} ` +
`(${readyStates[this.readyState]})`
);
if (cb) return cb(err);
throw err;
}
if (typeof data === 'number') data = data.toString();
if (mask === undefined) mask = !this._isServer;
this._sender.pong(data || constants.EMPTY_BUFFER, mask, cb);
}
/**
* Send a data message.
*
* @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 which is executed when data is written out
* @public
*/
send (data, options, cb) {
if (typeof options === 'function') {
cb = options;
options = {};
}
if (this.readyState !== WebSocket.OPEN) {
const err = new Error(
`WebSocket is not open: readyState ${this.readyState} ` +
`(${readyStates[this.readyState]})`
);
if (cb) return cb(err);
throw err;
}
if (typeof data === 'number') data = data.toString();
const opts = Object.assign({
binary: typeof data !== 'string',
mask: !this._isServer,
compress: true,
fin: true
}, options);
if (!this._extensions[PerMessageDeflate.extensionName]) {
opts.compress = false;
}
this._sender.send(data || constants.EMPTY_BUFFER, opts, cb);
}
/**
* Forcibly close the connection.
*
* @public
*/
terminate () {
if (this.readyState === WebSocket.CLOSED) return;
if (this.readyState === WebSocket.CONNECTING) {
this._req.abort();
this.finalize(
new Error('WebSocket was closed before the connection was established')
);
return;
}
this.finalize(true);
}
}
readyStates.forEach((readyState, i) => {
WebSocket[readyStates[i]] = i;
});
//
// Add the `onopen`, `onerror`, `onclose`, and `onmessage` attributes.
// See https://html.spec.whatwg.org/multipage/comms.html#the-websocket-interface
//
['open', 'error', 'close', 'message'].forEach((method) => {
Object.defineProperty(WebSocket.prototype, `on${method}`, {
/**
* Return the listener of the event.
*
* @return {(Function|undefined)} The event listener or `undefined`
* @public
*/
get () {
const listeners = this.listeners(method);
for (var i = 0; i < listeners.length; i++) {
if (listeners[i]._listener) return listeners[i]._listener;
}
},
/**
* Add a listener for the event.
*
* @param {Function} listener The listener to add
* @public
*/
set (listener) {
const listeners = this.listeners(method);
for (var i = 0; i < listeners.length; i++) {
//
// Remove only the listeners added via `addEventListener`.
//
if (listeners[i]._listener) this.removeListener(method, listeners[i]);
}
this.addEventListener(method, listener);
}
});
});
WebSocket.prototype.addEventListener = EventTarget.addEventListener;
WebSocket.prototype.removeEventListener = EventTarget.removeEventListener;
module.exports = WebSocket;
/**
* Initialize a WebSocket client.
*
* @param {String} address The URL to which to connect
* @param {String[]} protocols The list of subprotocols
* @param {Object} options Connection options
* @param {String} options.protocol Value of the `Sec-WebSocket-Protocol` header
* @param {(Boolean|Object)} options.perMessageDeflate Enable/disable permessage-deflate
* @param {Number} options.handshakeTimeout Timeout in milliseconds for the handshake request
* @param {String} options.localAddress Local interface to bind for network connections
* @param {Number} options.protocolVersion Value of the `Sec-WebSocket-Version` header
* @param {Object} options.headers An object containing request headers
* @param {String} options.origin Value of the `Origin` or `Sec-WebSocket-Origin` header
* @param {http.Agent} options.agent Use the specified Agent
* @param {String} options.host Value of the `Host` header
* @param {Number} options.family IP address family to use during hostname lookup (4 or 6).
* @param {Function} options.checkServerIdentity A function to validate the server hostname
* @param {Boolean} options.rejectUnauthorized Verify or not the server certificate
* @param {String} options.passphrase The passphrase for the private key or pfx
* @param {String} options.ciphers The ciphers to use or exclude
* @param {String} options.ecdhCurve The curves for ECDH key agreement to use or exclude
* @param {(String|String[]|Buffer|Buffer[])} options.cert The certificate key
* @param {(String|String[]|Buffer|Buffer[])} options.key The private key
* @param {(String|Buffer)} options.pfx The private key, certificate, and CA certs
* @param {(String|String[]|Buffer|Buffer[])} options.ca Trusted certificates
* @private
*/
function initAsClient (address, protocols, options) {
options = Object.assign({
protocolVersion: protocolVersions[1],
protocol: protocols.join(','),
perMessageDeflate: true,
handshakeTimeout: null,
localAddress: null,
headers: null,
family: null,
origin: null,
agent: null,
host: null,
//
// SSL options.
//
checkServerIdentity: null,
rejectUnauthorized: null,
passphrase: null,
ciphers: null,
ecdhCurve: null,
cert: null,
key: null,
pfx: null,
ca: null
}, options);
if (protocolVersions.indexOf(options.protocolVersion) === -1) {
throw new RangeError(
`Unsupported protocol version: ${options.protocolVersion} ` +
`(supported versions: ${protocolVersions.join(', ')})`
);
}
this._isServer = false;
this.url = address;
const serverUrl = url.parse(address);
const isUnixSocket = serverUrl.protocol === 'ws+unix:';
if (!serverUrl.host && (!isUnixSocket || !serverUrl.path)) {
throw new Error(`Invalid URL: ${address}`);
}
const isSecure = serverUrl.protocol === 'wss:' || serverUrl.protocol === 'https:';
const key = crypto.randomBytes(16).toString('base64');
const httpObj = isSecure ? https : http;
var perMessageDeflate;
const requestOptions = {
port: serverUrl.port || (isSecure ? 443 : 80),
host: serverUrl.hostname,
path: '/',
headers: {
'Sec-WebSocket-Version': options.protocolVersion,
'Sec-WebSocket-Key': key,
'Connection': 'Upgrade',
'Upgrade': 'websocket'
}
};
if (options.headers) Object.assign(requestOptions.headers, options.headers);
if (options.perMessageDeflate) {
perMessageDeflate = new PerMessageDeflate(
options.perMessageDeflate !== true ? options.perMessageDeflate : {},
false
);
requestOptions.headers['Sec-WebSocket-Extensions'] = extension.format({
[PerMessageDeflate.extensionName]: perMessageDeflate.offer()
});
}
if (options.protocol) {
requestOptions.headers['Sec-WebSocket-Protocol'] = options.protocol;
}
if (options.origin) {
if (options.protocolVersion < 13) {
requestOptions.headers['Sec-WebSocket-Origin'] = options.origin;
} else {
requestOptions.headers.Origin = options.origin;
}
}
if (options.host) requestOptions.headers.Host = options.host;
if (serverUrl.auth) requestOptions.auth = serverUrl.auth;
if (options.localAddress) requestOptions.localAddress = options.localAddress;
if (options.family) requestOptions.family = options.family;
if (isUnixSocket) {
const parts = serverUrl.path.split(':');
requestOptions.socketPath = parts[0];
requestOptions.path = parts[1];
} else if (serverUrl.path) {
//
// Make sure that path starts with `/`.
//
if (serverUrl.path.charAt(0) !== '/') {
requestOptions.path = `/${serverUrl.path}`;
} else {
requestOptions.path = serverUrl.path;
}
}
var agent = options.agent;
//
// A custom agent is required for these options.
//
if (
options.rejectUnauthorized != null ||
options.checkServerIdentity ||
options.passphrase ||
options.ciphers ||
options.ecdhCurve ||
options.cert ||
options.key ||
options.pfx ||
options.ca
) {
if (options.passphrase) requestOptions.passphrase = options.passphrase;
if (options.ciphers) requestOptions.ciphers = options.ciphers;
if (options.ecdhCurve) requestOptions.ecdhCurve = options.ecdhCurve;
if (options.cert) requestOptions.cert = options.cert;
if (options.key) requestOptions.key = options.key;
if (options.pfx) requestOptions.pfx = options.pfx;
if (options.ca) requestOptions.ca = options.ca;
if (options.checkServerIdentity) {
requestOptions.checkServerIdentity = options.checkServerIdentity;
}
if (options.rejectUnauthorized != null) {
requestOptions.rejectUnauthorized = options.rejectUnauthorized;
}
if (!agent) agent = new httpObj.Agent(requestOptions);
}
if (agent) requestOptions.agent = agent;
this._req = httpObj.get(requestOptions);
if (options.handshakeTimeout) {
this._req.setTimeout(options.handshakeTimeout, () => {
this._req.abort();
this.finalize(new Error('Opening handshake has timed out'));
});
}
this._req.on('error', (error) => {
if (this._req.aborted) return;
this._req = null;
this.finalize(error);
});
this._req.on('response', (res) => {
if (!this.emit('unexpected-response', this._req, res)) {
this._req.abort();
this.finalize(new Error(`Unexpected server response: ${res.statusCode}`));
}
});
this._req.on('upgrade', (res, socket, head) => {
this.emit('upgrade', res);
//
// The user may have closed the connection from a listener of the `upgrade`
// event.
//
if (this.readyState !== WebSocket.CONNECTING) return;
this._req = null;
const digest = crypto.createHash('sha1')
.update(key + constants.GUID, 'binary')
.digest('base64');
if (res.headers['sec-websocket-accept'] !== digest) {
socket.destroy();
return this.finalize(new Error('Invalid Sec-WebSocket-Accept header'));
}
const serverProt = res.headers['sec-websocket-protocol'];
const protList = (options.protocol || '').split(/, */);
var protError;
if (!options.protocol && serverProt) {
protError = 'Server sent a subprotocol but none was requested';
} else if (options.protocol && !serverProt) {
protError = 'Server sent no subprotocol';
} else if (serverProt && protList.indexOf(serverProt) === -1) {
protError = 'Server sent an invalid subprotocol';
}
if (protError) {
socket.destroy();
return this.finalize(new Error(protError));
}
if (serverProt) this.protocol = serverProt;
if (perMessageDeflate) {
try {
const extensions = extension.parse(
res.headers['sec-websocket-extensions']
);
if (extensions[PerMessageDeflate.extensionName]) {
perMessageDeflate.accept(
extensions[PerMessageDeflate.extensionName]
);
this._extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
}
} catch (err) {
socket.destroy();
this.finalize(new Error('Invalid Sec-WebSocket-Extensions header'));
return;
}
}
this.setSocket(socket, head, 0);
});
}

79
node_modules/ws/package.json generated vendored Normal file
View File

@ -0,0 +1,79 @@
{
"_from": "ws@^4.0.0",
"_id": "ws@4.1.0",
"_inBundle": false,
"_integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==",
"_location": "/ws",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "ws@^4.0.0",
"name": "ws",
"escapedName": "ws",
"rawSpec": "^4.0.0",
"saveSpec": null,
"fetchSpec": "^4.0.0"
},
"_requiredBy": [
"/discord.js"
],
"_resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz",
"_shasum": "a979b5d7d4da68bf54efe0408967c324869a7289",
"_spec": "ws@^4.0.0",
"_where": "C:\\Users\\matia\\Madebot\\node_modules\\discord.js",
"author": {
"name": "Einar Otto Stangvik",
"email": "einaros@gmail.com",
"url": "http://2x.io"
},
"bugs": {
"url": "https://github.com/websockets/ws/issues"
},
"bundleDependencies": false,
"dependencies": {
"async-limiter": "~1.0.0",
"safe-buffer": "~5.1.0"
},
"deprecated": false,
"description": "Simple to use, blazing fast and thoroughly tested websocket client and server for Node.js",
"devDependencies": {
"benchmark": "~2.1.2",
"bufferutil": "~3.0.0",
"eslint": "~4.18.0",
"eslint-config-standard": "~11.0.0",
"eslint-plugin-import": "~2.9.0",
"eslint-plugin-node": "~6.0.0",
"eslint-plugin-promise": "~3.6.0",
"eslint-plugin-standard": "~3.0.0",
"mocha": "~5.0.0",
"nyc": "~11.4.1",
"utf-8-validate": "~4.0.0"
},
"files": [
"index.js",
"lib"
],
"homepage": "https://github.com/websockets/ws",
"keywords": [
"HyBi",
"Push",
"RFC-6455",
"WebSocket",
"WebSockets",
"real-time"
],
"license": "MIT",
"main": "index.js",
"name": "ws",
"repository": {
"type": "git",
"url": "git+https://github.com/websockets/ws.git"
},
"scripts": {
"integration": "eslint . && mocha test/*.integration.js",
"lint": "eslint .",
"test": "eslint . && nyc --reporter=html --reporter=text mocha test/*.test.js"
},
"version": "4.1.0"
}