Remove deprecated scripts for adding head-matter to wt_config.xml, including Python and Bash implementations, to streamline configuration management.
This commit is contained in:
19
node_modules/engine.io/LICENSE
generated
vendored
Normal file
19
node_modules/engine.io/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2014-present Guillermo Rauch and Socket.IO contributors
|
||||
|
||||
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.
|
||||
|
||||
603
node_modules/engine.io/README.md
generated
vendored
Normal file
603
node_modules/engine.io/README.md
generated
vendored
Normal file
@@ -0,0 +1,603 @@
|
||||
|
||||
# Engine.IO: the realtime engine
|
||||
|
||||
[](https://github.com/socketio/engine.io/actions)
|
||||
[](http://badge.fury.io/js/engine.io)
|
||||
|
||||
`Engine.IO` is the implementation of transport-based
|
||||
cross-browser/cross-device bi-directional communication layer for
|
||||
[Socket.IO](http://github.com/socketio/socket.io).
|
||||
|
||||
## How to use
|
||||
|
||||
### Server
|
||||
|
||||
#### (A) Listening on a port
|
||||
|
||||
```js
|
||||
const engine = require('engine.io');
|
||||
const server = engine.listen(80);
|
||||
|
||||
server.on('connection', socket => {
|
||||
socket.send('utf 8 string');
|
||||
socket.send(Buffer.from([0, 1, 2, 3, 4, 5])); // binary data
|
||||
});
|
||||
```
|
||||
|
||||
#### (B) Intercepting requests for a http.Server
|
||||
|
||||
```js
|
||||
const engine = require('engine.io');
|
||||
const http = require('http').createServer().listen(3000);
|
||||
const server = engine.attach(http);
|
||||
|
||||
server.on('connection', socket => {
|
||||
socket.on('message', data => { });
|
||||
socket.on('close', () => { });
|
||||
});
|
||||
```
|
||||
|
||||
#### (C) Passing in requests
|
||||
|
||||
```js
|
||||
const engine = require('engine.io');
|
||||
const server = new engine.Server();
|
||||
|
||||
server.on('connection', socket => {
|
||||
socket.send('hi');
|
||||
});
|
||||
|
||||
// …
|
||||
httpServer.on('upgrade', (req, socket, head) => {
|
||||
server.handleUpgrade(req, socket, head);
|
||||
});
|
||||
|
||||
httpServer.on('request', (req, res) => {
|
||||
server.handleRequest(req, res);
|
||||
});
|
||||
```
|
||||
|
||||
### Client
|
||||
|
||||
```html
|
||||
<script src="/path/to/engine.io.js"></script>
|
||||
<script>
|
||||
const socket = new eio.Socket('ws://localhost/');
|
||||
socket.on('open', () => {
|
||||
socket.on('message', data => {});
|
||||
socket.on('close', () => {});
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
For more information on the client refer to the
|
||||
[engine-client](http://github.com/socketio/engine.io-client) repository.
|
||||
|
||||
## What features does it have?
|
||||
|
||||
- **Maximum reliability**. Connections are established even in the presence of:
|
||||
- proxies and load balancers.
|
||||
- personal firewall and antivirus software.
|
||||
- for more information refer to **Goals** and **Architecture** sections
|
||||
- **Minimal client size** aided by:
|
||||
- lazy loading of flash transports.
|
||||
- lack of redundant transports.
|
||||
- **Scalable**
|
||||
- load balancer friendly
|
||||
- **Future proof**
|
||||
- **100% Node.JS core style**
|
||||
- No API sugar (left for higher level projects)
|
||||
|
||||
## API
|
||||
|
||||
### Server
|
||||
|
||||
<hr><br>
|
||||
|
||||
#### Top-level
|
||||
|
||||
These are exposed by `require('engine.io')`:
|
||||
|
||||
##### Events
|
||||
|
||||
- `flush`
|
||||
- Called when a socket buffer is being flushed.
|
||||
- **Arguments**
|
||||
- `Socket`: socket being flushed
|
||||
- `Array`: write buffer
|
||||
- `drain`
|
||||
- Called when a socket buffer is drained
|
||||
- **Arguments**
|
||||
- `Socket`: socket being flushed
|
||||
|
||||
##### Properties
|
||||
|
||||
- `protocol` _(Number)_: protocol revision number
|
||||
- `Server`: Server class constructor
|
||||
- `Socket`: Socket class constructor
|
||||
- `Transport` _(Function)_: transport constructor
|
||||
- `transports` _(Object)_: map of available transports
|
||||
|
||||
##### Methods
|
||||
|
||||
- `()`
|
||||
- Returns a new `Server` instance. If the first argument is an `http.Server` then the
|
||||
new `Server` instance will be attached to it. Otherwise, the arguments are passed
|
||||
directly to the `Server` constructor.
|
||||
- **Parameters**
|
||||
- `http.Server`: optional, server to attach to.
|
||||
- `Object`: optional, options object (see `Server#constructor` api docs below)
|
||||
|
||||
The following are identical ways to instantiate a server and then attach it.
|
||||
|
||||
```js
|
||||
const httpServer; // previously created with `http.createServer();` from node.js api.
|
||||
|
||||
// create a server first, and then attach
|
||||
const eioServer = require('engine.io').Server();
|
||||
eioServer.attach(httpServer);
|
||||
|
||||
// or call the module as a function to get `Server`
|
||||
const eioServer = require('engine.io')();
|
||||
eioServer.attach(httpServer);
|
||||
|
||||
// immediately attach
|
||||
const eioServer = require('engine.io')(httpServer);
|
||||
|
||||
// with custom options
|
||||
const eioServer = require('engine.io')(httpServer, {
|
||||
maxHttpBufferSize: 1e3
|
||||
});
|
||||
```
|
||||
|
||||
- `listen`
|
||||
- Creates an `http.Server` which listens on the given port and attaches WS
|
||||
to it. It returns `501 Not Implemented` for regular http requests.
|
||||
- **Parameters**
|
||||
- `Number`: port to listen on.
|
||||
- `Object`: optional, options object
|
||||
- `Function`: callback for `listen`.
|
||||
- **Options**
|
||||
- All options from `Server.attach` method, documented below.
|
||||
- **Additionally** See Server `constructor` below for options you can pass for creating the new Server
|
||||
- **Returns** `Server`
|
||||
|
||||
```js
|
||||
const engine = require('engine.io');
|
||||
const server = engine.listen(3000, {
|
||||
pingTimeout: 2000,
|
||||
pingInterval: 10000
|
||||
});
|
||||
|
||||
server.on('connection', /* ... */);
|
||||
```
|
||||
|
||||
- `attach`
|
||||
- Captures `upgrade` requests for a `http.Server`. In other words, makes
|
||||
a regular http.Server WebSocket-compatible.
|
||||
- **Parameters**
|
||||
- `http.Server`: server to attach to.
|
||||
- `Object`: optional, options object
|
||||
- **Options**
|
||||
- All options from `Server.attach` method, documented below.
|
||||
- **Additionally** See Server `constructor` below for options you can pass for creating the new Server
|
||||
- **Returns** `Server` a new Server instance.
|
||||
|
||||
```js
|
||||
const engine = require('engine.io');
|
||||
const httpServer = require('http').createServer().listen(3000);
|
||||
const server = engine.attach(httpServer, {
|
||||
wsEngine: require('eiows').Server // requires having eiows as dependency
|
||||
});
|
||||
|
||||
server.on('connection', /* ... */);
|
||||
```
|
||||
|
||||
#### Server
|
||||
|
||||
The main server/manager. _Inherits from EventEmitter_.
|
||||
|
||||
##### Events
|
||||
|
||||
- `connection`
|
||||
- Fired when a new connection is established.
|
||||
- **Arguments**
|
||||
- `Socket`: a Socket object
|
||||
|
||||
- `initial_headers`
|
||||
- Fired on the first request of the connection, before writing the response headers
|
||||
- **Arguments**
|
||||
- `headers` (`Object`): a hash of headers
|
||||
- `req` (`http.IncomingMessage`): the request
|
||||
|
||||
- `headers`
|
||||
- Fired on the all requests of the connection, before writing the response headers
|
||||
- **Arguments**
|
||||
- `headers` (`Object`): a hash of headers
|
||||
- `req` (`http.IncomingMessage`): the request
|
||||
|
||||
- `connection_error`
|
||||
- Fired when an error occurs when establishing the connection.
|
||||
- **Arguments**
|
||||
- `error`: an object with following properties:
|
||||
- `req` (`http.IncomingMessage`): the request that was dropped
|
||||
- `code` (`Number`): one of `Server.errors`
|
||||
- `message` (`string`): one of `Server.errorMessages`
|
||||
- `context` (`Object`): extra info about the error
|
||||
|
||||
| Code | Message |
|
||||
| ---- | ------- |
|
||||
| 0 | "Transport unknown"
|
||||
| 1 | "Session ID unknown"
|
||||
| 2 | "Bad handshake method"
|
||||
| 3 | "Bad request"
|
||||
| 4 | "Forbidden"
|
||||
| 5 | "Unsupported protocol version"
|
||||
|
||||
|
||||
##### Properties
|
||||
|
||||
**Important**: if you plan to use Engine.IO in a scalable way, please
|
||||
keep in mind the properties below will only reflect the clients connected
|
||||
to a single process.
|
||||
|
||||
- `clients` _(Object)_: hash of connected clients by id.
|
||||
- `clientsCount` _(Number)_: number of connected clients.
|
||||
|
||||
##### Methods
|
||||
|
||||
- **constructor**
|
||||
- Initializes the server
|
||||
- **Parameters**
|
||||
- `Object`: optional, options object
|
||||
- **Options**
|
||||
- `pingTimeout` (`Number`): how many ms without a pong packet to
|
||||
consider the connection closed (`20000`)
|
||||
- `pingInterval` (`Number`): how many ms before sending a new ping
|
||||
packet (`25000`)
|
||||
- `upgradeTimeout` (`Number`): how many ms before an uncompleted transport upgrade is cancelled (`10000`)
|
||||
- `maxHttpBufferSize` (`Number`): how many bytes or characters a message
|
||||
can be, before closing the session (to avoid DoS). Default
|
||||
value is `1E6`.
|
||||
- `allowRequest` (`Function`): A function that receives a given handshake
|
||||
or upgrade request as its first parameter, and can decide whether to
|
||||
continue or not. The second argument is a function that needs to be
|
||||
called with the decided information: `fn(err, success)`, where
|
||||
`success` is a boolean value where false means that the request is
|
||||
rejected, and err is an error code.
|
||||
- `transports` (`<Array> String`): transports to allow connections
|
||||
to (`['polling', 'websocket']`)
|
||||
- `allowUpgrades` (`Boolean`): whether to allow transport upgrades
|
||||
(`true`)
|
||||
- `perMessageDeflate` (`Object|Boolean`): parameters of the WebSocket permessage-deflate extension
|
||||
(see [ws module](https://github.com/einaros/ws) api docs). Set to `true` to enable. (defaults to `false`)
|
||||
- `threshold` (`Number`): data is compressed only if the byte size is above this value (`1024`)
|
||||
- `httpCompression` (`Object|Boolean`): parameters of the http compression for the polling transports
|
||||
(see [zlib](http://nodejs.org/api/zlib.html#zlib_options) api docs). Set to `false` to disable. (`true`)
|
||||
- `threshold` (`Number`): data is compressed only if the byte size is above this value (`1024`)
|
||||
- `cookie` (`Object|Boolean`): configuration of the cookie that
|
||||
contains the client sid to send as part of handshake response
|
||||
headers. This cookie might be used for sticky-session. Defaults to not sending any cookie (`false`).
|
||||
See [here](https://github.com/jshttp/cookie#options-1) for all supported options.
|
||||
- `wsEngine` (`Function`): what WebSocket server implementation to use. Specified module must conform to the `ws` interface (see [ws module api docs](https://github.com/websockets/ws/blob/master/doc/ws.md)). Default value is `ws`. An alternative c++ addon is also available by installing `eiows` module.
|
||||
- `cors` (`Object`): the options that will be forwarded to the cors module. See [there](https://github.com/expressjs/cors#configuration-options) for all available options. Defaults to no CORS allowed.
|
||||
- `initialPacket` (`Object`): an optional packet which will be concatenated to the handshake packet emitted by Engine.IO.
|
||||
- `allowEIO3` (`Boolean`): whether to support v3 Engine.IO clients (defaults to `false`)
|
||||
- `close`
|
||||
- Closes all clients
|
||||
- **Returns** `Server` for chaining
|
||||
- `handleRequest`
|
||||
- Called internally when a `Engine` request is intercepted.
|
||||
- **Parameters**
|
||||
- `http.IncomingMessage`: a node request object
|
||||
- `http.ServerResponse`: a node response object
|
||||
- **Returns** `Server` for chaining
|
||||
- `handleUpgrade`
|
||||
- Called internally when a `Engine` ws upgrade is intercepted.
|
||||
- **Parameters** (same as `upgrade` event)
|
||||
- `http.IncomingMessage`: a node request object
|
||||
- `net.Stream`: TCP socket for the request
|
||||
- `Buffer`: legacy tail bytes
|
||||
- **Returns** `Server` for chaining
|
||||
- `attach`
|
||||
- Attach this Server instance to an `http.Server`
|
||||
- Captures `upgrade` requests for a `http.Server`. In other words, makes
|
||||
a regular http.Server WebSocket-compatible.
|
||||
- **Parameters**
|
||||
- `http.Server`: server to attach to.
|
||||
- `Object`: optional, options object
|
||||
- **Options**
|
||||
- `path` (`String`): name of the path to capture (`/engine.io`).
|
||||
- `destroyUpgrade` (`Boolean`): destroy unhandled upgrade requests (`true`)
|
||||
- `destroyUpgradeTimeout` (`Number`): milliseconds after which unhandled requests are ended (`1000`)
|
||||
- `generateId`
|
||||
- Generate a socket id.
|
||||
- Overwrite this method to generate your custom socket id.
|
||||
- **Parameters**
|
||||
- `http.IncomingMessage`: a node request object
|
||||
- **Returns** A socket id for connected client.
|
||||
|
||||
<hr><br>
|
||||
|
||||
#### Socket
|
||||
|
||||
A representation of a client. _Inherits from EventEmitter_.
|
||||
|
||||
##### Events
|
||||
|
||||
- `close`
|
||||
- Fired when the client is disconnected.
|
||||
- **Arguments**
|
||||
- `String`: reason for closing
|
||||
- `Object`: description object (optional)
|
||||
- `message`
|
||||
- Fired when the client sends a message.
|
||||
- **Arguments**
|
||||
- `String` or `Buffer`: Unicode string or Buffer with binary contents
|
||||
- `error`
|
||||
- Fired when an error occurs.
|
||||
- **Arguments**
|
||||
- `Error`: error object
|
||||
- `upgrading`
|
||||
- Fired when the client starts the upgrade to a better transport like WebSocket.
|
||||
- **Arguments**
|
||||
- `Object`: the transport
|
||||
- `upgrade`
|
||||
- Fired when the client completes the upgrade to a better transport like WebSocket.
|
||||
- **Arguments**
|
||||
- `Object`: the transport
|
||||
- `flush`
|
||||
- Called when the write buffer is being flushed.
|
||||
- **Arguments**
|
||||
- `Array`: write buffer
|
||||
- `drain`
|
||||
- Called when the write buffer is drained
|
||||
- `packet`
|
||||
- Called when a socket received a packet (`message`, `ping`)
|
||||
- **Arguments**
|
||||
- `type`: packet type
|
||||
- `data`: packet data (if type is message)
|
||||
- `packetCreate`
|
||||
- Called before a socket sends a packet (`message`, `ping`)
|
||||
- **Arguments**
|
||||
- `type`: packet type
|
||||
- `data`: packet data (if type is message)
|
||||
- `heartbeat`
|
||||
- Called when `ping` or `pong` packed is received (depends of client version)
|
||||
|
||||
##### Properties
|
||||
|
||||
- `id` _(String)_: unique identifier
|
||||
- `server` _(Server)_: engine parent reference
|
||||
- `request` _(http.IncomingMessage)_: request that originated the Socket
|
||||
- `upgraded` _(Boolean)_: whether the transport has been upgraded
|
||||
- `readyState` _(String)_: opening|open|closing|closed
|
||||
- `transport` _(Transport)_: transport reference
|
||||
|
||||
##### Methods
|
||||
|
||||
- `send`:
|
||||
- Sends a message, performing `message = toString(arguments[0])` unless
|
||||
sending binary data, which is sent as is.
|
||||
- **Parameters**
|
||||
- `String` | `Buffer` | `ArrayBuffer` | `ArrayBufferView`: a string or any object implementing `toString()`, with outgoing data, or a Buffer or ArrayBuffer with binary data. Also any ArrayBufferView can be sent as is.
|
||||
- `Object`: optional, options object
|
||||
- `Function`: optional, a callback executed when the message gets flushed out by the transport
|
||||
- **Options**
|
||||
- `compress` (`Boolean`): whether to compress sending data. This option might be ignored and forced to be `true` when using polling. (`true`)
|
||||
- **Returns** `Socket` for chaining
|
||||
- `close`
|
||||
- Disconnects the client
|
||||
- **Returns** `Socket` for chaining
|
||||
|
||||
### Client
|
||||
|
||||
<hr><br>
|
||||
|
||||
Exposed in the `eio` global namespace (in the browser), or by
|
||||
`require('engine.io-client')` (in Node.JS).
|
||||
|
||||
For the client API refer to the
|
||||
[engine-client](http://github.com/learnboost/engine.io-client) repository.
|
||||
|
||||
## Debug / logging
|
||||
|
||||
Engine.IO is powered by [debug](http://github.com/visionmedia/debug).
|
||||
In order to see all the debug output, run your app with the environment variable
|
||||
`DEBUG` including the desired scope.
|
||||
|
||||
To see the output from all of Engine.IO's debugging scopes you can use:
|
||||
|
||||
```
|
||||
DEBUG=engine* node myapp
|
||||
```
|
||||
|
||||
## Transports
|
||||
|
||||
- `polling`: XHR / JSONP polling transport.
|
||||
- `websocket`: WebSocket transport.
|
||||
|
||||
## Plugins
|
||||
|
||||
- [engine.io-conflation](https://github.com/EugenDueck/engine.io-conflation): Makes **conflation and aggregation** of messages straightforward.
|
||||
|
||||
## Support
|
||||
|
||||
The support channels for `engine.io` are the same as `socket.io`:
|
||||
- irc.freenode.net **#socket.io**
|
||||
- [Google Groups](http://groups.google.com/group/socket_io)
|
||||
- [Website](http://socket.io)
|
||||
|
||||
## Development
|
||||
|
||||
To contribute patches, run tests or benchmarks, make sure to clone the
|
||||
repository:
|
||||
|
||||
```
|
||||
git clone git://github.com/LearnBoost/engine.io.git
|
||||
```
|
||||
|
||||
Then:
|
||||
|
||||
```
|
||||
cd engine.io
|
||||
npm install
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
Tests run with `npm test`. It runs the server tests that are aided by
|
||||
the usage of `engine.io-client`.
|
||||
|
||||
Make sure `npm install` is run first.
|
||||
|
||||
## Goals
|
||||
|
||||
The main goal of `Engine` is ensuring the most reliable realtime communication.
|
||||
Unlike the previous Socket.IO core, it always establishes a long-polling
|
||||
connection first, then tries to upgrade to better transports that are "tested" on
|
||||
the side.
|
||||
|
||||
During the lifetime of the Socket.IO projects, we've found countless drawbacks
|
||||
to relying on `HTML5 WebSocket` or `Flash Socket` as the first connection
|
||||
mechanisms.
|
||||
|
||||
Both are clearly the _right way_ of establishing a bidirectional communication,
|
||||
with HTML5 WebSocket being the way of the future. However, to answer most business
|
||||
needs, alternative traditional HTTP 1.1 mechanisms are just as good as delivering
|
||||
the same solution.
|
||||
|
||||
WebSocket based connections have two fundamental benefits:
|
||||
|
||||
1. **Better server performance**
|
||||
- _A: Load balancers_<br>
|
||||
Load balancing a long polling connection poses a serious architectural nightmare
|
||||
since requests can come from any number of open sockets by the user agent, but
|
||||
they all need to be routed to the process and computer that owns the `Engine`
|
||||
connection. This negatively impacts RAM and CPU usage.
|
||||
- _B: Network traffic_<br>
|
||||
WebSocket is designed around the premise that each message frame has to be
|
||||
surrounded by the least amount of data. In HTTP 1.1 transports, each message
|
||||
frame is surrounded by HTTP headers and chunked encoding frames. If you try to
|
||||
send the message _"Hello world"_ with xhr-polling, the message ultimately
|
||||
becomes larger than if you were to send it with WebSocket.
|
||||
- _C: Lightweight parser_<br>
|
||||
As an effect of **B**, the server has to do a lot more work to parse the network
|
||||
data and figure out the message when traditional HTTP requests are used
|
||||
(as in long polling). This means that another advantage of WebSocket is
|
||||
less server CPU usage.
|
||||
|
||||
2. **Better user experience**
|
||||
|
||||
Due to the reasons stated in point **1**, the most important effect of being able
|
||||
to establish a WebSocket connection is raw data transfer speed, which translates
|
||||
in _some_ cases in better user experience.
|
||||
|
||||
Applications with heavy realtime interaction (such as games) will benefit greatly,
|
||||
whereas applications like realtime chat (Gmail/Facebook), newsfeeds (Facebook) or
|
||||
timelines (Twitter) will have negligible user experience improvements.
|
||||
|
||||
Having said this, attempting to establish a WebSocket connection directly so far has
|
||||
proven problematic:
|
||||
|
||||
1. **Proxies**<br>
|
||||
Many corporate proxies block WebSocket traffic.
|
||||
|
||||
2. **Personal firewall and antivirus software**<br>
|
||||
As a result of our research, we've found that at least 3 personal security
|
||||
applications block WebSocket traffic.
|
||||
|
||||
3. **Cloud application platforms**<br>
|
||||
Platforms like Heroku or No.de have had trouble keeping up with the fast-paced
|
||||
nature of the evolution of the WebSocket protocol. Applications therefore end up
|
||||
inevitably using long polling, but the seamless installation experience of
|
||||
Socket.IO we strive for (_"require() it and it just works"_) disappears.
|
||||
|
||||
Some of these problems have solutions. In the case of proxies and personal programs,
|
||||
however, the solutions many times involve upgrading software. Experience has shown
|
||||
that relying on client software upgrades to deliver a business solution is
|
||||
fruitless: the very existence of this project has to do with a fragmented panorama
|
||||
of user agent distribution, with clients connecting with latest versions of the most
|
||||
modern user agents (Chrome, Firefox and Safari), but others with versions as low as
|
||||
IE 5.5.
|
||||
|
||||
From the user perspective, an unsuccessful WebSocket connection can translate in
|
||||
up to at least 10 seconds of waiting for the realtime application to begin
|
||||
exchanging data. This **perceptively** hurts user experience.
|
||||
|
||||
To summarize, **Engine** focuses on reliability and user experience first, marginal
|
||||
potential UX improvements and increased server performance second. `Engine` is the
|
||||
result of all the lessons learned with WebSocket in the wild.
|
||||
|
||||
## Architecture
|
||||
|
||||
The main premise of `Engine`, and the core of its existence, is the ability to
|
||||
swap transports on the fly. A connection starts as xhr-polling, but it can
|
||||
switch to WebSocket.
|
||||
|
||||
The central problem this poses is: how do we switch transports without losing
|
||||
messages?
|
||||
|
||||
`Engine` only switches from polling to another transport in between polling
|
||||
cycles. Since the server closes the connection after a certain timeout when
|
||||
there's no activity, and the polling transport implementation buffers messages
|
||||
in between connections, this ensures no message loss and optimal performance.
|
||||
|
||||
Another benefit of this design is that we workaround almost all the limitations
|
||||
of **Flash Socket**, such as slow connection times, increased file size (we can
|
||||
safely lazy load it without hurting user experience), etc.
|
||||
|
||||
## FAQ
|
||||
|
||||
### Can I use engine without Socket.IO ?
|
||||
|
||||
Absolutely. Although the recommended framework for building realtime applications
|
||||
is Socket.IO, since it provides fundamental features for real-world applications
|
||||
such as multiplexing, reconnection support, etc.
|
||||
|
||||
`Engine` is to Socket.IO what Connect is to Express. An essential piece for building
|
||||
realtime frameworks, but something you _probably_ won't be using for building
|
||||
actual applications.
|
||||
|
||||
### Does the server serve the client?
|
||||
|
||||
No. The main reason is that `Engine` is meant to be bundled with frameworks.
|
||||
Socket.IO includes `Engine`, therefore serving two clients is not necessary. If
|
||||
you use Socket.IO, including
|
||||
|
||||
```html
|
||||
<script src="/socket.io/socket.io.js">
|
||||
```
|
||||
|
||||
has you covered.
|
||||
|
||||
### Can I implement `Engine` in other languages?
|
||||
|
||||
Absolutely. The [engine.io-protocol](https://github.com/socketio/engine.io-protocol)
|
||||
repository contains the most up-to-date description of the specification
|
||||
at all times.
|
||||
|
||||
## License
|
||||
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2014 Guillermo Rauch <guillermo@learnboost.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
113
node_modules/engine.io/build/contrib/types.cookie.d.ts
generated
vendored
Normal file
113
node_modules/engine.io/build/contrib/types.cookie.d.ts
generated
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Basic HTTP cookie parser and serializer for HTTP servers.
|
||||
*/
|
||||
/**
|
||||
* Additional serialization options
|
||||
*/
|
||||
export interface CookieSerializeOptions {
|
||||
/**
|
||||
* Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.3|Domain Set-Cookie attribute}. By default, no
|
||||
* domain is set, and most clients will consider the cookie to apply to only
|
||||
* the current domain.
|
||||
*/
|
||||
domain?: string | undefined;
|
||||
/**
|
||||
* Specifies a function that will be used to encode a cookie's value. Since
|
||||
* value of a cookie has a limited character set (and must be a simple
|
||||
* string), this function can be used to encode a value into a string suited
|
||||
* for a cookie's value.
|
||||
*
|
||||
* The default function is the global `encodeURIComponent`, which will
|
||||
* encode a JavaScript string into UTF-8 byte sequences and then URL-encode
|
||||
* any that fall outside of the cookie range.
|
||||
*/
|
||||
encode?(value: string): string;
|
||||
/**
|
||||
* Specifies the `Date` object to be the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.1|`Expires` `Set-Cookie` attribute}. By default,
|
||||
* no expiration is set, and most clients will consider this a "non-persistent cookie" and will delete
|
||||
* it on a condition like exiting a web browser application.
|
||||
*
|
||||
* *Note* the {@link https://tools.ietf.org/html/rfc6265#section-5.3|cookie storage model specification}
|
||||
* states that if both `expires` and `maxAge` are set, then `maxAge` takes precedence, but it is
|
||||
* possible not all clients by obey this, so if both are set, they should
|
||||
* point to the same date and time.
|
||||
*/
|
||||
expires?: Date | undefined;
|
||||
/**
|
||||
* Specifies the boolean value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.6|`HttpOnly` `Set-Cookie` attribute}.
|
||||
* When truthy, the `HttpOnly` attribute is set, otherwise it is not. By
|
||||
* default, the `HttpOnly` attribute is not set.
|
||||
*
|
||||
* *Note* be careful when setting this to true, as compliant clients will
|
||||
* not allow client-side JavaScript to see the cookie in `document.cookie`.
|
||||
*/
|
||||
httpOnly?: boolean | undefined;
|
||||
/**
|
||||
* Specifies the number (in seconds) to be the value for the `Max-Age`
|
||||
* `Set-Cookie` attribute. The given number will be converted to an integer
|
||||
* by rounding down. By default, no maximum age is set.
|
||||
*
|
||||
* *Note* the {@link https://tools.ietf.org/html/rfc6265#section-5.3|cookie storage model specification}
|
||||
* states that if both `expires` and `maxAge` are set, then `maxAge` takes precedence, but it is
|
||||
* possible not all clients by obey this, so if both are set, they should
|
||||
* point to the same date and time.
|
||||
*/
|
||||
maxAge?: number | undefined;
|
||||
/**
|
||||
* Specifies the `boolean` value for the [`Partitioned` `Set-Cookie`](rfc-cutler-httpbis-partitioned-cookies)
|
||||
* attribute. When truthy, the `Partitioned` attribute is set, otherwise it is not. By default, the
|
||||
* `Partitioned` attribute is not set.
|
||||
*
|
||||
* **note** This is an attribute that has not yet been fully standardized, and may change in the future.
|
||||
* This also means many clients may ignore this attribute until they understand it.
|
||||
*
|
||||
* More information about can be found in [the proposal](https://github.com/privacycg/CHIPS)
|
||||
*/
|
||||
partitioned?: boolean | undefined;
|
||||
/**
|
||||
* Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.4|`Path` `Set-Cookie` attribute}.
|
||||
* By default, the path is considered the "default path".
|
||||
*/
|
||||
path?: string | undefined;
|
||||
/**
|
||||
* Specifies the `string` to be the value for the [`Priority` `Set-Cookie` attribute][rfc-west-cookie-priority-00-4.1].
|
||||
*
|
||||
* - `'low'` will set the `Priority` attribute to `Low`.
|
||||
* - `'medium'` will set the `Priority` attribute to `Medium`, the default priority when not set.
|
||||
* - `'high'` will set the `Priority` attribute to `High`.
|
||||
*
|
||||
* More information about the different priority levels can be found in
|
||||
* [the specification][rfc-west-cookie-priority-00-4.1].
|
||||
*
|
||||
* **note** This is an attribute that has not yet been fully standardized, and may change in the future.
|
||||
* This also means many clients may ignore this attribute until they understand it.
|
||||
*/
|
||||
priority?: "low" | "medium" | "high" | undefined;
|
||||
/**
|
||||
* Specifies the boolean or string to be the value for the {@link https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.7|`SameSite` `Set-Cookie` attribute}.
|
||||
*
|
||||
* - `true` will set the `SameSite` attribute to `Strict` for strict same
|
||||
* site enforcement.
|
||||
* - `false` will not set the `SameSite` attribute.
|
||||
* - `'lax'` will set the `SameSite` attribute to Lax for lax same site
|
||||
* enforcement.
|
||||
* - `'strict'` will set the `SameSite` attribute to Strict for strict same
|
||||
* site enforcement.
|
||||
* - `'none'` will set the SameSite attribute to None for an explicit
|
||||
* cross-site cookie.
|
||||
*
|
||||
* More information about the different enforcement levels can be found in {@link https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.7|the specification}.
|
||||
*
|
||||
* *note* This is an attribute that has not yet been fully standardized, and may change in the future. This also means many clients may ignore this attribute until they understand it.
|
||||
*/
|
||||
sameSite?: true | false | "lax" | "strict" | "none" | undefined;
|
||||
/**
|
||||
* Specifies the boolean value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.5|`Secure` `Set-Cookie` attribute}. When truthy, the
|
||||
* `Secure` attribute is set, otherwise it is not. By default, the `Secure` attribute is not set.
|
||||
*
|
||||
* *Note* be careful when setting this to `true`, as compliant clients will
|
||||
* not send the cookie back to the server in the future if the browser does
|
||||
* not have an HTTPS connection.
|
||||
*/
|
||||
secure?: boolean | undefined;
|
||||
}
|
||||
6
node_modules/engine.io/build/contrib/types.cookie.js
generated
vendored
Normal file
6
node_modules/engine.io/build/contrib/types.cookie.js
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
"use strict";
|
||||
// imported from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/b83cf9ef8b044e69f05b2a00aa7c6cb767a9acd2/types/cookie/index.d.ts (now deleted)
|
||||
/**
|
||||
* Basic HTTP cookie parser and serializer for HTTP servers.
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
26
node_modules/engine.io/build/engine.io.d.ts
generated
vendored
Normal file
26
node_modules/engine.io/build/engine.io.d.ts
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Server, AttachOptions, ServerOptions } from "./server";
|
||||
import transports from "./transports/index";
|
||||
import * as parser from "engine.io-parser";
|
||||
export { Server, transports, listen, attach, parser };
|
||||
export type { AttachOptions, ServerOptions, BaseServer } from "./server";
|
||||
export { uServer } from "./userver";
|
||||
export { Socket } from "./socket";
|
||||
export { Transport } from "./transport";
|
||||
export declare const protocol = 4;
|
||||
/**
|
||||
* Creates an http.Server exclusively used for WS upgrades.
|
||||
*
|
||||
* @param {Number} port
|
||||
* @param {Function} callback
|
||||
* @param {Object} options
|
||||
* @return {Server} websocket.io server
|
||||
*/
|
||||
declare function listen(port: any, options: AttachOptions & ServerOptions, fn: any): Server;
|
||||
/**
|
||||
* Captures upgrade requests for a http.Server.
|
||||
*
|
||||
* @param {http.Server} server
|
||||
* @param {Object} options
|
||||
* @return {Server} engine server
|
||||
*/
|
||||
declare function attach(server: any, options: AttachOptions & ServerOptions): Server;
|
||||
54
node_modules/engine.io/build/engine.io.js
generated
vendored
Normal file
54
node_modules/engine.io/build/engine.io.js
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.protocol = exports.Transport = exports.Socket = exports.uServer = exports.parser = exports.transports = exports.Server = void 0;
|
||||
exports.listen = listen;
|
||||
exports.attach = attach;
|
||||
const http_1 = require("http");
|
||||
const server_1 = require("./server");
|
||||
Object.defineProperty(exports, "Server", { enumerable: true, get: function () { return server_1.Server; } });
|
||||
const index_1 = require("./transports/index");
|
||||
exports.transports = index_1.default;
|
||||
const parser = require("engine.io-parser");
|
||||
exports.parser = parser;
|
||||
var userver_1 = require("./userver");
|
||||
Object.defineProperty(exports, "uServer", { enumerable: true, get: function () { return userver_1.uServer; } });
|
||||
var socket_1 = require("./socket");
|
||||
Object.defineProperty(exports, "Socket", { enumerable: true, get: function () { return socket_1.Socket; } });
|
||||
var transport_1 = require("./transport");
|
||||
Object.defineProperty(exports, "Transport", { enumerable: true, get: function () { return transport_1.Transport; } });
|
||||
exports.protocol = parser.protocol;
|
||||
/**
|
||||
* Creates an http.Server exclusively used for WS upgrades.
|
||||
*
|
||||
* @param {Number} port
|
||||
* @param {Function} callback
|
||||
* @param {Object} options
|
||||
* @return {Server} websocket.io server
|
||||
*/
|
||||
function listen(port, options, fn) {
|
||||
if ("function" === typeof options) {
|
||||
fn = options;
|
||||
options = {};
|
||||
}
|
||||
const server = (0, http_1.createServer)(function (req, res) {
|
||||
res.writeHead(501);
|
||||
res.end("Not Implemented");
|
||||
});
|
||||
// create engine server
|
||||
const engine = attach(server, options);
|
||||
engine.httpServer = server;
|
||||
server.listen(port, fn);
|
||||
return engine;
|
||||
}
|
||||
/**
|
||||
* Captures upgrade requests for a http.Server.
|
||||
*
|
||||
* @param {http.Server} server
|
||||
* @param {Object} options
|
||||
* @return {Server} engine server
|
||||
*/
|
||||
function attach(server, options) {
|
||||
const engine = new server_1.Server(options);
|
||||
engine.attach(server, options);
|
||||
return engine;
|
||||
}
|
||||
94
node_modules/engine.io/build/parser-v3/index.d.ts
generated
vendored
Normal file
94
node_modules/engine.io/build/parser-v3/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Current protocol version.
|
||||
*/
|
||||
export declare const protocol = 3;
|
||||
/**
|
||||
* Packet types.
|
||||
*/
|
||||
export declare const packets: {
|
||||
open: number;
|
||||
close: number;
|
||||
ping: number;
|
||||
pong: number;
|
||||
message: number;
|
||||
upgrade: number;
|
||||
noop: number;
|
||||
};
|
||||
/**
|
||||
* Encodes a packet.
|
||||
*
|
||||
* <packet type id> [ <data> ]
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* 5hello world
|
||||
* 3
|
||||
* 4
|
||||
*
|
||||
* Binary is encoded in an identical principle
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
export declare function encodePacket(packet: any, supportsBinary: any, utf8encode: any, callback: any): any;
|
||||
/**
|
||||
* Encodes a packet with binary data in a base64 string
|
||||
*
|
||||
* @param {Object} packet, has `type` and `data`
|
||||
* @return {String} base64 encoded message
|
||||
*/
|
||||
export declare function encodeBase64Packet(packet: any, callback: any): any;
|
||||
/**
|
||||
* Decodes a packet. Data also available as an ArrayBuffer if requested.
|
||||
*
|
||||
* @return {Object} with `type` and `data` (if any)
|
||||
* @api private
|
||||
*/
|
||||
export declare function decodePacket(data: any, binaryType: any, utf8decode: any): {
|
||||
type: string;
|
||||
data: any;
|
||||
} | {
|
||||
type: string;
|
||||
data?: undefined;
|
||||
};
|
||||
/**
|
||||
* Decodes a packet encoded in a base64 string.
|
||||
*
|
||||
* @param {String} base64 encoded message
|
||||
* @return {Object} with `type` and `data` (if any)
|
||||
*/
|
||||
export declare function decodeBase64Packet(msg: any, binaryType: any): {
|
||||
type: string;
|
||||
data: Buffer;
|
||||
};
|
||||
/**
|
||||
* Encodes multiple messages (payload).
|
||||
*
|
||||
* <length>:data
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* 11:hello world2:hi
|
||||
*
|
||||
* If any contents are binary, they will be encoded as base64 strings. Base64
|
||||
* encoded strings are marked with a b before the length specifier
|
||||
*
|
||||
* @param {Array} packets
|
||||
* @api private
|
||||
*/
|
||||
export declare function encodePayload(packets: any, supportsBinary: any, callback: any): any;
|
||||
export declare function decodePayload(data: any, binaryType: any, callback: any): any;
|
||||
/**
|
||||
* Encodes multiple messages (payload) as binary.
|
||||
*
|
||||
* <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number
|
||||
* 255><data>
|
||||
*
|
||||
* Example:
|
||||
* 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers
|
||||
*
|
||||
* @param {Array} packets
|
||||
* @return {Buffer} encoded payload
|
||||
* @api private
|
||||
*/
|
||||
export declare function encodePayloadAsBinary(packets: any, callback: any): any;
|
||||
export declare function decodePayloadAsBinary(data: any, binaryType: any, callback: any): any;
|
||||
424
node_modules/engine.io/build/parser-v3/index.js
generated
vendored
Normal file
424
node_modules/engine.io/build/parser-v3/index.js
generated
vendored
Normal file
@@ -0,0 +1,424 @@
|
||||
"use strict";
|
||||
// imported from https://github.com/socketio/engine.io-parser/tree/2.2.x
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.packets = exports.protocol = void 0;
|
||||
exports.encodePacket = encodePacket;
|
||||
exports.encodeBase64Packet = encodeBase64Packet;
|
||||
exports.decodePacket = decodePacket;
|
||||
exports.decodeBase64Packet = decodeBase64Packet;
|
||||
exports.encodePayload = encodePayload;
|
||||
exports.decodePayload = decodePayload;
|
||||
exports.encodePayloadAsBinary = encodePayloadAsBinary;
|
||||
exports.decodePayloadAsBinary = decodePayloadAsBinary;
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
var utf8 = require('./utf8');
|
||||
/**
|
||||
* Current protocol version.
|
||||
*/
|
||||
exports.protocol = 3;
|
||||
const hasBinary = (packets) => {
|
||||
for (const packet of packets) {
|
||||
if (packet.data instanceof ArrayBuffer || ArrayBuffer.isView(packet.data)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
/**
|
||||
* Packet types.
|
||||
*/
|
||||
exports.packets = {
|
||||
open: 0 // non-ws
|
||||
,
|
||||
close: 1 // non-ws
|
||||
,
|
||||
ping: 2,
|
||||
pong: 3,
|
||||
message: 4,
|
||||
upgrade: 5,
|
||||
noop: 6
|
||||
};
|
||||
var packetslist = Object.keys(exports.packets);
|
||||
/**
|
||||
* Premade error packet.
|
||||
*/
|
||||
var err = { type: 'error', data: 'parser error' };
|
||||
const EMPTY_BUFFER = Buffer.concat([]);
|
||||
/**
|
||||
* Encodes a packet.
|
||||
*
|
||||
* <packet type id> [ <data> ]
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* 5hello world
|
||||
* 3
|
||||
* 4
|
||||
*
|
||||
* Binary is encoded in an identical principle
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
function encodePacket(packet, supportsBinary, utf8encode, callback) {
|
||||
if (typeof supportsBinary === 'function') {
|
||||
callback = supportsBinary;
|
||||
supportsBinary = null;
|
||||
}
|
||||
if (typeof utf8encode === 'function') {
|
||||
callback = utf8encode;
|
||||
utf8encode = null;
|
||||
}
|
||||
if (Buffer.isBuffer(packet.data)) {
|
||||
return encodeBuffer(packet, supportsBinary, callback);
|
||||
}
|
||||
else if (packet.data && (packet.data.buffer || packet.data) instanceof ArrayBuffer) {
|
||||
return encodeBuffer({ type: packet.type, data: arrayBufferToBuffer(packet.data) }, supportsBinary, callback);
|
||||
}
|
||||
// Sending data as a utf-8 string
|
||||
var encoded = exports.packets[packet.type];
|
||||
// data fragment is optional
|
||||
if (undefined !== packet.data) {
|
||||
encoded += utf8encode ? utf8.encode(String(packet.data), { strict: false }) : String(packet.data);
|
||||
}
|
||||
return callback('' + encoded);
|
||||
}
|
||||
;
|
||||
/**
|
||||
* Encode Buffer data
|
||||
*/
|
||||
function encodeBuffer(packet, supportsBinary, callback) {
|
||||
if (!supportsBinary) {
|
||||
return encodeBase64Packet(packet, callback);
|
||||
}
|
||||
var data = packet.data;
|
||||
var typeBuffer = Buffer.allocUnsafe(1);
|
||||
typeBuffer[0] = exports.packets[packet.type];
|
||||
return callback(Buffer.concat([typeBuffer, data]));
|
||||
}
|
||||
/**
|
||||
* Encodes a packet with binary data in a base64 string
|
||||
*
|
||||
* @param {Object} packet, has `type` and `data`
|
||||
* @return {String} base64 encoded message
|
||||
*/
|
||||
function encodeBase64Packet(packet, callback) {
|
||||
var data = Buffer.isBuffer(packet.data) ? packet.data : arrayBufferToBuffer(packet.data);
|
||||
var message = 'b' + exports.packets[packet.type];
|
||||
message += data.toString('base64');
|
||||
return callback(message);
|
||||
}
|
||||
;
|
||||
/**
|
||||
* Decodes a packet. Data also available as an ArrayBuffer if requested.
|
||||
*
|
||||
* @return {Object} with `type` and `data` (if any)
|
||||
* @api private
|
||||
*/
|
||||
function decodePacket(data, binaryType, utf8decode) {
|
||||
if (data === undefined) {
|
||||
return err;
|
||||
}
|
||||
var type;
|
||||
// String data
|
||||
if (typeof data === 'string') {
|
||||
type = data.charAt(0);
|
||||
if (type === 'b') {
|
||||
return decodeBase64Packet(data.slice(1), binaryType);
|
||||
}
|
||||
if (utf8decode) {
|
||||
data = tryDecode(data);
|
||||
if (data === false) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
if (Number(type) != type || !packetslist[type]) {
|
||||
return err;
|
||||
}
|
||||
if (data.length > 1) {
|
||||
return { type: packetslist[type], data: data.slice(1) };
|
||||
}
|
||||
else {
|
||||
return { type: packetslist[type] };
|
||||
}
|
||||
}
|
||||
// Binary data
|
||||
if (binaryType === 'arraybuffer') {
|
||||
// wrap Buffer/ArrayBuffer data into an Uint8Array
|
||||
var intArray = new Uint8Array(data);
|
||||
type = intArray[0];
|
||||
return { type: packetslist[type], data: intArray.buffer.slice(1) };
|
||||
}
|
||||
if (data instanceof ArrayBuffer) {
|
||||
data = arrayBufferToBuffer(data);
|
||||
}
|
||||
type = data[0];
|
||||
return { type: packetslist[type], data: data.slice(1) };
|
||||
}
|
||||
;
|
||||
function tryDecode(data) {
|
||||
try {
|
||||
data = utf8.decode(data, { strict: false });
|
||||
}
|
||||
catch (e) {
|
||||
return false;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
* Decodes a packet encoded in a base64 string.
|
||||
*
|
||||
* @param {String} base64 encoded message
|
||||
* @return {Object} with `type` and `data` (if any)
|
||||
*/
|
||||
function decodeBase64Packet(msg, binaryType) {
|
||||
var type = packetslist[msg.charAt(0)];
|
||||
var data = Buffer.from(msg.slice(1), 'base64');
|
||||
if (binaryType === 'arraybuffer') {
|
||||
var abv = new Uint8Array(data.length);
|
||||
for (var i = 0; i < abv.length; i++) {
|
||||
abv[i] = data[i];
|
||||
}
|
||||
// @ts-ignore
|
||||
data = abv.buffer;
|
||||
}
|
||||
return { type: type, data: data };
|
||||
}
|
||||
;
|
||||
/**
|
||||
* Encodes multiple messages (payload).
|
||||
*
|
||||
* <length>:data
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* 11:hello world2:hi
|
||||
*
|
||||
* If any contents are binary, they will be encoded as base64 strings. Base64
|
||||
* encoded strings are marked with a b before the length specifier
|
||||
*
|
||||
* @param {Array} packets
|
||||
* @api private
|
||||
*/
|
||||
function encodePayload(packets, supportsBinary, callback) {
|
||||
if (typeof supportsBinary === 'function') {
|
||||
callback = supportsBinary;
|
||||
supportsBinary = null;
|
||||
}
|
||||
if (supportsBinary && hasBinary(packets)) {
|
||||
return encodePayloadAsBinary(packets, callback);
|
||||
}
|
||||
if (!packets.length) {
|
||||
return callback('0:');
|
||||
}
|
||||
function encodeOne(packet, doneCallback) {
|
||||
encodePacket(packet, supportsBinary, false, function (message) {
|
||||
doneCallback(null, setLengthHeader(message));
|
||||
});
|
||||
}
|
||||
map(packets, encodeOne, function (err, results) {
|
||||
return callback(results.join(''));
|
||||
});
|
||||
}
|
||||
;
|
||||
function setLengthHeader(message) {
|
||||
return message.length + ':' + message;
|
||||
}
|
||||
/**
|
||||
* Async array map using after
|
||||
*/
|
||||
function map(ary, each, done) {
|
||||
const results = new Array(ary.length);
|
||||
let count = 0;
|
||||
for (let i = 0; i < ary.length; i++) {
|
||||
each(ary[i], (error, msg) => {
|
||||
results[i] = msg;
|
||||
if (++count === ary.length) {
|
||||
done(null, results);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Decodes data when a payload is maybe expected. Possible binary contents are
|
||||
* decoded from their base64 representation
|
||||
*
|
||||
* @param {String} data, callback method
|
||||
* @api public
|
||||
*/
|
||||
function decodePayload(data, binaryType, callback) {
|
||||
if (typeof data !== 'string') {
|
||||
return decodePayloadAsBinary(data, binaryType, callback);
|
||||
}
|
||||
if (typeof binaryType === 'function') {
|
||||
callback = binaryType;
|
||||
binaryType = null;
|
||||
}
|
||||
if (data === '') {
|
||||
// parser error - ignoring payload
|
||||
return callback(err, 0, 1);
|
||||
}
|
||||
var length = '', n, msg, packet;
|
||||
for (var i = 0, l = data.length; i < l; i++) {
|
||||
var chr = data.charAt(i);
|
||||
if (chr !== ':') {
|
||||
length += chr;
|
||||
continue;
|
||||
}
|
||||
// @ts-ignore
|
||||
if (length === '' || (length != (n = Number(length)))) {
|
||||
// parser error - ignoring payload
|
||||
return callback(err, 0, 1);
|
||||
}
|
||||
msg = data.slice(i + 1, i + 1 + n);
|
||||
if (length != msg.length) {
|
||||
// parser error - ignoring payload
|
||||
return callback(err, 0, 1);
|
||||
}
|
||||
if (msg.length) {
|
||||
packet = decodePacket(msg, binaryType, false);
|
||||
if (err.type === packet.type && err.data === packet.data) {
|
||||
// parser error in individual packet - ignoring payload
|
||||
return callback(err, 0, 1);
|
||||
}
|
||||
var more = callback(packet, i + n, l);
|
||||
if (false === more)
|
||||
return;
|
||||
}
|
||||
// advance cursor
|
||||
i += n;
|
||||
length = '';
|
||||
}
|
||||
if (length !== '') {
|
||||
// parser error - ignoring payload
|
||||
return callback(err, 0, 1);
|
||||
}
|
||||
}
|
||||
;
|
||||
/**
|
||||
*
|
||||
* Converts a buffer to a utf8.js encoded string
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
function bufferToString(buffer) {
|
||||
var str = '';
|
||||
for (var i = 0, l = buffer.length; i < l; i++) {
|
||||
str += String.fromCharCode(buffer[i]);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* Converts a utf8.js encoded string to a buffer
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
function stringToBuffer(string) {
|
||||
var buf = Buffer.allocUnsafe(string.length);
|
||||
for (var i = 0, l = string.length; i < l; i++) {
|
||||
buf.writeUInt8(string.charCodeAt(i), i);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* Converts an ArrayBuffer to a Buffer
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
function arrayBufferToBuffer(data) {
|
||||
// data is either an ArrayBuffer or ArrayBufferView.
|
||||
var length = data.byteLength || data.length;
|
||||
var offset = data.byteOffset || 0;
|
||||
return Buffer.from(data.buffer || data, offset, length);
|
||||
}
|
||||
/**
|
||||
* Encodes multiple messages (payload) as binary.
|
||||
*
|
||||
* <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number
|
||||
* 255><data>
|
||||
*
|
||||
* Example:
|
||||
* 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers
|
||||
*
|
||||
* @param {Array} packets
|
||||
* @return {Buffer} encoded payload
|
||||
* @api private
|
||||
*/
|
||||
function encodePayloadAsBinary(packets, callback) {
|
||||
if (!packets.length) {
|
||||
return callback(EMPTY_BUFFER);
|
||||
}
|
||||
map(packets, encodeOneBinaryPacket, function (err, results) {
|
||||
return callback(Buffer.concat(results));
|
||||
});
|
||||
}
|
||||
;
|
||||
function encodeOneBinaryPacket(p, doneCallback) {
|
||||
function onBinaryPacketEncode(packet) {
|
||||
var encodingLength = '' + packet.length;
|
||||
var sizeBuffer;
|
||||
if (typeof packet === 'string') {
|
||||
sizeBuffer = Buffer.allocUnsafe(encodingLength.length + 2);
|
||||
sizeBuffer[0] = 0; // is a string (not true binary = 0)
|
||||
for (var i = 0; i < encodingLength.length; i++) {
|
||||
sizeBuffer[i + 1] = parseInt(encodingLength[i], 10);
|
||||
}
|
||||
sizeBuffer[sizeBuffer.length - 1] = 255;
|
||||
return doneCallback(null, Buffer.concat([sizeBuffer, stringToBuffer(packet)]));
|
||||
}
|
||||
sizeBuffer = Buffer.allocUnsafe(encodingLength.length + 2);
|
||||
sizeBuffer[0] = 1; // is binary (true binary = 1)
|
||||
for (var i = 0; i < encodingLength.length; i++) {
|
||||
sizeBuffer[i + 1] = parseInt(encodingLength[i], 10);
|
||||
}
|
||||
sizeBuffer[sizeBuffer.length - 1] = 255;
|
||||
doneCallback(null, Buffer.concat([sizeBuffer, packet]));
|
||||
}
|
||||
encodePacket(p, true, true, onBinaryPacketEncode);
|
||||
}
|
||||
/*
|
||||
* Decodes data when a payload is maybe expected. Strings are decoded by
|
||||
* interpreting each byte as a key code for entries marked to start with 0. See
|
||||
* description of encodePayloadAsBinary
|
||||
|
||||
* @param {Buffer} data, callback method
|
||||
* @api public
|
||||
*/
|
||||
function decodePayloadAsBinary(data, binaryType, callback) {
|
||||
if (typeof binaryType === 'function') {
|
||||
callback = binaryType;
|
||||
binaryType = null;
|
||||
}
|
||||
var bufferTail = data;
|
||||
var buffers = [];
|
||||
var i;
|
||||
while (bufferTail.length > 0) {
|
||||
var strLen = '';
|
||||
var isString = bufferTail[0] === 0;
|
||||
for (i = 1;; i++) {
|
||||
if (bufferTail[i] === 255)
|
||||
break;
|
||||
// 310 = char length of Number.MAX_VALUE
|
||||
if (strLen.length > 310) {
|
||||
return callback(err, 0, 1);
|
||||
}
|
||||
strLen += '' + bufferTail[i];
|
||||
}
|
||||
bufferTail = bufferTail.slice(strLen.length + 1);
|
||||
var msgLength = parseInt(strLen, 10);
|
||||
var msg = bufferTail.slice(1, msgLength + 1);
|
||||
if (isString)
|
||||
msg = bufferToString(msg);
|
||||
buffers.push(msg);
|
||||
bufferTail = bufferTail.slice(msgLength + 1);
|
||||
}
|
||||
var total = buffers.length;
|
||||
for (i = 0; i < total; i++) {
|
||||
var buffer = buffers[i];
|
||||
callback(decodePacket(buffer, binaryType, true), i, total);
|
||||
}
|
||||
}
|
||||
;
|
||||
14
node_modules/engine.io/build/parser-v3/utf8.d.ts
generated
vendored
Normal file
14
node_modules/engine.io/build/parser-v3/utf8.d.ts
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/*! https://mths.be/utf8js v2.1.2 by @mathias */
|
||||
declare var stringFromCharCode: (...codes: number[]) => string;
|
||||
declare function ucs2decode(string: any): any[];
|
||||
declare function ucs2encode(array: any): string;
|
||||
declare function checkScalarValue(codePoint: any, strict: any): boolean;
|
||||
declare function createByte(codePoint: any, shift: any): string;
|
||||
declare function encodeCodePoint(codePoint: any, strict: any): string;
|
||||
declare function utf8encode(string: any, opts: any): string;
|
||||
declare function readContinuationByte(): number;
|
||||
declare function decodeSymbol(strict: any): any;
|
||||
declare var byteArray: any;
|
||||
declare var byteCount: any;
|
||||
declare var byteIndex: any;
|
||||
declare function utf8decode(byteString: any, opts: any): string;
|
||||
187
node_modules/engine.io/build/parser-v3/utf8.js
generated
vendored
Normal file
187
node_modules/engine.io/build/parser-v3/utf8.js
generated
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
/*! https://mths.be/utf8js v2.1.2 by @mathias */
|
||||
var stringFromCharCode = String.fromCharCode;
|
||||
// Taken from https://mths.be/punycode
|
||||
function ucs2decode(string) {
|
||||
var output = [];
|
||||
var counter = 0;
|
||||
var length = string.length;
|
||||
var value;
|
||||
var extra;
|
||||
while (counter < length) {
|
||||
value = string.charCodeAt(counter++);
|
||||
if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
|
||||
// high surrogate, and there is a next character
|
||||
extra = string.charCodeAt(counter++);
|
||||
if ((extra & 0xFC00) == 0xDC00) { // low surrogate
|
||||
output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
|
||||
}
|
||||
else {
|
||||
// unmatched surrogate; only append this code unit, in case the next
|
||||
// code unit is the high surrogate of a surrogate pair
|
||||
output.push(value);
|
||||
counter--;
|
||||
}
|
||||
}
|
||||
else {
|
||||
output.push(value);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
// Taken from https://mths.be/punycode
|
||||
function ucs2encode(array) {
|
||||
var length = array.length;
|
||||
var index = -1;
|
||||
var value;
|
||||
var output = '';
|
||||
while (++index < length) {
|
||||
value = array[index];
|
||||
if (value > 0xFFFF) {
|
||||
value -= 0x10000;
|
||||
output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
|
||||
value = 0xDC00 | value & 0x3FF;
|
||||
}
|
||||
output += stringFromCharCode(value);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
function checkScalarValue(codePoint, strict) {
|
||||
if (codePoint >= 0xD800 && codePoint <= 0xDFFF) {
|
||||
if (strict) {
|
||||
throw Error('Lone surrogate U+' + codePoint.toString(16).toUpperCase() +
|
||||
' is not a scalar value');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/*--------------------------------------------------------------------------*/
|
||||
function createByte(codePoint, shift) {
|
||||
return stringFromCharCode(((codePoint >> shift) & 0x3F) | 0x80);
|
||||
}
|
||||
function encodeCodePoint(codePoint, strict) {
|
||||
if ((codePoint & 0xFFFFFF80) == 0) { // 1-byte sequence
|
||||
return stringFromCharCode(codePoint);
|
||||
}
|
||||
var symbol = '';
|
||||
if ((codePoint & 0xFFFFF800) == 0) { // 2-byte sequence
|
||||
symbol = stringFromCharCode(((codePoint >> 6) & 0x1F) | 0xC0);
|
||||
}
|
||||
else if ((codePoint & 0xFFFF0000) == 0) { // 3-byte sequence
|
||||
if (!checkScalarValue(codePoint, strict)) {
|
||||
codePoint = 0xFFFD;
|
||||
}
|
||||
symbol = stringFromCharCode(((codePoint >> 12) & 0x0F) | 0xE0);
|
||||
symbol += createByte(codePoint, 6);
|
||||
}
|
||||
else if ((codePoint & 0xFFE00000) == 0) { // 4-byte sequence
|
||||
symbol = stringFromCharCode(((codePoint >> 18) & 0x07) | 0xF0);
|
||||
symbol += createByte(codePoint, 12);
|
||||
symbol += createByte(codePoint, 6);
|
||||
}
|
||||
symbol += stringFromCharCode((codePoint & 0x3F) | 0x80);
|
||||
return symbol;
|
||||
}
|
||||
function utf8encode(string, opts) {
|
||||
opts = opts || {};
|
||||
var strict = false !== opts.strict;
|
||||
var codePoints = ucs2decode(string);
|
||||
var length = codePoints.length;
|
||||
var index = -1;
|
||||
var codePoint;
|
||||
var byteString = '';
|
||||
while (++index < length) {
|
||||
codePoint = codePoints[index];
|
||||
byteString += encodeCodePoint(codePoint, strict);
|
||||
}
|
||||
return byteString;
|
||||
}
|
||||
/*--------------------------------------------------------------------------*/
|
||||
function readContinuationByte() {
|
||||
if (byteIndex >= byteCount) {
|
||||
throw Error('Invalid byte index');
|
||||
}
|
||||
var continuationByte = byteArray[byteIndex] & 0xFF;
|
||||
byteIndex++;
|
||||
if ((continuationByte & 0xC0) == 0x80) {
|
||||
return continuationByte & 0x3F;
|
||||
}
|
||||
// If we end up here, it’s not a continuation byte
|
||||
throw Error('Invalid continuation byte');
|
||||
}
|
||||
function decodeSymbol(strict) {
|
||||
var byte1;
|
||||
var byte2;
|
||||
var byte3;
|
||||
var byte4;
|
||||
var codePoint;
|
||||
if (byteIndex > byteCount) {
|
||||
throw Error('Invalid byte index');
|
||||
}
|
||||
if (byteIndex == byteCount) {
|
||||
return false;
|
||||
}
|
||||
// Read first byte
|
||||
byte1 = byteArray[byteIndex] & 0xFF;
|
||||
byteIndex++;
|
||||
// 1-byte sequence (no continuation bytes)
|
||||
if ((byte1 & 0x80) == 0) {
|
||||
return byte1;
|
||||
}
|
||||
// 2-byte sequence
|
||||
if ((byte1 & 0xE0) == 0xC0) {
|
||||
byte2 = readContinuationByte();
|
||||
codePoint = ((byte1 & 0x1F) << 6) | byte2;
|
||||
if (codePoint >= 0x80) {
|
||||
return codePoint;
|
||||
}
|
||||
else {
|
||||
throw Error('Invalid continuation byte');
|
||||
}
|
||||
}
|
||||
// 3-byte sequence (may include unpaired surrogates)
|
||||
if ((byte1 & 0xF0) == 0xE0) {
|
||||
byte2 = readContinuationByte();
|
||||
byte3 = readContinuationByte();
|
||||
codePoint = ((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3;
|
||||
if (codePoint >= 0x0800) {
|
||||
return checkScalarValue(codePoint, strict) ? codePoint : 0xFFFD;
|
||||
}
|
||||
else {
|
||||
throw Error('Invalid continuation byte');
|
||||
}
|
||||
}
|
||||
// 4-byte sequence
|
||||
if ((byte1 & 0xF8) == 0xF0) {
|
||||
byte2 = readContinuationByte();
|
||||
byte3 = readContinuationByte();
|
||||
byte4 = readContinuationByte();
|
||||
codePoint = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0C) |
|
||||
(byte3 << 0x06) | byte4;
|
||||
if (codePoint >= 0x010000 && codePoint <= 0x10FFFF) {
|
||||
return codePoint;
|
||||
}
|
||||
}
|
||||
throw Error('Invalid UTF-8 detected');
|
||||
}
|
||||
var byteArray;
|
||||
var byteCount;
|
||||
var byteIndex;
|
||||
function utf8decode(byteString, opts) {
|
||||
opts = opts || {};
|
||||
var strict = false !== opts.strict;
|
||||
byteArray = ucs2decode(byteString);
|
||||
byteCount = byteArray.length;
|
||||
byteIndex = 0;
|
||||
var codePoints = [];
|
||||
var tmp;
|
||||
while ((tmp = decodeSymbol(strict)) !== false) {
|
||||
codePoints.push(tmp);
|
||||
}
|
||||
return ucs2encode(codePoints);
|
||||
}
|
||||
module.exports = {
|
||||
version: '2.1.2',
|
||||
encode: utf8encode,
|
||||
decode: utf8decode
|
||||
};
|
||||
267
node_modules/engine.io/build/server.d.ts
generated
vendored
Normal file
267
node_modules/engine.io/build/server.d.ts
generated
vendored
Normal file
@@ -0,0 +1,267 @@
|
||||
import { EventEmitter } from "events";
|
||||
import { Socket } from "./socket";
|
||||
import type { IncomingMessage, Server as HttpServer, ServerResponse } from "http";
|
||||
import type { CorsOptions, CorsOptionsDelegate } from "cors";
|
||||
import type { Duplex } from "stream";
|
||||
import type { EngineRequest } from "./transport";
|
||||
import type { CookieSerializeOptions } from "./contrib/types.cookie";
|
||||
type Transport = "polling" | "websocket" | "webtransport";
|
||||
export interface AttachOptions {
|
||||
/**
|
||||
* name of the path to capture
|
||||
* @default "/engine.io"
|
||||
*/
|
||||
path?: string;
|
||||
/**
|
||||
* destroy unhandled upgrade requests
|
||||
* @default true
|
||||
*/
|
||||
destroyUpgrade?: boolean;
|
||||
/**
|
||||
* milliseconds after which unhandled requests are ended
|
||||
* @default 1000
|
||||
*/
|
||||
destroyUpgradeTimeout?: number;
|
||||
/**
|
||||
* Whether we should add a trailing slash to the request path.
|
||||
* @default true
|
||||
*/
|
||||
addTrailingSlash?: boolean;
|
||||
}
|
||||
export interface ServerOptions {
|
||||
/**
|
||||
* how many ms without a pong packet to consider the connection closed
|
||||
* @default 20000
|
||||
*/
|
||||
pingTimeout?: number;
|
||||
/**
|
||||
* how many ms before sending a new ping packet
|
||||
* @default 25000
|
||||
*/
|
||||
pingInterval?: number;
|
||||
/**
|
||||
* how many ms before an uncompleted transport upgrade is cancelled
|
||||
* @default 10000
|
||||
*/
|
||||
upgradeTimeout?: number;
|
||||
/**
|
||||
* how many bytes or characters a message can be, before closing the session (to avoid DoS).
|
||||
* @default 1e5 (100 KB)
|
||||
*/
|
||||
maxHttpBufferSize?: number;
|
||||
/**
|
||||
* A function that receives a given handshake or upgrade request as its first parameter,
|
||||
* and can decide whether to continue or not. The second argument is a function that needs
|
||||
* to be called with the decided information: fn(err, success), where success is a boolean
|
||||
* value where false means that the request is rejected, and err is an error code.
|
||||
*/
|
||||
allowRequest?: (req: IncomingMessage, fn: (err: string | null | undefined, success: boolean) => void) => void;
|
||||
/**
|
||||
* The low-level transports that are enabled. WebTransport is disabled by default and must be manually enabled:
|
||||
*
|
||||
* @example
|
||||
* new Server({
|
||||
* transports: ["polling", "websocket", "webtransport"]
|
||||
* });
|
||||
*
|
||||
* @default ["polling", "websocket"]
|
||||
*/
|
||||
transports?: Transport[];
|
||||
/**
|
||||
* whether to allow transport upgrades
|
||||
* @default true
|
||||
*/
|
||||
allowUpgrades?: boolean;
|
||||
/**
|
||||
* parameters of the WebSocket permessage-deflate extension (see ws module api docs). Set to false to disable.
|
||||
* @default false
|
||||
*/
|
||||
perMessageDeflate?: boolean | object;
|
||||
/**
|
||||
* parameters of the http compression for the polling transports (see zlib api docs). Set to false to disable.
|
||||
* @default true
|
||||
*/
|
||||
httpCompression?: boolean | object;
|
||||
/**
|
||||
* what WebSocket server implementation to use. Specified module must
|
||||
* conform to the ws interface (see ws module api docs).
|
||||
* An alternative c++ addon is also available by installing eiows module.
|
||||
*
|
||||
* @default `require("ws").Server`
|
||||
*/
|
||||
wsEngine?: any;
|
||||
/**
|
||||
* an optional packet which will be concatenated to the handshake packet emitted by Engine.IO.
|
||||
*/
|
||||
initialPacket?: any;
|
||||
/**
|
||||
* configuration of the cookie that contains the client sid to send as part of handshake response headers. This cookie
|
||||
* might be used for sticky-session. Defaults to not sending any cookie.
|
||||
* @default false
|
||||
*/
|
||||
cookie?: (CookieSerializeOptions & {
|
||||
name: string;
|
||||
}) | boolean;
|
||||
/**
|
||||
* the options that will be forwarded to the cors module
|
||||
*/
|
||||
cors?: CorsOptions | CorsOptionsDelegate;
|
||||
/**
|
||||
* whether to enable compatibility with Socket.IO v2 clients
|
||||
* @default false
|
||||
*/
|
||||
allowEIO3?: boolean;
|
||||
}
|
||||
/**
|
||||
* An Express-compatible middleware.
|
||||
*
|
||||
* Middleware functions are functions that have access to the request object (req), the response object (res), and the
|
||||
* next middleware function in the application’s request-response cycle.
|
||||
*
|
||||
* @see https://expressjs.com/en/guide/using-middleware.html
|
||||
*/
|
||||
type Middleware = (req: IncomingMessage, res: ServerResponse, next: (err?: any) => void) => void;
|
||||
export declare abstract class BaseServer extends EventEmitter {
|
||||
opts: ServerOptions;
|
||||
protected clients: Record<string, Socket>;
|
||||
clientsCount: number;
|
||||
protected middlewares: Middleware[];
|
||||
/**
|
||||
* Server constructor.
|
||||
*
|
||||
* @param {Object} opts - options
|
||||
*/
|
||||
constructor(opts?: ServerOptions);
|
||||
protected abstract init(): any;
|
||||
/**
|
||||
* Compute the pathname of the requests that are handled by the server
|
||||
* @param options
|
||||
* @protected
|
||||
*/
|
||||
protected _computePath(options: AttachOptions): string;
|
||||
/**
|
||||
* Returns a list of available transports for upgrade given a certain transport.
|
||||
*
|
||||
* @return {Array}
|
||||
*/
|
||||
upgrades(transport: string): any;
|
||||
/**
|
||||
* Verifies a request.
|
||||
*
|
||||
* @param {EngineRequest} req
|
||||
* @param upgrade - whether it's an upgrade request
|
||||
* @param fn
|
||||
* @protected
|
||||
*/
|
||||
protected verify(req: any, upgrade: boolean, fn: (errorCode?: number, errorContext?: any) => void): void;
|
||||
/**
|
||||
* Adds a new middleware.
|
||||
*
|
||||
* @example
|
||||
* import helmet from "helmet";
|
||||
*
|
||||
* engine.use(helmet());
|
||||
*
|
||||
* @param fn
|
||||
*/
|
||||
use(fn: any): void;
|
||||
/**
|
||||
* Apply the middlewares to the request.
|
||||
*
|
||||
* @param req
|
||||
* @param res
|
||||
* @param callback
|
||||
* @protected
|
||||
*/
|
||||
protected _applyMiddlewares(req: IncomingMessage, res: ServerResponse, callback: (err?: any) => void): void;
|
||||
/**
|
||||
* Closes all clients.
|
||||
*/
|
||||
close(): this;
|
||||
protected abstract cleanup(): any;
|
||||
/**
|
||||
* generate a socket id.
|
||||
* Overwrite this method to generate your custom socket id
|
||||
*
|
||||
* @param {IncomingMessage} req - the request object
|
||||
*/
|
||||
generateId(req: IncomingMessage): any;
|
||||
/**
|
||||
* Handshakes a new client.
|
||||
*
|
||||
* @param {String} transportName
|
||||
* @param {Object} req - the request object
|
||||
* @param {Function} closeConnection
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
protected handshake(transportName: string, req: any, closeConnection: (errorCode?: number, errorContext?: any) => void): Promise<any>;
|
||||
onWebTransportSession(session: any): Promise<any>;
|
||||
protected abstract createTransport(transportName: any, req: any): any;
|
||||
/**
|
||||
* Protocol errors mappings.
|
||||
*/
|
||||
static errors: {
|
||||
UNKNOWN_TRANSPORT: number;
|
||||
UNKNOWN_SID: number;
|
||||
BAD_HANDSHAKE_METHOD: number;
|
||||
BAD_REQUEST: number;
|
||||
FORBIDDEN: number;
|
||||
UNSUPPORTED_PROTOCOL_VERSION: number;
|
||||
};
|
||||
static errorMessages: {
|
||||
0: string;
|
||||
1: string;
|
||||
2: string;
|
||||
3: string;
|
||||
4: string;
|
||||
5: string;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* An Engine.IO server based on Node.js built-in HTTP server and the `ws` package for WebSocket connections.
|
||||
*/
|
||||
export declare class Server extends BaseServer {
|
||||
httpServer?: HttpServer;
|
||||
private ws;
|
||||
/**
|
||||
* Initialize websocket server
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
protected init(): void;
|
||||
protected cleanup(): void;
|
||||
/**
|
||||
* Prepares a request by processing the query string.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private prepare;
|
||||
protected createTransport(transportName: string, req: IncomingMessage): any;
|
||||
/**
|
||||
* Handles an Engine.IO HTTP request.
|
||||
*
|
||||
* @param {EngineRequest} req
|
||||
* @param {ServerResponse} res
|
||||
*/
|
||||
handleRequest(req: EngineRequest, res: ServerResponse): void;
|
||||
/**
|
||||
* Handles an Engine.IO HTTP Upgrade.
|
||||
*/
|
||||
handleUpgrade(req: EngineRequest, socket: Duplex, upgradeHead: Buffer): void;
|
||||
/**
|
||||
* Called upon a ws.io connection.
|
||||
*
|
||||
* @param {ws.Socket} websocket
|
||||
* @private
|
||||
*/
|
||||
private onWebSocket;
|
||||
/**
|
||||
* Captures upgrade requests for a http.Server.
|
||||
*
|
||||
* @param {http.Server} server
|
||||
* @param {Object} options
|
||||
*/
|
||||
attach(server: HttpServer, options?: AttachOptions): void;
|
||||
}
|
||||
export {};
|
||||
786
node_modules/engine.io/build/server.js
generated
vendored
Normal file
786
node_modules/engine.io/build/server.js
generated
vendored
Normal file
@@ -0,0 +1,786 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Server = exports.BaseServer = void 0;
|
||||
const qs = require("querystring");
|
||||
const url_1 = require("url");
|
||||
const base64id = require("base64id");
|
||||
const transports_1 = require("./transports");
|
||||
const events_1 = require("events");
|
||||
const socket_1 = require("./socket");
|
||||
const debug_1 = require("debug");
|
||||
const cookie_1 = require("cookie");
|
||||
const ws_1 = require("ws");
|
||||
const webtransport_1 = require("./transports/webtransport");
|
||||
const engine_io_parser_1 = require("engine.io-parser");
|
||||
const debug = (0, debug_1.default)("engine");
|
||||
const kResponseHeaders = Symbol("responseHeaders");
|
||||
function parseSessionId(data) {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
if (typeof parsed.sid === "string") {
|
||||
return parsed.sid;
|
||||
}
|
||||
}
|
||||
catch (e) { }
|
||||
}
|
||||
class BaseServer extends events_1.EventEmitter {
|
||||
/**
|
||||
* Server constructor.
|
||||
*
|
||||
* @param {Object} opts - options
|
||||
*/
|
||||
constructor(opts = {}) {
|
||||
super();
|
||||
this.middlewares = [];
|
||||
this.clients = {};
|
||||
this.clientsCount = 0;
|
||||
this.opts = Object.assign({
|
||||
wsEngine: ws_1.Server,
|
||||
pingTimeout: 20000,
|
||||
pingInterval: 25000,
|
||||
upgradeTimeout: 10000,
|
||||
maxHttpBufferSize: 1e6,
|
||||
transports: ["polling", "websocket"], // WebTransport is disabled by default
|
||||
allowUpgrades: true,
|
||||
httpCompression: {
|
||||
threshold: 1024,
|
||||
},
|
||||
cors: false,
|
||||
allowEIO3: false,
|
||||
}, opts);
|
||||
if (opts.cookie) {
|
||||
this.opts.cookie = Object.assign({
|
||||
name: "io",
|
||||
path: "/",
|
||||
// @ts-ignore
|
||||
httpOnly: opts.cookie.path !== false,
|
||||
sameSite: "lax",
|
||||
}, opts.cookie);
|
||||
}
|
||||
if (this.opts.cors) {
|
||||
this.use(require("cors")(this.opts.cors));
|
||||
}
|
||||
if (opts.perMessageDeflate) {
|
||||
this.opts.perMessageDeflate = Object.assign({
|
||||
threshold: 1024,
|
||||
}, opts.perMessageDeflate);
|
||||
}
|
||||
this.init();
|
||||
}
|
||||
/**
|
||||
* Compute the pathname of the requests that are handled by the server
|
||||
* @param options
|
||||
* @protected
|
||||
*/
|
||||
_computePath(options) {
|
||||
let path = (options.path || "/engine.io").replace(/\/$/, "");
|
||||
if (options.addTrailingSlash !== false) {
|
||||
// normalize path
|
||||
path += "/";
|
||||
}
|
||||
return path;
|
||||
}
|
||||
/**
|
||||
* Returns a list of available transports for upgrade given a certain transport.
|
||||
*
|
||||
* @return {Array}
|
||||
*/
|
||||
upgrades(transport) {
|
||||
if (!this.opts.allowUpgrades)
|
||||
return [];
|
||||
return transports_1.default[transport].upgradesTo || [];
|
||||
}
|
||||
/**
|
||||
* Verifies a request.
|
||||
*
|
||||
* @param {EngineRequest} req
|
||||
* @param upgrade - whether it's an upgrade request
|
||||
* @param fn
|
||||
* @protected
|
||||
*/
|
||||
verify(req, upgrade, fn) {
|
||||
// transport check
|
||||
const transport = req._query.transport;
|
||||
// WebTransport does not go through the verify() method, see the onWebTransportSession() method
|
||||
if (!~this.opts.transports.indexOf(transport) ||
|
||||
transport === "webtransport") {
|
||||
debug('unknown transport "%s"', transport);
|
||||
return fn(Server.errors.UNKNOWN_TRANSPORT, { transport });
|
||||
}
|
||||
// 'Origin' header check
|
||||
const isOriginInvalid = checkInvalidHeaderChar(req.headers.origin);
|
||||
if (isOriginInvalid) {
|
||||
const origin = req.headers.origin;
|
||||
req.headers.origin = null;
|
||||
debug("origin header invalid");
|
||||
return fn(Server.errors.BAD_REQUEST, {
|
||||
name: "INVALID_ORIGIN",
|
||||
origin,
|
||||
});
|
||||
}
|
||||
// sid check
|
||||
const sid = req._query.sid;
|
||||
if (sid) {
|
||||
if (!this.clients.hasOwnProperty(sid)) {
|
||||
debug('unknown sid "%s"', sid);
|
||||
return fn(Server.errors.UNKNOWN_SID, {
|
||||
sid,
|
||||
});
|
||||
}
|
||||
const previousTransport = this.clients[sid].transport.name;
|
||||
if (!upgrade && previousTransport !== transport) {
|
||||
debug("bad request: unexpected transport without upgrade");
|
||||
return fn(Server.errors.BAD_REQUEST, {
|
||||
name: "TRANSPORT_MISMATCH",
|
||||
transport,
|
||||
previousTransport,
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
// handshake is GET only
|
||||
if ("GET" !== req.method) {
|
||||
return fn(Server.errors.BAD_HANDSHAKE_METHOD, {
|
||||
method: req.method,
|
||||
});
|
||||
}
|
||||
if (transport === "websocket" && !upgrade) {
|
||||
debug("invalid transport upgrade");
|
||||
return fn(Server.errors.BAD_REQUEST, {
|
||||
name: "TRANSPORT_HANDSHAKE_ERROR",
|
||||
});
|
||||
}
|
||||
if (!this.opts.allowRequest)
|
||||
return fn();
|
||||
return this.opts.allowRequest(req, (message, success) => {
|
||||
if (!success) {
|
||||
return fn(Server.errors.FORBIDDEN, {
|
||||
message,
|
||||
});
|
||||
}
|
||||
fn();
|
||||
});
|
||||
}
|
||||
fn();
|
||||
}
|
||||
/**
|
||||
* Adds a new middleware.
|
||||
*
|
||||
* @example
|
||||
* import helmet from "helmet";
|
||||
*
|
||||
* engine.use(helmet());
|
||||
*
|
||||
* @param fn
|
||||
*/
|
||||
use(fn) {
|
||||
this.middlewares.push(fn);
|
||||
}
|
||||
/**
|
||||
* Apply the middlewares to the request.
|
||||
*
|
||||
* @param req
|
||||
* @param res
|
||||
* @param callback
|
||||
* @protected
|
||||
*/
|
||||
_applyMiddlewares(req, res, callback) {
|
||||
if (this.middlewares.length === 0) {
|
||||
debug("no middleware to apply, skipping");
|
||||
return callback();
|
||||
}
|
||||
const apply = (i) => {
|
||||
debug("applying middleware n°%d", i + 1);
|
||||
this.middlewares[i](req, res, (err) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
if (i + 1 < this.middlewares.length) {
|
||||
apply(i + 1);
|
||||
}
|
||||
else {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
};
|
||||
apply(0);
|
||||
}
|
||||
/**
|
||||
* Closes all clients.
|
||||
*/
|
||||
close() {
|
||||
debug("closing all open clients");
|
||||
for (let i in this.clients) {
|
||||
if (this.clients.hasOwnProperty(i)) {
|
||||
this.clients[i].close(true);
|
||||
}
|
||||
}
|
||||
this.cleanup();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* generate a socket id.
|
||||
* Overwrite this method to generate your custom socket id
|
||||
*
|
||||
* @param {IncomingMessage} req - the request object
|
||||
*/
|
||||
generateId(req) {
|
||||
return base64id.generateId();
|
||||
}
|
||||
/**
|
||||
* Handshakes a new client.
|
||||
*
|
||||
* @param {String} transportName
|
||||
* @param {Object} req - the request object
|
||||
* @param {Function} closeConnection
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
async handshake(transportName, req, closeConnection) {
|
||||
const protocol = req._query.EIO === "4" ? 4 : 3; // 3rd revision by default
|
||||
if (protocol === 3 && !this.opts.allowEIO3) {
|
||||
debug("unsupported protocol version");
|
||||
this.emit("connection_error", {
|
||||
req,
|
||||
code: Server.errors.UNSUPPORTED_PROTOCOL_VERSION,
|
||||
message: Server.errorMessages[Server.errors.UNSUPPORTED_PROTOCOL_VERSION],
|
||||
context: {
|
||||
protocol,
|
||||
},
|
||||
});
|
||||
closeConnection(Server.errors.UNSUPPORTED_PROTOCOL_VERSION);
|
||||
return;
|
||||
}
|
||||
let id;
|
||||
try {
|
||||
id = await this.generateId(req);
|
||||
}
|
||||
catch (e) {
|
||||
debug("error while generating an id");
|
||||
this.emit("connection_error", {
|
||||
req,
|
||||
code: Server.errors.BAD_REQUEST,
|
||||
message: Server.errorMessages[Server.errors.BAD_REQUEST],
|
||||
context: {
|
||||
name: "ID_GENERATION_ERROR",
|
||||
error: e,
|
||||
},
|
||||
});
|
||||
closeConnection(Server.errors.BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
debug('handshaking client "%s"', id);
|
||||
try {
|
||||
var transport = this.createTransport(transportName, req);
|
||||
if ("polling" === transportName) {
|
||||
transport.maxHttpBufferSize = this.opts.maxHttpBufferSize;
|
||||
transport.httpCompression = this.opts.httpCompression;
|
||||
}
|
||||
else if ("websocket" === transportName) {
|
||||
transport.perMessageDeflate = this.opts.perMessageDeflate;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
debug('error handshaking to transport "%s"', transportName);
|
||||
this.emit("connection_error", {
|
||||
req,
|
||||
code: Server.errors.BAD_REQUEST,
|
||||
message: Server.errorMessages[Server.errors.BAD_REQUEST],
|
||||
context: {
|
||||
name: "TRANSPORT_HANDSHAKE_ERROR",
|
||||
error: e,
|
||||
},
|
||||
});
|
||||
closeConnection(Server.errors.BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
const socket = new socket_1.Socket(id, this, transport, req, protocol);
|
||||
transport.on("headers", (headers, req) => {
|
||||
const isInitialRequest = !req._query.sid;
|
||||
if (isInitialRequest) {
|
||||
if (this.opts.cookie) {
|
||||
headers["Set-Cookie"] = [
|
||||
// @ts-ignore
|
||||
(0, cookie_1.serialize)(this.opts.cookie.name, id, this.opts.cookie),
|
||||
];
|
||||
}
|
||||
this.emit("initial_headers", headers, req);
|
||||
}
|
||||
this.emit("headers", headers, req);
|
||||
});
|
||||
transport.onRequest(req);
|
||||
this.clients[id] = socket;
|
||||
this.clientsCount++;
|
||||
socket.once("close", () => {
|
||||
delete this.clients[id];
|
||||
this.clientsCount--;
|
||||
});
|
||||
this.emit("connection", socket);
|
||||
return transport;
|
||||
}
|
||||
async onWebTransportSession(session) {
|
||||
const timeout = setTimeout(() => {
|
||||
debug("the client failed to establish a bidirectional stream in the given period");
|
||||
session.close();
|
||||
}, this.opts.upgradeTimeout);
|
||||
const streamReader = session.incomingBidirectionalStreams.getReader();
|
||||
const result = await streamReader.read();
|
||||
if (result.done) {
|
||||
debug("session is closed");
|
||||
return;
|
||||
}
|
||||
const stream = result.value;
|
||||
const transformStream = (0, engine_io_parser_1.createPacketDecoderStream)(this.opts.maxHttpBufferSize, "nodebuffer");
|
||||
const reader = stream.readable.pipeThrough(transformStream).getReader();
|
||||
// reading the first packet of the stream
|
||||
const { value, done } = await reader.read();
|
||||
if (done) {
|
||||
debug("stream is closed");
|
||||
return;
|
||||
}
|
||||
clearTimeout(timeout);
|
||||
if (value.type !== "open") {
|
||||
debug("invalid WebTransport handshake");
|
||||
return session.close();
|
||||
}
|
||||
if (value.data === undefined) {
|
||||
const transport = new webtransport_1.WebTransport(session, stream, reader);
|
||||
// note: we cannot use "this.generateId()", because there is no "req" argument
|
||||
const id = base64id.generateId();
|
||||
debug('handshaking client "%s" (WebTransport)', id);
|
||||
const socket = new socket_1.Socket(id, this, transport, null, 4);
|
||||
this.clients[id] = socket;
|
||||
this.clientsCount++;
|
||||
socket.once("close", () => {
|
||||
delete this.clients[id];
|
||||
this.clientsCount--;
|
||||
});
|
||||
this.emit("connection", socket);
|
||||
return;
|
||||
}
|
||||
const sid = parseSessionId(value.data);
|
||||
if (!sid) {
|
||||
debug("invalid WebTransport handshake");
|
||||
return session.close();
|
||||
}
|
||||
const client = this.clients[sid];
|
||||
if (!client) {
|
||||
debug("upgrade attempt for closed client");
|
||||
session.close();
|
||||
}
|
||||
else if (client.upgrading) {
|
||||
debug("transport has already been trying to upgrade");
|
||||
session.close();
|
||||
}
|
||||
else if (client.upgraded) {
|
||||
debug("transport had already been upgraded");
|
||||
session.close();
|
||||
}
|
||||
else {
|
||||
debug("upgrading existing transport");
|
||||
const transport = new webtransport_1.WebTransport(session, stream, reader);
|
||||
client._maybeUpgrade(transport);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.BaseServer = BaseServer;
|
||||
/**
|
||||
* Protocol errors mappings.
|
||||
*/
|
||||
BaseServer.errors = {
|
||||
UNKNOWN_TRANSPORT: 0,
|
||||
UNKNOWN_SID: 1,
|
||||
BAD_HANDSHAKE_METHOD: 2,
|
||||
BAD_REQUEST: 3,
|
||||
FORBIDDEN: 4,
|
||||
UNSUPPORTED_PROTOCOL_VERSION: 5,
|
||||
};
|
||||
BaseServer.errorMessages = {
|
||||
0: "Transport unknown",
|
||||
1: "Session ID unknown",
|
||||
2: "Bad handshake method",
|
||||
3: "Bad request",
|
||||
4: "Forbidden",
|
||||
5: "Unsupported protocol version",
|
||||
};
|
||||
/**
|
||||
* Exposes a subset of the http.ServerResponse interface, in order to be able to apply the middlewares to an upgrade
|
||||
* request.
|
||||
*
|
||||
* @see https://nodejs.org/api/http.html#class-httpserverresponse
|
||||
*/
|
||||
class WebSocketResponse {
|
||||
constructor(req, socket) {
|
||||
this.req = req;
|
||||
this.socket = socket;
|
||||
// temporarily store the response headers on the req object (see the "headers" event)
|
||||
req[kResponseHeaders] = {};
|
||||
}
|
||||
setHeader(name, value) {
|
||||
this.req[kResponseHeaders][name] = value;
|
||||
}
|
||||
getHeader(name) {
|
||||
return this.req[kResponseHeaders][name];
|
||||
}
|
||||
removeHeader(name) {
|
||||
delete this.req[kResponseHeaders][name];
|
||||
}
|
||||
write() { }
|
||||
writeHead() { }
|
||||
end() {
|
||||
// we could return a proper error code, but the WebSocket client will emit an "error" event anyway.
|
||||
this.socket.destroy();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* An Engine.IO server based on Node.js built-in HTTP server and the `ws` package for WebSocket connections.
|
||||
*/
|
||||
class Server extends BaseServer {
|
||||
/**
|
||||
* Initialize websocket server
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
init() {
|
||||
if (!~this.opts.transports.indexOf("websocket"))
|
||||
return;
|
||||
if (this.ws)
|
||||
this.ws.close();
|
||||
this.ws = new this.opts.wsEngine({
|
||||
noServer: true,
|
||||
clientTracking: false,
|
||||
perMessageDeflate: this.opts.perMessageDeflate,
|
||||
maxPayload: this.opts.maxHttpBufferSize,
|
||||
});
|
||||
if (typeof this.ws.on === "function") {
|
||||
this.ws.on("headers", (headersArray, req) => {
|
||||
// note: 'ws' uses an array of headers, while Engine.IO uses an object (response.writeHead() accepts both formats)
|
||||
// we could also try to parse the array and then sync the values, but that will be error-prone
|
||||
const additionalHeaders = req[kResponseHeaders] || {};
|
||||
delete req[kResponseHeaders];
|
||||
const isInitialRequest = !req._query.sid;
|
||||
if (isInitialRequest) {
|
||||
this.emit("initial_headers", additionalHeaders, req);
|
||||
}
|
||||
this.emit("headers", additionalHeaders, req);
|
||||
debug("writing headers: %j", additionalHeaders);
|
||||
Object.keys(additionalHeaders).forEach((key) => {
|
||||
headersArray.push(`${key}: ${additionalHeaders[key]}`);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
cleanup() {
|
||||
if (this.ws) {
|
||||
debug("closing webSocketServer");
|
||||
this.ws.close();
|
||||
// don't delete this.ws because it can be used again if the http server starts listening again
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Prepares a request by processing the query string.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
prepare(req) {
|
||||
// try to leverage pre-existing `req._query` (e.g: from connect)
|
||||
if (!req._query) {
|
||||
req._query = (~req.url.indexOf("?") ? qs.parse((0, url_1.parse)(req.url).query) : {});
|
||||
}
|
||||
}
|
||||
createTransport(transportName, req) {
|
||||
return new transports_1.default[transportName](req);
|
||||
}
|
||||
/**
|
||||
* Handles an Engine.IO HTTP request.
|
||||
*
|
||||
* @param {EngineRequest} req
|
||||
* @param {ServerResponse} res
|
||||
*/
|
||||
handleRequest(req, res) {
|
||||
debug('handling "%s" http request "%s"', req.method, req.url);
|
||||
this.prepare(req);
|
||||
req.res = res;
|
||||
const callback = (errorCode, errorContext) => {
|
||||
if (errorCode !== undefined) {
|
||||
this.emit("connection_error", {
|
||||
req,
|
||||
code: errorCode,
|
||||
message: Server.errorMessages[errorCode],
|
||||
context: errorContext,
|
||||
});
|
||||
abortRequest(res, errorCode, errorContext);
|
||||
return;
|
||||
}
|
||||
if (req._query.sid) {
|
||||
debug("setting new request for existing client");
|
||||
this.clients[req._query.sid].transport.onRequest(req);
|
||||
}
|
||||
else {
|
||||
const closeConnection = (errorCode, errorContext) => abortRequest(res, errorCode, errorContext);
|
||||
this.handshake(req._query.transport, req, closeConnection);
|
||||
}
|
||||
};
|
||||
this._applyMiddlewares(req, res, (err) => {
|
||||
if (err) {
|
||||
callback(Server.errors.BAD_REQUEST, { name: "MIDDLEWARE_FAILURE" });
|
||||
}
|
||||
else {
|
||||
this.verify(req, false, callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Handles an Engine.IO HTTP Upgrade.
|
||||
*/
|
||||
handleUpgrade(req, socket, upgradeHead) {
|
||||
this.prepare(req);
|
||||
const res = new WebSocketResponse(req, socket);
|
||||
const callback = (errorCode, errorContext) => {
|
||||
if (errorCode !== undefined) {
|
||||
this.emit("connection_error", {
|
||||
req,
|
||||
code: errorCode,
|
||||
message: Server.errorMessages[errorCode],
|
||||
context: errorContext,
|
||||
});
|
||||
abortUpgrade(socket, errorCode, errorContext);
|
||||
return;
|
||||
}
|
||||
const head = Buffer.from(upgradeHead);
|
||||
upgradeHead = null;
|
||||
// some middlewares (like express-session) wait for the writeHead() call to flush their headers
|
||||
// see https://github.com/expressjs/session/blob/1010fadc2f071ddf2add94235d72224cf65159c6/index.js#L220-L244
|
||||
res.writeHead();
|
||||
// delegate to ws
|
||||
this.ws.handleUpgrade(req, socket, head, (websocket) => {
|
||||
this.onWebSocket(req, socket, websocket);
|
||||
});
|
||||
};
|
||||
this._applyMiddlewares(req, res, (err) => {
|
||||
if (err) {
|
||||
callback(Server.errors.BAD_REQUEST, { name: "MIDDLEWARE_FAILURE" });
|
||||
}
|
||||
else {
|
||||
this.verify(req, true, callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Called upon a ws.io connection.
|
||||
*
|
||||
* @param {ws.Socket} websocket
|
||||
* @private
|
||||
*/
|
||||
onWebSocket(req, socket, websocket) {
|
||||
websocket.on("error", onUpgradeError);
|
||||
if (transports_1.default[req._query.transport] !== undefined &&
|
||||
!transports_1.default[req._query.transport].prototype.handlesUpgrades) {
|
||||
debug("transport doesnt handle upgraded requests");
|
||||
websocket.close();
|
||||
return;
|
||||
}
|
||||
// get client id
|
||||
const id = req._query.sid;
|
||||
// keep a reference to the ws.Socket
|
||||
req.websocket = websocket;
|
||||
if (id) {
|
||||
const client = this.clients[id];
|
||||
if (!client) {
|
||||
debug("upgrade attempt for closed client");
|
||||
websocket.close();
|
||||
}
|
||||
else if (client.upgrading) {
|
||||
debug("transport has already been trying to upgrade");
|
||||
websocket.close();
|
||||
}
|
||||
else if (client.upgraded) {
|
||||
debug("transport had already been upgraded");
|
||||
websocket.close();
|
||||
}
|
||||
else {
|
||||
debug("upgrading existing transport");
|
||||
// transport error handling takes over
|
||||
websocket.removeListener("error", onUpgradeError);
|
||||
const transport = this.createTransport(req._query.transport, req);
|
||||
transport.perMessageDeflate = this.opts.perMessageDeflate;
|
||||
client._maybeUpgrade(transport);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const closeConnection = (errorCode, errorContext) => abortUpgrade(socket, errorCode, errorContext);
|
||||
this.handshake(req._query.transport, req, closeConnection);
|
||||
}
|
||||
function onUpgradeError() {
|
||||
debug("websocket error before upgrade");
|
||||
// websocket.close() not needed
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Captures upgrade requests for a http.Server.
|
||||
*
|
||||
* @param {http.Server} server
|
||||
* @param {Object} options
|
||||
*/
|
||||
attach(server, options = {}) {
|
||||
const path = this._computePath(options);
|
||||
const destroyUpgradeTimeout = options.destroyUpgradeTimeout || 1000;
|
||||
function check(req) {
|
||||
// TODO use `path === new URL(...).pathname` in the next major release (ref: https://nodejs.org/api/url.html)
|
||||
return path === req.url.slice(0, path.length);
|
||||
}
|
||||
// cache and clean up listeners
|
||||
const listeners = server.listeners("request").slice(0);
|
||||
server.removeAllListeners("request");
|
||||
server.on("close", this.close.bind(this));
|
||||
server.on("listening", this.init.bind(this));
|
||||
// add request handler
|
||||
server.on("request", (req, res) => {
|
||||
if (check(req)) {
|
||||
debug('intercepting request for path "%s"', path);
|
||||
this.handleRequest(req, res);
|
||||
}
|
||||
else {
|
||||
let i = 0;
|
||||
const l = listeners.length;
|
||||
for (; i < l; i++) {
|
||||
listeners[i].call(server, req, res);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (~this.opts.transports.indexOf("websocket")) {
|
||||
server.on("upgrade", (req, socket, head) => {
|
||||
if (check(req)) {
|
||||
this.handleUpgrade(req, socket, head);
|
||||
}
|
||||
else if (false !== options.destroyUpgrade) {
|
||||
// default node behavior is to disconnect when no handlers
|
||||
// but by adding a handler, we prevent that
|
||||
// and if no eio thing handles the upgrade
|
||||
// then the socket needs to die!
|
||||
setTimeout(function () {
|
||||
// @ts-ignore
|
||||
if (socket.writable && socket.bytesWritten <= 0) {
|
||||
socket.on("error", (e) => {
|
||||
debug("error while destroying upgrade: %s", e.message);
|
||||
});
|
||||
return socket.end();
|
||||
}
|
||||
}, destroyUpgradeTimeout);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.Server = Server;
|
||||
/**
|
||||
* Close the HTTP long-polling request
|
||||
*
|
||||
* @param res - the response object
|
||||
* @param errorCode - the error code
|
||||
* @param errorContext - additional error context
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function abortRequest(res, errorCode, errorContext) {
|
||||
const statusCode = errorCode === Server.errors.FORBIDDEN ? 403 : 400;
|
||||
const message = errorContext && errorContext.message
|
||||
? errorContext.message
|
||||
: Server.errorMessages[errorCode];
|
||||
res.writeHead(statusCode, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({
|
||||
code: errorCode,
|
||||
message,
|
||||
}));
|
||||
}
|
||||
/**
|
||||
* Close the WebSocket connection
|
||||
*
|
||||
* @param {net.Socket} socket
|
||||
* @param {string} errorCode - the error code
|
||||
* @param {object} errorContext - additional error context
|
||||
*/
|
||||
function abortUpgrade(socket, errorCode, errorContext = {}) {
|
||||
socket.on("error", () => {
|
||||
debug("ignoring error from closed connection");
|
||||
});
|
||||
if (socket.writable) {
|
||||
const message = errorContext.message || Server.errorMessages[errorCode];
|
||||
const length = Buffer.byteLength(message);
|
||||
socket.write("HTTP/1.1 400 Bad Request\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"Content-type: text/html\r\n" +
|
||||
"Content-Length: " +
|
||||
length +
|
||||
"\r\n" +
|
||||
"\r\n" +
|
||||
message);
|
||||
}
|
||||
socket.destroy();
|
||||
}
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* From https://github.com/nodejs/node/blob/v8.4.0/lib/_http_common.js#L303-L354
|
||||
*
|
||||
* True if val contains an invalid field-vchar
|
||||
* field-value = *( field-content / obs-fold )
|
||||
* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
||||
* field-vchar = VCHAR / obs-text
|
||||
*
|
||||
* checkInvalidHeaderChar() is currently designed to be inlinable by v8,
|
||||
* so take care when making changes to the implementation so that the source
|
||||
* code size does not exceed v8's default max_inlined_source_size setting.
|
||||
**/
|
||||
// prettier-ignore
|
||||
const validHdrChars = [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32 - 47
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48 - 63
|
||||
1, 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, 1, 1, 1, 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, 1, 1, 1, 1, 0, // 112 - 127
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128 ...
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // ... 255
|
||||
];
|
||||
function checkInvalidHeaderChar(val) {
|
||||
val += "";
|
||||
if (val.length < 1)
|
||||
return false;
|
||||
if (!validHdrChars[val.charCodeAt(0)]) {
|
||||
debug('invalid header, index 0, char "%s"', val.charCodeAt(0));
|
||||
return true;
|
||||
}
|
||||
if (val.length < 2)
|
||||
return false;
|
||||
if (!validHdrChars[val.charCodeAt(1)]) {
|
||||
debug('invalid header, index 1, char "%s"', val.charCodeAt(1));
|
||||
return true;
|
||||
}
|
||||
if (val.length < 3)
|
||||
return false;
|
||||
if (!validHdrChars[val.charCodeAt(2)]) {
|
||||
debug('invalid header, index 2, char "%s"', val.charCodeAt(2));
|
||||
return true;
|
||||
}
|
||||
if (val.length < 4)
|
||||
return false;
|
||||
if (!validHdrChars[val.charCodeAt(3)]) {
|
||||
debug('invalid header, index 3, char "%s"', val.charCodeAt(3));
|
||||
return true;
|
||||
}
|
||||
for (let i = 4; i < val.length; ++i) {
|
||||
if (!validHdrChars[val.charCodeAt(i)]) {
|
||||
debug('invalid header, index "%i", char "%s"', i, val.charCodeAt(i));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
180
node_modules/engine.io/build/socket.d.ts
generated
vendored
Normal file
180
node_modules/engine.io/build/socket.d.ts
generated
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
import { EventEmitter } from "events";
|
||||
import type { IncomingMessage } from "http";
|
||||
import type { EngineRequest, Transport } from "./transport";
|
||||
import type { BaseServer } from "./server";
|
||||
import type { RawData } from "engine.io-parser";
|
||||
export interface SendOptions {
|
||||
compress?: boolean;
|
||||
}
|
||||
type ReadyState = "opening" | "open" | "closing" | "closed";
|
||||
type SendCallback = (transport: Transport) => void;
|
||||
export declare class Socket extends EventEmitter {
|
||||
/**
|
||||
* The revision of the protocol:
|
||||
*
|
||||
* - 3rd is used in Engine.IO v3 / Socket.IO v2
|
||||
* - 4th is used in Engine.IO v4 and above / Socket.IO v3 and above
|
||||
*
|
||||
* It is found in the `EIO` query parameters of the HTTP requests.
|
||||
*
|
||||
* @see https://github.com/socketio/engine.io-protocol
|
||||
*/
|
||||
readonly protocol: number;
|
||||
/**
|
||||
* A reference to the first HTTP request of the session
|
||||
*
|
||||
* TODO for the next major release: remove it
|
||||
*/
|
||||
request: IncomingMessage;
|
||||
/**
|
||||
* The IP address of the client.
|
||||
*/
|
||||
readonly remoteAddress: string;
|
||||
/**
|
||||
* The current state of the socket.
|
||||
*/
|
||||
_readyState: ReadyState;
|
||||
/**
|
||||
* The current low-level transport.
|
||||
*/
|
||||
transport: Transport;
|
||||
private server;
|
||||
upgrading: boolean;
|
||||
upgraded: boolean;
|
||||
private writeBuffer;
|
||||
private packetsFn;
|
||||
private sentCallbackFn;
|
||||
private cleanupFn;
|
||||
private pingTimeoutTimer;
|
||||
private pingIntervalTimer;
|
||||
/**
|
||||
* This is the session identifier that the client will use in the subsequent HTTP requests. It must not be shared with
|
||||
* others parties, as it might lead to session hijacking.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private readonly id;
|
||||
get readyState(): ReadyState;
|
||||
set readyState(state: ReadyState);
|
||||
constructor(id: string, server: BaseServer, transport: Transport, req: EngineRequest, protocol: number);
|
||||
/**
|
||||
* Called upon transport considered open.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private onOpen;
|
||||
/**
|
||||
* Called upon transport packet.
|
||||
*
|
||||
* @param {Object} packet
|
||||
* @private
|
||||
*/
|
||||
private onPacket;
|
||||
/**
|
||||
* Called upon transport error.
|
||||
*
|
||||
* @param {Error} err - error object
|
||||
* @private
|
||||
*/
|
||||
private onError;
|
||||
/**
|
||||
* Pings client every `this.pingInterval` and expects response
|
||||
* within `this.pingTimeout` or closes connection.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private schedulePing;
|
||||
/**
|
||||
* Resets ping timeout.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private resetPingTimeout;
|
||||
/**
|
||||
* Attaches handlers for the given transport.
|
||||
*
|
||||
* @param {Transport} transport
|
||||
* @private
|
||||
*/
|
||||
private setTransport;
|
||||
/**
|
||||
* Upon transport "drain" event
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private onDrain;
|
||||
/**
|
||||
* Upgrades socket to the given transport
|
||||
*
|
||||
* @param {Transport} transport
|
||||
* @private
|
||||
*/
|
||||
_maybeUpgrade(transport: Transport): void;
|
||||
/**
|
||||
* Clears listeners and timers associated with current transport.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private clearTransport;
|
||||
/**
|
||||
* Called upon transport considered closed.
|
||||
* Possible reasons: `ping timeout`, `client error`, `parse error`,
|
||||
* `transport error`, `server close`, `transport close`
|
||||
*/
|
||||
private onClose;
|
||||
/**
|
||||
* Sends a message packet.
|
||||
*
|
||||
* @param {Object} data
|
||||
* @param {Object} options
|
||||
* @param {Function} callback
|
||||
* @return {Socket} for chaining
|
||||
*/
|
||||
send(data: RawData, options?: SendOptions, callback?: SendCallback): this;
|
||||
/**
|
||||
* Alias of {@link send}.
|
||||
*
|
||||
* @param data
|
||||
* @param options
|
||||
* @param callback
|
||||
*/
|
||||
write(data: RawData, options?: SendOptions, callback?: SendCallback): this;
|
||||
/**
|
||||
* Sends a packet.
|
||||
*
|
||||
* @param {String} type - packet type
|
||||
* @param {String} data
|
||||
* @param {Object} options
|
||||
* @param {Function} callback
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private sendPacket;
|
||||
/**
|
||||
* Attempts to flush the packets buffer.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private flush;
|
||||
/**
|
||||
* Get available upgrades for this socket.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private getAvailableUpgrades;
|
||||
/**
|
||||
* Closes the socket and underlying transport.
|
||||
*
|
||||
* @param {Boolean} discard - optional, discard the transport
|
||||
* @return {Socket} for chaining
|
||||
*/
|
||||
close(discard?: boolean): void;
|
||||
/**
|
||||
* Closes the underlying transport.
|
||||
*
|
||||
* @param {Boolean} discard
|
||||
* @private
|
||||
*/
|
||||
private closeTransport;
|
||||
}
|
||||
export {};
|
||||
460
node_modules/engine.io/build/socket.js
generated
vendored
Normal file
460
node_modules/engine.io/build/socket.js
generated
vendored
Normal file
@@ -0,0 +1,460 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Socket = void 0;
|
||||
const events_1 = require("events");
|
||||
const debug_1 = require("debug");
|
||||
const timers_1 = require("timers");
|
||||
const debug = (0, debug_1.default)("engine:socket");
|
||||
class Socket extends events_1.EventEmitter {
|
||||
get readyState() {
|
||||
return this._readyState;
|
||||
}
|
||||
set readyState(state) {
|
||||
debug("readyState updated from %s to %s", this._readyState, state);
|
||||
this._readyState = state;
|
||||
}
|
||||
constructor(id, server, transport, req, protocol) {
|
||||
super();
|
||||
/**
|
||||
* The current state of the socket.
|
||||
*/
|
||||
this._readyState = "opening";
|
||||
/* private */ this.upgrading = false;
|
||||
/* private */ this.upgraded = false;
|
||||
this.writeBuffer = [];
|
||||
this.packetsFn = [];
|
||||
this.sentCallbackFn = [];
|
||||
this.cleanupFn = [];
|
||||
this.id = id;
|
||||
this.server = server;
|
||||
this.request = req;
|
||||
this.protocol = protocol;
|
||||
// Cache IP since it might not be in the req later
|
||||
if (req) {
|
||||
if (req.websocket && req.websocket._socket) {
|
||||
this.remoteAddress = req.websocket._socket.remoteAddress;
|
||||
}
|
||||
else {
|
||||
this.remoteAddress = req.connection.remoteAddress;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TODO there is currently no way to get the IP address of the client when it connects with WebTransport
|
||||
// see https://github.com/fails-components/webtransport/issues/114
|
||||
}
|
||||
this.pingTimeoutTimer = null;
|
||||
this.pingIntervalTimer = null;
|
||||
this.setTransport(transport);
|
||||
this.onOpen();
|
||||
}
|
||||
/**
|
||||
* Called upon transport considered open.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
onOpen() {
|
||||
this.readyState = "open";
|
||||
// sends an `open` packet
|
||||
this.transport.sid = this.id;
|
||||
this.sendPacket("open", JSON.stringify({
|
||||
sid: this.id,
|
||||
upgrades: this.getAvailableUpgrades(),
|
||||
pingInterval: this.server.opts.pingInterval,
|
||||
pingTimeout: this.server.opts.pingTimeout,
|
||||
maxPayload: this.server.opts.maxHttpBufferSize,
|
||||
}));
|
||||
if (this.server.opts.initialPacket) {
|
||||
this.sendPacket("message", this.server.opts.initialPacket);
|
||||
}
|
||||
this.emit("open");
|
||||
if (this.protocol === 3) {
|
||||
// in protocol v3, the client sends a ping, and the server answers with a pong
|
||||
this.resetPingTimeout();
|
||||
}
|
||||
else {
|
||||
// in protocol v4, the server sends a ping, and the client answers with a pong
|
||||
this.schedulePing();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Called upon transport packet.
|
||||
*
|
||||
* @param {Object} packet
|
||||
* @private
|
||||
*/
|
||||
onPacket(packet) {
|
||||
if ("open" !== this.readyState) {
|
||||
return debug("packet received with closed socket");
|
||||
}
|
||||
// export packet event
|
||||
debug(`received packet ${packet.type}`);
|
||||
this.emit("packet", packet);
|
||||
switch (packet.type) {
|
||||
case "ping":
|
||||
if (this.transport.protocol !== 3) {
|
||||
this.onError(new Error("invalid heartbeat direction"));
|
||||
return;
|
||||
}
|
||||
debug("got ping");
|
||||
this.pingTimeoutTimer.refresh();
|
||||
this.sendPacket("pong");
|
||||
this.emit("heartbeat");
|
||||
break;
|
||||
case "pong":
|
||||
if (this.transport.protocol === 3) {
|
||||
this.onError(new Error("invalid heartbeat direction"));
|
||||
return;
|
||||
}
|
||||
debug("got pong");
|
||||
(0, timers_1.clearTimeout)(this.pingTimeoutTimer);
|
||||
this.pingIntervalTimer.refresh();
|
||||
this.emit("heartbeat");
|
||||
break;
|
||||
case "error":
|
||||
this.onClose("parse error");
|
||||
break;
|
||||
case "message":
|
||||
this.emit("data", packet.data);
|
||||
this.emit("message", packet.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Called upon transport error.
|
||||
*
|
||||
* @param {Error} err - error object
|
||||
* @private
|
||||
*/
|
||||
onError(err) {
|
||||
debug("transport error");
|
||||
this.onClose("transport error", err);
|
||||
}
|
||||
/**
|
||||
* Pings client every `this.pingInterval` and expects response
|
||||
* within `this.pingTimeout` or closes connection.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
schedulePing() {
|
||||
this.pingIntervalTimer = (0, timers_1.setTimeout)(() => {
|
||||
debug("writing ping packet - expecting pong within %sms", this.server.opts.pingTimeout);
|
||||
this.sendPacket("ping");
|
||||
this.resetPingTimeout();
|
||||
}, this.server.opts.pingInterval);
|
||||
}
|
||||
/**
|
||||
* Resets ping timeout.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
resetPingTimeout() {
|
||||
(0, timers_1.clearTimeout)(this.pingTimeoutTimer);
|
||||
this.pingTimeoutTimer = (0, timers_1.setTimeout)(() => {
|
||||
if (this.readyState === "closed")
|
||||
return;
|
||||
this.onClose("ping timeout");
|
||||
}, this.protocol === 3
|
||||
? this.server.opts.pingInterval + this.server.opts.pingTimeout
|
||||
: this.server.opts.pingTimeout);
|
||||
}
|
||||
/**
|
||||
* Attaches handlers for the given transport.
|
||||
*
|
||||
* @param {Transport} transport
|
||||
* @private
|
||||
*/
|
||||
setTransport(transport) {
|
||||
const onError = this.onError.bind(this);
|
||||
const onReady = () => this.flush();
|
||||
const onPacket = this.onPacket.bind(this);
|
||||
const onDrain = this.onDrain.bind(this);
|
||||
const onClose = this.onClose.bind(this, "transport close");
|
||||
this.transport = transport;
|
||||
this.transport.once("error", onError);
|
||||
this.transport.on("ready", onReady);
|
||||
this.transport.on("packet", onPacket);
|
||||
this.transport.on("drain", onDrain);
|
||||
this.transport.once("close", onClose);
|
||||
this.cleanupFn.push(function () {
|
||||
transport.removeListener("error", onError);
|
||||
transport.removeListener("ready", onReady);
|
||||
transport.removeListener("packet", onPacket);
|
||||
transport.removeListener("drain", onDrain);
|
||||
transport.removeListener("close", onClose);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Upon transport "drain" event
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
onDrain() {
|
||||
if (this.sentCallbackFn.length > 0) {
|
||||
debug("executing batch send callback");
|
||||
const seqFn = this.sentCallbackFn.shift();
|
||||
if (seqFn) {
|
||||
for (let i = 0; i < seqFn.length; i++) {
|
||||
seqFn[i](this.transport);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Upgrades socket to the given transport
|
||||
*
|
||||
* @param {Transport} transport
|
||||
* @private
|
||||
*/
|
||||
/* private */ _maybeUpgrade(transport) {
|
||||
debug('might upgrade socket transport from "%s" to "%s"', this.transport.name, transport.name);
|
||||
this.upgrading = true;
|
||||
// set transport upgrade timer
|
||||
const upgradeTimeoutTimer = (0, timers_1.setTimeout)(() => {
|
||||
debug("client did not complete upgrade - closing transport");
|
||||
cleanup();
|
||||
if ("open" === transport.readyState) {
|
||||
transport.close();
|
||||
}
|
||||
}, this.server.opts.upgradeTimeout);
|
||||
let checkIntervalTimer;
|
||||
const onPacket = (packet) => {
|
||||
if ("ping" === packet.type && "probe" === packet.data) {
|
||||
debug("got probe ping packet, sending pong");
|
||||
transport.send([{ type: "pong", data: "probe" }]);
|
||||
this.emit("upgrading", transport);
|
||||
clearInterval(checkIntervalTimer);
|
||||
checkIntervalTimer = setInterval(check, 100);
|
||||
}
|
||||
else if ("upgrade" === packet.type && this.readyState !== "closed") {
|
||||
debug("got upgrade packet - upgrading");
|
||||
cleanup();
|
||||
this.transport.discard();
|
||||
this.upgraded = true;
|
||||
this.clearTransport();
|
||||
this.setTransport(transport);
|
||||
this.emit("upgrade", transport);
|
||||
this.flush();
|
||||
if (this.readyState === "closing") {
|
||||
transport.close(() => {
|
||||
this.onClose("forced close");
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
cleanup();
|
||||
transport.close();
|
||||
}
|
||||
};
|
||||
// we force a polling cycle to ensure a fast upgrade
|
||||
const check = () => {
|
||||
if ("polling" === this.transport.name && this.transport.writable) {
|
||||
debug("writing a noop packet to polling for fast upgrade");
|
||||
this.transport.send([{ type: "noop" }]);
|
||||
}
|
||||
};
|
||||
const cleanup = () => {
|
||||
this.upgrading = false;
|
||||
clearInterval(checkIntervalTimer);
|
||||
(0, timers_1.clearTimeout)(upgradeTimeoutTimer);
|
||||
transport.removeListener("packet", onPacket);
|
||||
transport.removeListener("close", onTransportClose);
|
||||
transport.removeListener("error", onError);
|
||||
this.removeListener("close", onClose);
|
||||
};
|
||||
const onError = (err) => {
|
||||
debug("client did not complete upgrade - %s", err);
|
||||
cleanup();
|
||||
transport.close();
|
||||
transport = null;
|
||||
};
|
||||
const onTransportClose = () => {
|
||||
onError("transport closed");
|
||||
};
|
||||
const onClose = () => {
|
||||
onError("socket closed");
|
||||
};
|
||||
transport.on("packet", onPacket);
|
||||
transport.once("close", onTransportClose);
|
||||
transport.once("error", onError);
|
||||
this.once("close", onClose);
|
||||
}
|
||||
/**
|
||||
* Clears listeners and timers associated with current transport.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
clearTransport() {
|
||||
let cleanup;
|
||||
const toCleanUp = this.cleanupFn.length;
|
||||
for (let i = 0; i < toCleanUp; i++) {
|
||||
cleanup = this.cleanupFn.shift();
|
||||
cleanup();
|
||||
}
|
||||
// silence further transport errors and prevent uncaught exceptions
|
||||
this.transport.on("error", function () {
|
||||
debug("error triggered by discarded transport");
|
||||
});
|
||||
// ensure transport won't stay open
|
||||
this.transport.close();
|
||||
(0, timers_1.clearTimeout)(this.pingTimeoutTimer);
|
||||
}
|
||||
/**
|
||||
* Called upon transport considered closed.
|
||||
* Possible reasons: `ping timeout`, `client error`, `parse error`,
|
||||
* `transport error`, `server close`, `transport close`
|
||||
*/
|
||||
onClose(reason, description) {
|
||||
if ("closed" !== this.readyState) {
|
||||
this.readyState = "closed";
|
||||
// clear timers
|
||||
(0, timers_1.clearTimeout)(this.pingIntervalTimer);
|
||||
(0, timers_1.clearTimeout)(this.pingTimeoutTimer);
|
||||
// clean writeBuffer in next tick, so developers can still
|
||||
// grab the writeBuffer on 'close' event
|
||||
process.nextTick(() => {
|
||||
this.writeBuffer = [];
|
||||
});
|
||||
this.packetsFn = [];
|
||||
this.sentCallbackFn = [];
|
||||
this.clearTransport();
|
||||
this.emit("close", reason, description);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Sends a message packet.
|
||||
*
|
||||
* @param {Object} data
|
||||
* @param {Object} options
|
||||
* @param {Function} callback
|
||||
* @return {Socket} for chaining
|
||||
*/
|
||||
send(data, options, callback) {
|
||||
this.sendPacket("message", data, options, callback);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Alias of {@link send}.
|
||||
*
|
||||
* @param data
|
||||
* @param options
|
||||
* @param callback
|
||||
*/
|
||||
write(data, options, callback) {
|
||||
this.sendPacket("message", data, options, callback);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Sends a packet.
|
||||
*
|
||||
* @param {String} type - packet type
|
||||
* @param {String} data
|
||||
* @param {Object} options
|
||||
* @param {Function} callback
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
sendPacket(type, data, options = {}, callback) {
|
||||
if ("function" === typeof options) {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
if ("closing" !== this.readyState && "closed" !== this.readyState) {
|
||||
debug('sending packet "%s" (%s)', type, data);
|
||||
// compression is enabled by default
|
||||
options.compress = options.compress !== false;
|
||||
const packet = {
|
||||
type,
|
||||
options: options,
|
||||
};
|
||||
if (data)
|
||||
packet.data = data;
|
||||
// exports packetCreate event
|
||||
this.emit("packetCreate", packet);
|
||||
this.writeBuffer.push(packet);
|
||||
// add send callback to object, if defined
|
||||
if ("function" === typeof callback)
|
||||
this.packetsFn.push(callback);
|
||||
this.flush();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Attempts to flush the packets buffer.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
flush() {
|
||||
if ("closed" !== this.readyState &&
|
||||
this.transport.writable &&
|
||||
this.writeBuffer.length) {
|
||||
debug("flushing buffer to transport");
|
||||
this.emit("flush", this.writeBuffer);
|
||||
this.server.emit("flush", this, this.writeBuffer);
|
||||
const wbuf = this.writeBuffer;
|
||||
this.writeBuffer = [];
|
||||
if (this.packetsFn.length) {
|
||||
this.sentCallbackFn.push(this.packetsFn);
|
||||
this.packetsFn = [];
|
||||
}
|
||||
else {
|
||||
this.sentCallbackFn.push(null);
|
||||
}
|
||||
this.transport.send(wbuf);
|
||||
this.emit("drain");
|
||||
this.server.emit("drain", this);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get available upgrades for this socket.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
getAvailableUpgrades() {
|
||||
const availableUpgrades = [];
|
||||
const allUpgrades = this.server.upgrades(this.transport.name);
|
||||
for (let i = 0; i < allUpgrades.length; ++i) {
|
||||
const upg = allUpgrades[i];
|
||||
if (this.server.opts.transports.indexOf(upg) !== -1) {
|
||||
availableUpgrades.push(upg);
|
||||
}
|
||||
}
|
||||
return availableUpgrades;
|
||||
}
|
||||
/**
|
||||
* Closes the socket and underlying transport.
|
||||
*
|
||||
* @param {Boolean} discard - optional, discard the transport
|
||||
* @return {Socket} for chaining
|
||||
*/
|
||||
close(discard) {
|
||||
if (discard &&
|
||||
(this.readyState === "open" || this.readyState === "closing")) {
|
||||
return this.closeTransport(discard);
|
||||
}
|
||||
if ("open" !== this.readyState)
|
||||
return;
|
||||
this.readyState = "closing";
|
||||
if (this.writeBuffer.length) {
|
||||
debug("there are %d remaining packets in the buffer, waiting for the 'drain' event", this.writeBuffer.length);
|
||||
this.once("drain", () => {
|
||||
debug("all packets have been sent, closing the transport");
|
||||
this.closeTransport(discard);
|
||||
});
|
||||
return;
|
||||
}
|
||||
debug("the buffer is empty, closing the transport right away");
|
||||
this.closeTransport(discard);
|
||||
}
|
||||
/**
|
||||
* Closes the underlying transport.
|
||||
*
|
||||
* @param {Boolean} discard
|
||||
* @private
|
||||
*/
|
||||
closeTransport(discard) {
|
||||
debug("closing the transport (discard? %s)", !!discard);
|
||||
if (discard)
|
||||
this.transport.discard();
|
||||
this.transport.close(this.onClose.bind(this, "forced close"));
|
||||
}
|
||||
}
|
||||
exports.Socket = Socket;
|
||||
124
node_modules/engine.io/build/transport.d.ts
generated
vendored
Normal file
124
node_modules/engine.io/build/transport.d.ts
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
import { EventEmitter } from "events";
|
||||
import type { IncomingMessage, ServerResponse } from "http";
|
||||
import { Packet, RawData } from "engine.io-parser";
|
||||
type ReadyState = "open" | "closing" | "closed";
|
||||
export type EngineRequest = IncomingMessage & {
|
||||
_query: Record<string, string>;
|
||||
res?: ServerResponse;
|
||||
cleanup?: Function;
|
||||
websocket?: any;
|
||||
};
|
||||
export declare abstract class Transport extends EventEmitter {
|
||||
/**
|
||||
* The session ID.
|
||||
*/
|
||||
sid: string;
|
||||
/**
|
||||
* Whether the transport is currently ready to send packets.
|
||||
*/
|
||||
writable: boolean;
|
||||
/**
|
||||
* The revision of the protocol:
|
||||
*
|
||||
* - 3 is used in Engine.IO v3 / Socket.IO v2
|
||||
* - 4 is used in Engine.IO v4 and above / Socket.IO v3 and above
|
||||
*
|
||||
* It is found in the `EIO` query parameters of the HTTP requests.
|
||||
*
|
||||
* @see https://github.com/socketio/engine.io-protocol
|
||||
*/
|
||||
protocol: number;
|
||||
/**
|
||||
* The current state of the transport.
|
||||
* @protected
|
||||
*/
|
||||
protected _readyState: ReadyState;
|
||||
/**
|
||||
* Whether the transport is discarded and can be safely closed (used during upgrade).
|
||||
* @protected
|
||||
*/
|
||||
protected discarded: boolean;
|
||||
/**
|
||||
* The parser to use (depends on the revision of the {@link Transport#protocol}.
|
||||
* @protected
|
||||
*/
|
||||
protected parser: any;
|
||||
/**
|
||||
* Whether the transport supports binary payloads (else it will be base64-encoded)
|
||||
* @protected
|
||||
*/
|
||||
protected supportsBinary: boolean;
|
||||
get readyState(): ReadyState;
|
||||
set readyState(state: ReadyState);
|
||||
/**
|
||||
* Transport constructor.
|
||||
*
|
||||
* @param {EngineRequest} req
|
||||
*/
|
||||
constructor(req: {
|
||||
_query: Record<string, string>;
|
||||
});
|
||||
/**
|
||||
* Flags the transport as discarded.
|
||||
*
|
||||
* @package
|
||||
*/
|
||||
discard(): void;
|
||||
/**
|
||||
* Called with an incoming HTTP request.
|
||||
*
|
||||
* @param req
|
||||
* @package
|
||||
*/
|
||||
onRequest(req: any): void;
|
||||
/**
|
||||
* Closes the transport.
|
||||
*
|
||||
* @package
|
||||
*/
|
||||
close(fn?: () => void): void;
|
||||
/**
|
||||
* Called with a transport error.
|
||||
*
|
||||
* @param {String} msg - message error
|
||||
* @param {Object} desc - error description
|
||||
* @protected
|
||||
*/
|
||||
protected onError(msg: string, desc?: any): void;
|
||||
/**
|
||||
* Called with parsed out a packets from the data stream.
|
||||
*
|
||||
* @param {Object} packet
|
||||
* @protected
|
||||
*/
|
||||
protected onPacket(packet: Packet): void;
|
||||
/**
|
||||
* Called with the encoded packet data.
|
||||
*
|
||||
* @param {String} data
|
||||
* @protected
|
||||
*/
|
||||
protected onData(data: RawData): void;
|
||||
/**
|
||||
* Called upon transport close.
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
protected onClose(): void;
|
||||
/**
|
||||
* The name of the transport.
|
||||
*/
|
||||
abstract get name(): string;
|
||||
/**
|
||||
* Sends an array of packets.
|
||||
*
|
||||
* @param {Array} packets
|
||||
* @package
|
||||
*/
|
||||
abstract send(packets: Packet[]): void;
|
||||
/**
|
||||
* Closes the transport.
|
||||
*/
|
||||
abstract doClose(fn?: () => void): void;
|
||||
}
|
||||
export {};
|
||||
117
node_modules/engine.io/build/transport.js
generated
vendored
Normal file
117
node_modules/engine.io/build/transport.js
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Transport = void 0;
|
||||
const events_1 = require("events");
|
||||
const parser_v4 = require("engine.io-parser");
|
||||
const parser_v3 = require("./parser-v3/index");
|
||||
const debug_1 = require("debug");
|
||||
const debug = (0, debug_1.default)("engine:transport");
|
||||
function noop() { }
|
||||
class Transport extends events_1.EventEmitter {
|
||||
get readyState() {
|
||||
return this._readyState;
|
||||
}
|
||||
set readyState(state) {
|
||||
debug("readyState updated from %s to %s (%s)", this._readyState, state, this.name);
|
||||
this._readyState = state;
|
||||
}
|
||||
/**
|
||||
* Transport constructor.
|
||||
*
|
||||
* @param {EngineRequest} req
|
||||
*/
|
||||
constructor(req) {
|
||||
super();
|
||||
/**
|
||||
* Whether the transport is currently ready to send packets.
|
||||
*/
|
||||
this.writable = false;
|
||||
/**
|
||||
* The current state of the transport.
|
||||
* @protected
|
||||
*/
|
||||
this._readyState = "open";
|
||||
/**
|
||||
* Whether the transport is discarded and can be safely closed (used during upgrade).
|
||||
* @protected
|
||||
*/
|
||||
this.discarded = false;
|
||||
this.protocol = req._query.EIO === "4" ? 4 : 3; // 3rd revision by default
|
||||
this.parser = this.protocol === 4 ? parser_v4 : parser_v3;
|
||||
this.supportsBinary = !(req._query && req._query.b64);
|
||||
}
|
||||
/**
|
||||
* Flags the transport as discarded.
|
||||
*
|
||||
* @package
|
||||
*/
|
||||
discard() {
|
||||
this.discarded = true;
|
||||
}
|
||||
/**
|
||||
* Called with an incoming HTTP request.
|
||||
*
|
||||
* @param req
|
||||
* @package
|
||||
*/
|
||||
onRequest(req) { }
|
||||
/**
|
||||
* Closes the transport.
|
||||
*
|
||||
* @package
|
||||
*/
|
||||
close(fn) {
|
||||
if ("closed" === this.readyState || "closing" === this.readyState)
|
||||
return;
|
||||
this.readyState = "closing";
|
||||
this.doClose(fn || noop);
|
||||
}
|
||||
/**
|
||||
* Called with a transport error.
|
||||
*
|
||||
* @param {String} msg - message error
|
||||
* @param {Object} desc - error description
|
||||
* @protected
|
||||
*/
|
||||
onError(msg, desc) {
|
||||
if (this.listeners("error").length) {
|
||||
const err = new Error(msg);
|
||||
// @ts-ignore
|
||||
err.type = "TransportError";
|
||||
// @ts-ignore
|
||||
err.description = desc;
|
||||
this.emit("error", err);
|
||||
}
|
||||
else {
|
||||
debug("ignored transport error %s (%s)", msg, desc);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Called with parsed out a packets from the data stream.
|
||||
*
|
||||
* @param {Object} packet
|
||||
* @protected
|
||||
*/
|
||||
onPacket(packet) {
|
||||
this.emit("packet", packet);
|
||||
}
|
||||
/**
|
||||
* Called with the encoded packet data.
|
||||
*
|
||||
* @param {String} data
|
||||
* @protected
|
||||
*/
|
||||
onData(data) {
|
||||
this.onPacket(this.parser.decodePacket(data));
|
||||
}
|
||||
/**
|
||||
* Called upon transport close.
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
onClose() {
|
||||
this.readyState = "closed";
|
||||
this.emit("close");
|
||||
}
|
||||
}
|
||||
exports.Transport = Transport;
|
||||
7
node_modules/engine.io/build/transports-uws/index.d.ts
generated
vendored
Normal file
7
node_modules/engine.io/build/transports-uws/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Polling } from "./polling";
|
||||
import { WebSocket } from "./websocket";
|
||||
declare const _default: {
|
||||
polling: typeof Polling;
|
||||
websocket: typeof WebSocket;
|
||||
};
|
||||
export default _default;
|
||||
8
node_modules/engine.io/build/transports-uws/index.js
generated
vendored
Normal file
8
node_modules/engine.io/build/transports-uws/index.js
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const polling_1 = require("./polling");
|
||||
const websocket_1 = require("./websocket");
|
||||
exports.default = {
|
||||
polling: polling_1.Polling,
|
||||
websocket: websocket_1.WebSocket,
|
||||
};
|
||||
99
node_modules/engine.io/build/transports-uws/polling.d.ts
generated
vendored
Normal file
99
node_modules/engine.io/build/transports-uws/polling.d.ts
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
import { Transport } from "../transport";
|
||||
export declare class Polling extends Transport {
|
||||
maxHttpBufferSize: number;
|
||||
httpCompression: any;
|
||||
private req;
|
||||
private res;
|
||||
private dataReq;
|
||||
private dataRes;
|
||||
private shouldClose;
|
||||
private readonly closeTimeout;
|
||||
/**
|
||||
* HTTP polling constructor.
|
||||
*/
|
||||
constructor(req: any);
|
||||
/**
|
||||
* Transport name
|
||||
*/
|
||||
get name(): string;
|
||||
/**
|
||||
* Overrides onRequest.
|
||||
*
|
||||
* @param req
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
onRequest(req: any): void;
|
||||
/**
|
||||
* The client sends a request awaiting for us to send data.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
onPollRequest(req: any, res: any): void;
|
||||
/**
|
||||
* The client sends a request with data.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
onDataRequest(req: any, res: any): void;
|
||||
/**
|
||||
* Cleanup request.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private onDataRequestCleanup;
|
||||
/**
|
||||
* Processes the incoming data payload.
|
||||
*
|
||||
* @param {String} encoded payload
|
||||
* @private
|
||||
*/
|
||||
onData(data: any): void;
|
||||
/**
|
||||
* Overrides onClose.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
onClose(): void;
|
||||
/**
|
||||
* Writes a packet payload.
|
||||
*
|
||||
* @param {Object} packet
|
||||
* @private
|
||||
*/
|
||||
send(packets: any): void;
|
||||
/**
|
||||
* Writes data as response to poll request.
|
||||
*
|
||||
* @param {String} data
|
||||
* @param {Object} options
|
||||
* @private
|
||||
*/
|
||||
write(data: any, options: any): void;
|
||||
/**
|
||||
* Performs the write.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
doWrite(data: any, options: any, callback: any): void;
|
||||
/**
|
||||
* Compresses data.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
compress(data: any, encoding: any, callback: any): void;
|
||||
/**
|
||||
* Closes the transport.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
doClose(fn: any): void;
|
||||
/**
|
||||
* Returns headers for a response.
|
||||
*
|
||||
* @param req - request
|
||||
* @param {Object} extra headers
|
||||
* @private
|
||||
*/
|
||||
headers(req: any, headers: any): any;
|
||||
}
|
||||
364
node_modules/engine.io/build/transports-uws/polling.js
generated
vendored
Normal file
364
node_modules/engine.io/build/transports-uws/polling.js
generated
vendored
Normal file
@@ -0,0 +1,364 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Polling = void 0;
|
||||
const transport_1 = require("../transport");
|
||||
const zlib_1 = require("zlib");
|
||||
const accepts = require("accepts");
|
||||
const debug_1 = require("debug");
|
||||
const debug = (0, debug_1.default)("engine:polling");
|
||||
const compressionMethods = {
|
||||
gzip: zlib_1.createGzip,
|
||||
deflate: zlib_1.createDeflate,
|
||||
};
|
||||
class Polling extends transport_1.Transport {
|
||||
/**
|
||||
* HTTP polling constructor.
|
||||
*/
|
||||
constructor(req) {
|
||||
super(req);
|
||||
this.closeTimeout = 30 * 1000;
|
||||
}
|
||||
/**
|
||||
* Transport name
|
||||
*/
|
||||
get name() {
|
||||
return "polling";
|
||||
}
|
||||
/**
|
||||
* Overrides onRequest.
|
||||
*
|
||||
* @param req
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
onRequest(req) {
|
||||
const res = req.res;
|
||||
// remove the reference to the ServerResponse object (as the first request of the session is kept in memory by default)
|
||||
req.res = null;
|
||||
if (req.getMethod() === "get") {
|
||||
this.onPollRequest(req, res);
|
||||
}
|
||||
else if (req.getMethod() === "post") {
|
||||
this.onDataRequest(req, res);
|
||||
}
|
||||
else {
|
||||
res.writeStatus("500 Internal Server Error");
|
||||
res.end();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The client sends a request awaiting for us to send data.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
onPollRequest(req, res) {
|
||||
if (this.req) {
|
||||
debug("request overlap");
|
||||
// assert: this.res, '.req and .res should be (un)set together'
|
||||
this.onError("overlap from client");
|
||||
res.writeStatus("500 Internal Server Error");
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
debug("setting request");
|
||||
this.req = req;
|
||||
this.res = res;
|
||||
const onClose = () => {
|
||||
this.writable = false;
|
||||
this.onError("poll connection closed prematurely");
|
||||
};
|
||||
const cleanup = () => {
|
||||
this.req = this.res = null;
|
||||
};
|
||||
req.cleanup = cleanup;
|
||||
res.onAborted(onClose);
|
||||
this.writable = true;
|
||||
this.emit("ready");
|
||||
// if we're still writable but had a pending close, trigger an empty send
|
||||
if (this.writable && this.shouldClose) {
|
||||
debug("triggering empty send to append close packet");
|
||||
this.send([{ type: "noop" }]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The client sends a request with data.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
onDataRequest(req, res) {
|
||||
if (this.dataReq) {
|
||||
// assert: this.dataRes, '.dataReq and .dataRes should be (un)set together'
|
||||
this.onError("data request overlap from client");
|
||||
res.writeStatus("500 Internal Server Error");
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
const expectedContentLength = Number(req.headers["content-length"]);
|
||||
if (!expectedContentLength) {
|
||||
this.onError("content-length header required");
|
||||
res.writeStatus("411 Length Required").end();
|
||||
return;
|
||||
}
|
||||
if (expectedContentLength > this.maxHttpBufferSize) {
|
||||
this.onError("payload too large");
|
||||
res.writeStatus("413 Payload Too Large").end();
|
||||
return;
|
||||
}
|
||||
const isBinary = "application/octet-stream" === req.headers["content-type"];
|
||||
if (isBinary && this.protocol === 4) {
|
||||
return this.onError("invalid content");
|
||||
}
|
||||
this.dataReq = req;
|
||||
this.dataRes = res;
|
||||
let buffer;
|
||||
let offset = 0;
|
||||
const headers = {
|
||||
// text/html is required instead of text/plain to avoid an
|
||||
// unwanted download dialog on certain user-agents (GH-43)
|
||||
"Content-Type": "text/html",
|
||||
};
|
||||
this.headers(req, headers);
|
||||
for (let key in headers) {
|
||||
res.writeHeader(key, String(headers[key]));
|
||||
}
|
||||
const onEnd = (buffer) => {
|
||||
this.onData(buffer.toString());
|
||||
this.onDataRequestCleanup();
|
||||
res.cork(() => {
|
||||
res.end("ok");
|
||||
});
|
||||
};
|
||||
res.onAborted(() => {
|
||||
this.onDataRequestCleanup();
|
||||
this.onError("data request connection closed prematurely");
|
||||
});
|
||||
res.onData((arrayBuffer, isLast) => {
|
||||
const totalLength = offset + arrayBuffer.byteLength;
|
||||
if (totalLength > expectedContentLength) {
|
||||
this.onError("content-length mismatch");
|
||||
res.close(); // calls onAborted
|
||||
return;
|
||||
}
|
||||
if (!buffer) {
|
||||
if (isLast) {
|
||||
onEnd(Buffer.from(arrayBuffer));
|
||||
return;
|
||||
}
|
||||
buffer = Buffer.allocUnsafe(expectedContentLength);
|
||||
}
|
||||
Buffer.from(arrayBuffer).copy(buffer, offset);
|
||||
if (isLast) {
|
||||
if (totalLength != expectedContentLength) {
|
||||
this.onError("content-length mismatch");
|
||||
res.writeStatus("400 Content-Length Mismatch").end();
|
||||
this.onDataRequestCleanup();
|
||||
return;
|
||||
}
|
||||
onEnd(buffer);
|
||||
return;
|
||||
}
|
||||
offset = totalLength;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Cleanup request.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
onDataRequestCleanup() {
|
||||
this.dataReq = this.dataRes = null;
|
||||
}
|
||||
/**
|
||||
* Processes the incoming data payload.
|
||||
*
|
||||
* @param {String} encoded payload
|
||||
* @private
|
||||
*/
|
||||
onData(data) {
|
||||
debug('received "%s"', data);
|
||||
const callback = (packet) => {
|
||||
if ("close" === packet.type) {
|
||||
debug("got xhr close packet");
|
||||
this.onClose();
|
||||
return false;
|
||||
}
|
||||
this.onPacket(packet);
|
||||
};
|
||||
if (this.protocol === 3) {
|
||||
this.parser.decodePayload(data, callback);
|
||||
}
|
||||
else {
|
||||
this.parser.decodePayload(data).forEach(callback);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Overrides onClose.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
onClose() {
|
||||
if (this.writable) {
|
||||
// close pending poll request
|
||||
this.send([{ type: "noop" }]);
|
||||
}
|
||||
super.onClose();
|
||||
}
|
||||
/**
|
||||
* Writes a packet payload.
|
||||
*
|
||||
* @param {Object} packet
|
||||
* @private
|
||||
*/
|
||||
send(packets) {
|
||||
this.writable = false;
|
||||
if (this.shouldClose) {
|
||||
debug("appending close packet to payload");
|
||||
packets.push({ type: "close" });
|
||||
this.shouldClose();
|
||||
this.shouldClose = null;
|
||||
}
|
||||
const doWrite = (data) => {
|
||||
const compress = packets.some((packet) => {
|
||||
return packet.options && packet.options.compress;
|
||||
});
|
||||
this.write(data, { compress });
|
||||
};
|
||||
if (this.protocol === 3) {
|
||||
this.parser.encodePayload(packets, this.supportsBinary, doWrite);
|
||||
}
|
||||
else {
|
||||
this.parser.encodePayload(packets, doWrite);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Writes data as response to poll request.
|
||||
*
|
||||
* @param {String} data
|
||||
* @param {Object} options
|
||||
* @private
|
||||
*/
|
||||
write(data, options) {
|
||||
debug('writing "%s"', data);
|
||||
this.doWrite(data, options, () => {
|
||||
this.req.cleanup();
|
||||
this.emit("drain");
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Performs the write.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
doWrite(data, options, callback) {
|
||||
// explicit UTF-8 is required for pages not served under utf
|
||||
const isString = typeof data === "string";
|
||||
const contentType = isString
|
||||
? "text/plain; charset=UTF-8"
|
||||
: "application/octet-stream";
|
||||
const headers = {
|
||||
"Content-Type": contentType,
|
||||
};
|
||||
const respond = (data) => {
|
||||
this.headers(this.req, headers);
|
||||
this.res.cork(() => {
|
||||
Object.keys(headers).forEach((key) => {
|
||||
this.res.writeHeader(key, String(headers[key]));
|
||||
});
|
||||
this.res.end(data);
|
||||
});
|
||||
callback();
|
||||
};
|
||||
if (!this.httpCompression || !options.compress) {
|
||||
respond(data);
|
||||
return;
|
||||
}
|
||||
const len = isString ? Buffer.byteLength(data) : data.length;
|
||||
if (len < this.httpCompression.threshold) {
|
||||
respond(data);
|
||||
return;
|
||||
}
|
||||
const encoding = accepts(this.req).encodings(["gzip", "deflate"]);
|
||||
if (!encoding) {
|
||||
respond(data);
|
||||
return;
|
||||
}
|
||||
this.compress(data, encoding, (err, data) => {
|
||||
if (err) {
|
||||
this.res.writeStatus("500 Internal Server Error");
|
||||
this.res.end();
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
headers["Content-Encoding"] = encoding;
|
||||
respond(data);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Compresses data.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
compress(data, encoding, callback) {
|
||||
debug("compressing");
|
||||
const buffers = [];
|
||||
let nread = 0;
|
||||
compressionMethods[encoding](this.httpCompression)
|
||||
.on("error", callback)
|
||||
.on("data", function (chunk) {
|
||||
buffers.push(chunk);
|
||||
nread += chunk.length;
|
||||
})
|
||||
.on("end", function () {
|
||||
callback(null, Buffer.concat(buffers, nread));
|
||||
})
|
||||
.end(data);
|
||||
}
|
||||
/**
|
||||
* Closes the transport.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
doClose(fn) {
|
||||
debug("closing");
|
||||
let closeTimeoutTimer;
|
||||
const onClose = () => {
|
||||
clearTimeout(closeTimeoutTimer);
|
||||
fn();
|
||||
this.onClose();
|
||||
};
|
||||
if (this.writable) {
|
||||
debug("transport writable - closing right away");
|
||||
this.send([{ type: "close" }]);
|
||||
onClose();
|
||||
}
|
||||
else if (this.discarded) {
|
||||
debug("transport discarded - closing right away");
|
||||
onClose();
|
||||
}
|
||||
else {
|
||||
debug("transport not writable - buffering orderly close");
|
||||
this.shouldClose = onClose;
|
||||
closeTimeoutTimer = setTimeout(onClose, this.closeTimeout);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns headers for a response.
|
||||
*
|
||||
* @param req - request
|
||||
* @param {Object} extra headers
|
||||
* @private
|
||||
*/
|
||||
headers(req, headers) {
|
||||
headers = headers || {};
|
||||
// prevent XSS warnings on IE
|
||||
// https://github.com/LearnBoost/socket.io/pull/1333
|
||||
const ua = req.headers["user-agent"];
|
||||
if (ua && (~ua.indexOf(";MSIE") || ~ua.indexOf("Trident/"))) {
|
||||
headers["X-XSS-Protection"] = "0";
|
||||
}
|
||||
headers["cache-control"] = "no-store";
|
||||
this.emit("headers", headers, req);
|
||||
return headers;
|
||||
}
|
||||
}
|
||||
exports.Polling = Polling;
|
||||
32
node_modules/engine.io/build/transports-uws/websocket.d.ts
generated
vendored
Normal file
32
node_modules/engine.io/build/transports-uws/websocket.d.ts
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Transport } from "../transport";
|
||||
export declare class WebSocket extends Transport {
|
||||
protected perMessageDeflate: any;
|
||||
private socket;
|
||||
/**
|
||||
* WebSocket transport
|
||||
*
|
||||
* @param req
|
||||
*/
|
||||
constructor(req: any);
|
||||
/**
|
||||
* Transport name
|
||||
*/
|
||||
get name(): string;
|
||||
/**
|
||||
* Advertise upgrade support.
|
||||
*/
|
||||
get handlesUpgrades(): boolean;
|
||||
/**
|
||||
* Writes a packet payload.
|
||||
*
|
||||
* @param {Array} packets
|
||||
* @private
|
||||
*/
|
||||
send(packets: any): void;
|
||||
/**
|
||||
* Closes the transport.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
doClose(fn: any): void;
|
||||
}
|
||||
73
node_modules/engine.io/build/transports-uws/websocket.js
generated
vendored
Normal file
73
node_modules/engine.io/build/transports-uws/websocket.js
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.WebSocket = void 0;
|
||||
const transport_1 = require("../transport");
|
||||
const debug_1 = require("debug");
|
||||
const debug = (0, debug_1.default)("engine:ws");
|
||||
class WebSocket extends transport_1.Transport {
|
||||
/**
|
||||
* WebSocket transport
|
||||
*
|
||||
* @param req
|
||||
*/
|
||||
constructor(req) {
|
||||
super(req);
|
||||
this.writable = false;
|
||||
this.perMessageDeflate = null;
|
||||
}
|
||||
/**
|
||||
* Transport name
|
||||
*/
|
||||
get name() {
|
||||
return "websocket";
|
||||
}
|
||||
/**
|
||||
* Advertise upgrade support.
|
||||
*/
|
||||
get handlesUpgrades() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Writes a packet payload.
|
||||
*
|
||||
* @param {Array} packets
|
||||
* @private
|
||||
*/
|
||||
send(packets) {
|
||||
this.writable = false;
|
||||
for (let i = 0; i < packets.length; i++) {
|
||||
const packet = packets[i];
|
||||
const isLast = i + 1 === packets.length;
|
||||
const send = (data) => {
|
||||
const isBinary = typeof data !== "string";
|
||||
const compress = this.perMessageDeflate &&
|
||||
Buffer.byteLength(data) > this.perMessageDeflate.threshold;
|
||||
debug('writing "%s"', data);
|
||||
this.socket.send(data, isBinary, compress);
|
||||
if (isLast) {
|
||||
this.emit("drain");
|
||||
this.writable = true;
|
||||
this.emit("ready");
|
||||
}
|
||||
};
|
||||
if (packet.options && typeof packet.options.wsPreEncoded === "string") {
|
||||
send(packet.options.wsPreEncoded);
|
||||
}
|
||||
else {
|
||||
this.parser.encodePacket(packet, this.supportsBinary, send);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Closes the transport.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
doClose(fn) {
|
||||
debug("closing");
|
||||
fn && fn();
|
||||
// call fn first since socket.end() immediately emits a "close" event
|
||||
this.socket.end();
|
||||
}
|
||||
}
|
||||
exports.WebSocket = WebSocket;
|
||||
16
node_modules/engine.io/build/transports/index.d.ts
generated
vendored
Normal file
16
node_modules/engine.io/build/transports/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Polling as XHR } from "./polling";
|
||||
import { WebSocket } from "./websocket";
|
||||
import { WebTransport } from "./webtransport";
|
||||
declare const _default: {
|
||||
polling: typeof polling;
|
||||
websocket: typeof WebSocket;
|
||||
webtransport: typeof WebTransport;
|
||||
};
|
||||
export default _default;
|
||||
/**
|
||||
* Polling polymorphic constructor.
|
||||
*/
|
||||
declare function polling(req: any): XHR;
|
||||
declare namespace polling {
|
||||
var upgradesTo: string[];
|
||||
}
|
||||
23
node_modules/engine.io/build/transports/index.js
generated
vendored
Normal file
23
node_modules/engine.io/build/transports/index.js
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const polling_1 = require("./polling");
|
||||
const polling_jsonp_1 = require("./polling-jsonp");
|
||||
const websocket_1 = require("./websocket");
|
||||
const webtransport_1 = require("./webtransport");
|
||||
exports.default = {
|
||||
polling: polling,
|
||||
websocket: websocket_1.WebSocket,
|
||||
webtransport: webtransport_1.WebTransport,
|
||||
};
|
||||
/**
|
||||
* Polling polymorphic constructor.
|
||||
*/
|
||||
function polling(req) {
|
||||
if ("string" === typeof req._query.j) {
|
||||
return new polling_jsonp_1.JSONP(req);
|
||||
}
|
||||
else {
|
||||
return new polling_1.Polling(req);
|
||||
}
|
||||
}
|
||||
polling.upgradesTo = ["websocket", "webtransport"];
|
||||
12
node_modules/engine.io/build/transports/polling-jsonp.d.ts
generated
vendored
Normal file
12
node_modules/engine.io/build/transports/polling-jsonp.d.ts
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Polling } from "./polling";
|
||||
import type { RawData } from "engine.io-parser";
|
||||
export declare class JSONP extends Polling {
|
||||
private readonly head;
|
||||
private readonly foot;
|
||||
/**
|
||||
* JSON-P polling transport.
|
||||
*/
|
||||
constructor(req: any);
|
||||
onData(data: RawData): void;
|
||||
doWrite(data: any, options: any, callback: any): void;
|
||||
}
|
||||
41
node_modules/engine.io/build/transports/polling-jsonp.js
generated
vendored
Normal file
41
node_modules/engine.io/build/transports/polling-jsonp.js
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.JSONP = void 0;
|
||||
const polling_1 = require("./polling");
|
||||
const qs = require("querystring");
|
||||
const rDoubleSlashes = /\\\\n/g;
|
||||
const rSlashes = /(\\)?\\n/g;
|
||||
class JSONP extends polling_1.Polling {
|
||||
/**
|
||||
* JSON-P polling transport.
|
||||
*/
|
||||
constructor(req) {
|
||||
super(req);
|
||||
this.head = "___eio[" + (req._query.j || "").replace(/[^0-9]/g, "") + "](";
|
||||
this.foot = ");";
|
||||
}
|
||||
onData(data) {
|
||||
// we leverage the qs module so that we get built-in DoS protection
|
||||
// and the fast alternative to decodeURIComponent
|
||||
data = qs.parse(data).d;
|
||||
if ("string" === typeof data) {
|
||||
// client will send already escaped newlines as \\\\n and newlines as \\n
|
||||
// \\n must be replaced with \n and \\\\n with \\n
|
||||
data = data.replace(rSlashes, function (match, slashes) {
|
||||
return slashes ? match : "\n";
|
||||
});
|
||||
super.onData(data.replace(rDoubleSlashes, "\\n"));
|
||||
}
|
||||
}
|
||||
doWrite(data, options, callback) {
|
||||
// we must output valid javascript, not valid json
|
||||
// see: http://timelessrepo.com/json-isnt-a-javascript-subset
|
||||
const js = JSON.stringify(data)
|
||||
.replace(/\u2028/g, "\\u2028")
|
||||
.replace(/\u2029/g, "\\u2029");
|
||||
// prepare response
|
||||
data = this.head + js + this.foot;
|
||||
super.doWrite(data, options, callback);
|
||||
}
|
||||
}
|
||||
exports.JSONP = JSONP;
|
||||
87
node_modules/engine.io/build/transports/polling.d.ts
generated
vendored
Normal file
87
node_modules/engine.io/build/transports/polling.d.ts
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
import { EngineRequest, Transport } from "../transport";
|
||||
import type { Packet, RawData } from "engine.io-parser";
|
||||
export declare class Polling extends Transport {
|
||||
maxHttpBufferSize: number;
|
||||
httpCompression: any;
|
||||
private req;
|
||||
private res;
|
||||
private dataReq;
|
||||
private dataRes;
|
||||
private shouldClose;
|
||||
private readonly closeTimeout;
|
||||
/**
|
||||
* HTTP polling constructor.
|
||||
*/
|
||||
constructor(req: any);
|
||||
/**
|
||||
* Transport name
|
||||
*/
|
||||
get name(): string;
|
||||
/**
|
||||
* Overrides onRequest.
|
||||
*
|
||||
* @param {EngineRequest} req
|
||||
* @package
|
||||
*/
|
||||
onRequest(req: EngineRequest): void;
|
||||
/**
|
||||
* The client sends a request awaiting for us to send data.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private onPollRequest;
|
||||
/**
|
||||
* The client sends a request with data.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private onDataRequest;
|
||||
/**
|
||||
* Processes the incoming data payload.
|
||||
*
|
||||
* @param data - encoded payload
|
||||
* @protected
|
||||
*/
|
||||
onData(data: RawData): void;
|
||||
/**
|
||||
* Overrides onClose.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
onClose(): void;
|
||||
send(packets: Packet[]): void;
|
||||
/**
|
||||
* Writes data as response to poll request.
|
||||
*
|
||||
* @param {String} data
|
||||
* @param {Object} options
|
||||
* @private
|
||||
*/
|
||||
private write;
|
||||
/**
|
||||
* Performs the write.
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
protected doWrite(data: any, options: any, callback: any): void;
|
||||
/**
|
||||
* Compresses data.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private compress;
|
||||
/**
|
||||
* Closes the transport.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
doClose(fn: () => void): void;
|
||||
/**
|
||||
* Returns headers for a response.
|
||||
*
|
||||
* @param {http.IncomingMessage} req
|
||||
* @param {Object} headers - extra headers
|
||||
* @private
|
||||
*/
|
||||
private headers;
|
||||
}
|
||||
332
node_modules/engine.io/build/transports/polling.js
generated
vendored
Normal file
332
node_modules/engine.io/build/transports/polling.js
generated
vendored
Normal file
@@ -0,0 +1,332 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Polling = void 0;
|
||||
const transport_1 = require("../transport");
|
||||
const zlib_1 = require("zlib");
|
||||
const accepts = require("accepts");
|
||||
const debug_1 = require("debug");
|
||||
const debug = (0, debug_1.default)("engine:polling");
|
||||
const compressionMethods = {
|
||||
gzip: zlib_1.createGzip,
|
||||
deflate: zlib_1.createDeflate,
|
||||
};
|
||||
class Polling extends transport_1.Transport {
|
||||
/**
|
||||
* HTTP polling constructor.
|
||||
*/
|
||||
constructor(req) {
|
||||
super(req);
|
||||
this.closeTimeout = 30 * 1000;
|
||||
}
|
||||
/**
|
||||
* Transport name
|
||||
*/
|
||||
get name() {
|
||||
return "polling";
|
||||
}
|
||||
/**
|
||||
* Overrides onRequest.
|
||||
*
|
||||
* @param {EngineRequest} req
|
||||
* @package
|
||||
*/
|
||||
onRequest(req) {
|
||||
const res = req.res;
|
||||
// remove the reference to the ServerResponse object (as the first request of the session is kept in memory by default)
|
||||
req.res = null;
|
||||
if ("GET" === req.method) {
|
||||
this.onPollRequest(req, res);
|
||||
}
|
||||
else if ("POST" === req.method) {
|
||||
this.onDataRequest(req, res);
|
||||
}
|
||||
else {
|
||||
res.writeHead(500);
|
||||
res.end();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The client sends a request awaiting for us to send data.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
onPollRequest(req, res) {
|
||||
if (this.req) {
|
||||
debug("request overlap");
|
||||
// assert: this.res, '.req and .res should be (un)set together'
|
||||
this.onError("overlap from client");
|
||||
res.writeHead(400);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
debug("setting request");
|
||||
this.req = req;
|
||||
this.res = res;
|
||||
const onClose = () => {
|
||||
this.onError("poll connection closed prematurely");
|
||||
};
|
||||
const cleanup = () => {
|
||||
req.removeListener("close", onClose);
|
||||
this.req = this.res = null;
|
||||
};
|
||||
req.cleanup = cleanup;
|
||||
req.on("close", onClose);
|
||||
this.writable = true;
|
||||
this.emit("ready");
|
||||
// if we're still writable but had a pending close, trigger an empty send
|
||||
if (this.writable && this.shouldClose) {
|
||||
debug("triggering empty send to append close packet");
|
||||
this.send([{ type: "noop" }]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The client sends a request with data.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
onDataRequest(req, res) {
|
||||
if (this.dataReq) {
|
||||
// assert: this.dataRes, '.dataReq and .dataRes should be (un)set together'
|
||||
this.onError("data request overlap from client");
|
||||
res.writeHead(400);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
const isBinary = "application/octet-stream" === req.headers["content-type"];
|
||||
if (isBinary && this.protocol === 4) {
|
||||
return this.onError("invalid content");
|
||||
}
|
||||
this.dataReq = req;
|
||||
this.dataRes = res;
|
||||
let chunks = isBinary ? Buffer.concat([]) : "";
|
||||
const cleanup = () => {
|
||||
req.removeListener("data", onData);
|
||||
req.removeListener("end", onEnd);
|
||||
req.removeListener("close", onClose);
|
||||
this.dataReq = this.dataRes = chunks = null;
|
||||
};
|
||||
const onClose = () => {
|
||||
cleanup();
|
||||
this.onError("data request connection closed prematurely");
|
||||
};
|
||||
const onData = (data) => {
|
||||
let contentLength;
|
||||
if (isBinary) {
|
||||
chunks = Buffer.concat([chunks, data]);
|
||||
contentLength = chunks.length;
|
||||
}
|
||||
else {
|
||||
chunks += data;
|
||||
contentLength = Buffer.byteLength(chunks);
|
||||
}
|
||||
if (contentLength > this.maxHttpBufferSize) {
|
||||
res.writeHead(413).end();
|
||||
cleanup();
|
||||
}
|
||||
};
|
||||
const onEnd = () => {
|
||||
this.onData(chunks);
|
||||
const headers = {
|
||||
// text/html is required instead of text/plain to avoid an
|
||||
// unwanted download dialog on certain user-agents (GH-43)
|
||||
"Content-Type": "text/html",
|
||||
"Content-Length": "2",
|
||||
};
|
||||
res.writeHead(200, this.headers(req, headers));
|
||||
res.end("ok");
|
||||
cleanup();
|
||||
};
|
||||
req.on("close", onClose);
|
||||
if (!isBinary)
|
||||
req.setEncoding("utf8");
|
||||
req.on("data", onData);
|
||||
req.on("end", onEnd);
|
||||
}
|
||||
/**
|
||||
* Processes the incoming data payload.
|
||||
*
|
||||
* @param data - encoded payload
|
||||
* @protected
|
||||
*/
|
||||
onData(data) {
|
||||
debug('received "%s"', data);
|
||||
const callback = (packet) => {
|
||||
if ("close" === packet.type) {
|
||||
debug("got xhr close packet");
|
||||
this.onClose();
|
||||
return false;
|
||||
}
|
||||
this.onPacket(packet);
|
||||
};
|
||||
if (this.protocol === 3) {
|
||||
this.parser.decodePayload(data, callback);
|
||||
}
|
||||
else {
|
||||
this.parser.decodePayload(data).forEach(callback);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Overrides onClose.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
onClose() {
|
||||
if (this.writable) {
|
||||
// close pending poll request
|
||||
this.send([{ type: "noop" }]);
|
||||
}
|
||||
super.onClose();
|
||||
}
|
||||
send(packets) {
|
||||
this.writable = false;
|
||||
if (this.shouldClose) {
|
||||
debug("appending close packet to payload");
|
||||
packets.push({ type: "close" });
|
||||
this.shouldClose();
|
||||
this.shouldClose = null;
|
||||
}
|
||||
const doWrite = (data) => {
|
||||
const compress = packets.some((packet) => {
|
||||
return packet.options && packet.options.compress;
|
||||
});
|
||||
this.write(data, { compress });
|
||||
};
|
||||
if (this.protocol === 3) {
|
||||
this.parser.encodePayload(packets, this.supportsBinary, doWrite);
|
||||
}
|
||||
else {
|
||||
this.parser.encodePayload(packets, doWrite);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Writes data as response to poll request.
|
||||
*
|
||||
* @param {String} data
|
||||
* @param {Object} options
|
||||
* @private
|
||||
*/
|
||||
write(data, options) {
|
||||
debug('writing "%s"', data);
|
||||
this.doWrite(data, options, () => {
|
||||
this.req.cleanup();
|
||||
this.emit("drain");
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Performs the write.
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
doWrite(data, options, callback) {
|
||||
// explicit UTF-8 is required for pages not served under utf
|
||||
const isString = typeof data === "string";
|
||||
const contentType = isString
|
||||
? "text/plain; charset=UTF-8"
|
||||
: "application/octet-stream";
|
||||
const headers = {
|
||||
"Content-Type": contentType,
|
||||
};
|
||||
const respond = (data) => {
|
||||
headers["Content-Length"] =
|
||||
"string" === typeof data ? Buffer.byteLength(data) : data.length;
|
||||
this.res.writeHead(200, this.headers(this.req, headers));
|
||||
this.res.end(data);
|
||||
callback();
|
||||
};
|
||||
if (!this.httpCompression || !options.compress) {
|
||||
respond(data);
|
||||
return;
|
||||
}
|
||||
const len = isString ? Buffer.byteLength(data) : data.length;
|
||||
if (len < this.httpCompression.threshold) {
|
||||
respond(data);
|
||||
return;
|
||||
}
|
||||
const encoding = accepts(this.req).encodings(["gzip", "deflate"]);
|
||||
if (!encoding) {
|
||||
respond(data);
|
||||
return;
|
||||
}
|
||||
this.compress(data, encoding, (err, data) => {
|
||||
if (err) {
|
||||
this.res.writeHead(500);
|
||||
this.res.end();
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
headers["Content-Encoding"] = encoding;
|
||||
respond(data);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Compresses data.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
compress(data, encoding, callback) {
|
||||
debug("compressing");
|
||||
const buffers = [];
|
||||
let nread = 0;
|
||||
compressionMethods[encoding](this.httpCompression)
|
||||
.on("error", callback)
|
||||
.on("data", function (chunk) {
|
||||
buffers.push(chunk);
|
||||
nread += chunk.length;
|
||||
})
|
||||
.on("end", function () {
|
||||
callback(null, Buffer.concat(buffers, nread));
|
||||
})
|
||||
.end(data);
|
||||
}
|
||||
/**
|
||||
* Closes the transport.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
doClose(fn) {
|
||||
debug("closing");
|
||||
let closeTimeoutTimer;
|
||||
if (this.dataReq) {
|
||||
debug("aborting ongoing data request");
|
||||
this.dataReq.destroy();
|
||||
}
|
||||
const onClose = () => {
|
||||
clearTimeout(closeTimeoutTimer);
|
||||
fn();
|
||||
this.onClose();
|
||||
};
|
||||
if (this.writable) {
|
||||
debug("transport writable - closing right away");
|
||||
this.send([{ type: "close" }]);
|
||||
onClose();
|
||||
}
|
||||
else if (this.discarded) {
|
||||
debug("transport discarded - closing right away");
|
||||
onClose();
|
||||
}
|
||||
else {
|
||||
debug("transport not writable - buffering orderly close");
|
||||
this.shouldClose = onClose;
|
||||
closeTimeoutTimer = setTimeout(onClose, this.closeTimeout);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns headers for a response.
|
||||
*
|
||||
* @param {http.IncomingMessage} req
|
||||
* @param {Object} headers - extra headers
|
||||
* @private
|
||||
*/
|
||||
headers(req, headers = {}) {
|
||||
// prevent XSS warnings on IE
|
||||
// https://github.com/LearnBoost/socket.io/pull/1333
|
||||
const ua = req.headers["user-agent"];
|
||||
if (ua && (~ua.indexOf(";MSIE") || ~ua.indexOf("Trident/"))) {
|
||||
headers["X-XSS-Protection"] = "0";
|
||||
}
|
||||
headers["cache-control"] = "no-store";
|
||||
this.emit("headers", headers, req);
|
||||
return headers;
|
||||
}
|
||||
}
|
||||
exports.Polling = Polling;
|
||||
32
node_modules/engine.io/build/transports/websocket.d.ts
generated
vendored
Normal file
32
node_modules/engine.io/build/transports/websocket.d.ts
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
import { EngineRequest, Transport } from "../transport";
|
||||
import type { Packet } from "engine.io-parser";
|
||||
export declare class WebSocket extends Transport {
|
||||
protected perMessageDeflate: any;
|
||||
private socket;
|
||||
/**
|
||||
* WebSocket transport
|
||||
*
|
||||
* @param {EngineRequest} req
|
||||
*/
|
||||
constructor(req: EngineRequest);
|
||||
/**
|
||||
* Transport name
|
||||
*/
|
||||
get name(): string;
|
||||
/**
|
||||
* Advertise upgrade support.
|
||||
*/
|
||||
get handlesUpgrades(): boolean;
|
||||
send(packets: Packet[]): void;
|
||||
/**
|
||||
* Whether the encoding of the WebSocket frame can be skipped.
|
||||
* @param packet
|
||||
* @private
|
||||
*/
|
||||
private _canSendPreEncodedFrame;
|
||||
private _doSend;
|
||||
private _doSendLast;
|
||||
private _onSent;
|
||||
private _onSentLast;
|
||||
doClose(fn?: () => void): void;
|
||||
}
|
||||
94
node_modules/engine.io/build/transports/websocket.js
generated
vendored
Normal file
94
node_modules/engine.io/build/transports/websocket.js
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.WebSocket = void 0;
|
||||
const transport_1 = require("../transport");
|
||||
const debug_1 = require("debug");
|
||||
const debug = (0, debug_1.default)("engine:ws");
|
||||
class WebSocket extends transport_1.Transport {
|
||||
/**
|
||||
* WebSocket transport
|
||||
*
|
||||
* @param {EngineRequest} req
|
||||
*/
|
||||
constructor(req) {
|
||||
super(req);
|
||||
this._doSend = (data) => {
|
||||
this.socket.send(data, this._onSent);
|
||||
};
|
||||
this._doSendLast = (data) => {
|
||||
this.socket.send(data, this._onSentLast);
|
||||
};
|
||||
this._onSent = (err) => {
|
||||
if (err) {
|
||||
this.onError("write error", err.stack);
|
||||
}
|
||||
};
|
||||
this._onSentLast = (err) => {
|
||||
if (err) {
|
||||
this.onError("write error", err.stack);
|
||||
}
|
||||
else {
|
||||
this.emit("drain");
|
||||
this.writable = true;
|
||||
this.emit("ready");
|
||||
}
|
||||
};
|
||||
this.socket = req.websocket;
|
||||
this.socket.on("message", (data, isBinary) => {
|
||||
const message = isBinary ? data : data.toString();
|
||||
debug('received "%s"', message);
|
||||
super.onData(message);
|
||||
});
|
||||
this.socket.once("close", this.onClose.bind(this));
|
||||
this.socket.on("error", this.onError.bind(this));
|
||||
this.writable = true;
|
||||
this.perMessageDeflate = null;
|
||||
}
|
||||
/**
|
||||
* Transport name
|
||||
*/
|
||||
get name() {
|
||||
return "websocket";
|
||||
}
|
||||
/**
|
||||
* Advertise upgrade support.
|
||||
*/
|
||||
get handlesUpgrades() {
|
||||
return true;
|
||||
}
|
||||
send(packets) {
|
||||
this.writable = false;
|
||||
for (let i = 0; i < packets.length; i++) {
|
||||
const packet = packets[i];
|
||||
const isLast = i + 1 === packets.length;
|
||||
if (this._canSendPreEncodedFrame(packet)) {
|
||||
// the WebSocket frame was computed with WebSocket.Sender.frame()
|
||||
// see https://github.com/websockets/ws/issues/617#issuecomment-283002469
|
||||
this.socket._sender.sendFrame(
|
||||
// @ts-ignore
|
||||
packet.options.wsPreEncodedFrame, isLast ? this._onSentLast : this._onSent);
|
||||
}
|
||||
else {
|
||||
this.parser.encodePacket(packet, this.supportsBinary, isLast ? this._doSendLast : this._doSend);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Whether the encoding of the WebSocket frame can be skipped.
|
||||
* @param packet
|
||||
* @private
|
||||
*/
|
||||
_canSendPreEncodedFrame(packet) {
|
||||
var _a, _b, _c;
|
||||
return (!this.perMessageDeflate &&
|
||||
typeof ((_b = (_a = this.socket) === null || _a === void 0 ? void 0 : _a._sender) === null || _b === void 0 ? void 0 : _b.sendFrame) === "function" &&
|
||||
// @ts-ignore
|
||||
((_c = packet.options) === null || _c === void 0 ? void 0 : _c.wsPreEncodedFrame) !== undefined);
|
||||
}
|
||||
doClose(fn) {
|
||||
debug("closing");
|
||||
this.socket.close();
|
||||
fn && fn();
|
||||
}
|
||||
}
|
||||
exports.WebSocket = WebSocket;
|
||||
12
node_modules/engine.io/build/transports/webtransport.d.ts
generated
vendored
Normal file
12
node_modules/engine.io/build/transports/webtransport.d.ts
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Transport } from "../transport";
|
||||
/**
|
||||
* Reference: https://developer.mozilla.org/en-US/docs/Web/API/WebTransport_API
|
||||
*/
|
||||
export declare class WebTransport extends Transport {
|
||||
private readonly session;
|
||||
private readonly writer;
|
||||
constructor(session: any, stream: any, reader: any);
|
||||
get name(): string;
|
||||
send(packets: any): Promise<void>;
|
||||
doClose(fn: any): void;
|
||||
}
|
||||
63
node_modules/engine.io/build/transports/webtransport.js
generated
vendored
Normal file
63
node_modules/engine.io/build/transports/webtransport.js
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.WebTransport = void 0;
|
||||
const transport_1 = require("../transport");
|
||||
const debug_1 = require("debug");
|
||||
const engine_io_parser_1 = require("engine.io-parser");
|
||||
const debug = (0, debug_1.default)("engine:webtransport");
|
||||
/**
|
||||
* Reference: https://developer.mozilla.org/en-US/docs/Web/API/WebTransport_API
|
||||
*/
|
||||
class WebTransport extends transport_1.Transport {
|
||||
constructor(session, stream, reader) {
|
||||
super({ _query: { EIO: "4" } });
|
||||
this.session = session;
|
||||
const transformStream = (0, engine_io_parser_1.createPacketEncoderStream)();
|
||||
transformStream.readable.pipeTo(stream.writable).catch(() => {
|
||||
debug("the stream was closed");
|
||||
});
|
||||
this.writer = transformStream.writable.getWriter();
|
||||
(async () => {
|
||||
try {
|
||||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
if (done) {
|
||||
debug("session is closed");
|
||||
break;
|
||||
}
|
||||
debug("received chunk: %o", value);
|
||||
this.onPacket(value);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
debug("error while reading: %s", e.message);
|
||||
}
|
||||
})();
|
||||
session.closed.then(() => this.onClose());
|
||||
this.writable = true;
|
||||
}
|
||||
get name() {
|
||||
return "webtransport";
|
||||
}
|
||||
async send(packets) {
|
||||
this.writable = false;
|
||||
try {
|
||||
for (let i = 0; i < packets.length; i++) {
|
||||
const packet = packets[i];
|
||||
await this.writer.write(packet);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
debug("error while writing: %s", e.message);
|
||||
}
|
||||
this.emit("drain");
|
||||
this.writable = true;
|
||||
this.emit("ready");
|
||||
}
|
||||
doClose(fn) {
|
||||
debug("closing WebTransport session");
|
||||
this.session.close();
|
||||
fn && fn();
|
||||
}
|
||||
}
|
||||
exports.WebTransport = WebTransport;
|
||||
42
node_modules/engine.io/build/userver.d.ts
generated
vendored
Normal file
42
node_modules/engine.io/build/userver.d.ts
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
import { AttachOptions, BaseServer } from "./server";
|
||||
export interface uOptions {
|
||||
/**
|
||||
* What permessage-deflate compression to use. uWS.DISABLED, uWS.SHARED_COMPRESSOR or any of the uWS.DEDICATED_COMPRESSOR_xxxKB.
|
||||
* @default uWS.DISABLED
|
||||
*/
|
||||
compression?: number;
|
||||
/**
|
||||
* Maximum amount of seconds that may pass without sending or getting a message. Connection is closed if this timeout passes. Resolution (granularity) for timeouts are typically 4 seconds, rounded to closest. Disable by using 0.
|
||||
* @default 120
|
||||
*/
|
||||
idleTimeout?: number;
|
||||
/**
|
||||
* Maximum length of allowed backpressure per socket when publishing or sending messages. Slow receivers with too high backpressure will be skipped until they catch up or timeout.
|
||||
* @default 1024 * 1024
|
||||
*/
|
||||
maxBackpressure?: number;
|
||||
}
|
||||
/**
|
||||
* An Engine.IO server based on the `uWebSockets.js` package.
|
||||
*/
|
||||
export declare class uServer extends BaseServer {
|
||||
protected init(): void;
|
||||
protected cleanup(): void;
|
||||
/**
|
||||
* Prepares a request by processing the query string.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private prepare;
|
||||
protected createTransport(transportName: any, req: any): any;
|
||||
/**
|
||||
* Attach the engine to a µWebSockets.js server
|
||||
* @param app
|
||||
* @param options
|
||||
*/
|
||||
attach(app: any, options?: AttachOptions & uOptions): void;
|
||||
_applyMiddlewares(req: any, res: any, callback: (err?: any) => void): void;
|
||||
private handleRequest;
|
||||
private handleUpgrade;
|
||||
private abortRequest;
|
||||
}
|
||||
279
node_modules/engine.io/build/userver.js
generated
vendored
Normal file
279
node_modules/engine.io/build/userver.js
generated
vendored
Normal file
@@ -0,0 +1,279 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.uServer = void 0;
|
||||
const debug_1 = require("debug");
|
||||
const server_1 = require("./server");
|
||||
const transports_uws_1 = require("./transports-uws");
|
||||
const debug = (0, debug_1.default)("engine:uws");
|
||||
/**
|
||||
* An Engine.IO server based on the `uWebSockets.js` package.
|
||||
*/
|
||||
// TODO export it into its own package
|
||||
class uServer extends server_1.BaseServer {
|
||||
init() { }
|
||||
cleanup() { }
|
||||
/**
|
||||
* Prepares a request by processing the query string.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
prepare(req, res) {
|
||||
req.method = req.getMethod().toUpperCase();
|
||||
req.url = req.getUrl();
|
||||
const params = new URLSearchParams(req.getQuery());
|
||||
req._query = Object.fromEntries(params.entries());
|
||||
req.headers = {};
|
||||
req.forEach((key, value) => {
|
||||
req.headers[key] = value;
|
||||
});
|
||||
req.connection = {
|
||||
remoteAddress: Buffer.from(res.getRemoteAddressAsText()).toString(),
|
||||
};
|
||||
res.onAborted(() => {
|
||||
debug("response has been aborted");
|
||||
});
|
||||
}
|
||||
createTransport(transportName, req) {
|
||||
return new transports_uws_1.default[transportName](req);
|
||||
}
|
||||
/**
|
||||
* Attach the engine to a µWebSockets.js server
|
||||
* @param app
|
||||
* @param options
|
||||
*/
|
||||
attach(app /* : TemplatedApp */, options = {}) {
|
||||
const path = this._computePath(options);
|
||||
app
|
||||
.any(path, this.handleRequest.bind(this))
|
||||
//
|
||||
.ws(path, {
|
||||
compression: options.compression,
|
||||
idleTimeout: options.idleTimeout,
|
||||
maxBackpressure: options.maxBackpressure,
|
||||
maxPayloadLength: this.opts.maxHttpBufferSize,
|
||||
upgrade: this.handleUpgrade.bind(this),
|
||||
open: (ws) => {
|
||||
const transport = ws.getUserData().transport;
|
||||
transport.socket = ws;
|
||||
transport.writable = true;
|
||||
transport.emit("ready");
|
||||
},
|
||||
message: (ws, message, isBinary) => {
|
||||
ws.getUserData().transport.onData(isBinary ? message : Buffer.from(message).toString());
|
||||
},
|
||||
close: (ws, code, message) => {
|
||||
ws.getUserData().transport.onClose(code, message);
|
||||
},
|
||||
});
|
||||
}
|
||||
_applyMiddlewares(req, res, callback) {
|
||||
if (this.middlewares.length === 0) {
|
||||
return callback();
|
||||
}
|
||||
// needed to buffer headers until the status is computed
|
||||
req.res = new ResponseWrapper(res);
|
||||
super._applyMiddlewares(req, req.res, (err) => {
|
||||
// some middlewares (like express-session) wait for the writeHead() call to flush their headers
|
||||
// see https://github.com/expressjs/session/blob/1010fadc2f071ddf2add94235d72224cf65159c6/index.js#L220-L244
|
||||
req.res.writeHead();
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
handleRequest(res, req) {
|
||||
debug('handling "%s" http request "%s"', req.getMethod(), req.getUrl());
|
||||
this.prepare(req, res);
|
||||
req.res = res;
|
||||
const callback = (errorCode, errorContext) => {
|
||||
if (errorCode !== undefined) {
|
||||
this.emit("connection_error", {
|
||||
req,
|
||||
code: errorCode,
|
||||
message: server_1.Server.errorMessages[errorCode],
|
||||
context: errorContext,
|
||||
});
|
||||
this.abortRequest(req.res, errorCode, errorContext);
|
||||
return;
|
||||
}
|
||||
if (req._query.sid) {
|
||||
debug("setting new request for existing client");
|
||||
// @ts-ignore
|
||||
this.clients[req._query.sid].transport.onRequest(req);
|
||||
}
|
||||
else {
|
||||
const closeConnection = (errorCode, errorContext) => this.abortRequest(res, errorCode, errorContext);
|
||||
this.handshake(req._query.transport, req, closeConnection);
|
||||
}
|
||||
};
|
||||
this._applyMiddlewares(req, res, (err) => {
|
||||
if (err) {
|
||||
callback(server_1.Server.errors.BAD_REQUEST, { name: "MIDDLEWARE_FAILURE" });
|
||||
}
|
||||
else {
|
||||
this.verify(req, false, callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
handleUpgrade(res, req, context) {
|
||||
debug("on upgrade");
|
||||
this.prepare(req, res);
|
||||
req.res = res;
|
||||
const callback = async (errorCode, errorContext) => {
|
||||
if (errorCode !== undefined) {
|
||||
this.emit("connection_error", {
|
||||
req,
|
||||
code: errorCode,
|
||||
message: server_1.Server.errorMessages[errorCode],
|
||||
context: errorContext,
|
||||
});
|
||||
this.abortRequest(res, errorCode, errorContext);
|
||||
return;
|
||||
}
|
||||
const id = req._query.sid;
|
||||
let transport;
|
||||
if (id) {
|
||||
const client = this.clients[id];
|
||||
if (!client) {
|
||||
debug("upgrade attempt for closed client");
|
||||
return res.close();
|
||||
}
|
||||
else if (client.upgrading) {
|
||||
debug("transport has already been trying to upgrade");
|
||||
return res.close();
|
||||
}
|
||||
else if (client.upgraded) {
|
||||
debug("transport had already been upgraded");
|
||||
return res.close();
|
||||
}
|
||||
else {
|
||||
debug("upgrading existing transport");
|
||||
transport = this.createTransport(req._query.transport, req);
|
||||
client._maybeUpgrade(transport);
|
||||
}
|
||||
}
|
||||
else {
|
||||
transport = await this.handshake(req._query.transport, req, (errorCode, errorContext) => this.abortRequest(res, errorCode, errorContext));
|
||||
if (!transport) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// calling writeStatus() triggers the flushing of any header added in a middleware
|
||||
req.res.writeStatus("101 Switching Protocols");
|
||||
res.upgrade({
|
||||
transport,
|
||||
}, req.getHeader("sec-websocket-key"), req.getHeader("sec-websocket-protocol"), req.getHeader("sec-websocket-extensions"), context);
|
||||
};
|
||||
this._applyMiddlewares(req, res, (err) => {
|
||||
if (err) {
|
||||
callback(server_1.Server.errors.BAD_REQUEST, { name: "MIDDLEWARE_FAILURE" });
|
||||
}
|
||||
else {
|
||||
this.verify(req, true, callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
abortRequest(res, errorCode, errorContext) {
|
||||
const statusCode = errorCode === server_1.Server.errors.FORBIDDEN
|
||||
? "403 Forbidden"
|
||||
: "400 Bad Request";
|
||||
const message = errorContext && errorContext.message
|
||||
? errorContext.message
|
||||
: server_1.Server.errorMessages[errorCode];
|
||||
res.writeStatus(statusCode);
|
||||
res.writeHeader("Content-Type", "application/json");
|
||||
res.end(JSON.stringify({
|
||||
code: errorCode,
|
||||
message,
|
||||
}));
|
||||
}
|
||||
}
|
||||
exports.uServer = uServer;
|
||||
class ResponseWrapper {
|
||||
constructor(res) {
|
||||
this.res = res;
|
||||
this.statusWritten = false;
|
||||
this.headers = [];
|
||||
this.isAborted = false;
|
||||
}
|
||||
set statusCode(status) {
|
||||
if (!status) {
|
||||
return;
|
||||
}
|
||||
// FIXME: handle all status codes?
|
||||
this.writeStatus(status === 200 ? "200 OK" : "204 No Content");
|
||||
}
|
||||
writeHead(status) {
|
||||
this.statusCode = status;
|
||||
}
|
||||
setHeader(key, value) {
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((val) => {
|
||||
this.writeHeader(key, val);
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.writeHeader(key, value);
|
||||
}
|
||||
}
|
||||
removeHeader() {
|
||||
// FIXME: not implemented
|
||||
}
|
||||
// needed by vary: https://github.com/jshttp/vary/blob/5d725d059b3871025cf753e9dfa08924d0bcfa8f/index.js#L134
|
||||
getHeader() { }
|
||||
writeStatus(status) {
|
||||
if (this.isAborted)
|
||||
return;
|
||||
this.res.writeStatus(status);
|
||||
this.statusWritten = true;
|
||||
this.writeBufferedHeaders();
|
||||
return this;
|
||||
}
|
||||
writeHeader(key, value) {
|
||||
if (this.isAborted)
|
||||
return;
|
||||
if (key === "Content-Length") {
|
||||
// the content length is automatically added by uWebSockets.js
|
||||
return;
|
||||
}
|
||||
if (this.statusWritten) {
|
||||
this.res.writeHeader(key, value);
|
||||
}
|
||||
else {
|
||||
this.headers.push([key, value]);
|
||||
}
|
||||
}
|
||||
writeBufferedHeaders() {
|
||||
this.headers.forEach(([key, value]) => {
|
||||
this.res.writeHeader(key, value);
|
||||
});
|
||||
}
|
||||
end(data) {
|
||||
if (this.isAborted)
|
||||
return;
|
||||
this.res.cork(() => {
|
||||
if (!this.statusWritten) {
|
||||
// status will be inferred as "200 OK"
|
||||
this.writeBufferedHeaders();
|
||||
}
|
||||
this.res.end(data);
|
||||
});
|
||||
}
|
||||
onData(fn) {
|
||||
if (this.isAborted)
|
||||
return;
|
||||
this.res.onData(fn);
|
||||
}
|
||||
onAborted(fn) {
|
||||
if (this.isAborted)
|
||||
return;
|
||||
this.res.onAborted(() => {
|
||||
// Any attempt to use the UWS response object after abort will throw!
|
||||
this.isAborted = true;
|
||||
fn();
|
||||
});
|
||||
}
|
||||
cork(fn) {
|
||||
if (this.isAborted)
|
||||
return;
|
||||
this.res.cork(fn);
|
||||
}
|
||||
}
|
||||
20
node_modules/engine.io/node_modules/debug/LICENSE
generated
vendored
Normal file
20
node_modules/engine.io/node_modules/debug/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2014-2017 TJ Holowaychuk <tj@vision-media.ca>
|
||||
Copyright (c) 2018-2021 Josh Junon
|
||||
|
||||
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.
|
||||
|
||||
481
node_modules/engine.io/node_modules/debug/README.md
generated
vendored
Normal file
481
node_modules/engine.io/node_modules/debug/README.md
generated
vendored
Normal file
@@ -0,0 +1,481 @@
|
||||
# debug
|
||||
[](#backers)
|
||||
[](#sponsors)
|
||||
|
||||
<img width="647" src="https://user-images.githubusercontent.com/71256/29091486-fa38524c-7c37-11e7-895f-e7ec8e1039b6.png">
|
||||
|
||||
A tiny JavaScript debugging utility modelled after Node.js core's debugging
|
||||
technique. Works in Node.js and web browsers.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
$ npm install debug
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
`debug` exposes a function; simply pass this function the name of your module, and it will return a decorated version of `console.error` for you to pass debug statements to. This will allow you to toggle the debug output for different parts of your module as well as the module as a whole.
|
||||
|
||||
Example [_app.js_](./examples/node/app.js):
|
||||
|
||||
```js
|
||||
var debug = require('debug')('http')
|
||||
, http = require('http')
|
||||
, name = 'My App';
|
||||
|
||||
// fake app
|
||||
|
||||
debug('booting %o', name);
|
||||
|
||||
http.createServer(function(req, res){
|
||||
debug(req.method + ' ' + req.url);
|
||||
res.end('hello\n');
|
||||
}).listen(3000, function(){
|
||||
debug('listening');
|
||||
});
|
||||
|
||||
// fake worker of some kind
|
||||
|
||||
require('./worker');
|
||||
```
|
||||
|
||||
Example [_worker.js_](./examples/node/worker.js):
|
||||
|
||||
```js
|
||||
var a = require('debug')('worker:a')
|
||||
, b = require('debug')('worker:b');
|
||||
|
||||
function work() {
|
||||
a('doing lots of uninteresting work');
|
||||
setTimeout(work, Math.random() * 1000);
|
||||
}
|
||||
|
||||
work();
|
||||
|
||||
function workb() {
|
||||
b('doing some work');
|
||||
setTimeout(workb, Math.random() * 2000);
|
||||
}
|
||||
|
||||
workb();
|
||||
```
|
||||
|
||||
The `DEBUG` environment variable is then used to enable these based on space or
|
||||
comma-delimited names.
|
||||
|
||||
Here are some examples:
|
||||
|
||||
<img width="647" alt="screen shot 2017-08-08 at 12 53 04 pm" src="https://user-images.githubusercontent.com/71256/29091703-a6302cdc-7c38-11e7-8304-7c0b3bc600cd.png">
|
||||
<img width="647" alt="screen shot 2017-08-08 at 12 53 38 pm" src="https://user-images.githubusercontent.com/71256/29091700-a62a6888-7c38-11e7-800b-db911291ca2b.png">
|
||||
<img width="647" alt="screen shot 2017-08-08 at 12 53 25 pm" src="https://user-images.githubusercontent.com/71256/29091701-a62ea114-7c38-11e7-826a-2692bedca740.png">
|
||||
|
||||
#### Windows command prompt notes
|
||||
|
||||
##### CMD
|
||||
|
||||
On Windows the environment variable is set using the `set` command.
|
||||
|
||||
```cmd
|
||||
set DEBUG=*,-not_this
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```cmd
|
||||
set DEBUG=* & node app.js
|
||||
```
|
||||
|
||||
##### PowerShell (VS Code default)
|
||||
|
||||
PowerShell uses different syntax to set environment variables.
|
||||
|
||||
```cmd
|
||||
$env:DEBUG = "*,-not_this"
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```cmd
|
||||
$env:DEBUG='app';node app.js
|
||||
```
|
||||
|
||||
Then, run the program to be debugged as usual.
|
||||
|
||||
npm script example:
|
||||
```js
|
||||
"windowsDebug": "@powershell -Command $env:DEBUG='*';node app.js",
|
||||
```
|
||||
|
||||
## Namespace Colors
|
||||
|
||||
Every debug instance has a color generated for it based on its namespace name.
|
||||
This helps when visually parsing the debug output to identify which debug instance
|
||||
a debug line belongs to.
|
||||
|
||||
#### Node.js
|
||||
|
||||
In Node.js, colors are enabled when stderr is a TTY. You also _should_ install
|
||||
the [`supports-color`](https://npmjs.org/supports-color) module alongside debug,
|
||||
otherwise debug will only use a small handful of basic colors.
|
||||
|
||||
<img width="521" src="https://user-images.githubusercontent.com/71256/29092181-47f6a9e6-7c3a-11e7-9a14-1928d8a711cd.png">
|
||||
|
||||
#### Web Browser
|
||||
|
||||
Colors are also enabled on "Web Inspectors" that understand the `%c` formatting
|
||||
option. These are WebKit web inspectors, Firefox ([since version
|
||||
31](https://hacks.mozilla.org/2014/05/editable-box-model-multiple-selection-sublime-text-keys-much-more-firefox-developer-tools-episode-31/))
|
||||
and the Firebug plugin for Firefox (any version).
|
||||
|
||||
<img width="524" src="https://user-images.githubusercontent.com/71256/29092033-b65f9f2e-7c39-11e7-8e32-f6f0d8e865c1.png">
|
||||
|
||||
|
||||
## Millisecond diff
|
||||
|
||||
When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls.
|
||||
|
||||
<img width="647" src="https://user-images.githubusercontent.com/71256/29091486-fa38524c-7c37-11e7-895f-e7ec8e1039b6.png">
|
||||
|
||||
When stdout is not a TTY, `Date#toISOString()` is used, making it more useful for logging the debug information as shown below:
|
||||
|
||||
<img width="647" src="https://user-images.githubusercontent.com/71256/29091956-6bd78372-7c39-11e7-8c55-c948396d6edd.png">
|
||||
|
||||
|
||||
## Conventions
|
||||
|
||||
If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser". If you append a "*" to the end of your name, it will always be enabled regardless of the setting of the DEBUG environment variable. You can then use it for normal output as well as debug output.
|
||||
|
||||
## Wildcards
|
||||
|
||||
The `*` character may be used as a wildcard. Suppose for example your library has
|
||||
debuggers named "connect:bodyParser", "connect:compress", "connect:session",
|
||||
instead of listing all three with
|
||||
`DEBUG=connect:bodyParser,connect:compress,connect:session`, you may simply do
|
||||
`DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`.
|
||||
|
||||
You can also exclude specific debuggers by prefixing them with a "-" character.
|
||||
For example, `DEBUG=*,-connect:*` would include all debuggers except those
|
||||
starting with "connect:".
|
||||
|
||||
## Environment Variables
|
||||
|
||||
When running through Node.js, you can set a few environment variables that will
|
||||
change the behavior of the debug logging:
|
||||
|
||||
| Name | Purpose |
|
||||
|-----------|-------------------------------------------------|
|
||||
| `DEBUG` | Enables/disables specific debugging namespaces. |
|
||||
| `DEBUG_HIDE_DATE` | Hide date from debug output (non-TTY). |
|
||||
| `DEBUG_COLORS`| Whether or not to use colors in the debug output. |
|
||||
| `DEBUG_DEPTH` | Object inspection depth. |
|
||||
| `DEBUG_SHOW_HIDDEN` | Shows hidden properties on inspected objects. |
|
||||
|
||||
|
||||
__Note:__ The environment variables beginning with `DEBUG_` end up being
|
||||
converted into an Options object that gets used with `%o`/`%O` formatters.
|
||||
See the Node.js documentation for
|
||||
[`util.inspect()`](https://nodejs.org/api/util.html#util_util_inspect_object_options)
|
||||
for the complete list.
|
||||
|
||||
## Formatters
|
||||
|
||||
Debug uses [printf-style](https://wikipedia.org/wiki/Printf_format_string) formatting.
|
||||
Below are the officially supported formatters:
|
||||
|
||||
| Formatter | Representation |
|
||||
|-----------|----------------|
|
||||
| `%O` | Pretty-print an Object on multiple lines. |
|
||||
| `%o` | Pretty-print an Object all on a single line. |
|
||||
| `%s` | String. |
|
||||
| `%d` | Number (both integer and float). |
|
||||
| `%j` | JSON. Replaced with the string '[Circular]' if the argument contains circular references. |
|
||||
| `%%` | Single percent sign ('%'). This does not consume an argument. |
|
||||
|
||||
|
||||
### Custom formatters
|
||||
|
||||
You can add custom formatters by extending the `debug.formatters` object.
|
||||
For example, if you wanted to add support for rendering a Buffer as hex with
|
||||
`%h`, you could do something like:
|
||||
|
||||
```js
|
||||
const createDebug = require('debug')
|
||||
createDebug.formatters.h = (v) => {
|
||||
return v.toString('hex')
|
||||
}
|
||||
|
||||
// …elsewhere
|
||||
const debug = createDebug('foo')
|
||||
debug('this is hex: %h', new Buffer('hello world'))
|
||||
// foo this is hex: 68656c6c6f20776f726c6421 +0ms
|
||||
```
|
||||
|
||||
|
||||
## Browser Support
|
||||
|
||||
You can build a browser-ready script using [browserify](https://github.com/substack/node-browserify),
|
||||
or just use the [browserify-as-a-service](https://wzrd.in/) [build](https://wzrd.in/standalone/debug@latest),
|
||||
if you don't want to build it yourself.
|
||||
|
||||
Debug's enable state is currently persisted by `localStorage`.
|
||||
Consider the situation shown below where you have `worker:a` and `worker:b`,
|
||||
and wish to debug both. You can enable this using `localStorage.debug`:
|
||||
|
||||
```js
|
||||
localStorage.debug = 'worker:*'
|
||||
```
|
||||
|
||||
And then refresh the page.
|
||||
|
||||
```js
|
||||
a = debug('worker:a');
|
||||
b = debug('worker:b');
|
||||
|
||||
setInterval(function(){
|
||||
a('doing some work');
|
||||
}, 1000);
|
||||
|
||||
setInterval(function(){
|
||||
b('doing some work');
|
||||
}, 1200);
|
||||
```
|
||||
|
||||
In Chromium-based web browsers (e.g. Brave, Chrome, and Electron), the JavaScript console will—by default—only show messages logged by `debug` if the "Verbose" log level is _enabled_.
|
||||
|
||||
<img width="647" src="https://user-images.githubusercontent.com/7143133/152083257-29034707-c42c-4959-8add-3cee850e6fcf.png">
|
||||
|
||||
## Output streams
|
||||
|
||||
By default `debug` will log to stderr, however this can be configured per-namespace by overriding the `log` method:
|
||||
|
||||
Example [_stdout.js_](./examples/node/stdout.js):
|
||||
|
||||
```js
|
||||
var debug = require('debug');
|
||||
var error = debug('app:error');
|
||||
|
||||
// by default stderr is used
|
||||
error('goes to stderr!');
|
||||
|
||||
var log = debug('app:log');
|
||||
// set this namespace to log via console.log
|
||||
log.log = console.log.bind(console); // don't forget to bind to console!
|
||||
log('goes to stdout');
|
||||
error('still goes to stderr!');
|
||||
|
||||
// set all output to go via console.info
|
||||
// overrides all per-namespace log settings
|
||||
debug.log = console.info.bind(console);
|
||||
error('now goes to stdout via console.info');
|
||||
log('still goes to stdout, but via console.info now');
|
||||
```
|
||||
|
||||
## Extend
|
||||
You can simply extend debugger
|
||||
```js
|
||||
const log = require('debug')('auth');
|
||||
|
||||
//creates new debug instance with extended namespace
|
||||
const logSign = log.extend('sign');
|
||||
const logLogin = log.extend('login');
|
||||
|
||||
log('hello'); // auth hello
|
||||
logSign('hello'); //auth:sign hello
|
||||
logLogin('hello'); //auth:login hello
|
||||
```
|
||||
|
||||
## Set dynamically
|
||||
|
||||
You can also enable debug dynamically by calling the `enable()` method :
|
||||
|
||||
```js
|
||||
let debug = require('debug');
|
||||
|
||||
console.log(1, debug.enabled('test'));
|
||||
|
||||
debug.enable('test');
|
||||
console.log(2, debug.enabled('test'));
|
||||
|
||||
debug.disable();
|
||||
console.log(3, debug.enabled('test'));
|
||||
|
||||
```
|
||||
|
||||
print :
|
||||
```
|
||||
1 false
|
||||
2 true
|
||||
3 false
|
||||
```
|
||||
|
||||
Usage :
|
||||
`enable(namespaces)`
|
||||
`namespaces` can include modes separated by a colon and wildcards.
|
||||
|
||||
Note that calling `enable()` completely overrides previously set DEBUG variable :
|
||||
|
||||
```
|
||||
$ DEBUG=foo node -e 'var dbg = require("debug"); dbg.enable("bar"); console.log(dbg.enabled("foo"))'
|
||||
=> false
|
||||
```
|
||||
|
||||
`disable()`
|
||||
|
||||
Will disable all namespaces. The functions returns the namespaces currently
|
||||
enabled (and skipped). This can be useful if you want to disable debugging
|
||||
temporarily without knowing what was enabled to begin with.
|
||||
|
||||
For example:
|
||||
|
||||
```js
|
||||
let debug = require('debug');
|
||||
debug.enable('foo:*,-foo:bar');
|
||||
let namespaces = debug.disable();
|
||||
debug.enable(namespaces);
|
||||
```
|
||||
|
||||
Note: There is no guarantee that the string will be identical to the initial
|
||||
enable string, but semantically they will be identical.
|
||||
|
||||
## Checking whether a debug target is enabled
|
||||
|
||||
After you've created a debug instance, you can determine whether or not it is
|
||||
enabled by checking the `enabled` property:
|
||||
|
||||
```javascript
|
||||
const debug = require('debug')('http');
|
||||
|
||||
if (debug.enabled) {
|
||||
// do stuff...
|
||||
}
|
||||
```
|
||||
|
||||
You can also manually toggle this property to force the debug instance to be
|
||||
enabled or disabled.
|
||||
|
||||
## Usage in child processes
|
||||
|
||||
Due to the way `debug` detects if the output is a TTY or not, colors are not shown in child processes when `stderr` is piped. A solution is to pass the `DEBUG_COLORS=1` environment variable to the child process.
|
||||
For example:
|
||||
|
||||
```javascript
|
||||
worker = fork(WORKER_WRAP_PATH, [workerPath], {
|
||||
stdio: [
|
||||
/* stdin: */ 0,
|
||||
/* stdout: */ 'pipe',
|
||||
/* stderr: */ 'pipe',
|
||||
'ipc',
|
||||
],
|
||||
env: Object.assign({}, process.env, {
|
||||
DEBUG_COLORS: 1 // without this settings, colors won't be shown
|
||||
}),
|
||||
});
|
||||
|
||||
worker.stderr.pipe(process.stderr, { end: false });
|
||||
```
|
||||
|
||||
|
||||
## Authors
|
||||
|
||||
- TJ Holowaychuk
|
||||
- Nathan Rajlich
|
||||
- Andrew Rhyne
|
||||
- Josh Junon
|
||||
|
||||
## Backers
|
||||
|
||||
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/debug#backer)]
|
||||
|
||||
<a href="https://opencollective.com/debug/backer/0/website" target="_blank"><img src="https://opencollective.com/debug/backer/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/1/website" target="_blank"><img src="https://opencollective.com/debug/backer/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/2/website" target="_blank"><img src="https://opencollective.com/debug/backer/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/3/website" target="_blank"><img src="https://opencollective.com/debug/backer/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/4/website" target="_blank"><img src="https://opencollective.com/debug/backer/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/5/website" target="_blank"><img src="https://opencollective.com/debug/backer/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/6/website" target="_blank"><img src="https://opencollective.com/debug/backer/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/7/website" target="_blank"><img src="https://opencollective.com/debug/backer/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/8/website" target="_blank"><img src="https://opencollective.com/debug/backer/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/9/website" target="_blank"><img src="https://opencollective.com/debug/backer/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/10/website" target="_blank"><img src="https://opencollective.com/debug/backer/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/11/website" target="_blank"><img src="https://opencollective.com/debug/backer/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/12/website" target="_blank"><img src="https://opencollective.com/debug/backer/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/13/website" target="_blank"><img src="https://opencollective.com/debug/backer/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/14/website" target="_blank"><img src="https://opencollective.com/debug/backer/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/15/website" target="_blank"><img src="https://opencollective.com/debug/backer/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/16/website" target="_blank"><img src="https://opencollective.com/debug/backer/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/17/website" target="_blank"><img src="https://opencollective.com/debug/backer/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/18/website" target="_blank"><img src="https://opencollective.com/debug/backer/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/19/website" target="_blank"><img src="https://opencollective.com/debug/backer/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/20/website" target="_blank"><img src="https://opencollective.com/debug/backer/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/21/website" target="_blank"><img src="https://opencollective.com/debug/backer/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/22/website" target="_blank"><img src="https://opencollective.com/debug/backer/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/23/website" target="_blank"><img src="https://opencollective.com/debug/backer/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/24/website" target="_blank"><img src="https://opencollective.com/debug/backer/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/25/website" target="_blank"><img src="https://opencollective.com/debug/backer/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/26/website" target="_blank"><img src="https://opencollective.com/debug/backer/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/27/website" target="_blank"><img src="https://opencollective.com/debug/backer/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/28/website" target="_blank"><img src="https://opencollective.com/debug/backer/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/29/website" target="_blank"><img src="https://opencollective.com/debug/backer/29/avatar.svg"></a>
|
||||
|
||||
|
||||
## Sponsors
|
||||
|
||||
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/debug#sponsor)]
|
||||
|
||||
<a href="https://opencollective.com/debug/sponsor/0/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/1/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/2/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/3/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/4/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/5/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/6/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/7/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/8/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/9/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/10/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/11/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/12/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/13/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/14/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/15/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/16/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/17/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/18/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/19/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/20/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/21/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/22/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/23/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/24/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/25/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/26/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/27/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/28/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/29/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/29/avatar.svg"></a>
|
||||
|
||||
## License
|
||||
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2014-2017 TJ Holowaychuk <tj@vision-media.ca>
|
||||
Copyright (c) 2018-2021 Josh Junon
|
||||
|
||||
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.
|
||||
60
node_modules/engine.io/node_modules/debug/package.json
generated
vendored
Normal file
60
node_modules/engine.io/node_modules/debug/package.json
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"name": "debug",
|
||||
"version": "4.3.7",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/debug-js/debug.git"
|
||||
},
|
||||
"description": "Lightweight debugging utility for Node.js and the browser",
|
||||
"keywords": [
|
||||
"debug",
|
||||
"log",
|
||||
"debugger"
|
||||
],
|
||||
"files": [
|
||||
"src",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
],
|
||||
"author": "Josh Junon (https://github.com/qix-)",
|
||||
"contributors": [
|
||||
"TJ Holowaychuk <tj@vision-media.ca>",
|
||||
"Nathan Rajlich <nathan@tootallnate.net> (http://n8.io)",
|
||||
"Andrew Rhyne <rhyneandrew@gmail.com>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"lint": "xo",
|
||||
"test": "npm run test:node && npm run test:browser && npm run lint",
|
||||
"test:node": "istanbul cover _mocha -- test.js test.node.js",
|
||||
"test:browser": "karma start --single-run",
|
||||
"test:coverage": "cat ./coverage/lcov.info | coveralls"
|
||||
},
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"brfs": "^2.0.1",
|
||||
"browserify": "^16.2.3",
|
||||
"coveralls": "^3.0.2",
|
||||
"istanbul": "^0.4.5",
|
||||
"karma": "^3.1.4",
|
||||
"karma-browserify": "^6.0.0",
|
||||
"karma-chrome-launcher": "^2.2.0",
|
||||
"karma-mocha": "^1.3.0",
|
||||
"mocha": "^5.2.0",
|
||||
"mocha-lcov-reporter": "^1.2.0",
|
||||
"sinon": "^14.0.0",
|
||||
"xo": "^0.23.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"main": "./src/index.js",
|
||||
"browser": "./src/browser.js",
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
}
|
||||
}
|
||||
271
node_modules/engine.io/node_modules/debug/src/browser.js
generated
vendored
Normal file
271
node_modules/engine.io/node_modules/debug/src/browser.js
generated
vendored
Normal file
@@ -0,0 +1,271 @@
|
||||
/* eslint-env browser */
|
||||
|
||||
/**
|
||||
* This is the web browser implementation of `debug()`.
|
||||
*/
|
||||
|
||||
exports.formatArgs = formatArgs;
|
||||
exports.save = save;
|
||||
exports.load = load;
|
||||
exports.useColors = useColors;
|
||||
exports.storage = localstorage();
|
||||
exports.destroy = (() => {
|
||||
let warned = false;
|
||||
|
||||
return () => {
|
||||
if (!warned) {
|
||||
warned = true;
|
||||
console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
/**
|
||||
* Colors.
|
||||
*/
|
||||
|
||||
exports.colors = [
|
||||
'#0000CC',
|
||||
'#0000FF',
|
||||
'#0033CC',
|
||||
'#0033FF',
|
||||
'#0066CC',
|
||||
'#0066FF',
|
||||
'#0099CC',
|
||||
'#0099FF',
|
||||
'#00CC00',
|
||||
'#00CC33',
|
||||
'#00CC66',
|
||||
'#00CC99',
|
||||
'#00CCCC',
|
||||
'#00CCFF',
|
||||
'#3300CC',
|
||||
'#3300FF',
|
||||
'#3333CC',
|
||||
'#3333FF',
|
||||
'#3366CC',
|
||||
'#3366FF',
|
||||
'#3399CC',
|
||||
'#3399FF',
|
||||
'#33CC00',
|
||||
'#33CC33',
|
||||
'#33CC66',
|
||||
'#33CC99',
|
||||
'#33CCCC',
|
||||
'#33CCFF',
|
||||
'#6600CC',
|
||||
'#6600FF',
|
||||
'#6633CC',
|
||||
'#6633FF',
|
||||
'#66CC00',
|
||||
'#66CC33',
|
||||
'#9900CC',
|
||||
'#9900FF',
|
||||
'#9933CC',
|
||||
'#9933FF',
|
||||
'#99CC00',
|
||||
'#99CC33',
|
||||
'#CC0000',
|
||||
'#CC0033',
|
||||
'#CC0066',
|
||||
'#CC0099',
|
||||
'#CC00CC',
|
||||
'#CC00FF',
|
||||
'#CC3300',
|
||||
'#CC3333',
|
||||
'#CC3366',
|
||||
'#CC3399',
|
||||
'#CC33CC',
|
||||
'#CC33FF',
|
||||
'#CC6600',
|
||||
'#CC6633',
|
||||
'#CC9900',
|
||||
'#CC9933',
|
||||
'#CCCC00',
|
||||
'#CCCC33',
|
||||
'#FF0000',
|
||||
'#FF0033',
|
||||
'#FF0066',
|
||||
'#FF0099',
|
||||
'#FF00CC',
|
||||
'#FF00FF',
|
||||
'#FF3300',
|
||||
'#FF3333',
|
||||
'#FF3366',
|
||||
'#FF3399',
|
||||
'#FF33CC',
|
||||
'#FF33FF',
|
||||
'#FF6600',
|
||||
'#FF6633',
|
||||
'#FF9900',
|
||||
'#FF9933',
|
||||
'#FFCC00',
|
||||
'#FFCC33'
|
||||
];
|
||||
|
||||
/**
|
||||
* Currently only WebKit-based Web Inspectors, Firefox >= v31,
|
||||
* and the Firebug extension (any Firefox version) are known
|
||||
* to support "%c" CSS customizations.
|
||||
*
|
||||
* TODO: add a `localStorage` variable to explicitly enable/disable colors
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
function useColors() {
|
||||
// NB: In an Electron preload script, document will be defined but not fully
|
||||
// initialized. Since we know we're in Chrome, we'll just detect this case
|
||||
// explicitly
|
||||
if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Internet Explorer and Edge do not support colors.
|
||||
if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let m;
|
||||
|
||||
// Is webkit? http://stackoverflow.com/a/16459606/376773
|
||||
// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
|
||||
return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
|
||||
// Is firebug? http://stackoverflow.com/a/398120/376773
|
||||
(typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
|
||||
// Is firefox >= v31?
|
||||
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
|
||||
(typeof navigator !== 'undefined' && navigator.userAgent && (m = navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)) && parseInt(m[1], 10) >= 31) ||
|
||||
// Double check webkit in userAgent just in case we are in a worker
|
||||
(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
|
||||
}
|
||||
|
||||
/**
|
||||
* Colorize log arguments if enabled.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function formatArgs(args) {
|
||||
args[0] = (this.useColors ? '%c' : '') +
|
||||
this.namespace +
|
||||
(this.useColors ? ' %c' : ' ') +
|
||||
args[0] +
|
||||
(this.useColors ? '%c ' : ' ') +
|
||||
'+' + module.exports.humanize(this.diff);
|
||||
|
||||
if (!this.useColors) {
|
||||
return;
|
||||
}
|
||||
|
||||
const c = 'color: ' + this.color;
|
||||
args.splice(1, 0, c, 'color: inherit');
|
||||
|
||||
// The final "%c" is somewhat tricky, because there could be other
|
||||
// arguments passed either before or after the %c, so we need to
|
||||
// figure out the correct index to insert the CSS into
|
||||
let index = 0;
|
||||
let lastC = 0;
|
||||
args[0].replace(/%[a-zA-Z%]/g, match => {
|
||||
if (match === '%%') {
|
||||
return;
|
||||
}
|
||||
index++;
|
||||
if (match === '%c') {
|
||||
// We only are interested in the *last* %c
|
||||
// (the user may have provided their own)
|
||||
lastC = index;
|
||||
}
|
||||
});
|
||||
|
||||
args.splice(lastC, 0, c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes `console.debug()` when available.
|
||||
* No-op when `console.debug` is not a "function".
|
||||
* If `console.debug` is not available, falls back
|
||||
* to `console.log`.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
exports.log = console.debug || console.log || (() => {});
|
||||
|
||||
/**
|
||||
* Save `namespaces`.
|
||||
*
|
||||
* @param {String} namespaces
|
||||
* @api private
|
||||
*/
|
||||
function save(namespaces) {
|
||||
try {
|
||||
if (namespaces) {
|
||||
exports.storage.setItem('debug', namespaces);
|
||||
} else {
|
||||
exports.storage.removeItem('debug');
|
||||
}
|
||||
} catch (error) {
|
||||
// Swallow
|
||||
// XXX (@Qix-) should we be logging these?
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load `namespaces`.
|
||||
*
|
||||
* @return {String} returns the previously persisted debug modes
|
||||
* @api private
|
||||
*/
|
||||
function load() {
|
||||
let r;
|
||||
try {
|
||||
r = exports.storage.getItem('debug');
|
||||
} catch (error) {
|
||||
// Swallow
|
||||
// XXX (@Qix-) should we be logging these?
|
||||
}
|
||||
|
||||
// If debug isn't set in LS, and we're in Electron, try to load $DEBUG
|
||||
if (!r && typeof process !== 'undefined' && 'env' in process) {
|
||||
r = process.env.DEBUG;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Localstorage attempts to return the localstorage.
|
||||
*
|
||||
* This is necessary because safari throws
|
||||
* when a user disables cookies/localstorage
|
||||
* and you attempt to access it.
|
||||
*
|
||||
* @return {LocalStorage}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function localstorage() {
|
||||
try {
|
||||
// TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context
|
||||
// The Browser also has localStorage in the global context.
|
||||
return localStorage;
|
||||
} catch (error) {
|
||||
// Swallow
|
||||
// XXX (@Qix-) should we be logging these?
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = require('./common')(exports);
|
||||
|
||||
const {formatters} = module.exports;
|
||||
|
||||
/**
|
||||
* Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
|
||||
*/
|
||||
|
||||
formatters.j = function (v) {
|
||||
try {
|
||||
return JSON.stringify(v);
|
||||
} catch (error) {
|
||||
return '[UnexpectedJSONParseError]: ' + error.message;
|
||||
}
|
||||
};
|
||||
274
node_modules/engine.io/node_modules/debug/src/common.js
generated
vendored
Normal file
274
node_modules/engine.io/node_modules/debug/src/common.js
generated
vendored
Normal file
@@ -0,0 +1,274 @@
|
||||
|
||||
/**
|
||||
* This is the common logic for both the Node.js and web browser
|
||||
* implementations of `debug()`.
|
||||
*/
|
||||
|
||||
function setup(env) {
|
||||
createDebug.debug = createDebug;
|
||||
createDebug.default = createDebug;
|
||||
createDebug.coerce = coerce;
|
||||
createDebug.disable = disable;
|
||||
createDebug.enable = enable;
|
||||
createDebug.enabled = enabled;
|
||||
createDebug.humanize = require('ms');
|
||||
createDebug.destroy = destroy;
|
||||
|
||||
Object.keys(env).forEach(key => {
|
||||
createDebug[key] = env[key];
|
||||
});
|
||||
|
||||
/**
|
||||
* The currently active debug mode names, and names to skip.
|
||||
*/
|
||||
|
||||
createDebug.names = [];
|
||||
createDebug.skips = [];
|
||||
|
||||
/**
|
||||
* Map of special "%n" handling functions, for the debug "format" argument.
|
||||
*
|
||||
* Valid key names are a single, lower or upper-case letter, i.e. "n" and "N".
|
||||
*/
|
||||
createDebug.formatters = {};
|
||||
|
||||
/**
|
||||
* Selects a color for a debug namespace
|
||||
* @param {String} namespace The namespace string for the debug instance to be colored
|
||||
* @return {Number|String} An ANSI color code for the given namespace
|
||||
* @api private
|
||||
*/
|
||||
function selectColor(namespace) {
|
||||
let hash = 0;
|
||||
|
||||
for (let i = 0; i < namespace.length; i++) {
|
||||
hash = ((hash << 5) - hash) + namespace.charCodeAt(i);
|
||||
hash |= 0; // Convert to 32bit integer
|
||||
}
|
||||
|
||||
return createDebug.colors[Math.abs(hash) % createDebug.colors.length];
|
||||
}
|
||||
createDebug.selectColor = selectColor;
|
||||
|
||||
/**
|
||||
* Create a debugger with the given `namespace`.
|
||||
*
|
||||
* @param {String} namespace
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
function createDebug(namespace) {
|
||||
let prevTime;
|
||||
let enableOverride = null;
|
||||
let namespacesCache;
|
||||
let enabledCache;
|
||||
|
||||
function debug(...args) {
|
||||
// Disabled?
|
||||
if (!debug.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const self = debug;
|
||||
|
||||
// Set `diff` timestamp
|
||||
const curr = Number(new Date());
|
||||
const ms = curr - (prevTime || curr);
|
||||
self.diff = ms;
|
||||
self.prev = prevTime;
|
||||
self.curr = curr;
|
||||
prevTime = curr;
|
||||
|
||||
args[0] = createDebug.coerce(args[0]);
|
||||
|
||||
if (typeof args[0] !== 'string') {
|
||||
// Anything else let's inspect with %O
|
||||
args.unshift('%O');
|
||||
}
|
||||
|
||||
// Apply any `formatters` transformations
|
||||
let index = 0;
|
||||
args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {
|
||||
// If we encounter an escaped % then don't increase the array index
|
||||
if (match === '%%') {
|
||||
return '%';
|
||||
}
|
||||
index++;
|
||||
const formatter = createDebug.formatters[format];
|
||||
if (typeof formatter === 'function') {
|
||||
const val = args[index];
|
||||
match = formatter.call(self, val);
|
||||
|
||||
// Now we need to remove `args[index]` since it's inlined in the `format`
|
||||
args.splice(index, 1);
|
||||
index--;
|
||||
}
|
||||
return match;
|
||||
});
|
||||
|
||||
// Apply env-specific formatting (colors, etc.)
|
||||
createDebug.formatArgs.call(self, args);
|
||||
|
||||
const logFn = self.log || createDebug.log;
|
||||
logFn.apply(self, args);
|
||||
}
|
||||
|
||||
debug.namespace = namespace;
|
||||
debug.useColors = createDebug.useColors();
|
||||
debug.color = createDebug.selectColor(namespace);
|
||||
debug.extend = extend;
|
||||
debug.destroy = createDebug.destroy; // XXX Temporary. Will be removed in the next major release.
|
||||
|
||||
Object.defineProperty(debug, 'enabled', {
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
get: () => {
|
||||
if (enableOverride !== null) {
|
||||
return enableOverride;
|
||||
}
|
||||
if (namespacesCache !== createDebug.namespaces) {
|
||||
namespacesCache = createDebug.namespaces;
|
||||
enabledCache = createDebug.enabled(namespace);
|
||||
}
|
||||
|
||||
return enabledCache;
|
||||
},
|
||||
set: v => {
|
||||
enableOverride = v;
|
||||
}
|
||||
});
|
||||
|
||||
// Env-specific initialization logic for debug instances
|
||||
if (typeof createDebug.init === 'function') {
|
||||
createDebug.init(debug);
|
||||
}
|
||||
|
||||
return debug;
|
||||
}
|
||||
|
||||
function extend(namespace, delimiter) {
|
||||
const newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace);
|
||||
newDebug.log = this.log;
|
||||
return newDebug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables a debug mode by namespaces. This can include modes
|
||||
* separated by a colon and wildcards.
|
||||
*
|
||||
* @param {String} namespaces
|
||||
* @api public
|
||||
*/
|
||||
function enable(namespaces) {
|
||||
createDebug.save(namespaces);
|
||||
createDebug.namespaces = namespaces;
|
||||
|
||||
createDebug.names = [];
|
||||
createDebug.skips = [];
|
||||
|
||||
let i;
|
||||
const split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/);
|
||||
const len = split.length;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!split[i]) {
|
||||
// ignore empty strings
|
||||
continue;
|
||||
}
|
||||
|
||||
namespaces = split[i].replace(/\*/g, '.*?');
|
||||
|
||||
if (namespaces[0] === '-') {
|
||||
createDebug.skips.push(new RegExp('^' + namespaces.slice(1) + '$'));
|
||||
} else {
|
||||
createDebug.names.push(new RegExp('^' + namespaces + '$'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable debug output.
|
||||
*
|
||||
* @return {String} namespaces
|
||||
* @api public
|
||||
*/
|
||||
function disable() {
|
||||
const namespaces = [
|
||||
...createDebug.names.map(toNamespace),
|
||||
...createDebug.skips.map(toNamespace).map(namespace => '-' + namespace)
|
||||
].join(',');
|
||||
createDebug.enable('');
|
||||
return namespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given mode name is enabled, false otherwise.
|
||||
*
|
||||
* @param {String} name
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
function enabled(name) {
|
||||
if (name[name.length - 1] === '*') {
|
||||
return true;
|
||||
}
|
||||
|
||||
let i;
|
||||
let len;
|
||||
|
||||
for (i = 0, len = createDebug.skips.length; i < len; i++) {
|
||||
if (createDebug.skips[i].test(name)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0, len = createDebug.names.length; i < len; i++) {
|
||||
if (createDebug.names[i].test(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert regexp to namespace
|
||||
*
|
||||
* @param {RegExp} regxep
|
||||
* @return {String} namespace
|
||||
* @api private
|
||||
*/
|
||||
function toNamespace(regexp) {
|
||||
return regexp.toString()
|
||||
.substring(2, regexp.toString().length - 2)
|
||||
.replace(/\.\*\?$/, '*');
|
||||
}
|
||||
|
||||
/**
|
||||
* Coerce `val`.
|
||||
*
|
||||
* @param {Mixed} val
|
||||
* @return {Mixed}
|
||||
* @api private
|
||||
*/
|
||||
function coerce(val) {
|
||||
if (val instanceof Error) {
|
||||
return val.stack || val.message;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* XXX DO NOT USE. This is a temporary stub function.
|
||||
* XXX It WILL be removed in the next major release.
|
||||
*/
|
||||
function destroy() {
|
||||
console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');
|
||||
}
|
||||
|
||||
createDebug.enable(createDebug.load());
|
||||
|
||||
return createDebug;
|
||||
}
|
||||
|
||||
module.exports = setup;
|
||||
10
node_modules/engine.io/node_modules/debug/src/index.js
generated
vendored
Normal file
10
node_modules/engine.io/node_modules/debug/src/index.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Detect Electron renderer / nwjs process, which is node, but we should
|
||||
* treat as a browser.
|
||||
*/
|
||||
|
||||
if (typeof process === 'undefined' || process.type === 'renderer' || process.browser === true || process.__nwjs) {
|
||||
module.exports = require('./browser.js');
|
||||
} else {
|
||||
module.exports = require('./node.js');
|
||||
}
|
||||
263
node_modules/engine.io/node_modules/debug/src/node.js
generated
vendored
Normal file
263
node_modules/engine.io/node_modules/debug/src/node.js
generated
vendored
Normal file
@@ -0,0 +1,263 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
const tty = require('tty');
|
||||
const util = require('util');
|
||||
|
||||
/**
|
||||
* This is the Node.js implementation of `debug()`.
|
||||
*/
|
||||
|
||||
exports.init = init;
|
||||
exports.log = log;
|
||||
exports.formatArgs = formatArgs;
|
||||
exports.save = save;
|
||||
exports.load = load;
|
||||
exports.useColors = useColors;
|
||||
exports.destroy = util.deprecate(
|
||||
() => {},
|
||||
'Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.'
|
||||
);
|
||||
|
||||
/**
|
||||
* Colors.
|
||||
*/
|
||||
|
||||
exports.colors = [6, 2, 3, 4, 5, 1];
|
||||
|
||||
try {
|
||||
// Optional dependency (as in, doesn't need to be installed, NOT like optionalDependencies in package.json)
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
const supportsColor = require('supports-color');
|
||||
|
||||
if (supportsColor && (supportsColor.stderr || supportsColor).level >= 2) {
|
||||
exports.colors = [
|
||||
20,
|
||||
21,
|
||||
26,
|
||||
27,
|
||||
32,
|
||||
33,
|
||||
38,
|
||||
39,
|
||||
40,
|
||||
41,
|
||||
42,
|
||||
43,
|
||||
44,
|
||||
45,
|
||||
56,
|
||||
57,
|
||||
62,
|
||||
63,
|
||||
68,
|
||||
69,
|
||||
74,
|
||||
75,
|
||||
76,
|
||||
77,
|
||||
78,
|
||||
79,
|
||||
80,
|
||||
81,
|
||||
92,
|
||||
93,
|
||||
98,
|
||||
99,
|
||||
112,
|
||||
113,
|
||||
128,
|
||||
129,
|
||||
134,
|
||||
135,
|
||||
148,
|
||||
149,
|
||||
160,
|
||||
161,
|
||||
162,
|
||||
163,
|
||||
164,
|
||||
165,
|
||||
166,
|
||||
167,
|
||||
168,
|
||||
169,
|
||||
170,
|
||||
171,
|
||||
172,
|
||||
173,
|
||||
178,
|
||||
179,
|
||||
184,
|
||||
185,
|
||||
196,
|
||||
197,
|
||||
198,
|
||||
199,
|
||||
200,
|
||||
201,
|
||||
202,
|
||||
203,
|
||||
204,
|
||||
205,
|
||||
206,
|
||||
207,
|
||||
208,
|
||||
209,
|
||||
214,
|
||||
215,
|
||||
220,
|
||||
221
|
||||
];
|
||||
}
|
||||
} catch (error) {
|
||||
// Swallow - we only care if `supports-color` is available; it doesn't have to be.
|
||||
}
|
||||
|
||||
/**
|
||||
* Build up the default `inspectOpts` object from the environment variables.
|
||||
*
|
||||
* $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js
|
||||
*/
|
||||
|
||||
exports.inspectOpts = Object.keys(process.env).filter(key => {
|
||||
return /^debug_/i.test(key);
|
||||
}).reduce((obj, key) => {
|
||||
// Camel-case
|
||||
const prop = key
|
||||
.substring(6)
|
||||
.toLowerCase()
|
||||
.replace(/_([a-z])/g, (_, k) => {
|
||||
return k.toUpperCase();
|
||||
});
|
||||
|
||||
// Coerce string value into JS value
|
||||
let val = process.env[key];
|
||||
if (/^(yes|on|true|enabled)$/i.test(val)) {
|
||||
val = true;
|
||||
} else if (/^(no|off|false|disabled)$/i.test(val)) {
|
||||
val = false;
|
||||
} else if (val === 'null') {
|
||||
val = null;
|
||||
} else {
|
||||
val = Number(val);
|
||||
}
|
||||
|
||||
obj[prop] = val;
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
/**
|
||||
* Is stdout a TTY? Colored output is enabled when `true`.
|
||||
*/
|
||||
|
||||
function useColors() {
|
||||
return 'colors' in exports.inspectOpts ?
|
||||
Boolean(exports.inspectOpts.colors) :
|
||||
tty.isatty(process.stderr.fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds ANSI color escape codes if enabled.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function formatArgs(args) {
|
||||
const {namespace: name, useColors} = this;
|
||||
|
||||
if (useColors) {
|
||||
const c = this.color;
|
||||
const colorCode = '\u001B[3' + (c < 8 ? c : '8;5;' + c);
|
||||
const prefix = ` ${colorCode};1m${name} \u001B[0m`;
|
||||
|
||||
args[0] = prefix + args[0].split('\n').join('\n' + prefix);
|
||||
args.push(colorCode + 'm+' + module.exports.humanize(this.diff) + '\u001B[0m');
|
||||
} else {
|
||||
args[0] = getDate() + name + ' ' + args[0];
|
||||
}
|
||||
}
|
||||
|
||||
function getDate() {
|
||||
if (exports.inspectOpts.hideDate) {
|
||||
return '';
|
||||
}
|
||||
return new Date().toISOString() + ' ';
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes `util.formatWithOptions()` with the specified arguments and writes to stderr.
|
||||
*/
|
||||
|
||||
function log(...args) {
|
||||
return process.stderr.write(util.formatWithOptions(exports.inspectOpts, ...args) + '\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Save `namespaces`.
|
||||
*
|
||||
* @param {String} namespaces
|
||||
* @api private
|
||||
*/
|
||||
function save(namespaces) {
|
||||
if (namespaces) {
|
||||
process.env.DEBUG = namespaces;
|
||||
} else {
|
||||
// If you set a process.env field to null or undefined, it gets cast to the
|
||||
// string 'null' or 'undefined'. Just delete instead.
|
||||
delete process.env.DEBUG;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load `namespaces`.
|
||||
*
|
||||
* @return {String} returns the previously persisted debug modes
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function load() {
|
||||
return process.env.DEBUG;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init logic for `debug` instances.
|
||||
*
|
||||
* Create a new `inspectOpts` object in case `useColors` is set
|
||||
* differently for a particular `debug` instance.
|
||||
*/
|
||||
|
||||
function init(debug) {
|
||||
debug.inspectOpts = {};
|
||||
|
||||
const keys = Object.keys(exports.inspectOpts);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = require('./common')(exports);
|
||||
|
||||
const {formatters} = module.exports;
|
||||
|
||||
/**
|
||||
* Map %o to `util.inspect()`, all on a single line.
|
||||
*/
|
||||
|
||||
formatters.o = function (v) {
|
||||
this.inspectOpts.colors = this.useColors;
|
||||
return util.inspect(v, this.inspectOpts)
|
||||
.split('\n')
|
||||
.map(str => str.trim())
|
||||
.join(' ');
|
||||
};
|
||||
|
||||
/**
|
||||
* Map %O to `util.inspect()`, allowing multiple lines if needed.
|
||||
*/
|
||||
|
||||
formatters.O = function (v) {
|
||||
this.inspectOpts.colors = this.useColors;
|
||||
return util.inspect(v, this.inspectOpts);
|
||||
};
|
||||
162
node_modules/engine.io/node_modules/ms/index.js
generated
vendored
Normal file
162
node_modules/engine.io/node_modules/ms/index.js
generated
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
/**
|
||||
* Helpers.
|
||||
*/
|
||||
|
||||
var s = 1000;
|
||||
var m = s * 60;
|
||||
var h = m * 60;
|
||||
var d = h * 24;
|
||||
var w = d * 7;
|
||||
var y = d * 365.25;
|
||||
|
||||
/**
|
||||
* Parse or format the given `val`.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `long` verbose formatting [false]
|
||||
*
|
||||
* @param {String|Number} val
|
||||
* @param {Object} [options]
|
||||
* @throws {Error} throw an error if val is not a non-empty string or a number
|
||||
* @return {String|Number}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function (val, options) {
|
||||
options = options || {};
|
||||
var type = typeof val;
|
||||
if (type === 'string' && val.length > 0) {
|
||||
return parse(val);
|
||||
} else if (type === 'number' && isFinite(val)) {
|
||||
return options.long ? fmtLong(val) : fmtShort(val);
|
||||
}
|
||||
throw new Error(
|
||||
'val is not a non-empty string or a valid number. val=' +
|
||||
JSON.stringify(val)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse the given `str` and return milliseconds.
|
||||
*
|
||||
* @param {String} str
|
||||
* @return {Number}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function parse(str) {
|
||||
str = String(str);
|
||||
if (str.length > 100) {
|
||||
return;
|
||||
}
|
||||
var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(
|
||||
str
|
||||
);
|
||||
if (!match) {
|
||||
return;
|
||||
}
|
||||
var n = parseFloat(match[1]);
|
||||
var type = (match[2] || 'ms').toLowerCase();
|
||||
switch (type) {
|
||||
case 'years':
|
||||
case 'year':
|
||||
case 'yrs':
|
||||
case 'yr':
|
||||
case 'y':
|
||||
return n * y;
|
||||
case 'weeks':
|
||||
case 'week':
|
||||
case 'w':
|
||||
return n * w;
|
||||
case 'days':
|
||||
case 'day':
|
||||
case 'd':
|
||||
return n * d;
|
||||
case 'hours':
|
||||
case 'hour':
|
||||
case 'hrs':
|
||||
case 'hr':
|
||||
case 'h':
|
||||
return n * h;
|
||||
case 'minutes':
|
||||
case 'minute':
|
||||
case 'mins':
|
||||
case 'min':
|
||||
case 'm':
|
||||
return n * m;
|
||||
case 'seconds':
|
||||
case 'second':
|
||||
case 'secs':
|
||||
case 'sec':
|
||||
case 's':
|
||||
return n * s;
|
||||
case 'milliseconds':
|
||||
case 'millisecond':
|
||||
case 'msecs':
|
||||
case 'msec':
|
||||
case 'ms':
|
||||
return n;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Short format for `ms`.
|
||||
*
|
||||
* @param {Number} ms
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function fmtShort(ms) {
|
||||
var msAbs = Math.abs(ms);
|
||||
if (msAbs >= d) {
|
||||
return Math.round(ms / d) + 'd';
|
||||
}
|
||||
if (msAbs >= h) {
|
||||
return Math.round(ms / h) + 'h';
|
||||
}
|
||||
if (msAbs >= m) {
|
||||
return Math.round(ms / m) + 'm';
|
||||
}
|
||||
if (msAbs >= s) {
|
||||
return Math.round(ms / s) + 's';
|
||||
}
|
||||
return ms + 'ms';
|
||||
}
|
||||
|
||||
/**
|
||||
* Long format for `ms`.
|
||||
*
|
||||
* @param {Number} ms
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function fmtLong(ms) {
|
||||
var msAbs = Math.abs(ms);
|
||||
if (msAbs >= d) {
|
||||
return plural(ms, msAbs, d, 'day');
|
||||
}
|
||||
if (msAbs >= h) {
|
||||
return plural(ms, msAbs, h, 'hour');
|
||||
}
|
||||
if (msAbs >= m) {
|
||||
return plural(ms, msAbs, m, 'minute');
|
||||
}
|
||||
if (msAbs >= s) {
|
||||
return plural(ms, msAbs, s, 'second');
|
||||
}
|
||||
return ms + ' ms';
|
||||
}
|
||||
|
||||
/**
|
||||
* Pluralization helper.
|
||||
*/
|
||||
|
||||
function plural(ms, msAbs, n, name) {
|
||||
var isPlural = msAbs >= n * 1.5;
|
||||
return Math.round(ms / n) + ' ' + name + (isPlural ? 's' : '');
|
||||
}
|
||||
21
node_modules/engine.io/node_modules/ms/license.md
generated
vendored
Normal file
21
node_modules/engine.io/node_modules/ms/license.md
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2020 Vercel, Inc.
|
||||
|
||||
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.
|
||||
38
node_modules/engine.io/node_modules/ms/package.json
generated
vendored
Normal file
38
node_modules/engine.io/node_modules/ms/package.json
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "ms",
|
||||
"version": "2.1.3",
|
||||
"description": "Tiny millisecond conversion utility",
|
||||
"repository": "vercel/ms",
|
||||
"main": "./index",
|
||||
"files": [
|
||||
"index.js"
|
||||
],
|
||||
"scripts": {
|
||||
"precommit": "lint-staged",
|
||||
"lint": "eslint lib/* bin/*",
|
||||
"test": "mocha tests.js"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "eslint:recommended",
|
||||
"env": {
|
||||
"node": true,
|
||||
"es6": true
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.js": [
|
||||
"npm run lint",
|
||||
"prettier --single-quote --write",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"eslint": "4.18.2",
|
||||
"expect.js": "0.3.1",
|
||||
"husky": "0.14.3",
|
||||
"lint-staged": "5.0.0",
|
||||
"mocha": "4.0.1",
|
||||
"prettier": "2.0.5"
|
||||
}
|
||||
}
|
||||
59
node_modules/engine.io/node_modules/ms/readme.md
generated
vendored
Normal file
59
node_modules/engine.io/node_modules/ms/readme.md
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
# ms
|
||||
|
||||

|
||||
|
||||
Use this package to easily convert various time formats to milliseconds.
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
ms('2 days') // 172800000
|
||||
ms('1d') // 86400000
|
||||
ms('10h') // 36000000
|
||||
ms('2.5 hrs') // 9000000
|
||||
ms('2h') // 7200000
|
||||
ms('1m') // 60000
|
||||
ms('5s') // 5000
|
||||
ms('1y') // 31557600000
|
||||
ms('100') // 100
|
||||
ms('-3 days') // -259200000
|
||||
ms('-1h') // -3600000
|
||||
ms('-200') // -200
|
||||
```
|
||||
|
||||
### Convert from Milliseconds
|
||||
|
||||
```js
|
||||
ms(60000) // "1m"
|
||||
ms(2 * 60000) // "2m"
|
||||
ms(-3 * 60000) // "-3m"
|
||||
ms(ms('10 hours')) // "10h"
|
||||
```
|
||||
|
||||
### Time Format Written-Out
|
||||
|
||||
```js
|
||||
ms(60000, { long: true }) // "1 minute"
|
||||
ms(2 * 60000, { long: true }) // "2 minutes"
|
||||
ms(-3 * 60000, { long: true }) // "-3 minutes"
|
||||
ms(ms('10 hours'), { long: true }) // "10 hours"
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Works both in [Node.js](https://nodejs.org) and in the browser
|
||||
- If a number is supplied to `ms`, a string with a unit is returned
|
||||
- If a string that contains the number is supplied, it returns it as a number (e.g.: it returns `100` for `'100'`)
|
||||
- If you pass a string with a number and a valid unit, the number of equivalent milliseconds is returned
|
||||
|
||||
## Related Packages
|
||||
|
||||
- [ms.macro](https://github.com/knpwrs/ms.macro) - Run `ms` as a macro at build-time.
|
||||
|
||||
## Caught a Bug?
|
||||
|
||||
1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device
|
||||
2. Link the package to the global module directory: `npm link`
|
||||
3. Within the module you want to test your local development instance of ms, just link it to the dependencies: `npm link ms`. Instead of the default one from npm, Node.js will now use your clone of ms!
|
||||
|
||||
As always, you can run the tests using: `npm test`
|
||||
20
node_modules/engine.io/node_modules/ws/LICENSE
generated
vendored
Normal file
20
node_modules/engine.io/node_modules/ws/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
|
||||
Copyright (c) 2013 Arnout Kazemier and contributors
|
||||
Copyright (c) 2016 Luigi Pinca and contributors
|
||||
|
||||
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.
|
||||
548
node_modules/engine.io/node_modules/ws/README.md
generated
vendored
Normal file
548
node_modules/engine.io/node_modules/ws/README.md
generated
vendored
Normal file
@@ -0,0 +1,548 @@
|
||||
# ws: a Node.js WebSocket library
|
||||
|
||||
[](https://www.npmjs.com/package/ws)
|
||||
[](https://github.com/websockets/ws/actions?query=workflow%3ACI+branch%3Amaster)
|
||||
[](https://coveralls.io/github/websockets/ws)
|
||||
|
||||
ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and
|
||||
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 backend 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](#opt-in-for-performance)
|
||||
- [Legacy opt-in for performance](#legacy-opt-in-for-performance)
|
||||
- [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)
|
||||
- [Client authentication](#client-authentication)
|
||||
- [Server broadcast](#server-broadcast)
|
||||
- [Round-trip time](#round-trip-time)
|
||||
- [Use the Node.js streams API](#use-the-nodejs-streams-api)
|
||||
- [Other examples](#other-examples)
|
||||
- [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 ws
|
||||
```
|
||||
|
||||
### Opt-in for performance
|
||||
|
||||
[bufferutil][] is an optional module that can be installed alongside the ws
|
||||
module:
|
||||
|
||||
```
|
||||
npm install --save-optional bufferutil
|
||||
```
|
||||
|
||||
This is a binary addon that improves the performance of certain operations such
|
||||
as masking and unmasking the data payload of the WebSocket frames. Prebuilt
|
||||
binaries are available for the most popular platforms, so you don't necessarily
|
||||
need to have a C++ compiler installed on your machine.
|
||||
|
||||
To force ws to not use bufferutil, use the
|
||||
[`WS_NO_BUFFER_UTIL`](./doc/ws.md#ws_no_buffer_util) environment variable. This
|
||||
can be useful to enhance security in systems where a user can put a package in
|
||||
the package search path of an application of another user, due to how the
|
||||
Node.js resolver algorithm works.
|
||||
|
||||
#### Legacy opt-in for performance
|
||||
|
||||
If you are running on an old version of Node.js (prior to v18.14.0), ws also
|
||||
supports the [utf-8-validate][] module:
|
||||
|
||||
```
|
||||
npm install --save-optional utf-8-validate
|
||||
```
|
||||
|
||||
This contains a binary polyfill for [`buffer.isUtf8()`][].
|
||||
|
||||
To force ws not to use utf-8-validate, use the
|
||||
[`WS_NO_UTF_8_VALIDATE`](./doc/ws.md#ws_no_utf_8_validate) environment variable.
|
||||
|
||||
## API docs
|
||||
|
||||
See [`/doc/ws.md`](./doc/ws.md) for Node.js-like documentation of ws classes and
|
||||
utility functions.
|
||||
|
||||
## 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
|
||||
import WebSocket, { WebSocketServer } from 'ws';
|
||||
|
||||
const wss = new WebSocketServer({
|
||||
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.
|
||||
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 if context takeover is disabled.
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
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
|
||||
import WebSocket from 'ws';
|
||||
|
||||
const ws = new WebSocket('ws://www.host.com/path', {
|
||||
perMessageDeflate: false
|
||||
});
|
||||
```
|
||||
|
||||
## Usage examples
|
||||
|
||||
### Sending and receiving text data
|
||||
|
||||
```js
|
||||
import WebSocket from 'ws';
|
||||
|
||||
const ws = new WebSocket('ws://www.host.com/path');
|
||||
|
||||
ws.on('error', console.error);
|
||||
|
||||
ws.on('open', function open() {
|
||||
ws.send('something');
|
||||
});
|
||||
|
||||
ws.on('message', function message(data) {
|
||||
console.log('received: %s', data);
|
||||
});
|
||||
```
|
||||
|
||||
### Sending binary data
|
||||
|
||||
```js
|
||||
import WebSocket from 'ws';
|
||||
|
||||
const ws = new WebSocket('ws://www.host.com/path');
|
||||
|
||||
ws.on('error', console.error);
|
||||
|
||||
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
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
const wss = new WebSocketServer({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('error', console.error);
|
||||
|
||||
ws.on('message', function message(data) {
|
||||
console.log('received: %s', data);
|
||||
});
|
||||
|
||||
ws.send('something');
|
||||
});
|
||||
```
|
||||
|
||||
### External HTTP/S server
|
||||
|
||||
```js
|
||||
import { createServer } from 'https';
|
||||
import { readFileSync } from 'fs';
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
const server = createServer({
|
||||
cert: readFileSync('/path/to/cert.pem'),
|
||||
key: readFileSync('/path/to/key.pem')
|
||||
});
|
||||
const wss = new WebSocketServer({ server });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('error', console.error);
|
||||
|
||||
ws.on('message', function message(data) {
|
||||
console.log('received: %s', data);
|
||||
});
|
||||
|
||||
ws.send('something');
|
||||
});
|
||||
|
||||
server.listen(8080);
|
||||
```
|
||||
|
||||
### Multiple servers sharing a single HTTP/S server
|
||||
|
||||
```js
|
||||
import { createServer } from 'http';
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
const server = createServer();
|
||||
const wss1 = new WebSocketServer({ noServer: true });
|
||||
const wss2 = new WebSocketServer({ noServer: true });
|
||||
|
||||
wss1.on('connection', function connection(ws) {
|
||||
ws.on('error', console.error);
|
||||
|
||||
// ...
|
||||
});
|
||||
|
||||
wss2.on('connection', function connection(ws) {
|
||||
ws.on('error', console.error);
|
||||
|
||||
// ...
|
||||
});
|
||||
|
||||
server.on('upgrade', function upgrade(request, socket, head) {
|
||||
const { pathname } = new URL(request.url, 'wss://base.url');
|
||||
|
||||
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);
|
||||
```
|
||||
|
||||
### Client authentication
|
||||
|
||||
```js
|
||||
import { createServer } from 'http';
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
function onSocketError(err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
const server = createServer();
|
||||
const wss = new WebSocketServer({ noServer: true });
|
||||
|
||||
wss.on('connection', function connection(ws, request, client) {
|
||||
ws.on('error', console.error);
|
||||
|
||||
ws.on('message', function message(data) {
|
||||
console.log(`Received message ${data} from user ${client}`);
|
||||
});
|
||||
});
|
||||
|
||||
server.on('upgrade', function upgrade(request, socket, head) {
|
||||
socket.on('error', onSocketError);
|
||||
|
||||
// This function is not defined on purpose. Implement it with your own logic.
|
||||
authenticate(request, function next(err, client) {
|
||||
if (err || !client) {
|
||||
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
socket.removeListener('error', onSocketError);
|
||||
|
||||
wss.handleUpgrade(request, socket, head, function done(ws) {
|
||||
wss.emit('connection', ws, request, client);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(8080);
|
||||
```
|
||||
|
||||
Also see the provided [example][session-parse-example] using `express-session`.
|
||||
|
||||
### Server broadcast
|
||||
|
||||
A client WebSocket broadcasting to all connected WebSocket clients, including
|
||||
itself.
|
||||
|
||||
```js
|
||||
import WebSocket, { WebSocketServer } from 'ws';
|
||||
|
||||
const wss = new WebSocketServer({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('error', console.error);
|
||||
|
||||
ws.on('message', function message(data, isBinary) {
|
||||
wss.clients.forEach(function each(client) {
|
||||
if (client.readyState === WebSocket.OPEN) {
|
||||
client.send(data, { binary: isBinary });
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
A client WebSocket broadcasting to every other connected WebSocket clients,
|
||||
excluding itself.
|
||||
|
||||
```js
|
||||
import WebSocket, { WebSocketServer } from 'ws';
|
||||
|
||||
const wss = new WebSocketServer({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('error', console.error);
|
||||
|
||||
ws.on('message', function message(data, isBinary) {
|
||||
wss.clients.forEach(function each(client) {
|
||||
if (client !== ws && client.readyState === WebSocket.OPEN) {
|
||||
client.send(data, { binary: isBinary });
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Round-trip time
|
||||
|
||||
```js
|
||||
import WebSocket from 'ws';
|
||||
|
||||
const ws = new WebSocket('wss://websocket-echo.com/');
|
||||
|
||||
ws.on('error', console.error);
|
||||
|
||||
ws.on('open', function open() {
|
||||
console.log('connected');
|
||||
ws.send(Date.now());
|
||||
});
|
||||
|
||||
ws.on('close', function close() {
|
||||
console.log('disconnected');
|
||||
});
|
||||
|
||||
ws.on('message', function message(data) {
|
||||
console.log(`Round-trip time: ${Date.now() - data} ms`);
|
||||
|
||||
setTimeout(function timeout() {
|
||||
ws.send(Date.now());
|
||||
}, 500);
|
||||
});
|
||||
```
|
||||
|
||||
### Use the Node.js streams API
|
||||
|
||||
```js
|
||||
import WebSocket, { createWebSocketStream } from 'ws';
|
||||
|
||||
const ws = new WebSocket('wss://websocket-echo.com/');
|
||||
|
||||
const duplex = createWebSocketStream(ws, { encoding: 'utf8' });
|
||||
|
||||
duplex.on('error', console.error);
|
||||
|
||||
duplex.pipe(process.stdout);
|
||||
process.stdin.pipe(duplex);
|
||||
```
|
||||
|
||||
### Other examples
|
||||
|
||||
For a full example with a browser client communicating with a ws server, see the
|
||||
examples folder.
|
||||
|
||||
Otherwise, see the test cases.
|
||||
|
||||
## FAQ
|
||||
|
||||
### How to get the IP address of the client?
|
||||
|
||||
The remote IP address can be obtained from the raw socket.
|
||||
|
||||
```js
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
const wss = new WebSocketServer({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws, req) {
|
||||
const ip = req.socket.remoteAddress;
|
||||
|
||||
ws.on('error', console.error);
|
||||
});
|
||||
```
|
||||
|
||||
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(',')[0].trim();
|
||||
|
||||
ws.on('error', console.error);
|
||||
});
|
||||
```
|
||||
|
||||
### 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
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
function heartbeat() {
|
||||
this.isAlive = true;
|
||||
}
|
||||
|
||||
const wss = new WebSocketServer({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.isAlive = true;
|
||||
ws.on('error', console.error);
|
||||
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();
|
||||
});
|
||||
}, 30000);
|
||||
|
||||
wss.on('close', function close() {
|
||||
clearInterval(interval);
|
||||
});
|
||||
```
|
||||
|
||||
Pong messages are automatically sent in response to ping messages as required by
|
||||
the spec.
|
||||
|
||||
Just like the server example above, your clients might as well lose connection
|
||||
without knowing it. You might want to add a ping listener on your clients to
|
||||
prevent that. A simple implementation would be:
|
||||
|
||||
```js
|
||||
import WebSocket from 'ws';
|
||||
|
||||
function heartbeat() {
|
||||
clearTimeout(this.pingTimeout);
|
||||
|
||||
// Use `WebSocket#terminate()`, which immediately destroys the connection,
|
||||
// instead of `WebSocket#close()`, which waits for the close timer.
|
||||
// Delay should be equal to the interval at which your server
|
||||
// sends out pings plus a conservative assumption of the latency.
|
||||
this.pingTimeout = setTimeout(() => {
|
||||
this.terminate();
|
||||
}, 30000 + 1000);
|
||||
}
|
||||
|
||||
const client = new WebSocket('wss://websocket-echo.com/');
|
||||
|
||||
client.on('error', console.error);
|
||||
client.on('open', heartbeat);
|
||||
client.on('ping', heartbeat);
|
||||
client.on('close', function clear() {
|
||||
clearTimeout(this.pingTimeout);
|
||||
});
|
||||
```
|
||||
|
||||
### 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)
|
||||
|
||||
[`buffer.isutf8()`]: https://nodejs.org/api/buffer.html#bufferisutf8input
|
||||
[bufferutil]: https://github.com/websockets/bufferutil
|
||||
[changelog]: https://github.com/websockets/ws/releases
|
||||
[client-report]: http://websockets.github.io/ws/autobahn/clients/
|
||||
[https-proxy-agent]: https://github.com/TooTallNate/node-https-proxy-agent
|
||||
[node-zlib-bug]: https://github.com/nodejs/node/issues/8871
|
||||
[node-zlib-deflaterawdocs]:
|
||||
https://nodejs.org/api/zlib.html#zlib_zlib_createdeflateraw_options
|
||||
[permessage-deflate]: https://tools.ietf.org/html/rfc7692
|
||||
[server-report]: http://websockets.github.io/ws/autobahn/servers/
|
||||
[session-parse-example]: ./examples/express-session-parse
|
||||
[socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent
|
||||
[utf-8-validate]: https://github.com/websockets/utf-8-validate
|
||||
[ws-server-options]: ./doc/ws.md#new-websocketserveroptions-callback
|
||||
8
node_modules/engine.io/node_modules/ws/browser.js
generated
vendored
Normal file
8
node_modules/engine.io/node_modules/ws/browser.js
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function () {
|
||||
throw new Error(
|
||||
'ws does not work in the browser. Browser clients must use the native ' +
|
||||
'WebSocket object'
|
||||
);
|
||||
};
|
||||
13
node_modules/engine.io/node_modules/ws/index.js
generated
vendored
Normal file
13
node_modules/engine.io/node_modules/ws/index.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
const WebSocket = require('./lib/websocket');
|
||||
|
||||
WebSocket.createWebSocketStream = require('./lib/stream');
|
||||
WebSocket.Server = require('./lib/websocket-server');
|
||||
WebSocket.Receiver = require('./lib/receiver');
|
||||
WebSocket.Sender = require('./lib/sender');
|
||||
|
||||
WebSocket.WebSocket = WebSocket;
|
||||
WebSocket.WebSocketServer = WebSocket.Server;
|
||||
|
||||
module.exports = WebSocket;
|
||||
131
node_modules/engine.io/node_modules/ws/lib/buffer-util.js
generated
vendored
Normal file
131
node_modules/engine.io/node_modules/ws/lib/buffer-util.js
generated
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
'use strict';
|
||||
|
||||
const { EMPTY_BUFFER } = require('./constants');
|
||||
|
||||
const FastBuffer = Buffer[Symbol.species];
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
if (list.length === 0) return EMPTY_BUFFER;
|
||||
if (list.length === 1) return list[0];
|
||||
|
||||
const target = Buffer.allocUnsafe(totalLength);
|
||||
let offset = 0;
|
||||
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const buf = list[i];
|
||||
target.set(buf, offset);
|
||||
offset += buf.length;
|
||||
}
|
||||
|
||||
if (offset < totalLength) {
|
||||
return new FastBuffer(target.buffer, target.byteOffset, offset);
|
||||
}
|
||||
|
||||
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 (let 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) {
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
buffer[i] ^= mask[i & 3];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a buffer to an `ArrayBuffer`.
|
||||
*
|
||||
* @param {Buffer} buf The buffer to convert
|
||||
* @return {ArrayBuffer} Converted buffer
|
||||
* @public
|
||||
*/
|
||||
function toArrayBuffer(buf) {
|
||||
if (buf.length === buf.buffer.byteLength) {
|
||||
return buf.buffer;
|
||||
}
|
||||
|
||||
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts `data` to a `Buffer`.
|
||||
*
|
||||
* @param {*} data The data to convert
|
||||
* @return {Buffer} The buffer
|
||||
* @throws {TypeError}
|
||||
* @public
|
||||
*/
|
||||
function toBuffer(data) {
|
||||
toBuffer.readOnly = true;
|
||||
|
||||
if (Buffer.isBuffer(data)) return data;
|
||||
|
||||
let buf;
|
||||
|
||||
if (data instanceof ArrayBuffer) {
|
||||
buf = new FastBuffer(data);
|
||||
} else if (ArrayBuffer.isView(data)) {
|
||||
buf = new FastBuffer(data.buffer, data.byteOffset, data.byteLength);
|
||||
} else {
|
||||
buf = Buffer.from(data);
|
||||
toBuffer.readOnly = false;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
concat,
|
||||
mask: _mask,
|
||||
toArrayBuffer,
|
||||
toBuffer,
|
||||
unmask: _unmask
|
||||
};
|
||||
|
||||
/* istanbul ignore else */
|
||||
if (!process.env.WS_NO_BUFFER_UTIL) {
|
||||
try {
|
||||
const bufferUtil = require('bufferutil');
|
||||
|
||||
module.exports.mask = function (source, mask, output, offset, length) {
|
||||
if (length < 48) _mask(source, mask, output, offset, length);
|
||||
else bufferUtil.mask(source, mask, output, offset, length);
|
||||
};
|
||||
|
||||
module.exports.unmask = function (buffer, mask) {
|
||||
if (buffer.length < 32) _unmask(buffer, mask);
|
||||
else bufferUtil.unmask(buffer, mask);
|
||||
};
|
||||
} catch (e) {
|
||||
// Continue regardless of the error.
|
||||
}
|
||||
}
|
||||
12
node_modules/engine.io/node_modules/ws/lib/constants.js
generated
vendored
Normal file
12
node_modules/engine.io/node_modules/ws/lib/constants.js
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
BINARY_TYPES: ['nodebuffer', 'arraybuffer', 'fragments'],
|
||||
EMPTY_BUFFER: Buffer.alloc(0),
|
||||
GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
|
||||
kForOnEventAttribute: Symbol('kIsForOnEventAttribute'),
|
||||
kListener: Symbol('kListener'),
|
||||
kStatusCode: Symbol('status-code'),
|
||||
kWebSocket: Symbol('websocket'),
|
||||
NOOP: () => {}
|
||||
};
|
||||
292
node_modules/engine.io/node_modules/ws/lib/event-target.js
generated
vendored
Normal file
292
node_modules/engine.io/node_modules/ws/lib/event-target.js
generated
vendored
Normal file
@@ -0,0 +1,292 @@
|
||||
'use strict';
|
||||
|
||||
const { kForOnEventAttribute, kListener } = require('./constants');
|
||||
|
||||
const kCode = Symbol('kCode');
|
||||
const kData = Symbol('kData');
|
||||
const kError = Symbol('kError');
|
||||
const kMessage = Symbol('kMessage');
|
||||
const kReason = Symbol('kReason');
|
||||
const kTarget = Symbol('kTarget');
|
||||
const kType = Symbol('kType');
|
||||
const kWasClean = Symbol('kWasClean');
|
||||
|
||||
/**
|
||||
* Class representing an event.
|
||||
*/
|
||||
class Event {
|
||||
/**
|
||||
* Create a new `Event`.
|
||||
*
|
||||
* @param {String} type The name of the event
|
||||
* @throws {TypeError} If the `type` argument is not specified
|
||||
*/
|
||||
constructor(type) {
|
||||
this[kTarget] = null;
|
||||
this[kType] = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {*}
|
||||
*/
|
||||
get target() {
|
||||
return this[kTarget];
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {String}
|
||||
*/
|
||||
get type() {
|
||||
return this[kType];
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(Event.prototype, 'target', { enumerable: true });
|
||||
Object.defineProperty(Event.prototype, 'type', { enumerable: true });
|
||||
|
||||
/**
|
||||
* Class representing a close event.
|
||||
*
|
||||
* @extends Event
|
||||
*/
|
||||
class CloseEvent extends Event {
|
||||
/**
|
||||
* Create a new `CloseEvent`.
|
||||
*
|
||||
* @param {String} type The name of the event
|
||||
* @param {Object} [options] A dictionary object that allows for setting
|
||||
* attributes via object members of the same name
|
||||
* @param {Number} [options.code=0] The status code explaining why the
|
||||
* connection was closed
|
||||
* @param {String} [options.reason=''] A human-readable string explaining why
|
||||
* the connection was closed
|
||||
* @param {Boolean} [options.wasClean=false] Indicates whether or not the
|
||||
* connection was cleanly closed
|
||||
*/
|
||||
constructor(type, options = {}) {
|
||||
super(type);
|
||||
|
||||
this[kCode] = options.code === undefined ? 0 : options.code;
|
||||
this[kReason] = options.reason === undefined ? '' : options.reason;
|
||||
this[kWasClean] = options.wasClean === undefined ? false : options.wasClean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Number}
|
||||
*/
|
||||
get code() {
|
||||
return this[kCode];
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {String}
|
||||
*/
|
||||
get reason() {
|
||||
return this[kReason];
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Boolean}
|
||||
*/
|
||||
get wasClean() {
|
||||
return this[kWasClean];
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(CloseEvent.prototype, 'code', { enumerable: true });
|
||||
Object.defineProperty(CloseEvent.prototype, 'reason', { enumerable: true });
|
||||
Object.defineProperty(CloseEvent.prototype, 'wasClean', { enumerable: true });
|
||||
|
||||
/**
|
||||
* Class representing an error event.
|
||||
*
|
||||
* @extends Event
|
||||
*/
|
||||
class ErrorEvent extends Event {
|
||||
/**
|
||||
* Create a new `ErrorEvent`.
|
||||
*
|
||||
* @param {String} type The name of the event
|
||||
* @param {Object} [options] A dictionary object that allows for setting
|
||||
* attributes via object members of the same name
|
||||
* @param {*} [options.error=null] The error that generated this event
|
||||
* @param {String} [options.message=''] The error message
|
||||
*/
|
||||
constructor(type, options = {}) {
|
||||
super(type);
|
||||
|
||||
this[kError] = options.error === undefined ? null : options.error;
|
||||
this[kMessage] = options.message === undefined ? '' : options.message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {*}
|
||||
*/
|
||||
get error() {
|
||||
return this[kError];
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {String}
|
||||
*/
|
||||
get message() {
|
||||
return this[kMessage];
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(ErrorEvent.prototype, 'error', { enumerable: true });
|
||||
Object.defineProperty(ErrorEvent.prototype, 'message', { enumerable: true });
|
||||
|
||||
/**
|
||||
* Class representing a message event.
|
||||
*
|
||||
* @extends Event
|
||||
*/
|
||||
class MessageEvent extends Event {
|
||||
/**
|
||||
* Create a new `MessageEvent`.
|
||||
*
|
||||
* @param {String} type The name of the event
|
||||
* @param {Object} [options] A dictionary object that allows for setting
|
||||
* attributes via object members of the same name
|
||||
* @param {*} [options.data=null] The message content
|
||||
*/
|
||||
constructor(type, options = {}) {
|
||||
super(type);
|
||||
|
||||
this[kData] = options.data === undefined ? null : options.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {*}
|
||||
*/
|
||||
get data() {
|
||||
return this[kData];
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(MessageEvent.prototype, 'data', { enumerable: true });
|
||||
|
||||
/**
|
||||
* This provides methods for emulating the `EventTarget` interface. It's not
|
||||
* meant to be used directly.
|
||||
*
|
||||
* @mixin
|
||||
*/
|
||||
const EventTarget = {
|
||||
/**
|
||||
* Register an event listener.
|
||||
*
|
||||
* @param {String} type A string representing the event type to listen for
|
||||
* @param {(Function|Object)} handler The listener to add
|
||||
* @param {Object} [options] An options object specifies characteristics about
|
||||
* the event listener
|
||||
* @param {Boolean} [options.once=false] A `Boolean` indicating that the
|
||||
* listener should be invoked at most once after being added. If `true`,
|
||||
* the listener would be automatically removed when invoked.
|
||||
* @public
|
||||
*/
|
||||
addEventListener(type, handler, options = {}) {
|
||||
for (const listener of this.listeners(type)) {
|
||||
if (
|
||||
!options[kForOnEventAttribute] &&
|
||||
listener[kListener] === handler &&
|
||||
!listener[kForOnEventAttribute]
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let wrapper;
|
||||
|
||||
if (type === 'message') {
|
||||
wrapper = function onMessage(data, isBinary) {
|
||||
const event = new MessageEvent('message', {
|
||||
data: isBinary ? data : data.toString()
|
||||
});
|
||||
|
||||
event[kTarget] = this;
|
||||
callListener(handler, this, event);
|
||||
};
|
||||
} else if (type === 'close') {
|
||||
wrapper = function onClose(code, message) {
|
||||
const event = new CloseEvent('close', {
|
||||
code,
|
||||
reason: message.toString(),
|
||||
wasClean: this._closeFrameReceived && this._closeFrameSent
|
||||
});
|
||||
|
||||
event[kTarget] = this;
|
||||
callListener(handler, this, event);
|
||||
};
|
||||
} else if (type === 'error') {
|
||||
wrapper = function onError(error) {
|
||||
const event = new ErrorEvent('error', {
|
||||
error,
|
||||
message: error.message
|
||||
});
|
||||
|
||||
event[kTarget] = this;
|
||||
callListener(handler, this, event);
|
||||
};
|
||||
} else if (type === 'open') {
|
||||
wrapper = function onOpen() {
|
||||
const event = new Event('open');
|
||||
|
||||
event[kTarget] = this;
|
||||
callListener(handler, this, event);
|
||||
};
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
wrapper[kForOnEventAttribute] = !!options[kForOnEventAttribute];
|
||||
wrapper[kListener] = handler;
|
||||
|
||||
if (options.once) {
|
||||
this.once(type, wrapper);
|
||||
} else {
|
||||
this.on(type, wrapper);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove an event listener.
|
||||
*
|
||||
* @param {String} type A string representing the event type to remove
|
||||
* @param {(Function|Object)} handler The listener to remove
|
||||
* @public
|
||||
*/
|
||||
removeEventListener(type, handler) {
|
||||
for (const listener of this.listeners(type)) {
|
||||
if (listener[kListener] === handler && !listener[kForOnEventAttribute]) {
|
||||
this.removeListener(type, listener);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
CloseEvent,
|
||||
ErrorEvent,
|
||||
Event,
|
||||
EventTarget,
|
||||
MessageEvent
|
||||
};
|
||||
|
||||
/**
|
||||
* Call an event listener
|
||||
*
|
||||
* @param {(Function|Object)} listener The listener to call
|
||||
* @param {*} thisArg The value to use as `this`` when calling the listener
|
||||
* @param {Event} event The event to pass to the listener
|
||||
* @private
|
||||
*/
|
||||
function callListener(listener, thisArg, event) {
|
||||
if (typeof listener === 'object' && listener.handleEvent) {
|
||||
listener.handleEvent.call(listener, event);
|
||||
} else {
|
||||
listener.call(thisArg, event);
|
||||
}
|
||||
}
|
||||
203
node_modules/engine.io/node_modules/ws/lib/extension.js
generated
vendored
Normal file
203
node_modules/engine.io/node_modules/ws/lib/extension.js
generated
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
'use strict';
|
||||
|
||||
const { tokenChars } = require('./validation');
|
||||
|
||||
/**
|
||||
* 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 (dest[name] === undefined) dest[name] = [elem];
|
||||
else dest[name].push(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 = Object.create(null);
|
||||
let params = Object.create(null);
|
||||
let mustUnescape = false;
|
||||
let isEscaping = false;
|
||||
let inQuotes = false;
|
||||
let extensionName;
|
||||
let paramName;
|
||||
let start = -1;
|
||||
let code = -1;
|
||||
let end = -1;
|
||||
let i = 0;
|
||||
|
||||
for (; i < header.length; i++) {
|
||||
code = header.charCodeAt(i);
|
||||
|
||||
if (extensionName === undefined) {
|
||||
if (end === -1 && tokenChars[code] === 1) {
|
||||
if (start === -1) start = i;
|
||||
} else if (
|
||||
i !== 0 &&
|
||||
(code === 0x20 /* ' ' */ || code === 0x09) /* '\t' */
|
||||
) {
|
||||
if (end === -1 && start !== -1) end = i;
|
||||
} else if (code === 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 = Object.create(null);
|
||||
} 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 = Object.create(null);
|
||||
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;
|
||||
let 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 = Object.create(null);
|
||||
extensionName = undefined;
|
||||
}
|
||||
|
||||
paramName = undefined;
|
||||
start = end = -1;
|
||||
} else {
|
||||
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (start === -1 || inQuotes || code === 0x20 || code === 0x09) {
|
||||
throw new SyntaxError('Unexpected end of input');
|
||||
}
|
||||
|
||||
if (end === -1) end = i;
|
||||
const token = header.slice(start, end);
|
||||
if (extensionName === undefined) {
|
||||
push(offers, token, params);
|
||||
} 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) => {
|
||||
let configurations = extensions[extension];
|
||||
if (!Array.isArray(configurations)) configurations = [configurations];
|
||||
return configurations
|
||||
.map((params) => {
|
||||
return [extension]
|
||||
.concat(
|
||||
Object.keys(params).map((k) => {
|
||||
let 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 };
|
||||
55
node_modules/engine.io/node_modules/ws/lib/limiter.js
generated
vendored
Normal file
55
node_modules/engine.io/node_modules/ws/lib/limiter.js
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
'use strict';
|
||||
|
||||
const kDone = Symbol('kDone');
|
||||
const kRun = Symbol('kRun');
|
||||
|
||||
/**
|
||||
* A very simple job queue with adjustable concurrency. Adapted from
|
||||
* https://github.com/STRML/async-limiter
|
||||
*/
|
||||
class Limiter {
|
||||
/**
|
||||
* Creates a new `Limiter`.
|
||||
*
|
||||
* @param {Number} [concurrency=Infinity] The maximum number of jobs allowed
|
||||
* to run concurrently
|
||||
*/
|
||||
constructor(concurrency) {
|
||||
this[kDone] = () => {
|
||||
this.pending--;
|
||||
this[kRun]();
|
||||
};
|
||||
this.concurrency = concurrency || Infinity;
|
||||
this.jobs = [];
|
||||
this.pending = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a job to the queue.
|
||||
*
|
||||
* @param {Function} job The job to run
|
||||
* @public
|
||||
*/
|
||||
add(job) {
|
||||
this.jobs.push(job);
|
||||
this[kRun]();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a job from the queue and runs it if possible.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
[kRun]() {
|
||||
if (this.pending === this.concurrency) return;
|
||||
|
||||
if (this.jobs.length) {
|
||||
const job = this.jobs.shift();
|
||||
|
||||
this.pending++;
|
||||
job(this[kDone]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Limiter;
|
||||
514
node_modules/engine.io/node_modules/ws/lib/permessage-deflate.js
generated
vendored
Normal file
514
node_modules/engine.io/node_modules/ws/lib/permessage-deflate.js
generated
vendored
Normal file
@@ -0,0 +1,514 @@
|
||||
'use strict';
|
||||
|
||||
const zlib = require('zlib');
|
||||
|
||||
const bufferUtil = require('./buffer-util');
|
||||
const Limiter = require('./limiter');
|
||||
const { kStatusCode } = require('./constants');
|
||||
|
||||
const FastBuffer = Buffer[Symbol.species];
|
||||
const TRAILER = Buffer.from([0x00, 0x00, 0xff, 0xff]);
|
||||
const kPerMessageDeflate = Symbol('permessage-deflate');
|
||||
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|Number)} [options.clientMaxWindowBits] Advertise support
|
||||
* for, or request, a custom client window size
|
||||
* @param {Boolean} [options.clientNoContextTakeover=false] Advertise/
|
||||
* acknowledge disabling of client context takeover
|
||||
* @param {Number} [options.concurrencyLimit=10] The number of concurrent
|
||||
* calls to zlib
|
||||
* @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the
|
||||
* use of a custom server window size
|
||||
* @param {Boolean} [options.serverNoContextTakeover=false] Request/accept
|
||||
* disabling of server context takeover
|
||||
* @param {Number} [options.threshold=1024] Size (in bytes) below which
|
||||
* messages should not be compressed if context takeover is disabled
|
||||
* @param {Object} [options.zlibDeflateOptions] Options to pass to zlib on
|
||||
* deflate
|
||||
* @param {Object} [options.zlibInflateOptions] Options to pass to zlib on
|
||||
* inflate
|
||||
* @param {Boolean} [isServer=false] Create the instance in either server or
|
||||
* client mode
|
||||
* @param {Number} [maxPayload=0] 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) {
|
||||
this._inflate.close();
|
||||
this._inflate = null;
|
||||
}
|
||||
|
||||
if (this._deflate) {
|
||||
const callback = this._deflate[kCallback];
|
||||
|
||||
this._deflate.close();
|
||||
this._deflate = null;
|
||||
|
||||
if (callback) {
|
||||
callback(
|
||||
new Error(
|
||||
'The deflate stream was closed while data was being processed'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) => {
|
||||
let 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.
|
||||
*
|
||||
* @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.add((done) => {
|
||||
this._decompress(data, fin, (err, result) => {
|
||||
done();
|
||||
callback(err, result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Compress data. Concurrency limited.
|
||||
*
|
||||
* @param {(Buffer|String)} 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.add((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({
|
||||
...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.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 (this._inflate._readableState.endEmitted) {
|
||||
this._inflate.close();
|
||||
this._inflate = null;
|
||||
} else {
|
||||
this._inflate[kTotalLength] = 0;
|
||||
this._inflate[kBuffers] = [];
|
||||
|
||||
if (fin && this.params[`${endpoint}_no_context_takeover`]) {
|
||||
this._inflate.reset();
|
||||
}
|
||||
}
|
||||
|
||||
callback(null, data);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Compress data.
|
||||
*
|
||||
* @param {(Buffer|String)} 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) {
|
||||
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({
|
||||
...this._options.zlibDeflateOptions,
|
||||
windowBits
|
||||
});
|
||||
|
||||
this._deflate[kTotalLength] = 0;
|
||||
this._deflate[kBuffers] = [];
|
||||
|
||||
this._deflate.on('data', deflateOnData);
|
||||
}
|
||||
|
||||
this._deflate[kCallback] = callback;
|
||||
|
||||
this._deflate.write(data);
|
||||
this._deflate.flush(zlib.Z_SYNC_FLUSH, () => {
|
||||
if (!this._deflate) {
|
||||
//
|
||||
// The deflate stream was closed while data was being processed.
|
||||
//
|
||||
return;
|
||||
}
|
||||
|
||||
let data = bufferUtil.concat(
|
||||
this._deflate[kBuffers],
|
||||
this._deflate[kTotalLength]
|
||||
);
|
||||
|
||||
if (fin) {
|
||||
data = new FastBuffer(data.buffer, data.byteOffset, data.length - 4);
|
||||
}
|
||||
|
||||
//
|
||||
// Ensure that the callback will not be called again in
|
||||
// `PerMessageDeflate#cleanup()`.
|
||||
//
|
||||
this._deflate[kCallback] = null;
|
||||
|
||||
this._deflate[kTotalLength] = 0;
|
||||
this._deflate[kBuffers] = [];
|
||||
|
||||
if (fin && this.params[`${endpoint}_no_context_takeover`]) {
|
||||
this._deflate.reset();
|
||||
}
|
||||
|
||||
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].code = 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH';
|
||||
this[kError][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[kStatusCode] = 1007;
|
||||
this[kCallback](err);
|
||||
}
|
||||
704
node_modules/engine.io/node_modules/ws/lib/receiver.js
generated
vendored
Normal file
704
node_modules/engine.io/node_modules/ws/lib/receiver.js
generated
vendored
Normal file
@@ -0,0 +1,704 @@
|
||||
'use strict';
|
||||
|
||||
const { Writable } = require('stream');
|
||||
|
||||
const PerMessageDeflate = require('./permessage-deflate');
|
||||
const {
|
||||
BINARY_TYPES,
|
||||
EMPTY_BUFFER,
|
||||
kStatusCode,
|
||||
kWebSocket
|
||||
} = require('./constants');
|
||||
const { concat, toArrayBuffer, unmask } = require('./buffer-util');
|
||||
const { isValidStatusCode, isValidUTF8 } = require('./validation');
|
||||
|
||||
const FastBuffer = Buffer[Symbol.species];
|
||||
|
||||
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;
|
||||
const DEFER_EVENT = 6;
|
||||
|
||||
/**
|
||||
* HyBi Receiver implementation.
|
||||
*
|
||||
* @extends Writable
|
||||
*/
|
||||
class Receiver extends Writable {
|
||||
/**
|
||||
* Creates a Receiver instance.
|
||||
*
|
||||
* @param {Object} [options] Options object
|
||||
* @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether
|
||||
* any of the `'message'`, `'ping'`, and `'pong'` events can be emitted
|
||||
* multiple times in the same tick
|
||||
* @param {String} [options.binaryType=nodebuffer] The type for binary data
|
||||
* @param {Object} [options.extensions] An object containing the negotiated
|
||||
* extensions
|
||||
* @param {Boolean} [options.isServer=false] Specifies whether to operate in
|
||||
* client or server mode
|
||||
* @param {Number} [options.maxPayload=0] The maximum allowed message length
|
||||
* @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
|
||||
* not to skip UTF-8 validation for text and close messages
|
||||
*/
|
||||
constructor(options = {}) {
|
||||
super();
|
||||
|
||||
this._allowSynchronousEvents =
|
||||
options.allowSynchronousEvents !== undefined
|
||||
? options.allowSynchronousEvents
|
||||
: true;
|
||||
this._binaryType = options.binaryType || BINARY_TYPES[0];
|
||||
this._extensions = options.extensions || {};
|
||||
this._isServer = !!options.isServer;
|
||||
this._maxPayload = options.maxPayload | 0;
|
||||
this._skipUTF8Validation = !!options.skipUTF8Validation;
|
||||
this[kWebSocket] = undefined;
|
||||
|
||||
this._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._errored = false;
|
||||
this._loop = false;
|
||||
this._state = GET_INFO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @private
|
||||
*/
|
||||
_write(chunk, encoding, cb) {
|
||||
if (this._opcode === 0x08 && this._state == GET_INFO) 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] = new FastBuffer(
|
||||
buf.buffer,
|
||||
buf.byteOffset + n,
|
||||
buf.length - n
|
||||
);
|
||||
|
||||
return new FastBuffer(buf.buffer, buf.byteOffset, n);
|
||||
}
|
||||
|
||||
const dst = Buffer.allocUnsafe(n);
|
||||
|
||||
do {
|
||||
const buf = this._buffers[0];
|
||||
const offset = dst.length - n;
|
||||
|
||||
if (n >= buf.length) {
|
||||
dst.set(this._buffers.shift(), offset);
|
||||
} else {
|
||||
dst.set(new Uint8Array(buf.buffer, buf.byteOffset, n), offset);
|
||||
this._buffers[0] = new FastBuffer(
|
||||
buf.buffer,
|
||||
buf.byteOffset + n,
|
||||
buf.length - n
|
||||
);
|
||||
}
|
||||
|
||||
n -= buf.length;
|
||||
} while (n > 0);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the parsing loop.
|
||||
*
|
||||
* @param {Function} cb Callback
|
||||
* @private
|
||||
*/
|
||||
startLoop(cb) {
|
||||
this._loop = true;
|
||||
|
||||
do {
|
||||
switch (this._state) {
|
||||
case GET_INFO:
|
||||
this.getInfo(cb);
|
||||
break;
|
||||
case GET_PAYLOAD_LENGTH_16:
|
||||
this.getPayloadLength16(cb);
|
||||
break;
|
||||
case GET_PAYLOAD_LENGTH_64:
|
||||
this.getPayloadLength64(cb);
|
||||
break;
|
||||
case GET_MASK:
|
||||
this.getMask();
|
||||
break;
|
||||
case GET_DATA:
|
||||
this.getData(cb);
|
||||
break;
|
||||
case INFLATING:
|
||||
case DEFER_EVENT:
|
||||
this._loop = false;
|
||||
return;
|
||||
}
|
||||
} while (this._loop);
|
||||
|
||||
if (!this._errored) cb();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the first two bytes of a frame.
|
||||
*
|
||||
* @param {Function} cb Callback
|
||||
* @private
|
||||
*/
|
||||
getInfo(cb) {
|
||||
if (this._bufferedBytes < 2) {
|
||||
this._loop = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const buf = this.consume(2);
|
||||
|
||||
if ((buf[0] & 0x30) !== 0x00) {
|
||||
const error = this.createError(
|
||||
RangeError,
|
||||
'RSV2 and RSV3 must be clear',
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_UNEXPECTED_RSV_2_3'
|
||||
);
|
||||
|
||||
cb(error);
|
||||
return;
|
||||
}
|
||||
|
||||
const compressed = (buf[0] & 0x40) === 0x40;
|
||||
|
||||
if (compressed && !this._extensions[PerMessageDeflate.extensionName]) {
|
||||
const error = this.createError(
|
||||
RangeError,
|
||||
'RSV1 must be clear',
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_UNEXPECTED_RSV_1'
|
||||
);
|
||||
|
||||
cb(error);
|
||||
return;
|
||||
}
|
||||
|
||||
this._fin = (buf[0] & 0x80) === 0x80;
|
||||
this._opcode = buf[0] & 0x0f;
|
||||
this._payloadLength = buf[1] & 0x7f;
|
||||
|
||||
if (this._opcode === 0x00) {
|
||||
if (compressed) {
|
||||
const error = this.createError(
|
||||
RangeError,
|
||||
'RSV1 must be clear',
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_UNEXPECTED_RSV_1'
|
||||
);
|
||||
|
||||
cb(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._fragmented) {
|
||||
const error = this.createError(
|
||||
RangeError,
|
||||
'invalid opcode 0',
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_INVALID_OPCODE'
|
||||
);
|
||||
|
||||
cb(error);
|
||||
return;
|
||||
}
|
||||
|
||||
this._opcode = this._fragmented;
|
||||
} else if (this._opcode === 0x01 || this._opcode === 0x02) {
|
||||
if (this._fragmented) {
|
||||
const error = this.createError(
|
||||
RangeError,
|
||||
`invalid opcode ${this._opcode}`,
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_INVALID_OPCODE'
|
||||
);
|
||||
|
||||
cb(error);
|
||||
return;
|
||||
}
|
||||
|
||||
this._compressed = compressed;
|
||||
} else if (this._opcode > 0x07 && this._opcode < 0x0b) {
|
||||
if (!this._fin) {
|
||||
const error = this.createError(
|
||||
RangeError,
|
||||
'FIN must be set',
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_EXPECTED_FIN'
|
||||
);
|
||||
|
||||
cb(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (compressed) {
|
||||
const error = this.createError(
|
||||
RangeError,
|
||||
'RSV1 must be clear',
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_UNEXPECTED_RSV_1'
|
||||
);
|
||||
|
||||
cb(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
this._payloadLength > 0x7d ||
|
||||
(this._opcode === 0x08 && this._payloadLength === 1)
|
||||
) {
|
||||
const error = this.createError(
|
||||
RangeError,
|
||||
`invalid payload length ${this._payloadLength}`,
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH'
|
||||
);
|
||||
|
||||
cb(error);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const error = this.createError(
|
||||
RangeError,
|
||||
`invalid opcode ${this._opcode}`,
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_INVALID_OPCODE'
|
||||
);
|
||||
|
||||
cb(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._fin && !this._fragmented) this._fragmented = this._opcode;
|
||||
this._masked = (buf[1] & 0x80) === 0x80;
|
||||
|
||||
if (this._isServer) {
|
||||
if (!this._masked) {
|
||||
const error = this.createError(
|
||||
RangeError,
|
||||
'MASK must be set',
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_EXPECTED_MASK'
|
||||
);
|
||||
|
||||
cb(error);
|
||||
return;
|
||||
}
|
||||
} else if (this._masked) {
|
||||
const error = this.createError(
|
||||
RangeError,
|
||||
'MASK must be clear',
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_UNEXPECTED_MASK'
|
||||
);
|
||||
|
||||
cb(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16;
|
||||
else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64;
|
||||
else this.haveLength(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets extended payload length (7+16).
|
||||
*
|
||||
* @param {Function} cb Callback
|
||||
* @private
|
||||
*/
|
||||
getPayloadLength16(cb) {
|
||||
if (this._bufferedBytes < 2) {
|
||||
this._loop = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this._payloadLength = this.consume(2).readUInt16BE(0);
|
||||
this.haveLength(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets extended payload length (7+64).
|
||||
*
|
||||
* @param {Function} cb Callback
|
||||
* @private
|
||||
*/
|
||||
getPayloadLength64(cb) {
|
||||
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) {
|
||||
const error = this.createError(
|
||||
RangeError,
|
||||
'Unsupported WebSocket frame: payload length > 2^53 - 1',
|
||||
false,
|
||||
1009,
|
||||
'WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH'
|
||||
);
|
||||
|
||||
cb(error);
|
||||
return;
|
||||
}
|
||||
|
||||
this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4);
|
||||
this.haveLength(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Payload length has been read.
|
||||
*
|
||||
* @param {Function} cb Callback
|
||||
* @private
|
||||
*/
|
||||
haveLength(cb) {
|
||||
if (this._payloadLength && this._opcode < 0x08) {
|
||||
this._totalPayloadLength += this._payloadLength;
|
||||
if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) {
|
||||
const error = this.createError(
|
||||
RangeError,
|
||||
'Max payload size exceeded',
|
||||
false,
|
||||
1009,
|
||||
'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'
|
||||
);
|
||||
|
||||
cb(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
* @private
|
||||
*/
|
||||
getData(cb) {
|
||||
let data = EMPTY_BUFFER;
|
||||
|
||||
if (this._payloadLength) {
|
||||
if (this._bufferedBytes < this._payloadLength) {
|
||||
this._loop = false;
|
||||
return;
|
||||
}
|
||||
|
||||
data = this.consume(this._payloadLength);
|
||||
|
||||
if (
|
||||
this._masked &&
|
||||
(this._mask[0] | this._mask[1] | this._mask[2] | this._mask[3]) !== 0
|
||||
) {
|
||||
unmask(data, this._mask);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._opcode > 0x07) {
|
||||
this.controlMessage(data, cb);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._compressed) {
|
||||
this._state = INFLATING;
|
||||
this.decompress(data, cb);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.length) {
|
||||
//
|
||||
// This message is not compressed so its length is the sum of the payload
|
||||
// length of all fragments.
|
||||
//
|
||||
this._messageLength = this._totalPayloadLength;
|
||||
this._fragments.push(data);
|
||||
}
|
||||
|
||||
this.dataMessage(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
const error = this.createError(
|
||||
RangeError,
|
||||
'Max payload size exceeded',
|
||||
false,
|
||||
1009,
|
||||
'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'
|
||||
);
|
||||
|
||||
cb(error);
|
||||
return;
|
||||
}
|
||||
|
||||
this._fragments.push(buf);
|
||||
}
|
||||
|
||||
this.dataMessage(cb);
|
||||
if (this._state === GET_INFO) this.startLoop(cb);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a data message.
|
||||
*
|
||||
* @param {Function} cb Callback
|
||||
* @private
|
||||
*/
|
||||
dataMessage(cb) {
|
||||
if (!this._fin) {
|
||||
this._state = GET_INFO;
|
||||
return;
|
||||
}
|
||||
|
||||
const messageLength = this._messageLength;
|
||||
const fragments = this._fragments;
|
||||
|
||||
this._totalPayloadLength = 0;
|
||||
this._messageLength = 0;
|
||||
this._fragmented = 0;
|
||||
this._fragments = [];
|
||||
|
||||
if (this._opcode === 2) {
|
||||
let data;
|
||||
|
||||
if (this._binaryType === 'nodebuffer') {
|
||||
data = concat(fragments, messageLength);
|
||||
} else if (this._binaryType === 'arraybuffer') {
|
||||
data = toArrayBuffer(concat(fragments, messageLength));
|
||||
} else {
|
||||
data = fragments;
|
||||
}
|
||||
|
||||
if (this._allowSynchronousEvents) {
|
||||
this.emit('message', data, true);
|
||||
this._state = GET_INFO;
|
||||
} else {
|
||||
this._state = DEFER_EVENT;
|
||||
setImmediate(() => {
|
||||
this.emit('message', data, true);
|
||||
this._state = GET_INFO;
|
||||
this.startLoop(cb);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const buf = concat(fragments, messageLength);
|
||||
|
||||
if (!this._skipUTF8Validation && !isValidUTF8(buf)) {
|
||||
const error = this.createError(
|
||||
Error,
|
||||
'invalid UTF-8 sequence',
|
||||
true,
|
||||
1007,
|
||||
'WS_ERR_INVALID_UTF8'
|
||||
);
|
||||
|
||||
cb(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._state === INFLATING || this._allowSynchronousEvents) {
|
||||
this.emit('message', buf, false);
|
||||
this._state = GET_INFO;
|
||||
} else {
|
||||
this._state = DEFER_EVENT;
|
||||
setImmediate(() => {
|
||||
this.emit('message', buf, false);
|
||||
this._state = GET_INFO;
|
||||
this.startLoop(cb);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a control message.
|
||||
*
|
||||
* @param {Buffer} data Data to handle
|
||||
* @return {(Error|RangeError|undefined)} A possible error
|
||||
* @private
|
||||
*/
|
||||
controlMessage(data, cb) {
|
||||
if (this._opcode === 0x08) {
|
||||
if (data.length === 0) {
|
||||
this._loop = false;
|
||||
this.emit('conclude', 1005, EMPTY_BUFFER);
|
||||
this.end();
|
||||
} else {
|
||||
const code = data.readUInt16BE(0);
|
||||
|
||||
if (!isValidStatusCode(code)) {
|
||||
const error = this.createError(
|
||||
RangeError,
|
||||
`invalid status code ${code}`,
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_INVALID_CLOSE_CODE'
|
||||
);
|
||||
|
||||
cb(error);
|
||||
return;
|
||||
}
|
||||
|
||||
const buf = new FastBuffer(
|
||||
data.buffer,
|
||||
data.byteOffset + 2,
|
||||
data.length - 2
|
||||
);
|
||||
|
||||
if (!this._skipUTF8Validation && !isValidUTF8(buf)) {
|
||||
const error = this.createError(
|
||||
Error,
|
||||
'invalid UTF-8 sequence',
|
||||
true,
|
||||
1007,
|
||||
'WS_ERR_INVALID_UTF8'
|
||||
);
|
||||
|
||||
cb(error);
|
||||
return;
|
||||
}
|
||||
|
||||
this._loop = false;
|
||||
this.emit('conclude', code, buf);
|
||||
this.end();
|
||||
}
|
||||
|
||||
this._state = GET_INFO;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._allowSynchronousEvents) {
|
||||
this.emit(this._opcode === 0x09 ? 'ping' : 'pong', data);
|
||||
this._state = GET_INFO;
|
||||
} else {
|
||||
this._state = DEFER_EVENT;
|
||||
setImmediate(() => {
|
||||
this.emit(this._opcode === 0x09 ? 'ping' : 'pong', data);
|
||||
this._state = GET_INFO;
|
||||
this.startLoop(cb);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an error object.
|
||||
*
|
||||
* @param {function(new:Error|RangeError)} ErrorCtor The error constructor
|
||||
* @param {String} message The error message
|
||||
* @param {Boolean} prefix Specifies whether or not to add a default prefix to
|
||||
* `message`
|
||||
* @param {Number} statusCode The status code
|
||||
* @param {String} errorCode The exposed error code
|
||||
* @return {(Error|RangeError)} The error
|
||||
* @private
|
||||
*/
|
||||
createError(ErrorCtor, message, prefix, statusCode, errorCode) {
|
||||
this._loop = false;
|
||||
this._errored = true;
|
||||
|
||||
const err = new ErrorCtor(
|
||||
prefix ? `Invalid WebSocket frame: ${message}` : message
|
||||
);
|
||||
|
||||
Error.captureStackTrace(err, this.createError);
|
||||
err.code = errorCode;
|
||||
err[kStatusCode] = statusCode;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Receiver;
|
||||
497
node_modules/engine.io/node_modules/ws/lib/sender.js
generated
vendored
Normal file
497
node_modules/engine.io/node_modules/ws/lib/sender.js
generated
vendored
Normal file
@@ -0,0 +1,497 @@
|
||||
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex" }] */
|
||||
|
||||
'use strict';
|
||||
|
||||
const { Duplex } = require('stream');
|
||||
const { randomFillSync } = require('crypto');
|
||||
|
||||
const PerMessageDeflate = require('./permessage-deflate');
|
||||
const { EMPTY_BUFFER } = require('./constants');
|
||||
const { isValidStatusCode } = require('./validation');
|
||||
const { mask: applyMask, toBuffer } = require('./buffer-util');
|
||||
|
||||
const kByteLength = Symbol('kByteLength');
|
||||
const maskBuffer = Buffer.alloc(4);
|
||||
const RANDOM_POOL_SIZE = 8 * 1024;
|
||||
let randomPool;
|
||||
let randomPoolPointer = RANDOM_POOL_SIZE;
|
||||
|
||||
/**
|
||||
* HyBi Sender implementation.
|
||||
*/
|
||||
class Sender {
|
||||
/**
|
||||
* Creates a Sender instance.
|
||||
*
|
||||
* @param {Duplex} socket The connection socket
|
||||
* @param {Object} [extensions] An object containing the negotiated extensions
|
||||
* @param {Function} [generateMask] The function used to generate the masking
|
||||
* key
|
||||
*/
|
||||
constructor(socket, extensions, generateMask) {
|
||||
this._extensions = extensions || {};
|
||||
|
||||
if (generateMask) {
|
||||
this._generateMask = generateMask;
|
||||
this._maskBuffer = Buffer.alloc(4);
|
||||
}
|
||||
|
||||
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|String)} data The data to frame
|
||||
* @param {Object} options Options object
|
||||
* @param {Boolean} [options.fin=false] Specifies whether or not to set the
|
||||
* FIN bit
|
||||
* @param {Function} [options.generateMask] The function used to generate the
|
||||
* masking key
|
||||
* @param {Boolean} [options.mask=false] Specifies whether or not to mask
|
||||
* `data`
|
||||
* @param {Buffer} [options.maskBuffer] The buffer used to store the masking
|
||||
* key
|
||||
* @param {Number} options.opcode The opcode
|
||||
* @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
|
||||
* modified
|
||||
* @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
|
||||
* RSV1 bit
|
||||
* @return {(Buffer|String)[]} The framed data
|
||||
* @public
|
||||
*/
|
||||
static frame(data, options) {
|
||||
let mask;
|
||||
let merge = false;
|
||||
let offset = 2;
|
||||
let skipMasking = false;
|
||||
|
||||
if (options.mask) {
|
||||
mask = options.maskBuffer || maskBuffer;
|
||||
|
||||
if (options.generateMask) {
|
||||
options.generateMask(mask);
|
||||
} else {
|
||||
if (randomPoolPointer === RANDOM_POOL_SIZE) {
|
||||
/* istanbul ignore else */
|
||||
if (randomPool === undefined) {
|
||||
//
|
||||
// This is lazily initialized because server-sent frames must not
|
||||
// be masked so it may never be used.
|
||||
//
|
||||
randomPool = Buffer.alloc(RANDOM_POOL_SIZE);
|
||||
}
|
||||
|
||||
randomFillSync(randomPool, 0, RANDOM_POOL_SIZE);
|
||||
randomPoolPointer = 0;
|
||||
}
|
||||
|
||||
mask[0] = randomPool[randomPoolPointer++];
|
||||
mask[1] = randomPool[randomPoolPointer++];
|
||||
mask[2] = randomPool[randomPoolPointer++];
|
||||
mask[3] = randomPool[randomPoolPointer++];
|
||||
}
|
||||
|
||||
skipMasking = (mask[0] | mask[1] | mask[2] | mask[3]) === 0;
|
||||
offset = 6;
|
||||
}
|
||||
|
||||
let dataLength;
|
||||
|
||||
if (typeof data === 'string') {
|
||||
if (
|
||||
(!options.mask || skipMasking) &&
|
||||
options[kByteLength] !== undefined
|
||||
) {
|
||||
dataLength = options[kByteLength];
|
||||
} else {
|
||||
data = Buffer.from(data);
|
||||
dataLength = data.length;
|
||||
}
|
||||
} else {
|
||||
dataLength = data.length;
|
||||
merge = options.mask && options.readOnly && !skipMasking;
|
||||
}
|
||||
|
||||
let payloadLength = dataLength;
|
||||
|
||||
if (dataLength >= 65536) {
|
||||
offset += 8;
|
||||
payloadLength = 127;
|
||||
} else if (dataLength > 125) {
|
||||
offset += 2;
|
||||
payloadLength = 126;
|
||||
}
|
||||
|
||||
const target = Buffer.allocUnsafe(merge ? dataLength + offset : offset);
|
||||
|
||||
target[0] = options.fin ? options.opcode | 0x80 : options.opcode;
|
||||
if (options.rsv1) target[0] |= 0x40;
|
||||
|
||||
target[1] = payloadLength;
|
||||
|
||||
if (payloadLength === 126) {
|
||||
target.writeUInt16BE(dataLength, 2);
|
||||
} else if (payloadLength === 127) {
|
||||
target[2] = target[3] = 0;
|
||||
target.writeUIntBE(dataLength, 4, 6);
|
||||
}
|
||||
|
||||
if (!options.mask) return [target, data];
|
||||
|
||||
target[1] |= 0x80;
|
||||
target[offset - 4] = mask[0];
|
||||
target[offset - 3] = mask[1];
|
||||
target[offset - 2] = mask[2];
|
||||
target[offset - 1] = mask[3];
|
||||
|
||||
if (skipMasking) return [target, data];
|
||||
|
||||
if (merge) {
|
||||
applyMask(data, mask, target, offset, dataLength);
|
||||
return [target];
|
||||
}
|
||||
|
||||
applyMask(data, mask, data, 0, dataLength);
|
||||
return [target, data];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a close message to the other peer.
|
||||
*
|
||||
* @param {Number} [code] The status code component of the body
|
||||
* @param {(String|Buffer)} [data] The message component of the body
|
||||
* @param {Boolean} [mask=false] Specifies whether or not to mask the message
|
||||
* @param {Function} [cb] Callback
|
||||
* @public
|
||||
*/
|
||||
close(code, data, mask, cb) {
|
||||
let buf;
|
||||
|
||||
if (code === undefined) {
|
||||
buf = EMPTY_BUFFER;
|
||||
} else if (typeof code !== 'number' || !isValidStatusCode(code)) {
|
||||
throw new TypeError('First argument must be a valid error code number');
|
||||
} else if (data === undefined || !data.length) {
|
||||
buf = Buffer.allocUnsafe(2);
|
||||
buf.writeUInt16BE(code, 0);
|
||||
} else {
|
||||
const length = Buffer.byteLength(data);
|
||||
|
||||
if (length > 123) {
|
||||
throw new RangeError('The message must not be greater than 123 bytes');
|
||||
}
|
||||
|
||||
buf = Buffer.allocUnsafe(2 + length);
|
||||
buf.writeUInt16BE(code, 0);
|
||||
|
||||
if (typeof data === 'string') {
|
||||
buf.write(data, 2);
|
||||
} else {
|
||||
buf.set(data, 2);
|
||||
}
|
||||
}
|
||||
|
||||
const options = {
|
||||
[kByteLength]: buf.length,
|
||||
fin: true,
|
||||
generateMask: this._generateMask,
|
||||
mask,
|
||||
maskBuffer: this._maskBuffer,
|
||||
opcode: 0x08,
|
||||
readOnly: false,
|
||||
rsv1: false
|
||||
};
|
||||
|
||||
if (this._deflating) {
|
||||
this.enqueue([this.dispatch, buf, false, options, cb]);
|
||||
} else {
|
||||
this.sendFrame(Sender.frame(buf, options), cb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a ping message to the other peer.
|
||||
*
|
||||
* @param {*} data The message to send
|
||||
* @param {Boolean} [mask=false] Specifies whether or not to mask `data`
|
||||
* @param {Function} [cb] Callback
|
||||
* @public
|
||||
*/
|
||||
ping(data, mask, cb) {
|
||||
let byteLength;
|
||||
let readOnly;
|
||||
|
||||
if (typeof data === 'string') {
|
||||
byteLength = Buffer.byteLength(data);
|
||||
readOnly = false;
|
||||
} else {
|
||||
data = toBuffer(data);
|
||||
byteLength = data.length;
|
||||
readOnly = toBuffer.readOnly;
|
||||
}
|
||||
|
||||
if (byteLength > 125) {
|
||||
throw new RangeError('The data size must not be greater than 125 bytes');
|
||||
}
|
||||
|
||||
const options = {
|
||||
[kByteLength]: byteLength,
|
||||
fin: true,
|
||||
generateMask: this._generateMask,
|
||||
mask,
|
||||
maskBuffer: this._maskBuffer,
|
||||
opcode: 0x09,
|
||||
readOnly,
|
||||
rsv1: false
|
||||
};
|
||||
|
||||
if (this._deflating) {
|
||||
this.enqueue([this.dispatch, data, false, options, cb]);
|
||||
} else {
|
||||
this.sendFrame(Sender.frame(data, options), cb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a pong message to the other peer.
|
||||
*
|
||||
* @param {*} data The message to send
|
||||
* @param {Boolean} [mask=false] Specifies whether or not to mask `data`
|
||||
* @param {Function} [cb] Callback
|
||||
* @public
|
||||
*/
|
||||
pong(data, mask, cb) {
|
||||
let byteLength;
|
||||
let readOnly;
|
||||
|
||||
if (typeof data === 'string') {
|
||||
byteLength = Buffer.byteLength(data);
|
||||
readOnly = false;
|
||||
} else {
|
||||
data = toBuffer(data);
|
||||
byteLength = data.length;
|
||||
readOnly = toBuffer.readOnly;
|
||||
}
|
||||
|
||||
if (byteLength > 125) {
|
||||
throw new RangeError('The data size must not be greater than 125 bytes');
|
||||
}
|
||||
|
||||
const options = {
|
||||
[kByteLength]: byteLength,
|
||||
fin: true,
|
||||
generateMask: this._generateMask,
|
||||
mask,
|
||||
maskBuffer: this._maskBuffer,
|
||||
opcode: 0x0a,
|
||||
readOnly,
|
||||
rsv1: false
|
||||
};
|
||||
|
||||
if (this._deflating) {
|
||||
this.enqueue([this.dispatch, data, false, options, cb]);
|
||||
} else {
|
||||
this.sendFrame(Sender.frame(data, options), cb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a data message to the other peer.
|
||||
*
|
||||
* @param {*} data The message to send
|
||||
* @param {Object} options Options object
|
||||
* @param {Boolean} [options.binary=false] Specifies whether `data` is binary
|
||||
* or text
|
||||
* @param {Boolean} [options.compress=false] Specifies whether or not to
|
||||
* compress `data`
|
||||
* @param {Boolean} [options.fin=false] Specifies whether the fragment is the
|
||||
* last one
|
||||
* @param {Boolean} [options.mask=false] Specifies whether or not to mask
|
||||
* `data`
|
||||
* @param {Function} [cb] Callback
|
||||
* @public
|
||||
*/
|
||||
send(data, options, cb) {
|
||||
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
|
||||
let opcode = options.binary ? 2 : 1;
|
||||
let rsv1 = options.compress;
|
||||
|
||||
let byteLength;
|
||||
let readOnly;
|
||||
|
||||
if (typeof data === 'string') {
|
||||
byteLength = Buffer.byteLength(data);
|
||||
readOnly = false;
|
||||
} else {
|
||||
data = toBuffer(data);
|
||||
byteLength = data.length;
|
||||
readOnly = toBuffer.readOnly;
|
||||
}
|
||||
|
||||
if (this._firstFragment) {
|
||||
this._firstFragment = false;
|
||||
if (
|
||||
rsv1 &&
|
||||
perMessageDeflate &&
|
||||
perMessageDeflate.params[
|
||||
perMessageDeflate._isServer
|
||||
? 'server_no_context_takeover'
|
||||
: 'client_no_context_takeover'
|
||||
]
|
||||
) {
|
||||
rsv1 = byteLength >= perMessageDeflate._threshold;
|
||||
}
|
||||
this._compress = rsv1;
|
||||
} else {
|
||||
rsv1 = false;
|
||||
opcode = 0;
|
||||
}
|
||||
|
||||
if (options.fin) this._firstFragment = true;
|
||||
|
||||
if (perMessageDeflate) {
|
||||
const opts = {
|
||||
[kByteLength]: byteLength,
|
||||
fin: options.fin,
|
||||
generateMask: this._generateMask,
|
||||
mask: options.mask,
|
||||
maskBuffer: this._maskBuffer,
|
||||
opcode,
|
||||
readOnly,
|
||||
rsv1
|
||||
};
|
||||
|
||||
if (this._deflating) {
|
||||
this.enqueue([this.dispatch, data, this._compress, opts, cb]);
|
||||
} else {
|
||||
this.dispatch(data, this._compress, opts, cb);
|
||||
}
|
||||
} else {
|
||||
this.sendFrame(
|
||||
Sender.frame(data, {
|
||||
[kByteLength]: byteLength,
|
||||
fin: options.fin,
|
||||
generateMask: this._generateMask,
|
||||
mask: options.mask,
|
||||
maskBuffer: this._maskBuffer,
|
||||
opcode,
|
||||
readOnly,
|
||||
rsv1: false
|
||||
}),
|
||||
cb
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches a message.
|
||||
*
|
||||
* @param {(Buffer|String)} data The message to send
|
||||
* @param {Boolean} [compress=false] Specifies whether or not to compress
|
||||
* `data`
|
||||
* @param {Object} options Options object
|
||||
* @param {Boolean} [options.fin=false] Specifies whether or not to set the
|
||||
* FIN bit
|
||||
* @param {Function} [options.generateMask] The function used to generate the
|
||||
* masking key
|
||||
* @param {Boolean} [options.mask=false] Specifies whether or not to mask
|
||||
* `data`
|
||||
* @param {Buffer} [options.maskBuffer] The buffer used to store the masking
|
||||
* key
|
||||
* @param {Number} options.opcode The opcode
|
||||
* @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
|
||||
* modified
|
||||
* @param {Boolean} [options.rsv1=false] 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._bufferedBytes += options[kByteLength];
|
||||
this._deflating = true;
|
||||
perMessageDeflate.compress(data, options.fin, (_, buf) => {
|
||||
if (this._socket.destroyed) {
|
||||
const err = new Error(
|
||||
'The socket was closed while data was being compressed'
|
||||
);
|
||||
|
||||
if (typeof cb === 'function') cb(err);
|
||||
|
||||
for (let i = 0; i < this._queue.length; i++) {
|
||||
const params = this._queue[i];
|
||||
const callback = params[params.length - 1];
|
||||
|
||||
if (typeof callback === 'function') callback(err);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this._bufferedBytes -= options[kByteLength];
|
||||
this._deflating = false;
|
||||
options.readOnly = false;
|
||||
this.sendFrame(Sender.frame(buf, options), cb);
|
||||
this.dequeue();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes queued send operations.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
dequeue() {
|
||||
while (!this._deflating && this._queue.length) {
|
||||
const params = this._queue.shift();
|
||||
|
||||
this._bufferedBytes -= params[3][kByteLength];
|
||||
Reflect.apply(params[0], this, params.slice(1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues a send operation.
|
||||
*
|
||||
* @param {Array} params Send operation parameters.
|
||||
* @private
|
||||
*/
|
||||
enqueue(params) {
|
||||
this._bufferedBytes += params[3][kByteLength];
|
||||
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.cork();
|
||||
this._socket.write(list[0]);
|
||||
this._socket.write(list[1], cb);
|
||||
this._socket.uncork();
|
||||
} else {
|
||||
this._socket.write(list[0], cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Sender;
|
||||
159
node_modules/engine.io/node_modules/ws/lib/stream.js
generated
vendored
Normal file
159
node_modules/engine.io/node_modules/ws/lib/stream.js
generated
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
'use strict';
|
||||
|
||||
const { Duplex } = require('stream');
|
||||
|
||||
/**
|
||||
* Emits the `'close'` event on a stream.
|
||||
*
|
||||
* @param {Duplex} stream The stream.
|
||||
* @private
|
||||
*/
|
||||
function emitClose(stream) {
|
||||
stream.emit('close');
|
||||
}
|
||||
|
||||
/**
|
||||
* The listener of the `'end'` event.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function duplexOnEnd() {
|
||||
if (!this.destroyed && this._writableState.finished) {
|
||||
this.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The listener of the `'error'` event.
|
||||
*
|
||||
* @param {Error} err The error
|
||||
* @private
|
||||
*/
|
||||
function duplexOnError(err) {
|
||||
this.removeListener('error', duplexOnError);
|
||||
this.destroy();
|
||||
if (this.listenerCount('error') === 0) {
|
||||
// Do not suppress the throwing behavior.
|
||||
this.emit('error', err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a `WebSocket` in a duplex stream.
|
||||
*
|
||||
* @param {WebSocket} ws The `WebSocket` to wrap
|
||||
* @param {Object} [options] The options for the `Duplex` constructor
|
||||
* @return {Duplex} The duplex stream
|
||||
* @public
|
||||
*/
|
||||
function createWebSocketStream(ws, options) {
|
||||
let terminateOnDestroy = true;
|
||||
|
||||
const duplex = new Duplex({
|
||||
...options,
|
||||
autoDestroy: false,
|
||||
emitClose: false,
|
||||
objectMode: false,
|
||||
writableObjectMode: false
|
||||
});
|
||||
|
||||
ws.on('message', function message(msg, isBinary) {
|
||||
const data =
|
||||
!isBinary && duplex._readableState.objectMode ? msg.toString() : msg;
|
||||
|
||||
if (!duplex.push(data)) ws.pause();
|
||||
});
|
||||
|
||||
ws.once('error', function error(err) {
|
||||
if (duplex.destroyed) return;
|
||||
|
||||
// Prevent `ws.terminate()` from being called by `duplex._destroy()`.
|
||||
//
|
||||
// - If the `'error'` event is emitted before the `'open'` event, then
|
||||
// `ws.terminate()` is a noop as no socket is assigned.
|
||||
// - Otherwise, the error is re-emitted by the listener of the `'error'`
|
||||
// event of the `Receiver` object. The listener already closes the
|
||||
// connection by calling `ws.close()`. This allows a close frame to be
|
||||
// sent to the other peer. If `ws.terminate()` is called right after this,
|
||||
// then the close frame might not be sent.
|
||||
terminateOnDestroy = false;
|
||||
duplex.destroy(err);
|
||||
});
|
||||
|
||||
ws.once('close', function close() {
|
||||
if (duplex.destroyed) return;
|
||||
|
||||
duplex.push(null);
|
||||
});
|
||||
|
||||
duplex._destroy = function (err, callback) {
|
||||
if (ws.readyState === ws.CLOSED) {
|
||||
callback(err);
|
||||
process.nextTick(emitClose, duplex);
|
||||
return;
|
||||
}
|
||||
|
||||
let called = false;
|
||||
|
||||
ws.once('error', function error(err) {
|
||||
called = true;
|
||||
callback(err);
|
||||
});
|
||||
|
||||
ws.once('close', function close() {
|
||||
if (!called) callback(err);
|
||||
process.nextTick(emitClose, duplex);
|
||||
});
|
||||
|
||||
if (terminateOnDestroy) ws.terminate();
|
||||
};
|
||||
|
||||
duplex._final = function (callback) {
|
||||
if (ws.readyState === ws.CONNECTING) {
|
||||
ws.once('open', function open() {
|
||||
duplex._final(callback);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// If the value of the `_socket` property is `null` it means that `ws` is a
|
||||
// client websocket and the handshake failed. In fact, when this happens, a
|
||||
// socket is never assigned to the websocket. Wait for the `'error'` event
|
||||
// that will be emitted by the websocket.
|
||||
if (ws._socket === null) return;
|
||||
|
||||
if (ws._socket._writableState.finished) {
|
||||
callback();
|
||||
if (duplex._readableState.endEmitted) duplex.destroy();
|
||||
} else {
|
||||
ws._socket.once('finish', function finish() {
|
||||
// `duplex` is not destroyed here because the `'end'` event will be
|
||||
// emitted on `duplex` after this `'finish'` event. The EOF signaling
|
||||
// `null` chunk is, in fact, pushed when the websocket emits `'close'`.
|
||||
callback();
|
||||
});
|
||||
ws.close();
|
||||
}
|
||||
};
|
||||
|
||||
duplex._read = function () {
|
||||
if (ws.isPaused) ws.resume();
|
||||
};
|
||||
|
||||
duplex._write = function (chunk, encoding, callback) {
|
||||
if (ws.readyState === ws.CONNECTING) {
|
||||
ws.once('open', function open() {
|
||||
duplex._write(chunk, encoding, callback);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
ws.send(chunk, callback);
|
||||
};
|
||||
|
||||
duplex.on('end', duplexOnEnd);
|
||||
duplex.on('error', duplexOnError);
|
||||
return duplex;
|
||||
}
|
||||
|
||||
module.exports = createWebSocketStream;
|
||||
62
node_modules/engine.io/node_modules/ws/lib/subprotocol.js
generated
vendored
Normal file
62
node_modules/engine.io/node_modules/ws/lib/subprotocol.js
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
'use strict';
|
||||
|
||||
const { tokenChars } = require('./validation');
|
||||
|
||||
/**
|
||||
* Parses the `Sec-WebSocket-Protocol` header into a set of subprotocol names.
|
||||
*
|
||||
* @param {String} header The field value of the header
|
||||
* @return {Set} The subprotocol names
|
||||
* @public
|
||||
*/
|
||||
function parse(header) {
|
||||
const protocols = new Set();
|
||||
let start = -1;
|
||||
let end = -1;
|
||||
let i = 0;
|
||||
|
||||
for (i; i < header.length; i++) {
|
||||
const code = header.charCodeAt(i);
|
||||
|
||||
if (end === -1 && tokenChars[code] === 1) {
|
||||
if (start === -1) start = i;
|
||||
} else if (
|
||||
i !== 0 &&
|
||||
(code === 0x20 /* ' ' */ || code === 0x09) /* '\t' */
|
||||
) {
|
||||
if (end === -1 && start !== -1) end = i;
|
||||
} else if (code === 0x2c /* ',' */) {
|
||||
if (start === -1) {
|
||||
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||
}
|
||||
|
||||
if (end === -1) end = i;
|
||||
|
||||
const protocol = header.slice(start, end);
|
||||
|
||||
if (protocols.has(protocol)) {
|
||||
throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
|
||||
}
|
||||
|
||||
protocols.add(protocol);
|
||||
start = end = -1;
|
||||
} else {
|
||||
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (start === -1 || end !== -1) {
|
||||
throw new SyntaxError('Unexpected end of input');
|
||||
}
|
||||
|
||||
const protocol = header.slice(start, i);
|
||||
|
||||
if (protocols.has(protocol)) {
|
||||
throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
|
||||
}
|
||||
|
||||
protocols.add(protocol);
|
||||
return protocols;
|
||||
}
|
||||
|
||||
module.exports = { parse };
|
||||
130
node_modules/engine.io/node_modules/ws/lib/validation.js
generated
vendored
Normal file
130
node_modules/engine.io/node_modules/ws/lib/validation.js
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
'use strict';
|
||||
|
||||
const { isUtf8 } = require('buffer');
|
||||
|
||||
//
|
||||
// Allowed token characters:
|
||||
//
|
||||
// '!', '#', '$', '%', '&', ''', '*', '+', '-',
|
||||
// '.', 0-9, A-Z, '^', '_', '`', a-z, '|', '~'
|
||||
//
|
||||
// tokenChars[32] === 0 // ' '
|
||||
// tokenChars[33] === 1 // '!'
|
||||
// tokenChars[34] === 0 // '"'
|
||||
// ...
|
||||
//
|
||||
// prettier-ignore
|
||||
const tokenChars = [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31
|
||||
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32 - 47
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80 - 95
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 // 112 - 127
|
||||
];
|
||||
|
||||
/**
|
||||
* Checks if a status code is allowed in a close frame.
|
||||
*
|
||||
* @param {Number} code The status code
|
||||
* @return {Boolean} `true` if the status code is valid, else `false`
|
||||
* @public
|
||||
*/
|
||||
function isValidStatusCode(code) {
|
||||
return (
|
||||
(code >= 1000 &&
|
||||
code <= 1014 &&
|
||||
code !== 1004 &&
|
||||
code !== 1005 &&
|
||||
code !== 1006) ||
|
||||
(code >= 3000 && code <= 4999)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given buffer contains only correct UTF-8.
|
||||
* Ported from https://www.cl.cam.ac.uk/%7Emgk25/ucs/utf8_check.c by
|
||||
* Markus Kuhn.
|
||||
*
|
||||
* @param {Buffer} buf The buffer to check
|
||||
* @return {Boolean} `true` if `buf` contains only correct UTF-8, else `false`
|
||||
* @public
|
||||
*/
|
||||
function _isValidUTF8(buf) {
|
||||
const len = buf.length;
|
||||
let i = 0;
|
||||
|
||||
while (i < len) {
|
||||
if ((buf[i] & 0x80) === 0) {
|
||||
// 0xxxxxxx
|
||||
i++;
|
||||
} else if ((buf[i] & 0xe0) === 0xc0) {
|
||||
// 110xxxxx 10xxxxxx
|
||||
if (
|
||||
i + 1 === len ||
|
||||
(buf[i + 1] & 0xc0) !== 0x80 ||
|
||||
(buf[i] & 0xfe) === 0xc0 // Overlong
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
i += 2;
|
||||
} else if ((buf[i] & 0xf0) === 0xe0) {
|
||||
// 1110xxxx 10xxxxxx 10xxxxxx
|
||||
if (
|
||||
i + 2 >= len ||
|
||||
(buf[i + 1] & 0xc0) !== 0x80 ||
|
||||
(buf[i + 2] & 0xc0) !== 0x80 ||
|
||||
(buf[i] === 0xe0 && (buf[i + 1] & 0xe0) === 0x80) || // Overlong
|
||||
(buf[i] === 0xed && (buf[i + 1] & 0xe0) === 0xa0) // Surrogate (U+D800 - U+DFFF)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
i += 3;
|
||||
} else if ((buf[i] & 0xf8) === 0xf0) {
|
||||
// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
if (
|
||||
i + 3 >= len ||
|
||||
(buf[i + 1] & 0xc0) !== 0x80 ||
|
||||
(buf[i + 2] & 0xc0) !== 0x80 ||
|
||||
(buf[i + 3] & 0xc0) !== 0x80 ||
|
||||
(buf[i] === 0xf0 && (buf[i + 1] & 0xf0) === 0x80) || // Overlong
|
||||
(buf[i] === 0xf4 && buf[i + 1] > 0x8f) ||
|
||||
buf[i] > 0xf4 // > U+10FFFF
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
i += 4;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isValidStatusCode,
|
||||
isValidUTF8: _isValidUTF8,
|
||||
tokenChars
|
||||
};
|
||||
|
||||
if (isUtf8) {
|
||||
module.exports.isValidUTF8 = function (buf) {
|
||||
return buf.length < 24 ? _isValidUTF8(buf) : isUtf8(buf);
|
||||
};
|
||||
} /* istanbul ignore else */ else if (!process.env.WS_NO_UTF_8_VALIDATE) {
|
||||
try {
|
||||
const isValidUTF8 = require('utf-8-validate');
|
||||
|
||||
module.exports.isValidUTF8 = function (buf) {
|
||||
return buf.length < 32 ? _isValidUTF8(buf) : isValidUTF8(buf);
|
||||
};
|
||||
} catch (e) {
|
||||
// Continue regardless of the error.
|
||||
}
|
||||
}
|
||||
540
node_modules/engine.io/node_modules/ws/lib/websocket-server.js
generated
vendored
Normal file
540
node_modules/engine.io/node_modules/ws/lib/websocket-server.js
generated
vendored
Normal file
@@ -0,0 +1,540 @@
|
||||
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex$", "caughtErrors": "none" }] */
|
||||
|
||||
'use strict';
|
||||
|
||||
const EventEmitter = require('events');
|
||||
const http = require('http');
|
||||
const { Duplex } = require('stream');
|
||||
const { createHash } = require('crypto');
|
||||
|
||||
const extension = require('./extension');
|
||||
const PerMessageDeflate = require('./permessage-deflate');
|
||||
const subprotocol = require('./subprotocol');
|
||||
const WebSocket = require('./websocket');
|
||||
const { GUID, kWebSocket } = require('./constants');
|
||||
|
||||
const keyRegex = /^[+/0-9A-Za-z]{22}==$/;
|
||||
|
||||
const RUNNING = 0;
|
||||
const CLOSING = 1;
|
||||
const CLOSED = 2;
|
||||
|
||||
/**
|
||||
* Class representing a WebSocket server.
|
||||
*
|
||||
* @extends EventEmitter
|
||||
*/
|
||||
class WebSocketServer extends EventEmitter {
|
||||
/**
|
||||
* Create a `WebSocketServer` instance.
|
||||
*
|
||||
* @param {Object} options Configuration options
|
||||
* @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether
|
||||
* any of the `'message'`, `'ping'`, and `'pong'` events can be emitted
|
||||
* multiple times in the same tick
|
||||
* @param {Boolean} [options.autoPong=true] Specifies whether or not to
|
||||
* automatically send a pong in response to a ping
|
||||
* @param {Number} [options.backlog=511] The maximum length of the queue of
|
||||
* pending connections
|
||||
* @param {Boolean} [options.clientTracking=true] Specifies whether or not to
|
||||
* track clients
|
||||
* @param {Function} [options.handleProtocols] A hook to handle protocols
|
||||
* @param {String} [options.host] The hostname where to bind the server
|
||||
* @param {Number} [options.maxPayload=104857600] The maximum allowed message
|
||||
* size
|
||||
* @param {Boolean} [options.noServer=false] Enable no server mode
|
||||
* @param {String} [options.path] Accept only connections matching this path
|
||||
* @param {(Boolean|Object)} [options.perMessageDeflate=false] Enable/disable
|
||||
* permessage-deflate
|
||||
* @param {Number} [options.port] The port where to bind the server
|
||||
* @param {(http.Server|https.Server)} [options.server] A pre-created HTTP/S
|
||||
* server to use
|
||||
* @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
|
||||
* not to skip UTF-8 validation for text and close messages
|
||||
* @param {Function} [options.verifyClient] A hook to reject connections
|
||||
* @param {Function} [options.WebSocket=WebSocket] Specifies the `WebSocket`
|
||||
* class to use. It must be the `WebSocket` class or class that extends it
|
||||
* @param {Function} [callback] A listener for the `listening` event
|
||||
*/
|
||||
constructor(options, callback) {
|
||||
super();
|
||||
|
||||
options = {
|
||||
allowSynchronousEvents: true,
|
||||
autoPong: true,
|
||||
maxPayload: 100 * 1024 * 1024,
|
||||
skipUTF8Validation: false,
|
||||
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,
|
||||
WebSocket,
|
||||
...options
|
||||
};
|
||||
|
||||
if (
|
||||
(options.port == null && !options.server && !options.noServer) ||
|
||||
(options.port != null && (options.server || options.noServer)) ||
|
||||
(options.server && options.noServer)
|
||||
) {
|
||||
throw new TypeError(
|
||||
'One and only 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) {
|
||||
const emitConnection = this.emit.bind(this, 'connection');
|
||||
|
||||
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, emitConnection);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (options.perMessageDeflate === true) options.perMessageDeflate = {};
|
||||
if (options.clientTracking) {
|
||||
this.clients = new Set();
|
||||
this._shouldEmitClose = false;
|
||||
}
|
||||
|
||||
this.options = options;
|
||||
this._state = RUNNING;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the server from accepting new connections and emit the `'close'` event
|
||||
* when all existing connections are closed.
|
||||
*
|
||||
* @param {Function} [cb] A one-time listener for the `'close'` event
|
||||
* @public
|
||||
*/
|
||||
close(cb) {
|
||||
if (this._state === CLOSED) {
|
||||
if (cb) {
|
||||
this.once('close', () => {
|
||||
cb(new Error('The server is not running'));
|
||||
});
|
||||
}
|
||||
|
||||
process.nextTick(emitClose, this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cb) this.once('close', cb);
|
||||
|
||||
if (this._state === CLOSING) return;
|
||||
this._state = CLOSING;
|
||||
|
||||
if (this.options.noServer || this.options.server) {
|
||||
if (this._server) {
|
||||
this._removeListeners();
|
||||
this._removeListeners = this._server = null;
|
||||
}
|
||||
|
||||
if (this.clients) {
|
||||
if (!this.clients.size) {
|
||||
process.nextTick(emitClose, this);
|
||||
} else {
|
||||
this._shouldEmitClose = true;
|
||||
}
|
||||
} else {
|
||||
process.nextTick(emitClose, this);
|
||||
}
|
||||
} else {
|
||||
const server = this._server;
|
||||
|
||||
this._removeListeners();
|
||||
this._removeListeners = this._server = null;
|
||||
|
||||
//
|
||||
// The HTTP/S server was created internally. Close it, and rely on its
|
||||
// `'close'` event.
|
||||
//
|
||||
server.close(() => {
|
||||
emitClose(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
const index = req.url.indexOf('?');
|
||||
const pathname = index !== -1 ? req.url.slice(0, index) : req.url;
|
||||
|
||||
if (pathname !== this.options.path) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a HTTP Upgrade request.
|
||||
*
|
||||
* @param {http.IncomingMessage} req The request object
|
||||
* @param {Duplex} 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 key = req.headers['sec-websocket-key'];
|
||||
const upgrade = req.headers.upgrade;
|
||||
const version = +req.headers['sec-websocket-version'];
|
||||
|
||||
if (req.method !== 'GET') {
|
||||
const message = 'Invalid HTTP method';
|
||||
abortHandshakeOrEmitwsClientError(this, req, socket, 405, message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (upgrade === undefined || upgrade.toLowerCase() !== 'websocket') {
|
||||
const message = 'Invalid Upgrade header';
|
||||
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (key === undefined || !keyRegex.test(key)) {
|
||||
const message = 'Missing or invalid Sec-WebSocket-Key header';
|
||||
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (version !== 8 && version !== 13) {
|
||||
const message = 'Missing or invalid Sec-WebSocket-Version header';
|
||||
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.shouldHandle(req)) {
|
||||
abortHandshake(socket, 400);
|
||||
return;
|
||||
}
|
||||
|
||||
const secWebSocketProtocol = req.headers['sec-websocket-protocol'];
|
||||
let protocols = new Set();
|
||||
|
||||
if (secWebSocketProtocol !== undefined) {
|
||||
try {
|
||||
protocols = subprotocol.parse(secWebSocketProtocol);
|
||||
} catch (err) {
|
||||
const message = 'Invalid Sec-WebSocket-Protocol header';
|
||||
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const secWebSocketExtensions = req.headers['sec-websocket-extensions'];
|
||||
const extensions = {};
|
||||
|
||||
if (
|
||||
this.options.perMessageDeflate &&
|
||||
secWebSocketExtensions !== undefined
|
||||
) {
|
||||
const perMessageDeflate = new PerMessageDeflate(
|
||||
this.options.perMessageDeflate,
|
||||
true,
|
||||
this.options.maxPayload
|
||||
);
|
||||
|
||||
try {
|
||||
const offers = extension.parse(secWebSocketExtensions);
|
||||
|
||||
if (offers[PerMessageDeflate.extensionName]) {
|
||||
perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]);
|
||||
extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
|
||||
}
|
||||
} catch (err) {
|
||||
const message =
|
||||
'Invalid or unacceptable Sec-WebSocket-Extensions header';
|
||||
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Optionally call external client verification handler.
|
||||
//
|
||||
if (this.options.verifyClient) {
|
||||
const info = {
|
||||
origin:
|
||||
req.headers[`${version === 8 ? 'sec-websocket-origin' : 'origin'}`],
|
||||
secure: !!(req.socket.authorized || req.socket.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,
|
||||
key,
|
||||
protocols,
|
||||
req,
|
||||
socket,
|
||||
head,
|
||||
cb
|
||||
);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.options.verifyClient(info)) return abortHandshake(socket, 401);
|
||||
}
|
||||
|
||||
this.completeUpgrade(extensions, key, protocols, req, socket, head, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrade the connection to WebSocket.
|
||||
*
|
||||
* @param {Object} extensions The accepted extensions
|
||||
* @param {String} key The value of the `Sec-WebSocket-Key` header
|
||||
* @param {Set} protocols The subprotocols
|
||||
* @param {http.IncomingMessage} req The request object
|
||||
* @param {Duplex} socket The network socket between the server and client
|
||||
* @param {Buffer} head The first packet of the upgraded stream
|
||||
* @param {Function} cb Callback
|
||||
* @throws {Error} If called more than once with the same socket
|
||||
* @private
|
||||
*/
|
||||
completeUpgrade(extensions, key, protocols, req, socket, head, cb) {
|
||||
//
|
||||
// Destroy the socket if the client has already sent a FIN packet.
|
||||
//
|
||||
if (!socket.readable || !socket.writable) return socket.destroy();
|
||||
|
||||
if (socket[kWebSocket]) {
|
||||
throw new Error(
|
||||
'server.handleUpgrade() was called more than once with the same ' +
|
||||
'socket, possibly due to a misconfiguration'
|
||||
);
|
||||
}
|
||||
|
||||
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
||||
|
||||
const digest = createHash('sha1')
|
||||
.update(key + GUID)
|
||||
.digest('base64');
|
||||
|
||||
const headers = [
|
||||
'HTTP/1.1 101 Switching Protocols',
|
||||
'Upgrade: websocket',
|
||||
'Connection: Upgrade',
|
||||
`Sec-WebSocket-Accept: ${digest}`
|
||||
];
|
||||
|
||||
const ws = new this.options.WebSocket(null, undefined, this.options);
|
||||
|
||||
if (protocols.size) {
|
||||
//
|
||||
// Optionally call external protocol selection handler.
|
||||
//
|
||||
const protocol = this.options.handleProtocols
|
||||
? this.options.handleProtocols(protocols, req)
|
||||
: protocols.values().next().value;
|
||||
|
||||
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, {
|
||||
allowSynchronousEvents: this.options.allowSynchronousEvents,
|
||||
maxPayload: this.options.maxPayload,
|
||||
skipUTF8Validation: this.options.skipUTF8Validation
|
||||
});
|
||||
|
||||
if (this.clients) {
|
||||
this.clients.add(ws);
|
||||
ws.on('close', () => {
|
||||
this.clients.delete(ws);
|
||||
|
||||
if (this._shouldEmitClose && !this.clients.size) {
|
||||
process.nextTick(emitClose, this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cb(ws, req);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WebSocketServer;
|
||||
|
||||
/**
|
||||
* Add event listeners on an `EventEmitter` using a map of <event, listener>
|
||||
* pairs.
|
||||
*
|
||||
* @param {EventEmitter} server The event emitter
|
||||
* @param {Object.<String, Function>} map The listeners to add
|
||||
* @return {Function} A function that will remove the added listeners when
|
||||
* called
|
||||
* @private
|
||||
*/
|
||||
function addListeners(server, map) {
|
||||
for (const event of Object.keys(map)) server.on(event, map[event]);
|
||||
|
||||
return function removeListeners() {
|
||||
for (const event of Object.keys(map)) {
|
||||
server.removeListener(event, map[event]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a `'close'` event on an `EventEmitter`.
|
||||
*
|
||||
* @param {EventEmitter} server The event emitter
|
||||
* @private
|
||||
*/
|
||||
function emitClose(server) {
|
||||
server._state = CLOSED;
|
||||
server.emit('close');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle socket errors.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function socketOnError() {
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the connection when preconditions are not fulfilled.
|
||||
*
|
||||
* @param {Duplex} 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) {
|
||||
//
|
||||
// The socket is writable unless the user destroyed or ended it before calling
|
||||
// `server.handleUpgrade()` or in the `verifyClient` function, which is a user
|
||||
// error. Handling this does not make much sense as the worst that can happen
|
||||
// is that some of the data written by the user might be discarded due to the
|
||||
// call to `socket.end()` below, which triggers an `'error'` event that in
|
||||
// turn causes the socket to be destroyed.
|
||||
//
|
||||
message = message || http.STATUS_CODES[code];
|
||||
headers = {
|
||||
Connection: 'close',
|
||||
'Content-Type': 'text/html',
|
||||
'Content-Length': Buffer.byteLength(message),
|
||||
...headers
|
||||
};
|
||||
|
||||
socket.once('finish', socket.destroy);
|
||||
|
||||
socket.end(
|
||||
`HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\r\n` +
|
||||
Object.keys(headers)
|
||||
.map((h) => `${h}: ${headers[h]}`)
|
||||
.join('\r\n') +
|
||||
'\r\n\r\n' +
|
||||
message
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a `'wsClientError'` event on a `WebSocketServer` if there is at least
|
||||
* one listener for it, otherwise call `abortHandshake()`.
|
||||
*
|
||||
* @param {WebSocketServer} server The WebSocket server
|
||||
* @param {http.IncomingMessage} req The request object
|
||||
* @param {Duplex} socket The socket of the upgrade request
|
||||
* @param {Number} code The HTTP response status code
|
||||
* @param {String} message The HTTP response body
|
||||
* @private
|
||||
*/
|
||||
function abortHandshakeOrEmitwsClientError(server, req, socket, code, message) {
|
||||
if (server.listenerCount('wsClientError')) {
|
||||
const err = new Error(message);
|
||||
Error.captureStackTrace(err, abortHandshakeOrEmitwsClientError);
|
||||
|
||||
server.emit('wsClientError', err, socket, req);
|
||||
} else {
|
||||
abortHandshake(socket, code, message);
|
||||
}
|
||||
}
|
||||
1338
node_modules/engine.io/node_modules/ws/lib/websocket.js
generated
vendored
Normal file
1338
node_modules/engine.io/node_modules/ws/lib/websocket.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
69
node_modules/engine.io/node_modules/ws/package.json
generated
vendored
Normal file
69
node_modules/engine.io/node_modules/ws/package.json
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"name": "ws",
|
||||
"version": "8.17.1",
|
||||
"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": "https://github.com/websockets/ws/issues",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/websockets/ws.git"
|
||||
},
|
||||
"author": "Einar Otto Stangvik <einaros@gmail.com> (http://2x.io)",
|
||||
"license": "MIT",
|
||||
"main": "index.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"browser": "./browser.js",
|
||||
"import": "./wrapper.mjs",
|
||||
"require": "./index.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"browser": "browser.js",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"files": [
|
||||
"browser.js",
|
||||
"index.js",
|
||||
"lib/*.js",
|
||||
"wrapper.mjs"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "nyc --reporter=lcov --reporter=text mocha --throw-deprecation test/*.test.js",
|
||||
"integration": "mocha --throw-deprecation test/*.integration.js",
|
||||
"lint": "eslint . && prettier --check --ignore-path .gitignore \"**/*.{json,md,yaml,yml}\""
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"benchmark": "^2.1.4",
|
||||
"bufferutil": "^4.0.1",
|
||||
"eslint": "^9.0.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"globals": "^15.0.0",
|
||||
"mocha": "^8.4.0",
|
||||
"nyc": "^15.0.0",
|
||||
"prettier": "^3.0.0",
|
||||
"utf-8-validate": "^6.0.0"
|
||||
}
|
||||
}
|
||||
8
node_modules/engine.io/node_modules/ws/wrapper.mjs
generated
vendored
Normal file
8
node_modules/engine.io/node_modules/ws/wrapper.mjs
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import createWebSocketStream from './lib/stream.js';
|
||||
import Receiver from './lib/receiver.js';
|
||||
import Sender from './lib/sender.js';
|
||||
import WebSocket from './lib/websocket.js';
|
||||
import WebSocketServer from './lib/websocket-server.js';
|
||||
|
||||
export { createWebSocketStream, Receiver, Sender, WebSocket, WebSocketServer };
|
||||
export default WebSocket;
|
||||
70
node_modules/engine.io/package.json
generated
vendored
Normal file
70
node_modules/engine.io/package.json
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"name": "engine.io",
|
||||
"version": "6.6.4",
|
||||
"description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server",
|
||||
"type": "commonjs",
|
||||
"main": "./build/engine.io.js",
|
||||
"types": "./build/engine.io.d.ts",
|
||||
"exports": {
|
||||
"types": "./build/engine.io.d.ts",
|
||||
"import": "./wrapper.mjs",
|
||||
"require": "./build/engine.io.js"
|
||||
},
|
||||
"author": "Guillermo Rauch <guillermo@learnboost.com>",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Eugen Dueck",
|
||||
"web": "https://github.com/EugenDueck"
|
||||
},
|
||||
{
|
||||
"name": "Afshin Mehrabani",
|
||||
"web": "https://github.com/afshinm"
|
||||
},
|
||||
{
|
||||
"name": "Christoph Dorn",
|
||||
"web": "https://github.com/cadorn"
|
||||
},
|
||||
{
|
||||
"name": "Mark Mokryn",
|
||||
"email": "mokesmokes@gmail.com"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/node": ">=10.0.0",
|
||||
"accepts": "~1.3.4",
|
||||
"base64id": "2.0.0",
|
||||
"cookie": "~0.7.2",
|
||||
"cors": "~2.8.5",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
"ws": "~8.17.1"
|
||||
},
|
||||
"scripts": {
|
||||
"compile": "rimraf ./build && tsc",
|
||||
"test": "npm run compile && npm run format:check && npm run test:default && npm run test:compat-v3",
|
||||
"test:default": "mocha --bail --exit",
|
||||
"test:compat-v3": "EIO_CLIENT=3 mocha --exit",
|
||||
"test:eiows": "EIO_WS_ENGINE=eiows mocha --exit",
|
||||
"test:uws": "EIO_WS_ENGINE=uws mocha --exit",
|
||||
"format:check": "prettier --check \"wrapper.mjs\" \"lib/**/*.ts\" \"test/**/*.js\" \"test/webtransport.mjs\"",
|
||||
"format:fix": "prettier --write \"wrapper.mjs\" \"lib/**/*.ts\" \"test/**/*.js\" \"test/webtransport.mjs\"",
|
||||
"prepack": "npm run compile"
|
||||
},
|
||||
"homepage": "https://github.com/socketio/socket.io/tree/main/packages/engine.io#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/socketio/socket.io.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/socketio/socket.io/issues"
|
||||
},
|
||||
"files": [
|
||||
"build/",
|
||||
"wrapper.mjs"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10.2.0"
|
||||
}
|
||||
}
|
||||
10
node_modules/engine.io/wrapper.mjs
generated
vendored
Normal file
10
node_modules/engine.io/wrapper.mjs
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
export {
|
||||
Server,
|
||||
Socket,
|
||||
Transport,
|
||||
transports,
|
||||
listen,
|
||||
attach,
|
||||
parser,
|
||||
protocol,
|
||||
} from "./build/engine.io.js";
|
||||
Reference in New Issue
Block a user