mirror of
https://github.com/S2-/minifyfromhtml.git
synced 2025-08-02 12:00:03 +02:00
update packages to latest version
This commit is contained in:
2
node_modules/ws/LICENSE
generated
vendored
2
node_modules/ws/LICENSE
generated
vendored
@@ -1,5 +1,3 @@
|
||||
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
|
||||
|
137
node_modules/ws/README.md
generated
vendored
137
node_modules/ws/README.md
generated
vendored
@@ -1,8 +1,7 @@
|
||||
# ws: a Node.js WebSocket library
|
||||
|
||||
[](https://www.npmjs.com/package/ws)
|
||||
[](https://github.com/websockets/ws/actions?query=workflow%3ACI+branch%3Amaster)
|
||||
[](https://ci.appveyor.com/project/lpinca/ws)
|
||||
[](https://github.com/websockets/ws/actions?query=workflow%3ACI+branch%3Amaster)
|
||||
[](https://coveralls.io/github/websockets/ws)
|
||||
|
||||
ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and
|
||||
@@ -34,7 +33,7 @@ can use one of the many wrappers available on npm, like
|
||||
- [Multiple servers sharing a single HTTP/S server](#multiple-servers-sharing-a-single-https-server)
|
||||
- [Client authentication](#client-authentication)
|
||||
- [Server broadcast](#server-broadcast)
|
||||
- [echo.websocket.org demo](#echowebsocketorg-demo)
|
||||
- [Round-trip time](#round-trip-time)
|
||||
- [Use the Node.js streams API](#use-the-nodejs-streams-api)
|
||||
- [Other examples](#other-examples)
|
||||
- [FAQ](#faq)
|
||||
@@ -69,6 +68,13 @@ necessarily need to have a C++ compiler installed on your machine.
|
||||
- `npm install --save-optional utf-8-validate`: Allows to efficiently check if a
|
||||
message contains valid UTF-8.
|
||||
|
||||
To not even try to require and use these modules, use the
|
||||
[`WS_NO_BUFFER_UTIL`](./doc/ws.md#ws_no_buffer_util) and
|
||||
[`WS_NO_UTF_8_VALIDATE`](./doc/ws.md#ws_no_utf_8_validate) environment
|
||||
variables. These might be useful to enhance security in systems where a user can
|
||||
put a package in the package search path of an application of another user, due
|
||||
to how the Node.js resolver algorithm works.
|
||||
|
||||
## API docs
|
||||
|
||||
See [`/doc/ws.md`](./doc/ws.md) for Node.js-like documentation of ws classes and
|
||||
@@ -98,9 +104,9 @@ 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');
|
||||
import WebSocket, { WebSocketServer } from 'ws';
|
||||
|
||||
const wss = new WebSocket.Server({
|
||||
const wss = new WebSocketServer({
|
||||
port: 8080,
|
||||
perMessageDeflate: {
|
||||
zlibDeflateOptions: {
|
||||
@@ -119,7 +125,7 @@ const wss = new WebSocket.Server({
|
||||
// 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.
|
||||
// should not be compressed if context takeover is disabled.
|
||||
}
|
||||
});
|
||||
```
|
||||
@@ -129,7 +135,7 @@ server. To always disable the extension on the client set the
|
||||
`perMessageDeflate` option to `false`.
|
||||
|
||||
```js
|
||||
const WebSocket = require('ws');
|
||||
import WebSocket from 'ws';
|
||||
|
||||
const ws = new WebSocket('ws://www.host.com/path', {
|
||||
perMessageDeflate: false
|
||||
@@ -141,7 +147,7 @@ const ws = new WebSocket('ws://www.host.com/path', {
|
||||
### Sending and receiving text data
|
||||
|
||||
```js
|
||||
const WebSocket = require('ws');
|
||||
import WebSocket from 'ws';
|
||||
|
||||
const ws = new WebSocket('ws://www.host.com/path');
|
||||
|
||||
@@ -149,15 +155,15 @@ ws.on('open', function open() {
|
||||
ws.send('something');
|
||||
});
|
||||
|
||||
ws.on('message', function incoming(data) {
|
||||
console.log(data);
|
||||
ws.on('message', function message(data) {
|
||||
console.log('received: %s', data);
|
||||
});
|
||||
```
|
||||
|
||||
### Sending binary data
|
||||
|
||||
```js
|
||||
const WebSocket = require('ws');
|
||||
import WebSocket from 'ws';
|
||||
|
||||
const ws = new WebSocket('ws://www.host.com/path');
|
||||
|
||||
@@ -175,13 +181,13 @@ ws.on('open', function open() {
|
||||
### Simple server
|
||||
|
||||
```js
|
||||
const WebSocket = require('ws');
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
const wss = new WebSocket.Server({ port: 8080 });
|
||||
const wss = new WebSocketServer({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('message', function incoming(message) {
|
||||
console.log('received: %s', message);
|
||||
ws.on('message', function message(data) {
|
||||
console.log('received: %s', data);
|
||||
});
|
||||
|
||||
ws.send('something');
|
||||
@@ -191,19 +197,19 @@ wss.on('connection', function connection(ws) {
|
||||
### External HTTP/S server
|
||||
|
||||
```js
|
||||
const fs = require('fs');
|
||||
const https = require('https');
|
||||
const WebSocket = require('ws');
|
||||
import { createServer } from 'https';
|
||||
import { readFileSync } from 'fs';
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
const server = https.createServer({
|
||||
cert: fs.readFileSync('/path/to/cert.pem'),
|
||||
key: fs.readFileSync('/path/to/key.pem')
|
||||
const server = createServer({
|
||||
cert: readFileSync('/path/to/cert.pem'),
|
||||
key: readFileSync('/path/to/key.pem')
|
||||
});
|
||||
const wss = new WebSocket.Server({ server });
|
||||
const wss = new WebSocketServer({ server });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('message', function incoming(message) {
|
||||
console.log('received: %s', message);
|
||||
ws.on('message', function message(data) {
|
||||
console.log('received: %s', data);
|
||||
});
|
||||
|
||||
ws.send('something');
|
||||
@@ -215,13 +221,13 @@ server.listen(8080);
|
||||
### Multiple servers sharing a single HTTP/S server
|
||||
|
||||
```js
|
||||
const http = require('http');
|
||||
const WebSocket = require('ws');
|
||||
const url = require('url');
|
||||
import { createServer } from 'http';
|
||||
import { parse } from 'url';
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
const server = http.createServer();
|
||||
const wss1 = new WebSocket.Server({ noServer: true });
|
||||
const wss2 = new WebSocket.Server({ noServer: true });
|
||||
const server = createServer();
|
||||
const wss1 = new WebSocketServer({ noServer: true });
|
||||
const wss2 = new WebSocketServer({ noServer: true });
|
||||
|
||||
wss1.on('connection', function connection(ws) {
|
||||
// ...
|
||||
@@ -232,7 +238,7 @@ wss2.on('connection', function connection(ws) {
|
||||
});
|
||||
|
||||
server.on('upgrade', function upgrade(request, socket, head) {
|
||||
const pathname = url.parse(request.url).pathname;
|
||||
const { pathname } = parse(request.url);
|
||||
|
||||
if (pathname === '/foo') {
|
||||
wss1.handleUpgrade(request, socket, head, function done(ws) {
|
||||
@@ -253,21 +259,21 @@ server.listen(8080);
|
||||
### Client authentication
|
||||
|
||||
```js
|
||||
const http = require('http');
|
||||
const WebSocket = require('ws');
|
||||
import { createServer } from 'http';
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
const server = http.createServer();
|
||||
const wss = new WebSocket.Server({ noServer: true });
|
||||
const server = createServer();
|
||||
const wss = new WebSocketServer({ noServer: true });
|
||||
|
||||
wss.on('connection', function connection(ws, request, client) {
|
||||
ws.on('message', function message(msg) {
|
||||
console.log(`Received message ${msg} from user ${client}`);
|
||||
ws.on('message', function message(data) {
|
||||
console.log(`Received message ${data} from user ${client}`);
|
||||
});
|
||||
});
|
||||
|
||||
server.on('upgrade', function upgrade(request, socket, head) {
|
||||
// This function is not defined on purpose. Implement it with your own logic.
|
||||
authenticate(request, (err, client) => {
|
||||
authenticate(request, function next(err, client) {
|
||||
if (err || !client) {
|
||||
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
||||
socket.destroy();
|
||||
@@ -291,15 +297,15 @@ A client WebSocket broadcasting to all connected WebSocket clients, including
|
||||
itself.
|
||||
|
||||
```js
|
||||
const WebSocket = require('ws');
|
||||
import WebSocket, { WebSocketServer } from 'ws';
|
||||
|
||||
const wss = new WebSocket.Server({ port: 8080 });
|
||||
const wss = new WebSocketServer({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('message', function incoming(data) {
|
||||
ws.on('message', function message(data, isBinary) {
|
||||
wss.clients.forEach(function each(client) {
|
||||
if (client.readyState === WebSocket.OPEN) {
|
||||
client.send(data);
|
||||
client.send(data, { binary: isBinary });
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -310,29 +316,27 @@ A client WebSocket broadcasting to every other connected WebSocket clients,
|
||||
excluding itself.
|
||||
|
||||
```js
|
||||
const WebSocket = require('ws');
|
||||
import WebSocket, { WebSocketServer } from 'ws';
|
||||
|
||||
const wss = new WebSocket.Server({ port: 8080 });
|
||||
const wss = new WebSocketServer({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('message', function incoming(data) {
|
||||
ws.on('message', function message(data, isBinary) {
|
||||
wss.clients.forEach(function each(client) {
|
||||
if (client !== ws && client.readyState === WebSocket.OPEN) {
|
||||
client.send(data);
|
||||
client.send(data, { binary: isBinary });
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### echo.websocket.org demo
|
||||
### Round-trip time
|
||||
|
||||
```js
|
||||
const WebSocket = require('ws');
|
||||
import WebSocket from 'ws';
|
||||
|
||||
const ws = new WebSocket('wss://echo.websocket.org/', {
|
||||
origin: 'https://websocket.org'
|
||||
});
|
||||
const ws = new WebSocket('wss://websocket-echo.com/');
|
||||
|
||||
ws.on('open', function open() {
|
||||
console.log('connected');
|
||||
@@ -343,8 +347,8 @@ ws.on('close', function close() {
|
||||
console.log('disconnected');
|
||||
});
|
||||
|
||||
ws.on('message', function incoming(data) {
|
||||
console.log(`Roundtrip time: ${Date.now() - data} ms`);
|
||||
ws.on('message', function message(data) {
|
||||
console.log(`Round-trip time: ${Date.now() - data} ms`);
|
||||
|
||||
setTimeout(function timeout() {
|
||||
ws.send(Date.now());
|
||||
@@ -355,13 +359,11 @@ ws.on('message', function incoming(data) {
|
||||
### Use the Node.js streams API
|
||||
|
||||
```js
|
||||
const WebSocket = require('ws');
|
||||
import WebSocket, { createWebSocketStream } from 'ws';
|
||||
|
||||
const ws = new WebSocket('wss://echo.websocket.org/', {
|
||||
origin: 'https://websocket.org'
|
||||
});
|
||||
const ws = new WebSocket('wss://websocket-echo.com/');
|
||||
|
||||
const duplex = WebSocket.createWebSocketStream(ws, { encoding: 'utf8' });
|
||||
const duplex = createWebSocketStream(ws, { encoding: 'utf8' });
|
||||
|
||||
duplex.pipe(process.stdout);
|
||||
process.stdin.pipe(duplex);
|
||||
@@ -381,9 +383,9 @@ Otherwise, see the test cases.
|
||||
The remote IP address can be obtained from the raw socket.
|
||||
|
||||
```js
|
||||
const WebSocket = require('ws');
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
const wss = new WebSocket.Server({ port: 8080 });
|
||||
const wss = new WebSocketServer({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws, req) {
|
||||
const ip = req.socket.remoteAddress;
|
||||
@@ -409,15 +411,13 @@ 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');
|
||||
|
||||
function noop() {}
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
function heartbeat() {
|
||||
this.isAlive = true;
|
||||
}
|
||||
|
||||
const wss = new WebSocket.Server({ port: 8080 });
|
||||
const wss = new WebSocketServer({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.isAlive = true;
|
||||
@@ -429,7 +429,7 @@ const interval = setInterval(function ping() {
|
||||
if (ws.isAlive === false) return ws.terminate();
|
||||
|
||||
ws.isAlive = false;
|
||||
ws.ping(noop);
|
||||
ws.ping();
|
||||
});
|
||||
}, 30000);
|
||||
|
||||
@@ -446,7 +446,7 @@ without knowing it. You might want to add a ping listener on your clients to
|
||||
prevent that. A simple implementation would be:
|
||||
|
||||
```js
|
||||
const WebSocket = require('ws');
|
||||
import WebSocket from 'ws';
|
||||
|
||||
function heartbeat() {
|
||||
clearTimeout(this.pingTimeout);
|
||||
@@ -460,7 +460,7 @@ function heartbeat() {
|
||||
}, 30000 + 1000);
|
||||
}
|
||||
|
||||
const client = new WebSocket('wss://echo.websocket.org/');
|
||||
const client = new WebSocket('wss://websocket-echo.com/');
|
||||
|
||||
client.on('open', heartbeat);
|
||||
client.on('ping', heartbeat);
|
||||
@@ -492,5 +492,4 @@ We're using the GitHub [releases][changelog] for changelog entries.
|
||||
[server-report]: http://websockets.github.io/ws/autobahn/servers/
|
||||
[session-parse-example]: ./examples/express-session-parse
|
||||
[socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent
|
||||
[ws-server-options]:
|
||||
https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback
|
||||
[ws-server-options]: ./doc/ws.md#new-websocketserveroptions-callback
|
||||
|
3
node_modules/ws/index.js
generated
vendored
3
node_modules/ws/index.js
generated
vendored
@@ -7,4 +7,7 @@ WebSocket.Server = require('./lib/websocket-server');
|
||||
WebSocket.Receiver = require('./lib/receiver');
|
||||
WebSocket.Sender = require('./lib/sender');
|
||||
|
||||
WebSocket.WebSocket = WebSocket;
|
||||
WebSocket.WebSocketServer = WebSocket.Server;
|
||||
|
||||
module.exports = WebSocket;
|
||||
|
48
node_modules/ws/lib/buffer-util.js
generated
vendored
48
node_modules/ws/lib/buffer-util.js
generated
vendored
@@ -52,9 +52,7 @@ function _mask(source, mask, output, offset, length) {
|
||||
* @public
|
||||
*/
|
||||
function _unmask(buffer, mask) {
|
||||
// Required until https://github.com/nodejs/node/issues/9006 is resolved.
|
||||
const length = buffer.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
buffer[i] ^= mask[i & 3];
|
||||
}
|
||||
}
|
||||
@@ -101,29 +99,29 @@ function toBuffer(data) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
try {
|
||||
const bufferUtil = require('bufferutil');
|
||||
const bu = bufferUtil.BufferUtil || bufferUtil;
|
||||
module.exports = {
|
||||
concat,
|
||||
mask: _mask,
|
||||
toArrayBuffer,
|
||||
toBuffer,
|
||||
unmask: _unmask
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
concat,
|
||||
mask(source, mask, output, offset, length) {
|
||||
/* istanbul ignore else */
|
||||
if (!process.env.WS_NO_BUFFER_UTIL) {
|
||||
try {
|
||||
const bufferUtil = require('bufferutil');
|
||||
|
||||
module.exports.mask = function (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) {
|
||||
else bufferUtil.mask(source, mask, output, offset, length);
|
||||
};
|
||||
|
||||
module.exports.unmask = function (buffer, mask) {
|
||||
if (buffer.length < 32) _unmask(buffer, mask);
|
||||
else bu.unmask(buffer, mask);
|
||||
}
|
||||
};
|
||||
} catch (e) /* istanbul ignore next */ {
|
||||
module.exports = {
|
||||
concat,
|
||||
mask: _mask,
|
||||
toArrayBuffer,
|
||||
toBuffer,
|
||||
unmask: _unmask
|
||||
};
|
||||
else bufferUtil.unmask(buffer, mask);
|
||||
};
|
||||
} catch (e) {
|
||||
// Continue regardless of the error.
|
||||
}
|
||||
}
|
||||
|
4
node_modules/ws/lib/constants.js
generated
vendored
4
node_modules/ws/lib/constants.js
generated
vendored
@@ -2,9 +2,11 @@
|
||||
|
||||
module.exports = {
|
||||
BINARY_TYPES: ['nodebuffer', 'arraybuffer', 'fragments'],
|
||||
EMPTY_BUFFER: Buffer.alloc(0),
|
||||
GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
|
||||
kForOnEventAttribute: Symbol('kIsForOnEventAttribute'),
|
||||
kListener: Symbol('kListener'),
|
||||
kStatusCode: Symbol('status-code'),
|
||||
kWebSocket: Symbol('websocket'),
|
||||
EMPTY_BUFFER: Buffer.alloc(0),
|
||||
NOOP: () => {}
|
||||
};
|
||||
|
272
node_modules/ws/lib/event-target.js
generated
vendored
272
node_modules/ws/lib/event-target.js
generated
vendored
@@ -1,111 +1,172 @@
|
||||
'use strict';
|
||||
|
||||
const { kForOnEventAttribute, kListener } = require('./constants');
|
||||
|
||||
const kCode = Symbol('kCode');
|
||||
const kData = Symbol('kData');
|
||||
const kError = Symbol('kError');
|
||||
const kMessage = Symbol('kMessage');
|
||||
const kReason = Symbol('kReason');
|
||||
const kTarget = Symbol('kTarget');
|
||||
const kType = Symbol('kType');
|
||||
const kWasClean = Symbol('kWasClean');
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws {TypeError} If the `type` argument is not specified
|
||||
*/
|
||||
constructor(type, target) {
|
||||
this.target = target;
|
||||
this.type = type;
|
||||
constructor(type) {
|
||||
this[kTarget] = null;
|
||||
this[kType] = 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
|
||||
* @type {*}
|
||||
*/
|
||||
constructor(data, target) {
|
||||
super('message', target);
|
||||
get target() {
|
||||
return this[kTarget];
|
||||
}
|
||||
|
||||
this.data = data;
|
||||
/**
|
||||
* @type {String}
|
||||
*/
|
||||
get type() {
|
||||
return this[kType];
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(Event.prototype, 'target', { enumerable: true });
|
||||
Object.defineProperty(Event.prototype, 'type', { enumerable: true });
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param {String} type The name of the event
|
||||
* @param {Object} [options] A dictionary object that allows for setting
|
||||
* attributes via object members of the same name
|
||||
* @param {Number} [options.code=0] The status code explaining why the
|
||||
* connection was closed
|
||||
* @param {String} [options.reason=''] A human-readable string explaining why
|
||||
* the connection was closed
|
||||
* @param {Boolean} [options.wasClean=false] Indicates whether or not the
|
||||
* connection was cleanly closed
|
||||
*/
|
||||
constructor(code, reason, target) {
|
||||
super('close', target);
|
||||
constructor(type, options = {}) {
|
||||
super(type);
|
||||
|
||||
this.wasClean = target._closeFrameReceived && target._closeFrameSent;
|
||||
this.reason = reason;
|
||||
this.code = code;
|
||||
this[kCode] = options.code === undefined ? 0 : options.code;
|
||||
this[kReason] = options.reason === undefined ? '' : options.reason;
|
||||
this[kWasClean] = options.wasClean === undefined ? false : options.wasClean;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @type {Number}
|
||||
*/
|
||||
constructor(target) {
|
||||
super('open', target);
|
||||
get code() {
|
||||
return this[kCode];
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {String}
|
||||
*/
|
||||
get reason() {
|
||||
return this[kReason];
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Boolean}
|
||||
*/
|
||||
get wasClean() {
|
||||
return this[kWasClean];
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(CloseEvent.prototype, 'code', { enumerable: true });
|
||||
Object.defineProperty(CloseEvent.prototype, 'reason', { enumerable: true });
|
||||
Object.defineProperty(CloseEvent.prototype, 'wasClean', { enumerable: true });
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param {String} type The name of the event
|
||||
* @param {Object} [options] A dictionary object that allows for setting
|
||||
* attributes via object members of the same name
|
||||
* @param {*} [options.error=null] The error that generated this event
|
||||
* @param {String} [options.message=''] The error message
|
||||
*/
|
||||
constructor(error, target) {
|
||||
super('error', target);
|
||||
constructor(type, options = {}) {
|
||||
super(type);
|
||||
|
||||
this.message = error.message;
|
||||
this.error = error;
|
||||
this[kError] = options.error === undefined ? null : options.error;
|
||||
this[kMessage] = options.message === undefined ? '' : options.message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {*}
|
||||
*/
|
||||
get error() {
|
||||
return this[kError];
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {String}
|
||||
*/
|
||||
get message() {
|
||||
return this[kMessage];
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(ErrorEvent.prototype, 'error', { enumerable: true });
|
||||
Object.defineProperty(ErrorEvent.prototype, 'message', { enumerable: true });
|
||||
|
||||
/**
|
||||
* Class representing a message event.
|
||||
*
|
||||
* @extends Event
|
||||
*/
|
||||
class MessageEvent extends Event {
|
||||
/**
|
||||
* Create a new `MessageEvent`.
|
||||
*
|
||||
* @param {String} type The name of the event
|
||||
* @param {Object} [options] A dictionary object that allows for setting
|
||||
* attributes via object members of the same name
|
||||
* @param {*} [options.data=null] The message content
|
||||
*/
|
||||
constructor(type, options = {}) {
|
||||
super(type);
|
||||
|
||||
this[kData] = options.data === undefined ? null : options.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {*}
|
||||
*/
|
||||
get data() {
|
||||
return this[kData];
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(MessageEvent.prototype, 'data', { enumerable: true });
|
||||
|
||||
/**
|
||||
* This provides methods for emulating the `EventTarget` interface. It's not
|
||||
* meant to be used directly.
|
||||
@@ -120,46 +181,62 @@ const EventTarget = {
|
||||
* @param {Function} listener The listener to add
|
||||
* @param {Object} [options] An options object specifies characteristics about
|
||||
* the event listener
|
||||
* @param {Boolean} [options.once=false] A `Boolean`` indicating that the
|
||||
* @param {Boolean} [options.once=false] A `Boolean` indicating that the
|
||||
* listener should be invoked at most once after being added. If `true`,
|
||||
* the listener would be automatically removed when invoked.
|
||||
* @public
|
||||
*/
|
||||
addEventListener(type, listener, options) {
|
||||
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));
|
||||
}
|
||||
|
||||
const method = options && options.once ? 'once' : 'on';
|
||||
addEventListener(type, listener, options = {}) {
|
||||
let wrapper;
|
||||
|
||||
if (type === 'message') {
|
||||
onMessage._listener = listener;
|
||||
this[method](type, onMessage);
|
||||
wrapper = function onMessage(data, isBinary) {
|
||||
const event = new MessageEvent('message', {
|
||||
data: isBinary ? data : data.toString()
|
||||
});
|
||||
|
||||
event[kTarget] = this;
|
||||
listener.call(this, event);
|
||||
};
|
||||
} else if (type === 'close') {
|
||||
onClose._listener = listener;
|
||||
this[method](type, onClose);
|
||||
wrapper = function onClose(code, message) {
|
||||
const event = new CloseEvent('close', {
|
||||
code,
|
||||
reason: message.toString(),
|
||||
wasClean: this._closeFrameReceived && this._closeFrameSent
|
||||
});
|
||||
|
||||
event[kTarget] = this;
|
||||
listener.call(this, event);
|
||||
};
|
||||
} else if (type === 'error') {
|
||||
onError._listener = listener;
|
||||
this[method](type, onError);
|
||||
wrapper = function onError(error) {
|
||||
const event = new ErrorEvent('error', {
|
||||
error,
|
||||
message: error.message
|
||||
});
|
||||
|
||||
event[kTarget] = this;
|
||||
listener.call(this, event);
|
||||
};
|
||||
} else if (type === 'open') {
|
||||
onOpen._listener = listener;
|
||||
this[method](type, onOpen);
|
||||
wrapper = function onOpen() {
|
||||
const event = new Event('open');
|
||||
|
||||
event[kTarget] = this;
|
||||
listener.call(this, event);
|
||||
};
|
||||
} else {
|
||||
this[method](type, listener);
|
||||
return;
|
||||
}
|
||||
|
||||
wrapper[kForOnEventAttribute] = !!options[kForOnEventAttribute];
|
||||
wrapper[kListener] = listener;
|
||||
|
||||
if (options.once) {
|
||||
this.once(type, wrapper);
|
||||
} else {
|
||||
this.on(type, wrapper);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -167,18 +244,23 @@ const EventTarget = {
|
||||
* Remove an event listener.
|
||||
*
|
||||
* @param {String} type A string representing the event type to remove
|
||||
* @param {Function} listener The listener to remove
|
||||
* @param {Function} handler The listener to remove
|
||||
* @public
|
||||
*/
|
||||
removeEventListener(type, listener) {
|
||||
const listeners = this.listeners(type);
|
||||
|
||||
for (let i = 0; i < listeners.length; i++) {
|
||||
if (listeners[i] === listener || listeners[i]._listener === listener) {
|
||||
this.removeListener(type, listeners[i]);
|
||||
removeEventListener(type, handler) {
|
||||
for (const listener of this.listeners(type)) {
|
||||
if (listener[kListener] === handler && !listener[kForOnEventAttribute]) {
|
||||
this.removeListener(type, listener);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = EventTarget;
|
||||
module.exports = {
|
||||
CloseEvent,
|
||||
ErrorEvent,
|
||||
Event,
|
||||
EventTarget,
|
||||
MessageEvent
|
||||
};
|
||||
|
36
node_modules/ws/lib/extension.js
generated
vendored
36
node_modules/ws/lib/extension.js
generated
vendored
@@ -1,27 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
//
|
||||
// Allowed token characters:
|
||||
//
|
||||
// '!', '#', '$', '%', '&', ''', '*', '+', '-',
|
||||
// '.', 0-9, A-Z, '^', '_', '`', a-z, '|', '~'
|
||||
//
|
||||
// tokenChars[32] === 0 // ' '
|
||||
// tokenChars[33] === 1 // '!'
|
||||
// tokenChars[34] === 0 // '"'
|
||||
// ...
|
||||
//
|
||||
// prettier-ignore
|
||||
const tokenChars = [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31
|
||||
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
|
||||
];
|
||||
const { tokenChars } = require('./validation');
|
||||
|
||||
/**
|
||||
* Adds an offer to the map of extension offers or a parameter to the map of
|
||||
@@ -47,9 +26,6 @@ function push(dest, name, elem) {
|
||||
*/
|
||||
function parse(header) {
|
||||
const offers = Object.create(null);
|
||||
|
||||
if (header === undefined || header === '') return offers;
|
||||
|
||||
let params = Object.create(null);
|
||||
let mustUnescape = false;
|
||||
let isEscaping = false;
|
||||
@@ -57,16 +33,20 @@ function parse(header) {
|
||||
let extensionName;
|
||||
let paramName;
|
||||
let start = -1;
|
||||
let code = -1;
|
||||
let end = -1;
|
||||
let i = 0;
|
||||
|
||||
for (; i < header.length; i++) {
|
||||
const code = header.charCodeAt(i);
|
||||
code = header.charCodeAt(i);
|
||||
|
||||
if (extensionName === undefined) {
|
||||
if (end === -1 && tokenChars[code] === 1) {
|
||||
if (start === -1) start = i;
|
||||
} else if (code === 0x20 /* ' ' */ || code === 0x09 /* '\t' */) {
|
||||
} else if (
|
||||
i !== 0 &&
|
||||
(code === 0x20 /* ' ' */ || code === 0x09) /* '\t' */
|
||||
) {
|
||||
if (end === -1 && start !== -1) end = i;
|
||||
} else if (code === 0x3b /* ';' */ || code === 0x2c /* ',' */) {
|
||||
if (start === -1) {
|
||||
@@ -167,7 +147,7 @@ function parse(header) {
|
||||
}
|
||||
}
|
||||
|
||||
if (start === -1 || inQuotes) {
|
||||
if (start === -1 || inQuotes || code === 0x20 || code === 0x09) {
|
||||
throw new SyntaxError('Unexpected end of input');
|
||||
}
|
||||
|
||||
|
33
node_modules/ws/lib/permessage-deflate.js
generated
vendored
33
node_modules/ws/lib/permessage-deflate.js
generated
vendored
@@ -4,7 +4,7 @@ const zlib = require('zlib');
|
||||
|
||||
const bufferUtil = require('./buffer-util');
|
||||
const Limiter = require('./limiter');
|
||||
const { kStatusCode, NOOP } = require('./constants');
|
||||
const { kStatusCode } = require('./constants');
|
||||
|
||||
const TRAILER = Buffer.from([0x00, 0x00, 0xff, 0xff]);
|
||||
const kPerMessageDeflate = Symbol('permessage-deflate');
|
||||
@@ -30,22 +30,22 @@ class PerMessageDeflate {
|
||||
* Creates a PerMessageDeflate instance.
|
||||
*
|
||||
* @param {Object} [options] Configuration options
|
||||
* @param {Boolean} [options.serverNoContextTakeover=false] Request/accept
|
||||
* disabling of server context takeover
|
||||
* @param {Boolean} [options.clientNoContextTakeover=false] Advertise/
|
||||
* acknowledge disabling of client context takeover
|
||||
* @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the
|
||||
* use of a custom server window size
|
||||
* @param {(Boolean|Number)} [options.clientMaxWindowBits] Advertise support
|
||||
* for, or request, a custom client window size
|
||||
* @param {Boolean} [options.clientNoContextTakeover=false] Advertise/
|
||||
* acknowledge disabling of client context takeover
|
||||
* @param {Number} [options.concurrencyLimit=10] The number of concurrent
|
||||
* calls to zlib
|
||||
* @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the
|
||||
* use of a custom server window size
|
||||
* @param {Boolean} [options.serverNoContextTakeover=false] Request/accept
|
||||
* disabling of server context takeover
|
||||
* @param {Number} [options.threshold=1024] Size (in bytes) below which
|
||||
* messages should not be compressed if context takeover is disabled
|
||||
* @param {Object} [options.zlibDeflateOptions] Options to pass to zlib on
|
||||
* deflate
|
||||
* @param {Object} [options.zlibInflateOptions] Options to pass to zlib on
|
||||
* inflate
|
||||
* @param {Number} [options.threshold=1024] Size (in bytes) below which
|
||||
* messages should not be compressed
|
||||
* @param {Number} [options.concurrencyLimit=10] The number of concurrent
|
||||
* calls to zlib
|
||||
* @param {Boolean} [isServer=false] Create the instance in either server or
|
||||
* client mode
|
||||
* @param {Number} [maxPayload=0] The maximum allowed message length
|
||||
@@ -313,7 +313,7 @@ class PerMessageDeflate {
|
||||
/**
|
||||
* Compress data. Concurrency limited.
|
||||
*
|
||||
* @param {Buffer} data Data to compress
|
||||
* @param {(Buffer|String)} data Data to compress
|
||||
* @param {Boolean} fin Specifies whether or not this is the last fragment
|
||||
* @param {Function} callback Callback
|
||||
* @public
|
||||
@@ -395,7 +395,7 @@ class PerMessageDeflate {
|
||||
/**
|
||||
* Compress data.
|
||||
*
|
||||
* @param {Buffer} data Data to compress
|
||||
* @param {(Buffer|String)} data Data to compress
|
||||
* @param {Boolean} fin Specifies whether or not this is the last fragment
|
||||
* @param {Function} callback Callback
|
||||
* @private
|
||||
@@ -418,13 +418,6 @@ class PerMessageDeflate {
|
||||
this._deflate[kTotalLength] = 0;
|
||||
this._deflate[kBuffers] = [];
|
||||
|
||||
//
|
||||
// An `'error'` event is emitted, only on Node.js < 10.0.0, if the
|
||||
// `zlib.DeflateRaw` instance is closed while data is being processed.
|
||||
// This can happen if `PerMessageDeflate#cleanup()` is called at the wrong
|
||||
// time due to an abnormal WebSocket closure.
|
||||
//
|
||||
this._deflate.on('error', NOOP);
|
||||
this._deflate.on('data', deflateOnData);
|
||||
}
|
||||
|
||||
|
51
node_modules/ws/lib/receiver.js
generated
vendored
51
node_modules/ws/lib/receiver.js
generated
vendored
@@ -22,26 +22,31 @@ const INFLATING = 5;
|
||||
/**
|
||||
* HyBi Receiver implementation.
|
||||
*
|
||||
* @extends stream.Writable
|
||||
* @extends Writable
|
||||
*/
|
||||
class Receiver extends Writable {
|
||||
/**
|
||||
* Creates a Receiver instance.
|
||||
*
|
||||
* @param {String} [binaryType=nodebuffer] The type for binary data
|
||||
* @param {Object} [extensions] An object containing the negotiated extensions
|
||||
* @param {Boolean} [isServer=false] Specifies whether to operate in client or
|
||||
* server mode
|
||||
* @param {Number} [maxPayload=0] The maximum allowed message length
|
||||
* @param {Object} [options] Options object
|
||||
* @param {String} [options.binaryType=nodebuffer] The type for binary data
|
||||
* @param {Object} [options.extensions] An object containing the negotiated
|
||||
* extensions
|
||||
* @param {Boolean} [options.isServer=false] Specifies whether to operate in
|
||||
* client or server mode
|
||||
* @param {Number} [options.maxPayload=0] The maximum allowed message length
|
||||
* @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
|
||||
* not to skip UTF-8 validation for text and close messages
|
||||
*/
|
||||
constructor(binaryType, extensions, isServer, maxPayload) {
|
||||
constructor(options = {}) {
|
||||
super();
|
||||
|
||||
this._binaryType = binaryType || BINARY_TYPES[0];
|
||||
this._binaryType = options.binaryType || BINARY_TYPES[0];
|
||||
this._extensions = options.extensions || {};
|
||||
this._isServer = !!options.isServer;
|
||||
this._maxPayload = options.maxPayload | 0;
|
||||
this._skipUTF8Validation = !!options.skipUTF8Validation;
|
||||
this[kWebSocket] = undefined;
|
||||
this._extensions = extensions || {};
|
||||
this._isServer = !!isServer;
|
||||
this._maxPayload = maxPayload | 0;
|
||||
|
||||
this._bufferedBytes = 0;
|
||||
this._buffers = [];
|
||||
@@ -412,7 +417,13 @@ class Receiver extends Writable {
|
||||
}
|
||||
|
||||
data = this.consume(this._payloadLength);
|
||||
if (this._masked) unmask(data, this._mask);
|
||||
|
||||
if (
|
||||
this._masked &&
|
||||
(this._mask[0] | this._mask[1] | this._mask[2] | this._mask[3]) !== 0
|
||||
) {
|
||||
unmask(data, this._mask);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._opcode > 0x07) return this.controlMessage(data);
|
||||
@@ -425,7 +436,7 @@ class Receiver extends Writable {
|
||||
|
||||
if (data.length) {
|
||||
//
|
||||
// This message is not compressed so its lenght is the sum of the payload
|
||||
// This message is not compressed so its length is the sum of the payload
|
||||
// length of all fragments.
|
||||
//
|
||||
this._messageLength = this._totalPayloadLength;
|
||||
@@ -499,11 +510,11 @@ class Receiver extends Writable {
|
||||
data = fragments;
|
||||
}
|
||||
|
||||
this.emit('message', data);
|
||||
this.emit('message', data, true);
|
||||
} else {
|
||||
const buf = concat(fragments, messageLength);
|
||||
|
||||
if (!isValidUTF8(buf)) {
|
||||
if (!this._skipUTF8Validation && !isValidUTF8(buf)) {
|
||||
this._loop = false;
|
||||
return error(
|
||||
Error,
|
||||
@@ -514,7 +525,7 @@ class Receiver extends Writable {
|
||||
);
|
||||
}
|
||||
|
||||
this.emit('message', buf.toString());
|
||||
this.emit('message', buf, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -533,7 +544,7 @@ class Receiver extends Writable {
|
||||
this._loop = false;
|
||||
|
||||
if (data.length === 0) {
|
||||
this.emit('conclude', 1005, '');
|
||||
this.emit('conclude', 1005, EMPTY_BUFFER);
|
||||
this.end();
|
||||
} else if (data.length === 1) {
|
||||
return error(
|
||||
@@ -558,7 +569,7 @@ class Receiver extends Writable {
|
||||
|
||||
const buf = data.slice(2);
|
||||
|
||||
if (!isValidUTF8(buf)) {
|
||||
if (!this._skipUTF8Validation && !isValidUTF8(buf)) {
|
||||
return error(
|
||||
Error,
|
||||
'invalid UTF-8 sequence',
|
||||
@@ -568,7 +579,7 @@ class Receiver extends Writable {
|
||||
);
|
||||
}
|
||||
|
||||
this.emit('conclude', code, buf.toString());
|
||||
this.emit('conclude', code, buf);
|
||||
this.end();
|
||||
}
|
||||
} else if (this._opcode === 0x09) {
|
||||
@@ -586,7 +597,7 @@ module.exports = Receiver;
|
||||
/**
|
||||
* Builds an error object.
|
||||
*
|
||||
* @param {(Error|RangeError)} ErrorCtor The error constructor
|
||||
* @param {function(new:Error|RangeError)} ErrorCtor The error constructor
|
||||
* @param {String} message The error message
|
||||
* @param {Boolean} prefix Specifies whether or not to add a default prefix to
|
||||
* `message`
|
||||
|
331
node_modules/ws/lib/sender.js
generated
vendored
331
node_modules/ws/lib/sender.js
generated
vendored
@@ -1,5 +1,9 @@
|
||||
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^net|tls$" }] */
|
||||
|
||||
'use strict';
|
||||
|
||||
const net = require('net');
|
||||
const tls = require('tls');
|
||||
const { randomFillSync } = require('crypto');
|
||||
|
||||
const PerMessageDeflate = require('./permessage-deflate');
|
||||
@@ -7,7 +11,8 @@ const { EMPTY_BUFFER } = require('./constants');
|
||||
const { isValidStatusCode } = require('./validation');
|
||||
const { mask: applyMask, toBuffer } = require('./buffer-util');
|
||||
|
||||
const mask = Buffer.alloc(4);
|
||||
const kByteLength = Symbol('kByteLength');
|
||||
const maskBuffer = Buffer.alloc(4);
|
||||
|
||||
/**
|
||||
* HyBi Sender implementation.
|
||||
@@ -16,11 +21,19 @@ class Sender {
|
||||
/**
|
||||
* Creates a Sender instance.
|
||||
*
|
||||
* @param {net.Socket} socket The connection socket
|
||||
* @param {(net.Socket|tls.Socket)} socket The connection socket
|
||||
* @param {Object} [extensions] An object containing the negotiated extensions
|
||||
* @param {Function} [generateMask] The function used to generate the masking
|
||||
* key
|
||||
*/
|
||||
constructor(socket, extensions) {
|
||||
constructor(socket, extensions, generateMask) {
|
||||
this._extensions = extensions || {};
|
||||
|
||||
if (generateMask) {
|
||||
this._generateMask = generateMask;
|
||||
this._maskBuffer = Buffer.alloc(4);
|
||||
}
|
||||
|
||||
this._socket = socket;
|
||||
|
||||
this._firstFragment = true;
|
||||
@@ -34,34 +47,71 @@ class Sender {
|
||||
/**
|
||||
* Frames a piece of data according to the HyBi WebSocket protocol.
|
||||
*
|
||||
* @param {Buffer} data The data to frame
|
||||
* @param {(Buffer|String)} data The data to frame
|
||||
* @param {Object} options Options object
|
||||
* @param {Boolean} [options.fin=false] Specifies whether or not to set the
|
||||
* FIN bit
|
||||
* @param {Function} [options.generateMask] The function used to generate the
|
||||
* masking key
|
||||
* @param {Boolean} [options.mask=false] Specifies whether or not to mask
|
||||
* `data`
|
||||
* @param {Buffer} [options.maskBuffer] The buffer used to store the masking
|
||||
* key
|
||||
* @param {Number} options.opcode The opcode
|
||||
* @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
|
||||
* modified
|
||||
* @param {Boolean} [options.fin=false] Specifies whether or not to set the
|
||||
* FIN bit
|
||||
* @param {Boolean} [options.mask=false] Specifies whether or not to mask
|
||||
* `data`
|
||||
* @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
|
||||
* RSV1 bit
|
||||
* @return {Buffer[]} The framed data as a list of `Buffer` instances
|
||||
* @return {(Buffer|String)[]} The framed data
|
||||
* @public
|
||||
*/
|
||||
static frame(data, options) {
|
||||
const merge = options.mask && options.readOnly;
|
||||
let offset = options.mask ? 6 : 2;
|
||||
let payloadLength = data.length;
|
||||
let mask;
|
||||
let merge = false;
|
||||
let offset = 2;
|
||||
let skipMasking = false;
|
||||
|
||||
if (data.length >= 65536) {
|
||||
if (options.mask) {
|
||||
mask = options.maskBuffer || maskBuffer;
|
||||
|
||||
if (options.generateMask) {
|
||||
options.generateMask(mask);
|
||||
} else {
|
||||
randomFillSync(mask, 0, 4);
|
||||
}
|
||||
|
||||
skipMasking = (mask[0] | mask[1] | mask[2] | mask[3]) === 0;
|
||||
offset = 6;
|
||||
}
|
||||
|
||||
let dataLength;
|
||||
|
||||
if (typeof data === 'string') {
|
||||
if (
|
||||
(!options.mask || skipMasking) &&
|
||||
options[kByteLength] !== undefined
|
||||
) {
|
||||
dataLength = options[kByteLength];
|
||||
} else {
|
||||
data = Buffer.from(data);
|
||||
dataLength = data.length;
|
||||
}
|
||||
} else {
|
||||
dataLength = data.length;
|
||||
merge = options.mask && options.readOnly && !skipMasking;
|
||||
}
|
||||
|
||||
let payloadLength = dataLength;
|
||||
|
||||
if (dataLength >= 65536) {
|
||||
offset += 8;
|
||||
payloadLength = 127;
|
||||
} else if (data.length > 125) {
|
||||
} else if (dataLength > 125) {
|
||||
offset += 2;
|
||||
payloadLength = 126;
|
||||
}
|
||||
|
||||
const target = Buffer.allocUnsafe(merge ? data.length + offset : offset);
|
||||
const target = Buffer.allocUnsafe(merge ? dataLength + offset : offset);
|
||||
|
||||
target[0] = options.fin ? options.opcode | 0x80 : options.opcode;
|
||||
if (options.rsv1) target[0] |= 0x40;
|
||||
@@ -69,28 +119,28 @@ class Sender {
|
||||
target[1] = payloadLength;
|
||||
|
||||
if (payloadLength === 126) {
|
||||
target.writeUInt16BE(data.length, 2);
|
||||
target.writeUInt16BE(dataLength, 2);
|
||||
} else if (payloadLength === 127) {
|
||||
target.writeUInt32BE(0, 2);
|
||||
target.writeUInt32BE(data.length, 6);
|
||||
target[2] = target[3] = 0;
|
||||
target.writeUIntBE(dataLength, 4, 6);
|
||||
}
|
||||
|
||||
if (!options.mask) return [target, data];
|
||||
|
||||
randomFillSync(mask, 0, 4);
|
||||
|
||||
target[1] |= 0x80;
|
||||
target[offset - 4] = mask[0];
|
||||
target[offset - 3] = mask[1];
|
||||
target[offset - 2] = mask[2];
|
||||
target[offset - 1] = mask[3];
|
||||
|
||||
if (skipMasking) return [target, data];
|
||||
|
||||
if (merge) {
|
||||
applyMask(data, mask, target, offset, data.length);
|
||||
applyMask(data, mask, target, offset, dataLength);
|
||||
return [target];
|
||||
}
|
||||
|
||||
applyMask(data, mask, data, 0, data.length);
|
||||
applyMask(data, mask, data, 0, dataLength);
|
||||
return [target, data];
|
||||
}
|
||||
|
||||
@@ -98,7 +148,7 @@ class Sender {
|
||||
* Sends a close message to the other peer.
|
||||
*
|
||||
* @param {Number} [code] The status code component of the body
|
||||
* @param {String} [data] The message component of the body
|
||||
* @param {(String|Buffer)} [data] The message component of the body
|
||||
* @param {Boolean} [mask=false] Specifies whether or not to mask the message
|
||||
* @param {Function} [cb] Callback
|
||||
* @public
|
||||
@@ -110,7 +160,7 @@ class Sender {
|
||||
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 === '') {
|
||||
} else if (data === undefined || !data.length) {
|
||||
buf = Buffer.allocUnsafe(2);
|
||||
buf.writeUInt16BE(code, 0);
|
||||
} else {
|
||||
@@ -122,37 +172,32 @@ class Sender {
|
||||
|
||||
buf = Buffer.allocUnsafe(2 + length);
|
||||
buf.writeUInt16BE(code, 0);
|
||||
buf.write(data, 2);
|
||||
|
||||
if (typeof data === 'string') {
|
||||
buf.write(data, 2);
|
||||
} else {
|
||||
buf.set(data, 2);
|
||||
}
|
||||
}
|
||||
|
||||
const options = {
|
||||
[kByteLength]: buf.length,
|
||||
fin: true,
|
||||
generateMask: this._generateMask,
|
||||
mask,
|
||||
maskBuffer: this._maskBuffer,
|
||||
opcode: 0x08,
|
||||
readOnly: false,
|
||||
rsv1: false
|
||||
};
|
||||
|
||||
if (this._deflating) {
|
||||
this.enqueue([this.doClose, buf, mask, cb]);
|
||||
this.enqueue([this.dispatch, buf, false, options, cb]);
|
||||
} else {
|
||||
this.doClose(buf, mask, cb);
|
||||
this.sendFrame(Sender.frame(buf, options), cb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Frames and sends a close message.
|
||||
*
|
||||
* @param {Buffer} data The message to send
|
||||
* @param {Boolean} [mask=false] Specifies whether or not to mask `data`
|
||||
* @param {Function} [cb] Callback
|
||||
* @private
|
||||
*/
|
||||
doClose(data, mask, cb) {
|
||||
this.sendFrame(
|
||||
Sender.frame(data, {
|
||||
fin: true,
|
||||
rsv1: false,
|
||||
opcode: 0x08,
|
||||
mask,
|
||||
readOnly: false
|
||||
}),
|
||||
cb
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a ping message to the other peer.
|
||||
*
|
||||
@@ -162,39 +207,38 @@ class Sender {
|
||||
* @public
|
||||
*/
|
||||
ping(data, mask, cb) {
|
||||
const buf = toBuffer(data);
|
||||
let byteLength;
|
||||
let readOnly;
|
||||
|
||||
if (buf.length > 125) {
|
||||
if (typeof data === 'string') {
|
||||
byteLength = Buffer.byteLength(data);
|
||||
readOnly = false;
|
||||
} else {
|
||||
data = toBuffer(data);
|
||||
byteLength = data.length;
|
||||
readOnly = toBuffer.readOnly;
|
||||
}
|
||||
|
||||
if (byteLength > 125) {
|
||||
throw new RangeError('The data size must not be greater than 125 bytes');
|
||||
}
|
||||
|
||||
if (this._deflating) {
|
||||
this.enqueue([this.doPing, buf, mask, toBuffer.readOnly, cb]);
|
||||
} else {
|
||||
this.doPing(buf, mask, toBuffer.readOnly, cb);
|
||||
}
|
||||
}
|
||||
const options = {
|
||||
[kByteLength]: byteLength,
|
||||
fin: true,
|
||||
generateMask: this._generateMask,
|
||||
mask,
|
||||
maskBuffer: this._maskBuffer,
|
||||
opcode: 0x09,
|
||||
readOnly,
|
||||
rsv1: false
|
||||
};
|
||||
|
||||
/**
|
||||
* Frames and sends a ping message.
|
||||
*
|
||||
* @param {Buffer} data The message to send
|
||||
* @param {Boolean} [mask=false] Specifies whether or not to mask `data`
|
||||
* @param {Boolean} [readOnly=false] Specifies whether `data` can be modified
|
||||
* @param {Function} [cb] Callback
|
||||
* @private
|
||||
*/
|
||||
doPing(data, mask, readOnly, cb) {
|
||||
this.sendFrame(
|
||||
Sender.frame(data, {
|
||||
fin: true,
|
||||
rsv1: false,
|
||||
opcode: 0x09,
|
||||
mask,
|
||||
readOnly
|
||||
}),
|
||||
cb
|
||||
);
|
||||
if (this._deflating) {
|
||||
this.enqueue([this.dispatch, data, false, options, cb]);
|
||||
} else {
|
||||
this.sendFrame(Sender.frame(data, options), cb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,39 +250,38 @@ class Sender {
|
||||
* @public
|
||||
*/
|
||||
pong(data, mask, cb) {
|
||||
const buf = toBuffer(data);
|
||||
let byteLength;
|
||||
let readOnly;
|
||||
|
||||
if (buf.length > 125) {
|
||||
if (typeof data === 'string') {
|
||||
byteLength = Buffer.byteLength(data);
|
||||
readOnly = false;
|
||||
} else {
|
||||
data = toBuffer(data);
|
||||
byteLength = data.length;
|
||||
readOnly = toBuffer.readOnly;
|
||||
}
|
||||
|
||||
if (byteLength > 125) {
|
||||
throw new RangeError('The data size must not be greater than 125 bytes');
|
||||
}
|
||||
|
||||
if (this._deflating) {
|
||||
this.enqueue([this.doPong, buf, mask, toBuffer.readOnly, cb]);
|
||||
} else {
|
||||
this.doPong(buf, mask, toBuffer.readOnly, cb);
|
||||
}
|
||||
}
|
||||
const options = {
|
||||
[kByteLength]: byteLength,
|
||||
fin: true,
|
||||
generateMask: this._generateMask,
|
||||
mask,
|
||||
maskBuffer: this._maskBuffer,
|
||||
opcode: 0x0a,
|
||||
readOnly,
|
||||
rsv1: false
|
||||
};
|
||||
|
||||
/**
|
||||
* Frames and sends a pong message.
|
||||
*
|
||||
* @param {Buffer} data The message to send
|
||||
* @param {Boolean} [mask=false] Specifies whether or not to mask `data`
|
||||
* @param {Boolean} [readOnly=false] Specifies whether `data` can be modified
|
||||
* @param {Function} [cb] Callback
|
||||
* @private
|
||||
*/
|
||||
doPong(data, mask, readOnly, cb) {
|
||||
this.sendFrame(
|
||||
Sender.frame(data, {
|
||||
fin: true,
|
||||
rsv1: false,
|
||||
opcode: 0x0a,
|
||||
mask,
|
||||
readOnly
|
||||
}),
|
||||
cb
|
||||
);
|
||||
if (this._deflating) {
|
||||
this.enqueue([this.dispatch, data, false, options, cb]);
|
||||
} else {
|
||||
this.sendFrame(Sender.frame(data, options), cb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -246,10 +289,10 @@ class Sender {
|
||||
*
|
||||
* @param {*} data The message to send
|
||||
* @param {Object} options Options object
|
||||
* @param {Boolean} [options.compress=false] Specifies whether or not to
|
||||
* compress `data`
|
||||
* @param {Boolean} [options.binary=false] Specifies whether `data` is binary
|
||||
* or text
|
||||
* @param {Boolean} [options.compress=false] Specifies whether or not to
|
||||
* compress `data`
|
||||
* @param {Boolean} [options.fin=false] Specifies whether the fragment is the
|
||||
* last one
|
||||
* @param {Boolean} [options.mask=false] Specifies whether or not to mask
|
||||
@@ -258,15 +301,34 @@ class Sender {
|
||||
* @public
|
||||
*/
|
||||
send(data, options, cb) {
|
||||
const buf = toBuffer(data);
|
||||
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
|
||||
let opcode = options.binary ? 2 : 1;
|
||||
let rsv1 = options.compress;
|
||||
|
||||
let byteLength;
|
||||
let readOnly;
|
||||
|
||||
if (typeof data === 'string') {
|
||||
byteLength = Buffer.byteLength(data);
|
||||
readOnly = false;
|
||||
} else {
|
||||
data = toBuffer(data);
|
||||
byteLength = data.length;
|
||||
readOnly = toBuffer.readOnly;
|
||||
}
|
||||
|
||||
if (this._firstFragment) {
|
||||
this._firstFragment = false;
|
||||
if (rsv1 && perMessageDeflate) {
|
||||
rsv1 = buf.length >= perMessageDeflate._threshold;
|
||||
if (
|
||||
rsv1 &&
|
||||
perMessageDeflate &&
|
||||
perMessageDeflate.params[
|
||||
perMessageDeflate._isServer
|
||||
? 'server_no_context_takeover'
|
||||
: 'client_no_context_takeover'
|
||||
]
|
||||
) {
|
||||
rsv1 = byteLength >= perMessageDeflate._threshold;
|
||||
}
|
||||
this._compress = rsv1;
|
||||
} else {
|
||||
@@ -278,26 +340,32 @@ class Sender {
|
||||
|
||||
if (perMessageDeflate) {
|
||||
const opts = {
|
||||
[kByteLength]: byteLength,
|
||||
fin: options.fin,
|
||||
rsv1,
|
||||
opcode,
|
||||
generateMask: this._generateMask,
|
||||
mask: options.mask,
|
||||
readOnly: toBuffer.readOnly
|
||||
maskBuffer: this._maskBuffer,
|
||||
opcode,
|
||||
readOnly,
|
||||
rsv1
|
||||
};
|
||||
|
||||
if (this._deflating) {
|
||||
this.enqueue([this.dispatch, buf, this._compress, opts, cb]);
|
||||
this.enqueue([this.dispatch, data, this._compress, opts, cb]);
|
||||
} else {
|
||||
this.dispatch(buf, this._compress, opts, cb);
|
||||
this.dispatch(data, this._compress, opts, cb);
|
||||
}
|
||||
} else {
|
||||
this.sendFrame(
|
||||
Sender.frame(buf, {
|
||||
Sender.frame(data, {
|
||||
[kByteLength]: byteLength,
|
||||
fin: options.fin,
|
||||
rsv1: false,
|
||||
opcode,
|
||||
generateMask: this._generateMask,
|
||||
mask: options.mask,
|
||||
readOnly: toBuffer.readOnly
|
||||
maskBuffer: this._maskBuffer,
|
||||
opcode,
|
||||
readOnly,
|
||||
rsv1: false
|
||||
}),
|
||||
cb
|
||||
);
|
||||
@@ -305,19 +373,23 @@ class Sender {
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches a data message.
|
||||
* Dispatches a message.
|
||||
*
|
||||
* @param {Buffer} data The message to send
|
||||
* @param {(Buffer|String)} data The message to send
|
||||
* @param {Boolean} [compress=false] Specifies whether or not to compress
|
||||
* `data`
|
||||
* @param {Object} options Options object
|
||||
* @param {Boolean} [options.fin=false] Specifies whether or not to set the
|
||||
* FIN bit
|
||||
* @param {Function} [options.generateMask] The function used to generate the
|
||||
* masking key
|
||||
* @param {Boolean} [options.mask=false] Specifies whether or not to mask
|
||||
* `data`
|
||||
* @param {Buffer} [options.maskBuffer] The buffer used to store the masking
|
||||
* key
|
||||
* @param {Number} options.opcode The opcode
|
||||
* @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
|
||||
* modified
|
||||
* @param {Boolean} [options.fin=false] Specifies whether or not to set the
|
||||
* FIN bit
|
||||
* @param {Boolean} [options.mask=false] Specifies whether or not to mask
|
||||
* `data`
|
||||
* @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
|
||||
* RSV1 bit
|
||||
* @param {Function} [cb] Callback
|
||||
@@ -331,7 +403,7 @@ class Sender {
|
||||
|
||||
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
|
||||
|
||||
this._bufferedBytes += data.length;
|
||||
this._bufferedBytes += options[kByteLength];
|
||||
this._deflating = true;
|
||||
perMessageDeflate.compress(data, options.fin, (_, buf) => {
|
||||
if (this._socket.destroyed) {
|
||||
@@ -342,7 +414,8 @@ class Sender {
|
||||
if (typeof cb === 'function') cb(err);
|
||||
|
||||
for (let i = 0; i < this._queue.length; i++) {
|
||||
const callback = this._queue[i][4];
|
||||
const params = this._queue[i];
|
||||
const callback = params[params.length - 1];
|
||||
|
||||
if (typeof callback === 'function') callback(err);
|
||||
}
|
||||
@@ -350,7 +423,7 @@ class Sender {
|
||||
return;
|
||||
}
|
||||
|
||||
this._bufferedBytes -= data.length;
|
||||
this._bufferedBytes -= options[kByteLength];
|
||||
this._deflating = false;
|
||||
options.readOnly = false;
|
||||
this.sendFrame(Sender.frame(buf, options), cb);
|
||||
@@ -367,7 +440,7 @@ class Sender {
|
||||
while (!this._deflating && this._queue.length) {
|
||||
const params = this._queue.shift();
|
||||
|
||||
this._bufferedBytes -= params[1].length;
|
||||
this._bufferedBytes -= params[3][kByteLength];
|
||||
Reflect.apply(params[0], this, params.slice(1));
|
||||
}
|
||||
}
|
||||
@@ -379,7 +452,7 @@ class Sender {
|
||||
* @private
|
||||
*/
|
||||
enqueue(params) {
|
||||
this._bufferedBytes += params[1].length;
|
||||
this._bufferedBytes += params[3][kByteLength];
|
||||
this._queue.push(params);
|
||||
}
|
||||
|
||||
|
42
node_modules/ws/lib/stream.js
generated
vendored
42
node_modules/ws/lib/stream.js
generated
vendored
@@ -5,7 +5,7 @@ const { Duplex } = require('stream');
|
||||
/**
|
||||
* Emits the `'close'` event on a stream.
|
||||
*
|
||||
* @param {stream.Duplex} The stream.
|
||||
* @param {Duplex} stream The stream.
|
||||
* @private
|
||||
*/
|
||||
function emitClose(stream) {
|
||||
@@ -43,27 +43,12 @@ function duplexOnError(err) {
|
||||
*
|
||||
* @param {WebSocket} ws The `WebSocket` to wrap
|
||||
* @param {Object} [options] The options for the `Duplex` constructor
|
||||
* @return {stream.Duplex} The duplex stream
|
||||
* @return {Duplex} The duplex stream
|
||||
* @public
|
||||
*/
|
||||
function createWebSocketStream(ws, options) {
|
||||
let resumeOnReceiverDrain = true;
|
||||
let terminateOnDestroy = true;
|
||||
|
||||
function receiverOnDrain() {
|
||||
if (resumeOnReceiverDrain) ws._socket.resume();
|
||||
}
|
||||
|
||||
if (ws.readyState === ws.CONNECTING) {
|
||||
ws.once('open', function open() {
|
||||
ws._receiver.removeAllListeners('drain');
|
||||
ws._receiver.on('drain', receiverOnDrain);
|
||||
});
|
||||
} else {
|
||||
ws._receiver.removeAllListeners('drain');
|
||||
ws._receiver.on('drain', receiverOnDrain);
|
||||
}
|
||||
|
||||
const duplex = new Duplex({
|
||||
...options,
|
||||
autoDestroy: false,
|
||||
@@ -72,11 +57,11 @@ function createWebSocketStream(ws, options) {
|
||||
writableObjectMode: false
|
||||
});
|
||||
|
||||
ws.on('message', function message(msg) {
|
||||
if (!duplex.push(msg)) {
|
||||
resumeOnReceiverDrain = false;
|
||||
ws._socket.pause();
|
||||
}
|
||||
ws.on('message', function message(msg, isBinary) {
|
||||
const data =
|
||||
!isBinary && duplex._readableState.objectMode ? msg.toString() : msg;
|
||||
|
||||
if (!duplex.push(data)) ws.pause();
|
||||
});
|
||||
|
||||
ws.once('error', function error(err) {
|
||||
@@ -84,13 +69,13 @@ function createWebSocketStream(ws, options) {
|
||||
|
||||
// Prevent `ws.terminate()` from being called by `duplex._destroy()`.
|
||||
//
|
||||
// - If the state of the `WebSocket` connection is `CONNECTING`,
|
||||
// `ws.terminate()` is a noop as no socket was assigned.
|
||||
// - Otherwise, the error was re-emitted from the listener of the `'error'`
|
||||
// - If the `'error'` event is emitted before the `'open'` event, then
|
||||
// `ws.terminate()` is a noop as no socket is assigned.
|
||||
// - Otherwise, the error is re-emitted by the listener of the `'error'`
|
||||
// event of the `Receiver` object. The listener already closes the
|
||||
// connection by calling `ws.close()`. This allows a close frame to be
|
||||
// sent to the other peer. If `ws.terminate()` is called right after this,
|
||||
// the close frame might not be sent.
|
||||
// then the close frame might not be sent.
|
||||
terminateOnDestroy = false;
|
||||
duplex.destroy(err);
|
||||
});
|
||||
@@ -152,10 +137,7 @@ function createWebSocketStream(ws, options) {
|
||||
};
|
||||
|
||||
duplex._read = function () {
|
||||
if (ws.readyState === ws.OPEN && !resumeOnReceiverDrain) {
|
||||
resumeOnReceiverDrain = true;
|
||||
if (!ws._receiver._writableState.needDrain) ws._socket.resume();
|
||||
}
|
||||
if (ws.isPaused) ws.resume();
|
||||
};
|
||||
|
||||
duplex._write = function (chunk, encoding, callback) {
|
||||
|
62
node_modules/ws/lib/subprotocol.js
generated
vendored
Normal file
62
node_modules/ws/lib/subprotocol.js
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
'use strict';
|
||||
|
||||
const { tokenChars } = require('./validation');
|
||||
|
||||
/**
|
||||
* Parses the `Sec-WebSocket-Protocol` header into a set of subprotocol names.
|
||||
*
|
||||
* @param {String} header The field value of the header
|
||||
* @return {Set} The subprotocol names
|
||||
* @public
|
||||
*/
|
||||
function parse(header) {
|
||||
const protocols = new Set();
|
||||
let start = -1;
|
||||
let end = -1;
|
||||
let i = 0;
|
||||
|
||||
for (i; i < header.length; i++) {
|
||||
const code = header.charCodeAt(i);
|
||||
|
||||
if (end === -1 && tokenChars[code] === 1) {
|
||||
if (start === -1) start = i;
|
||||
} else if (
|
||||
i !== 0 &&
|
||||
(code === 0x20 /* ' ' */ || code === 0x09) /* '\t' */
|
||||
) {
|
||||
if (end === -1 && start !== -1) end = i;
|
||||
} else if (code === 0x2c /* ',' */) {
|
||||
if (start === -1) {
|
||||
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||
}
|
||||
|
||||
if (end === -1) end = i;
|
||||
|
||||
const protocol = header.slice(start, end);
|
||||
|
||||
if (protocols.has(protocol)) {
|
||||
throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
|
||||
}
|
||||
|
||||
protocols.add(protocol);
|
||||
start = end = -1;
|
||||
} else {
|
||||
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (start === -1 || end !== -1) {
|
||||
throw new SyntaxError('Unexpected end of input');
|
||||
}
|
||||
|
||||
const protocol = header.slice(start, i);
|
||||
|
||||
if (protocols.has(protocol)) {
|
||||
throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
|
||||
}
|
||||
|
||||
protocols.add(protocol);
|
||||
return protocols;
|
||||
}
|
||||
|
||||
module.exports = { parse };
|
53
node_modules/ws/lib/validation.js
generated
vendored
53
node_modules/ws/lib/validation.js
generated
vendored
@@ -1,5 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
//
|
||||
// Allowed token characters:
|
||||
//
|
||||
// '!', '#', '$', '%', '&', ''', '*', '+', '-',
|
||||
// '.', 0-9, A-Z, '^', '_', '`', a-z, '|', '~'
|
||||
//
|
||||
// tokenChars[32] === 0 // ' '
|
||||
// tokenChars[33] === 1 // '!'
|
||||
// tokenChars[34] === 0 // '"'
|
||||
// ...
|
||||
//
|
||||
// prettier-ignore
|
||||
const tokenChars = [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31
|
||||
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
|
||||
];
|
||||
|
||||
/**
|
||||
* Checks if a status code is allowed in a close frame.
|
||||
*
|
||||
@@ -82,23 +105,21 @@ function _isValidUTF8(buf) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
let isValidUTF8 = require('utf-8-validate');
|
||||
module.exports = {
|
||||
isValidStatusCode,
|
||||
isValidUTF8: _isValidUTF8,
|
||||
tokenChars
|
||||
};
|
||||
|
||||
/* istanbul ignore if */
|
||||
if (typeof isValidUTF8 === 'object') {
|
||||
isValidUTF8 = isValidUTF8.Validation.isValidUTF8; // utf-8-validate@<3.0.0
|
||||
}
|
||||
/* istanbul ignore else */
|
||||
if (!process.env.WS_NO_UTF_8_VALIDATE) {
|
||||
try {
|
||||
const isValidUTF8 = require('utf-8-validate');
|
||||
|
||||
module.exports = {
|
||||
isValidStatusCode,
|
||||
isValidUTF8(buf) {
|
||||
module.exports.isValidUTF8 = function (buf) {
|
||||
return buf.length < 150 ? _isValidUTF8(buf) : isValidUTF8(buf);
|
||||
}
|
||||
};
|
||||
} catch (e) /* istanbul ignore next */ {
|
||||
module.exports = {
|
||||
isValidStatusCode,
|
||||
isValidUTF8: _isValidUTF8
|
||||
};
|
||||
};
|
||||
} catch (e) {
|
||||
// Continue regardless of the error.
|
||||
}
|
||||
}
|
||||
|
291
node_modules/ws/lib/websocket-server.js
generated
vendored
291
node_modules/ws/lib/websocket-server.js
generated
vendored
@@ -1,16 +1,26 @@
|
||||
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^net|tls|https$" }] */
|
||||
|
||||
'use strict';
|
||||
|
||||
const EventEmitter = require('events');
|
||||
const http = require('http');
|
||||
const https = require('https');
|
||||
const net = require('net');
|
||||
const tls = require('tls');
|
||||
const { createHash } = require('crypto');
|
||||
const { createServer, STATUS_CODES } = require('http');
|
||||
|
||||
const extension = require('./extension');
|
||||
const PerMessageDeflate = require('./permessage-deflate');
|
||||
const subprotocol = require('./subprotocol');
|
||||
const WebSocket = require('./websocket');
|
||||
const { format, parse } = require('./extension');
|
||||
const { GUID, kWebSocket } = require('./constants');
|
||||
|
||||
const keyRegex = /^[+/0-9A-Za-z]{22}==$/;
|
||||
|
||||
const RUNNING = 0;
|
||||
const CLOSING = 1;
|
||||
const CLOSED = 2;
|
||||
|
||||
/**
|
||||
* Class representing a WebSocket server.
|
||||
*
|
||||
@@ -34,8 +44,13 @@ class WebSocketServer extends EventEmitter {
|
||||
* @param {(Boolean|Object)} [options.perMessageDeflate=false] 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 {(http.Server|https.Server)} [options.server] A pre-created HTTP/S
|
||||
* server to use
|
||||
* @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
|
||||
* not to skip UTF-8 validation for text and close messages
|
||||
* @param {Function} [options.verifyClient] A hook to reject connections
|
||||
* @param {Function} [options.WebSocket=WebSocket] Specifies the `WebSocket`
|
||||
* class to use. It must be the `WebSocket` class or class that extends it
|
||||
* @param {Function} [callback] A listener for the `listening` event
|
||||
*/
|
||||
constructor(options, callback) {
|
||||
@@ -43,6 +58,7 @@ class WebSocketServer extends EventEmitter {
|
||||
|
||||
options = {
|
||||
maxPayload: 100 * 1024 * 1024,
|
||||
skipUTF8Validation: false,
|
||||
perMessageDeflate: false,
|
||||
handleProtocols: null,
|
||||
clientTracking: true,
|
||||
@@ -53,18 +69,24 @@ class WebSocketServer extends EventEmitter {
|
||||
host: null,
|
||||
path: null,
|
||||
port: null,
|
||||
WebSocket,
|
||||
...options
|
||||
};
|
||||
|
||||
if (options.port == null && !options.server && !options.noServer) {
|
||||
if (
|
||||
(options.port == null && !options.server && !options.noServer) ||
|
||||
(options.port != null && (options.server || options.noServer)) ||
|
||||
(options.server && options.noServer)
|
||||
) {
|
||||
throw new TypeError(
|
||||
'One of the "port", "server", or "noServer" options must be specified'
|
||||
'One and only one of the "port", "server", or "noServer" options ' +
|
||||
'must be specified'
|
||||
);
|
||||
}
|
||||
|
||||
if (options.port != null) {
|
||||
this._server = createServer((req, res) => {
|
||||
const body = STATUS_CODES[426];
|
||||
this._server = http.createServer((req, res) => {
|
||||
const body = http.STATUS_CODES[426];
|
||||
|
||||
res.writeHead(426, {
|
||||
'Content-Length': body.length,
|
||||
@@ -95,8 +117,13 @@ class WebSocketServer extends EventEmitter {
|
||||
}
|
||||
|
||||
if (options.perMessageDeflate === true) options.perMessageDeflate = {};
|
||||
if (options.clientTracking) this.clients = new Set();
|
||||
if (options.clientTracking) {
|
||||
this.clients = new Set();
|
||||
this._shouldEmitClose = false;
|
||||
}
|
||||
|
||||
this.options = options;
|
||||
this._state = RUNNING;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,37 +145,58 @@ class WebSocketServer extends EventEmitter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the server.
|
||||
* Stop the server from accepting new connections and emit the `'close'` event
|
||||
* when all existing connections are closed.
|
||||
*
|
||||
* @param {Function} [cb] Callback
|
||||
* @param {Function} [cb] A one-time listener for the `'close'` event
|
||||
* @public
|
||||
*/
|
||||
close(cb) {
|
||||
if (cb) this.once('close', cb);
|
||||
if (this._state === CLOSED) {
|
||||
if (cb) {
|
||||
this.once('close', () => {
|
||||
cb(new Error('The server is not running'));
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// Terminate all associated clients.
|
||||
//
|
||||
if (this.clients) {
|
||||
for (const client of this.clients) client.terminate();
|
||||
process.nextTick(emitClose, this);
|
||||
return;
|
||||
}
|
||||
|
||||
const server = this._server;
|
||||
if (cb) this.once('close', cb);
|
||||
|
||||
if (this._state === CLOSING) return;
|
||||
this._state = CLOSING;
|
||||
|
||||
if (this.options.noServer || this.options.server) {
|
||||
if (this._server) {
|
||||
this._removeListeners();
|
||||
this._removeListeners = this._server = null;
|
||||
}
|
||||
|
||||
if (this.clients) {
|
||||
if (!this.clients.size) {
|
||||
process.nextTick(emitClose, this);
|
||||
} else {
|
||||
this._shouldEmitClose = true;
|
||||
}
|
||||
} else {
|
||||
process.nextTick(emitClose, this);
|
||||
}
|
||||
} else {
|
||||
const server = this._server;
|
||||
|
||||
if (server) {
|
||||
this._removeListeners();
|
||||
this._removeListeners = this._server = null;
|
||||
|
||||
//
|
||||
// Close the http server if it was internally created.
|
||||
// The HTTP/S server was created internally. Close it, and rely on its
|
||||
// `'close'` event.
|
||||
//
|
||||
if (this.options.port != null) {
|
||||
server.close(() => this.emit('close'));
|
||||
return;
|
||||
}
|
||||
server.close(() => {
|
||||
emitClose(this);
|
||||
});
|
||||
}
|
||||
|
||||
process.nextTick(emitClose, this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,7 +221,8 @@ class WebSocketServer extends EventEmitter {
|
||||
* Handle a HTTP Upgrade request.
|
||||
*
|
||||
* @param {http.IncomingMessage} req The request object
|
||||
* @param {net.Socket} socket The network socket between the server and client
|
||||
* @param {(net.Socket|tls.Socket)} socket The network socket between the
|
||||
* server and client
|
||||
* @param {Buffer} head The first packet of the upgraded stream
|
||||
* @param {Function} cb Callback
|
||||
* @public
|
||||
@@ -181,25 +230,58 @@ 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 key = req.headers['sec-websocket-key'];
|
||||
const version = +req.headers['sec-websocket-version'];
|
||||
|
||||
if (req.method !== 'GET') {
|
||||
const message = 'Invalid HTTP method';
|
||||
abortHandshakeOrEmitwsClientError(this, req, socket, 405, message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.headers.upgrade.toLowerCase() !== 'websocket') {
|
||||
const message = 'Invalid Upgrade header';
|
||||
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!key || !keyRegex.test(key)) {
|
||||
const message = 'Missing or invalid Sec-WebSocket-Key header';
|
||||
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (version !== 8 && version !== 13) {
|
||||
const message = 'Missing or invalid Sec-WebSocket-Version header';
|
||||
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.shouldHandle(req)) {
|
||||
abortHandshake(socket, 400);
|
||||
return;
|
||||
}
|
||||
|
||||
const secWebSocketProtocol = req.headers['sec-websocket-protocol'];
|
||||
let protocols = new Set();
|
||||
|
||||
if (secWebSocketProtocol !== undefined) {
|
||||
try {
|
||||
protocols = subprotocol.parse(secWebSocketProtocol);
|
||||
} catch (err) {
|
||||
const message = 'Invalid Sec-WebSocket-Protocol header';
|
||||
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const secWebSocketExtensions = req.headers['sec-websocket-extensions'];
|
||||
const extensions = {};
|
||||
|
||||
if (
|
||||
req.method !== 'GET' ||
|
||||
req.headers.upgrade.toLowerCase() !== 'websocket' ||
|
||||
!key ||
|
||||
!keyRegex.test(key) ||
|
||||
(version !== 8 && version !== 13) ||
|
||||
!this.shouldHandle(req)
|
||||
this.options.perMessageDeflate &&
|
||||
secWebSocketExtensions !== undefined
|
||||
) {
|
||||
return abortHandshake(socket, 400);
|
||||
}
|
||||
|
||||
if (this.options.perMessageDeflate) {
|
||||
const perMessageDeflate = new PerMessageDeflate(
|
||||
this.options.perMessageDeflate,
|
||||
true,
|
||||
@@ -207,14 +289,17 @@ class WebSocketServer extends EventEmitter {
|
||||
);
|
||||
|
||||
try {
|
||||
const offers = parse(req.headers['sec-websocket-extensions']);
|
||||
const offers = extension.parse(secWebSocketExtensions);
|
||||
|
||||
if (offers[PerMessageDeflate.extensionName]) {
|
||||
perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]);
|
||||
extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
|
||||
}
|
||||
} catch (err) {
|
||||
return abortHandshake(socket, 400);
|
||||
const message =
|
||||
'Invalid or unacceptable Sec-WebSocket-Extensions header';
|
||||
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +320,15 @@ class WebSocketServer extends EventEmitter {
|
||||
return abortHandshake(socket, code || 401, message, headers);
|
||||
}
|
||||
|
||||
this.completeUpgrade(key, extensions, req, socket, head, cb);
|
||||
this.completeUpgrade(
|
||||
extensions,
|
||||
key,
|
||||
protocols,
|
||||
req,
|
||||
socket,
|
||||
head,
|
||||
cb
|
||||
);
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -243,22 +336,24 @@ class WebSocketServer extends EventEmitter {
|
||||
if (!this.options.verifyClient(info)) return abortHandshake(socket, 401);
|
||||
}
|
||||
|
||||
this.completeUpgrade(key, extensions, req, socket, head, cb);
|
||||
this.completeUpgrade(extensions, key, protocols, 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 {String} key The value of the `Sec-WebSocket-Key` header
|
||||
* @param {Set} protocols The subprotocols
|
||||
* @param {http.IncomingMessage} req The request object
|
||||
* @param {net.Socket} socket The network socket between the server and client
|
||||
* @param {(net.Socket|tls.Socket)} socket The network socket between the
|
||||
* server and client
|
||||
* @param {Buffer} head The first packet of the upgraded stream
|
||||
* @param {Function} cb Callback
|
||||
* @throws {Error} If called more than once with the same socket
|
||||
* @private
|
||||
*/
|
||||
completeUpgrade(key, extensions, req, socket, head, cb) {
|
||||
completeUpgrade(extensions, key, protocols, req, socket, head, cb) {
|
||||
//
|
||||
// Destroy the socket if the client has already sent a FIN packet.
|
||||
//
|
||||
@@ -271,6 +366,8 @@ class WebSocketServer extends EventEmitter {
|
||||
);
|
||||
}
|
||||
|
||||
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
||||
|
||||
const digest = createHash('sha1')
|
||||
.update(key + GUID)
|
||||
.digest('base64');
|
||||
@@ -282,20 +379,15 @@ class WebSocketServer extends EventEmitter {
|
||||
`Sec-WebSocket-Accept: ${digest}`
|
||||
];
|
||||
|
||||
const ws = new WebSocket(null);
|
||||
let protocol = req.headers['sec-websocket-protocol'];
|
||||
|
||||
if (protocol) {
|
||||
protocol = protocol.split(',').map(trim);
|
||||
const ws = new this.options.WebSocket(null);
|
||||
|
||||
if (protocols.size) {
|
||||
//
|
||||
// Optionally call external protocol selection handler.
|
||||
//
|
||||
if (this.options.handleProtocols) {
|
||||
protocol = this.options.handleProtocols(protocol, req);
|
||||
} else {
|
||||
protocol = protocol[0];
|
||||
}
|
||||
const protocol = this.options.handleProtocols
|
||||
? this.options.handleProtocols(protocols, req)
|
||||
: protocols.values().next().value;
|
||||
|
||||
if (protocol) {
|
||||
headers.push(`Sec-WebSocket-Protocol: ${protocol}`);
|
||||
@@ -305,7 +397,7 @@ class WebSocketServer extends EventEmitter {
|
||||
|
||||
if (extensions[PerMessageDeflate.extensionName]) {
|
||||
const params = extensions[PerMessageDeflate.extensionName].params;
|
||||
const value = format({
|
||||
const value = extension.format({
|
||||
[PerMessageDeflate.extensionName]: [params]
|
||||
});
|
||||
headers.push(`Sec-WebSocket-Extensions: ${value}`);
|
||||
@@ -320,11 +412,20 @@ class WebSocketServer extends EventEmitter {
|
||||
socket.write(headers.concat('\r\n').join('\r\n'));
|
||||
socket.removeListener('error', socketOnError);
|
||||
|
||||
ws.setSocket(socket, head, this.options.maxPayload);
|
||||
ws.setSocket(socket, head, {
|
||||
maxPayload: this.options.maxPayload,
|
||||
skipUTF8Validation: this.options.skipUTF8Validation
|
||||
});
|
||||
|
||||
if (this.clients) {
|
||||
this.clients.add(ws);
|
||||
ws.on('close', () => this.clients.delete(ws));
|
||||
ws.on('close', () => {
|
||||
this.clients.delete(ws);
|
||||
|
||||
if (this._shouldEmitClose && !this.clients.size) {
|
||||
process.nextTick(emitClose, this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cb(ws, req);
|
||||
@@ -360,11 +461,12 @@ function addListeners(server, map) {
|
||||
* @private
|
||||
*/
|
||||
function emitClose(server) {
|
||||
server._state = CLOSED;
|
||||
server.emit('close');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle premature socket errors.
|
||||
* Handle socket errors.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
@@ -375,44 +477,59 @@ function socketOnError() {
|
||||
/**
|
||||
* Close the connection when preconditions are not fulfilled.
|
||||
*
|
||||
* @param {net.Socket} socket The socket of the upgrade request
|
||||
* @param {(net.Socket|tls.Socket)} socket The socket of the upgrade request
|
||||
* @param {Number} code The HTTP response status code
|
||||
* @param {String} [message] The HTTP response body
|
||||
* @param {Object} [headers] Additional HTTP response headers
|
||||
* @private
|
||||
*/
|
||||
function abortHandshake(socket, code, message, headers) {
|
||||
if (socket.writable) {
|
||||
message = message || STATUS_CODES[code];
|
||||
headers = {
|
||||
Connection: 'close',
|
||||
'Content-Type': 'text/html',
|
||||
'Content-Length': Buffer.byteLength(message),
|
||||
...headers
|
||||
};
|
||||
//
|
||||
// The socket is writable unless the user destroyed or ended it before calling
|
||||
// `server.handleUpgrade()` or in the `verifyClient` function, which is a user
|
||||
// error. Handling this does not make much sense as the worst that can happen
|
||||
// is that some of the data written by the user might be discarded due to the
|
||||
// call to `socket.end()` below, which triggers an `'error'` event that in
|
||||
// turn causes the socket to be destroyed.
|
||||
//
|
||||
message = message || http.STATUS_CODES[code];
|
||||
headers = {
|
||||
Connection: 'close',
|
||||
'Content-Type': 'text/html',
|
||||
'Content-Length': Buffer.byteLength(message),
|
||||
...headers
|
||||
};
|
||||
|
||||
socket.write(
|
||||
`HTTP/1.1 ${code} ${STATUS_CODES[code]}\r\n` +
|
||||
Object.keys(headers)
|
||||
.map((h) => `${h}: ${headers[h]}`)
|
||||
.join('\r\n') +
|
||||
'\r\n\r\n' +
|
||||
message
|
||||
);
|
||||
}
|
||||
socket.once('finish', socket.destroy);
|
||||
|
||||
socket.removeListener('error', socketOnError);
|
||||
socket.destroy();
|
||||
socket.end(
|
||||
`HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\r\n` +
|
||||
Object.keys(headers)
|
||||
.map((h) => `${h}: ${headers[h]}`)
|
||||
.join('\r\n') +
|
||||
'\r\n\r\n' +
|
||||
message
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove whitespace characters from both ends of a string.
|
||||
* Emit a `'wsClientError'` event on a `WebSocketServer` if there is at least
|
||||
* one listener for it, otherwise call `abortHandshake()`.
|
||||
*
|
||||
* @param {String} str The string
|
||||
* @return {String} A new string representing `str` stripped of whitespace
|
||||
* characters from both its beginning and end
|
||||
* @param {WebSocketServer} server The WebSocket server
|
||||
* @param {http.IncomingMessage} req The request object
|
||||
* @param {(net.Socket|tls.Socket)} socket The socket of the upgrade request
|
||||
* @param {Number} code The HTTP response status code
|
||||
* @param {String} message The HTTP response body
|
||||
* @private
|
||||
*/
|
||||
function trim(str) {
|
||||
return str.trim();
|
||||
function abortHandshakeOrEmitwsClientError(server, req, socket, code, message) {
|
||||
if (server.listenerCount('wsClientError')) {
|
||||
const err = new Error(message);
|
||||
Error.captureStackTrace(err, abortHandshakeOrEmitwsClientError);
|
||||
|
||||
server.emit('wsClientError', err, socket, req);
|
||||
} else {
|
||||
abortHandshake(socket, code, message);
|
||||
}
|
||||
}
|
||||
|
614
node_modules/ws/lib/websocket.js
generated
vendored
614
node_modules/ws/lib/websocket.js
generated
vendored
@@ -1,3 +1,5 @@
|
||||
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Readable$" }] */
|
||||
|
||||
'use strict';
|
||||
|
||||
const EventEmitter = require('events');
|
||||
@@ -6,6 +8,7 @@ const http = require('http');
|
||||
const net = require('net');
|
||||
const tls = require('tls');
|
||||
const { randomBytes, createHash } = require('crypto');
|
||||
const { Readable } = require('stream');
|
||||
const { URL } = require('url');
|
||||
|
||||
const PerMessageDeflate = require('./permessage-deflate');
|
||||
@@ -15,17 +18,23 @@ const {
|
||||
BINARY_TYPES,
|
||||
EMPTY_BUFFER,
|
||||
GUID,
|
||||
kForOnEventAttribute,
|
||||
kListener,
|
||||
kStatusCode,
|
||||
kWebSocket,
|
||||
NOOP
|
||||
} = require('./constants');
|
||||
const { addEventListener, removeEventListener } = require('./event-target');
|
||||
const {
|
||||
EventTarget: { addEventListener, removeEventListener }
|
||||
} = require('./event-target');
|
||||
const { format, parse } = require('./extension');
|
||||
const { toBuffer } = require('./buffer-util');
|
||||
|
||||
const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
|
||||
const protocolVersions = [8, 13];
|
||||
const closeTimeout = 30 * 1000;
|
||||
const kAborted = Symbol('kAborted');
|
||||
const protocolVersions = [8, 13];
|
||||
const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
|
||||
const subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
|
||||
|
||||
/**
|
||||
* Class representing a WebSocket.
|
||||
@@ -36,7 +45,7 @@ class WebSocket extends EventEmitter {
|
||||
/**
|
||||
* Create a new `WebSocket`.
|
||||
*
|
||||
* @param {(String|url.URL)} address The URL to which to connect
|
||||
* @param {(String|URL)} address The URL to which to connect
|
||||
* @param {(String|String[])} [protocols] The subprotocols
|
||||
* @param {Object} [options] Connection options
|
||||
*/
|
||||
@@ -47,9 +56,10 @@ class WebSocket extends EventEmitter {
|
||||
this._closeCode = 1006;
|
||||
this._closeFrameReceived = false;
|
||||
this._closeFrameSent = false;
|
||||
this._closeMessage = '';
|
||||
this._closeMessage = EMPTY_BUFFER;
|
||||
this._closeTimer = null;
|
||||
this._extensions = {};
|
||||
this._paused = false;
|
||||
this._protocol = '';
|
||||
this._readyState = WebSocket.CONNECTING;
|
||||
this._receiver = null;
|
||||
@@ -61,11 +71,15 @@ class WebSocket extends EventEmitter {
|
||||
this._isServer = false;
|
||||
this._redirects = 0;
|
||||
|
||||
if (Array.isArray(protocols)) {
|
||||
protocols = protocols.join(', ');
|
||||
} else if (typeof protocols === 'object' && protocols !== null) {
|
||||
options = protocols;
|
||||
protocols = undefined;
|
||||
if (protocols === undefined) {
|
||||
protocols = [];
|
||||
} else if (!Array.isArray(protocols)) {
|
||||
if (typeof protocols === 'object' && protocols !== null) {
|
||||
options = protocols;
|
||||
protocols = [];
|
||||
} else {
|
||||
protocols = [protocols];
|
||||
}
|
||||
}
|
||||
|
||||
initAsClient(this, address, protocols, options);
|
||||
@@ -112,6 +126,45 @@ class WebSocket extends EventEmitter {
|
||||
return Object.keys(this._extensions).join();
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Boolean}
|
||||
*/
|
||||
get isPaused() {
|
||||
return this._paused;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Function}
|
||||
*/
|
||||
/* istanbul ignore next */
|
||||
get onclose() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Function}
|
||||
*/
|
||||
/* istanbul ignore next */
|
||||
get onerror() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Function}
|
||||
*/
|
||||
/* istanbul ignore next */
|
||||
get onopen() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Function}
|
||||
*/
|
||||
/* istanbul ignore next */
|
||||
get onmessage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {String}
|
||||
*/
|
||||
@@ -136,20 +189,27 @@ class WebSocket extends EventEmitter {
|
||||
/**
|
||||
* Set up the socket and the internal resources.
|
||||
*
|
||||
* @param {net.Socket} socket The network socket between the server and client
|
||||
* @param {(net.Socket|tls.Socket)} socket The network socket between the
|
||||
* server and client
|
||||
* @param {Buffer} head The first packet of the upgraded stream
|
||||
* @param {Number} [maxPayload=0] The maximum allowed message size
|
||||
* @param {Object} options Options object
|
||||
* @param {Function} [options.generateMask] The function used to generate the
|
||||
* masking key
|
||||
* @param {Number} [options.maxPayload=0] The maximum allowed message size
|
||||
* @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
|
||||
* not to skip UTF-8 validation for text and close messages
|
||||
* @private
|
||||
*/
|
||||
setSocket(socket, head, maxPayload) {
|
||||
const receiver = new Receiver(
|
||||
this.binaryType,
|
||||
this._extensions,
|
||||
this._isServer,
|
||||
maxPayload
|
||||
);
|
||||
setSocket(socket, head, options) {
|
||||
const receiver = new Receiver({
|
||||
binaryType: this.binaryType,
|
||||
extensions: this._extensions,
|
||||
isServer: this._isServer,
|
||||
maxPayload: options.maxPayload,
|
||||
skipUTF8Validation: options.skipUTF8Validation
|
||||
});
|
||||
|
||||
this._sender = new Sender(socket, this._extensions);
|
||||
this._sender = new Sender(socket, this._extensions, options.generateMask);
|
||||
this._receiver = receiver;
|
||||
this._socket = socket;
|
||||
|
||||
@@ -214,7 +274,8 @@ class WebSocket extends EventEmitter {
|
||||
* +---+
|
||||
*
|
||||
* @param {Number} [code] Status code explaining why the connection is closing
|
||||
* @param {String} [data] A string explaining why the connection is closing
|
||||
* @param {(String|Buffer)} [data] The reason why the connection is
|
||||
* closing
|
||||
* @public
|
||||
*/
|
||||
close(code, data) {
|
||||
@@ -225,7 +286,13 @@ class WebSocket extends EventEmitter {
|
||||
}
|
||||
|
||||
if (this.readyState === WebSocket.CLOSING) {
|
||||
if (this._closeFrameSent && this._closeFrameReceived) this._socket.end();
|
||||
if (
|
||||
this._closeFrameSent &&
|
||||
(this._closeFrameReceived || this._receiver._writableState.errorEmitted)
|
||||
) {
|
||||
this._socket.end();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -238,7 +305,13 @@ class WebSocket extends EventEmitter {
|
||||
if (err) return;
|
||||
|
||||
this._closeFrameSent = true;
|
||||
if (this._closeFrameReceived) this._socket.end();
|
||||
|
||||
if (
|
||||
this._closeFrameReceived ||
|
||||
this._receiver._writableState.errorEmitted
|
||||
) {
|
||||
this._socket.end();
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
@@ -250,6 +323,23 @@ class WebSocket extends EventEmitter {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause the socket.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
pause() {
|
||||
if (
|
||||
this.readyState === WebSocket.CONNECTING ||
|
||||
this.readyState === WebSocket.CLOSED
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._paused = true;
|
||||
this._socket.pause();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a ping.
|
||||
*
|
||||
@@ -314,15 +404,32 @@ class WebSocket extends EventEmitter {
|
||||
this._sender.pong(data || EMPTY_BUFFER, mask, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume the socket.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
resume() {
|
||||
if (
|
||||
this.readyState === WebSocket.CONNECTING ||
|
||||
this.readyState === WebSocket.CLOSED
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._paused = false;
|
||||
if (!this._receiver._writableState.needDrain) this._socket.resume();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.compress] Specifies whether or not to compress
|
||||
* `data`
|
||||
* @param {Boolean} [options.fin=true] Specifies whether the fragment is the
|
||||
* last one
|
||||
* @param {Boolean} [options.mask] Specifies whether or not to mask `data`
|
||||
@@ -380,17 +487,83 @@ class WebSocket extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
readyStates.forEach((readyState, i) => {
|
||||
const descriptor = { enumerable: true, value: i };
|
||||
/**
|
||||
* @constant {Number} CONNECTING
|
||||
* @memberof WebSocket
|
||||
*/
|
||||
Object.defineProperty(WebSocket, 'CONNECTING', {
|
||||
enumerable: true,
|
||||
value: readyStates.indexOf('CONNECTING')
|
||||
});
|
||||
|
||||
Object.defineProperty(WebSocket.prototype, readyState, descriptor);
|
||||
Object.defineProperty(WebSocket, readyState, descriptor);
|
||||
/**
|
||||
* @constant {Number} CONNECTING
|
||||
* @memberof WebSocket.prototype
|
||||
*/
|
||||
Object.defineProperty(WebSocket.prototype, 'CONNECTING', {
|
||||
enumerable: true,
|
||||
value: readyStates.indexOf('CONNECTING')
|
||||
});
|
||||
|
||||
/**
|
||||
* @constant {Number} OPEN
|
||||
* @memberof WebSocket
|
||||
*/
|
||||
Object.defineProperty(WebSocket, 'OPEN', {
|
||||
enumerable: true,
|
||||
value: readyStates.indexOf('OPEN')
|
||||
});
|
||||
|
||||
/**
|
||||
* @constant {Number} OPEN
|
||||
* @memberof WebSocket.prototype
|
||||
*/
|
||||
Object.defineProperty(WebSocket.prototype, 'OPEN', {
|
||||
enumerable: true,
|
||||
value: readyStates.indexOf('OPEN')
|
||||
});
|
||||
|
||||
/**
|
||||
* @constant {Number} CLOSING
|
||||
* @memberof WebSocket
|
||||
*/
|
||||
Object.defineProperty(WebSocket, 'CLOSING', {
|
||||
enumerable: true,
|
||||
value: readyStates.indexOf('CLOSING')
|
||||
});
|
||||
|
||||
/**
|
||||
* @constant {Number} CLOSING
|
||||
* @memberof WebSocket.prototype
|
||||
*/
|
||||
Object.defineProperty(WebSocket.prototype, 'CLOSING', {
|
||||
enumerable: true,
|
||||
value: readyStates.indexOf('CLOSING')
|
||||
});
|
||||
|
||||
/**
|
||||
* @constant {Number} CLOSED
|
||||
* @memberof WebSocket
|
||||
*/
|
||||
Object.defineProperty(WebSocket, 'CLOSED', {
|
||||
enumerable: true,
|
||||
value: readyStates.indexOf('CLOSED')
|
||||
});
|
||||
|
||||
/**
|
||||
* @constant {Number} CLOSED
|
||||
* @memberof WebSocket.prototype
|
||||
*/
|
||||
Object.defineProperty(WebSocket.prototype, 'CLOSED', {
|
||||
enumerable: true,
|
||||
value: readyStates.indexOf('CLOSED')
|
||||
});
|
||||
|
||||
[
|
||||
'binaryType',
|
||||
'bufferedAmount',
|
||||
'extensions',
|
||||
'isPaused',
|
||||
'protocol',
|
||||
'readyState',
|
||||
'url'
|
||||
@@ -404,37 +577,27 @@ readyStates.forEach((readyState, i) => {
|
||||
//
|
||||
['open', 'error', 'close', 'message'].forEach((method) => {
|
||||
Object.defineProperty(WebSocket.prototype, `on${method}`, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
/**
|
||||
* Return the listener of the event.
|
||||
*
|
||||
* @return {(Function|undefined)} The event listener or `undefined`
|
||||
* @public
|
||||
*/
|
||||
get() {
|
||||
const listeners = this.listeners(method);
|
||||
for (let i = 0; i < listeners.length; i++) {
|
||||
if (listeners[i]._listener) return listeners[i]._listener;
|
||||
for (const listener of this.listeners(method)) {
|
||||
if (listener[kForOnEventAttribute]) return listener[kListener];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return null;
|
||||
},
|
||||
/**
|
||||
* Add a listener for the event.
|
||||
*
|
||||
* @param {Function} listener The listener to add
|
||||
* @public
|
||||
*/
|
||||
set(listener) {
|
||||
const listeners = this.listeners(method);
|
||||
for (let i = 0; i < listeners.length; i++) {
|
||||
//
|
||||
// Remove only the listeners added via `addEventListener`.
|
||||
//
|
||||
if (listeners[i]._listener) this.removeListener(method, listeners[i]);
|
||||
set(handler) {
|
||||
for (const listener of this.listeners(method)) {
|
||||
if (listener[kForOnEventAttribute]) {
|
||||
this.removeListener(method, listener);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.addEventListener(method, listener);
|
||||
|
||||
if (typeof handler !== 'function') return;
|
||||
|
||||
this.addEventListener(method, handler, {
|
||||
[kForOnEventAttribute]: true
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -448,29 +611,34 @@ module.exports = WebSocket;
|
||||
* Initialize a WebSocket client.
|
||||
*
|
||||
* @param {WebSocket} websocket The client to initialize
|
||||
* @param {(String|url.URL)} address The URL to which to connect
|
||||
* @param {String} [protocols] The subprotocols
|
||||
* @param {(String|URL)} address The URL to which to connect
|
||||
* @param {Array} protocols The subprotocols
|
||||
* @param {Object} [options] Connection options
|
||||
* @param {(Boolean|Object)} [options.perMessageDeflate=true] Enable/disable
|
||||
* permessage-deflate
|
||||
* @param {Number} [options.handshakeTimeout] Timeout in milliseconds for the
|
||||
* handshake request
|
||||
* @param {Number} [options.protocolVersion=13] Value of the
|
||||
* `Sec-WebSocket-Version` header
|
||||
* @param {String} [options.origin] Value of the `Origin` or
|
||||
* `Sec-WebSocket-Origin` header
|
||||
* @param {Number} [options.maxPayload=104857600] The maximum allowed message
|
||||
* size
|
||||
* @param {Boolean} [options.followRedirects=false] Whether or not to follow
|
||||
* redirects
|
||||
* @param {Function} [options.generateMask] The function used to generate the
|
||||
* masking key
|
||||
* @param {Number} [options.handshakeTimeout] Timeout in milliseconds for the
|
||||
* handshake request
|
||||
* @param {Number} [options.maxPayload=104857600] The maximum allowed message
|
||||
* size
|
||||
* @param {Number} [options.maxRedirects=10] The maximum number of redirects
|
||||
* allowed
|
||||
* @param {String} [options.origin] Value of the `Origin` or
|
||||
* `Sec-WebSocket-Origin` header
|
||||
* @param {(Boolean|Object)} [options.perMessageDeflate=true] Enable/disable
|
||||
* permessage-deflate
|
||||
* @param {Number} [options.protocolVersion=13] Value of the
|
||||
* `Sec-WebSocket-Version` header
|
||||
* @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
|
||||
* not to skip UTF-8 validation for text and close messages
|
||||
* @private
|
||||
*/
|
||||
function initAsClient(websocket, address, protocols, options) {
|
||||
const opts = {
|
||||
protocolVersion: protocolVersions[1],
|
||||
maxPayload: 100 * 1024 * 1024,
|
||||
skipUTF8Validation: false,
|
||||
perMessageDeflate: true,
|
||||
followRedirects: false,
|
||||
maxRedirects: 10,
|
||||
@@ -480,7 +648,7 @@ function initAsClient(websocket, address, protocols, options) {
|
||||
hostname: undefined,
|
||||
protocol: undefined,
|
||||
timeout: undefined,
|
||||
method: undefined,
|
||||
method: 'GET',
|
||||
host: undefined,
|
||||
path: undefined,
|
||||
port: undefined
|
||||
@@ -499,21 +667,43 @@ function initAsClient(websocket, address, protocols, options) {
|
||||
parsedUrl = address;
|
||||
websocket._url = address.href;
|
||||
} else {
|
||||
parsedUrl = new URL(address);
|
||||
try {
|
||||
parsedUrl = new URL(address);
|
||||
} catch (e) {
|
||||
throw new SyntaxError(`Invalid URL: ${address}`);
|
||||
}
|
||||
|
||||
websocket._url = address;
|
||||
}
|
||||
|
||||
const isSecure = parsedUrl.protocol === 'wss:';
|
||||
const isUnixSocket = parsedUrl.protocol === 'ws+unix:';
|
||||
let invalidURLMessage;
|
||||
|
||||
if (!parsedUrl.host && (!isUnixSocket || !parsedUrl.pathname)) {
|
||||
throw new Error(`Invalid URL: ${websocket.url}`);
|
||||
if (parsedUrl.protocol !== 'ws:' && !isSecure && !isUnixSocket) {
|
||||
invalidURLMessage =
|
||||
'The URL\'s protocol must be one of "ws:", "wss:", or "ws+unix:"';
|
||||
} else if (isUnixSocket && !parsedUrl.pathname) {
|
||||
invalidURLMessage = "The URL's pathname is empty";
|
||||
} else if (parsedUrl.hash) {
|
||||
invalidURLMessage = 'The URL contains a fragment identifier';
|
||||
}
|
||||
|
||||
if (invalidURLMessage) {
|
||||
const err = new SyntaxError(invalidURLMessage);
|
||||
|
||||
if (websocket._redirects === 0) {
|
||||
throw err;
|
||||
} else {
|
||||
emitErrorAndClose(websocket, err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const isSecure =
|
||||
parsedUrl.protocol === 'wss:' || parsedUrl.protocol === 'https:';
|
||||
const defaultPort = isSecure ? 443 : 80;
|
||||
const key = randomBytes(16).toString('base64');
|
||||
const get = isSecure ? https.get : http.get;
|
||||
const request = isSecure ? https.request : http.request;
|
||||
const protocolSet = new Set();
|
||||
let perMessageDeflate;
|
||||
|
||||
opts.createConnection = isSecure ? tlsConnect : netConnect;
|
||||
@@ -523,11 +713,11 @@ function initAsClient(websocket, address, protocols, options) {
|
||||
? parsedUrl.hostname.slice(1, -1)
|
||||
: parsedUrl.hostname;
|
||||
opts.headers = {
|
||||
...opts.headers,
|
||||
'Sec-WebSocket-Version': opts.protocolVersion,
|
||||
'Sec-WebSocket-Key': key,
|
||||
Connection: 'Upgrade',
|
||||
Upgrade: 'websocket',
|
||||
...opts.headers
|
||||
Upgrade: 'websocket'
|
||||
};
|
||||
opts.path = parsedUrl.pathname + parsedUrl.search;
|
||||
opts.timeout = opts.handshakeTimeout;
|
||||
@@ -542,8 +732,22 @@ function initAsClient(websocket, address, protocols, options) {
|
||||
[PerMessageDeflate.extensionName]: perMessageDeflate.offer()
|
||||
});
|
||||
}
|
||||
if (protocols) {
|
||||
opts.headers['Sec-WebSocket-Protocol'] = protocols;
|
||||
if (protocols.length) {
|
||||
for (const protocol of protocols) {
|
||||
if (
|
||||
typeof protocol !== 'string' ||
|
||||
!subprotocolRegex.test(protocol) ||
|
||||
protocolSet.has(protocol)
|
||||
) {
|
||||
throw new SyntaxError(
|
||||
'An invalid or duplicated subprotocol was specified'
|
||||
);
|
||||
}
|
||||
|
||||
protocolSet.add(protocol);
|
||||
}
|
||||
|
||||
opts.headers['Sec-WebSocket-Protocol'] = protocols.join(',');
|
||||
}
|
||||
if (opts.origin) {
|
||||
if (opts.protocolVersion < 13) {
|
||||
@@ -563,7 +767,79 @@ function initAsClient(websocket, address, protocols, options) {
|
||||
opts.path = parts[1];
|
||||
}
|
||||
|
||||
let req = (websocket._req = get(opts));
|
||||
let req;
|
||||
|
||||
if (opts.followRedirects) {
|
||||
if (websocket._redirects === 0) {
|
||||
websocket._originalUnixSocket = isUnixSocket;
|
||||
websocket._originalSecure = isSecure;
|
||||
websocket._originalHostOrSocketPath = isUnixSocket
|
||||
? opts.socketPath
|
||||
: parsedUrl.host;
|
||||
|
||||
const headers = options && options.headers;
|
||||
|
||||
//
|
||||
// Shallow copy the user provided options so that headers can be changed
|
||||
// without mutating the original object.
|
||||
//
|
||||
options = { ...options, headers: {} };
|
||||
|
||||
if (headers) {
|
||||
for (const [key, value] of Object.entries(headers)) {
|
||||
options.headers[key.toLowerCase()] = value;
|
||||
}
|
||||
}
|
||||
} else if (websocket.listenerCount('redirect') === 0) {
|
||||
const isSameHost = isUnixSocket
|
||||
? websocket._originalUnixSocket
|
||||
? opts.socketPath === websocket._originalHostOrSocketPath
|
||||
: false
|
||||
: websocket._originalUnixSocket
|
||||
? false
|
||||
: parsedUrl.host === websocket._originalHostOrSocketPath;
|
||||
|
||||
if (!isSameHost || (websocket._originalSecure && !isSecure)) {
|
||||
//
|
||||
// Match curl 7.77.0 behavior and drop the following headers. These
|
||||
// headers are also dropped when following a redirect to a subdomain.
|
||||
//
|
||||
delete opts.headers.authorization;
|
||||
delete opts.headers.cookie;
|
||||
|
||||
if (!isSameHost) delete opts.headers.host;
|
||||
|
||||
opts.auth = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Match curl 7.77.0 behavior and make the first `Authorization` header win.
|
||||
// If the `Authorization` header is set, then there is nothing to do as it
|
||||
// will take precedence.
|
||||
//
|
||||
if (opts.auth && !options.headers.authorization) {
|
||||
options.headers.authorization =
|
||||
'Basic ' + Buffer.from(opts.auth).toString('base64');
|
||||
}
|
||||
|
||||
req = websocket._req = request(opts);
|
||||
|
||||
if (websocket._redirects) {
|
||||
//
|
||||
// Unlike what is done for the `'upgrade'` event, no early exit is
|
||||
// triggered here if the user calls `websocket.close()` or
|
||||
// `websocket.terminate()` from a listener of the `'redirect'` event. This
|
||||
// is because the user can also call `request.destroy()` with an error
|
||||
// before calling `websocket.close()` or `websocket.terminate()` and this
|
||||
// would result in an error being emitted on the `request` object with no
|
||||
// `'error'` event listeners attached.
|
||||
//
|
||||
websocket.emit('redirect', websocket.url, req);
|
||||
}
|
||||
} else {
|
||||
req = websocket._req = request(opts);
|
||||
}
|
||||
|
||||
if (opts.timeout) {
|
||||
req.on('timeout', () => {
|
||||
@@ -572,12 +848,10 @@ function initAsClient(websocket, address, protocols, options) {
|
||||
}
|
||||
|
||||
req.on('error', (err) => {
|
||||
if (req === null || req.aborted) return;
|
||||
if (req === null || req[kAborted]) return;
|
||||
|
||||
req = websocket._req = null;
|
||||
websocket._readyState = WebSocket.CLOSING;
|
||||
websocket.emit('error', err);
|
||||
websocket.emitClose();
|
||||
emitErrorAndClose(websocket, err);
|
||||
});
|
||||
|
||||
req.on('response', (res) => {
|
||||
@@ -597,7 +871,15 @@ function initAsClient(websocket, address, protocols, options) {
|
||||
|
||||
req.abort();
|
||||
|
||||
const addr = new URL(location, address);
|
||||
let addr;
|
||||
|
||||
try {
|
||||
addr = new URL(location, address);
|
||||
} catch (e) {
|
||||
const err = new SyntaxError(`Invalid URL: ${location}`);
|
||||
emitErrorAndClose(websocket, err);
|
||||
return;
|
||||
}
|
||||
|
||||
initAsClient(websocket, addr, protocols, options);
|
||||
} else if (!websocket.emit('unexpected-response', req, res)) {
|
||||
@@ -613,13 +895,18 @@ function initAsClient(websocket, address, protocols, options) {
|
||||
websocket.emit('upgrade', res);
|
||||
|
||||
//
|
||||
// The user may have closed the connection from a listener of the `upgrade`
|
||||
// event.
|
||||
// The user may have closed the connection from a listener of the
|
||||
// `'upgrade'` event.
|
||||
//
|
||||
if (websocket.readyState !== WebSocket.CONNECTING) return;
|
||||
|
||||
req = websocket._req = null;
|
||||
|
||||
if (res.headers.upgrade.toLowerCase() !== 'websocket') {
|
||||
abortHandshake(websocket, socket, 'Invalid Upgrade header');
|
||||
return;
|
||||
}
|
||||
|
||||
const digest = createHash('sha1')
|
||||
.update(key + GUID)
|
||||
.digest('base64');
|
||||
@@ -630,15 +917,16 @@ function initAsClient(websocket, address, protocols, options) {
|
||||
}
|
||||
|
||||
const serverProt = res.headers['sec-websocket-protocol'];
|
||||
const protList = (protocols || '').split(/, */);
|
||||
let protError;
|
||||
|
||||
if (!protocols && serverProt) {
|
||||
protError = 'Server sent a subprotocol but none was requested';
|
||||
} else if (protocols && !serverProt) {
|
||||
if (serverProt !== undefined) {
|
||||
if (!protocolSet.size) {
|
||||
protError = 'Server sent a subprotocol but none was requested';
|
||||
} else if (!protocolSet.has(serverProt)) {
|
||||
protError = 'Server sent an invalid subprotocol';
|
||||
}
|
||||
} else if (protocolSet.size) {
|
||||
protError = 'Server sent no subprotocol';
|
||||
} else if (serverProt && !protList.includes(serverProt)) {
|
||||
protError = 'Server sent an invalid subprotocol';
|
||||
}
|
||||
|
||||
if (protError) {
|
||||
@@ -648,27 +936,71 @@ function initAsClient(websocket, address, protocols, options) {
|
||||
|
||||
if (serverProt) websocket._protocol = serverProt;
|
||||
|
||||
if (perMessageDeflate) {
|
||||
try {
|
||||
const extensions = parse(res.headers['sec-websocket-extensions']);
|
||||
const secWebSocketExtensions = res.headers['sec-websocket-extensions'];
|
||||
|
||||
if (extensions[PerMessageDeflate.extensionName]) {
|
||||
perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]);
|
||||
websocket._extensions[PerMessageDeflate.extensionName] =
|
||||
perMessageDeflate;
|
||||
}
|
||||
} catch (err) {
|
||||
abortHandshake(
|
||||
websocket,
|
||||
socket,
|
||||
'Invalid Sec-WebSocket-Extensions header'
|
||||
);
|
||||
if (secWebSocketExtensions !== undefined) {
|
||||
if (!perMessageDeflate) {
|
||||
const message =
|
||||
'Server sent a Sec-WebSocket-Extensions header but no extension ' +
|
||||
'was requested';
|
||||
abortHandshake(websocket, socket, message);
|
||||
return;
|
||||
}
|
||||
|
||||
let extensions;
|
||||
|
||||
try {
|
||||
extensions = parse(secWebSocketExtensions);
|
||||
} catch (err) {
|
||||
const message = 'Invalid Sec-WebSocket-Extensions header';
|
||||
abortHandshake(websocket, socket, message);
|
||||
return;
|
||||
}
|
||||
|
||||
const extensionNames = Object.keys(extensions);
|
||||
|
||||
if (
|
||||
extensionNames.length !== 1 ||
|
||||
extensionNames[0] !== PerMessageDeflate.extensionName
|
||||
) {
|
||||
const message = 'Server indicated an extension that was not requested';
|
||||
abortHandshake(websocket, socket, message);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]);
|
||||
} catch (err) {
|
||||
const message = 'Invalid Sec-WebSocket-Extensions header';
|
||||
abortHandshake(websocket, socket, message);
|
||||
return;
|
||||
}
|
||||
|
||||
websocket._extensions[PerMessageDeflate.extensionName] =
|
||||
perMessageDeflate;
|
||||
}
|
||||
|
||||
websocket.setSocket(socket, head, opts.maxPayload);
|
||||
websocket.setSocket(socket, head, {
|
||||
generateMask: opts.generateMask,
|
||||
maxPayload: opts.maxPayload,
|
||||
skipUTF8Validation: opts.skipUTF8Validation
|
||||
});
|
||||
});
|
||||
|
||||
req.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit the `'error'` and `'close'` events.
|
||||
*
|
||||
* @param {WebSocket} websocket The WebSocket instance
|
||||
* @param {Error} The error to emit
|
||||
* @private
|
||||
*/
|
||||
function emitErrorAndClose(websocket, err) {
|
||||
websocket._readyState = WebSocket.CLOSING;
|
||||
websocket.emit('error', err);
|
||||
websocket.emitClose();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -704,8 +1036,8 @@ function tlsConnect(options) {
|
||||
* Abort the handshake and emit an error.
|
||||
*
|
||||
* @param {WebSocket} websocket The WebSocket instance
|
||||
* @param {(http.ClientRequest|net.Socket)} stream The request to abort or the
|
||||
* socket to destroy
|
||||
* @param {(http.ClientRequest|net.Socket|tls.Socket)} stream The request to
|
||||
* abort or the socket to destroy
|
||||
* @param {String} message The error message
|
||||
* @private
|
||||
*/
|
||||
@@ -716,6 +1048,7 @@ function abortHandshake(websocket, stream, message) {
|
||||
Error.captureStackTrace(err, abortHandshake);
|
||||
|
||||
if (stream.setHeader) {
|
||||
stream[kAborted] = true;
|
||||
stream.abort();
|
||||
|
||||
if (stream.socket && !stream.socket.destroyed) {
|
||||
@@ -727,8 +1060,7 @@ function abortHandshake(websocket, stream, message) {
|
||||
stream.socket.destroy();
|
||||
}
|
||||
|
||||
stream.once('abort', websocket.emitClose.bind(websocket));
|
||||
websocket.emit('error', err);
|
||||
process.nextTick(emitErrorAndClose, websocket, err);
|
||||
} else {
|
||||
stream.destroy(err);
|
||||
stream.once('error', websocket.emit.bind(websocket, 'error'));
|
||||
@@ -772,19 +1104,21 @@ function sendAfterClose(websocket, data, cb) {
|
||||
* The listener of the `Receiver` `'conclude'` event.
|
||||
*
|
||||
* @param {Number} code The status code
|
||||
* @param {String} reason The reason for closing
|
||||
* @param {Buffer} reason The reason for closing
|
||||
* @private
|
||||
*/
|
||||
function receiverOnConclude(code, reason) {
|
||||
const websocket = this[kWebSocket];
|
||||
|
||||
websocket._socket.removeListener('data', socketOnData);
|
||||
websocket._socket.resume();
|
||||
|
||||
websocket._closeFrameReceived = true;
|
||||
websocket._closeMessage = reason;
|
||||
websocket._closeCode = code;
|
||||
|
||||
if (websocket._socket[kWebSocket] === undefined) return;
|
||||
|
||||
websocket._socket.removeListener('data', socketOnData);
|
||||
process.nextTick(resume, websocket._socket);
|
||||
|
||||
if (code === 1005) websocket.close();
|
||||
else websocket.close(code, reason);
|
||||
}
|
||||
@@ -795,7 +1129,9 @@ function receiverOnConclude(code, reason) {
|
||||
* @private
|
||||
*/
|
||||
function receiverOnDrain() {
|
||||
this[kWebSocket]._socket.resume();
|
||||
const websocket = this[kWebSocket];
|
||||
|
||||
if (!websocket.isPaused) websocket._socket.resume();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -807,10 +1143,18 @@ function receiverOnDrain() {
|
||||
function receiverOnError(err) {
|
||||
const websocket = this[kWebSocket];
|
||||
|
||||
websocket._socket.removeListener('data', socketOnData);
|
||||
websocket._socket.resume();
|
||||
if (websocket._socket[kWebSocket] !== undefined) {
|
||||
websocket._socket.removeListener('data', socketOnData);
|
||||
|
||||
//
|
||||
// On Node.js < 14.0.0 the `'error'` event is emitted synchronously. See
|
||||
// https://github.com/websockets/ws/issues/1940.
|
||||
//
|
||||
process.nextTick(resume, websocket._socket);
|
||||
|
||||
websocket.close(err[kStatusCode]);
|
||||
}
|
||||
|
||||
websocket.close(err[kStatusCode]);
|
||||
websocket.emit('error', err);
|
||||
}
|
||||
|
||||
@@ -826,11 +1170,12 @@ function receiverOnFinish() {
|
||||
/**
|
||||
* The listener of the `Receiver` `'message'` event.
|
||||
*
|
||||
* @param {(String|Buffer|ArrayBuffer|Buffer[])} data The message
|
||||
* @param {Buffer|ArrayBuffer|Buffer[])} data The message
|
||||
* @param {Boolean} isBinary Specifies whether the message is binary or not
|
||||
* @private
|
||||
*/
|
||||
function receiverOnMessage(data) {
|
||||
this[kWebSocket].emit('message', data);
|
||||
function receiverOnMessage(data, isBinary) {
|
||||
this[kWebSocket].emit('message', data, isBinary);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -856,6 +1201,16 @@ function receiverOnPong(data) {
|
||||
this[kWebSocket].emit('pong', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume a readable stream
|
||||
*
|
||||
* @param {Readable} stream The readable stream
|
||||
* @private
|
||||
*/
|
||||
function resume(stream) {
|
||||
stream.resume();
|
||||
}
|
||||
|
||||
/**
|
||||
* The listener of the `net.Socket` `'close'` event.
|
||||
*
|
||||
@@ -865,10 +1220,13 @@ function socketOnClose() {
|
||||
const websocket = this[kWebSocket];
|
||||
|
||||
this.removeListener('close', socketOnClose);
|
||||
this.removeListener('data', socketOnData);
|
||||
this.removeListener('end', socketOnEnd);
|
||||
|
||||
websocket._readyState = WebSocket.CLOSING;
|
||||
|
||||
let chunk;
|
||||
|
||||
//
|
||||
// The close frame might not have been received or the `'end'` event emitted,
|
||||
// for example, if the socket was destroyed due to an error. Ensure that the
|
||||
@@ -876,13 +1234,19 @@ function socketOnClose() {
|
||||
// it. If the readable side of the socket is in flowing mode then there is no
|
||||
// buffered data as everything has been already written and `readable.read()`
|
||||
// will return `null`. If instead, the socket is paused, any possible buffered
|
||||
// data will be read as a single chunk and emitted synchronously in a single
|
||||
// `'data'` event.
|
||||
// data will be read as a single chunk.
|
||||
//
|
||||
websocket._socket.read();
|
||||
if (
|
||||
!this._readableState.endEmitted &&
|
||||
!websocket._closeFrameReceived &&
|
||||
!websocket._receiver._writableState.errorEmitted &&
|
||||
(chunk = websocket._socket.read()) !== null
|
||||
) {
|
||||
websocket._receiver.write(chunk);
|
||||
}
|
||||
|
||||
websocket._receiver.end();
|
||||
|
||||
this.removeListener('data', socketOnData);
|
||||
this[kWebSocket] = undefined;
|
||||
|
||||
clearTimeout(websocket._closeTimer);
|
||||
|
35
node_modules/ws/package.json
generated
vendored
35
node_modules/ws/package.json
generated
vendored
@@ -1,26 +1,26 @@
|
||||
{
|
||||
"_from": "ws@^7.4.5",
|
||||
"_id": "ws@7.5.0",
|
||||
"_from": "ws@^8.8.0",
|
||||
"_id": "ws@8.8.1",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw==",
|
||||
"_integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==",
|
||||
"_location": "/ws",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "range",
|
||||
"registry": true,
|
||||
"raw": "ws@^7.4.5",
|
||||
"raw": "ws@^8.8.0",
|
||||
"name": "ws",
|
||||
"escapedName": "ws",
|
||||
"rawSpec": "^7.4.5",
|
||||
"rawSpec": "^8.8.0",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "^7.4.5"
|
||||
"fetchSpec": "^8.8.0"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/jsdom"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/ws/-/ws-7.5.0.tgz",
|
||||
"_shasum": "0033bafea031fb9df041b2026fc72a571ca44691",
|
||||
"_spec": "ws@^7.4.5",
|
||||
"_resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz",
|
||||
"_shasum": "5dbad0feb7ade8ecc99b830c1d77c913d4955ff0",
|
||||
"_spec": "ws@^8.8.0",
|
||||
"_where": "D:\\Projects\\minifyfromhtml\\node_modules\\jsdom",
|
||||
"author": {
|
||||
"name": "Einar Otto Stangvik",
|
||||
@@ -37,21 +37,26 @@
|
||||
"devDependencies": {
|
||||
"benchmark": "^2.1.4",
|
||||
"bufferutil": "^4.0.1",
|
||||
"eslint": "^7.2.0",
|
||||
"eslint": "^8.0.0",
|
||||
"eslint-config-prettier": "^8.1.0",
|
||||
"eslint-plugin-prettier": "^3.0.1",
|
||||
"mocha": "^7.0.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"mocha": "^8.4.0",
|
||||
"nyc": "^15.0.0",
|
||||
"prettier": "^2.0.5",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"exports": {
|
||||
"import": "./wrapper.mjs",
|
||||
"require": "./index.js"
|
||||
},
|
||||
"files": [
|
||||
"browser.js",
|
||||
"index.js",
|
||||
"lib/*.js"
|
||||
"lib/*.js",
|
||||
"wrapper.mjs"
|
||||
],
|
||||
"homepage": "https://github.com/websockets/ws",
|
||||
"keywords": [
|
||||
@@ -86,5 +91,5 @@
|
||||
"lint": "eslint --ignore-path .gitignore . && prettier --check --ignore-path .gitignore \"**/*.{json,md,yaml,yml}\"",
|
||||
"test": "nyc --reporter=lcov --reporter=text mocha --throw-deprecation test/*.test.js"
|
||||
},
|
||||
"version": "7.5.0"
|
||||
"version": "8.8.1"
|
||||
}
|
||||
|
8
node_modules/ws/wrapper.mjs
generated
vendored
Normal file
8
node_modules/ws/wrapper.mjs
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import createWebSocketStream from './lib/stream.js';
|
||||
import Receiver from './lib/receiver.js';
|
||||
import Sender from './lib/sender.js';
|
||||
import WebSocket from './lib/websocket.js';
|
||||
import WebSocketServer from './lib/websocket-server.js';
|
||||
|
||||
export { createWebSocketStream, Receiver, Sender, WebSocket, WebSocketServer };
|
||||
export default WebSocket;
|
Reference in New Issue
Block a user