From: Felix Date: Fri, 15 Nov 2019 01:08:47 +0000 (+0000) Subject: controller and vsorc data viewers done X-Git-Url: https://git.josue.xyz/?a=commitdiff_plain;h=2b1de44527123fab80901384e0f374367500ced8;p=VSoRC%2F.git controller and vsorc data viewers done --- diff --git a/fifo b/fifo index e69de29..6ffbe14 100644 --- a/fifo +++ b/fifo @@ -0,0 +1 @@ +pingall diff --git a/node_modules/express-ws/.babelrc b/node_modules/express-ws/.babelrc new file mode 100644 index 0000000..22e308d --- /dev/null +++ b/node_modules/express-ws/.babelrc @@ -0,0 +1,3 @@ +{ + presets: ["es2015"] +} \ No newline at end of file diff --git a/node_modules/express-ws/.editorconfig b/node_modules/express-ws/.editorconfig new file mode 100644 index 0000000..d2a7f03 --- /dev/null +++ b/node_modules/express-ws/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +indent_size = 4 diff --git a/node_modules/express-ws/.eslintrc b/node_modules/express-ws/.eslintrc new file mode 100644 index 0000000..f644a4b --- /dev/null +++ b/node_modules/express-ws/.eslintrc @@ -0,0 +1,7 @@ +{ + "extends": "airbnb/base", + "rules": { + "comma-dangle": 0, + "no-param-reassign": 0 + } +} diff --git a/node_modules/express-ws/LICENSE b/node_modules/express-ws/LICENSE new file mode 100644 index 0000000..bcd14ff --- /dev/null +++ b/node_modules/express-ws/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2016, Henning Morud +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/node_modules/express-ws/README.md b/node_modules/express-ws/README.md new file mode 100644 index 0000000..9ade18a --- /dev/null +++ b/node_modules/express-ws/README.md @@ -0,0 +1,111 @@ +# express-ws [![Dependency Status](https://snyk.io/test/github/henningm/express-ws/badge.svg)](https://snyk.io/test/github/henningm/express-ws) + +[WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) endpoints for [Express](http://expressjs.com/) applications. Lets you define WebSocket endpoints like any other type of route, and applies regular Express middleware. The WebSocket support is implemented with the help of the [ws](https://github.com/websockets/ws) library. + +## Installation + +`npm install --save express-ws` + +## Usage + +__Full documentation can be found in the API section below. This section only shows a brief example.__ + +Add this line to your Express application: + +```javascript +var expressWs = require('express-ws')(app); +``` + +__Important: Make sure to set up the `express-ws` module like above *before* loading or defining your routers!__ Otherwise, `express-ws` won't get a chance to set up support for Express routers, and you might run into an error along the lines of `router.ws is not a function`. + +After setting up `express-ws`, you will be able to add WebSocket routes (almost) the same way you add other routes. The following snippet sets up a simple echo server at `/echo`. The `ws` parameter is an instance of the WebSocket class described [here](https://github.com/websockets/ws/blob/master/doc/ws.md#class-websocket). + +```javascript +app.ws('/echo', function(ws, req) { + ws.on('message', function(msg) { + ws.send(msg); + }); +}); +``` + +It works with routers, too, this time at `/ws-stuff/echo`: + +```javascript +var router = express.Router(); + +router.ws('/echo', function(ws, req) { + ws.on('message', function(msg) { + ws.send(msg); + }); +}); + +app.use("/ws-stuff", router); +``` + +## Full example + +```javascript +var express = require('express'); +var app = express(); +var expressWs = require('express-ws')(app); + +app.use(function (req, res, next) { + console.log('middleware'); + req.testing = 'testing'; + return next(); +}); + +app.get('/', function(req, res, next){ + console.log('get route', req.testing); + res.end(); +}); + +app.ws('/', function(ws, req) { + ws.on('message', function(msg) { + console.log(msg); + }); + console.log('socket', req.testing); +}); + +app.listen(3000); +``` + +## API + +### expressWs(app, *server*, *options*) + +Sets up `express-ws` on the specified `app`. This will modify the global Router prototype for Express as well - see the `leaveRouterUntouched` option for more information on disabling this. + +* __app__: The Express application to set up `express-ws` on. +* __server__: *Optional.* When using a custom `http.Server`, you should pass it in here, so that `express-ws` can use it to set up the WebSocket upgrade handlers. If you don't specify a `server`, you will only be able to use it with the server that is created automatically when you call `app.listen`. +* __options__: *Optional.* An object containing further options. + * __leaveRouterUntouched:__ Set this to `true` to keep `express-ws` from modifying the Router prototype. You will have to manually `applyTo` every Router that you wish to make `.ws` available on, when this is enabled. + * __wsOptions:__ Options object passed to WebSocketServer constructor. Necessary for any ws specific features. + +This function will return a new `express-ws` API object, which will be referred to as `wsInstance` in the rest of the documentation. + +### wsInstance.app + +This property contains the `app` that `express-ws` was set up on. + +### wsInstance.getWss() + +Returns the underlying WebSocket server/handler. You can use `wsInstance.getWss().clients` to obtain a list of all the connected WebSocket clients for this server. + +Note that this list will include *all* clients, not just those for a specific route - this means that it's often *not* a good idea to use this for broadcasts, for example. + +### wsInstance.applyTo(router) + +Sets up `express-ws` on the given `router` (or other Router-like object). You will only need this in two scenarios: + +1. You have enabled `options.leaveRouterUntouched`, or +2. You are using a custom router that is not based on the express.Router prototype. + +In most cases, you won't need this at all. + +## Development + +This module is written in ES6, and uses Babel for compilation. What this means in practice: + +* The source code lives in the `src/` directory. +* After changing this code, make sure to run `npm run build` to compile it. diff --git a/node_modules/express-ws/examples/broadcast.js b/node_modules/express-ws/examples/broadcast.js new file mode 100755 index 0000000..3d8c606 --- /dev/null +++ b/node_modules/express-ws/examples/broadcast.js @@ -0,0 +1,20 @@ +var express = require('express'); +var expressWs = require('..') + +var expressWs = expressWs(express()); +var app = expressWs.app; + +app.ws('/a', function(ws, req) { +}); +var aWss = expressWs.getWss('/a'); + +app.ws('/b', function(ws, req) { +}); + +setInterval(function () { + aWss.clients.forEach(function (client) { + client.send('hello'); + }); +}, 5000); + +app.listen(3000) diff --git a/node_modules/express-ws/examples/https.js b/node_modules/express-ws/examples/https.js new file mode 100644 index 0000000..f881fda --- /dev/null +++ b/node_modules/express-ws/examples/https.js @@ -0,0 +1,33 @@ +var https = require('https'); +var fs = require('fs'); + +var express = require('express'); +var expressWs = require('..'); + +var options = { + key: fs.readFileSync('key.pem'), + cert: fs.readFileSync('cert.pem') +}; +var app = express(); +var server = https.createServer(options, app); +var expressWs = expressWs(app, server); + +app.use(function (req, res, next) { + console.log('middleware'); + req.testing = 'testing'; + return next(); +}); + +app.get('/', function(req, res, next){ + console.log('get route', req.testing); + res.end(); +}); + +app.ws('/', function(ws, req) { + ws.on('message', function(msg) { + console.log(msg); + }); + console.log('socket', req.testing); +}); + +server.listen(3000) diff --git a/node_modules/express-ws/examples/params.js b/node_modules/express-ws/examples/params.js new file mode 100644 index 0000000..b982a59 --- /dev/null +++ b/node_modules/express-ws/examples/params.js @@ -0,0 +1,26 @@ +var express = require('express'); +var expressWs = require('..'); + +var expressWs = expressWs(express()); +var app = expressWs.app; + +app.param('world', function (req, res, next, world) { + req.world = world || 'world'; + return next(); +}); + +app.get('/hello/:world', function(req, res, next){ + console.log('hello', req.world); + res.end(); + next(); +}); + +app.ws('/hello/:world', function(ws, req, next) { + ws.on('message', function(msg) { + console.log(msg); + }); + console.log('socket hello', req.world); + next(); +}); + +app.listen(3000) diff --git a/node_modules/express-ws/examples/simple.js b/node_modules/express-ws/examples/simple.js new file mode 100755 index 0000000..d4b1abd --- /dev/null +++ b/node_modules/express-ws/examples/simple.js @@ -0,0 +1,25 @@ +var express = require('express'); +var expressWs = require('..'); + +var expressWs = expressWs(express()); +var app = expressWs.app; + +app.use(function (req, res, next) { + console.log('middleware'); + req.testing = 'testing'; + return next(); +}); + +app.get('/', function(req, res, next){ + console.log('get route', req.testing); + res.end(); +}); + +app.ws('/', function(ws, req) { + ws.on('message', function(msg) { + console.log(msg); + }); + console.log('socket', req.testing); +}); + +app.listen(3000) diff --git a/node_modules/express-ws/index.js b/node_modules/express-ws/index.js new file mode 100644 index 0000000..651c499 --- /dev/null +++ b/node_modules/express-ws/index.js @@ -0,0 +1 @@ +module.exports = require("./lib").default; diff --git a/node_modules/express-ws/lib/add-ws-method.js b/node_modules/express-ws/lib/add-ws-method.js new file mode 100644 index 0000000..0ea78ea --- /dev/null +++ b/node_modules/express-ws/lib/add-ws-method.js @@ -0,0 +1,50 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = addWsMethod; + +var _wrapMiddleware = require('./wrap-middleware'); + +var _wrapMiddleware2 = _interopRequireDefault(_wrapMiddleware); + +var _websocketUrl = require('./websocket-url'); + +var _websocketUrl2 = _interopRequireDefault(_websocketUrl); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + +function addWsMethod(target) { + /* This prevents conflict with other things setting `.ws`. */ + if (target.ws === null || target.ws === undefined) { + target.ws = function addWsRoute(route) { + for (var _len = arguments.length, middlewares = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + middlewares[_key - 1] = arguments[_key]; + } + + var wrappedMiddlewares = middlewares.map(_wrapMiddleware2.default); + + /* We append `/.websocket` to the route path here. Why? To prevent conflicts when + * a non-WebSocket request is made to the same GET route - after all, we are only + * interested in handling WebSocket requests. + * + * Whereas the original `express-ws` prefixed this path segment, we suffix it - + * this makes it possible to let requests propagate through Routers like normal, + * which allows us to specify WebSocket routes on Routers as well \o/! */ + var wsRoute = (0, _websocketUrl2.default)(route); + + /* Here we configure our new GET route. It will never get called by a client + * directly, it's just to let our request propagate internally, so that we can + * leave the regular middleware execution and error handling to Express. */ + this.get.apply(this, _toConsumableArray([wsRoute].concat(wrappedMiddlewares))); + + /* + * Return `this` to allow for chaining: + */ + return this; + }; + } +} \ No newline at end of file diff --git a/node_modules/express-ws/lib/index.js b/node_modules/express-ws/lib/index.js new file mode 100644 index 0000000..8e3e696 --- /dev/null +++ b/node_modules/express-ws/lib/index.js @@ -0,0 +1,110 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = expressWs; + +var _http = require('http'); + +var _http2 = _interopRequireDefault(_http); + +var _express = require('express'); + +var _express2 = _interopRequireDefault(_express); + +var _ws = require('ws'); + +var _ws2 = _interopRequireDefault(_ws); + +var _websocketUrl = require('./websocket-url'); + +var _websocketUrl2 = _interopRequireDefault(_websocketUrl); + +var _addWsMethod = require('./add-ws-method'); + +var _addWsMethod2 = _interopRequireDefault(_addWsMethod); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function expressWs(app, httpServer) { + var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + var server = httpServer; + + if (server === null || server === undefined) { + /* No HTTP server was explicitly provided, create one for our Express application. */ + server = _http2.default.createServer(app); + + app.listen = function serverListen() { + var _server; + + return (_server = server).listen.apply(_server, arguments); + }; + } + + /* Make our custom `.ws` method available directly on the Express application. You should + * really be using Routers, though. */ + (0, _addWsMethod2.default)(app); + + /* Monkeypatch our custom `.ws` method into Express' Router prototype. This makes it possible, + * when using the standard Express Router, to use the `.ws` method without any further calls + * to `makeRouter`. When using a custom router, the use of `makeRouter` may still be necessary. + * + * This approach works, because Express does a strange mixin hack - the Router factory + * function is simultaneously the prototype that gets assigned to the resulting Router + * object. */ + if (!options.leaveRouterUntouched) { + (0, _addWsMethod2.default)(_express2.default.Router); + } + + // allow caller to pass in options to WebSocketServer constructor + var wsOptions = options.wsOptions || {}; + wsOptions.server = server; + var wsServer = new _ws2.default.Server(wsOptions); + + wsServer.on('connection', function (socket, request) { + if ('upgradeReq' in socket) { + request = socket.upgradeReq; + } + + request.ws = socket; + request.wsHandled = false; + + /* By setting this fake `.url` on the request, we ensure that it will end up in the fake + * `.get` handler that we defined above - where the wrapper will then unpack the `.ws` + * property, indicate that the WebSocket has been handled, and call the actual handler. */ + request.url = (0, _websocketUrl2.default)(request.url); + + var dummyResponse = new _http2.default.ServerResponse(request); + + dummyResponse.writeHead = function writeHead(statusCode) { + if (statusCode > 200) { + /* Something in the middleware chain signalled an error. */ + dummyResponse._header = ''; + socket.close(); + } + }; + + app.handle(request, dummyResponse, function () { + if (!request.wsHandled) { + /* There was no matching WebSocket-specific route for this request. We'll close + * the connection, as no endpoint was able to handle the request anyway... */ + socket.close(); + } + }); + }); + + return { + app: app, + getWss: function getWss() { + return wsServer; + }, + applyTo: function applyTo(router) { + (0, _addWsMethod2.default)(router); + } + }; +} /* This module does a lot of monkeypatching, but unfortunately that appears to be the only way to + * accomplish this kind of stuff in Express. + * + * Here be dragons. */ \ No newline at end of file diff --git a/node_modules/express-ws/lib/trailing-slash.js b/node_modules/express-ws/lib/trailing-slash.js new file mode 100644 index 0000000..04b1a7f --- /dev/null +++ b/node_modules/express-ws/lib/trailing-slash.js @@ -0,0 +1,13 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = addTrailingSlash; +function addTrailingSlash(string) { + var suffixed = string; + if (suffixed.charAt(suffixed.length - 1) !== '/') { + suffixed = suffixed + '/'; + } + return suffixed; +} \ No newline at end of file diff --git a/node_modules/express-ws/lib/websocket-url.js b/node_modules/express-ws/lib/websocket-url.js new file mode 100644 index 0000000..b77992e --- /dev/null +++ b/node_modules/express-ws/lib/websocket-url.js @@ -0,0 +1,28 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +exports.default = websocketUrl; + +var _trailingSlash = require('./trailing-slash'); + +var _trailingSlash2 = _interopRequireDefault(_trailingSlash); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/* The following fixes HenningM/express-ws#17, correctly. */ +function websocketUrl(url) { + if (url.indexOf('?') !== -1) { + var _url$split = url.split('?'), + _url$split2 = _slicedToArray(_url$split, 2), + baseUrl = _url$split2[0], + query = _url$split2[1]; + + return (0, _trailingSlash2.default)(baseUrl) + '.websocket?' + query; + } + return (0, _trailingSlash2.default)(url) + '.websocket'; +} \ No newline at end of file diff --git a/node_modules/express-ws/lib/wrap-middleware.js b/node_modules/express-ws/lib/wrap-middleware.js new file mode 100644 index 0000000..473c704 --- /dev/null +++ b/node_modules/express-ws/lib/wrap-middleware.js @@ -0,0 +1,23 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = wrapMiddleware; +function wrapMiddleware(middleware) { + return function (req, res, next) { + if (req.ws !== null && req.ws !== undefined) { + req.wsHandled = true; + try { + /* Unpack the `.ws` property and call the actual handler. */ + middleware(req.ws, req, next); + } catch (err) { + /* If an error is thrown, let's send that on to any error handling */ + next(err); + } + } else { + /* This wasn't a WebSocket request, so skip this middleware. */ + next(); + } + }; +} \ No newline at end of file diff --git a/node_modules/express-ws/node_modules/ws/LICENSE b/node_modules/express-ws/node_modules/ws/LICENSE new file mode 100644 index 0000000..a145cd1 --- /dev/null +++ b/node_modules/express-ws/node_modules/ws/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2011 Einar Otto Stangvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/express-ws/node_modules/ws/README.md b/node_modules/express-ws/node_modules/ws/README.md new file mode 100644 index 0000000..3fd9a8c --- /dev/null +++ b/node_modules/express-ws/node_modules/ws/README.md @@ -0,0 +1,417 @@ +# ws: a Node.js WebSocket library + +[![Version npm](https://img.shields.io/npm/v/ws.svg)](https://www.npmjs.com/package/ws) +[![Linux Build](https://img.shields.io/travis/websockets/ws/master.svg)](https://travis-ci.org/websockets/ws) +[![Windows Build](https://ci.appveyor.com/api/projects/status/github/websockets/ws?branch=master&svg=true)](https://ci.appveyor.com/project/lpinca/ws) +[![Coverage Status](https://img.shields.io/coveralls/websockets/ws/master.svg)](https://coveralls.io/r/websockets/ws?branch=master) + +ws is a simple to use, blazing fast, and thoroughly tested WebSocket client +and server implementation. + +Passes the quite extensive Autobahn test suite: [server][server-report], +[client][client-report]. + +**Note**: This module does not work in the browser. The client in the docs is a +reference to a back end with the role of a client in the WebSocket +communication. Browser clients must use the native +[`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) object. +To make the same code work seamlessly on Node.js and the browser, you can use +one of the many wrappers available on npm, like +[isomorphic-ws](https://github.com/heineiuo/isomorphic-ws). + +## Table of Contents + +* [Protocol support](#protocol-support) +* [Installing](#installing) + + [Opt-in for performance and spec compliance](#opt-in-for-performance-and-spec-compliance) +* [API docs](#api-docs) +* [WebSocket compression](#websocket-compression) +* [Usage examples](#usage-examples) + + [Sending and receiving text data](#sending-and-receiving-text-data) + + [Sending binary data](#sending-binary-data) + + [Simple server](#simple-server) + + [External HTTP/S server](#external-https-server) + + [Multiple servers sharing a single HTTP/S server](#multiple-servers-sharing-a-single-https-server) + + [Server broadcast](#server-broadcast) + + [echo.websocket.org demo](#echowebsocketorg-demo) + + [Other examples](#other-examples) +* [Error handling best practices](#error-handling-best-practices) +* [FAQ](#faq) + + [How to get the IP address of the client?](#how-to-get-the-ip-address-of-the-client) + + [How to detect and close broken connections?](#how-to-detect-and-close-broken-connections) + + [How to connect via a proxy?](#how-to-connect-via-a-proxy) +* [Changelog](#changelog) +* [License](#license) + +## Protocol support + +* **HyBi drafts 07-12** (Use the option `protocolVersion: 8`) +* **HyBi drafts 13-17** (Current default, alternatively option `protocolVersion: 13`) + +## Installing + +``` +npm install --save ws +``` + +### Opt-in for performance and spec compliance + +There are 2 optional modules that can be installed along side with the ws +module. These modules are binary addons which improve certain operations. +Prebuilt binaries are available for the most popular platforms so you don't +necessarily need to have a C++ compiler installed on your machine. + +- `npm install --save-optional bufferutil`: Allows to efficiently perform + operations such as masking and unmasking the data payload of the WebSocket + frames. +- `npm install --save-optional utf-8-validate`: Allows to efficiently check + if a message contains valid UTF-8 as required by the spec. + +## API docs + +See [`/doc/ws.md`](./doc/ws.md) for Node.js-like docs for the ws classes. + +## WebSocket compression + +ws supports the [permessage-deflate extension][permessage-deflate] which +enables the client and server to negotiate a compression algorithm and its +parameters, and then selectively apply it to the data payloads of each +WebSocket message. + +The extension is disabled by default on the server and enabled by default on +the client. It adds a significant overhead in terms of performance and memory +consumption so we suggest to enable it only if it is really needed. + +Note that Node.js has a variety of issues with high-performance compression, +where increased concurrency, especially on Linux, can lead to +[catastrophic memory fragmentation][node-zlib-bug] and slow performance. +If you intend to use permessage-deflate in production, it is worthwhile to set +up a test representative of your workload and ensure Node.js/zlib will handle +it with acceptable performance and memory usage. + +Tuning of permessage-deflate can be done via the options defined below. You can +also use `zlibDeflateOptions` and `zlibInflateOptions`, which is passed directly +into the creation of [raw deflate/inflate streams][node-zlib-deflaterawdocs]. + +See [the docs][ws-server-options] for more options. + +```js +const WebSocket = require('ws'); + +const wss = new WebSocket.Server({ + port: 8080, + perMessageDeflate: { + zlibDeflateOptions: { // See zlib defaults. + chunkSize: 1024, + memLevel: 7, + level: 3, + }, + zlibInflateOptions: { + chunkSize: 10 * 1024 + }, + // Other options settable: + clientNoContextTakeover: true, // Defaults to negotiated value. + serverNoContextTakeover: true, // Defaults to negotiated value. + clientMaxWindowBits: 10, // Defaults to negotiated value. + serverMaxWindowBits: 10, // Defaults to negotiated value. + // Below options specified as default values. + concurrencyLimit: 10, // Limits zlib concurrency for perf. + threshold: 1024, // Size (in bytes) below which messages + // should not be compressed. + } +}); +``` + +The client will only use the extension if it is supported and enabled on the +server. To always disable the extension on the client set the +`perMessageDeflate` option to `false`. + +```js +const WebSocket = require('ws'); + +const ws = new WebSocket('ws://www.host.com/path', { + perMessageDeflate: false +}); +``` + +## Usage examples + +### Sending and receiving text data + +```js +const WebSocket = require('ws'); + +const ws = new WebSocket('ws://www.host.com/path'); + +ws.on('open', function open() { + ws.send('something'); +}); + +ws.on('message', function incoming(data) { + console.log(data); +}); +``` + +### Sending binary data + +```js +const WebSocket = require('ws'); + +const ws = new WebSocket('ws://www.host.com/path'); + +ws.on('open', function open() { + const array = new Float32Array(5); + + for (var i = 0; i < array.length; ++i) { + array[i] = i / 2; + } + + ws.send(array); +}); +``` + +### Simple server + +```js +const WebSocket = require('ws'); + +const wss = new WebSocket.Server({ port: 8080 }); + +wss.on('connection', function connection(ws) { + ws.on('message', function incoming(message) { + console.log('received: %s', message); + }); + + ws.send('something'); +}); +``` + +### External HTTP/S server + +```js +const fs = require('fs'); +const https = require('https'); +const WebSocket = require('ws'); + +const server = new https.createServer({ + cert: fs.readFileSync('/path/to/cert.pem'), + key: fs.readFileSync('/path/to/key.pem') +}); +const wss = new WebSocket.Server({ server }); + +wss.on('connection', function connection(ws) { + ws.on('message', function incoming(message) { + console.log('received: %s', message); + }); + + ws.send('something'); +}); + +server.listen(8080); +``` + +### Multiple servers sharing a single HTTP/S server + +```js +const http = require('http'); +const WebSocket = require('ws'); + +const server = http.createServer(); +const wss1 = new WebSocket.Server({ noServer: true }); +const wss2 = new WebSocket.Server({ noServer: true }); + +wss1.on('connection', function connection(ws) { + // ... +}); + +wss2.on('connection', function connection(ws) { + // ... +}); + +server.on('upgrade', function upgrade(request, socket, head) { + const pathname = url.parse(request.url).pathname; + + if (pathname === '/foo') { + wss1.handleUpgrade(request, socket, head, function done(ws) { + wss1.emit('connection', ws, request); + }); + } else if (pathname === '/bar') { + wss2.handleUpgrade(request, socket, head, function done(ws) { + wss2.emit('connection', ws, request); + }); + } else { + socket.destroy(); + } +}); + +server.listen(8080); +``` + +### Server broadcast + +```js +const WebSocket = require('ws'); + +const wss = new WebSocket.Server({ port: 8080 }); + +// Broadcast to all. +wss.broadcast = function broadcast(data) { + wss.clients.forEach(function each(client) { + if (client.readyState === WebSocket.OPEN) { + client.send(data); + } + }); +}; + +wss.on('connection', function connection(ws) { + ws.on('message', function incoming(data) { + // Broadcast to everyone else. + wss.clients.forEach(function each(client) { + if (client !== ws && client.readyState === WebSocket.OPEN) { + client.send(data); + } + }); + }); +}); +``` + +### echo.websocket.org demo + +```js +const WebSocket = require('ws'); + +const ws = new WebSocket('wss://echo.websocket.org/', { + origin: 'https://websocket.org' +}); + +ws.on('open', function open() { + console.log('connected'); + ws.send(Date.now()); +}); + +ws.on('close', function close() { + console.log('disconnected'); +}); + +ws.on('message', function incoming(data) { + console.log(`Roundtrip time: ${Date.now() - data} ms`); + + setTimeout(function timeout() { + ws.send(Date.now()); + }, 500); +}); +``` + +### Other examples + +For a full example with a browser client communicating with a ws server, see the +examples folder. + +Otherwise, see the test cases. + +## Error handling best practices + +```js +// If the WebSocket is closed before the following send is attempted +ws.send('something'); + +// Errors (both immediate and async write errors) can be detected in an optional +// callback. The callback is also the only way of being notified that data has +// actually been sent. +ws.send('something', function ack(error) { + // If error is not defined, the send has been completed, otherwise the error + // object will indicate what failed. +}); + +// Immediate errors can also be handled with `try...catch`, but **note** that +// since sends are inherently asynchronous, socket write failures will *not* be +// captured when this technique is used. +try { ws.send('something'); } +catch (e) { /* handle error */ } +``` + +## FAQ + +### How to get the IP address of the client? + +The remote IP address can be obtained from the raw socket. + +```js +const WebSocket = require('ws'); + +const wss = new WebSocket.Server({ port: 8080 }); + +wss.on('connection', function connection(ws, req) { + const ip = req.connection.remoteAddress; +}); +``` + +When the server runs behind a proxy like NGINX, the de-facto standard is to use +the `X-Forwarded-For` header. + +```js +wss.on('connection', function connection(ws, req) { + const ip = req.headers['x-forwarded-for'].split(/\s*,\s*/)[0]; +}); +``` + +### How to detect and close broken connections? + +Sometimes the link between the server and the client can be interrupted in a +way that keeps both the server and the client unaware of the broken state of the +connection (e.g. when pulling the cord). + +In these cases ping messages can be used as a means to verify that the remote +endpoint is still responsive. + +```js +const WebSocket = require('ws'); + +const wss = new WebSocket.Server({ port: 8080 }); + +function noop() {} + +function heartbeat() { + this.isAlive = true; +} + +wss.on('connection', function connection(ws) { + ws.isAlive = true; + ws.on('pong', heartbeat); +}); + +const interval = setInterval(function ping() { + wss.clients.forEach(function each(ws) { + if (ws.isAlive === false) return ws.terminate(); + + ws.isAlive = false; + ws.ping(noop); + }); +}, 30000); +``` + +Pong messages are automatically sent in response to ping messages as required +by the spec. + +### How to connect via a proxy? + +Use a custom `http.Agent` implementation like [https-proxy-agent][] or +[socks-proxy-agent][]. + +## Changelog + +We're using the GitHub [releases][changelog] for changelog entries. + +## License + +[MIT](LICENSE) + +[https-proxy-agent]: https://github.com/TooTallNate/node-https-proxy-agent +[socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent +[client-report]: http://websockets.github.io/ws/autobahn/clients/ +[server-report]: http://websockets.github.io/ws/autobahn/servers/ +[permessage-deflate]: https://tools.ietf.org/html/rfc7692 +[changelog]: https://github.com/websockets/ws/releases +[node-zlib-bug]: https://github.com/nodejs/node/issues/8871 +[node-zlib-deflaterawdocs]: https://nodejs.org/api/zlib.html#zlib_zlib_createdeflateraw_options +[ws-server-options]: https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback diff --git a/node_modules/express-ws/node_modules/ws/index.js b/node_modules/express-ws/node_modules/ws/index.js new file mode 100644 index 0000000..b8d6be1 --- /dev/null +++ b/node_modules/express-ws/node_modules/ws/index.js @@ -0,0 +1,9 @@ +'use strict'; + +const WebSocket = require('./lib/websocket'); + +WebSocket.Server = require('./lib/websocket-server'); +WebSocket.Receiver = require('./lib/receiver'); +WebSocket.Sender = require('./lib/sender'); + +module.exports = WebSocket; diff --git a/node_modules/express-ws/node_modules/ws/lib/buffer-util.js b/node_modules/express-ws/node_modules/ws/lib/buffer-util.js new file mode 100644 index 0000000..6974dd6 --- /dev/null +++ b/node_modules/express-ws/node_modules/ws/lib/buffer-util.js @@ -0,0 +1,72 @@ +'use strict'; + +/** + * Merges an array of buffers into a new buffer. + * + * @param {Buffer[]} list The array of buffers to concat + * @param {Number} totalLength The total length of buffers in the list + * @return {Buffer} The resulting buffer + * @public + */ +function concat (list, totalLength) { + const target = Buffer.allocUnsafe(totalLength); + var offset = 0; + + for (var i = 0; i < list.length; i++) { + const buf = list[i]; + buf.copy(target, offset); + offset += buf.length; + } + + return target; +} + +/** + * Masks a buffer using the given mask. + * + * @param {Buffer} source The buffer to mask + * @param {Buffer} mask The mask to use + * @param {Buffer} output The buffer where to store the result + * @param {Number} offset The offset at which to start writing + * @param {Number} length The number of bytes to mask. + * @public + */ +function _mask (source, mask, output, offset, length) { + for (var i = 0; i < length; i++) { + output[offset + i] = source[i] ^ mask[i & 3]; + } +} + +/** + * Unmasks a buffer using the given mask. + * + * @param {Buffer} buffer The buffer to unmask + * @param {Buffer} mask The mask to use + * @public + */ +function _unmask (buffer, mask) { + // Required until https://github.com/nodejs/node/issues/9006 is resolved. + const length = buffer.length; + for (var i = 0; i < length; i++) { + buffer[i] ^= mask[i & 3]; + } +} + +try { + const bufferUtil = require('bufferutil'); + const bu = bufferUtil.BufferUtil || bufferUtil; + + module.exports = { + mask (source, mask, output, offset, length) { + if (length < 48) _mask(source, mask, output, offset, length); + else bu.mask(source, mask, output, offset, length); + }, + unmask (buffer, mask) { + if (buffer.length < 32) _unmask(buffer, mask); + else bu.unmask(buffer, mask); + }, + concat + }; +} catch (e) /* istanbul ignore next */ { + module.exports = { concat, mask: _mask, unmask: _unmask }; +} diff --git a/node_modules/express-ws/node_modules/ws/lib/constants.js b/node_modules/express-ws/node_modules/ws/lib/constants.js new file mode 100644 index 0000000..4082981 --- /dev/null +++ b/node_modules/express-ws/node_modules/ws/lib/constants.js @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = { + BINARY_TYPES: ['nodebuffer', 'arraybuffer', 'fragments'], + GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', + kStatusCode: Symbol('status-code'), + kWebSocket: Symbol('websocket'), + EMPTY_BUFFER: Buffer.alloc(0), + NOOP: () => {} +}; diff --git a/node_modules/express-ws/node_modules/ws/lib/event-target.js b/node_modules/express-ws/node_modules/ws/lib/event-target.js new file mode 100644 index 0000000..574e908 --- /dev/null +++ b/node_modules/express-ws/node_modules/ws/lib/event-target.js @@ -0,0 +1,170 @@ +'use strict'; + +/** + * Class representing an event. + * + * @private + */ +class Event { + /** + * Create a new `Event`. + * + * @param {String} type The name of the event + * @param {Object} target A reference to the target to which the event was dispatched + */ + constructor (type, target) { + this.target = target; + this.type = type; + } +} + +/** + * Class representing a message event. + * + * @extends Event + * @private + */ +class MessageEvent extends Event { + /** + * Create a new `MessageEvent`. + * + * @param {(String|Buffer|ArrayBuffer|Buffer[])} data The received data + * @param {WebSocket} target A reference to the target to which the event was dispatched + */ + constructor (data, target) { + super('message', target); + + this.data = data; + } +} + +/** + * Class representing a close event. + * + * @extends Event + * @private + */ +class CloseEvent extends Event { + /** + * Create a new `CloseEvent`. + * + * @param {Number} code The status code explaining why the connection is being closed + * @param {String} reason A human-readable string explaining why the connection is closing + * @param {WebSocket} target A reference to the target to which the event was dispatched + */ + constructor (code, reason, target) { + super('close', target); + + this.wasClean = target._closeFrameReceived && target._closeFrameSent; + this.reason = reason; + this.code = code; + } +} + +/** + * Class representing an open event. + * + * @extends Event + * @private + */ +class OpenEvent extends Event { + /** + * Create a new `OpenEvent`. + * + * @param {WebSocket} target A reference to the target to which the event was dispatched + */ + constructor (target) { + super('open', target); + } +} + +/** + * Class representing an error event. + * + * @extends Event + * @private + */ +class ErrorEvent extends Event { + /** + * Create a new `ErrorEvent`. + * + * @param {Object} error The error that generated this event + * @param {WebSocket} target A reference to the target to which the event was dispatched + */ + constructor (error, target) { + super('error', target); + + this.message = error.message; + this.error = error; + } +} + +/** + * This provides methods for emulating the `EventTarget` interface. It's not + * meant to be used directly. + * + * @mixin + */ +const EventTarget = { + /** + * Register an event listener. + * + * @param {String} method A string representing the event type to listen for + * @param {Function} listener The listener to add + * @public + */ + addEventListener (method, listener) { + if (typeof listener !== 'function') return; + + function onMessage (data) { + listener.call(this, new MessageEvent(data, this)); + } + + function onClose (code, message) { + listener.call(this, new CloseEvent(code, message, this)); + } + + function onError (error) { + listener.call(this, new ErrorEvent(error, this)); + } + + function onOpen () { + listener.call(this, new OpenEvent(this)); + } + + if (method === 'message') { + onMessage._listener = listener; + this.on(method, onMessage); + } else if (method === 'close') { + onClose._listener = listener; + this.on(method, onClose); + } else if (method === 'error') { + onError._listener = listener; + this.on(method, onError); + } else if (method === 'open') { + onOpen._listener = listener; + this.on(method, onOpen); + } else { + this.on(method, listener); + } + }, + + /** + * Remove an event listener. + * + * @param {String} method A string representing the event type to remove + * @param {Function} listener The listener to remove + * @public + */ + removeEventListener (method, listener) { + const listeners = this.listeners(method); + + for (var i = 0; i < listeners.length; i++) { + if (listeners[i] === listener || listeners[i]._listener === listener) { + this.removeListener(method, listeners[i]); + } + } + } +}; + +module.exports = EventTarget; diff --git a/node_modules/express-ws/node_modules/ws/lib/extension.js b/node_modules/express-ws/node_modules/ws/lib/extension.js new file mode 100644 index 0000000..3f48d75 --- /dev/null +++ b/node_modules/express-ws/node_modules/ws/lib/extension.js @@ -0,0 +1,211 @@ +'use strict'; + +// +// Allowed token characters: +// +// '!', '#', '$', '%', '&', ''', '*', '+', '-', +// '.', 0-9, A-Z, '^', '_', '`', a-z, '|', '~' +// +// tokenChars[32] === 0 // ' ' +// tokenChars[33] === 1 // '!' +// tokenChars[34] === 0 // '"' +// ... +// +const tokenChars = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31 + 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32 - 47 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80 - 95 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 // 112 - 127 +]; + +/** + * Adds an offer to the map of extension offers or a parameter to the map of + * parameters. + * + * @param {Object} dest The map of extension offers or parameters + * @param {String} name The extension or parameter name + * @param {(Object|Boolean|String)} elem The extension parameters or the + * parameter value + * @private + */ +function push (dest, name, elem) { + if (Object.prototype.hasOwnProperty.call(dest, name)) dest[name].push(elem); + else dest[name] = [elem]; +} + +/** + * Parses the `Sec-WebSocket-Extensions` header into an object. + * + * @param {String} header The field value of the header + * @return {Object} The parsed object + * @public + */ +function parse (header) { + const offers = {}; + + if (header === undefined || header === '') return offers; + + var params = {}; + var mustUnescape = false; + var isEscaping = false; + var inQuotes = false; + var extensionName; + var paramName; + var start = -1; + var end = -1; + + for (var i = 0; i < header.length; i++) { + const code = header.charCodeAt(i); + + if (extensionName === undefined) { + if (end === -1 && tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (code === 0x20/* ' ' */|| code === 0x09/* '\t' */) { + if (end === -1 && start !== -1) end = i; + } else if (code === 0x3b/* ';' */ || code === 0x2c/* ',' */) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + + if (end === -1) end = i; + const name = header.slice(start, end); + if (code === 0x2c) { + push(offers, name, params); + params = {}; + } else { + extensionName = name; + } + + start = end = -1; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } else if (paramName === undefined) { + if (end === -1 && tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (code === 0x20 || code === 0x09) { + if (end === -1 && start !== -1) end = i; + } else if (code === 0x3b || code === 0x2c) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + + if (end === -1) end = i; + push(params, header.slice(start, end), true); + if (code === 0x2c) { + push(offers, extensionName, params); + params = {}; + extensionName = undefined; + } + + start = end = -1; + } else if (code === 0x3d/* '=' */&& start !== -1 && end === -1) { + paramName = header.slice(start, i); + start = end = -1; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } else { + // + // The value of a quoted-string after unescaping must conform to the + // token ABNF, so only token characters are valid. + // Ref: https://tools.ietf.org/html/rfc6455#section-9.1 + // + if (isEscaping) { + if (tokenChars[code] !== 1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + if (start === -1) start = i; + else if (!mustUnescape) mustUnescape = true; + isEscaping = false; + } else if (inQuotes) { + if (tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (code === 0x22/* '"' */ && start !== -1) { + inQuotes = false; + end = i; + } else if (code === 0x5c/* '\' */) { + isEscaping = true; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } else if (code === 0x22 && header.charCodeAt(i - 1) === 0x3d) { + inQuotes = true; + } else if (end === -1 && tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (start !== -1 && (code === 0x20 || code === 0x09)) { + if (end === -1) end = i; + } else if (code === 0x3b || code === 0x2c) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + + if (end === -1) end = i; + var value = header.slice(start, end); + if (mustUnescape) { + value = value.replace(/\\/g, ''); + mustUnescape = false; + } + push(params, paramName, value); + if (code === 0x2c) { + push(offers, extensionName, params); + params = {}; + extensionName = undefined; + } + + paramName = undefined; + start = end = -1; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } + } + + if (start === -1 || inQuotes) { + throw new SyntaxError('Unexpected end of input'); + } + + if (end === -1) end = i; + const token = header.slice(start, end); + if (extensionName === undefined) { + push(offers, token, {}); + } else { + if (paramName === undefined) { + push(params, token, true); + } else if (mustUnescape) { + push(params, paramName, token.replace(/\\/g, '')); + } else { + push(params, paramName, token); + } + push(offers, extensionName, params); + } + + return offers; +} + +/** + * Builds the `Sec-WebSocket-Extensions` header field value. + * + * @param {Object} extensions The map of extensions and parameters to format + * @return {String} A string representing the given object + * @public + */ +function format (extensions) { + return Object.keys(extensions).map((extension) => { + var configurations = extensions[extension]; + if (!Array.isArray(configurations)) configurations = [configurations]; + return configurations.map((params) => { + return [extension].concat(Object.keys(params).map((k) => { + var values = params[k]; + if (!Array.isArray(values)) values = [values]; + return values.map((v) => v === true ? k : `${k}=${v}`).join('; '); + })).join('; '); + }).join(', '); + }).join(', '); +} + +module.exports = { format, parse }; diff --git a/node_modules/express-ws/node_modules/ws/lib/permessage-deflate.js b/node_modules/express-ws/node_modules/ws/lib/permessage-deflate.js new file mode 100644 index 0000000..0d54f03 --- /dev/null +++ b/node_modules/express-ws/node_modules/ws/lib/permessage-deflate.js @@ -0,0 +1,516 @@ +'use strict'; + +const Limiter = require('async-limiter'); +const zlib = require('zlib'); + +const bufferUtil = require('./buffer-util'); +const constants = require('./constants'); + +const TRAILER = Buffer.from([0x00, 0x00, 0xff, 0xff]); +const EMPTY_BLOCK = Buffer.from([0x00]); + +const kPerMessageDeflate = Symbol('permessage-deflate'); +const kWriteInProgress = Symbol('write-in-progress'); +const kPendingClose = Symbol('pending-close'); +const kTotalLength = Symbol('total-length'); +const kCallback = Symbol('callback'); +const kBuffers = Symbol('buffers'); +const kError = Symbol('error'); + +// +// We limit zlib concurrency, which prevents severe memory fragmentation +// as documented in https://github.com/nodejs/node/issues/8871#issuecomment-250915913 +// and https://github.com/websockets/ws/issues/1202 +// +// Intentionally global; it's the global thread pool that's an issue. +// +let zlibLimiter; + +/** + * permessage-deflate implementation. + */ +class PerMessageDeflate { + /** + * Creates a PerMessageDeflate instance. + * + * @param {Object} options Configuration options + * @param {Boolean} options.serverNoContextTakeover Request/accept disabling + * of server context takeover + * @param {Boolean} options.clientNoContextTakeover Advertise/acknowledge + * disabling of client context takeover + * @param {(Boolean|Number)} options.serverMaxWindowBits Request/confirm the + * use of a custom server window size + * @param {(Boolean|Number)} options.clientMaxWindowBits Advertise support + * for, or request, a custom client window size + * @param {Object} options.zlibDeflateOptions Options to pass to zlib on deflate + * @param {Object} options.zlibInflateOptions Options to pass to zlib on inflate + * @param {Number} options.threshold Size (in bytes) below which messages + * should not be compressed + * @param {Number} options.concurrencyLimit The number of concurrent calls to + * zlib + * @param {Boolean} isServer Create the instance in either server or client + * mode + * @param {Number} maxPayload The maximum allowed message length + */ + constructor (options, isServer, maxPayload) { + this._maxPayload = maxPayload | 0; + this._options = options || {}; + this._threshold = this._options.threshold !== undefined + ? this._options.threshold + : 1024; + this._isServer = !!isServer; + this._deflate = null; + this._inflate = null; + + this.params = null; + + if (!zlibLimiter) { + const concurrency = this._options.concurrencyLimit !== undefined + ? this._options.concurrencyLimit + : 10; + zlibLimiter = new Limiter({ concurrency }); + } + } + + /** + * @type {String} + */ + static get extensionName () { + return 'permessage-deflate'; + } + + /** + * Create an extension negotiation offer. + * + * @return {Object} Extension parameters + * @public + */ + offer () { + const params = {}; + + if (this._options.serverNoContextTakeover) { + params.server_no_context_takeover = true; + } + if (this._options.clientNoContextTakeover) { + params.client_no_context_takeover = true; + } + if (this._options.serverMaxWindowBits) { + params.server_max_window_bits = this._options.serverMaxWindowBits; + } + if (this._options.clientMaxWindowBits) { + params.client_max_window_bits = this._options.clientMaxWindowBits; + } else if (this._options.clientMaxWindowBits == null) { + params.client_max_window_bits = true; + } + + return params; + } + + /** + * Accept an extension negotiation offer/response. + * + * @param {Array} configurations The extension negotiation offers/reponse + * @return {Object} Accepted configuration + * @public + */ + accept (configurations) { + configurations = this.normalizeParams(configurations); + + this.params = this._isServer + ? this.acceptAsServer(configurations) + : this.acceptAsClient(configurations); + + return this.params; + } + + /** + * Releases all resources used by the extension. + * + * @public + */ + cleanup () { + if (this._inflate) { + if (this._inflate[kWriteInProgress]) { + this._inflate[kPendingClose] = true; + } else { + this._inflate.close(); + this._inflate = null; + } + } + if (this._deflate) { + if (this._deflate[kWriteInProgress]) { + this._deflate[kPendingClose] = true; + } else { + this._deflate.close(); + this._deflate = null; + } + } + } + + /** + * Accept an extension negotiation offer. + * + * @param {Array} offers The extension negotiation offers + * @return {Object} Accepted configuration + * @private + */ + acceptAsServer (offers) { + const opts = this._options; + const accepted = offers.find((params) => { + if ( + (opts.serverNoContextTakeover === false && + params.server_no_context_takeover) || + (params.server_max_window_bits && + (opts.serverMaxWindowBits === false || + (typeof opts.serverMaxWindowBits === 'number' && + opts.serverMaxWindowBits > params.server_max_window_bits))) || + (typeof opts.clientMaxWindowBits === 'number' && + !params.client_max_window_bits) + ) { + return false; + } + + return true; + }); + + if (!accepted) { + throw new Error('None of the extension offers can be accepted'); + } + + if (opts.serverNoContextTakeover) { + accepted.server_no_context_takeover = true; + } + if (opts.clientNoContextTakeover) { + accepted.client_no_context_takeover = true; + } + if (typeof opts.serverMaxWindowBits === 'number') { + accepted.server_max_window_bits = opts.serverMaxWindowBits; + } + if (typeof opts.clientMaxWindowBits === 'number') { + accepted.client_max_window_bits = opts.clientMaxWindowBits; + } else if ( + accepted.client_max_window_bits === true || + opts.clientMaxWindowBits === false + ) { + delete accepted.client_max_window_bits; + } + + return accepted; + } + + /** + * Accept the extension negotiation response. + * + * @param {Array} response The extension negotiation response + * @return {Object} Accepted configuration + * @private + */ + acceptAsClient (response) { + const params = response[0]; + + if ( + this._options.clientNoContextTakeover === false && + params.client_no_context_takeover + ) { + throw new Error('Unexpected parameter "client_no_context_takeover"'); + } + + if (!params.client_max_window_bits) { + if (typeof this._options.clientMaxWindowBits === 'number') { + params.client_max_window_bits = this._options.clientMaxWindowBits; + } + } else if ( + this._options.clientMaxWindowBits === false || + (typeof this._options.clientMaxWindowBits === 'number' && + params.client_max_window_bits > this._options.clientMaxWindowBits) + ) { + throw new Error( + 'Unexpected or invalid parameter "client_max_window_bits"' + ); + } + + return params; + } + + /** + * Normalize parameters. + * + * @param {Array} configurations The extension negotiation offers/reponse + * @return {Array} The offers/response with normalized parameters + * @private + */ + normalizeParams (configurations) { + configurations.forEach((params) => { + Object.keys(params).forEach((key) => { + var value = params[key]; + + if (value.length > 1) { + throw new Error(`Parameter "${key}" must have only a single value`); + } + + value = value[0]; + + if (key === 'client_max_window_bits') { + if (value !== true) { + const num = +value; + if (!Number.isInteger(num) || num < 8 || num > 15) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + value = num; + } else if (!this._isServer) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + } else if (key === 'server_max_window_bits') { + const num = +value; + if (!Number.isInteger(num) || num < 8 || num > 15) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + value = num; + } else if ( + key === 'client_no_context_takeover' || + key === 'server_no_context_takeover' + ) { + if (value !== true) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + } else { + throw new Error(`Unknown parameter "${key}"`); + } + + params[key] = value; + }); + }); + + return configurations; + } + + /** + * Decompress data. Concurrency limited by async-limiter. + * + * @param {Buffer} data Compressed data + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @public + */ + decompress (data, fin, callback) { + zlibLimiter.push((done) => { + this._decompress(data, fin, (err, result) => { + done(); + callback(err, result); + }); + }); + } + + /** + * Compress data. Concurrency limited by async-limiter. + * + * @param {Buffer} data Data to compress + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @public + */ + compress (data, fin, callback) { + zlibLimiter.push((done) => { + this._compress(data, fin, (err, result) => { + done(); + callback(err, result); + }); + }); + } + + /** + * Decompress data. + * + * @param {Buffer} data Compressed data + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @private + */ + _decompress (data, fin, callback) { + const endpoint = this._isServer ? 'client' : 'server'; + + if (!this._inflate) { + const key = `${endpoint}_max_window_bits`; + const windowBits = typeof this.params[key] !== 'number' + ? zlib.Z_DEFAULT_WINDOWBITS + : this.params[key]; + + this._inflate = zlib.createInflateRaw( + Object.assign({}, this._options.zlibInflateOptions, { windowBits }) + ); + this._inflate[kPerMessageDeflate] = this; + this._inflate[kTotalLength] = 0; + this._inflate[kBuffers] = []; + this._inflate.on('error', inflateOnError); + this._inflate.on('data', inflateOnData); + } + + this._inflate[kCallback] = callback; + this._inflate[kWriteInProgress] = true; + + this._inflate.write(data); + if (fin) this._inflate.write(TRAILER); + + this._inflate.flush(() => { + const err = this._inflate[kError]; + + if (err) { + this._inflate.close(); + this._inflate = null; + callback(err); + return; + } + + const data = bufferUtil.concat( + this._inflate[kBuffers], + this._inflate[kTotalLength] + ); + + if ( + (fin && this.params[`${endpoint}_no_context_takeover`]) || + this._inflate[kPendingClose] + ) { + this._inflate.close(); + this._inflate = null; + } else { + this._inflate[kWriteInProgress] = false; + this._inflate[kTotalLength] = 0; + this._inflate[kBuffers] = []; + } + + callback(null, data); + }); + } + + /** + * Compress data. + * + * @param {Buffer} data Data to compress + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @private + */ + _compress (data, fin, callback) { + if (!data || data.length === 0) { + process.nextTick(callback, null, EMPTY_BLOCK); + return; + } + + const endpoint = this._isServer ? 'server' : 'client'; + + if (!this._deflate) { + const key = `${endpoint}_max_window_bits`; + const windowBits = typeof this.params[key] !== 'number' + ? zlib.Z_DEFAULT_WINDOWBITS + : this.params[key]; + + this._deflate = zlib.createDeflateRaw( + Object.assign( + // TODO deprecate memLevel/level and recommend zlibDeflateOptions instead + { + memLevel: this._options.memLevel, + level: this._options.level + }, + this._options.zlibDeflateOptions, + { windowBits } + ) + ); + + this._deflate[kTotalLength] = 0; + this._deflate[kBuffers] = []; + + // + // `zlib.DeflateRaw` emits an `'error'` event only when an attempt to use + // it is made after it has already been closed. This cannot happen here, + // so we only add a listener for the `'data'` event. + // + this._deflate.on('data', deflateOnData); + } + + this._deflate[kWriteInProgress] = true; + + this._deflate.write(data); + this._deflate.flush(zlib.Z_SYNC_FLUSH, () => { + var data = bufferUtil.concat( + this._deflate[kBuffers], + this._deflate[kTotalLength] + ); + + if (fin) data = data.slice(0, data.length - 4); + + if ( + (fin && this.params[`${endpoint}_no_context_takeover`]) || + this._deflate[kPendingClose] + ) { + this._deflate.close(); + this._deflate = null; + } else { + this._deflate[kWriteInProgress] = false; + this._deflate[kTotalLength] = 0; + this._deflate[kBuffers] = []; + } + + callback(null, data); + }); + } +} + +module.exports = PerMessageDeflate; + +/** + * The listener of the `zlib.DeflateRaw` stream `'data'` event. + * + * @param {Buffer} chunk A chunk of data + * @private + */ +function deflateOnData (chunk) { + this[kBuffers].push(chunk); + this[kTotalLength] += chunk.length; +} + +/** + * The listener of the `zlib.InflateRaw` stream `'data'` event. + * + * @param {Buffer} chunk A chunk of data + * @private + */ +function inflateOnData (chunk) { + this[kTotalLength] += chunk.length; + + if ( + this[kPerMessageDeflate]._maxPayload < 1 || + this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload + ) { + this[kBuffers].push(chunk); + return; + } + + this[kError] = new RangeError('Max payload size exceeded'); + this[kError][constants.kStatusCode] = 1009; + this.removeListener('data', inflateOnData); + this.reset(); +} + +/** + * The listener of the `zlib.InflateRaw` stream `'error'` event. + * + * @param {Error} err The emitted error + * @private + */ +function inflateOnError (err) { + // + // There is no need to call `Zlib#close()` as the handle is automatically + // closed when an error is emitted. + // + this[kPerMessageDeflate]._inflate = null; + err[constants.kStatusCode] = 1007; + this[kCallback](err); +} diff --git a/node_modules/express-ws/node_modules/ws/lib/receiver.js b/node_modules/express-ws/node_modules/ws/lib/receiver.js new file mode 100644 index 0000000..81dc0bf --- /dev/null +++ b/node_modules/express-ws/node_modules/ws/lib/receiver.js @@ -0,0 +1,513 @@ +'use strict'; + +const stream = require('stream'); + +const PerMessageDeflate = require('./permessage-deflate'); +const bufferUtil = require('./buffer-util'); +const validation = require('./validation'); +const constants = require('./constants'); + +const GET_INFO = 0; +const GET_PAYLOAD_LENGTH_16 = 1; +const GET_PAYLOAD_LENGTH_64 = 2; +const GET_MASK = 3; +const GET_DATA = 4; +const INFLATING = 5; + +/** + * HyBi Receiver implementation. + * + * @extends stream.Writable + */ +class Receiver extends stream.Writable { + /** + * Creates a Receiver instance. + * + * @param {String} binaryType The type for binary data + * @param {Object} extensions An object containing the negotiated extensions + * @param {Number} maxPayload The maximum allowed message length + */ + constructor (binaryType, extensions, maxPayload) { + super(); + + this._binaryType = binaryType || constants.BINARY_TYPES[0]; + this[constants.kWebSocket] = undefined; + this._extensions = extensions || {}; + this._maxPayload = maxPayload | 0; + + this._bufferedBytes = 0; + this._buffers = []; + + this._compressed = false; + this._payloadLength = 0; + this._mask = undefined; + this._fragmented = 0; + this._masked = false; + this._fin = false; + this._opcode = 0; + + this._totalPayloadLength = 0; + this._messageLength = 0; + this._fragments = []; + + this._state = GET_INFO; + this._loop = false; + } + + /** + * Implements `Writable.prototype._write()`. + * + * @param {Buffer} chunk The chunk of data to write + * @param {String} encoding The character encoding of `chunk` + * @param {Function} cb Callback + */ + _write (chunk, encoding, cb) { + if (this._opcode === 0x08) return cb(); + + this._bufferedBytes += chunk.length; + this._buffers.push(chunk); + this.startLoop(cb); + } + + /** + * Consumes `n` bytes from the buffered data. + * + * @param {Number} n The number of bytes to consume + * @return {Buffer} The consumed bytes + * @private + */ + consume (n) { + this._bufferedBytes -= n; + + if (n === this._buffers[0].length) return this._buffers.shift(); + + if (n < this._buffers[0].length) { + const buf = this._buffers[0]; + this._buffers[0] = buf.slice(n); + return buf.slice(0, n); + } + + const dst = Buffer.allocUnsafe(n); + + do { + const buf = this._buffers[0]; + + if (n >= buf.length) { + this._buffers.shift().copy(dst, dst.length - n); + } else { + buf.copy(dst, dst.length - n, 0, n); + this._buffers[0] = buf.slice(n); + } + + n -= buf.length; + } while (n > 0); + + return dst; + } + + /** + * Starts the parsing loop. + * + * @param {Function} cb Callback + * @private + */ + startLoop (cb) { + var err; + this._loop = true; + + do { + switch (this._state) { + case GET_INFO: + err = this.getInfo(); + break; + case GET_PAYLOAD_LENGTH_16: + err = this.getPayloadLength16(); + break; + case GET_PAYLOAD_LENGTH_64: + err = this.getPayloadLength64(); + break; + case GET_MASK: + this.getMask(); + break; + case GET_DATA: + err = this.getData(cb); + break; + default: // `INFLATING` + this._loop = false; + return; + } + } while (this._loop); + + cb(err); + } + + /** + * Reads the first two bytes of a frame. + * + * @return {(RangeError|undefined)} A possible error + * @private + */ + getInfo () { + if (this._bufferedBytes < 2) { + this._loop = false; + return; + } + + const buf = this.consume(2); + + if ((buf[0] & 0x30) !== 0x00) { + this._loop = false; + return error(RangeError, 'RSV2 and RSV3 must be clear', true, 1002); + } + + const compressed = (buf[0] & 0x40) === 0x40; + + if (compressed && !this._extensions[PerMessageDeflate.extensionName]) { + this._loop = false; + return error(RangeError, 'RSV1 must be clear', true, 1002); + } + + this._fin = (buf[0] & 0x80) === 0x80; + this._opcode = buf[0] & 0x0f; + this._payloadLength = buf[1] & 0x7f; + + if (this._opcode === 0x00) { + if (compressed) { + this._loop = false; + return error(RangeError, 'RSV1 must be clear', true, 1002); + } + + if (!this._fragmented) { + this._loop = false; + return error(RangeError, 'invalid opcode 0', true, 1002); + } + + this._opcode = this._fragmented; + } else if (this._opcode === 0x01 || this._opcode === 0x02) { + if (this._fragmented) { + this._loop = false; + return error(RangeError, `invalid opcode ${this._opcode}`, true, 1002); + } + + this._compressed = compressed; + } else if (this._opcode > 0x07 && this._opcode < 0x0b) { + if (!this._fin) { + this._loop = false; + return error(RangeError, 'FIN must be set', true, 1002); + } + + if (compressed) { + this._loop = false; + return error(RangeError, 'RSV1 must be clear', true, 1002); + } + + if (this._payloadLength > 0x7d) { + this._loop = false; + return error( + RangeError, + `invalid payload length ${this._payloadLength}`, + true, + 1002 + ); + } + } else { + this._loop = false; + return error(RangeError, `invalid opcode ${this._opcode}`, true, 1002); + } + + if (!this._fin && !this._fragmented) this._fragmented = this._opcode; + this._masked = (buf[1] & 0x80) === 0x80; + + if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16; + else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64; + else return this.haveLength(); + } + + /** + * Gets extended payload length (7+16). + * + * @return {(RangeError|undefined)} A possible error + * @private + */ + getPayloadLength16 () { + if (this._bufferedBytes < 2) { + this._loop = false; + return; + } + + this._payloadLength = this.consume(2).readUInt16BE(0); + return this.haveLength(); + } + + /** + * Gets extended payload length (7+64). + * + * @return {(RangeError|undefined)} A possible error + * @private + */ + getPayloadLength64 () { + if (this._bufferedBytes < 8) { + this._loop = false; + return; + } + + const buf = this.consume(8); + const num = buf.readUInt32BE(0); + + // + // The maximum safe integer in JavaScript is 2^53 - 1. An error is returned + // if payload length is greater than this number. + // + if (num > Math.pow(2, 53 - 32) - 1) { + this._loop = false; + return error( + RangeError, + 'Unsupported WebSocket frame: payload length > 2^53 - 1', + false, + 1009 + ); + } + + this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4); + return this.haveLength(); + } + + /** + * Payload length has been read. + * + * @return {(RangeError|undefined)} A possible error + * @private + */ + haveLength () { + if (this._payloadLength && this._opcode < 0x08) { + this._totalPayloadLength += this._payloadLength; + if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) { + this._loop = false; + return error(RangeError, 'Max payload size exceeded', false, 1009); + } + } + + if (this._masked) this._state = GET_MASK; + else this._state = GET_DATA; + } + + /** + * Reads mask bytes. + * + * @private + */ + getMask () { + if (this._bufferedBytes < 4) { + this._loop = false; + return; + } + + this._mask = this.consume(4); + this._state = GET_DATA; + } + + /** + * Reads data bytes. + * + * @param {Function} cb Callback + * @return {(Error|RangeError|undefined)} A possible error + * @private + */ + getData (cb) { + var data = constants.EMPTY_BUFFER; + + if (this._payloadLength) { + if (this._bufferedBytes < this._payloadLength) { + this._loop = false; + return; + } + + data = this.consume(this._payloadLength); + if (this._masked) bufferUtil.unmask(data, this._mask); + } + + if (this._opcode > 0x07) return this.controlMessage(data); + + if (this._compressed) { + this._state = INFLATING; + this.decompress(data, cb); + return; + } + + if (data.length) { + // + // This message is not compressed so its lenght is the sum of the payload + // length of all fragments. + // + this._messageLength = this._totalPayloadLength; + this._fragments.push(data); + } + + return this.dataMessage(); + } + + /** + * Decompresses data. + * + * @param {Buffer} data Compressed data + * @param {Function} cb Callback + * @private + */ + decompress (data, cb) { + const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; + + perMessageDeflate.decompress(data, this._fin, (err, buf) => { + if (err) return cb(err); + + if (buf.length) { + this._messageLength += buf.length; + if (this._messageLength > this._maxPayload && this._maxPayload > 0) { + return cb(error(RangeError, 'Max payload size exceeded', false, 1009)); + } + + this._fragments.push(buf); + } + + const er = this.dataMessage(); + if (er) return cb(er); + + this.startLoop(cb); + }); + } + + /** + * Handles a data message. + * + * @return {(Error|undefined)} A possible error + * @private + */ + dataMessage () { + if (this._fin) { + const messageLength = this._messageLength; + const fragments = this._fragments; + + this._totalPayloadLength = 0; + this._messageLength = 0; + this._fragmented = 0; + this._fragments = []; + + if (this._opcode === 2) { + var data; + + if (this._binaryType === 'nodebuffer') { + data = toBuffer(fragments, messageLength); + } else if (this._binaryType === 'arraybuffer') { + data = toArrayBuffer(toBuffer(fragments, messageLength)); + } else { + data = fragments; + } + + this.emit('message', data); + } else { + const buf = toBuffer(fragments, messageLength); + + if (!validation.isValidUTF8(buf)) { + this._loop = false; + return error(Error, 'invalid UTF-8 sequence', true, 1007); + } + + this.emit('message', buf.toString()); + } + } + + this._state = GET_INFO; + } + + /** + * Handles a control message. + * + * @param {Buffer} data Data to handle + * @return {(Error|RangeError|undefined)} A possible error + * @private + */ + controlMessage (data) { + if (this._opcode === 0x08) { + this._loop = false; + + if (data.length === 0) { + this.emit('conclude', 1005, ''); + this.end(); + } else if (data.length === 1) { + return error(RangeError, 'invalid payload length 1', true, 1002); + } else { + const code = data.readUInt16BE(0); + + if (!validation.isValidStatusCode(code)) { + return error(RangeError, `invalid status code ${code}`, true, 1002); + } + + const buf = data.slice(2); + + if (!validation.isValidUTF8(buf)) { + return error(Error, 'invalid UTF-8 sequence', true, 1007); + } + + this.emit('conclude', code, buf.toString()); + this.end(); + } + + return; + } + + if (this._opcode === 0x09) this.emit('ping', data); + else this.emit('pong', data); + + this._state = GET_INFO; + } +} + +module.exports = Receiver; + +/** + * Builds an error object. + * + * @param {(Error|RangeError)} ErrorCtor The error constructor + * @param {String} message The error message + * @param {Boolean} prefix Specifies whether or not to add a default prefix to + * `message` + * @param {Number} statusCode The status code + * @return {(Error|RangeError)} The error + * @private + */ +function error (ErrorCtor, message, prefix, statusCode) { + const err = new ErrorCtor( + prefix ? `Invalid WebSocket frame: ${message}` : message + ); + + Error.captureStackTrace(err, error); + err[constants.kStatusCode] = statusCode; + return err; +} + +/** + * Makes a buffer from a list of fragments. + * + * @param {Buffer[]} fragments The list of fragments composing the message + * @param {Number} messageLength The length of the message + * @return {Buffer} + * @private + */ +function toBuffer (fragments, messageLength) { + if (fragments.length === 1) return fragments[0]; + if (fragments.length > 1) return bufferUtil.concat(fragments, messageLength); + return constants.EMPTY_BUFFER; +} + +/** + * Converts a buffer to an `ArrayBuffer`. + * + * @param {Buffer} The buffer to convert + * @return {ArrayBuffer} Converted buffer + */ +function toArrayBuffer (buf) { + if (buf.byteOffset === 0 && buf.byteLength === buf.buffer.byteLength) { + return buf.buffer; + } + + return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); +} diff --git a/node_modules/express-ws/node_modules/ws/lib/sender.js b/node_modules/express-ws/node_modules/ws/lib/sender.js new file mode 100644 index 0000000..060e553 --- /dev/null +++ b/node_modules/express-ws/node_modules/ws/lib/sender.js @@ -0,0 +1,401 @@ +'use strict'; + +const crypto = require('crypto'); + +const PerMessageDeflate = require('./permessage-deflate'); +const bufferUtil = require('./buffer-util'); +const validation = require('./validation'); +const constants = require('./constants'); + +/** + * HyBi Sender implementation. + */ +class Sender { + /** + * Creates a Sender instance. + * + * @param {net.Socket} socket The connection socket + * @param {Object} extensions An object containing the negotiated extensions + */ + constructor (socket, extensions) { + this._extensions = extensions || {}; + this._socket = socket; + + this._firstFragment = true; + this._compress = false; + + this._bufferedBytes = 0; + this._deflating = false; + this._queue = []; + } + + /** + * Frames a piece of data according to the HyBi WebSocket protocol. + * + * @param {Buffer} data The data to frame + * @param {Object} options Options object + * @param {Number} options.opcode The opcode + * @param {Boolean} options.readOnly Specifies whether `data` can be modified + * @param {Boolean} options.fin Specifies whether or not to set the FIN bit + * @param {Boolean} options.mask Specifies whether or not to mask `data` + * @param {Boolean} options.rsv1 Specifies whether or not to set the RSV1 bit + * @return {Buffer[]} The framed data as a list of `Buffer` instances + * @public + */ + static frame (data, options) { + const merge = data.length < 1024 || (options.mask && options.readOnly); + var offset = options.mask ? 6 : 2; + var payloadLength = data.length; + + if (data.length >= 65536) { + offset += 8; + payloadLength = 127; + } else if (data.length > 125) { + offset += 2; + payloadLength = 126; + } + + const target = Buffer.allocUnsafe(merge ? data.length + offset : offset); + + target[0] = options.fin ? options.opcode | 0x80 : options.opcode; + if (options.rsv1) target[0] |= 0x40; + + if (payloadLength === 126) { + target.writeUInt16BE(data.length, 2); + } else if (payloadLength === 127) { + target.writeUInt32BE(0, 2); + target.writeUInt32BE(data.length, 6); + } + + if (!options.mask) { + target[1] = payloadLength; + if (merge) { + data.copy(target, offset); + return [target]; + } + + return [target, data]; + } + + const mask = crypto.randomBytes(4); + + target[1] = payloadLength | 0x80; + target[offset - 4] = mask[0]; + target[offset - 3] = mask[1]; + target[offset - 2] = mask[2]; + target[offset - 1] = mask[3]; + + if (merge) { + bufferUtil.mask(data, mask, target, offset, data.length); + return [target]; + } + + bufferUtil.mask(data, mask, data, 0, data.length); + return [target, data]; + } + + /** + * Sends a close message to the other peer. + * + * @param {(Number|undefined)} code The status code component of the body + * @param {String} data The message component of the body + * @param {Boolean} mask Specifies whether or not to mask the message + * @param {Function} cb Callback + * @public + */ + close (code, data, mask, cb) { + var buf; + + if (code === undefined) { + buf = constants.EMPTY_BUFFER; + } else if (typeof code !== 'number' || !validation.isValidStatusCode(code)) { + throw new TypeError('First argument must be a valid error code number'); + } else if (data === undefined || data === '') { + buf = Buffer.allocUnsafe(2); + buf.writeUInt16BE(code, 0); + } else { + buf = Buffer.allocUnsafe(2 + Buffer.byteLength(data)); + buf.writeUInt16BE(code, 0); + buf.write(data, 2); + } + + if (this._deflating) { + this.enqueue([this.doClose, buf, mask, cb]); + } else { + this.doClose(buf, mask, cb); + } + } + + /** + * Frames and sends a close message. + * + * @param {Buffer} data The message to send + * @param {Boolean} mask Specifies whether or not to mask `data` + * @param {Function} cb Callback + * @private + */ + doClose (data, mask, cb) { + this.sendFrame(Sender.frame(data, { + fin: true, + rsv1: false, + opcode: 0x08, + mask, + readOnly: false + }), cb); + } + + /** + * Sends a ping message to the other peer. + * + * @param {*} data The message to send + * @param {Boolean} mask Specifies whether or not to mask `data` + * @param {Function} cb Callback + * @public + */ + ping (data, mask, cb) { + var readOnly = true; + + if (!Buffer.isBuffer(data)) { + if (data instanceof ArrayBuffer) { + data = Buffer.from(data); + } else if (ArrayBuffer.isView(data)) { + data = viewToBuffer(data); + } else { + data = Buffer.from(data); + readOnly = false; + } + } + + if (this._deflating) { + this.enqueue([this.doPing, data, mask, readOnly, cb]); + } else { + this.doPing(data, mask, readOnly, cb); + } + } + + /** + * Frames and sends a ping message. + * + * @param {*} data The message to send + * @param {Boolean} mask Specifies whether or not to mask `data` + * @param {Boolean} readOnly Specifies whether `data` can be modified + * @param {Function} cb Callback + * @private + */ + doPing (data, mask, readOnly, cb) { + this.sendFrame(Sender.frame(data, { + fin: true, + rsv1: false, + opcode: 0x09, + mask, + readOnly + }), cb); + } + + /** + * Sends a pong message to the other peer. + * + * @param {*} data The message to send + * @param {Boolean} mask Specifies whether or not to mask `data` + * @param {Function} cb Callback + * @public + */ + pong (data, mask, cb) { + var readOnly = true; + + if (!Buffer.isBuffer(data)) { + if (data instanceof ArrayBuffer) { + data = Buffer.from(data); + } else if (ArrayBuffer.isView(data)) { + data = viewToBuffer(data); + } else { + data = Buffer.from(data); + readOnly = false; + } + } + + if (this._deflating) { + this.enqueue([this.doPong, data, mask, readOnly, cb]); + } else { + this.doPong(data, mask, readOnly, cb); + } + } + + /** + * Frames and sends a pong message. + * + * @param {*} data The message to send + * @param {Boolean} mask Specifies whether or not to mask `data` + * @param {Boolean} readOnly Specifies whether `data` can be modified + * @param {Function} cb Callback + * @private + */ + doPong (data, mask, readOnly, cb) { + this.sendFrame(Sender.frame(data, { + fin: true, + rsv1: false, + opcode: 0x0a, + mask, + readOnly + }), cb); + } + + /** + * Sends a data message to the other peer. + * + * @param {*} data The message to send + * @param {Object} options Options object + * @param {Boolean} options.compress Specifies whether or not to compress `data` + * @param {Boolean} options.binary Specifies whether `data` is binary or text + * @param {Boolean} options.fin Specifies whether the fragment is the last one + * @param {Boolean} options.mask Specifies whether or not to mask `data` + * @param {Function} cb Callback + * @public + */ + send (data, options, cb) { + var opcode = options.binary ? 2 : 1; + var rsv1 = options.compress; + var readOnly = true; + + if (!Buffer.isBuffer(data)) { + if (data instanceof ArrayBuffer) { + data = Buffer.from(data); + } else if (ArrayBuffer.isView(data)) { + data = viewToBuffer(data); + } else { + data = Buffer.from(data); + readOnly = false; + } + } + + const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; + + if (this._firstFragment) { + this._firstFragment = false; + if (rsv1 && perMessageDeflate) { + rsv1 = data.length >= perMessageDeflate._threshold; + } + this._compress = rsv1; + } else { + rsv1 = false; + opcode = 0; + } + + if (options.fin) this._firstFragment = true; + + if (perMessageDeflate) { + const opts = { + fin: options.fin, + rsv1, + opcode, + mask: options.mask, + readOnly + }; + + if (this._deflating) { + this.enqueue([this.dispatch, data, this._compress, opts, cb]); + } else { + this.dispatch(data, this._compress, opts, cb); + } + } else { + this.sendFrame(Sender.frame(data, { + fin: options.fin, + rsv1: false, + opcode, + mask: options.mask, + readOnly + }), cb); + } + } + + /** + * Dispatches a data message. + * + * @param {Buffer} data The message to send + * @param {Boolean} compress Specifies whether or not to compress `data` + * @param {Object} options Options object + * @param {Number} options.opcode The opcode + * @param {Boolean} options.readOnly Specifies whether `data` can be modified + * @param {Boolean} options.fin Specifies whether or not to set the FIN bit + * @param {Boolean} options.mask Specifies whether or not to mask `data` + * @param {Boolean} options.rsv1 Specifies whether or not to set the RSV1 bit + * @param {Function} cb Callback + * @private + */ + dispatch (data, compress, options, cb) { + if (!compress) { + this.sendFrame(Sender.frame(data, options), cb); + return; + } + + const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; + + this._deflating = true; + perMessageDeflate.compress(data, options.fin, (_, buf) => { + options.readOnly = false; + this.sendFrame(Sender.frame(buf, options), cb); + this._deflating = false; + this.dequeue(); + }); + } + + /** + * Executes queued send operations. + * + * @private + */ + dequeue () { + while (!this._deflating && this._queue.length) { + const params = this._queue.shift(); + + this._bufferedBytes -= params[1].length; + params[0].apply(this, params.slice(1)); + } + } + + /** + * Enqueues a send operation. + * + * @param {Array} params Send operation parameters. + * @private + */ + enqueue (params) { + this._bufferedBytes += params[1].length; + this._queue.push(params); + } + + /** + * Sends a frame. + * + * @param {Buffer[]} list The frame to send + * @param {Function} cb Callback + * @private + */ + sendFrame (list, cb) { + if (list.length === 2) { + this._socket.write(list[0]); + this._socket.write(list[1], cb); + } else { + this._socket.write(list[0], cb); + } + } +} + +module.exports = Sender; + +/** + * Converts an `ArrayBuffer` view into a buffer. + * + * @param {(DataView|TypedArray)} view The view to convert + * @return {Buffer} Converted view + * @private + */ +function viewToBuffer (view) { + const buf = Buffer.from(view.buffer); + + if (view.byteLength !== view.buffer.byteLength) { + return buf.slice(view.byteOffset, view.byteOffset + view.byteLength); + } + + return buf; +} diff --git a/node_modules/express-ws/node_modules/ws/lib/validation.js b/node_modules/express-ws/node_modules/ws/lib/validation.js new file mode 100644 index 0000000..06269fc --- /dev/null +++ b/node_modules/express-ws/node_modules/ws/lib/validation.js @@ -0,0 +1,29 @@ +'use strict'; + +try { + const isValidUTF8 = require('utf-8-validate'); + + exports.isValidUTF8 = typeof isValidUTF8 === 'object' + ? isValidUTF8.Validation.isValidUTF8 // utf-8-validate@<3.0.0 + : isValidUTF8; +} catch (e) /* istanbul ignore next */ { + exports.isValidUTF8 = () => true; +} + +/** + * Checks if a status code is allowed in a close frame. + * + * @param {Number} code The status code + * @return {Boolean} `true` if the status code is valid, else `false` + * @public + */ +exports.isValidStatusCode = (code) => { + return ( + (code >= 1000 && + code <= 1013 && + code !== 1004 && + code !== 1005 && + code !== 1006) || + (code >= 3000 && code <= 4999) + ); +}; diff --git a/node_modules/express-ws/node_modules/ws/lib/websocket-server.js b/node_modules/express-ws/node_modules/ws/lib/websocket-server.js new file mode 100644 index 0000000..70513ed --- /dev/null +++ b/node_modules/express-ws/node_modules/ws/lib/websocket-server.js @@ -0,0 +1,357 @@ +'use strict'; + +const EventEmitter = require('events'); +const crypto = require('crypto'); +const http = require('http'); +const url = require('url'); + +const PerMessageDeflate = require('./permessage-deflate'); +const extension = require('./extension'); +const constants = require('./constants'); +const WebSocket = require('./websocket'); + +/** + * Class representing a WebSocket server. + * + * @extends EventEmitter + */ +class WebSocketServer extends EventEmitter { + /** + * Create a `WebSocketServer` instance. + * + * @param {Object} options Configuration options + * @param {String} options.host The hostname where to bind the server + * @param {Number} options.port The port where to bind the server + * @param {http.Server} options.server A pre-created HTTP/S server to use + * @param {Function} options.verifyClient An hook to reject connections + * @param {Function} options.handleProtocols An hook to handle protocols + * @param {String} options.path Accept only connections matching this path + * @param {Boolean} options.noServer Enable no server mode + * @param {Boolean} options.clientTracking Specifies whether or not to track clients + * @param {(Boolean|Object)} options.perMessageDeflate Enable/disable permessage-deflate + * @param {Number} options.maxPayload The maximum allowed message size + * @param {Function} callback A listener for the `listening` event + */ + constructor (options, callback) { + super(); + + options = Object.assign({ + maxPayload: 100 * 1024 * 1024, + perMessageDeflate: false, + handleProtocols: null, + clientTracking: true, + verifyClient: null, + noServer: false, + backlog: null, // use default (511 as implemented in net.js) + server: null, + host: null, + path: null, + port: null + }, options); + + if (options.port == null && !options.server && !options.noServer) { + throw new TypeError( + 'One of the "port", "server", or "noServer" options must be specified' + ); + } + + if (options.port != null) { + this._server = http.createServer((req, res) => { + const body = http.STATUS_CODES[426]; + + res.writeHead(426, { + 'Content-Length': body.length, + 'Content-Type': 'text/plain' + }); + res.end(body); + }); + this._server.listen(options.port, options.host, options.backlog, callback); + } else if (options.server) { + this._server = options.server; + } + + if (this._server) { + this._removeListeners = addListeners(this._server, { + listening: this.emit.bind(this, 'listening'), + error: this.emit.bind(this, 'error'), + upgrade: (req, socket, head) => { + this.handleUpgrade(req, socket, head, (ws) => { + this.emit('connection', ws, req); + }); + } + }); + } + + if (options.perMessageDeflate === true) options.perMessageDeflate = {}; + if (options.clientTracking) this.clients = new Set(); + this.options = options; + } + + /** + * Returns the bound address, the address family name, and port of the server + * as reported by the operating system if listening on an IP socket. + * If the server is listening on a pipe or UNIX domain socket, the name is + * returned as a string. + * + * @return {(Object|String|null)} The address of the server + * @public + */ + address () { + if (this.options.noServer) { + throw new Error('The server is operating in "noServer" mode'); + } + + if (!this._server) return null; + return this._server.address(); + } + + /** + * Close the server. + * + * @param {Function} cb Callback + * @public + */ + close (cb) { + // + // Terminate all associated clients. + // + if (this.clients) { + for (const client of this.clients) client.terminate(); + } + + const server = this._server; + + if (server) { + this._removeListeners(); + this._removeListeners = this._server = null; + + // + // Close the http server if it was internally created. + // + if (this.options.port != null) return server.close(cb); + } + + if (cb) cb(); + } + + /** + * See if a given request should be handled by this server instance. + * + * @param {http.IncomingMessage} req Request object to inspect + * @return {Boolean} `true` if the request is valid, else `false` + * @public + */ + shouldHandle (req) { + if (this.options.path && url.parse(req.url).pathname !== this.options.path) { + return false; + } + + return true; + } + + /** + * Handle a HTTP Upgrade request. + * + * @param {http.IncomingMessage} req The request object + * @param {net.Socket} socket The network socket between the server and client + * @param {Buffer} head The first packet of the upgraded stream + * @param {Function} cb Callback + * @public + */ + handleUpgrade (req, socket, head, cb) { + socket.on('error', socketOnError); + + const version = +req.headers['sec-websocket-version']; + const extensions = {}; + + if ( + req.method !== 'GET' || req.headers.upgrade.toLowerCase() !== 'websocket' || + !req.headers['sec-websocket-key'] || (version !== 8 && version !== 13) || + !this.shouldHandle(req) + ) { + return abortHandshake(socket, 400); + } + + if (this.options.perMessageDeflate) { + const perMessageDeflate = new PerMessageDeflate( + this.options.perMessageDeflate, + true, + this.options.maxPayload + ); + + try { + const offers = extension.parse( + req.headers['sec-websocket-extensions'] + ); + + if (offers[PerMessageDeflate.extensionName]) { + perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]); + extensions[PerMessageDeflate.extensionName] = perMessageDeflate; + } + } catch (err) { + return abortHandshake(socket, 400); + } + } + + // + // Optionally call external client verification handler. + // + if (this.options.verifyClient) { + const info = { + origin: req.headers[`${version === 8 ? 'sec-websocket-origin' : 'origin'}`], + secure: !!(req.connection.authorized || req.connection.encrypted), + req + }; + + if (this.options.verifyClient.length === 2) { + this.options.verifyClient(info, (verified, code, message, headers) => { + if (!verified) { + return abortHandshake(socket, code || 401, message, headers); + } + + this.completeUpgrade(extensions, req, socket, head, cb); + }); + return; + } + + if (!this.options.verifyClient(info)) return abortHandshake(socket, 401); + } + + this.completeUpgrade(extensions, req, socket, head, cb); + } + + /** + * Upgrade the connection to WebSocket. + * + * @param {Object} extensions The accepted extensions + * @param {http.IncomingMessage} req The request object + * @param {net.Socket} socket The network socket between the server and client + * @param {Buffer} head The first packet of the upgraded stream + * @param {Function} cb Callback + * @private + */ + completeUpgrade (extensions, req, socket, head, cb) { + // + // Destroy the socket if the client has already sent a FIN packet. + // + if (!socket.readable || !socket.writable) return socket.destroy(); + + const key = crypto.createHash('sha1') + .update(req.headers['sec-websocket-key'] + constants.GUID, 'binary') + .digest('base64'); + + const headers = [ + 'HTTP/1.1 101 Switching Protocols', + 'Upgrade: websocket', + 'Connection: Upgrade', + `Sec-WebSocket-Accept: ${key}` + ]; + + const ws = new WebSocket(null); + var protocol = req.headers['sec-websocket-protocol']; + + if (protocol) { + protocol = protocol.trim().split(/ *, */); + + // + // Optionally call external protocol selection handler. + // + if (this.options.handleProtocols) { + protocol = this.options.handleProtocols(protocol, req); + } else { + protocol = protocol[0]; + } + + if (protocol) { + headers.push(`Sec-WebSocket-Protocol: ${protocol}`); + ws.protocol = protocol; + } + } + + if (extensions[PerMessageDeflate.extensionName]) { + const params = extensions[PerMessageDeflate.extensionName].params; + const value = extension.format({ + [PerMessageDeflate.extensionName]: [params] + }); + headers.push(`Sec-WebSocket-Extensions: ${value}`); + ws._extensions = extensions; + } + + // + // Allow external modification/inspection of handshake headers. + // + this.emit('headers', headers, req); + + socket.write(headers.concat('\r\n').join('\r\n')); + socket.removeListener('error', socketOnError); + + ws.setSocket(socket, head, this.options.maxPayload); + + if (this.clients) { + this.clients.add(ws); + ws.on('close', () => this.clients.delete(ws)); + } + + cb(ws); + } +} + +module.exports = WebSocketServer; + +/** + * Add event listeners on an `EventEmitter` using a map of + * pairs. + * + * @param {EventEmitter} server The event emitter + * @param {Object.} map The listeners to add + * @return {Function} A function that will remove the added listeners when called + * @private + */ +function addListeners (server, map) { + for (const event of Object.keys(map)) server.on(event, map[event]); + + return function removeListeners () { + for (const event of Object.keys(map)) { + server.removeListener(event, map[event]); + } + }; +} + +/** + * Handle premature socket errors. + * + * @private + */ +function socketOnError () { + this.destroy(); +} + +/** + * Close the connection when preconditions are not fulfilled. + * + * @param {net.Socket} socket The socket of the upgrade request + * @param {Number} code The HTTP response status code + * @param {String} [message] The HTTP response body + * @param {Object} [headers] Additional HTTP response headers + * @private + */ +function abortHandshake (socket, code, message, headers) { + if (socket.writable) { + message = message || http.STATUS_CODES[code]; + headers = Object.assign({ + 'Connection': 'close', + 'Content-type': 'text/html', + 'Content-Length': Buffer.byteLength(message) + }, headers); + + socket.write( + `HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\r\n` + + Object.keys(headers).map(h => `${h}: ${headers[h]}`).join('\r\n') + + '\r\n\r\n' + + message + ); + } + + socket.removeListener('error', socketOnError); + socket.destroy(); +} diff --git a/node_modules/express-ws/node_modules/ws/lib/websocket.js b/node_modules/express-ws/node_modules/ws/lib/websocket.js new file mode 100644 index 0000000..f2bdf0d --- /dev/null +++ b/node_modules/express-ws/node_modules/ws/lib/websocket.js @@ -0,0 +1,828 @@ +'use strict'; + +const EventEmitter = require('events'); +const crypto = require('crypto'); +const https = require('https'); +const http = require('http'); +const net = require('net'); +const tls = require('tls'); +const url = require('url'); + +const PerMessageDeflate = require('./permessage-deflate'); +const EventTarget = require('./event-target'); +const extension = require('./extension'); +const constants = require('./constants'); +const Receiver = require('./receiver'); +const Sender = require('./sender'); + +const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED']; +const kWebSocket = constants.kWebSocket; +const protocolVersions = [8, 13]; +const closeTimeout = 30 * 1000; // Allow 30 seconds to terminate the connection cleanly. + +/** + * Class representing a WebSocket. + * + * @extends EventEmitter + */ +class WebSocket extends EventEmitter { + /** + * Create a new `WebSocket`. + * + * @param {(String|url.Url|url.URL)} address The URL to which to connect + * @param {(String|String[])} protocols The subprotocols + * @param {Object} options Connection options + */ + constructor (address, protocols, options) { + super(); + + this.readyState = WebSocket.CONNECTING; + this.protocol = ''; + + this._binaryType = constants.BINARY_TYPES[0]; + this._closeFrameReceived = false; + this._closeFrameSent = false; + this._closeMessage = ''; + this._closeTimer = null; + this._closeCode = 1006; + this._extensions = {}; + this._isServer = true; + this._receiver = null; + this._sender = null; + this._socket = null; + + if (address !== null) { + if (Array.isArray(protocols)) { + protocols = protocols.join(', '); + } else if (typeof protocols === 'object' && protocols !== null) { + options = protocols; + protocols = undefined; + } + + initAsClient.call(this, address, protocols, options); + } + } + + get CONNECTING () { return WebSocket.CONNECTING; } + get CLOSING () { return WebSocket.CLOSING; } + get CLOSED () { return WebSocket.CLOSED; } + get OPEN () { return WebSocket.OPEN; } + + /** + * This deviates from the WHATWG interface since ws doesn't support the required + * default "blob" type (instead we define a custom "nodebuffer" type). + * + * @type {String} + */ + get binaryType () { + return this._binaryType; + } + + set binaryType (type) { + if (constants.BINARY_TYPES.indexOf(type) < 0) return; + + this._binaryType = type; + + // + // Allow to change `binaryType` on the fly. + // + if (this._receiver) this._receiver._binaryType = type; + } + + /** + * @type {Number} + */ + get bufferedAmount () { + if (!this._socket) return 0; + + // + // `socket.bufferSize` is `undefined` if the socket is closed. + // + return (this._socket.bufferSize || 0) + this._sender._bufferedBytes; + } + + /** + * @type {String} + */ + get extensions () { + return Object.keys(this._extensions).join(); + } + + /** + * Set up the socket and the internal resources. + * + * @param {net.Socket} socket The network socket between the server and client + * @param {Buffer} head The first packet of the upgraded stream + * @param {Number} maxPayload The maximum allowed message size + * @private + */ + setSocket (socket, head, maxPayload) { + const receiver = new Receiver( + this._binaryType, + this._extensions, + maxPayload + ); + + this._sender = new Sender(socket, this._extensions); + this._receiver = receiver; + this._socket = socket; + + receiver[kWebSocket] = this; + socket[kWebSocket] = this; + + receiver.on('conclude', receiverOnConclude); + receiver.on('drain', receiverOnDrain); + receiver.on('error', receiverOnError); + receiver.on('message', receiverOnMessage); + receiver.on('ping', receiverOnPing); + receiver.on('pong', receiverOnPong); + + socket.setTimeout(0); + socket.setNoDelay(); + + if (head.length > 0) socket.unshift(head); + + socket.on('close', socketOnClose); + socket.on('data', socketOnData); + socket.on('end', socketOnEnd); + socket.on('error', socketOnError); + + this.readyState = WebSocket.OPEN; + this.emit('open'); + } + + /** + * Emit the `'close'` event. + * + * @private + */ + emitClose () { + this.readyState = WebSocket.CLOSED; + + if (!this._socket) { + this.emit('close', this._closeCode, this._closeMessage); + return; + } + + if (this._extensions[PerMessageDeflate.extensionName]) { + this._extensions[PerMessageDeflate.extensionName].cleanup(); + } + + this._receiver.removeAllListeners(); + this.emit('close', this._closeCode, this._closeMessage); + } + + /** + * Start a closing handshake. + * + * +----------+ +-----------+ +----------+ + * - - -|ws.close()|-->|close frame|-->|ws.close()|- - - + * | +----------+ +-----------+ +----------+ | + * +----------+ +-----------+ | + * CLOSING |ws.close()|<--|close frame|<--+-----+ CLOSING + * +----------+ +-----------+ | + * | | | +---+ | + * +------------------------+-->|fin| - - - - + * | +---+ | +---+ + * - - - - -|fin|<---------------------+ + * +---+ + * + * @param {Number} code Status code explaining why the connection is closing + * @param {String} data A string explaining why the connection is closing + * @public + */ + close (code, data) { + if (this.readyState === WebSocket.CLOSED) return; + if (this.readyState === WebSocket.CONNECTING) { + const msg = 'WebSocket was closed before the connection was established'; + return abortHandshake(this, this._req, msg); + } + + if (this.readyState === WebSocket.CLOSING) { + if (this._closeFrameSent && this._closeFrameReceived) this._socket.end(); + return; + } + + this.readyState = WebSocket.CLOSING; + this._sender.close(code, data, !this._isServer, (err) => { + // + // This error is handled by the `'error'` listener on the socket. We only + // want to know if the close frame has been sent here. + // + if (err) return; + + this._closeFrameSent = true; + + if (this._socket.writable) { + if (this._closeFrameReceived) this._socket.end(); + + // + // Ensure that the connection is closed even if the closing handshake + // fails. + // + this._closeTimer = setTimeout( + this._socket.destroy.bind(this._socket), + closeTimeout + ); + } + }); + } + + /** + * Send a ping. + * + * @param {*} data The data to send + * @param {Boolean} mask Indicates whether or not to mask `data` + * @param {Function} cb Callback which is executed when the ping is sent + * @public + */ + ping (data, mask, cb) { + if (typeof data === 'function') { + cb = data; + data = mask = undefined; + } else if (typeof mask === 'function') { + cb = mask; + mask = undefined; + } + + if (this.readyState !== WebSocket.OPEN) { + const err = new Error( + `WebSocket is not open: readyState ${this.readyState} ` + + `(${readyStates[this.readyState]})` + ); + + if (cb) return cb(err); + throw err; + } + + if (typeof data === 'number') data = data.toString(); + if (mask === undefined) mask = !this._isServer; + this._sender.ping(data || constants.EMPTY_BUFFER, mask, cb); + } + + /** + * Send a pong. + * + * @param {*} data The data to send + * @param {Boolean} mask Indicates whether or not to mask `data` + * @param {Function} cb Callback which is executed when the pong is sent + * @public + */ + pong (data, mask, cb) { + if (typeof data === 'function') { + cb = data; + data = mask = undefined; + } else if (typeof mask === 'function') { + cb = mask; + mask = undefined; + } + + if (this.readyState !== WebSocket.OPEN) { + const err = new Error( + `WebSocket is not open: readyState ${this.readyState} ` + + `(${readyStates[this.readyState]})` + ); + + if (cb) return cb(err); + throw err; + } + + if (typeof data === 'number') data = data.toString(); + if (mask === undefined) mask = !this._isServer; + this._sender.pong(data || constants.EMPTY_BUFFER, mask, cb); + } + + /** + * Send a data message. + * + * @param {*} data The message to send + * @param {Object} options Options object + * @param {Boolean} options.compress Specifies whether or not to compress `data` + * @param {Boolean} options.binary Specifies whether `data` is binary or text + * @param {Boolean} options.fin Specifies whether the fragment is the last one + * @param {Boolean} options.mask Specifies whether or not to mask `data` + * @param {Function} cb Callback which is executed when data is written out + * @public + */ + send (data, options, cb) { + if (typeof options === 'function') { + cb = options; + options = {}; + } + + if (this.readyState !== WebSocket.OPEN) { + const err = new Error( + `WebSocket is not open: readyState ${this.readyState} ` + + `(${readyStates[this.readyState]})` + ); + + if (cb) return cb(err); + throw err; + } + + if (typeof data === 'number') data = data.toString(); + + const opts = Object.assign({ + binary: typeof data !== 'string', + mask: !this._isServer, + compress: true, + fin: true + }, options); + + if (!this._extensions[PerMessageDeflate.extensionName]) { + opts.compress = false; + } + + this._sender.send(data || constants.EMPTY_BUFFER, opts, cb); + } + + /** + * Forcibly close the connection. + * + * @public + */ + terminate () { + if (this.readyState === WebSocket.CLOSED) return; + if (this.readyState === WebSocket.CONNECTING) { + const msg = 'WebSocket was closed before the connection was established'; + return abortHandshake(this, this._req, msg); + } + + if (this._socket) { + this.readyState = WebSocket.CLOSING; + this._socket.destroy(); + } + } +} + +readyStates.forEach((readyState, i) => { + WebSocket[readyStates[i]] = i; +}); + +// +// Add the `onopen`, `onerror`, `onclose`, and `onmessage` attributes. +// See https://html.spec.whatwg.org/multipage/comms.html#the-websocket-interface +// +['open', 'error', 'close', 'message'].forEach((method) => { + Object.defineProperty(WebSocket.prototype, `on${method}`, { + /** + * Return the listener of the event. + * + * @return {(Function|undefined)} The event listener or `undefined` + * @public + */ + get () { + const listeners = this.listeners(method); + for (var i = 0; i < listeners.length; i++) { + if (listeners[i]._listener) return listeners[i]._listener; + } + }, + /** + * Add a listener for the event. + * + * @param {Function} listener The listener to add + * @public + */ + set (listener) { + const listeners = this.listeners(method); + for (var i = 0; i < listeners.length; i++) { + // + // Remove only the listeners added via `addEventListener`. + // + if (listeners[i]._listener) this.removeListener(method, listeners[i]); + } + this.addEventListener(method, listener); + } + }); +}); + +WebSocket.prototype.addEventListener = EventTarget.addEventListener; +WebSocket.prototype.removeEventListener = EventTarget.removeEventListener; + +module.exports = WebSocket; + +/** + * Initialize a WebSocket client. + * + * @param {(String|url.Url|url.URL)} address The URL to which to connect + * @param {String} protocols The subprotocols + * @param {Object} options Connection options + * @param {(Boolean|Object)} options.perMessageDeflate Enable/disable permessage-deflate + * @param {Number} options.handshakeTimeout Timeout in milliseconds for the handshake request + * @param {Number} options.protocolVersion Value of the `Sec-WebSocket-Version` header + * @param {String} options.origin Value of the `Origin` or `Sec-WebSocket-Origin` header + * @private + */ +function initAsClient (address, protocols, options) { + options = Object.assign({ + protocolVersion: protocolVersions[1], + perMessageDeflate: true + }, options, { + createConnection: undefined, + socketPath: undefined, + hostname: undefined, + protocol: undefined, + timeout: undefined, + method: undefined, + auth: undefined, + host: undefined, + path: undefined, + port: undefined + }); + + if (protocolVersions.indexOf(options.protocolVersion) === -1) { + throw new RangeError( + `Unsupported protocol version: ${options.protocolVersion} ` + + `(supported versions: ${protocolVersions.join(', ')})` + ); + } + + this._isServer = false; + + var parsedUrl; + + if (typeof address === 'object' && address.href !== undefined) { + parsedUrl = address; + this.url = address.href; + } else { + parsedUrl = url.parse(address); + this.url = address; + } + + const isUnixSocket = parsedUrl.protocol === 'ws+unix:'; + + if (!parsedUrl.host && (!isUnixSocket || !parsedUrl.pathname)) { + throw new Error(`Invalid URL: ${this.url}`); + } + + const isSecure = parsedUrl.protocol === 'wss:' || parsedUrl.protocol === 'https:'; + const key = crypto.randomBytes(16).toString('base64'); + const httpObj = isSecure ? https : http; + const path = parsedUrl.search + ? `${parsedUrl.pathname || '/'}${parsedUrl.search}` + : parsedUrl.pathname || '/'; + var perMessageDeflate; + + options.createConnection = isSecure ? tlsConnect : netConnect; + options.port = parsedUrl.port || (isSecure ? 443 : 80); + options.host = parsedUrl.hostname.startsWith('[') + ? parsedUrl.hostname.slice(1, -1) + : parsedUrl.hostname; + options.headers = Object.assign({ + 'Sec-WebSocket-Version': options.protocolVersion, + 'Sec-WebSocket-Key': key, + 'Connection': 'Upgrade', + 'Upgrade': 'websocket' + }, options.headers); + options.path = path; + + if (options.perMessageDeflate) { + perMessageDeflate = new PerMessageDeflate( + options.perMessageDeflate !== true ? options.perMessageDeflate : {}, + false + ); + options.headers['Sec-WebSocket-Extensions'] = extension.format({ + [PerMessageDeflate.extensionName]: perMessageDeflate.offer() + }); + } + if (protocols) { + options.headers['Sec-WebSocket-Protocol'] = protocols; + } + if (options.origin) { + if (options.protocolVersion < 13) { + options.headers['Sec-WebSocket-Origin'] = options.origin; + } else { + options.headers.Origin = options.origin; + } + } + if (parsedUrl.auth) { + options.auth = parsedUrl.auth; + } else if (parsedUrl.username || parsedUrl.password) { + options.auth = `${parsedUrl.username}:${parsedUrl.password}`; + } + + if (isUnixSocket) { + const parts = path.split(':'); + + if (options.agent == null && process.versions.modules < 57) { + // + // Setting `socketPath` in conjunction with `createConnection` without an + // agent throws an error on Node.js < 8. Work around the issue by using a + // different property. + // + options._socketPath = parts[0]; + } else { + options.socketPath = parts[0]; + } + + options.path = parts[1]; + } + + var req = this._req = httpObj.get(options); + + if (options.handshakeTimeout) { + req.setTimeout( + options.handshakeTimeout, + () => abortHandshake(this, req, 'Opening handshake has timed out') + ); + } + + req.on('error', (err) => { + if (this._req.aborted) return; + + req = this._req = null; + this.readyState = WebSocket.CLOSING; + this.emit('error', err); + this.emitClose(); + }); + + req.on('response', (res) => { + if (this.emit('unexpected-response', req, res)) return; + + abortHandshake(this, req, `Unexpected server response: ${res.statusCode}`); + }); + + req.on('upgrade', (res, socket, head) => { + this.emit('upgrade', res); + + // + // The user may have closed the connection from a listener of the `upgrade` + // event. + // + if (this.readyState !== WebSocket.CONNECTING) return; + + req = this._req = null; + + const digest = crypto.createHash('sha1') + .update(key + constants.GUID, 'binary') + .digest('base64'); + + if (res.headers['sec-websocket-accept'] !== digest) { + abortHandshake(this, socket, 'Invalid Sec-WebSocket-Accept header'); + return; + } + + const serverProt = res.headers['sec-websocket-protocol']; + const protList = (protocols || '').split(/, */); + var protError; + + if (!protocols && serverProt) { + protError = 'Server sent a subprotocol but none was requested'; + } else if (protocols && !serverProt) { + protError = 'Server sent no subprotocol'; + } else if (serverProt && protList.indexOf(serverProt) === -1) { + protError = 'Server sent an invalid subprotocol'; + } + + if (protError) { + abortHandshake(this, socket, protError); + return; + } + + if (serverProt) this.protocol = serverProt; + + if (perMessageDeflate) { + try { + const extensions = extension.parse( + res.headers['sec-websocket-extensions'] + ); + + if (extensions[PerMessageDeflate.extensionName]) { + perMessageDeflate.accept( + extensions[PerMessageDeflate.extensionName] + ); + this._extensions[PerMessageDeflate.extensionName] = perMessageDeflate; + } + } catch (err) { + abortHandshake(this, socket, 'Invalid Sec-WebSocket-Extensions header'); + return; + } + } + + this.setSocket(socket, head, 0); + }); +} + +/** + * Create a `net.Socket` and initiate a connection. + * + * @param {Object} options Connection options + * @return {net.Socket} The newly created socket used to start the connection + * @private + */ +function netConnect (options) { + options.path = options.socketPath || options._socketPath || undefined; + return net.connect(options); +} + +/** + * Create a `tls.TLSSocket` and initiate a connection. + * + * @param {Object} options Connection options + * @return {tls.TLSSocket} The newly created socket used to start the connection + * @private + */ +function tlsConnect (options) { + options.path = options.socketPath || options._socketPath || undefined; + options.servername = options.servername || options.host; + return tls.connect(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 {String} message The error message + * @private + */ +function abortHandshake (websocket, stream, message) { + websocket.readyState = WebSocket.CLOSING; + + const err = new Error(message); + Error.captureStackTrace(err, abortHandshake); + + if (stream.setHeader) { + stream.abort(); + stream.once('abort', websocket.emitClose.bind(websocket)); + websocket.emit('error', err); + } else { + stream.destroy(err); + stream.once('error', websocket.emit.bind(websocket, 'error')); + stream.once('close', websocket.emitClose.bind(websocket)); + } +} + +/** + * The listener of the `Receiver` `'conclude'` event. + * + * @param {Number} code The status code + * @param {String} 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 (code === 1005) websocket.close(); + else websocket.close(code, reason); +} + +/** + * The listener of the `Receiver` `'drain'` event. + * + * @private + */ +function receiverOnDrain () { + this[kWebSocket]._socket.resume(); +} + +/** + * The listener of the `Receiver` `'error'` event. + * + * @param {(RangeError|Error)} err The emitted error + * @private + */ +function receiverOnError (err) { + const websocket = this[kWebSocket]; + + websocket._socket.removeListener('data', socketOnData); + + websocket.readyState = WebSocket.CLOSING; + websocket._closeCode = err[constants.kStatusCode]; + websocket.emit('error', err); + websocket._socket.destroy(); +} + +/** + * The listener of the `Receiver` `'finish'` event. + * + * @private + */ +function receiverOnFinish () { + this[kWebSocket].emitClose(); +} + +/** + * The listener of the `Receiver` `'message'` event. + * + * @param {(String|Buffer|ArrayBuffer|Buffer[])} data The message + * @private + */ +function receiverOnMessage (data) { + this[kWebSocket].emit('message', data); +} + +/** + * The listener of the `Receiver` `'ping'` event. + * + * @param {Buffer} data The data included in the ping frame + * @private + */ +function receiverOnPing (data) { + const websocket = this[kWebSocket]; + + websocket.pong(data, !websocket._isServer, constants.NOOP); + websocket.emit('ping', data); +} + +/** + * The listener of the `Receiver` `'pong'` event. + * + * @param {Buffer} data The data included in the pong frame + * @private + */ +function receiverOnPong (data) { + this[kWebSocket].emit('pong', data); +} + +/** + * The listener of the `net.Socket` `'close'` event. + * + * @private + */ +function socketOnClose () { + const websocket = this[kWebSocket]; + + this.removeListener('close', socketOnClose); + this.removeListener('end', socketOnEnd); + + websocket.readyState = WebSocket.CLOSING; + + // + // 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 + // `receiver` stream is closed after writing any remaining buffered data to + // 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. + // + websocket._socket.read(); + websocket._receiver.end(); + + this.removeListener('data', socketOnData); + this[kWebSocket] = undefined; + + clearTimeout(websocket._closeTimer); + + if ( + websocket._receiver._writableState.finished || + websocket._receiver._writableState.errorEmitted + ) { + websocket.emitClose(); + } else { + websocket._receiver.on('error', receiverOnFinish); + websocket._receiver.on('finish', receiverOnFinish); + } +} + +/** + * The listener of the `net.Socket` `'data'` event. + * + * @param {Buffer} chunk A chunk of data + * @private + */ +function socketOnData (chunk) { + if (!this[kWebSocket]._receiver.write(chunk)) { + this.pause(); + } +} + +/** + * The listener of the `net.Socket` `'end'` event. + * + * @private + */ +function socketOnEnd () { + const websocket = this[kWebSocket]; + + websocket.readyState = WebSocket.CLOSING; + websocket._receiver.end(); + this.end(); +} + +/** + * The listener of the `net.Socket` `'error'` event. + * + * @private + */ +function socketOnError () { + const websocket = this[kWebSocket]; + + this.removeListener('error', socketOnError); + this.on('error', constants.NOOP); + + if (websocket) { + websocket.readyState = WebSocket.CLOSING; + this.destroy(); + } +} diff --git a/node_modules/express-ws/node_modules/ws/node_modules/async-limiter/.eslintignore b/node_modules/express-ws/node_modules/ws/node_modules/async-limiter/.eslintignore new file mode 100644 index 0000000..e1661e5 --- /dev/null +++ b/node_modules/express-ws/node_modules/ws/node_modules/async-limiter/.eslintignore @@ -0,0 +1,2 @@ +coverage +.nyc_output \ No newline at end of file diff --git a/node_modules/express-ws/node_modules/ws/node_modules/async-limiter/.nycrc b/node_modules/express-ws/node_modules/ws/node_modules/async-limiter/.nycrc new file mode 100644 index 0000000..874c1de --- /dev/null +++ b/node_modules/express-ws/node_modules/ws/node_modules/async-limiter/.nycrc @@ -0,0 +1,10 @@ +{ + "check-coverage": false, + "lines": 99, + "statements": 99, + "functions": 99, + "branches": 99, + "include": [ + "index.js" + ] +} \ No newline at end of file diff --git a/node_modules/express-ws/node_modules/ws/node_modules/async-limiter/.travis.yml b/node_modules/express-ws/node_modules/ws/node_modules/async-limiter/.travis.yml new file mode 100644 index 0000000..37026e2 --- /dev/null +++ b/node_modules/express-ws/node_modules/ws/node_modules/async-limiter/.travis.yml @@ -0,0 +1,9 @@ +language: node_js +node_js: + - "6" + - "8" + - "10" + - "node" +script: npm run travis +cache: + yarn: true diff --git a/node_modules/express-ws/node_modules/ws/node_modules/async-limiter/LICENSE b/node_modules/express-ws/node_modules/ws/node_modules/async-limiter/LICENSE new file mode 100644 index 0000000..9c91fb2 --- /dev/null +++ b/node_modules/express-ws/node_modules/ws/node_modules/async-limiter/LICENSE @@ -0,0 +1,8 @@ +The MIT License (MIT) +Copyright (c) 2017 Samuel Reed + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/express-ws/node_modules/ws/node_modules/async-limiter/index.js b/node_modules/express-ws/node_modules/ws/node_modules/async-limiter/index.js new file mode 100644 index 0000000..c9bd2f9 --- /dev/null +++ b/node_modules/express-ws/node_modules/ws/node_modules/async-limiter/index.js @@ -0,0 +1,67 @@ +'use strict'; + +function Queue(options) { + if (!(this instanceof Queue)) { + return new Queue(options); + } + + options = options || {}; + this.concurrency = options.concurrency || Infinity; + this.pending = 0; + this.jobs = []; + this.cbs = []; + this._done = done.bind(this); +} + +var arrayAddMethods = [ + 'push', + 'unshift', + 'splice' +]; + +arrayAddMethods.forEach(function(method) { + Queue.prototype[method] = function() { + var methodResult = Array.prototype[method].apply(this.jobs, arguments); + this._run(); + return methodResult; + }; +}); + +Object.defineProperty(Queue.prototype, 'length', { + get: function() { + return this.pending + this.jobs.length; + } +}); + +Queue.prototype._run = function() { + if (this.pending === this.concurrency) { + return; + } + if (this.jobs.length) { + var job = this.jobs.shift(); + this.pending++; + job(this._done); + this._run(); + } + + if (this.pending === 0) { + while (this.cbs.length !== 0) { + var cb = this.cbs.pop(); + process.nextTick(cb); + } + } +}; + +Queue.prototype.onDone = function(cb) { + if (typeof cb === 'function') { + this.cbs.push(cb); + this._run(); + } +}; + +function done() { + this.pending--; + this._run(); +} + +module.exports = Queue; diff --git a/node_modules/express-ws/node_modules/ws/node_modules/async-limiter/package.json b/node_modules/express-ws/node_modules/ws/node_modules/async-limiter/package.json new file mode 100644 index 0000000..da445ff --- /dev/null +++ b/node_modules/express-ws/node_modules/ws/node_modules/async-limiter/package.json @@ -0,0 +1,76 @@ +{ + "name": "async-limiter", + "version": "1.0.1", + "description": "asynchronous function queue with adjustable concurrency", + "keywords": [ + "throttle", + "async", + "limiter", + "asynchronous", + "job", + "task", + "concurrency", + "concurrent" + ], + "dependencies": {}, + "devDependencies": { + "coveralls": "^3.0.3", + "eslint": "^5.16.0", + "eslint-plugin-mocha": "^5.3.0", + "intelli-espower-loader": "^1.0.1", + "mocha": "^6.1.4", + "nyc": "^14.1.1", + "power-assert": "^1.6.1" + }, + "scripts": { + "test": "mocha --require intelli-espower-loader test/", + "travis": "npm run lint && npm run test", + "coverage": "nyc npm test && nyc report --reporter=text-lcov | coveralls", + "example": "node example", + "lint": "eslint ." + }, + "repository": { + "type": "git", + "url": "git+https://github.com/strml/async-limiter.git" + }, + "author": { + "name": "Samuel Reed" + }, + "license": "MIT", + "gitHead": "f3bb66f26e69a5747a6483e32c775a02632020ee", + "bugs": { + "url": "https://github.com/strml/async-limiter/issues" + }, + "homepage": "https://github.com/strml/async-limiter#readme", + "_id": "async-limiter@1.0.1", + "_nodeVersion": "10.16.0", + "_npmVersion": "6.9.0", + "dist": { + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "shasum": "dd379e94f0db8310b08291f9d64c3209766617fd", + "tarball": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "fileCount": 7, + "unpackedSize": 6900, + "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdRFo5CRA9TVsSAnZWagAAE8EP/AoamQTvsA8uUcSUKc4L\nL7rKbbH4m5Cv1Z7qeBXLV3KJHI+dhn/mKU2hOpnXHgks5Az4ELlOX9O1vo9j\nLYtN8ZMGEkMIx+k7OcVexaXLcK9ALliEMNoNy4cIVc+exBS4eKFPmaEx5DmD\nNf+eCG6jkA9WY/kYSmFnus7C0B7d2PMdmtBZKdzWya9PAB5BYEoz3/GYhJZG\nEFYHmWKtMDB6LMSZ0FSXwABV6QXWn5kk3fXaPX1NtMHLw+QCT/sWt+0cOnIE\nak2s8WOry7Fsx5wXQmKbd8854LC+yVT1f7RR7eBhKAlTk74nwfNDr84UBJIr\n+0G0RdgISOzLghtRFu3SqYKynXTjdlycZG9vvcHW9oPGI2ZiC2cHuiqc4+K7\ndYX1HGQICjflTmb+RR0vGNXiy3v6YBWgpItdeziPO2K+0uN6SJr1BidQ8oKI\nd49psu/xNvMhdwOo19+/Bt7n7nT4uzej8K7uQO81BJC0ITeNfaC/z9M/4VOg\nFuixwvvzfs+/RABxzXKZqOMVlAnAb4U/PBcliklyUBeZ62PDkqnBxdrOekf5\nacstUU3K5bAaBV8taKHEa1+tqYUjVEcaolDDKgmO0dxD9FlKAMlhck9ildO7\nnjODiNgcSMUlMmHGUZCEvjSt1YptntzC0DHwxWUjszaR4p0Iz0c0AyOYGH7T\nRewy\r\n=MPQY\r\n-----END PGP SIGNATURE-----\r\n" + }, + "maintainers": [ + { + "name": "strml", + "email": "samuel.trace.reed@gmail.com" + } + ], + "_npmUser": { + "name": "strml", + "email": "samuel.trace.reed@gmail.com" + }, + "directories": {}, + "_npmOperationalInternal": { + "host": "s3://npm-registry-packages", + "tmp": "tmp/async-limiter_1.0.1_1564760633070_0.6974331182093105" + }, + "_hasShrinkwrap": false, + "_shasum": "dd379e94f0db8310b08291f9d64c3209766617fd", + "_from": "async-limiter@~1.0.0", + "_resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/express-ws/node_modules/ws/node_modules/async-limiter/readme.md b/node_modules/express-ws/node_modules/ws/node_modules/async-limiter/readme.md new file mode 100644 index 0000000..fcaa22f --- /dev/null +++ b/node_modules/express-ws/node_modules/ws/node_modules/async-limiter/readme.md @@ -0,0 +1,132 @@ +# Async-Limiter + +A module for limiting concurrent asynchronous actions in flight. Forked from [queue](https://github.com/jessetane/queue). + +[![npm](http://img.shields.io/npm/v/async-limiter.svg?style=flat-square)](http://www.npmjs.org/async-limiter) +[![tests](https://img.shields.io/travis/STRML/async-limiter.svg?style=flat-square&branch=master)](https://travis-ci.org/STRML/async-limiter) +[![coverage](https://img.shields.io/coveralls/STRML/async-limiter.svg?style=flat-square&branch=master)](https://coveralls.io/r/STRML/async-limiter) + +This module exports a class `Limiter` that implements some of the `Array` API. +Pass async functions (ones that accept a callback or return a promise) to an instance's additive array methods. + +## Motivation + +Certain functions, like `zlib`, have [undesirable behavior](https://github.com/nodejs/node/issues/8871#issuecomment-250915913) when +run at infinite concurrency. + +In this case, it is actually faster, and takes far less memory, to limit concurrency. + +This module should do the absolute minimum work necessary to queue up functions. PRs are welcome that would +make this module faster or lighter, but new functionality is not desired. + +Style should confirm to nodejs/node style. + +## Example + +``` javascript +var Limiter = require('async-limiter') + +var t = new Limiter({concurrency: 2}); +var results = [] + +// add jobs using the familiar Array API +t.push(function (cb) { + results.push('two') + cb() +}) + +t.push( + function (cb) { + results.push('four') + cb() + }, + function (cb) { + results.push('five') + cb() + } +) + +t.unshift(function (cb) { + results.push('one') + cb() +}) + +t.splice(2, 0, function (cb) { + results.push('three') + cb() +}) + +// Jobs run automatically. If you want a callback when all are done, +// call 'onDone()'. +t.onDone(function () { + console.log('all done:', results) +}) +``` + +## Zlib Example + +```js +const zlib = require('zlib'); +const Limiter = require('async-limiter'); + +const message = {some: "data"}; +const payload = new Buffer(JSON.stringify(message)); + +// Try with different concurrency values to see how this actually +// slows significantly with higher concurrency! +// +// 5: 1398.607ms +// 10: 1375.668ms +// Infinity: 4423.300ms +// +const t = new Limiter({concurrency: 5}); +function deflate(payload, cb) { + t.push(function(done) { + zlib.deflate(payload, function(err, buffer) { + done(); + cb(err, buffer); + }); + }); +} + +console.time('deflate'); +for(let i = 0; i < 30000; ++i) { + deflate(payload, function (err, buffer) {}); +} +t.onDone(function() { + console.timeEnd('deflate'); +}); +``` + +## Install + +`npm install async-limiter` + +## Test + +`npm test` + +## API + +### `var t = new Limiter([opts])` +Constructor. `opts` may contain inital values for: +* `t.concurrency` + +## Instance methods + +### `t.onDone(fn)` +`fn` will be called once and only once, when the queue is empty. + +## Instance methods mixed in from `Array` +Mozilla has docs on how these methods work [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array). +### `t.push(element1, ..., elementN)` +### `t.unshift(element1, ..., elementN)` +### `t.splice(index , howMany[, element1[, ...[, elementN]]])` + +## Properties +### `t.concurrency` +Max number of jobs the queue should process concurrently, defaults to `Infinity`. + +### `t.length` +Jobs pending + jobs to process (readonly). + diff --git a/node_modules/express-ws/node_modules/ws/package.json b/node_modules/express-ws/node_modules/ws/package.json new file mode 100644 index 0000000..ac4ae73 --- /dev/null +++ b/node_modules/express-ws/node_modules/ws/package.json @@ -0,0 +1,97 @@ +{ + "name": "ws", + "version": "5.2.2", + "description": "Simple to use, blazing fast and thoroughly tested websocket client and server for Node.js", + "keywords": [ + "HyBi", + "Push", + "RFC-6455", + "WebSocket", + "WebSockets", + "real-time" + ], + "homepage": "https://github.com/websockets/ws", + "bugs": { + "url": "https://github.com/websockets/ws/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/websockets/ws.git" + }, + "author": { + "name": "Einar Otto Stangvik", + "email": "einaros@gmail.com", + "url": "http://2x.io" + }, + "license": "MIT", + "main": "index.js", + "files": [ + "index.js", + "lib" + ], + "scripts": { + "test": "eslint . && nyc --reporter=html --reporter=text mocha test/*.test.js", + "integration": "eslint . && mocha test/*.integration.js", + "lint": "eslint ." + }, + "dependencies": { + "async-limiter": "~1.0.0" + }, + "devDependencies": { + "benchmark": "~2.1.2", + "bufferutil": "~3.0.0", + "eslint": "~4.19.0", + "eslint-config-standard": "~11.0.0", + "eslint-plugin-import": "~2.12.0", + "eslint-plugin-node": "~6.0.0", + "eslint-plugin-promise": "~3.8.0", + "eslint-plugin-standard": "~3.0.0", + "mocha": "~5.2.0", + "nyc": "~12.0.2", + "utf-8-validate": "~4.0.0" + }, + "gitHead": "5d55e52529167c25f4fec35cb4753294e75bf9f2", + "_id": "ws@5.2.2", + "_npmVersion": "6.1.0", + "_nodeVersion": "10.6.0", + "_npmUser": { + "name": "lpinca", + "email": "luigipinca@gmail.com" + }, + "dist": { + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "shasum": "dffef14866b8e8dc9133582514d1befaf96e980f", + "tarball": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "fileCount": 14, + "unpackedSize": 99219, + "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRmFaCRA9TVsSAnZWagAAZH0P/iB0EIYIHqFAJznwT4el\n8xU2FN/na7yK3k+nV0cNYD+gKdOcTphij2IJGnQBM8hG4SlPBf+NBqy7/VBo\na3cmV3Rat395nmI3lhTgb9EDMFgYRQty3ORS3KAf2KEpFFA4QlTjOttjYsCq\nZN/j3GMnsnH47RxToPE9wTyC8d+cgIfdQHLN1k+5YaN5OtBCyKIXGbl+QJli\n2YLGAU1mp+yM+CF8CV+q6aodokoF/89D8LnJ7N5LjIgYGHTohB9c/fY/7v/5\nQLqd35RTo8OXMfiujUy2EhyGP5SyiTUzttAmXuSOxG3KQTtzss0dHMBxFeXJ\nO6ZDh124WW1VJYhdPKwfaHwszfmB6a95K2Gmu7xtvlq48qMq6Rfi9WQ1/rlc\nYyeyXAX1a/ykbEza4mm9oPfZpkPKSYM4s4fYufxyG3sAz3vKaOy4MQNA6gOC\n49sJBGT7kdTlPgHuE832t9T+J8ByCGNl/o2zJDDYLq6RLZqtgaSqAWtIBaFe\neW/yEVklhm2InF8e1yAiHg4au/6OKf4PFfKpcjdKvDbMZO8fFm6VDRYqFlI5\nnF2XzxK7p86sIe+YeFqVAzc4kMGcvYzrD7RhA25n+NBbjHaYChCLibgEDG7E\n2gqUv1T9BU2ihq845gapZ9h1b7/dpfqOKZCf5kvMxZLjp/rL8msRRPd4GIw9\nPXXp\r\n=QnaD\r\n-----END PGP SIGNATURE-----\r\n" + }, + "maintainers": [ + { + "name": "3rdeden", + "email": "npm@3rd-Eden.com" + }, + { + "name": "einaros", + "email": "einaros@gmail.com" + }, + { + "name": "lpinca", + "email": "luigipinca@gmail.com" + }, + { + "name": "v1", + "email": "npm@3rd-Eden.com" + } + ], + "directories": {}, + "_npmOperationalInternal": { + "host": "s3://npm-registry-packages", + "tmp": "tmp/ws_5.2.2_1531339098131_0.05866997625683701" + }, + "_hasShrinkwrap": false, + "_shasum": "dffef14866b8e8dc9133582514d1befaf96e980f", + "_from": "ws@^5.2.0", + "_resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/node_modules/express-ws/package.json b/node_modules/express-ws/package.json new file mode 100644 index 0000000..413730a --- /dev/null +++ b/node_modules/express-ws/package.json @@ -0,0 +1,104 @@ +{ + "name": "express-ws", + "version": "4.0.0", + "description": "WebSocket endpoints for Express applications", + "main": "index.js", + "scripts": { + "prepublish": "npm run build", + "build": "babel src/ -d lib/", + "lint": "eslint src/" + }, + "author": { + "name": "Henning Morud", + "email": "henning@morud.org" + }, + "contributors": [ + { + "name": "Jesús Leganés Combarro", + "email": "piranna@gmail.com" + }, + { + "name": "Sven Slootweg", + "email": "admin@cryto.net" + }, + { + "name": "Andrew Phillips", + "email": "theasp@gmail.com" + }, + { + "name": "Nicholas Schell", + "email": "nschell@gmail.com" + }, + { + "name": "Max Truxa", + "email": "dev@maxtruxa.com" + }, + { + "name": "Kræn Hansen", + "email": "mail@kraenhansen.dk" + } + ], + "license": "BSD-2-Clause", + "dependencies": { + "ws": "^5.2.0" + }, + "peerDependencies": { + "express": "^4.0.0 || ^5.0.0-alpha.1" + }, + "engines": { + "node": ">=4.5.0" + }, + "directories": { + "example": "examples" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/HenningM/express-ws.git" + }, + "keywords": [ + "express", + "ws", + "websocket" + ], + "bugs": { + "url": "https://github.com/HenningM/express-ws/issues" + }, + "homepage": "https://github.com/HenningM/express-ws", + "devDependencies": { + "babel-cli": "^6.26.0", + "babel-preset-es2015": "^6.5.0", + "eslint": "^4.19.0", + "eslint-config-airbnb": "^14.1.0", + "eslint-plugin-import": "^2.12.0", + "express": "^5.0.0-alpha.6" + }, + "gitHead": "11928f260bcb4f4897879bf09c8abe32ca4c65f2", + "_id": "express-ws@4.0.0", + "_npmVersion": "6.0.1", + "_nodeVersion": "6.3.1", + "_npmUser": { + "name": "henningm", + "email": "henning@morud.org" + }, + "dist": { + "integrity": "sha512-KEyUw8AwRET2iFjFsI1EJQrJ/fHeGiJtgpYgEWG3yDv4l/To/m3a2GaYfeGyB3lsWdvbesjF5XCMx+SVBgAAYw==", + "shasum": "dabd8dc974516418902a41fe6e30ed949b4d36c4", + "tarball": "https://registry.npmjs.org/express-ws/-/express-ws-4.0.0.tgz", + "fileCount": 16, + "unpackedSize": 17428, + "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbF/vpCRA9TVsSAnZWagAAPO8P/RH99Dr1f8tCRufhH3kz\n+pXjAyHACRzttVW4rKmiBSYNFqOPEJSeQ/iGvbRHEjQc/rBeC14u33OgyHa3\nhiOSY9RNe1AjvenhgfjfH6dreLsE6M7otNgDHGvGwwxKRwGxN4ak/YsyY4zP\nemOzZSCVACHICn+KFm+U+PFxecDEeszssnmhoPw6oEi6Sx0KT83N5NW5j4Wu\nRZpUbpaS8WzsKYlZAOjKvplBFnrQFgM357JkG2rWu7ONH6b+roSRu1eEFwxt\nUaYSNc2VvAXvhhX0nkEV1SeUvdBUOOEBmnF9o2tjubxshDt3ORzouonsXS0s\nAkZf0jmDkE9Ka9Kh/rb3YAGLxLVRE/1ttc7u6sytmgVubvzEa9ds83y9VMrO\nyu6uVvPt10CncdJaGX4wM8WLv9wxDK7onC3spKuqm8NQ1+6Z25cQN+YZ6C1m\n1mcGIdzWf4kS+J4ZIDImRwYYfrORnGNyaOv44OCFeTsM/o5wChFtUZmQyCFW\ncNvmdfw3udBUWswfLucM+wGlJCzJSqmteypzNvbR2gaV7EG9/pqGgVCSbEBH\nsWdzZoUuTP6FCfhnS7ngxLBQd6CCT9dEUGqmQT+Vh5Wynq2/sAtlcnz5ilWk\nbMt3pL4GapVu3kYwXuOqn9jU+EiI3qtk8GnUbLO+uVru4hoP+rsxsjVOx888\nwaBD\r\n=FGXN\r\n-----END PGP SIGNATURE-----\r\n" + }, + "maintainers": [ + { + "name": "henningm", + "email": "henning@morud.org" + } + ], + "_npmOperationalInternal": { + "host": "s3://npm-registry-packages", + "tmp": "tmp/express-ws_4.0.0_1528298471882_0.5274698752380893" + }, + "_shasum": "dabd8dc974516418902a41fe6e30ed949b4d36c4", + "_from": "express-ws@", + "_resolved": "https://registry.npmjs.org/express-ws/-/express-ws-4.0.0.tgz" +} diff --git a/node_modules/xterm-addon-attach/LICENSE b/node_modules/xterm-addon-attach/LICENSE new file mode 100644 index 0000000..e597698 --- /dev/null +++ b/node_modules/xterm-addon-attach/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2017, The xterm.js authors (https://github.com/xtermjs/xterm.js) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/xterm-addon-attach/README.md b/node_modules/xterm-addon-attach/README.md new file mode 100644 index 0000000..67ebf17 --- /dev/null +++ b/node_modules/xterm-addon-attach/README.md @@ -0,0 +1,22 @@ +## xterm-addon-attach + +An addon for [xterm.js](https://github.com/xtermjs/xterm.js) that enables attaching to a web socket. This addon requires xterm.js v4+. + +### Install + +```bash +npm install --save xterm-addon-attach +``` + +### Usage + +```ts +import { Terminal } from 'xterm'; +import { AttachAddon } from 'xterm-addon-attach'; + +const terminal = new Terminal(); +const attachAddon = new AttachAddon(webSocket); +terminal.loadAddon(attachAddon); +``` + +See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/xterm-addon-attach/typings/xterm-addon-attach.d.ts) for more advanced usage. diff --git a/node_modules/xterm-addon-attach/lib/xterm-addon-attach.js b/node_modules/xterm-addon-attach/lib/xterm-addon-attach.js new file mode 100644 index 0000000..fe176c9 --- /dev/null +++ b/node_modules/xterm-addon-attach/lib/xterm-addon-attach.js @@ -0,0 +1,2 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.AttachAddon=t():e.AttachAddon=t()}(window,function(){return function(e){var t={};function o(s){if(t[s])return t[s].exports;var n=t[s]={i:s,l:!1,exports:{}};return e[s].call(n.exports,n,n.exports,o),n.l=!0,n.exports}return o.m=e,o.c=t,o.d=function(e,t,s){o.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:s})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(e,t){if(1&t&&(e=o(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var s=Object.create(null);if(o.r(s),Object.defineProperty(s,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)o.d(s,n,function(t){return e[t]}.bind(null,n));return s},o.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.p="",o(o.s=0)}([function(e,t,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0});function s(e,t,o){return e.addEventListener(t,o),{dispose:()=>{o&&e.removeEventListener(t,o)}}}t.AttachAddon=class{constructor(e,t){this._disposables=[],this._socket=e,this._socket.binaryType="arraybuffer",this._bidirectional=!t||!1!==t.bidirectional}activate(e){this._disposables.push(s(this._socket,"message",t=>{const o=t.data;e.write("string"==typeof o?o:new Uint8Array(o))})),this._bidirectional&&this._disposables.push(e.onData(e=>this._sendData(e))),this._disposables.push(s(this._socket,"close",()=>this.dispose())),this._disposables.push(s(this._socket,"error",()=>this.dispose()))}dispose(){this._disposables.forEach(e=>e.dispose())}_sendData(e){1===this._socket.readyState&&this._socket.send(e)}}}])}); +//# sourceMappingURL=xterm-addon-attach.js.map \ No newline at end of file diff --git a/node_modules/xterm-addon-attach/lib/xterm-addon-attach.js.map b/node_modules/xterm-addon-attach/lib/xterm-addon-attach.js.map new file mode 100644 index 0000000..ca9c4b5 --- /dev/null +++ b/node_modules/xterm-addon-attach/lib/xterm-addon-attach.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack://AttachAddon/webpack/universalModuleDefinition","webpack://AttachAddon/webpack/bootstrap","webpack://AttachAddon/./src/AttachAddon.ts"],"names":["root","factory","exports","module","define","amd","window","installedModules","__webpack_require__","moduleId","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","addSocketListener","socket","type","handler","addEventListener","dispose","removeEventListener","options","_disposables","this","_socket","binaryType","_bidirectional","bidirectional","terminal","push","ev","data","write","Uint8Array","onData","_sendData","forEach","readyState","send"],"mappings":"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","file":"xterm-addon-attach.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"AttachAddon\"] = factory();\n\telse\n\t\troot[\"AttachAddon\"] = factory();\n})(window, function() {\nreturn "," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","/**\n * Copyright (c) 2014, 2019 The xterm.js authors. All rights reserved.\n * @license MIT\n *\n * Implements the attach method, that attaches the terminal to a WebSocket stream.\n */\n\nimport { Terminal, IDisposable, ITerminalAddon } from 'xterm';\n\ninterface IAttachOptions {\n bidirectional?: boolean;\n}\n\nexport class AttachAddon implements ITerminalAddon {\n private _socket: WebSocket;\n private _bidirectional: boolean;\n private _disposables: IDisposable[] = [];\n\n constructor(socket: WebSocket, options?: IAttachOptions) {\n this._socket = socket;\n // always set binary type to arraybuffer, we do not handle blobs\n this._socket.binaryType = 'arraybuffer';\n this._bidirectional = (options && options.bidirectional === false) ? false : true;\n }\n\n public activate(terminal: Terminal): void {\n this._disposables.push(\n addSocketListener(this._socket, 'message', ev => {\n const data: ArrayBuffer | string = ev.data;\n terminal.write(typeof data === 'string' ? data : new Uint8Array(data));\n })\n );\n\n if (this._bidirectional) {\n this._disposables.push(terminal.onData(data => this._sendData(data)));\n }\n\n this._disposables.push(addSocketListener(this._socket, 'close', () => this.dispose()));\n this._disposables.push(addSocketListener(this._socket, 'error', () => this.dispose()));\n }\n\n public dispose(): void {\n this._disposables.forEach(d => d.dispose());\n }\n\n private _sendData(data: string): void {\n // TODO: do something better than just swallowing\n // the data if the socket is not in a working condition\n if (this._socket.readyState !== 1) {\n return;\n }\n this._socket.send(data);\n }\n}\n\nfunction addSocketListener(socket: WebSocket, type: K, handler: (this: WebSocket, ev: WebSocketEventMap[K]) => any): IDisposable {\n socket.addEventListener(type, handler);\n return {\n dispose: () => {\n if (!handler) {\n // Already disposed\n return;\n }\n socket.removeEventListener(type, handler);\n }\n };\n}\n"],"sourceRoot":""} \ No newline at end of file diff --git a/node_modules/xterm-addon-attach/out/AttachAddon.api.js.map b/node_modules/xterm-addon-attach/out/AttachAddon.api.js.map new file mode 100644 index 0000000..e96706a --- /dev/null +++ b/node_modules/xterm-addon-attach/out/AttachAddon.api.js.map @@ -0,0 +1 @@ +{"version":3,"file":"AttachAddon.api.js","sourceRoot":"","sources":["../src/AttachAddon.api.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/node_modules/xterm-addon-attach/out/AttachAddon.js b/node_modules/xterm-addon-attach/out/AttachAddon.js new file mode 100644 index 0000000..2929602 --- /dev/null +++ b/node_modules/xterm-addon-attach/out/AttachAddon.js @@ -0,0 +1,43 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +class AttachAddon { + constructor(socket, options) { + this._disposables = []; + this._socket = socket; + this._socket.binaryType = 'arraybuffer'; + this._bidirectional = (options && options.bidirectional === false) ? false : true; + } + activate(terminal) { + this._disposables.push(addSocketListener(this._socket, 'message', ev => { + const data = ev.data; + terminal.write(typeof data === 'string' ? data : new Uint8Array(data)); + })); + if (this._bidirectional) { + this._disposables.push(terminal.onData(data => this._sendData(data))); + } + this._disposables.push(addSocketListener(this._socket, 'close', () => this.dispose())); + this._disposables.push(addSocketListener(this._socket, 'error', () => this.dispose())); + } + dispose() { + this._disposables.forEach(d => d.dispose()); + } + _sendData(data) { + if (this._socket.readyState !== 1) { + return; + } + this._socket.send(data); + } +} +exports.AttachAddon = AttachAddon; +function addSocketListener(socket, type, handler) { + socket.addEventListener(type, handler); + return { + dispose: () => { + if (!handler) { + return; + } + socket.removeEventListener(type, handler); + } + }; +} +//# sourceMappingURL=AttachAddon.js.map \ No newline at end of file diff --git a/node_modules/xterm-addon-attach/out/AttachAddon.js.map b/node_modules/xterm-addon-attach/out/AttachAddon.js.map new file mode 100644 index 0000000..cb46150 --- /dev/null +++ b/node_modules/xterm-addon-attach/out/AttachAddon.js.map @@ -0,0 +1 @@ +{"version":3,"file":"AttachAddon.js","sourceRoot":"","sources":["../src/AttachAddon.ts"],"names":[],"mappings":";;AAaA,MAAa,WAAW;IAKtB,YAAY,MAAiB,EAAE,OAAwB;QAF/C,iBAAY,GAAkB,EAAE,CAAC;QAGvC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QAEtB,IAAI,CAAC,OAAO,CAAC,UAAU,GAAG,aAAa,CAAC;QACxC,IAAI,CAAC,cAAc,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC,aAAa,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACpF,CAAC;IAEM,QAAQ,CAAC,QAAkB;QAChC,IAAI,CAAC,YAAY,CAAC,IAAI,CACpB,iBAAiB,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE,CAAC,EAAE;YAC9C,MAAM,IAAI,GAAyB,EAAE,CAAC,IAAI,CAAC;YAC3C,QAAQ,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;QACzE,CAAC,CAAC,CACH,CAAC;QAEF,IAAI,IAAI,CAAC,cAAc,EAAE;YACvB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SACvE;QAED,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACvF,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACzF,CAAC;IAEM,OAAO;QACZ,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IAC9C,CAAC;IAEO,SAAS,CAAC,IAAY;QAG5B,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,KAAK,CAAC,EAAE;YACjC,OAAO;SACR;QACD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;CACF;AAxCD,kCAwCC;AAED,SAAS,iBAAiB,CAAoC,MAAiB,EAAE,IAAO,EAAE,OAA2D;IACnJ,MAAM,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACvC,OAAO;QACL,OAAO,EAAE,GAAG,EAAE;YACZ,IAAI,CAAC,OAAO,EAAE;gBAEZ,OAAO;aACR;YACD,MAAM,CAAC,mBAAmB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC5C,CAAC;KACF,CAAC;AACJ,CAAC"} \ No newline at end of file diff --git a/node_modules/xterm-addon-attach/package.json b/node_modules/xterm-addon-attach/package.json new file mode 100644 index 0000000..54b4bf6 --- /dev/null +++ b/node_modules/xterm-addon-attach/package.json @@ -0,0 +1,59 @@ +{ + "name": "xterm-addon-attach", + "version": "0.3.0", + "author": { + "name": "The xterm.js authors", + "url": "https://xtermjs.org/" + }, + "main": "lib/xterm-addon-attach.js", + "types": "typings/xterm-addon-attach.d.ts", + "repository": { + "type": "git", + "url": "git+https://github.com/xtermjs/xterm.js.git" + }, + "license": "MIT", + "scripts": { + "build": "../../node_modules/.bin/tsc -p src", + "prepackage": "npm run build", + "package": "../../node_modules/.bin/webpack", + "prepublishOnly": "npm run package" + }, + "peerDependencies": { + "xterm": "^4.0.0" + }, + "description": "An addon for [xterm.js](https://github.com/xtermjs/xterm.js) that enables attaching to a web socket. This addon requires xterm.js v4+.", + "bugs": { + "url": "https://github.com/xtermjs/xterm.js/issues" + }, + "homepage": "https://github.com/xtermjs/xterm.js#readme", + "_id": "xterm-addon-attach@0.3.0", + "_npmVersion": "6.4.1", + "_nodeVersion": "8.16.1", + "_npmUser": { + "name": "tyriar", + "email": "tyriar@tyriar.com" + }, + "dist": { + "integrity": "sha512-uX1fFtnmhreb9jmUtwfDzAIRD8yL5bWw3yJWkPSHQB85o+Ug3i0sQci7X9xTdZfnY4IDh//u1QR58EvtDTrCAA==", + "shasum": "bd037c2eb496c02760d1ef647b179732500ebe49", + "tarball": "https://registry.npmjs.org/xterm-addon-attach/-/xterm-addon-attach-0.3.0.tgz", + "fileCount": 10, + "unpackedSize": 20885, + "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdm3CKCRA9TVsSAnZWagAAFQQP/RKiWj3UzumbVTkiMHy2\nikd7kGPtv7HFIQtT5ClkUjK9PLzX90L9PR8JL8kVq46YtvWHlwFj2yVGos9U\nyfZwODroUHbcuQVMJt0KUamCTvVyKrDWxsO1uKxp3q/F2YDcbKowqvCsNtzW\nNPmRStZGZNYLCUF5IFmrHC4xrT+aFvcIE3ILvNcVvS4UY6MngJRBVTUKw9O0\nN6/tndACS4ngyPjmMAGMreibmAl5eVJqQhSl4/B6C0EDDkMDpAmMhpe+CyVW\nmlB4j9OXbxxEJ3kGwSo/NOkBYhKVtCHrSgQUcyxwZW1Lgvll/8+V5QuK2LQW\neMt89F/KP6daCAtrqHsK2gj2RgpY3gw2c6h2sr5pRGwZVqHyonI/FVcICLKc\nVpc7rM/tUAstNxfEFsalonyzLW764XwRFo09grE/adL/GaqoZ7GekLqms7oY\nbDQZu7K2MYfh9x+O2ALkpYfxk6GORkyFzl8exhZ/csifYF38ZeTCfmLSBEjt\nRAq3YM6hLdCKnix/QXiHxTtY18Vv9KkUMuA8UHJZg36T/I3Xdp5G23FJgtc1\nLAbbRRxIixesA4cSvk7qy44SdI6giSUhduyQ5DyRmgTTTpFNd0PqbWe+yBcF\n6f+zUOBa77dlzovVFHVjCyFMil1CNHE7cYcVR1xec+nwfkUdgNs64/MvFIs4\ngclf\r\n=mA9X\r\n-----END PGP SIGNATURE-----\r\n" + }, + "maintainers": [ + { + "name": "tyriar", + "email": "tyriar@tyriar.com" + } + ], + "directories": {}, + "_npmOperationalInternal": { + "host": "s3://npm-registry-packages", + "tmp": "tmp/xterm-addon-attach_0.3.0_1570467978255_0.13947408295457797" + }, + "_hasShrinkwrap": false, + "_shasum": "bd037c2eb496c02760d1ef647b179732500ebe49", + "_from": "xterm-addon-attach@", + "_resolved": "https://registry.npmjs.org/xterm-addon-attach/-/xterm-addon-attach-0.3.0.tgz" +} diff --git a/node_modules/xterm-addon-attach/src/AttachAddon.ts b/node_modules/xterm-addon-attach/src/AttachAddon.ts new file mode 100644 index 0000000..117b2b5 --- /dev/null +++ b/node_modules/xterm-addon-attach/src/AttachAddon.ts @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2014, 2019 The xterm.js authors. All rights reserved. + * @license MIT + * + * Implements the attach method, that attaches the terminal to a WebSocket stream. + */ + +import { Terminal, IDisposable, ITerminalAddon } from 'xterm'; + +interface IAttachOptions { + bidirectional?: boolean; +} + +export class AttachAddon implements ITerminalAddon { + private _socket: WebSocket; + private _bidirectional: boolean; + private _disposables: IDisposable[] = []; + + constructor(socket: WebSocket, options?: IAttachOptions) { + this._socket = socket; + // always set binary type to arraybuffer, we do not handle blobs + this._socket.binaryType = 'arraybuffer'; + this._bidirectional = (options && options.bidirectional === false) ? false : true; + } + + public activate(terminal: Terminal): void { + this._disposables.push( + addSocketListener(this._socket, 'message', ev => { + const data: ArrayBuffer | string = ev.data; + terminal.write(typeof data === 'string' ? data : new Uint8Array(data)); + }) + ); + + if (this._bidirectional) { + this._disposables.push(terminal.onData(data => this._sendData(data))); + } + + this._disposables.push(addSocketListener(this._socket, 'close', () => this.dispose())); + this._disposables.push(addSocketListener(this._socket, 'error', () => this.dispose())); + } + + public dispose(): void { + this._disposables.forEach(d => d.dispose()); + } + + private _sendData(data: string): void { + // TODO: do something better than just swallowing + // the data if the socket is not in a working condition + if (this._socket.readyState !== 1) { + return; + } + this._socket.send(data); + } +} + +function addSocketListener(socket: WebSocket, type: K, handler: (this: WebSocket, ev: WebSocketEventMap[K]) => any): IDisposable { + socket.addEventListener(type, handler); + return { + dispose: () => { + if (!handler) { + // Already disposed + return; + } + socket.removeEventListener(type, handler); + } + }; +} diff --git a/node_modules/xterm-addon-attach/typings/xterm-addon-attach.d.ts b/node_modules/xterm-addon-attach/typings/xterm-addon-attach.d.ts new file mode 100644 index 0000000..1aa2135 --- /dev/null +++ b/node_modules/xterm-addon-attach/typings/xterm-addon-attach.d.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2017 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { Terminal, ILinkMatcherOptions, ITerminalAddon } from 'xterm'; + +declare module 'xterm-addon-attach' { + export interface IAttachOptions { + /** + * Whether input should be written to the backend. Defaults to `true`. + */ + bidirectional?: boolean; + } + + export class AttachAddon implements ITerminalAddon { + constructor(socket: WebSocket, options?: IAttachOptions); + public activate(terminal: Terminal): void; + public dispose(): void; + } +} diff --git a/node_modules/xterm-addon-fit/LICENSE b/node_modules/xterm-addon-fit/LICENSE new file mode 100644 index 0000000..8f17892 --- /dev/null +++ b/node_modules/xterm-addon-fit/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2019, The xterm.js authors (https://github.com/xtermjs/xterm.js) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/xterm-addon-fit/README.md b/node_modules/xterm-addon-fit/README.md new file mode 100644 index 0000000..321b2cf --- /dev/null +++ b/node_modules/xterm-addon-fit/README.md @@ -0,0 +1,24 @@ +## xterm-addon-fit + +An addon for [xterm.js](https://github.com/xtermjs/xterm.js) that enables fitting the terminal's dimensions to a containing element. This addon requires xterm.js v4+. + +### Install + +```bash +npm install --save xterm-addon-fit +``` + +### Usage + +```ts +import { Terminal } from 'xterm'; +import { FitAddon } from 'xterm-addon-fit'; + +const terminal = new Terminal(); +const fitAddon = new FitAddon(); +terminal.loadAddon(fitAddon); +terminal.open(containerElement); +fitAddon.fit(); +``` + +See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/xterm-addon-fit/typings/xterm-addon-fit.d.ts) for more advanced usage. diff --git a/node_modules/xterm-addon-fit/lib/xterm-addon-fit.js b/node_modules/xterm-addon-fit/lib/xterm-addon-fit.js new file mode 100644 index 0000000..b2b6500 --- /dev/null +++ b/node_modules/xterm-addon-fit/lib/xterm-addon-fit.js @@ -0,0 +1,2 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(window,function(){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=0)}([function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(){}return e.prototype.activate=function(e){this._terminal=e},e.prototype.dispose=function(){},e.prototype.fit=function(){var e=this.proposeDimensions();if(e&&this._terminal){var t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}},e.prototype.proposeDimensions=function(){if(this._terminal&&this._terminal.element&&this._terminal.element.parentElement){var e=this._terminal._core,t=window.getComputedStyle(this._terminal.element.parentElement),r=parseInt(t.getPropertyValue("height")),n=Math.max(0,parseInt(t.getPropertyValue("width"))),o=window.getComputedStyle(this._terminal.element),i=r-(parseInt(o.getPropertyValue("padding-top"))+parseInt(o.getPropertyValue("padding-bottom"))),a=n-(parseInt(o.getPropertyValue("padding-right"))+parseInt(o.getPropertyValue("padding-left")))-e.viewport.scrollBarWidth;return{cols:Math.max(2,Math.floor(a/e._renderService.dimensions.actualCellWidth)),rows:Math.max(1,Math.floor(i/e._renderService.dimensions.actualCellHeight))}}},e}();t.FitAddon=n}])}); +//# sourceMappingURL=xterm-addon-fit.js.map diff --git a/node_modules/xterm-addon-fit/lib/xterm-addon-fit.js.map b/node_modules/xterm-addon-fit/lib/xterm-addon-fit.js.map new file mode 100644 index 0000000..b080faa --- /dev/null +++ b/node_modules/xterm-addon-fit/lib/xterm-addon-fit.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack://FitAddon/webpack/universalModuleDefinition","webpack://FitAddon/webpack/bootstrap","webpack://FitAddon/./src/FitAddon.ts"],"names":["root","factory","exports","module","define","amd","window","installedModules","__webpack_require__","moduleId","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","activate","terminal","this","_terminal","dispose","fit","dims","proposeDimensions","core","_core","rows","cols","_renderService","clear","resize","element","parentElement","parentElementStyle","getComputedStyle","parentElementHeight","parseInt","getPropertyValue","parentElementWidth","Math","max","elementStyle","availableHeight","availableWidth","viewport","scrollBarWidth","floor","dimensions","actualCellWidth","actualCellHeight","FitAddon"],"mappings":"CAAA,SAA2CA,EAAMC,GAC1B,iBAAZC,SAA0C,iBAAXC,OACxCA,OAAOD,QAAUD,IACQ,mBAAXG,QAAyBA,OAAOC,IAC9CD,OAAO,GAAIH,GACe,iBAAZC,QACdA,QAAkB,SAAID,IAEtBD,EAAe,SAAIC,IARrB,CASGK,OAAQ,WACX,O,YCTE,IAAIC,EAAmB,GAGvB,SAASC,EAAoBC,GAG5B,GAAGF,EAAiBE,GACnB,OAAOF,EAAiBE,GAAUP,QAGnC,IAAIC,EAASI,EAAiBE,GAAY,CACzCC,EAAGD,EACHE,GAAG,EACHT,QAAS,IAUV,OANAU,EAAQH,GAAUI,KAAKV,EAAOD,QAASC,EAAQA,EAAOD,QAASM,GAG/DL,EAAOQ,GAAI,EAGJR,EAAOD,QA0Df,OArDAM,EAAoBM,EAAIF,EAGxBJ,EAAoBO,EAAIR,EAGxBC,EAAoBQ,EAAI,SAASd,EAASe,EAAMC,GAC3CV,EAAoBW,EAAEjB,EAASe,IAClCG,OAAOC,eAAenB,EAASe,EAAM,CAAEK,YAAY,EAAMC,IAAKL,KAKhEV,EAAoBgB,EAAI,SAAStB,GACX,oBAAXuB,QAA0BA,OAAOC,aAC1CN,OAAOC,eAAenB,EAASuB,OAAOC,YAAa,CAAEC,MAAO,WAE7DP,OAAOC,eAAenB,EAAS,aAAc,CAAEyB,OAAO,KAQvDnB,EAAoBoB,EAAI,SAASD,EAAOE,GAEvC,GADU,EAAPA,IAAUF,EAAQnB,EAAoBmB,IAC/B,EAAPE,EAAU,OAAOF,EACpB,GAAW,EAAPE,GAA8B,iBAAVF,GAAsBA,GAASA,EAAMG,WAAY,OAAOH,EAChF,IAAII,EAAKX,OAAOY,OAAO,MAGvB,GAFAxB,EAAoBgB,EAAEO,GACtBX,OAAOC,eAAeU,EAAI,UAAW,CAAET,YAAY,EAAMK,MAAOA,IACtD,EAAPE,GAA4B,iBAATF,EAAmB,IAAI,IAAIM,KAAON,EAAOnB,EAAoBQ,EAAEe,EAAIE,EAAK,SAASA,GAAO,OAAON,EAAMM,IAAQC,KAAK,KAAMD,IAC9I,OAAOF,GAIRvB,EAAoB2B,EAAI,SAAShC,GAChC,IAAIe,EAASf,GAAUA,EAAO2B,WAC7B,WAAwB,OAAO3B,EAAgB,SAC/C,WAA8B,OAAOA,GAEtC,OADAK,EAAoBQ,EAAEE,EAAQ,IAAKA,GAC5BA,GAIRV,EAAoBW,EAAI,SAASiB,EAAQC,GAAY,OAAOjB,OAAOkB,UAAUC,eAAe1B,KAAKuB,EAAQC,IAGzG7B,EAAoBgC,EAAI,GAIjBhC,EAAoBA,EAAoBiC,EAAI,G,gFC/DrD,IAGA,aAGE,cAwDF,OAtDS,YAAAC,SAAP,SAAgBC,GACdC,KAAKC,UAAYF,GAGZ,YAAAG,QAAP,aAEO,YAAAC,IAAP,WACE,IAAMC,EAAOJ,KAAKK,oBAClB,GAAKD,GAASJ,KAAKC,UAAnB,CAKA,IAAMK,EAAaN,KAAKC,UAAWM,MAG/BP,KAAKC,UAAUO,OAASJ,EAAKI,MAAQR,KAAKC,UAAUQ,OAASL,EAAKK,OACpEH,EAAKI,eAAeC,QACpBX,KAAKC,UAAUW,OAAOR,EAAKK,KAAML,EAAKI,SAInC,YAAAH,kBAAP,WACE,GAAKL,KAAKC,WAILD,KAAKC,UAAUY,SAAYb,KAAKC,UAAUY,QAAQC,cAAvD,CAKA,IAAMR,EAAaN,KAAKC,UAAWM,MAE7BQ,EAAqBrD,OAAOsD,iBAAiBhB,KAAKC,UAAUY,QAAQC,eACpEG,EAAsBC,SAASH,EAAmBI,iBAAiB,WACnEC,EAAqBC,KAAKC,IAAI,EAAGJ,SAASH,EAAmBI,iBAAiB,WAC9EI,EAAe7D,OAAOsD,iBAAiBhB,KAAKC,UAAUY,SAStDW,EAAkBP,GAPjBC,SAASK,EAAaJ,iBAAiB,gBACpCD,SAASK,EAAaJ,iBAAiB,oBAO3CM,EAAiBL,GANdF,SAASK,EAAaJ,iBAAiB,kBACxCD,SAASK,EAAaJ,iBAAiB,kBAKiBb,EAAKoB,SAASC,eAK9E,MAJiB,CACflB,KAAMY,KAAKC,IAzDI,EAyDcD,KAAKO,MAAMH,EAAiBnB,EAAKI,eAAemB,WAAWC,kBACxFtB,KAAMa,KAAKC,IAzDI,EAyDcD,KAAKO,MAAMJ,EAAkBlB,EAAKI,eAAemB,WAAWE,sBAI/F,EA3DA,GAAa,EAAAC","file":"xterm-addon-fit.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"FitAddon\"] = factory();\n\telse\n\t\troot[\"FitAddon\"] = factory();\n})(window, function() {\nreturn "," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { Terminal, ITerminalAddon } from 'xterm';\n\ninterface ITerminalDimensions {\n /**\n * The number of rows in the terminal.\n */\n rows: number;\n\n /**\n * The number of columns in the terminal.\n */\n cols: number;\n}\n\nconst MINIMUM_COLS = 2;\nconst MINIMUM_ROWS = 1;\n\nexport class FitAddon implements ITerminalAddon {\n private _terminal: Terminal | undefined;\n\n constructor() {}\n\n public activate(terminal: Terminal): void {\n this._terminal = terminal;\n }\n\n public dispose(): void {}\n\n public fit(): void {\n const dims = this.proposeDimensions();\n if (!dims || !this._terminal) {\n return;\n }\n\n // TODO: Remove reliance on private API\n const core = (this._terminal)._core;\n\n // Force a full render\n if (this._terminal.rows !== dims.rows || this._terminal.cols !== dims.cols) {\n core._renderService.clear();\n this._terminal.resize(dims.cols, dims.rows);\n }\n }\n\n public proposeDimensions(): ITerminalDimensions | undefined {\n if (!this._terminal) {\n return undefined;\n }\n\n if (!this._terminal.element || !this._terminal.element.parentElement) {\n return undefined;\n }\n\n // TODO: Remove reliance on private API\n const core = (this._terminal)._core;\n\n const parentElementStyle = window.getComputedStyle(this._terminal.element.parentElement);\n const parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height'));\n const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width')));\n const elementStyle = window.getComputedStyle(this._terminal.element);\n const elementPadding = {\n top: parseInt(elementStyle.getPropertyValue('padding-top')),\n bottom: parseInt(elementStyle.getPropertyValue('padding-bottom')),\n right: parseInt(elementStyle.getPropertyValue('padding-right')),\n left: parseInt(elementStyle.getPropertyValue('padding-left'))\n };\n const elementPaddingVer = elementPadding.top + elementPadding.bottom;\n const elementPaddingHor = elementPadding.right + elementPadding.left;\n const availableHeight = parentElementHeight - elementPaddingVer;\n const availableWidth = parentElementWidth - elementPaddingHor - core.viewport.scrollBarWidth;\n const geometry = {\n cols: Math.max(MINIMUM_COLS, Math.floor(availableWidth / core._renderService.dimensions.actualCellWidth)),\n rows: Math.max(MINIMUM_ROWS, Math.floor(availableHeight / core._renderService.dimensions.actualCellHeight))\n };\n return geometry;\n }\n}\n"],"sourceRoot":""} \ No newline at end of file diff --git a/node_modules/xterm-addon-fit/out/FitAddon.api.js.map b/node_modules/xterm-addon-fit/out/FitAddon.api.js.map new file mode 100644 index 0000000..fb65caf --- /dev/null +++ b/node_modules/xterm-addon-fit/out/FitAddon.api.js.map @@ -0,0 +1 @@ +{"version":3,"file":"FitAddon.api.js","sourceRoot":"","sources":["../src/FitAddon.api.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/node_modules/xterm-addon-fit/out/FitAddon.js b/node_modules/xterm-addon-fit/out/FitAddon.js new file mode 100644 index 0000000..5d7ab99 --- /dev/null +++ b/node_modules/xterm-addon-fit/out/FitAddon.js @@ -0,0 +1,54 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var MINIMUM_COLS = 2; +var MINIMUM_ROWS = 1; +var FitAddon = (function () { + function FitAddon() { + } + FitAddon.prototype.activate = function (terminal) { + this._terminal = terminal; + }; + FitAddon.prototype.dispose = function () { }; + FitAddon.prototype.fit = function () { + var dims = this.proposeDimensions(); + if (!dims || !this._terminal) { + return; + } + var core = this._terminal._core; + if (this._terminal.rows !== dims.rows || this._terminal.cols !== dims.cols) { + core._renderService.clear(); + this._terminal.resize(dims.cols, dims.rows); + } + }; + FitAddon.prototype.proposeDimensions = function () { + if (!this._terminal) { + return undefined; + } + if (!this._terminal.element || !this._terminal.element.parentElement) { + return undefined; + } + var core = this._terminal._core; + var parentElementStyle = window.getComputedStyle(this._terminal.element.parentElement); + var parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')); + var parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width'))); + var elementStyle = window.getComputedStyle(this._terminal.element); + var elementPadding = { + top: parseInt(elementStyle.getPropertyValue('padding-top')), + bottom: parseInt(elementStyle.getPropertyValue('padding-bottom')), + right: parseInt(elementStyle.getPropertyValue('padding-right')), + left: parseInt(elementStyle.getPropertyValue('padding-left')) + }; + var elementPaddingVer = elementPadding.top + elementPadding.bottom; + var elementPaddingHor = elementPadding.right + elementPadding.left; + var availableHeight = parentElementHeight - elementPaddingVer; + var availableWidth = parentElementWidth - elementPaddingHor - core.viewport.scrollBarWidth; + var geometry = { + cols: Math.max(MINIMUM_COLS, Math.floor(availableWidth / core._renderService.dimensions.actualCellWidth)), + rows: Math.max(MINIMUM_ROWS, Math.floor(availableHeight / core._renderService.dimensions.actualCellHeight)) + }; + return geometry; + }; + return FitAddon; +}()); +exports.FitAddon = FitAddon; +//# sourceMappingURL=FitAddon.js.map \ No newline at end of file diff --git a/node_modules/xterm-addon-fit/out/FitAddon.js.map b/node_modules/xterm-addon-fit/out/FitAddon.js.map new file mode 100644 index 0000000..33f3c37 --- /dev/null +++ b/node_modules/xterm-addon-fit/out/FitAddon.js.map @@ -0,0 +1 @@ +{"version":3,"file":"FitAddon.js","sourceRoot":"","sources":["../src/FitAddon.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/node_modules/xterm-addon-fit/package.json b/node_modules/xterm-addon-fit/package.json new file mode 100644 index 0000000..1410625 --- /dev/null +++ b/node_modules/xterm-addon-fit/package.json @@ -0,0 +1,59 @@ +{ + "name": "xterm-addon-fit", + "version": "0.3.0", + "author": { + "name": "The xterm.js authors", + "url": "https://xtermjs.org/" + }, + "main": "lib/xterm-addon-fit.js", + "types": "typings/xterm-addon-fit.d.ts", + "repository": { + "type": "git", + "url": "git+https://github.com/xtermjs/xterm.js.git" + }, + "license": "MIT", + "scripts": { + "build": "../../node_modules/.bin/tsc -p src", + "prepackage": "npm run build", + "package": "../../node_modules/.bin/webpack", + "prepublishOnly": "npm run package" + }, + "peerDependencies": { + "xterm": "^4.0.0" + }, + "description": "An addon for [xterm.js](https://github.com/xtermjs/xterm.js) that enables fitting the terminal's dimensions to a containing element. This addon requires xterm.js v4+.", + "bugs": { + "url": "https://github.com/xtermjs/xterm.js/issues" + }, + "homepage": "https://github.com/xtermjs/xterm.js#readme", + "_id": "xterm-addon-fit@0.3.0", + "_npmVersion": "6.4.1", + "_nodeVersion": "8.16.2", + "_npmUser": { + "name": "tyriar", + "email": "tyriar@tyriar.com" + }, + "dist": { + "integrity": "sha512-kvkiqHVrnMXgyCH9Xn0BOBJ7XaWC/4BgpSWQy3SueqximgW630t/QOankgqkvk11iTOCwWdAY9DTyQBXUMN3lw==", + "shasum": "341710741027de9d648a9f84415a01ddfdbbe715", + "tarball": "https://registry.npmjs.org/xterm-addon-fit/-/xterm-addon-fit-0.3.0.tgz", + "fileCount": 10, + "unpackedSize": 26905, + "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdsvJOCRA9TVsSAnZWagAASgsP+wTBoOnz7Dz9t1dtDXUN\natiKkKV1EyJGXj5aWkoy+BXx/pMrKsqf7IrbusxKrhAoQZOdWU5qTuC9oR8g\n5Wp5hB78YHdQK1AV5CeIIqh9TUJnFXfEiExUM++zp+SPy1qJCl758Uw9R4xF\ndxtPKILq6Lwesn8UV7HKIxDLDzPnjyFzatPbC4O/iJAKBl0wzREvoDvtYvse\nHaeXpIR7vwsh0sXBX+7NfN5fEmT9j2S5Ul5wwSaJR4yEj/QPixW1wEs5bmbj\nSIO/DSiFqXPpOSYbCiTTU5neg9DEZal2S9/cPhNHbXmHxW2AmGEhHDROCbrC\nECOsZTVzmxLNiMV23N+BRlwXOamedvtQlP8S6NTsPhHARsR4bd1ULpG27Yte\nSE2fRGTAFLFtzK6ChzcH9Ez+TUGkOHjLmunX9KANWFuCFuHuS+2TvsyL2c6I\nqWTTqYn7ZDHjAfeQZed1SJ60YFBqGaIMEg5EVsNbTkLSj9OXjxoT9xWAY0lQ\nI2NGktOQXdlFsVwk7asbjtjoWcDIRxEP6ZuYo9vPpFjeANwydqfm3LNgdcHu\nFuNz4XXgQgyrkoKQMhHu4eAeiEGA2RJF5qaWEqpT8WiHSF8XzoMLVrNee701\nk14t0FfVOVCNYGeaeT0QW6YErMMfJa/lNBqnWNN1I6i5VJvZA71XrkfJiY96\noZPY\r\n=zrhm\r\n-----END PGP SIGNATURE-----\r\n" + }, + "maintainers": [ + { + "name": "tyriar", + "email": "tyriar@tyriar.com" + } + ], + "directories": {}, + "_npmOperationalInternal": { + "host": "s3://npm-registry-packages", + "tmp": "tmp/xterm-addon-fit_0.3.0_1572008525934_0.7520475374900439" + }, + "_hasShrinkwrap": false, + "_shasum": "341710741027de9d648a9f84415a01ddfdbbe715", + "_from": "xterm-addon-fit@", + "_resolved": "https://registry.npmjs.org/xterm-addon-fit/-/xterm-addon-fit-0.3.0.tgz" +} diff --git a/node_modules/xterm-addon-fit/src/FitAddon.ts b/node_modules/xterm-addon-fit/src/FitAddon.ts new file mode 100644 index 0000000..ca7e24b --- /dev/null +++ b/node_modules/xterm-addon-fit/src/FitAddon.ts @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2017 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { Terminal, ITerminalAddon } from 'xterm'; + +interface ITerminalDimensions { + /** + * The number of rows in the terminal. + */ + rows: number; + + /** + * The number of columns in the terminal. + */ + cols: number; +} + +const MINIMUM_COLS = 2; +const MINIMUM_ROWS = 1; + +export class FitAddon implements ITerminalAddon { + private _terminal: Terminal | undefined; + + constructor() {} + + public activate(terminal: Terminal): void { + this._terminal = terminal; + } + + public dispose(): void {} + + public fit(): void { + const dims = this.proposeDimensions(); + if (!dims || !this._terminal) { + return; + } + + // TODO: Remove reliance on private API + const core = (this._terminal)._core; + + // Force a full render + if (this._terminal.rows !== dims.rows || this._terminal.cols !== dims.cols) { + core._renderService.clear(); + this._terminal.resize(dims.cols, dims.rows); + } + } + + public proposeDimensions(): ITerminalDimensions | undefined { + if (!this._terminal) { + return undefined; + } + + if (!this._terminal.element || !this._terminal.element.parentElement) { + return undefined; + } + + // TODO: Remove reliance on private API + const core = (this._terminal)._core; + + const parentElementStyle = window.getComputedStyle(this._terminal.element.parentElement); + const parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')); + const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width'))); + const elementStyle = window.getComputedStyle(this._terminal.element); + const elementPadding = { + top: parseInt(elementStyle.getPropertyValue('padding-top')), + bottom: parseInt(elementStyle.getPropertyValue('padding-bottom')), + right: parseInt(elementStyle.getPropertyValue('padding-right')), + left: parseInt(elementStyle.getPropertyValue('padding-left')) + }; + const elementPaddingVer = elementPadding.top + elementPadding.bottom; + const elementPaddingHor = elementPadding.right + elementPadding.left; + const availableHeight = parentElementHeight - elementPaddingVer; + const availableWidth = parentElementWidth - elementPaddingHor - core.viewport.scrollBarWidth; + const geometry = { + cols: Math.max(MINIMUM_COLS, Math.floor(availableWidth / core._renderService.dimensions.actualCellWidth)), + rows: Math.max(MINIMUM_ROWS, Math.floor(availableHeight / core._renderService.dimensions.actualCellHeight)) + }; + return geometry; + } +} diff --git a/node_modules/xterm-addon-fit/typings/xterm-addon-fit.d.ts b/node_modules/xterm-addon-fit/typings/xterm-addon-fit.d.ts new file mode 100644 index 0000000..c3b0b4a --- /dev/null +++ b/node_modules/xterm-addon-fit/typings/xterm-addon-fit.d.ts @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { Terminal, ITerminalAddon } from 'xterm'; + +declare module 'xterm-addon-fit' { + /** + * An xterm.js addon that enables resizing the terminal to the dimensions of + * its containing element. + */ + export class FitAddon implements ITerminalAddon { + /** + * Creates a new fit addon. + */ + constructor(); + + /** + * Activates the addon + * @param terminal The terminal the addon is being loaded in. + */ + public activate(terminal: Terminal): void; + + /** + * Disposes the addon. + */ + public dispose(): void; + + /** + * Resizes the terminal to the dimensions of its containing element. + */ + public fit(): void; + + /** + * Gets the proposed dimensions that will be used for a fit. + */ + public proposeDimensions(): ITerminalDimensions; + } + + /** + * Reprepresents the dimensions of a terminal. + */ + export interface ITerminalDimensions { + /** + * The number of rows in the terminal. + */ + rows: number; + + /** + * The number of columns in the terminal. + */ + cols: number; + } +} diff --git a/package.json b/package.json index 8ec9605..956aef4 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,8 @@ "description": "Esta es una web de prueba", "dependencies": { "ejs": "^2.6.2", - "express": "^4.17.1" + "express": "^4.17.1", + "xterm-addon-attach": "^0.3.0", + "xterm-addon-fit": "^0.3.0" } } diff --git a/src/index.js b/src/index.js index e231888..82abb75 100644 --- a/src/index.js +++ b/src/index.js @@ -1,8 +1,32 @@ const express = require('express'); // pide express sea requerido const path = require('path'); // pide que path sea requerido - +const pty = require('node-pty'); ////////////////ASKS FOR NODE-PTY // inicializaciones const app = express(); +const expressWs = require('express-ws')(app);///////////////////ASK FOR THE WEBSOCKET FROM EXPRESS +////////////////////////////////////////////////////////////////////////////////////////////////// +// Instantiate shell and set up data handlers +expressWs.app.ws('/shell', (ws, req) => { + // Spawn the shell + const shell = pty.spawn('/bin/bash', [], { + name: 'xterm-color', + cwd: process.env.PWD, + env: process.env + }); + // For all shell data send it to the websocket + shell.on('data', (data) => { + ws.send(data); + }); + // For all websocket data send it to the shell + ws.on('message', (msg) => { + shell.write(msg); + }); +}); +////////////////////////////////////////////////////////////////////////////////////////////////// + + + + //configuraciones app.set('port', process.env.PORT || 3000); diff --git a/src/routes/index.js b/src/routes/index.js index f8eca17..e0eb75b 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -71,6 +71,43 @@ router.get('/gettopo', (req, res) => { }); }); +router.get('/pingall', (req, res) => { + var sys = require('sys') + var exec = require('child_process').exec; + var child; + child = exec("echo pingall > fifo", function(error, stdout, stderr) { + console.log("pingall"); + res.send(stdout); + }); +}); +router.get('/placement', (req, res) => { + var sys = require('sys') + var exec = require('child_process').exec; + var child; + child = exec("echo placement > fifo", function(error, stdout, stderr) { + console.log("placement"); + res.send(stdout); + }); +}); + +router.get('/getvsorcdata', (req, res) => { + var sys = require('sys') + var exec = require('child_process').exec; + var child; + child = exec("cd /home/pi && cat aichivo 2>&1", function(error, stdout, stderr) { + console.log("getting vsorc data"); + res.send(stdout); + }); +}); +router.get('/getcontrollerdata', (req, res) => { + var sys = require('sys') + var exec = require('child_process').exec; + var child; + child = exec("cd /home/pi && cat controllerout 2>&1", function(error, stdout, stderr) { + console.log("getting controller data"); + res.send(stdout); + }); +}); router.get('/listswitch', (req, res) => { var sys = require('sys') var exec = require('child_process').exec; @@ -113,7 +150,8 @@ router.get('/startcontroller', (req, res) => { var exec = require('child_process').exec; var child; //cd /home/pi && setsid $(cat /home/pi/ejecutarcontroller.sh | grep sudo) >/dev/null 2>&1 < /dev/null & - child = exec("cd /home/pi && ./ejecutarcontroller.sh > /dev/null 2>&1 < /dev/null &", function(error, stdout, stderr) { + //cd /home/pi && ./ejecutarcontroller.sh > /dev/null 2>&1 < /dev/null & //comando anterior + child = exec("cd /home/pi && rm controllerout && touch controllerout && ./ejecutarcontroller.sh > controllerout 2>&1 &", function(error, stdout, stderr) { console.log("controller started"); res.send(stdout); }); @@ -123,7 +161,7 @@ router.get('/stopcontroller', (req, res) => { var sys = require('sys') var exec = require('child_process').exec; var child; - child = exec("sudo kill $(ps aux | grep python | grep ryu | awk {'print $2'})", function(error, stdout, stderr) { + child = exec("cd /home/pi && rm controllerout && sudo kill $(ps aux | grep python | grep ryu | awk {'print $2'})", function(error, stdout, stderr) { console.log("controller stopped"); res.send(stdout); }); @@ -167,22 +205,25 @@ router.get('/stopvsorc', (req,res) =>{ var child2; var child3; var payload - child1 = exec("cd /home/pi && exec 3>&- && rm fifo", function(error, stdout, stderr) { + console.log("erasing..."); + child1 = exec("cd /home/pi && exec 3>&- && rm fifo && rm aichivo", function(error, stdout, stderr) { console.log(stdout); - console.log("rm done"); + payload+="rm done\n\n"+stdout; });//esto cierra el fifo, lo cual cierra el programa //sudo kill $(ps aux | grep GRE| grep sudo|awk {'print $2'}) && cd /home/pi && ./multissh.sh sudo -E mn -c; sudo -E mn -c + console.log("killing all..."); child2 = exec("sudo kill $(ps aux | grep GRE| grep sudo|awk {'print $2'})", function(error, stdout, stderr) { console.log(stdout); - console.log("killed"); payload+="killed\n\n"+stdout; }); + console.log("Multisshing and cleaning..."); child3 = exec("cd /home/pi && ./multissh.sh sudo -E mn -c; sudo -E mn -c", function(error, stdout, stderr) { console.log(stdout); console.log("multisshed"); payload+="Multisshed\n\n"+stdout; + }); res.send(payload); }); diff --git a/src/views/starter.ejs b/src/views/starter.ejs new file mode 100644 index 0000000..3528815 --- /dev/null +++ b/src/views/starter.ejs @@ -0,0 +1,222 @@ + + + + + + + <%include ../../partials/head%> + + + + + + + + + + + +
+ <%include ../../partials/header%> + +
+ + + +
+
+ +
+ +
+ + + + +
+
+ +
+ +
+ + + +
+
+ +
+ +
+ +
+ +
+ +
+ +
+ + +
+ + + + +