Init. Map does not function and only working on backend at the moment.

This commit is contained in:
eetnaviation
2024-03-15 22:15:20 +02:00
commit d521b9db0c
1488 changed files with 252767 additions and 0 deletions

647
node_modules/engine.io-client/History.md generated vendored Normal file
View File

@ -0,0 +1,647 @@
1.8.6 / 2021-06-28
===================
* [chore] Bump xmlhttprequest-ssl to version 1.6.3
1.8.5 / 2017-12-27
===================
* [chore] Bump ws to version 1.1.5
1.8.4 / 2017-04-28
===================
(following engine.io bump)
1.8.3 / 2017-02-16
===================
* [chore] Bump ws to version 1.1.2 (vulnerability fix) (#539) (cherry-picked)
1.8.2 / 2016-12-11
===================
* [chore] Bump engine.io-parser to version 1.3.2 (#523)
1.8.1 / 2016-11-27
===================
* [fix] Only add defined callbacks to the stack (#447)
1.8.0 / 2016-11-20
===================
* [fix] Fixed regression creating connection over https from node (#513)
* [fix] Fixed regression creating connection over wss from node (#514)
* [feature] Enable definition of timeouts for xhr-polling (#456)
* [feature] Added flag forceNode to override the normal behavior of prefering Browser based implementations. (#469)
* [feature] add localAddress option (#487)
* [chore] update dependencies (#516)
* [chore] Speed up lint by avoiding '**/*.js' matching pattern (#517)
* [chore] Bump debug to version 2.3.3 (#520)
1.7.2 / 2016-10-24
===================
* [fix] Set accept header to */* to support react app proxy (#508)
* [fix] remove a workaround for ios (#465)
* [fix] onPacket now emits data on 'closing' state as well (#484)
* [fix] Obfuscate `ActiveXObject` occurrences (#509)
* [docs] Add missing `onlyBinaryUpgrades` option in the docs (#510)
* [chore] Add Github issue and PR templates (#511)
1.7.1 / 2016-10-20
===================
* [fix] Define "requestsCount" var and "requests" hash unconditionally (#490)
* [perf] Add all properties to the socket in the constructor (#488)
* [chore] Update zuul browser settings (#504)
* [chore] Bump engine.io-parser to 1.3.1 (#505)
* [chore] Use more-specific imports for StealJS compatibility (#467)
1.7.0 / 2016-10-05
===================
* [fix] Revert "default `rejectUnauthorized` to `true`" (#496)
* [fix] Use xhr.responseText if xhr.response is not provided (#483)
* [fix] Fix issue with errors during WebSocket creation not being caught (#475)
* [style] Add missing semi-colon (#501)
* [chore] Add gulp & babel in the build process (#455)
* [chore] Add eslint (#458)
* [chore] Bump zuul (#464)
* [chore] Remove unused submodule (#466)
* [chore] Bumping ws to 1.1.1 (#478)
* [chore] Update zuul browser settings following EOL notices (#486)
* [chore] Bump engine.io-parser (#492)
* [chore] Make the build status badge point towards master (#497)
* [chore] Bump zuul to 3.11.0 & zuul-ngrok to 4.0.0 (#498)
* [chore] Restrict files included in npm package (#499)
1.6.11 / 2016-06-23
===================
* bump version
1.6.10 / 2016-06-23
===================
* bump version
1.6.9 / 2016-05-02
==================
* default `rejectUnauthorized` to `true`
1.6.8 / 2016-01-25
==================
* safely resolve `ws` module
1.6.7 / 2016-01-10
==================
* prevent `ws` from being added to the bundle
* added jsonp fix for when no `<script>` is found
1.6.6 / 2016-01-07
==================
* support: add fallback to `global` for nativescript [@ligaz]
* exclude `ws` instead of ignoring it from build [@lpinca]
1.6.5 / 2016-01-05
==================
* package: bump `ws` for sec advisory
1.6.4 / 2015-12-04
==================
* ipv6 url support
* README: fix the description of the `timestampRequests` option
* transports: use `yeast` to generate the cache busting id
* fix arraybuffer > base64 for binary sends
1.6.3 / 2015-12-01
==================
* remove compress option from control packets
* threshold for permessage-deflate
1.6.2 / 2015-11-30
==================
* package: bump `ws` for memory fix with compression
* fix response parsing error for polling (unused)
1.6.1 / 2015-11-28
==================
* fix packet options that `ws` changes [Nibbler999]
* package: use published `engine.io-parser`
1.6.0 / 2015-11-28
==================
* test with travis containers
* socket: remove duplicate declaration (fixes #434)
* package: bump `debug` (fixes #433)
* bump zuul and zuul-ngrok
* package: fix repository url
* bump `ws` for several improvements
* fix rejectUnauthorized bug
* websocket: improve firing of `drain` in websocket transport
* socket: clean up buffers right after `close` event
* change semantics of the write callback for polling
(fire upon flush instead drain)
* socket: fix host parsing for IPv6 URLs
* socket: handle parser errors appropriately
* expose `ping` and `pong` events
* enable `supportsBinary` when running as a node client
* introduce `extraHeaders` support
* fix error when passing `WebSocket#send` second argument on Safari
* support compression
1.5.4 / 2015-09-09
==================
* package: bump `engine.io-parser`
1.5.3 / 2015-09-09
==================
* package: bump `ws` to fix node 0.4.0
1.5.2 / 2015-07-09
==================
* package: bump `ws` to fix windows build issues
1.5.1 / 2015-01-19
==================
* do not rely on `git(1)` for dep, point to tarball instead
1.5.0 / 2015-01-18
==================
* package: bump `engine.io-parser`
* fix IE tests firing too many connections [rase-]
* fix default port detection when host is specified [defunctzombie]
* add support for custom SSL options in constructor [rase-]
* only call xhr.abort() on error cases in `polling-xhr` [samcday]
1.4.3 / 2014-11-21
==================
* support: make the build system work with the latest browserify
* test: remove test with partial browser support
* Fixed calls to addEventListener in old browsers
1.4.2 / 2014-10-27
==================
* remove invalid value for strict mode
* IE10 should prefer using XHR2 over XDR because it's safer
* fixed transport close deferring logic [nkzawa]
* wait for buffer to be drained before closing [nkzawa]
1.4.1 / 2014-10-03
==================
* Fixed "jsonp polling iframe removal error"
* Move ws upgrade needing connection tests to a block checking browser support.
* check `readyState` in `sendPacket` and close on `upgradeError` too
* defer close while upgrading a transport
1.4.0 / 2014-09-03
==================
* add matrix support for travis runs.
* add `enablesXDR` option to turn on `XDomainRequest`
* package: bump `browserify`
* package: bump `engine.io-parser`
* style and close socket after assert
* add support for `jsonp` option to turn it off
1.3.1 / 2014-06-19
==================
* transport: catch utf8 decode errors
1.3.0 / 2014-06-13
==================
* smaller inherits utility
* fix a test for ws
* upgrade emitter dep to not rely on tarball
1.2.2 / 2014-05-30
==================
* package: bump `engine.io-parser` for binary utf8 fix
1.2.1 / 2014-05-22
==================
* build
1.2.0 / 2014-05-18
==================
* removed flashsocket, moving to userland
* new build
1.1.1 / 2014-05-14
==================
* jsonp-polling: fixed newline double escaping
* package: bump parser
* remove legacy Socket#onopen call
* added tests for multibyte strings
1.1.0 / 2014-04-27
==================
* bump zuul version
* renamed removeAllListeners to cleanup
* use inherits package instead of inherit
* removed all references to util.js
* fix if statement in FlashWS.ready method
* polling-jsonp: prevent spurious errors from being emitted when the window is unloaded
* polling-xhr: fix a comment and remove unneeded `document` reference
* enforce cache busting for all user agents
* JSONP and test fixes for fails in IE
* package: bump `engine.io-parser`
* polling-xhr: abort the request when the window is unloaded
1.0.5 / 2014-03-18
==================
* package: bump `engine.io-parser` for android binary fix
1.0.4 / 2014-03-14
==================
* no base64 encoding for no reason when using websockets
1.0.3 / 2014-03-12
==================
* fix browserify
1.0.2 / 2014-03-12
==================
* bump `engine.io-parser`
* made `parseJSON` and `parseURI` from `util` their own modules [gkoren]
* clean up tests
* clean up browserify
1.0.1 / 2014-03-06
==================
* package: bump `engine.io-parser`
1.0.0 / 2014-03-06
==================
* run browserify without shims
* emit socket upgrade event after upgrade done
* better feature detection for XHR2
* added `rememberUpgrade` option
* binary support
0.9.0 / 2014-02-09
==================
* Fix simple `host:port` URLs and IPV6 [bmalehorn]
* Fix XHR cleanup method [poohlty]
* Match semantics of `close` event with `WebSocket`. If an error occurs
and `open` hadn't fired before, we still emit `close` as per
WebSocket spec [mokesmokes].
* Removed SPEC (now in `engine.io-protocol` repository)
* Remove `Socket#open` docs (private API) [mokesmokes]
0.8.2 / 2014-01-18
==================
* polling-xhr: avoid catching user-thrown errors
* util: remove unused `hasCORS`
* polling: remove deferring for faster startup (fixes #174)
* engine now works perfectly on workers
0.8.1 / 2014-01-17
==================
* package: bump debug to fix `localStorage` issue (fixes #213)
* remove duplicate xmlhttprequest code
* add iphone automated testing
* bump zuul to 1.3.0 to improve tests performance
* use invalid ip address for incorrect connection test
* Fix GH-224, remove sockets array
0.8.0 / 2014-01-05
==================
* socket: emit upgrade errors as `upgradeError` instead of `error`
0.7.14 / 2014-01-01
===================
* test: increase timeouts for network tests
* test: whitelist globals
* test: improve socket closing test
* polling: improve url timestamp for ie11 and allow force disabling
* polling-xhr: handle errors for xhr creation (fixes `access denied` issues)
* polling-xhr: style
* socket: more instrumentation for transport creation
* socket: allow explicit `false` for `timestampRequests`
* socket: accept `null` as first argument
* Makefile: cleanup
* .travis: deprecate 0.6
0.7.13 / 2013-12-20
===================
* use `jsonp` in favor of `XDomainRequest` to preserve `Cookie`
headers in all situations [3rd-eden] (fixes #217)
* run zuul tests after node tests [defunctzombie]
* add zuul support for easier browser testing [defunctzombie]
0.7.12 / 2013-11-11
===================
* engine.io: updated build to fix WebSocket constructor issue
* package: move browserify into devDeps
0.7.11 / 2013-11-06
===================
* AMD support
* Makefile: build now smaller thanks to browserify
* add browserify support
0.7.10 / 2013-10-28
===================
* fixed issue which prevented IE9 and under to pass Cookies to server during handshake
* package: update "ws" to v0.4.31
* fixed - there is no host property only hostname
* fixed - flash socket creation
* fixed - emit errors thrown by xhr.send()
0.7.9 / 2013-08-30
==================
* websocket: pass `undefined` as the WebSocket "protocols"
0.7.8 / 2013-08-30
==================
* package: update "ws"
0.7.7 / 2013-08-30
==================
* package: bump ws to 0.4.30
* websocket: no more env sniffing, just require `ws` [TooTallNate]
* websocket: remove the "bufferedAmount" checking logic [TooTallNate]
0.7.6 / 2013-08-30
==================
* package: revert ws to avoid upgrade fail now
0.7.5 / 2013-08-30
==================
* package: bump ws to 0.4.30
0.7.4 / 2013-08-25
==================
* package: rolling back to `ws` `0.4.25` due to disconnection bug
0.7.3 / 2013-08-23
==================
* noop bump
0.7.2 / 2013-08-23
==================
* transports: fix WebSocket transport in the web browser (again)
0.7.1 / 2013-08-23
==================
* transports: fix WebSocket transport in the web browser
* package: upgrade "ws" to v0.4.29
0.7.0 / 2013-08-23
==================
* socket: add `agent` option
* package: point "xmlhttprequest" to our LearnBoost fork for agent support
* package: specify a newer version of "ws" that includes `agent` support
* util: use "component/has-cors"
* transport: fix whitespace
* util: use "component/global"
* package: Add repository field to readme
* socket: Don't lose packets writen during upgrade after a re-open
* socket: use a consistent "debug" name for socket.js
* package: Update emitter dep to 1.0.1 for old IE support
0.6.3 / 2013-06-21
==================
* fix check readyState in polling transport (Naoyuki Kanezawa)
* use http url in npm dependencies for emitter (Eric Schoffstall)
0.6.2 / 2013-06-15
==================
* transports: improve polling orderly close (fixes #164)
* socket: ignore further transport communication upon `onClose`
* socket: added missing `socket#onerror` support
* socket: don't call `socket#onclose` if previous state was not `open`
* transports: fix iOS5 crash issue
* Makefile: extra precaution when building to avoid 0.6.0 build problem
0.6.1 / 2013-06-06
==================
* engine.io: fixed build
0.6.0 / 2013-05-31
==================
* does not emit close on incorrect socket connection
* use indexof component for ie8 and below
* improved x-domain handling
* introduce public `ping` api
* added drain event
* fix `flush` and `flushComplete` events
* fixed `drain` bug splicing with upgrading
* add support for callbacks with socket.send()
0.5.0 / 2013-03-16
==================
* socket: implement qs support for `string`
* added query.EIO to take protocol version from parser
* use istanbul for code coverage
* integrated engine.io-protocol 0.3.0
* updated ws
* fixed JSONPPolling iframe removal error
* changed error message to match xhr error message on jsonp transport script tag
* Added onerror handler for script tag in jsonp transport
* remove uid qs
* Added missing colon in payload. Thanks @lsm
0.4.3 / 2013-02-08
==================
* package: removed unusued `parser.js`
0.4.2 / 2013-02-08
==================
* polling-jsonp: fix ie6 JSONP on SSL
* close also if socket.readyState is on "opening"
* parser.js: removed the file package.json: added the engine.io-parser dependency everything else: switched to engine.io-parser
* fix "TypeError: Object #<Object> has no method 'global'"
* client now ignores unsupported upgrades
0.4.1 / 2013-01-18
==================
* do not shadow global XMLHttpRequest
* socket: added `data` event (as synonym to `message`)
* socket: remove `resource` and fix `path`
* socket: fixed access to `opts`
* test: fixed transports tests
* socket: constructor can accept uri/opts simultaneously
* SPEC: simplified: removed resource from SPEC
* socket: proper `host`/`hostname` support
* socket: ensure `onclose` idempotency
* socket: added `onerror` instrumentation
* socket: fix style
* use window to detect platform and fix global reference
* *: fix references to `global` (fixes #79)
0.4.0 / 2012-12-09
==================
* *: now based on `component(1)`
* *: module now exports `Socket`
* socket: export constructors, utils and `protocol`
* *: implemented `emitter` component
* *: removed browserbuild and preprocessor instructions
0.3.10 / 2012-12-03
===================
* socket: fix closing the socket in an `open` listener [mmastrac]
* socket: perform ping interval/timer cleanup [mmastrac]
* fix SPEC (packages -> packets) [jxck]
* socket: handle probe's transport errors [indutny]
0.3.9 / 2012-10-23
==================
* socket: fix `hostname` instead of `host`
* socket: avoid duplicate `port` defaults
0.3.8 / 2012-10-23
==================
* socket: introduce introspection hooks
* socket: introduced `host` and `port` `location` defaults
* flashsocket: obfuscate activex (fixes #31)
* README: documented reconnect (closes #45)
* socket: unset `id` upon close
* socket: clear transport listeners upon force close
0.3.7 / 2012-10-21
==================
* fix `version` [quackingduck]
* ping timeout gets reset upon any packet received [indutny]
* timeout fixes [cadorn, indutny]
* transport: fix xdomain detection in absence of location.port (GH-38)
* socket: fix passing `false` as secure getting overridden
* socket: default `secure` to `true` for SSL-served pages
* socket: fix default port for SSL when `secure` is not supplied
0.3.6 / 2012-10-16
==================
* socket: reset timeout on any incoming data [indutny]
0.3.5 / 2012-10-14
==================
* new build
0.3.4 / 2012-10-14
==================
* package: fix `component` exports
0.3.3 / 2012-10-10
==================
* socket: fix `secure` default value discovery [quackingduck]
0.3.2 / 2012-10-08
==================
* Bump
0.3.1 / 2012-10-08
==================
* socket: added `write` alias for `send`
* package: added `component`
0.3.0 / 2012-09-04
==================
* IE's XDomainRequest cannot do requests that go from HTTPS to HTTP or HTTP to HTTPS [mixu]
* Switch to client-initiated ping, and set interval in handshake [cadorn]
0.2.2 / 2012-08-26
==================
* polling-jsonp: allow unneeded global leak (fixes #41)
* polling-jsonp: allow for multiple eio's in the same page
0.2.1 / 2012-08-13
==================
* Bump
0.2.0 / 2012-08-06
==================
* polling: introduced `poll` and `pollComplete` (formerly `poll`) events
0.1.2 / 2012-08-02
==================
* Bump
0.1.1 / 2012-08-01
==================
* Added options for request timestamping
* Made timestamp query param customizable
* Added automatic timestamping for Android
0.1.0 / 2012-07-03
==================
* Initial release.

22
node_modules/engine.io-client/LICENSE generated vendored Normal file
View File

@ -0,0 +1,22 @@
(The MIT License)
Copyright (c) 2014-2015 Automattic <dev@cloudup.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.

295
node_modules/engine.io-client/README.md generated vendored Normal file
View File

@ -0,0 +1,295 @@
# Engine.IO client
[![Build Status](https://travis-ci.org/socketio/engine.io-client.svg?branch=master)](http://travis-ci.org/socketio/engine.io-client)
[![NPM version](https://badge.fury.io/js/engine.io-client.svg)](http://badge.fury.io/js/engine.io-client)
This is the client for [Engine.IO](http://github.com/socketio/engine.io),
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
### Standalone
You can find an `engine.io.js` file in this repository, which is a
standalone build you can use as follows:
```html
<script src="/path/to/engine.io.js"></script>
<script>
// eio = Socket
var socket = eio('ws://localhost');
socket.on('open', function(){
socket.on('message', function(data){});
socket.on('close', function(){});
});
</script>
```
### With browserify
Engine.IO is a commonjs module, which means you can include it by using
`require` on the browser and package using [browserify](http://browserify.org/):
1. install the client package
```bash
$ npm install engine.io-client
```
1. write your app code
```js
var socket = require('engine.io-client')('ws://localhost');
socket.on('open', function(){
socket.on('message', function(data){});
socket.on('close', function(){});
});
```
1. build your app bundle
```bash
$ browserify app.js > bundle.js
```
1. include on your page
```html
<script src="/path/to/bundle.js"></script>
```
### Sending and receiving binary
```html
<script src="/path/to/engine.io.js"></script>
<script>
var socket = new eio.Socket('ws://localhost/');
socket.binaryType = 'blob';
socket.on('open', function () {
socket.send(new Int8Array(5));
socket.on('message', function(blob){});
socket.on('close', function(){ });
});
</script>
```
### Node.JS
Add `engine.io-client` to your `package.json` and then:
```js
var socket = require('engine.io-client')('ws://localhost');
socket.on('open', function(){
socket.on('message', function(data){});
socket.on('close', function(){});
});
```
### Node.js with certificates
```js
var opts = {
key: fs.readFileSync('test/fixtures/client.key'),
cert: fs.readFileSync('test/fixtures/client.crt'),
ca: fs.readFileSync('test/fixtures/ca.crt')
};
var socket = require('engine.io-client')('ws://localhost', opts);
socket.on('open', function(){
socket.on('message', function(data){});
socket.on('close', function(){});
});
```
### Node.js with extraHeaders
```js
var opts = {
extraHeaders: {
'X-Custom-Header-For-My-Project': 'my-secret-access-token',
'Cookie': 'user_session=NI2JlCKF90aE0sJZD9ZzujtdsUqNYSBYxzlTsvdSUe35ZzdtVRGqYFr0kdGxbfc5gUOkR9RGp20GVKza; path=/; expires=Tue, 07-Apr-2015 18:18:08 GMT; secure; HttpOnly'
}
};
var socket = require('engine.io-client')('ws://localhost', opts);
socket.on('open', function(){
socket.on('message', function(data){});
socket.on('close', function(){});
});
```
## Features
- Lightweight
- Runs on browser and node.js seamlessly
- Transports are independent of `Engine`
- Easy to debug
- Easy to unit test
- Runs inside HTML5 WebWorker
- Can send and receive binary data
- Receives as ArrayBuffer or Blob when in browser, and Buffer or ArrayBuffer
in Node
- When XHR2 or WebSockets are used, binary is emitted directly. Otherwise
binary is encoded into base64 strings, and decoded when binary types are
supported.
- With browsers that don't support ArrayBuffer, an object { base64: true,
data: dataAsBase64String } is emitted on the `message` event.
## API
### Socket
The client class. Mixes in [Emitter](http://github.com/component/emitter).
Exposed as `eio` in the browser standalone build.
#### Properties
- `protocol` _(Number)_: protocol revision number
- `binaryType` _(String)_ : can be set to 'arraybuffer' or 'blob' in browsers,
and `buffer` or `arraybuffer` in Node. Blob is only used in browser if it's
supported.
#### Events
- `open`
- Fired upon successful connection.
- `message`
- Fired when data is received from the server.
- **Arguments**
- `String` | `ArrayBuffer`: utf-8 encoded data or ArrayBuffer containing
binary data
- `close`
- Fired upon disconnection. In compliance with the WebSocket API spec, this event may be
fired even if the `open` event does not occur (i.e. due to connection error or `close()`).
- `error`
- Fired when an error occurs.
- `flush`
- Fired upon completing a buffer flush
- `drain`
- Fired after `drain` event of transport if writeBuffer is empty
- `upgradeError`
- Fired if an error occurs with a transport we're trying to upgrade to.
- `upgrade`
- Fired upon upgrade success, after the new transport is set
- `ping`
- Fired upon _flushing_ a ping packet (ie: actual packet write out)
- `pong`
- Fired upon receiving a pong packet.
#### Methods
- **constructor**
- Initializes the client
- **Parameters**
- `String` uri
- `Object`: optional, options object
- **Options**
- `agent` (`http.Agent`): `http.Agent` to use, defaults to `false` (NodeJS only)
- `upgrade` (`Boolean`): defaults to true, whether the client should try
to upgrade the transport from long-polling to something better.
- `forceJSONP` (`Boolean`): forces JSONP for polling transport.
- `jsonp` (`Boolean`): determines whether to use JSONP when
necessary for polling. If disabled (by settings to false) an error will
be emitted (saying "No transports available") if no other transports
are available. If another transport is available for opening a
connection (e.g. WebSocket) that transport
will be used instead.
- `forceBase64` (`Boolean`): forces base 64 encoding for polling transport even when XHR2 responseType is available and WebSocket even if the used standard supports binary.
- `enablesXDR` (`Boolean`): enables XDomainRequest for IE8 to avoid loading bar flashing with click sound. default to `false` because XDomainRequest has a flaw of not sending cookie.
- `timestampRequests` (`Boolean`): whether to add the timestamp with each
transport request. Note: polling requests are always stamped unless this
option is explicitly set to `false` (`false`)
- `timestampParam` (`String`): timestamp parameter (`t`)
- `policyPort` (`Number`): port the policy server listens on (`843`)
- `path` (`String`): path to connect to, default is `/engine.io`
- `transports` (`Array`): a list of transports to try (in order).
Defaults to `['polling', 'websocket']`. `Engine`
always attempts to connect directly with the first one, provided the
feature detection test for it passes.
- `rememberUpgrade` (`Boolean`): defaults to false.
If true and if the previous websocket connection to the server succeeded,
the connection attempt will bypass the normal upgrade process and will initially
try websocket. A connection attempt following a transport error will use the
normal upgrade process. It is recommended you turn this on only when using
SSL/TLS connections, or if you know that your network does not block websockets.
- `pfx` (`String`): Certificate, Private key and CA certificates to use for SSL. Can be used in Node.js client environment to manually specify certificate information.
- `key` (`String`): Private key to use for SSL. Can be used in Node.js client environment to manually specify certificate information.
- `passphrase` (`String`): A string of passphrase for the private key or pfx. Can be used in Node.js client environment to manually specify certificate information.
- `cert` (`String`): Public x509 certificate to use. Can be used in Node.js client environment to manually specify certificate information.
- `ca` (`String`|`Array`): An authority certificate or array of authority certificates to check the remote host against.. Can be used in Node.js client environment to manually specify certificate information.
- `ciphers` (`String`): A string describing the ciphers to use or exclude. Consult the [cipher format list](http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT) for details on the format. Can be used in Node.js client environment to manually specify certificate information.
- `rejectUnauthorized` (`Boolean`): If true, the server certificate is verified against the list of supplied CAs. An 'error' event is emitted if verification fails. Verification happens at the connection level, before the HTTP request is sent. Can be used in Node.js client environment to manually specify certificate information.
- `perMessageDeflate` (`Object|Boolean`): parameters of the WebSocket permessage-deflate extension
(see [ws module](https://github.com/einaros/ws) api docs). Set to `false` to disable. (`true`)
- `threshold` (`Number`): data is compressed only if the byte size is above this value. This option is ignored on the browser. (`1024`)
- `extraHeaders` (`Object`): Headers that will be passed for each request to the server (via xhr-polling and via websockets). These values then can be used during handshake or for special proxies. Can only be used in Node.js client environment.
- `onlyBinaryUpgrades` (`Boolean`): whether transport upgrades should be restricted to transports supporting binary data (`false`)
- `requestTimeout` (`Number`): Timeout for xhr-polling requests in milliseconds (`0`)
- `forceNode` (`Boolean`): Uses NodeJS implementation for websockets - even if there is a native Browser-Websocket available, which is preferred by default over the NodeJS implementation. (This is useful when using hybrid platforms like nw.js or electron) (`false`, NodeJS only)
- `localAddress` (`String`): the local IP address to connect to
- `send`
- Sends a message to the server
- **Parameters**
- `String` | `ArrayBuffer` | `ArrayBufferView` | `Blob`: data to send
- `Object`: optional, options object
- `Function`: optional, callback upon `drain`
- **Options**
- `compress` (`Boolean`): whether to compress sending data. This option is ignored and forced to be `true` on the browser. (`true`)
- `close`
- Disconnects the client.
### Transport
The transport class. Private. _Inherits from EventEmitter_.
#### Events
- `poll`: emitted by polling transports upon starting a new request
- `pollComplete`: emitted by polling transports upon completing a request
- `drain`: emitted by polling transports upon a buffer drain
## Tests
`engine.io-client` is used to test
[engine](http://github.com/socketio/engine.io). Running the `engine.io`
test suite ensures the client works and vice-versa.
Browser tests are run using [zuul](https://github.com/defunctzombie/zuul). You can
run the tests locally using the following command.
```
./node_modules/.bin/zuul --local 8080 -- test/index.js
```
Additionally, `engine.io-client` has a standalone test suite you can run
with `make test` which will run node.js and browser tests. You must have zuul setup with
a saucelabs account.
## Support
The support channels for `engine.io-client` 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:
```bash
git clone git://github.com/socketio/engine.io-client.git
```
Then:
```bash
cd engine.io-client
npm install
```
See the `Tests` section above for how to run tests before submitting any patches.
## License
MIT - Copyright (c) 2014 Automattic, Inc.

4670
node_modules/engine.io-client/engine.io.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

2
node_modules/engine.io-client/index.js generated vendored Normal file
View File

@ -0,0 +1,2 @@
module.exports = require('./lib/index');

10
node_modules/engine.io-client/lib/index.js generated vendored Normal file
View File

@ -0,0 +1,10 @@
module.exports = require('./socket');
/**
* Exports parser
*
* @api public
*
*/
module.exports.parser = require('engine.io-parser');

738
node_modules/engine.io-client/lib/socket.js generated vendored Normal file
View File

@ -0,0 +1,738 @@
/**
* Module dependencies.
*/
var transports = require('./transports/index');
var Emitter = require('component-emitter');
var debug = require('debug')('engine.io-client:socket');
var index = require('indexof');
var parser = require('engine.io-parser');
var parseuri = require('parseuri');
var parsejson = require('parsejson');
var parseqs = require('parseqs');
/**
* Module exports.
*/
module.exports = Socket;
/**
* Socket constructor.
*
* @param {String|Object} uri or options
* @param {Object} options
* @api public
*/
function Socket (uri, opts) {
if (!(this instanceof Socket)) return new Socket(uri, opts);
opts = opts || {};
if (uri && 'object' === typeof uri) {
opts = uri;
uri = null;
}
if (uri) {
uri = parseuri(uri);
opts.hostname = uri.host;
opts.secure = uri.protocol === 'https' || uri.protocol === 'wss';
opts.port = uri.port;
if (uri.query) opts.query = uri.query;
} else if (opts.host) {
opts.hostname = parseuri(opts.host).host;
}
this.secure = null != opts.secure ? opts.secure
: (global.location && 'https:' === location.protocol);
if (opts.hostname && !opts.port) {
// if no port is specified manually, use the protocol default
opts.port = this.secure ? '443' : '80';
}
this.agent = opts.agent || false;
this.hostname = opts.hostname ||
(global.location ? location.hostname : 'localhost');
this.port = opts.port || (global.location && location.port
? location.port
: (this.secure ? 443 : 80));
this.query = opts.query || {};
if ('string' === typeof this.query) this.query = parseqs.decode(this.query);
this.upgrade = false !== opts.upgrade;
this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/';
this.forceJSONP = !!opts.forceJSONP;
this.jsonp = false !== opts.jsonp;
this.forceBase64 = !!opts.forceBase64;
this.enablesXDR = !!opts.enablesXDR;
this.timestampParam = opts.timestampParam || 't';
this.timestampRequests = opts.timestampRequests;
this.transports = opts.transports || ['polling', 'websocket'];
this.readyState = '';
this.writeBuffer = [];
this.prevBufferLen = 0;
this.policyPort = opts.policyPort || 843;
this.rememberUpgrade = opts.rememberUpgrade || false;
this.binaryType = null;
this.onlyBinaryUpgrades = opts.onlyBinaryUpgrades;
this.perMessageDeflate = false !== opts.perMessageDeflate ? (opts.perMessageDeflate || {}) : false;
if (true === this.perMessageDeflate) this.perMessageDeflate = {};
if (this.perMessageDeflate && null == this.perMessageDeflate.threshold) {
this.perMessageDeflate.threshold = 1024;
}
// SSL options for Node.js client
this.pfx = opts.pfx || null;
this.key = opts.key || null;
this.passphrase = opts.passphrase || null;
this.cert = opts.cert || null;
this.ca = opts.ca || null;
this.ciphers = opts.ciphers || null;
this.rejectUnauthorized = opts.rejectUnauthorized === undefined ? null : opts.rejectUnauthorized;
this.forceNode = !!opts.forceNode;
// other options for Node.js client
var freeGlobal = typeof global === 'object' && global;
if (freeGlobal.global === freeGlobal) {
if (opts.extraHeaders && Object.keys(opts.extraHeaders).length > 0) {
this.extraHeaders = opts.extraHeaders;
}
if (opts.localAddress) {
this.localAddress = opts.localAddress;
}
}
// set on handshake
this.id = null;
this.upgrades = null;
this.pingInterval = null;
this.pingTimeout = null;
// set on heartbeat
this.pingIntervalTimer = null;
this.pingTimeoutTimer = null;
this.open();
}
Socket.priorWebsocketSuccess = false;
/**
* Mix in `Emitter`.
*/
Emitter(Socket.prototype);
/**
* Protocol version.
*
* @api public
*/
Socket.protocol = parser.protocol; // this is an int
/**
* Expose deps for legacy compatibility
* and standalone browser access.
*/
Socket.Socket = Socket;
Socket.Transport = require('./transport');
Socket.transports = require('./transports/index');
Socket.parser = require('engine.io-parser');
/**
* Creates transport of the given type.
*
* @param {String} transport name
* @return {Transport}
* @api private
*/
Socket.prototype.createTransport = function (name) {
debug('creating transport "%s"', name);
var query = clone(this.query);
// append engine.io protocol identifier
query.EIO = parser.protocol;
// transport name
query.transport = name;
// session id if we already have one
if (this.id) query.sid = this.id;
var transport = new transports[name]({
agent: this.agent,
hostname: this.hostname,
port: this.port,
secure: this.secure,
path: this.path,
query: query,
forceJSONP: this.forceJSONP,
jsonp: this.jsonp,
forceBase64: this.forceBase64,
enablesXDR: this.enablesXDR,
timestampRequests: this.timestampRequests,
timestampParam: this.timestampParam,
policyPort: this.policyPort,
socket: this,
pfx: this.pfx,
key: this.key,
passphrase: this.passphrase,
cert: this.cert,
ca: this.ca,
ciphers: this.ciphers,
rejectUnauthorized: this.rejectUnauthorized,
perMessageDeflate: this.perMessageDeflate,
extraHeaders: this.extraHeaders,
forceNode: this.forceNode,
localAddress: this.localAddress
});
return transport;
};
function clone (obj) {
var o = {};
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
o[i] = obj[i];
}
}
return o;
}
/**
* Initializes transport to use and starts probe.
*
* @api private
*/
Socket.prototype.open = function () {
var transport;
if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') !== -1) {
transport = 'websocket';
} else if (0 === this.transports.length) {
// Emit error on next tick so it can be listened to
var self = this;
setTimeout(function () {
self.emit('error', 'No transports available');
}, 0);
return;
} else {
transport = this.transports[0];
}
this.readyState = 'opening';
// Retry with the next transport if the transport is disabled (jsonp: false)
try {
transport = this.createTransport(transport);
} catch (e) {
this.transports.shift();
this.open();
return;
}
transport.open();
this.setTransport(transport);
};
/**
* Sets the current transport. Disables the existing one (if any).
*
* @api private
*/
Socket.prototype.setTransport = function (transport) {
debug('setting transport %s', transport.name);
var self = this;
if (this.transport) {
debug('clearing existing transport %s', this.transport.name);
this.transport.removeAllListeners();
}
// set up transport
this.transport = transport;
// set up transport listeners
transport
.on('drain', function () {
self.onDrain();
})
.on('packet', function (packet) {
self.onPacket(packet);
})
.on('error', function (e) {
self.onError(e);
})
.on('close', function () {
self.onClose('transport close');
});
};
/**
* Probes a transport.
*
* @param {String} transport name
* @api private
*/
Socket.prototype.probe = function (name) {
debug('probing transport "%s"', name);
var transport = this.createTransport(name, { probe: 1 });
var failed = false;
var self = this;
Socket.priorWebsocketSuccess = false;
function onTransportOpen () {
if (self.onlyBinaryUpgrades) {
var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary;
failed = failed || upgradeLosesBinary;
}
if (failed) return;
debug('probe transport "%s" opened', name);
transport.send([{ type: 'ping', data: 'probe' }]);
transport.once('packet', function (msg) {
if (failed) return;
if ('pong' === msg.type && 'probe' === msg.data) {
debug('probe transport "%s" pong', name);
self.upgrading = true;
self.emit('upgrading', transport);
if (!transport) return;
Socket.priorWebsocketSuccess = 'websocket' === transport.name;
debug('pausing current transport "%s"', self.transport.name);
self.transport.pause(function () {
if (failed) return;
if ('closed' === self.readyState) return;
debug('changing transport and sending upgrade packet');
cleanup();
self.setTransport(transport);
transport.send([{ type: 'upgrade' }]);
self.emit('upgrade', transport);
transport = null;
self.upgrading = false;
self.flush();
});
} else {
debug('probe transport "%s" failed', name);
var err = new Error('probe error');
err.transport = transport.name;
self.emit('upgradeError', err);
}
});
}
function freezeTransport () {
if (failed) return;
// Any callback called by transport should be ignored since now
failed = true;
cleanup();
transport.close();
transport = null;
}
// Handle any error that happens while probing
function onerror (err) {
var error = new Error('probe error: ' + err);
error.transport = transport.name;
freezeTransport();
debug('probe transport "%s" failed because of error: %s', name, err);
self.emit('upgradeError', error);
}
function onTransportClose () {
onerror('transport closed');
}
// When the socket is closed while we're probing
function onclose () {
onerror('socket closed');
}
// When the socket is upgraded while we're probing
function onupgrade (to) {
if (transport && to.name !== transport.name) {
debug('"%s" works - aborting "%s"', to.name, transport.name);
freezeTransport();
}
}
// Remove all listeners on the transport and on self
function cleanup () {
transport.removeListener('open', onTransportOpen);
transport.removeListener('error', onerror);
transport.removeListener('close', onTransportClose);
self.removeListener('close', onclose);
self.removeListener('upgrading', onupgrade);
}
transport.once('open', onTransportOpen);
transport.once('error', onerror);
transport.once('close', onTransportClose);
this.once('close', onclose);
this.once('upgrading', onupgrade);
transport.open();
};
/**
* Called when connection is deemed open.
*
* @api public
*/
Socket.prototype.onOpen = function () {
debug('socket open');
this.readyState = 'open';
Socket.priorWebsocketSuccess = 'websocket' === this.transport.name;
this.emit('open');
this.flush();
// we check for `readyState` in case an `open`
// listener already closed the socket
if ('open' === this.readyState && this.upgrade && this.transport.pause) {
debug('starting upgrade probes');
for (var i = 0, l = this.upgrades.length; i < l; i++) {
this.probe(this.upgrades[i]);
}
}
};
/**
* Handles a packet.
*
* @api private
*/
Socket.prototype.onPacket = function (packet) {
if ('opening' === this.readyState || 'open' === this.readyState ||
'closing' === this.readyState) {
debug('socket receive: type "%s", data "%s"', packet.type, packet.data);
this.emit('packet', packet);
// Socket is live - any packet counts
this.emit('heartbeat');
switch (packet.type) {
case 'open':
this.onHandshake(parsejson(packet.data));
break;
case 'pong':
this.setPing();
this.emit('pong');
break;
case 'error':
var err = new Error('server error');
err.code = packet.data;
this.onError(err);
break;
case 'message':
this.emit('data', packet.data);
this.emit('message', packet.data);
break;
}
} else {
debug('packet received with socket readyState "%s"', this.readyState);
}
};
/**
* Called upon handshake completion.
*
* @param {Object} handshake obj
* @api private
*/
Socket.prototype.onHandshake = function (data) {
this.emit('handshake', data);
this.id = data.sid;
this.transport.query.sid = data.sid;
this.upgrades = this.filterUpgrades(data.upgrades);
this.pingInterval = data.pingInterval;
this.pingTimeout = data.pingTimeout;
this.onOpen();
// In case open handler closes socket
if ('closed' === this.readyState) return;
this.setPing();
// Prolong liveness of socket on heartbeat
this.removeListener('heartbeat', this.onHeartbeat);
this.on('heartbeat', this.onHeartbeat);
};
/**
* Resets ping timeout.
*
* @api private
*/
Socket.prototype.onHeartbeat = function (timeout) {
clearTimeout(this.pingTimeoutTimer);
var self = this;
self.pingTimeoutTimer = setTimeout(function () {
if ('closed' === self.readyState) return;
self.onClose('ping timeout');
}, timeout || (self.pingInterval + self.pingTimeout));
};
/**
* Pings server every `this.pingInterval` and expects response
* within `this.pingTimeout` or closes connection.
*
* @api private
*/
Socket.prototype.setPing = function () {
var self = this;
clearTimeout(self.pingIntervalTimer);
self.pingIntervalTimer = setTimeout(function () {
debug('writing ping packet - expecting pong within %sms', self.pingTimeout);
self.ping();
self.onHeartbeat(self.pingTimeout);
}, self.pingInterval);
};
/**
* Sends a ping packet.
*
* @api private
*/
Socket.prototype.ping = function () {
var self = this;
this.sendPacket('ping', function () {
self.emit('ping');
});
};
/**
* Called on `drain` event
*
* @api private
*/
Socket.prototype.onDrain = function () {
this.writeBuffer.splice(0, this.prevBufferLen);
// setting prevBufferLen = 0 is very important
// for example, when upgrading, upgrade packet is sent over,
// and a nonzero prevBufferLen could cause problems on `drain`
this.prevBufferLen = 0;
if (0 === this.writeBuffer.length) {
this.emit('drain');
} else {
this.flush();
}
};
/**
* Flush write buffers.
*
* @api private
*/
Socket.prototype.flush = function () {
if ('closed' !== this.readyState && this.transport.writable &&
!this.upgrading && this.writeBuffer.length) {
debug('flushing %d packets in socket', this.writeBuffer.length);
this.transport.send(this.writeBuffer);
// keep track of current length of writeBuffer
// splice writeBuffer and callbackBuffer on `drain`
this.prevBufferLen = this.writeBuffer.length;
this.emit('flush');
}
};
/**
* Sends a message.
*
* @param {String} message.
* @param {Function} callback function.
* @param {Object} options.
* @return {Socket} for chaining.
* @api public
*/
Socket.prototype.write =
Socket.prototype.send = function (msg, options, fn) {
this.sendPacket('message', msg, options, fn);
return this;
};
/**
* Sends a packet.
*
* @param {String} packet type.
* @param {String} data.
* @param {Object} options.
* @param {Function} callback function.
* @api private
*/
Socket.prototype.sendPacket = function (type, data, options, fn) {
if ('function' === typeof data) {
fn = data;
data = undefined;
}
if ('function' === typeof options) {
fn = options;
options = null;
}
if ('closing' === this.readyState || 'closed' === this.readyState) {
return;
}
options = options || {};
options.compress = false !== options.compress;
var packet = {
type: type,
data: data,
options: options
};
this.emit('packetCreate', packet);
this.writeBuffer.push(packet);
if (fn) this.once('flush', fn);
this.flush();
};
/**
* Closes the connection.
*
* @api private
*/
Socket.prototype.close = function () {
if ('opening' === this.readyState || 'open' === this.readyState) {
this.readyState = 'closing';
var self = this;
if (this.writeBuffer.length) {
this.once('drain', function () {
if (this.upgrading) {
waitForUpgrade();
} else {
close();
}
});
} else if (this.upgrading) {
waitForUpgrade();
} else {
close();
}
}
function close () {
self.onClose('forced close');
debug('socket closing - telling transport to close');
self.transport.close();
}
function cleanupAndClose () {
self.removeListener('upgrade', cleanupAndClose);
self.removeListener('upgradeError', cleanupAndClose);
close();
}
function waitForUpgrade () {
// wait for upgrade to finish since we can't send packets while pausing a transport
self.once('upgrade', cleanupAndClose);
self.once('upgradeError', cleanupAndClose);
}
return this;
};
/**
* Called upon transport error
*
* @api private
*/
Socket.prototype.onError = function (err) {
debug('socket error %j', err);
Socket.priorWebsocketSuccess = false;
this.emit('error', err);
this.onClose('transport error', err);
};
/**
* Called upon transport close.
*
* @api private
*/
Socket.prototype.onClose = function (reason, desc) {
if ('opening' === this.readyState || 'open' === this.readyState || 'closing' === this.readyState) {
debug('socket close with reason: "%s"', reason);
var self = this;
// clear timers
clearTimeout(this.pingIntervalTimer);
clearTimeout(this.pingTimeoutTimer);
// stop event from firing again for transport
this.transport.removeAllListeners('close');
// ensure transport won't stay open
this.transport.close();
// ignore further transport communication
this.transport.removeAllListeners();
// set ready state
this.readyState = 'closed';
// clear session id
this.id = null;
// emit close event
this.emit('close', reason, desc);
// clean buffers after, so users can still
// grab the buffers on `close` event
self.writeBuffer = [];
self.prevBufferLen = 0;
}
};
/**
* Filters upgrades, returning only those matching client transports.
*
* @param {Array} server upgrades
* @api private
*
*/
Socket.prototype.filterUpgrades = function (upgrades) {
var filteredUpgrades = [];
for (var i = 0, j = upgrades.length; i < j; i++) {
if (~index(this.transports, upgrades[i])) filteredUpgrades.push(upgrades[i]);
}
return filteredUpgrades;
};

157
node_modules/engine.io-client/lib/transport.js generated vendored Normal file
View File

@ -0,0 +1,157 @@
/**
* Module dependencies.
*/
var parser = require('engine.io-parser');
var Emitter = require('component-emitter');
/**
* Module exports.
*/
module.exports = Transport;
/**
* Transport abstract constructor.
*
* @param {Object} options.
* @api private
*/
function Transport (opts) {
this.path = opts.path;
this.hostname = opts.hostname;
this.port = opts.port;
this.secure = opts.secure;
this.query = opts.query;
this.timestampParam = opts.timestampParam;
this.timestampRequests = opts.timestampRequests;
this.readyState = '';
this.agent = opts.agent || false;
this.socket = opts.socket;
this.enablesXDR = opts.enablesXDR;
// SSL options for Node.js client
this.pfx = opts.pfx;
this.key = opts.key;
this.passphrase = opts.passphrase;
this.cert = opts.cert;
this.ca = opts.ca;
this.ciphers = opts.ciphers;
this.rejectUnauthorized = opts.rejectUnauthorized;
this.forceNode = opts.forceNode;
// other options for Node.js client
this.extraHeaders = opts.extraHeaders;
this.localAddress = opts.localAddress;
}
/**
* Mix in `Emitter`.
*/
Emitter(Transport.prototype);
/**
* Emits an error.
*
* @param {String} str
* @return {Transport} for chaining
* @api public
*/
Transport.prototype.onError = function (msg, desc) {
var err = new Error(msg);
err.type = 'TransportError';
err.description = desc;
this.emit('error', err);
return this;
};
/**
* Opens the transport.
*
* @api public
*/
Transport.prototype.open = function () {
if ('closed' === this.readyState || '' === this.readyState) {
this.readyState = 'opening';
this.doOpen();
}
return this;
};
/**
* Closes the transport.
*
* @api private
*/
Transport.prototype.close = function () {
if ('opening' === this.readyState || 'open' === this.readyState) {
this.doClose();
this.onClose();
}
return this;
};
/**
* Sends multiple packets.
*
* @param {Array} packets
* @api private
*/
Transport.prototype.send = function (packets) {
if ('open' === this.readyState) {
this.write(packets);
} else {
throw new Error('Transport not open');
}
};
/**
* Called upon open
*
* @api private
*/
Transport.prototype.onOpen = function () {
this.readyState = 'open';
this.writable = true;
this.emit('open');
};
/**
* Called with data.
*
* @param {String} data
* @api private
*/
Transport.prototype.onData = function (data) {
var packet = parser.decodePacket(data, this.socket.binaryType);
this.onPacket(packet);
};
/**
* Called with a decoded packet.
*/
Transport.prototype.onPacket = function (packet) {
this.emit('packet', packet);
};
/**
* Called upon close.
*
* @api private
*/
Transport.prototype.onClose = function () {
this.readyState = 'closed';
this.emit('close');
};

53
node_modules/engine.io-client/lib/transports/index.js generated vendored Normal file
View File

@ -0,0 +1,53 @@
/**
* Module dependencies
*/
var XMLHttpRequest = require('xmlhttprequest-ssl');
var XHR = require('./polling-xhr');
var JSONP = require('./polling-jsonp');
var websocket = require('./websocket');
/**
* Export transports.
*/
exports.polling = polling;
exports.websocket = websocket;
/**
* Polling transport polymorphic constructor.
* Decides on xhr vs jsonp based on feature detection.
*
* @api private
*/
function polling (opts) {
var xhr;
var xd = false;
var xs = false;
var jsonp = false !== opts.jsonp;
if (global.location) {
var isSSL = 'https:' === location.protocol;
var port = location.port;
// some user agents have empty `location.port`
if (!port) {
port = isSSL ? 443 : 80;
}
xd = opts.hostname !== location.hostname || port !== opts.port;
xs = opts.secure !== isSSL;
}
opts.xdomain = xd;
opts.xscheme = xs;
xhr = new XMLHttpRequest(opts);
if ('open' in xhr && !opts.forceJSONP) {
return new XHR(opts);
} else {
if (!jsonp) throw new Error('JSONP disabled');
return new JSONP(opts);
}
}

View File

@ -0,0 +1,231 @@
/**
* Module requirements.
*/
var Polling = require('./polling');
var inherit = require('component-inherit');
/**
* Module exports.
*/
module.exports = JSONPPolling;
/**
* Cached regular expressions.
*/
var rNewline = /\n/g;
var rEscapedNewline = /\\n/g;
/**
* Global JSONP callbacks.
*/
var callbacks;
/**
* Noop.
*/
function empty () { }
/**
* JSONP Polling constructor.
*
* @param {Object} opts.
* @api public
*/
function JSONPPolling (opts) {
Polling.call(this, opts);
this.query = this.query || {};
// define global callbacks array if not present
// we do this here (lazily) to avoid unneeded global pollution
if (!callbacks) {
// we need to consider multiple engines in the same page
if (!global.___eio) global.___eio = [];
callbacks = global.___eio;
}
// callback identifier
this.index = callbacks.length;
// add callback to jsonp global
var self = this;
callbacks.push(function (msg) {
self.onData(msg);
});
// append to query string
this.query.j = this.index;
// prevent spurious errors from being emitted when the window is unloaded
if (global.document && global.addEventListener) {
global.addEventListener('beforeunload', function () {
if (self.script) self.script.onerror = empty;
}, false);
}
}
/**
* Inherits from Polling.
*/
inherit(JSONPPolling, Polling);
/*
* JSONP only supports binary as base64 encoded strings
*/
JSONPPolling.prototype.supportsBinary = false;
/**
* Closes the socket.
*
* @api private
*/
JSONPPolling.prototype.doClose = function () {
if (this.script) {
this.script.parentNode.removeChild(this.script);
this.script = null;
}
if (this.form) {
this.form.parentNode.removeChild(this.form);
this.form = null;
this.iframe = null;
}
Polling.prototype.doClose.call(this);
};
/**
* Starts a poll cycle.
*
* @api private
*/
JSONPPolling.prototype.doPoll = function () {
var self = this;
var script = document.createElement('script');
if (this.script) {
this.script.parentNode.removeChild(this.script);
this.script = null;
}
script.async = true;
script.src = this.uri();
script.onerror = function (e) {
self.onError('jsonp poll error', e);
};
var insertAt = document.getElementsByTagName('script')[0];
if (insertAt) {
insertAt.parentNode.insertBefore(script, insertAt);
} else {
(document.head || document.body).appendChild(script);
}
this.script = script;
var isUAgecko = 'undefined' !== typeof navigator && /gecko/i.test(navigator.userAgent);
if (isUAgecko) {
setTimeout(function () {
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
document.body.removeChild(iframe);
}, 100);
}
};
/**
* Writes with a hidden iframe.
*
* @param {String} data to send
* @param {Function} called upon flush.
* @api private
*/
JSONPPolling.prototype.doWrite = function (data, fn) {
var self = this;
if (!this.form) {
var form = document.createElement('form');
var area = document.createElement('textarea');
var id = this.iframeId = 'eio_iframe_' + this.index;
var iframe;
form.className = 'socketio';
form.style.position = 'absolute';
form.style.top = '-1000px';
form.style.left = '-1000px';
form.target = id;
form.method = 'POST';
form.setAttribute('accept-charset', 'utf-8');
area.name = 'd';
form.appendChild(area);
document.body.appendChild(form);
this.form = form;
this.area = area;
}
this.form.action = this.uri();
function complete () {
initIframe();
fn();
}
function initIframe () {
if (self.iframe) {
try {
self.form.removeChild(self.iframe);
} catch (e) {
self.onError('jsonp polling iframe removal error', e);
}
}
try {
// ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
var html = '<iframe src="javascript:0" name="' + self.iframeId + '">';
iframe = document.createElement(html);
} catch (e) {
iframe = document.createElement('iframe');
iframe.name = self.iframeId;
iframe.src = 'javascript:0';
}
iframe.id = self.iframeId;
self.form.appendChild(iframe);
self.iframe = iframe;
}
initIframe();
// escape \n to prevent it from being converted into \r\n by some UAs
// double escaping is required for escaped new lines because unescaping of new lines can be done safely on server-side
data = data.replace(rEscapedNewline, '\\\n');
this.area.value = data.replace(rNewline, '\\n');
try {
this.form.submit();
} catch (e) {}
if (this.iframe.attachEvent) {
this.iframe.onreadystatechange = function () {
if (self.iframe.readyState === 'complete') {
complete();
}
};
} else {
this.iframe.onload = complete;
}
};

View File

@ -0,0 +1,424 @@
/**
* Module requirements.
*/
var XMLHttpRequest = require('xmlhttprequest-ssl');
var Polling = require('./polling');
var Emitter = require('component-emitter');
var inherit = require('component-inherit');
var debug = require('debug')('engine.io-client:polling-xhr');
/**
* Module exports.
*/
module.exports = XHR;
module.exports.Request = Request;
/**
* Empty function
*/
function empty () {}
/**
* XHR Polling constructor.
*
* @param {Object} opts
* @api public
*/
function XHR (opts) {
Polling.call(this, opts);
this.requestTimeout = opts.requestTimeout;
if (global.location) {
var isSSL = 'https:' === location.protocol;
var port = location.port;
// some user agents have empty `location.port`
if (!port) {
port = isSSL ? 443 : 80;
}
this.xd = opts.hostname !== global.location.hostname ||
port !== opts.port;
this.xs = opts.secure !== isSSL;
} else {
this.extraHeaders = opts.extraHeaders;
}
}
/**
* Inherits from Polling.
*/
inherit(XHR, Polling);
/**
* XHR supports binary
*/
XHR.prototype.supportsBinary = true;
/**
* Creates a request.
*
* @param {String} method
* @api private
*/
XHR.prototype.request = function (opts) {
opts = opts || {};
opts.uri = this.uri();
opts.xd = this.xd;
opts.xs = this.xs;
opts.agent = this.agent || false;
opts.supportsBinary = this.supportsBinary;
opts.enablesXDR = this.enablesXDR;
// SSL options for Node.js client
opts.pfx = this.pfx;
opts.key = this.key;
opts.passphrase = this.passphrase;
opts.cert = this.cert;
opts.ca = this.ca;
opts.ciphers = this.ciphers;
opts.rejectUnauthorized = this.rejectUnauthorized;
opts.requestTimeout = this.requestTimeout;
// other options for Node.js client
opts.extraHeaders = this.extraHeaders;
return new Request(opts);
};
/**
* Sends data.
*
* @param {String} data to send.
* @param {Function} called upon flush.
* @api private
*/
XHR.prototype.doWrite = function (data, fn) {
var isBinary = typeof data !== 'string' && data !== undefined;
var req = this.request({ method: 'POST', data: data, isBinary: isBinary });
var self = this;
req.on('success', fn);
req.on('error', function (err) {
self.onError('xhr post error', err);
});
this.sendXhr = req;
};
/**
* Starts a poll cycle.
*
* @api private
*/
XHR.prototype.doPoll = function () {
debug('xhr poll');
var req = this.request();
var self = this;
req.on('data', function (data) {
self.onData(data);
});
req.on('error', function (err) {
self.onError('xhr poll error', err);
});
this.pollXhr = req;
};
/**
* Request constructor
*
* @param {Object} options
* @api public
*/
function Request (opts) {
this.method = opts.method || 'GET';
this.uri = opts.uri;
this.xd = !!opts.xd;
this.xs = !!opts.xs;
this.async = false !== opts.async;
this.data = undefined !== opts.data ? opts.data : null;
this.agent = opts.agent;
this.isBinary = opts.isBinary;
this.supportsBinary = opts.supportsBinary;
this.enablesXDR = opts.enablesXDR;
this.requestTimeout = opts.requestTimeout;
// SSL options for Node.js client
this.pfx = opts.pfx;
this.key = opts.key;
this.passphrase = opts.passphrase;
this.cert = opts.cert;
this.ca = opts.ca;
this.ciphers = opts.ciphers;
this.rejectUnauthorized = opts.rejectUnauthorized;
// other options for Node.js client
this.extraHeaders = opts.extraHeaders;
this.create();
}
/**
* Mix in `Emitter`.
*/
Emitter(Request.prototype);
/**
* Creates the XHR object and sends the request.
*
* @api private
*/
Request.prototype.create = function () {
var opts = { agent: this.agent, xdomain: this.xd, xscheme: this.xs, enablesXDR: this.enablesXDR };
// SSL options for Node.js client
opts.pfx = this.pfx;
opts.key = this.key;
opts.passphrase = this.passphrase;
opts.cert = this.cert;
opts.ca = this.ca;
opts.ciphers = this.ciphers;
opts.rejectUnauthorized = this.rejectUnauthorized;
var xhr = this.xhr = new XMLHttpRequest(opts);
var self = this;
try {
debug('xhr open %s: %s', this.method, this.uri);
xhr.open(this.method, this.uri, this.async);
try {
if (this.extraHeaders) {
xhr.setDisableHeaderCheck(true);
for (var i in this.extraHeaders) {
if (this.extraHeaders.hasOwnProperty(i)) {
xhr.setRequestHeader(i, this.extraHeaders[i]);
}
}
}
} catch (e) {}
if (this.supportsBinary) {
// This has to be done after open because Firefox is stupid
// http://stackoverflow.com/questions/13216903/get-binary-data-with-xmlhttprequest-in-a-firefox-extension
xhr.responseType = 'arraybuffer';
}
if ('POST' === this.method) {
try {
if (this.isBinary) {
xhr.setRequestHeader('Content-type', 'application/octet-stream');
} else {
xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8');
}
} catch (e) {}
}
try {
xhr.setRequestHeader('Accept', '*/*');
} catch (e) {}
// ie6 check
if ('withCredentials' in xhr) {
xhr.withCredentials = true;
}
if (this.requestTimeout) {
xhr.timeout = this.requestTimeout;
}
if (this.hasXDR()) {
xhr.onload = function () {
self.onLoad();
};
xhr.onerror = function () {
self.onError(xhr.responseText);
};
} else {
xhr.onreadystatechange = function () {
if (4 !== xhr.readyState) return;
if (200 === xhr.status || 1223 === xhr.status) {
self.onLoad();
} else {
// make sure the `error` event handler that's user-set
// does not throw in the same tick and gets caught here
setTimeout(function () {
self.onError(xhr.status);
}, 0);
}
};
}
debug('xhr data %s', this.data);
xhr.send(this.data);
} catch (e) {
// Need to defer since .create() is called directly fhrom the constructor
// and thus the 'error' event can only be only bound *after* this exception
// occurs. Therefore, also, we cannot throw here at all.
setTimeout(function () {
self.onError(e);
}, 0);
return;
}
if (global.document) {
this.index = Request.requestsCount++;
Request.requests[this.index] = this;
}
};
/**
* Called upon successful response.
*
* @api private
*/
Request.prototype.onSuccess = function () {
this.emit('success');
this.cleanup();
};
/**
* Called if we have data.
*
* @api private
*/
Request.prototype.onData = function (data) {
this.emit('data', data);
this.onSuccess();
};
/**
* Called upon error.
*
* @api private
*/
Request.prototype.onError = function (err) {
this.emit('error', err);
this.cleanup(true);
};
/**
* Cleans up house.
*
* @api private
*/
Request.prototype.cleanup = function (fromError) {
if ('undefined' === typeof this.xhr || null === this.xhr) {
return;
}
// xmlhttprequest
if (this.hasXDR()) {
this.xhr.onload = this.xhr.onerror = empty;
} else {
this.xhr.onreadystatechange = empty;
}
if (fromError) {
try {
this.xhr.abort();
} catch (e) {}
}
if (global.document) {
delete Request.requests[this.index];
}
this.xhr = null;
};
/**
* Called upon load.
*
* @api private
*/
Request.prototype.onLoad = function () {
var data;
try {
var contentType;
try {
contentType = this.xhr.getResponseHeader('Content-Type').split(';')[0];
} catch (e) {}
if (contentType === 'application/octet-stream') {
data = this.xhr.response || this.xhr.responseText;
} else {
if (!this.supportsBinary) {
data = this.xhr.responseText;
} else {
try {
data = String.fromCharCode.apply(null, new Uint8Array(this.xhr.response));
} catch (e) {
var ui8Arr = new Uint8Array(this.xhr.response);
var dataArray = [];
for (var idx = 0, length = ui8Arr.length; idx < length; idx++) {
dataArray.push(ui8Arr[idx]);
}
data = String.fromCharCode.apply(null, dataArray);
}
}
}
} catch (e) {
this.onError(e);
}
if (null != data) {
this.onData(data);
}
};
/**
* Check if it has XDomainRequest.
*
* @api private
*/
Request.prototype.hasXDR = function () {
return 'undefined' !== typeof global.XDomainRequest && !this.xs && this.enablesXDR;
};
/**
* Aborts the request.
*
* @api public
*/
Request.prototype.abort = function () {
this.cleanup();
};
/**
* Aborts pending requests when unloading the window. This is needed to prevent
* memory leaks (e.g. when using IE) and to ensure that no spurious error is
* emitted.
*/
Request.requestsCount = 0;
Request.requests = {};
if (global.document) {
if (global.attachEvent) {
global.attachEvent('onunload', unloadHandler);
} else if (global.addEventListener) {
global.addEventListener('beforeunload', unloadHandler, false);
}
}
function unloadHandler () {
for (var i in Request.requests) {
if (Request.requests.hasOwnProperty(i)) {
Request.requests[i].abort();
}
}
}

245
node_modules/engine.io-client/lib/transports/polling.js generated vendored Normal file
View File

@ -0,0 +1,245 @@
/**
* Module dependencies.
*/
var Transport = require('../transport');
var parseqs = require('parseqs');
var parser = require('engine.io-parser');
var inherit = require('component-inherit');
var yeast = require('yeast');
var debug = require('debug')('engine.io-client:polling');
/**
* Module exports.
*/
module.exports = Polling;
/**
* Is XHR2 supported?
*/
var hasXHR2 = (function () {
var XMLHttpRequest = require('xmlhttprequest-ssl');
var xhr = new XMLHttpRequest({ xdomain: false });
return null != xhr.responseType;
})();
/**
* Polling interface.
*
* @param {Object} opts
* @api private
*/
function Polling (opts) {
var forceBase64 = (opts && opts.forceBase64);
if (!hasXHR2 || forceBase64) {
this.supportsBinary = false;
}
Transport.call(this, opts);
}
/**
* Inherits from Transport.
*/
inherit(Polling, Transport);
/**
* Transport name.
*/
Polling.prototype.name = 'polling';
/**
* Opens the socket (triggers polling). We write a PING message to determine
* when the transport is open.
*
* @api private
*/
Polling.prototype.doOpen = function () {
this.poll();
};
/**
* Pauses polling.
*
* @param {Function} callback upon buffers are flushed and transport is paused
* @api private
*/
Polling.prototype.pause = function (onPause) {
var self = this;
this.readyState = 'pausing';
function pause () {
debug('paused');
self.readyState = 'paused';
onPause();
}
if (this.polling || !this.writable) {
var total = 0;
if (this.polling) {
debug('we are currently polling - waiting to pause');
total++;
this.once('pollComplete', function () {
debug('pre-pause polling complete');
--total || pause();
});
}
if (!this.writable) {
debug('we are currently writing - waiting to pause');
total++;
this.once('drain', function () {
debug('pre-pause writing complete');
--total || pause();
});
}
} else {
pause();
}
};
/**
* Starts polling cycle.
*
* @api public
*/
Polling.prototype.poll = function () {
debug('polling');
this.polling = true;
this.doPoll();
this.emit('poll');
};
/**
* Overloads onData to detect payloads.
*
* @api private
*/
Polling.prototype.onData = function (data) {
var self = this;
debug('polling got data %s', data);
var callback = function (packet, index, total) {
// if its the first message we consider the transport open
if ('opening' === self.readyState) {
self.onOpen();
}
// if its a close packet, we close the ongoing requests
if ('close' === packet.type) {
self.onClose();
return false;
}
// otherwise bypass onData and handle the message
self.onPacket(packet);
};
// decode payload
parser.decodePayload(data, this.socket.binaryType, callback);
// if an event did not trigger closing
if ('closed' !== this.readyState) {
// if we got data we're not polling
this.polling = false;
this.emit('pollComplete');
if ('open' === this.readyState) {
this.poll();
} else {
debug('ignoring poll - transport state "%s"', this.readyState);
}
}
};
/**
* For polling, send a close packet.
*
* @api private
*/
Polling.prototype.doClose = function () {
var self = this;
function close () {
debug('writing close packet');
self.write([{ type: 'close' }]);
}
if ('open' === this.readyState) {
debug('transport open - closing');
close();
} else {
// in case we're trying to close while
// handshaking is in progress (GH-164)
debug('transport not open - deferring close');
this.once('open', close);
}
};
/**
* Writes a packets payload.
*
* @param {Array} data packets
* @param {Function} drain callback
* @api private
*/
Polling.prototype.write = function (packets) {
var self = this;
this.writable = false;
var callbackfn = function () {
self.writable = true;
self.emit('drain');
};
parser.encodePayload(packets, this.supportsBinary, function (data) {
self.doWrite(data, callbackfn);
});
};
/**
* Generates uri for connection.
*
* @api private
*/
Polling.prototype.uri = function () {
var query = this.query || {};
var schema = this.secure ? 'https' : 'http';
var port = '';
// cache busting is forced
if (false !== this.timestampRequests) {
query[this.timestampParam] = yeast();
}
if (!this.supportsBinary && !query.sid) {
query.b64 = 1;
}
query = parseqs.encode(query);
// avoid port if default for schema
if (this.port && (('https' === schema && Number(this.port) !== 443) ||
('http' === schema && Number(this.port) !== 80))) {
port = ':' + this.port;
}
// prepend ? to query
if (query.length) {
query = '?' + query;
}
var ipv6 = this.hostname.indexOf(':') !== -1;
return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path + query;
};

View File

@ -0,0 +1,285 @@
/**
* Module dependencies.
*/
var Transport = require('../transport');
var parser = require('engine.io-parser');
var parseqs = require('parseqs');
var inherit = require('component-inherit');
var yeast = require('yeast');
var debug = require('debug')('engine.io-client:websocket');
var BrowserWebSocket = global.WebSocket || global.MozWebSocket;
var NodeWebSocket;
if (typeof window === 'undefined') {
try {
NodeWebSocket = require('ws');
} catch (e) { }
}
/**
* Get either the `WebSocket` or `MozWebSocket` globals
* in the browser or try to resolve WebSocket-compatible
* interface exposed by `ws` for Node-like environment.
*/
var WebSocket = BrowserWebSocket;
if (!WebSocket && typeof window === 'undefined') {
WebSocket = NodeWebSocket;
}
/**
* Module exports.
*/
module.exports = WS;
/**
* WebSocket transport constructor.
*
* @api {Object} connection options
* @api public
*/
function WS (opts) {
var forceBase64 = (opts && opts.forceBase64);
if (forceBase64) {
this.supportsBinary = false;
}
this.perMessageDeflate = opts.perMessageDeflate;
this.usingBrowserWebSocket = BrowserWebSocket && !opts.forceNode;
if (!this.usingBrowserWebSocket) {
WebSocket = NodeWebSocket;
}
Transport.call(this, opts);
}
/**
* Inherits from Transport.
*/
inherit(WS, Transport);
/**
* Transport name.
*
* @api public
*/
WS.prototype.name = 'websocket';
/*
* WebSockets support binary
*/
WS.prototype.supportsBinary = true;
/**
* Opens socket.
*
* @api private
*/
WS.prototype.doOpen = function () {
if (!this.check()) {
// let probe timeout
return;
}
var uri = this.uri();
var protocols = void (0);
var opts = {
agent: this.agent,
perMessageDeflate: this.perMessageDeflate
};
// SSL options for Node.js client
opts.pfx = this.pfx;
opts.key = this.key;
opts.passphrase = this.passphrase;
opts.cert = this.cert;
opts.ca = this.ca;
opts.ciphers = this.ciphers;
opts.rejectUnauthorized = this.rejectUnauthorized;
if (this.extraHeaders) {
opts.headers = this.extraHeaders;
}
if (this.localAddress) {
opts.localAddress = this.localAddress;
}
try {
this.ws = this.usingBrowserWebSocket ? new WebSocket(uri) : new WebSocket(uri, protocols, opts);
} catch (err) {
return this.emit('error', err);
}
if (this.ws.binaryType === undefined) {
this.supportsBinary = false;
}
if (this.ws.supports && this.ws.supports.binary) {
this.supportsBinary = true;
this.ws.binaryType = 'nodebuffer';
} else {
this.ws.binaryType = 'arraybuffer';
}
this.addEventListeners();
};
/**
* Adds event listeners to the socket
*
* @api private
*/
WS.prototype.addEventListeners = function () {
var self = this;
this.ws.onopen = function () {
self.onOpen();
};
this.ws.onclose = function () {
self.onClose();
};
this.ws.onmessage = function (ev) {
self.onData(ev.data);
};
this.ws.onerror = function (e) {
self.onError('websocket error', e);
};
};
/**
* Writes data to socket.
*
* @param {Array} array of packets.
* @api private
*/
WS.prototype.write = function (packets) {
var self = this;
this.writable = false;
// encodePacket efficient as it uses WS framing
// no need for encodePayload
var total = packets.length;
for (var i = 0, l = total; i < l; i++) {
(function (packet) {
parser.encodePacket(packet, self.supportsBinary, function (data) {
if (!self.usingBrowserWebSocket) {
// always create a new object (GH-437)
var opts = {};
if (packet.options) {
opts.compress = packet.options.compress;
}
if (self.perMessageDeflate) {
var len = 'string' === typeof data ? global.Buffer.byteLength(data) : data.length;
if (len < self.perMessageDeflate.threshold) {
opts.compress = false;
}
}
}
// Sometimes the websocket has already been closed but the browser didn't
// have a chance of informing us about it yet, in that case send will
// throw an error
try {
if (self.usingBrowserWebSocket) {
// TypeError is thrown when passing the second argument on Safari
self.ws.send(data);
} else {
self.ws.send(data, opts);
}
} catch (e) {
debug('websocket closed before onclose event');
}
--total || done();
});
})(packets[i]);
}
function done () {
self.emit('flush');
// fake drain
// defer to next tick to allow Socket to clear writeBuffer
setTimeout(function () {
self.writable = true;
self.emit('drain');
}, 0);
}
};
/**
* Called upon close
*
* @api private
*/
WS.prototype.onClose = function () {
Transport.prototype.onClose.call(this);
};
/**
* Closes socket.
*
* @api private
*/
WS.prototype.doClose = function () {
if (typeof this.ws !== 'undefined') {
this.ws.close();
}
};
/**
* Generates uri for connection.
*
* @api private
*/
WS.prototype.uri = function () {
var query = this.query || {};
var schema = this.secure ? 'wss' : 'ws';
var port = '';
// avoid port if default for schema
if (this.port && (('wss' === schema && Number(this.port) !== 443) ||
('ws' === schema && Number(this.port) !== 80))) {
port = ':' + this.port;
}
// append timestamp to URI
if (this.timestampRequests) {
query[this.timestampParam] = yeast();
}
// communicate binary support capabilities
if (!this.supportsBinary) {
query.b64 = 1;
}
query = parseqs.encode(query);
// prepend ? to query
if (query.length) {
query = '?' + query;
}
var ipv6 = this.hostname.indexOf(':') !== -1;
return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path + query;
};
/**
* Feature detection for WebSocket.
*
* @return {Boolean} whether this transport is available.
* @api public
*/
WS.prototype.check = function () {
return !!WebSocket && !('__initialize' in WebSocket && this.name === WS.prototype.name);
};

37
node_modules/engine.io-client/lib/xmlhttprequest.js generated vendored Normal file
View File

@ -0,0 +1,37 @@
// browser shim for xmlhttprequest module
var hasCORS = require('has-cors');
module.exports = function (opts) {
var xdomain = opts.xdomain;
// scheme must be same when usign XDomainRequest
// http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx
var xscheme = opts.xscheme;
// XDomainRequest has a flow of not sending cookie, therefore it should be disabled as a default.
// https://github.com/Automattic/engine.io-client/pull/217
var enablesXDR = opts.enablesXDR;
// XMLHttpRequest can be disabled on IE
try {
if ('undefined' !== typeof XMLHttpRequest && (!xdomain || hasCORS)) {
return new XMLHttpRequest();
}
} catch (e) { }
// Use XDomainRequest for IE8 if enablesXDR is true
// because loading bar keeps flashing when using jsonp-polling
// https://github.com/yujiosaka/socke.io-ie8-loading-example
try {
if ('undefined' !== typeof XDomainRequest && !xscheme && enablesXDR) {
return new XDomainRequest();
}
} catch (e) { }
if (!xdomain) {
try {
return new global[['Active'].concat('Object').join('X')]('Microsoft.XMLHTTP');
} catch (e) { }
}
};

View File

@ -0,0 +1,3 @@
{
"laxbreak": true
}

View File

@ -0,0 +1,7 @@
support
test
examples
example
*.sock
dist
yarn.lock

View File

@ -0,0 +1,235 @@
2.3.3 / 2016-11-09
==================
* Fix: Catch `JSON.stringify()` errors (#195, Jovan Alleyne)
* Fix: Returning `localStorage` saved values (#331, Levi Thomason)
* Improvement: Don't create an empty object when no `process` (Nathan Rajlich)
2.3.2 / 2016-11-09
==================
* Fix: be super-safe in index.js as well (@TooTallNate)
* Fix: should check whether process exists (Tom Newby)
2.3.1 / 2016-11-09
==================
* Fix: Added electron compatibility (#324, @paulcbetts)
* Improvement: Added performance optimizations (@tootallnate)
* Readme: Corrected PowerShell environment variable example (#252, @gimre)
* Misc: Removed yarn lock file from source control (#321, @fengmk2)
2.3.0 / 2016-11-07
==================
* Fix: Consistent placement of ms diff at end of output (#215, @gorangajic)
* Fix: Escaping of regex special characters in namespace strings (#250, @zacronos)
* Fix: Fixed bug causing crash on react-native (#282, @vkarpov15)
* Feature: Enabled ES6+ compatible import via default export (#212 @bucaran)
* Feature: Added %O formatter to reflect Chrome's console.log capability (#279, @oncletom)
* Package: Update "ms" to 0.7.2 (#315, @DevSide)
* Package: removed superfluous version property from bower.json (#207 @kkirsche)
* Readme: fix USE_COLORS to DEBUG_COLORS
* Readme: Doc fixes for format string sugar (#269, @mlucool)
* Readme: Updated docs for DEBUG_FD and DEBUG_COLORS environment variables (#232, @mattlyons0)
* Readme: doc fixes for PowerShell (#271 #243, @exoticknight @unreadable)
* Readme: better docs for browser support (#224, @matthewmueller)
* Tooling: Added yarn integration for development (#317, @thebigredgeek)
* Misc: Renamed History.md to CHANGELOG.md (@thebigredgeek)
* Misc: Added license file (#226 #274, @CantemoInternal @sdaitzman)
* Misc: Updated contributors (@thebigredgeek)
2.2.0 / 2015-05-09
==================
* package: update "ms" to v0.7.1 (#202, @dougwilson)
* README: add logging to file example (#193, @DanielOchoa)
* README: fixed a typo (#191, @amir-s)
* browser: expose `storage` (#190, @stephenmathieson)
* Makefile: add a `distclean` target (#189, @stephenmathieson)
2.1.3 / 2015-03-13
==================
* Updated stdout/stderr example (#186)
* Updated example/stdout.js to match debug current behaviour
* Renamed example/stderr.js to stdout.js
* Update Readme.md (#184)
* replace high intensity foreground color for bold (#182, #183)
2.1.2 / 2015-03-01
==================
* dist: recompile
* update "ms" to v0.7.0
* package: update "browserify" to v9.0.3
* component: fix "ms.js" repo location
* changed bower package name
* updated documentation about using debug in a browser
* fix: security error on safari (#167, #168, @yields)
2.1.1 / 2014-12-29
==================
* browser: use `typeof` to check for `console` existence
* browser: check for `console.log` truthiness (fix IE 8/9)
* browser: add support for Chrome apps
* Readme: added Windows usage remarks
* Add `bower.json` to properly support bower install
2.1.0 / 2014-10-15
==================
* node: implement `DEBUG_FD` env variable support
* package: update "browserify" to v6.1.0
* package: add "license" field to package.json (#135, @panuhorsmalahti)
2.0.0 / 2014-09-01
==================
* package: update "browserify" to v5.11.0
* node: use stderr rather than stdout for logging (#29, @stephenmathieson)
1.0.4 / 2014-07-15
==================
* dist: recompile
* example: remove `console.info()` log usage
* example: add "Content-Type" UTF-8 header to browser example
* browser: place %c marker after the space character
* browser: reset the "content" color via `color: inherit`
* browser: add colors support for Firefox >= v31
* debug: prefer an instance `log()` function over the global one (#119)
* Readme: update documentation about styled console logs for FF v31 (#116, @wryk)
1.0.3 / 2014-07-09
==================
* Add support for multiple wildcards in namespaces (#122, @seegno)
* browser: fix lint
1.0.2 / 2014-06-10
==================
* browser: update color palette (#113, @gscottolson)
* common: make console logging function configurable (#108, @timoxley)
* node: fix %o colors on old node <= 0.8.x
* Makefile: find node path using shell/which (#109, @timoxley)
1.0.1 / 2014-06-06
==================
* browser: use `removeItem()` to clear localStorage
* browser, node: don't set DEBUG if namespaces is undefined (#107, @leedm777)
* package: add "contributors" section
* node: fix comment typo
* README: list authors
1.0.0 / 2014-06-04
==================
* make ms diff be global, not be scope
* debug: ignore empty strings in enable()
* node: make DEBUG_COLORS able to disable coloring
* *: export the `colors` array
* npmignore: don't publish the `dist` dir
* Makefile: refactor to use browserify
* package: add "browserify" as a dev dependency
* Readme: add Web Inspector Colors section
* node: reset terminal color for the debug content
* node: map "%o" to `util.inspect()`
* browser: map "%j" to `JSON.stringify()`
* debug: add custom "formatters"
* debug: use "ms" module for humanizing the diff
* Readme: add "bash" syntax highlighting
* browser: add Firebug color support
* browser: add colors for WebKit browsers
* node: apply log to `console`
* rewrite: abstract common logic for Node & browsers
* add .jshintrc file
0.8.1 / 2014-04-14
==================
* package: re-add the "component" section
0.8.0 / 2014-03-30
==================
* add `enable()` method for nodejs. Closes #27
* change from stderr to stdout
* remove unnecessary index.js file
0.7.4 / 2013-11-13
==================
* remove "browserify" key from package.json (fixes something in browserify)
0.7.3 / 2013-10-30
==================
* fix: catch localStorage security error when cookies are blocked (Chrome)
* add debug(err) support. Closes #46
* add .browser prop to package.json. Closes #42
0.7.2 / 2013-02-06
==================
* fix package.json
* fix: Mobile Safari (private mode) is broken with debug
* fix: Use unicode to send escape character to shell instead of octal to work with strict mode javascript
0.7.1 / 2013-02-05
==================
* add repository URL to package.json
* add DEBUG_COLORED to force colored output
* add browserify support
* fix component. Closes #24
0.7.0 / 2012-05-04
==================
* Added .component to package.json
* Added debug.component.js build
0.6.0 / 2012-03-16
==================
* Added support for "-" prefix in DEBUG [Vinay Pulim]
* Added `.enabled` flag to the node version [TooTallNate]
0.5.0 / 2012-02-02
==================
* Added: humanize diffs. Closes #8
* Added `debug.disable()` to the CS variant
* Removed padding. Closes #10
* Fixed: persist client-side variant again. Closes #9
0.4.0 / 2012-02-01
==================
* Added browser variant support for older browsers [TooTallNate]
* Added `debug.enable('project:*')` to browser variant [TooTallNate]
* Added padding to diff (moved it to the right)
0.3.0 / 2012-01-26
==================
* Added millisecond diff when isatty, otherwise UTC string
0.2.0 / 2012-01-22
==================
* Added wildcard support
0.1.0 / 2011-12-02
==================
* Added: remove colors unless stderr isatty [TooTallNate]
0.0.1 / 2010-01-03
==================
* Initial release

View File

@ -0,0 +1,19 @@
(The MIT License)
Copyright (c) 2014 TJ Holowaychuk <tj@vision-media.ca>
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.

View File

@ -0,0 +1,37 @@
# get Makefile directory name: http://stackoverflow.com/a/5982798/376773
THIS_MAKEFILE_PATH:=$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
THIS_DIR:=$(shell cd $(dir $(THIS_MAKEFILE_PATH));pwd)
# BIN directory
BIN := $(THIS_DIR)/node_modules/.bin
# applications
NODE ?= $(shell which node)
YARN ?= $(shell which yarn)
PKG ?= $(if $(YARN),$(YARN),$(NODE) $(shell which npm))
BROWSERIFY ?= $(NODE) $(BIN)/browserify
all: dist/debug.js
install: node_modules
clean:
@rm -rf dist
dist:
@mkdir -p $@
dist/debug.js: node_modules browser.js debug.js dist
@$(BROWSERIFY) \
--standalone debug \
. > $@
distclean: clean
@rm -rf node_modules
node_modules: package.json
@NODE_ENV= $(PKG) install
@touch node_modules
.PHONY: all install clean distclean

View File

@ -0,0 +1,199 @@
# debug
tiny node.js debugging utility modelled after node core's debugging technique.
## Installation
```bash
$ npm install debug
```
## Usage
With `debug` you simply invoke the exported function to generate your debug function, passing it a name which will determine if a noop function is returned, or a decorated `console.error`, so all of the `console` [format string goodies](https://developer.chrome.com/devtools/docs/console-api#consolelogobject-object) you're used to work fine. A unique color is selected per-function for visibility.
Example _app.js_:
```js
var debug = require('debug')('http')
, http = require('http')
, name = 'My App';
// fake app
debug('booting %s', 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_:
```js
var debug = require('debug')('worker');
setInterval(function(){
debug('doing some work');
}, 1000);
```
The __DEBUG__ environment variable is then used to enable these based on space or comma-delimited names. Here are some examples:
![debug http and worker](http://f.cl.ly/items/18471z1H402O24072r1J/Screenshot.png)
![debug worker](http://f.cl.ly/items/1X413v1a3M0d3C2c1E0i/Screenshot.png)
#### Windows note
On Windows the environment variable is set using the `set` command.
```cmd
set DEBUG=*,-not_this
```
Note that PowerShell using different syntax to set environment variables.
```cmd
$env:DEBUG = "*,-not_this"
```
Then, run the program to be debugged as usual.
## 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.
![](http://f.cl.ly/items/2i3h1d3t121M2Z1A3Q0N/Screenshot.png)
When stdout is not a TTY, `Date#toUTCString()` is used, making it more useful for logging the debug information as shown below:
![](http://f.cl.ly/items/112H3i0e0o0P0a2Q2r11/Screenshot.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".
## 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:".
## Browser support
Debug works in the browser as well, 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);
```
#### Web Inspector Colors
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).
Colored output looks something like:
![](https://cloud.githubusercontent.com/assets/71256/3139768/b98c5fd8-e8ef-11e3-862a-f7253b6f47c6.png)
## Output streams
### stderr vs stdout
By default `debug` will log to stderr, however this can be changed by setting the environment variable `DEBUG_FD` to `1` for stdout and `2` for stderr (the default value).
You can also set an alternative logging method per-namespace by overriding the `log` method on a per-namespace or globally:
Example _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');
```
### Save debug output to a file
You can save all debug statements to a file by piping them.
Example:
```bash
$ DEBUG_FD=3 node your-app.js 3> whatever.log
```
### Terminal colors
By default colors will only be used in a TTY. However this can be overridden by setting the environment variable `DEBUG_COLORS` to `1`.
Note: Certain IDEs (such as WebStorm) don't support colors on stderr. In these cases you must set `DEBUG_COLORS` to `1` and additionally change `DEBUG_FD` to `1`.
## Authors
- TJ Holowaychuk
- Nathan Rajlich
- Andrew Rhyne
## License
(The MIT License)
Copyright (c) 2014-2016 TJ Holowaychuk &lt;tj@vision-media.ca&gt;
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.

View File

@ -0,0 +1,29 @@
{
"name": "visionmedia-debug",
"main": "dist/debug.js",
"homepage": "https://github.com/visionmedia/debug",
"authors": [
"TJ Holowaychuk <tj@vision-media.ca>",
"Nathan Rajlich <nathan@tootallnate.net> (http://n8.io)",
"Andrew Rhyne <rhyneandrew@gmail.com>"
],
"description": "visionmedia-debug",
"moduleType": [
"amd",
"es6",
"globals",
"node"
],
"keywords": [
"visionmedia",
"debug"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
]
}

View File

@ -0,0 +1,177 @@
/**
* This is the web browser implementation of `debug()`.
*
* Expose `debug()` as the module.
*/
exports = module.exports = require('./debug');
exports.log = log;
exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
exports.storage = 'undefined' != typeof chrome
&& 'undefined' != typeof chrome.storage
? chrome.storage.local
: localstorage();
/**
* Colors.
*/
exports.colors = [
'lightseagreen',
'forestgreen',
'goldenrod',
'dodgerblue',
'darkorchid',
'crimson'
];
/**
* 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
*/
function useColors() {
// 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' && 'WebkitAppearance' in document.documentElement.style) ||
// is firebug? http://stackoverflow.com/a/398120/376773
(window.console && (console.firebug || (console.exception && console.table))) ||
// is firefox >= v31?
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
(navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31);
}
/**
* Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
*/
exports.formatters.j = function(v) {
try {
return JSON.stringify(v);
} catch (err) {
return '[UnexpectedJSONParseError]: ' + err.message;
}
};
/**
* Colorize log arguments if enabled.
*
* @api public
*/
function formatArgs() {
var args = arguments;
var useColors = this.useColors;
args[0] = (useColors ? '%c' : '')
+ this.namespace
+ (useColors ? ' %c' : ' ')
+ args[0]
+ (useColors ? '%c ' : ' ')
+ '+' + exports.humanize(this.diff);
if (!useColors) return args;
var c = 'color: ' + this.color;
args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1));
// 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
var index = 0;
var lastC = 0;
args[0].replace(/%[a-z%]/g, function(match) {
if ('%%' === match) return;
index++;
if ('%c' === match) {
// we only are interested in the *last* %c
// (the user may have provided their own)
lastC = index;
}
});
args.splice(lastC, 0, c);
return args;
}
/**
* Invokes `console.log()` when available.
* No-op when `console.log` is not a "function".
*
* @api public
*/
function log() {
// this hackery is required for IE8/9, where
// the `console.log` function doesn't have 'apply'
return 'object' === typeof console
&& console.log
&& Function.prototype.apply.call(console.log, console, arguments);
}
/**
* Save `namespaces`.
*
* @param {String} namespaces
* @api private
*/
function save(namespaces) {
try {
if (null == namespaces) {
exports.storage.removeItem('debug');
} else {
exports.storage.debug = namespaces;
}
} catch(e) {}
}
/**
* Load `namespaces`.
*
* @return {String} returns the previously persisted debug modes
* @api private
*/
function load() {
var r;
try {
return exports.storage.debug;
} catch(e) {}
// If debug isn't set in LS, and we're in Electron, try to load $DEBUG
if (typeof process !== 'undefined' && 'env' in process) {
return process.env.DEBUG;
}
}
/**
* Enable namespaces listed in `localStorage.debug` initially.
*/
exports.enable(load());
/**
* 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 {
return window.localStorage;
} catch (e) {}
}

View File

@ -0,0 +1,19 @@
{
"name": "debug",
"repo": "visionmedia/debug",
"description": "small debugging utility",
"version": "2.3.3",
"keywords": [
"debug",
"log",
"debugger"
],
"main": "browser.js",
"scripts": [
"browser.js",
"debug.js"
],
"dependencies": {
"rauchg/ms.js": "0.7.1"
}
}

View File

@ -0,0 +1,200 @@
/**
* This is the common logic for both the Node.js and web browser
* implementations of `debug()`.
*
* Expose `debug()` as the module.
*/
exports = module.exports = debug.debug = debug;
exports.coerce = coerce;
exports.disable = disable;
exports.enable = enable;
exports.enabled = enabled;
exports.humanize = require('ms');
/**
* The currently active debug mode names, and names to skip.
*/
exports.names = [];
exports.skips = [];
/**
* Map of special "%n" handling functions, for the debug "format" argument.
*
* Valid key names are a single, lowercased letter, i.e. "n".
*/
exports.formatters = {};
/**
* Previously assigned color.
*/
var prevColor = 0;
/**
* Previous log timestamp.
*/
var prevTime;
/**
* Select a color.
*
* @return {Number}
* @api private
*/
function selectColor() {
return exports.colors[prevColor++ % exports.colors.length];
}
/**
* Create a debugger with the given `namespace`.
*
* @param {String} namespace
* @return {Function}
* @api public
*/
function debug(namespace) {
// define the `disabled` version
function disabled() {
}
disabled.enabled = false;
// define the `enabled` version
function enabled() {
var self = enabled;
// set `diff` timestamp
var curr = +new Date();
var ms = curr - (prevTime || curr);
self.diff = ms;
self.prev = prevTime;
self.curr = curr;
prevTime = curr;
// add the `color` if not set
if (null == self.useColors) self.useColors = exports.useColors();
if (null == self.color && self.useColors) self.color = selectColor();
var args = new Array(arguments.length);
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i];
}
args[0] = exports.coerce(args[0]);
if ('string' !== typeof args[0]) {
// anything else let's inspect with %o
args = ['%o'].concat(args);
}
// apply any `formatters` transformations
var index = 0;
args[0] = args[0].replace(/%([a-z%])/g, function(match, format) {
// if we encounter an escaped % then don't increase the array index
if (match === '%%') return match;
index++;
var formatter = exports.formatters[format];
if ('function' === typeof formatter) {
var 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
args = exports.formatArgs.apply(self, args);
var logFn = enabled.log || exports.log || console.log.bind(console);
logFn.apply(self, args);
}
enabled.enabled = true;
var fn = exports.enabled(namespace) ? enabled : disabled;
fn.namespace = namespace;
return fn;
}
/**
* Enables a debug mode by namespaces. This can include modes
* separated by a colon and wildcards.
*
* @param {String} namespaces
* @api public
*/
function enable(namespaces) {
exports.save(namespaces);
var split = (namespaces || '').split(/[\s,]+/);
var len = split.length;
for (var i = 0; i < len; i++) {
if (!split[i]) continue; // ignore empty strings
namespaces = split[i].replace(/[\\^$+?.()|[\]{}]/g, '\\$&').replace(/\*/g, '.*?');
if (namespaces[0] === '-') {
exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
} else {
exports.names.push(new RegExp('^' + namespaces + '$'));
}
}
}
/**
* Disable debug output.
*
* @api public
*/
function disable() {
exports.enable('');
}
/**
* Returns true if the given mode name is enabled, false otherwise.
*
* @param {String} name
* @return {Boolean}
* @api public
*/
function enabled(name) {
var i, len;
for (i = 0, len = exports.skips.length; i < len; i++) {
if (exports.skips[i].test(name)) {
return false;
}
}
for (i = 0, len = exports.names.length; i < len; i++) {
if (exports.names[i].test(name)) {
return true;
}
}
return false;
}
/**
* Coerce `val`.
*
* @param {Mixed} val
* @return {Mixed}
* @api private
*/
function coerce(val) {
if (val instanceof Error) return val.stack || val.message;
return val;
}

View File

@ -0,0 +1,10 @@
/**
* Detect Electron renderer process, which is node, but we should
* treat as a browser.
*/
if (typeof process !== 'undefined' && process.type === 'renderer') {
module.exports = require('./browser.js');
} else {
module.exports = require('./node.js');
}

View File

@ -0,0 +1,213 @@
/**
* Module dependencies.
*/
var tty = require('tty');
var util = require('util');
/**
* This is the Node.js implementation of `debug()`.
*
* Expose `debug()` as the module.
*/
exports = module.exports = require('./debug');
exports.log = log;
exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
/**
* Colors.
*/
exports.colors = [6, 2, 3, 4, 5, 1];
/**
* The file descriptor to write the `debug()` calls to.
* Set the `DEBUG_FD` env variable to override with another value. i.e.:
*
* $ DEBUG_FD=3 node script.js 3>debug.log
*/
var fd = parseInt(process.env.DEBUG_FD, 10) || 2;
var stream = 1 === fd ? process.stdout :
2 === fd ? process.stderr :
createWritableStdioStream(fd);
/**
* Is stdout a TTY? Colored output is enabled when `true`.
*/
function useColors() {
var debugColors = (process.env.DEBUG_COLORS || '').trim().toLowerCase();
if (0 === debugColors.length) {
return tty.isatty(fd);
} else {
return '0' !== debugColors
&& 'no' !== debugColors
&& 'false' !== debugColors
&& 'disabled' !== debugColors;
}
}
/**
* Map %o to `util.inspect()`, since Node doesn't do that out of the box.
*/
var inspect = (4 === util.inspect.length ?
// node <= 0.8.x
function (v, colors) {
return util.inspect(v, void 0, void 0, colors);
} :
// node > 0.8.x
function (v, colors) {
return util.inspect(v, { colors: colors });
}
);
exports.formatters.o = exports.formatters.O = function(v) {
return inspect(v, this.useColors)
.replace(/\s*\n\s*/g, ' ');
};
/**
* Adds ANSI color escape codes if enabled.
*
* @api public
*/
function formatArgs() {
var len = arguments.length;
var args = new Array(len);
var useColors = this.useColors;
var name = this.namespace;
for (var i = 0; i < len; i++) {
args[i] = arguments[i];
}
if (useColors) {
var c = this.color;
args[0] = ' \u001b[3' + c + ';1m' + name + ' '
+ '\u001b[0m'
+ args[0];
args.push('\u001b[3' + c + 'm+' + exports.humanize(this.diff) + '\u001b[0m');
} else {
args[0] = new Date().toUTCString()
+ ' ' + name + ' ' + args[0];
}
return args;
}
/**
* Invokes `console.error()` with the specified arguments.
*/
function log() {
return stream.write(util.format.apply(this, arguments) + '\n');
}
/**
* Save `namespaces`.
*
* @param {String} namespaces
* @api private
*/
function save(namespaces) {
if (null == namespaces) {
// 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;
} else {
process.env.DEBUG = namespaces;
}
}
/**
* Load `namespaces`.
*
* @return {String} returns the previously persisted debug modes
* @api private
*/
function load() {
return process.env.DEBUG;
}
/**
* Copied from `node/src/node.js`.
*
* XXX: It's lame that node doesn't expose this API out-of-the-box. It also
* relies on the undocumented `tty_wrap.guessHandleType()` which is also lame.
*/
function createWritableStdioStream (fd) {
var stream;
var tty_wrap = process.binding('tty_wrap');
// Note stream._type is used for test-module-load-list.js
switch (tty_wrap.guessHandleType(fd)) {
case 'TTY':
stream = new tty.WriteStream(fd);
stream._type = 'tty';
// Hack to have stream not keep the event loop alive.
// See https://github.com/joyent/node/issues/1726
if (stream._handle && stream._handle.unref) {
stream._handle.unref();
}
break;
case 'FILE':
var fs = require('fs');
stream = new fs.SyncWriteStream(fd, { autoClose: false });
stream._type = 'fs';
break;
case 'PIPE':
case 'TCP':
var net = require('net');
stream = new net.Socket({
fd: fd,
readable: false,
writable: true
});
// FIXME Should probably have an option in net.Socket to create a
// stream from an existing fd which is writable only. But for now
// we'll just add this hack and set the `readable` member to false.
// Test: ./node test/fixtures/echo.js < /etc/passwd
stream.readable = false;
stream.read = null;
stream._type = 'pipe';
// FIXME Hack to have stream not keep the event loop alive.
// See https://github.com/joyent/node/issues/1726
if (stream._handle && stream._handle.unref) {
stream._handle.unref();
}
break;
default:
// Probably an error on in uv_guess_handle()
throw new Error('Implement me. Unknown stream file type!');
}
// For supporting legacy API we put the FD here.
stream.fd = fd;
stream._isStdio = true;
return stream;
}
/**
* Enable namespaces listed in `process.env.DEBUG` initially.
*/
exports.enable(load());

View File

@ -0,0 +1,35 @@
{
"name": "debug",
"version": "2.3.3",
"repository": {
"type": "git",
"url": "git://github.com/visionmedia/debug.git"
},
"description": "small debugging utility",
"keywords": [
"debug",
"log",
"debugger"
],
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
"Nathan Rajlich <nathan@tootallnate.net> (http://n8.io)",
"Andrew Rhyne <rhyneandrew@gmail.com>"
],
"license": "MIT",
"dependencies": {
"ms": "0.7.2"
},
"devDependencies": {
"browserify": "9.0.3",
"mocha": "*"
},
"main": "./index.js",
"browser": "./browser.js",
"component": {
"scripts": {
"debug/index.js": "browser.js",
"debug/debug.js": "debug.js"
}
}
}

View File

@ -0,0 +1,141 @@
1.3.2 / 2016-12-07
==================
* [chore] Bump dependencies (#78)
1.3.1 / 2016-10-20
==================
* [fix] Add safety check for global object (#71)
* [fix] decodePacket now accepts both Buffer and ArrayBuffer as data (#64)
* [fix] Handle undefined case properly when decoding packet (#74)
* [chore] Bump zuul to 3.11.0 & zuul-ngrok to 4.0.0 (#70)
* [chore] Update zuul browser settings (#73)
1.3.0 / 2016-09-26
==================
* [fix] Fix crashes in React Native "navigator is not defined" (#55)
* [refactor] Require base64-arraybuffer module conditionally. (#58)
* [perf] Split try catch into separate function (#65)
* [chore] Use wtf-8 instead of utf8 to prevent lone surrogates from generating parsing error (#68)
* [chore] Restrict files included in npm package (#67)
* [chore] Update license and repository url (#66)
* [chore] Update zuul browser settings following EOL notices (#62)
* [chore] bump zuul (#56)
1.2.4 / 2015-12-04
==================
* fix `ArrayBuffer` encoding in base64 string
1.2.3 / 2015-11-28
==================
* fix encoding blob as base64
1.2.2 / 2015-09-09
==================
* fixes for iojs/node
1.2.1 / 2015-01-17
==================
* pass has-binary result to encodePacket [rase-]
* Fix parse error [rase-]
1.2.0 / 2015-01-11
==================
* fix return type for decodePacket
* README fixes
* use travis matrix for better test runs
* encode into binary only if needed
* add test cases for base64 object encoding.
* add encodeBase64Object to encoder for browser
* avoid sending Blobs on PhantomJS (as on Android)
* test that utf8 encoding is not on by default but can be switched on manually
1.1.0 / 2014-07-16
==================
* make utf8 encoding/decoding optional
1.0.8 / 2014-07-16
==================
* adjust protocol revision
* handle invalid utf8 errors gracefully
* fix memory leak on browser
1.0.7 / 2014-06-24
==================
* fix decodePayloadAsBinary memory leak [christophwitzko]
* README improvements
1.0.6 / 2014-05-30
==================
* utf8 fixes when using binary encoding [nkzawa]
1.0.5 / 2014-05-06
==================
* fix range error
1.0.4 / 2014-04-13
==================
* fix `encodePayloadAsBinary` method encodes packets to base64
1.0.3 / 2014-04-10
==================
* Fix length calculation when encoding as binary [binlain]
1.0.2 / 2014-03-16
==================
* fix binary for android due to a bug in Blob XHR2 implementation [Rase-]
1.0.1 / 2014-03-06
==================
* implement `blob` module to simplify code
* bump `arraybuffer.slice`
* style fixes
1.0.0 / 2014-02-18
==================
* parser: added binary encoding [Rase-]
* parser: switched to an async interface [Rase-]
0.3.0 / 2013-03-16
==================
* parser: if callback returns `false` ignore rest of payload
* test: fixed all broken tests
0.2.1 / 2013-03-16
==================
* added protocol version to index.js [albertyfwu]
0.2.0 / 2013-02-26
==================
* Changed `decodePayload` to use a callback instead of returning an array [sweetieSong, albertyfwu]
0.1.1 / 2013-01-26
==================
* package.json fixes
0.1.0 / 2013-01-19
==================
* Initial release

View File

@ -0,0 +1,22 @@
(The MIT License)
Copyright (c) 2016 Guillermo Rauch (@rauchg)
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.

View File

@ -0,0 +1,202 @@
# engine.io-parser
[![Build Status](https://secure.travis-ci.org/socketio/engine.io-parser.svg)](http://travis-ci.org/socketio/engine.io-parser)
[![NPM version](https://badge.fury.io/js/engine.io-parser.svg)](http://badge.fury.io/js/engine.io-parser)
This is the JavaScript parser for the engine.io protocol encoding,
shared by both
[engine.io-client](https://github.com/socketio/engine.io-client) and
[engine.io](https://github.com/socketio/engine.io).
## How to use
### Standalone
The parser can encode/decode packets, payloads, and payloads as binary
with the following methods: `encodePacket`, `decodePacket`, `encodePayload`,
`decodePayload`, `encodePayloadAsBinary`, `decodePayloadAsBinary`.
The browser-side parser also includes `encodePayloadAsArrayBuffer` and `encodePayloadAsBlob`.
Example:
```js
var parser = require('engine.io-parser');
var data = new Buffer(5);
for (var i = 0; i < data.length; i++) { data[i] = i; }
parser.encodePacket({ type: 'message', data: data }, function(encoded) {
var decodedData = parser.decodePacket(encoded); // { type: 'message', data: data }
});
```
### With browserify
Engine.IO Parser is a commonjs module, which means you can include it by using
`require` on the browser and package using [browserify](http://browserify.org/):
1. install the parser package
```shell
npm install engine.io-parser
```
1. write your app code
```js
var parser = require('engine.io-parser');
var testBuffer = new Int8Array(10);
for (var i = 0; i < testBuffer.length; i++) testBuffer[i] = i;
var packets = [{ type: 'message', data: testBuffer.buffer }, { type: 'message', data: 'hello' }];
parser.encodePayload(packets, function(encoded) {
parser.decodePayload(encoded,
function(packet, index, total) {
var isLast = index + 1 == total;
if (!isLast) {
var buffer = new Int8Array(packet.data); // testBuffer
} else {
var message = packet.data; // 'hello'
}
});
});
```
1. build your app bundle
```bash
$ browserify app.js > bundle.js
```
1. include on your page
```html
<script src="/path/to/bundle.js"></script>
```
## Features
- Runs on browser and node.js seamlessly
- Runs inside HTML5 WebWorker
- Can encode and decode packets
- Encodes from/to ArrayBuffer or Blob when in browser, and Buffer or ArrayBuffer in Node
## API
Note: `cb(type)` means the type is a callback function that contains a parameter of type `type` when called.
### Node
- `encodePacket`
- Encodes a packet.
- **Parameters**
- `Object`: the packet to encode, has `type` and `data`.
- `data`: can be a `String`, `Number`, `Buffer`, `ArrayBuffer`
- `Boolean`: optional, binary support
- `Function`: callback, returns the encoded packet (`cb(String)`)
- `decodePacket`
- Decodes a packet. Data also available as an ArrayBuffer if requested.
- Returns data as `String` or (`Blob` on browser, `ArrayBuffer` on Node)
- **Parameters**
- `String` | `ArrayBuffer`: the packet to decode, has `type` and `data`
- `String`: optional, the binary type
- `encodeBase64Packet`
- Encodes a packet with binary data in a base64 string (`String`)
- **Parameters**
- `Object`: the packet to encode, has `type` and `data`
- `Function`: callback, returns the base64 encoded message (`cb(String)`)
- `decodeBase64Packet`
- Decodes a packet encoded in a base64 string.
- **Parameters**
- `String`: the base64 encoded message
- `String`: optional, the binary type
- `encodePayload`
- Encodes multiple messages (payload).
- If any contents are binary, they will be encoded as base64 strings. Base64
encoded strings are marked with a b before the length specifier
- **Parameters**
- `Array`: an array of packets
- `Boolean`: optional, binary support
- `Function`: callback, returns the encoded payload (`cb(String)`)
- `decodePayload`
- Decodes data when a payload is maybe expected. Possible binary contents are
decoded from their base64 representation.
- **Parameters**
- `String`: the payload
- `String`: optional, the binary type
- `Function`: callback, returns (cb(`Object`: packet, `Number`:packet index, `Number`:packet total))
- `encodePayloadAsBinary`
- Encodes multiple messages (payload) as binary.
- **Parameters**
- `Array`: an array of packets
- `Function`: callback, returns the encoded payload (`cb(Buffer)`)
- `decodePayloadAsBinary`
- 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.
- **Parameters**
- `Buffer`: the buffer
- `String`: optional, the binary type
- `Function`: callback, returns the decoded packet (`cb(Object)`)
### Browser
- `encodePayloadAsArrayBuffer`
- Encodes multiple messages (payload) as binary.
- **Parameters**
- `Array`: an array of packets
- `Function`: callback, returns the encoded payload (`cb(ArrayBuffer)`)
- `encodePayloadAsBlob`
- Encodes multiple messages (payload) as blob.
- **Parameters**
- `Array`: an array of packets
- `Function`: callback, returns the encoded payload (`cb(Blob)`)
## Tests
Standalone tests can be run with `make test` which will run both node.js and browser tests.
Browser tests are run using [zuul](https://github.com/defunctzombie/zuul).
(You must have zuul setup with a saucelabs account.)
You can run the tests locally using the following command:
```
./node_modules/.bin/zuul --local 8080 -- test/index.js
```
## Support
The support channels for `engine.io-parser` 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:
```bash
git clone git://github.com/LearnBoost/engine.io-parser.git
```
Then:
```bash
cd engine.io-parser
npm install
```
See the `Tests` section above for how to run tests before submitting any patches.
## License
MIT

View File

@ -0,0 +1,2 @@
module.exports = require('./lib/');

View File

@ -0,0 +1,609 @@
/**
* Module dependencies.
*/
var keys = require('./keys');
var hasBinary = require('has-binary');
var sliceBuffer = require('arraybuffer.slice');
var after = require('after');
var utf8 = require('wtf-8');
var base64encoder;
if (global && global.ArrayBuffer) {
base64encoder = require('base64-arraybuffer');
}
/**
* Check if we are running an android browser. That requires us to use
* ArrayBuffer with polling transports...
*
* http://ghinda.net/jpeg-blob-ajax-android/
*/
var isAndroid = typeof navigator !== 'undefined' && /Android/i.test(navigator.userAgent);
/**
* Check if we are running in PhantomJS.
* Uploading a Blob with PhantomJS does not work correctly, as reported here:
* https://github.com/ariya/phantomjs/issues/11395
* @type boolean
*/
var isPhantomJS = typeof navigator !== 'undefined' && /PhantomJS/i.test(navigator.userAgent);
/**
* When true, avoids using Blobs to encode payloads.
* @type boolean
*/
var dontSendBlobs = isAndroid || isPhantomJS;
/**
* Current protocol version.
*/
exports.protocol = 3;
/**
* Packet types.
*/
var packets = exports.packets = {
open: 0 // non-ws
, close: 1 // non-ws
, ping: 2
, pong: 3
, message: 4
, upgrade: 5
, noop: 6
};
var packetslist = keys(packets);
/**
* Premade error packet.
*/
var err = { type: 'error', data: 'parser error' };
/**
* Create a blob api even for blob builder when vendor prefixes exist
*/
var Blob = require('blob');
/**
* Encodes a packet.
*
* <packet type id> [ <data> ]
*
* Example:
*
* 5hello world
* 3
* 4
*
* Binary is encoded in an identical principle
*
* @api private
*/
exports.encodePacket = function (packet, supportsBinary, utf8encode, callback) {
if ('function' == typeof supportsBinary) {
callback = supportsBinary;
supportsBinary = false;
}
if ('function' == typeof utf8encode) {
callback = utf8encode;
utf8encode = null;
}
var data = (packet.data === undefined)
? undefined
: packet.data.buffer || packet.data;
if (global.ArrayBuffer && data instanceof ArrayBuffer) {
return encodeArrayBuffer(packet, supportsBinary, callback);
} else if (Blob && data instanceof global.Blob) {
return encodeBlob(packet, supportsBinary, callback);
}
// might be an object with { base64: true, data: dataAsBase64String }
if (data && data.base64) {
return encodeBase64Object(packet, callback);
}
// Sending data as a utf-8 string
var encoded = packets[packet.type];
// data fragment is optional
if (undefined !== packet.data) {
encoded += utf8encode ? utf8.encode(String(packet.data)) : String(packet.data);
}
return callback('' + encoded);
};
function encodeBase64Object(packet, callback) {
// packet data is an object { base64: true, data: dataAsBase64String }
var message = 'b' + exports.packets[packet.type] + packet.data.data;
return callback(message);
}
/**
* Encode packet helpers for binary types
*/
function encodeArrayBuffer(packet, supportsBinary, callback) {
if (!supportsBinary) {
return exports.encodeBase64Packet(packet, callback);
}
var data = packet.data;
var contentArray = new Uint8Array(data);
var resultBuffer = new Uint8Array(1 + data.byteLength);
resultBuffer[0] = packets[packet.type];
for (var i = 0; i < contentArray.length; i++) {
resultBuffer[i+1] = contentArray[i];
}
return callback(resultBuffer.buffer);
}
function encodeBlobAsArrayBuffer(packet, supportsBinary, callback) {
if (!supportsBinary) {
return exports.encodeBase64Packet(packet, callback);
}
var fr = new FileReader();
fr.onload = function() {
packet.data = fr.result;
exports.encodePacket(packet, supportsBinary, true, callback);
};
return fr.readAsArrayBuffer(packet.data);
}
function encodeBlob(packet, supportsBinary, callback) {
if (!supportsBinary) {
return exports.encodeBase64Packet(packet, callback);
}
if (dontSendBlobs) {
return encodeBlobAsArrayBuffer(packet, supportsBinary, callback);
}
var length = new Uint8Array(1);
length[0] = packets[packet.type];
var blob = new Blob([length.buffer, packet.data]);
return callback(blob);
}
/**
* Encodes a packet with binary data in a base64 string
*
* @param {Object} packet, has `type` and `data`
* @return {String} base64 encoded message
*/
exports.encodeBase64Packet = function(packet, callback) {
var message = 'b' + exports.packets[packet.type];
if (Blob && packet.data instanceof global.Blob) {
var fr = new FileReader();
fr.onload = function() {
var b64 = fr.result.split(',')[1];
callback(message + b64);
};
return fr.readAsDataURL(packet.data);
}
var b64data;
try {
b64data = String.fromCharCode.apply(null, new Uint8Array(packet.data));
} catch (e) {
// iPhone Safari doesn't let you apply with typed arrays
var typed = new Uint8Array(packet.data);
var basic = new Array(typed.length);
for (var i = 0; i < typed.length; i++) {
basic[i] = typed[i];
}
b64data = String.fromCharCode.apply(null, basic);
}
message += global.btoa(b64data);
return callback(message);
};
/**
* Decodes a packet. Changes format to Blob if requested.
*
* @return {Object} with `type` and `data` (if any)
* @api private
*/
exports.decodePacket = function (data, binaryType, utf8decode) {
if (data === undefined) {
return err;
}
// String data
if (typeof data == 'string') {
if (data.charAt(0) == 'b') {
return exports.decodeBase64Packet(data.substr(1), binaryType);
}
if (utf8decode) {
data = tryDecode(data);
if (data === false) {
return err;
}
}
var type = data.charAt(0);
if (Number(type) != type || !packetslist[type]) {
return err;
}
if (data.length > 1) {
return { type: packetslist[type], data: data.substring(1) };
} else {
return { type: packetslist[type] };
}
}
var asArray = new Uint8Array(data);
var type = asArray[0];
var rest = sliceBuffer(data, 1);
if (Blob && binaryType === 'blob') {
rest = new Blob([rest]);
}
return { type: packetslist[type], data: rest };
};
function tryDecode(data) {
try {
data = utf8.decode(data);
} 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)
*/
exports.decodeBase64Packet = function(msg, binaryType) {
var type = packetslist[msg.charAt(0)];
if (!base64encoder) {
return { type: type, data: { base64: true, data: msg.substr(1) } };
}
var data = base64encoder.decode(msg.substr(1));
if (binaryType === 'blob' && Blob) {
data = new Blob([data]);
}
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
*/
exports.encodePayload = function (packets, supportsBinary, callback) {
if (typeof supportsBinary == 'function') {
callback = supportsBinary;
supportsBinary = null;
}
var isBinary = hasBinary(packets);
if (supportsBinary && isBinary) {
if (Blob && !dontSendBlobs) {
return exports.encodePayloadAsBlob(packets, callback);
}
return exports.encodePayloadAsArrayBuffer(packets, callback);
}
if (!packets.length) {
return callback('0:');
}
function setLengthHeader(message) {
return message.length + ':' + message;
}
function encodeOne(packet, doneCallback) {
exports.encodePacket(packet, !isBinary ? false : supportsBinary, true, function(message) {
doneCallback(null, setLengthHeader(message));
});
}
map(packets, encodeOne, function(err, results) {
return callback(results.join(''));
});
};
/**
* Async array map using after
*/
function map(ary, each, done) {
var result = new Array(ary.length);
var next = after(ary.length, done);
var eachWithIndex = function(i, el, cb) {
each(el, function(error, msg) {
result[i] = msg;
cb(error, result);
});
};
for (var i = 0; i < ary.length; i++) {
eachWithIndex(i, ary[i], next);
}
}
/*
* Decodes data when a payload is maybe expected. Possible binary contents are
* decoded from their base64 representation
*
* @param {String} data, callback method
* @api public
*/
exports.decodePayload = function (data, binaryType, callback) {
if (typeof data != 'string') {
return exports.decodePayloadAsBinary(data, binaryType, callback);
}
if (typeof binaryType === 'function') {
callback = binaryType;
binaryType = null;
}
var packet;
if (data == '') {
// parser error - ignoring payload
return callback(err, 0, 1);
}
var length = ''
, n, msg;
for (var i = 0, l = data.length; i < l; i++) {
var chr = data.charAt(i);
if (':' != chr) {
length += chr;
} else {
if ('' == length || (length != (n = Number(length)))) {
// parser error - ignoring payload
return callback(err, 0, 1);
}
msg = data.substr(i + 1, n);
if (length != msg.length) {
// parser error - ignoring payload
return callback(err, 0, 1);
}
if (msg.length) {
packet = exports.decodePacket(msg, binaryType, true);
if (err.type == packet.type && err.data == packet.data) {
// parser error in individual packet - ignoring payload
return callback(err, 0, 1);
}
var ret = callback(packet, i + n, l);
if (false === ret) return;
}
// advance cursor
i += n;
length = '';
}
}
if (length != '') {
// parser error - ignoring payload
return callback(err, 0, 1);
}
};
/**
* 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 {ArrayBuffer} encoded payload
* @api private
*/
exports.encodePayloadAsArrayBuffer = function(packets, callback) {
if (!packets.length) {
return callback(new ArrayBuffer(0));
}
function encodeOne(packet, doneCallback) {
exports.encodePacket(packet, true, true, function(data) {
return doneCallback(null, data);
});
}
map(packets, encodeOne, function(err, encodedPackets) {
var totalLength = encodedPackets.reduce(function(acc, p) {
var len;
if (typeof p === 'string'){
len = p.length;
} else {
len = p.byteLength;
}
return acc + len.toString().length + len + 2; // string/binary identifier + separator = 2
}, 0);
var resultArray = new Uint8Array(totalLength);
var bufferIndex = 0;
encodedPackets.forEach(function(p) {
var isString = typeof p === 'string';
var ab = p;
if (isString) {
var view = new Uint8Array(p.length);
for (var i = 0; i < p.length; i++) {
view[i] = p.charCodeAt(i);
}
ab = view.buffer;
}
if (isString) { // not true binary
resultArray[bufferIndex++] = 0;
} else { // true binary
resultArray[bufferIndex++] = 1;
}
var lenStr = ab.byteLength.toString();
for (var i = 0; i < lenStr.length; i++) {
resultArray[bufferIndex++] = parseInt(lenStr[i]);
}
resultArray[bufferIndex++] = 255;
var view = new Uint8Array(ab);
for (var i = 0; i < view.length; i++) {
resultArray[bufferIndex++] = view[i];
}
});
return callback(resultArray.buffer);
});
};
/**
* Encode as Blob
*/
exports.encodePayloadAsBlob = function(packets, callback) {
function encodeOne(packet, doneCallback) {
exports.encodePacket(packet, true, true, function(encoded) {
var binaryIdentifier = new Uint8Array(1);
binaryIdentifier[0] = 1;
if (typeof encoded === 'string') {
var view = new Uint8Array(encoded.length);
for (var i = 0; i < encoded.length; i++) {
view[i] = encoded.charCodeAt(i);
}
encoded = view.buffer;
binaryIdentifier[0] = 0;
}
var len = (encoded instanceof ArrayBuffer)
? encoded.byteLength
: encoded.size;
var lenStr = len.toString();
var lengthAry = new Uint8Array(lenStr.length + 1);
for (var i = 0; i < lenStr.length; i++) {
lengthAry[i] = parseInt(lenStr[i]);
}
lengthAry[lenStr.length] = 255;
if (Blob) {
var blob = new Blob([binaryIdentifier.buffer, lengthAry.buffer, encoded]);
doneCallback(null, blob);
}
});
}
map(packets, encodeOne, function(err, results) {
return callback(new Blob(results));
});
};
/*
* 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 {ArrayBuffer} data, callback method
* @api public
*/
exports.decodePayloadAsBinary = function (data, binaryType, callback) {
if (typeof binaryType === 'function') {
callback = binaryType;
binaryType = null;
}
var bufferTail = data;
var buffers = [];
var numberTooLong = false;
while (bufferTail.byteLength > 0) {
var tailArray = new Uint8Array(bufferTail);
var isString = tailArray[0] === 0;
var msgLength = '';
for (var i = 1; ; i++) {
if (tailArray[i] == 255) break;
if (msgLength.length > 310) {
numberTooLong = true;
break;
}
msgLength += tailArray[i];
}
if(numberTooLong) return callback(err, 0, 1);
bufferTail = sliceBuffer(bufferTail, 2 + msgLength.length);
msgLength = parseInt(msgLength);
var msg = sliceBuffer(bufferTail, 0, msgLength);
if (isString) {
try {
msg = String.fromCharCode.apply(null, new Uint8Array(msg));
} catch (e) {
// iPhone Safari doesn't let you apply to typed arrays
var typed = new Uint8Array(msg);
msg = '';
for (var i = 0; i < typed.length; i++) {
msg += String.fromCharCode(typed[i]);
}
}
}
buffers.push(msg);
bufferTail = sliceBuffer(bufferTail, msgLength);
}
var total = buffers.length;
buffers.forEach(function(buffer, i) {
callback(exports.decodePacket(buffer, binaryType, true), i, total);
});
};

View File

@ -0,0 +1,474 @@
/**
* Module dependencies.
*/
var utf8 = require('wtf-8');
var after = require('after');
var keys = require('./keys');
/**
* Current protocol version.
*/
exports.protocol = 3;
/**
* Packet types.
*/
var packets = exports.packets = {
open: 0 // non-ws
, close: 1 // non-ws
, ping: 2
, pong: 3
, message: 4
, upgrade: 5
, noop: 6
};
var packetslist = keys(packets);
/**
* Premade error packet.
*/
var err = { type: 'error', data: 'parser error' };
/**
* Encodes a packet.
*
* <packet type id> [ <data> ]
*
* Example:
*
* 5hello world
* 3
* 4
*
* Binary is encoded in an identical principle
*
* @api private
*/
exports.encodePacket = function (packet, supportsBinary, utf8encode, callback) {
if ('function' == typeof supportsBinary) {
callback = supportsBinary;
supportsBinary = null;
}
if ('function' == typeof utf8encode ) {
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) {
packet.data = arrayBufferToBuffer(packet.data);
return encodeBuffer(packet, supportsBinary, callback);
}
// Sending data as a utf-8 string
var encoded = packets[packet.type];
// data fragment is optional
if (undefined !== packet.data) {
encoded += utf8encode ? utf8.encode(String(packet.data)) : String(packet.data);
}
return callback('' + encoded);
};
/**
* Encode Buffer data
*/
function encodeBuffer(packet, supportsBinary, callback) {
var data = packet.data;
if (!supportsBinary) {
return exports.encodeBase64Packet(packet, callback);
}
var typeBuffer = new Buffer(1);
typeBuffer[0] = 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
*/
exports.encodeBase64Packet = function(packet, callback){
if (!Buffer.isBuffer(packet.data)) {
packet.data = arrayBufferToBuffer(packet.data);
}
var message = 'b' + packets[packet.type];
message += packet.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
*/
exports.decodePacket = function (data, binaryType, utf8decode) {
if (data === undefined) {
return err;
}
// String data
if (typeof data == 'string') {
if (data.charAt(0) == 'b') {
return exports.decodeBase64Packet(data.substr(1), binaryType);
}
var type = data.charAt(0);
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.substring(1) };
} else {
return { type: packetslist[type] };
}
}
// Binary data
if (binaryType === 'arraybuffer') {
// wrap Buffer/ArrayBuffer data into an Uint8Array
var intArray = new Uint8Array(data);
var type = intArray[0];
return { type: packetslist[type], data: intArray.buffer.slice(1) };
}
if (data instanceof ArrayBuffer) {
data = arrayBufferToBuffer(data);
}
var type = data[0];
return { type: packetslist[type], data: data.slice(1) };
};
function tryDecode(data) {
try {
data = utf8.decode(data);
} 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)
*/
exports.decodeBase64Packet = function(msg, binaryType) {
var type = packetslist[msg.charAt(0)];
var data = new Buffer(msg.substr(1), 'base64');
if (binaryType === 'arraybuffer') {
var abv = new Uint8Array(data.length);
for (var i = 0; i < abv.length; i++){
abv[i] = data[i];
}
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
*/
exports.encodePayload = function (packets, supportsBinary, callback) {
if (typeof supportsBinary == 'function') {
callback = supportsBinary;
supportsBinary = null;
}
if (supportsBinary) {
return exports.encodePayloadAsBinary(packets, callback);
}
if (!packets.length) {
return callback('0:');
}
function setLengthHeader(message) {
return message.length + ':' + message;
}
function encodeOne(packet, doneCallback) {
exports.encodePacket(packet, supportsBinary, true, function(message) {
doneCallback(null, setLengthHeader(message));
});
}
map(packets, encodeOne, function(err, results) {
return callback(results.join(''));
});
};
/**
* Async array map using after
*/
function map(ary, each, done) {
var result = new Array(ary.length);
var next = after(ary.length, done);
var eachWithIndex = function(i, el, cb) {
each(el, function(error, msg) {
result[i] = msg;
cb(error, result);
});
};
for (var i = 0; i < ary.length; i++) {
eachWithIndex(i, ary[i], next);
}
}
/*
* Decodes data when a payload is maybe expected. Possible binary contents are
* decoded from their base64 representation
*
* @param {String} data, callback method
* @api public
*/
exports.decodePayload = function (data, binaryType, callback) {
if ('string' != typeof data) {
return exports.decodePayloadAsBinary(data, binaryType, callback);
}
if (typeof binaryType === 'function') {
callback = binaryType;
binaryType = null;
}
var packet;
if (data == '') {
// parser error - ignoring payload
return callback(err, 0, 1);
}
var length = ''
, n, msg;
for (var i = 0, l = data.length; i < l; i++) {
var chr = data.charAt(i);
if (':' != chr) {
length += chr;
} else {
if ('' == length || (length != (n = Number(length)))) {
// parser error - ignoring payload
return callback(err, 0, 1);
}
msg = data.substr(i + 1, n);
if (length != msg.length) {
// parser error - ignoring payload
return callback(err, 0, 1);
}
if (msg.length) {
packet = exports.decodePacket(msg, binaryType, true);
if (err.type == packet.type && err.data == packet.data) {
// parser error in individual packet - ignoring payload
return callback(err, 0, 1);
}
var ret = callback(packet, i + n, l);
if (false === ret) 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; i < buffer.length; i++) {
str += String.fromCharCode(buffer[i]);
}
return str;
}
/**
*
* Converts a utf8.js encoded string to a buffer
*
* @api private
*/
function stringToBuffer(string) {
var buf = new Buffer(string.length);
for (var i = 0; i < string.length; 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 array = new Uint8Array(data.buffer || data);
var length = data.byteLength || data.length;
var offset = data.byteOffset || 0;
var buffer = new Buffer(length);
for (var i = 0; i < length; i++) {
buffer[i] = array[offset + i];
}
return buffer;
}
/**
* 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
*/
exports.encodePayloadAsBinary = function (packets, callback) {
if (!packets.length) {
return callback(new Buffer(0));
}
function encodeOne(p, doneCallback) {
exports.encodePacket(p, true, true, function(packet) {
if (typeof packet === 'string') {
var encodingLength = '' + packet.length;
var sizeBuffer = new Buffer(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)]));
}
var encodingLength = '' + packet.length;
var sizeBuffer = new Buffer(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]));
});
}
map(packets, encodeOne, function(err, results) {
return callback(Buffer.concat(results));
});
};
/*
* 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
*/
exports.decodePayloadAsBinary = function (data, binaryType, callback) {
if (typeof binaryType === 'function') {
callback = binaryType;
binaryType = null;
}
var bufferTail = data;
var buffers = [];
while (bufferTail.length > 0) {
var strLen = '';
var isString = bufferTail[0] === 0;
var numberTooLong = false;
for (var i = 1; ; i++) {
if (bufferTail[i] == 255) break;
// 310 = char length of Number.MAX_VALUE
if (strLen.length > 310) {
numberTooLong = true;
break;
}
strLen += '' + bufferTail[i];
}
if(numberTooLong) return callback(err, 0, 1);
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;
buffers.forEach(function(buffer, i) {
callback(exports.decodePacket(buffer, binaryType, true), i, total);
});
};

View File

@ -0,0 +1,19 @@
/**
* Gets the keys for an object.
*
* @return {Array} keys
* @api private
*/
module.exports = Object.keys || function keys (obj){
var arr = [];
var has = Object.prototype.hasOwnProperty;
for (var i in obj) {
if (has.call(obj, i)) {
arr.push(i);
}
}
return arr;
};

View File

@ -0,0 +1,33 @@
{
"name": "engine.io-parser",
"description": "Parser for the client for the realtime Engine",
"license": "MIT",
"version": "1.3.2",
"homepage": "https://github.com/socketio/engine.io-parser",
"devDependencies": {
"expect.js": "0.3.1",
"mocha": "3.2.0",
"zuul": "3.11.1",
"zuul-ngrok": "4.0.0"
},
"dependencies": {
"after": "0.8.2",
"arraybuffer.slice": "0.0.6",
"base64-arraybuffer": "0.1.5",
"blob": "0.0.4",
"has-binary": "0.1.7",
"wtf-8": "1.0.0"
},
"scripts": {
"test": "make test"
},
"repository": {
"type": "git",
"url": "git@github.com:socketio/engine.io-parser.git"
},
"files": [
"index.js",
"lib/"
],
"browser": "./lib/browser.js"
}

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Zeit, 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.

View File

@ -0,0 +1,52 @@
# ms
[![Build Status](https://travis-ci.org/zeit/ms.svg?branch=master)](https://travis-ci.org/zeit/ms)
[![XO code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo)
[![Slack Channel](https://zeit-slackin.now.sh/badge.svg)](https://zeit.chat/)
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
```
### Convert from milliseconds
```js
ms(60000) // "1m"
ms(2 * 60000) // "2m"
ms(ms('10 hours')) // "10h"
```
### Time format written-out
```js
ms(60000, { long: true }) // "1 minute"
ms(2 * 60000, { long: true }) // "2 minutes"
ms(ms('10 hours'), { long: true }) // "10 hours"
```
## Features
- Works both in [node](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 ms is returned.
## 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 will now use your clone of ms!
As always, you can run the tests using: `npm test`

149
node_modules/engine.io-client/node_modules/ms/index.js generated vendored Normal file
View File

@ -0,0 +1,149 @@
/**
* Helpers.
*/
var s = 1000
var m = s * 60
var h = m * 60
var d = h * 24
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' && isNaN(val) === false) {
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 > 10000) {
return
}
var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|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 '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) {
if (ms >= d) {
return Math.round(ms / d) + 'd'
}
if (ms >= h) {
return Math.round(ms / h) + 'h'
}
if (ms >= m) {
return Math.round(ms / m) + 'm'
}
if (ms >= s) {
return Math.round(ms / s) + 's'
}
return ms + 'ms'
}
/**
* Long format for `ms`.
*
* @param {Number} ms
* @return {String}
* @api private
*/
function fmtLong(ms) {
return plural(ms, d, 'day') ||
plural(ms, h, 'hour') ||
plural(ms, m, 'minute') ||
plural(ms, s, 'second') ||
ms + ' ms'
}
/**
* Pluralization helper.
*/
function plural(ms, n, name) {
if (ms < n) {
return
}
if (ms < n * 1.5) {
return Math.floor(ms / n) + ' ' + name
}
return Math.ceil(ms / n) + ' ' + name + 's'
}

View File

@ -0,0 +1,36 @@
{
"name": "ms",
"version": "0.7.2",
"description": "Tiny milisecond conversion utility",
"repository": "zeit/ms",
"main": "./index",
"files": [
"index.js"
],
"scripts": {
"test": "xo && mocha test/index.js",
"test-browser": "serve ./test"
},
"license": "MIT",
"devDependencies": {
"expect.js": "^0.3.1",
"mocha": "^3.0.2",
"serve": "^1.4.0",
"xo": "^0.17.0"
},
"component": {
"scripts": {
"ms/index.js": "index.js"
}
},
"xo": {
"space": true,
"semicolon": false,
"envs": [
"mocha"
],
"rules": {
"complexity": 0
}
}
}

235
node_modules/engine.io-client/node_modules/ws/README.md generated vendored Normal file
View File

@ -0,0 +1,235 @@
# ws: a node.js websocket library
[![Build Status](https://travis-ci.org/websockets/ws.svg?branch=master)](https://travis-ci.org/websockets/ws)
`ws` is a simple to use WebSocket implementation, up-to-date against RFC-6455,
and [probably the fastest WebSocket library for node.js][archive].
Passes the quite extensive Autobahn test suite. See http://websockets.github.com/ws
for the full reports.
## Protocol support
* **Hixie draft 76** (Old and deprecated, but still in use by Safari and Opera.
Added to ws version 0.4.2, but server only. Can be disabled by setting the
`disableHixie` option to true.)
* **HyBi drafts 07-12** (Use the option `protocolVersion: 8`)
* **HyBi drafts 13-17** (Current default, alternatively option `protocolVersion: 13`)
### Installing
```
npm install --save ws
```
### Opt-in for performance
There are 2 optional modules that can be installed along side with the `ws`
module. These modules are binary addons which improve certain operations, but as
they are binary addons they require compilation which can fail if no c++
compiler is installed on the host system.
- `npm install --save bufferutil`: Improves internal buffer operations which
allows for faster processing of masked WebSocket frames and general buffer
operations.
- `npm install --save utf-8-validate`: The specification requires validation of
invalid UTF-8 chars, some of these validations could not be done in JavaScript
hence the need for a binary addon. In most cases you will already be
validating the input that you receive for security purposes leading to double
validation. But if you want to be 100% spec-conforming and have fast
validation of UTF-8 then this module is a must.
### Sending and receiving text data
```js
var WebSocket = require('ws');
var ws = new WebSocket('ws://www.host.com/path');
ws.on('open', function open() {
ws.send('something');
});
ws.on('message', function(data, flags) {
// flags.binary will be set if a binary data is received.
// flags.masked will be set if the data was masked.
});
```
### Sending binary data
```js
var WebSocket = require('ws');
var ws = new WebSocket('ws://www.host.com/path');
ws.on('open', function open() {
var array = new Float32Array(5);
for (var i = 0; i < array.length; ++i) {
array[i] = i / 2;
}
ws.send(array, { binary: true, mask: true });
});
```
Setting `mask`, as done for the send options above, will cause the data to be
masked according to the WebSocket protocol. The same option applies for text
data.
### Server example
```js
var WebSocketServer = require('ws').Server
, wss = new WebSocketServer({ port: 8080 });
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});
ws.send('something');
});
```
### ExpressJS example
```js
var server = require('http').createServer()
, url = require('url')
, WebSocketServer = require('ws').Server
, wss = new WebSocketServer({ server: server })
, express = require('express')
, app = express()
, port = 4080;
app.use(function (req, res) {
res.send({ msg: "hello" });
});
wss.on('connection', function connection(ws) {
var location = url.parse(ws.upgradeReq.url, true);
// you might use location.query.access_token to authenticate or share sessions
// or ws.upgradeReq.headers.cookie (see http://stackoverflow.com/a/16395220/151312)
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});
ws.send('something');
});
server.on('request', app);
server.listen(port, function () { console.log('Listening on ' + server.address().port) });
```
### Server sending broadcast data
```js
var WebSocketServer = require('ws').Server
, wss = new WebSocketServer({ port: 8080 });
wss.broadcast = function broadcast(data) {
wss.clients.forEach(function each(client) {
client.send(data);
});
};
```
### Error handling best practices
```js
// If the WebSocket is closed before the following send is attempted
ws.send('something');
// Errors (both immediate and async write errors) can be detected in an optional
// callback. The callback is also the only way of being notified that data has
// actually been sent.
ws.send('something', function ack(error) {
// if error is not defined, the send has been completed,
// otherwise the error object will indicate what failed.
});
// Immediate errors can also be handled with try/catch-blocks, but **note** that
// since sends are inherently asynchronous, socket write failures will *not* be
// captured when this technique is used.
try { ws.send('something'); }
catch (e) { /* handle error */ }
```
### echo.websocket.org demo
```js
var WebSocket = require('ws');
var ws = new WebSocket('ws://echo.websocket.org/', {
protocolVersion: 8,
origin: 'http://websocket.org'
});
ws.on('open', function open() {
console.log('connected');
ws.send(Date.now().toString(), {mask: true});
});
ws.on('close', function close() {
console.log('disconnected');
});
ws.on('message', function message(data, flags) {
console.log('Roundtrip time: ' + (Date.now() - parseInt(data)) + 'ms', flags);
setTimeout(function timeout() {
ws.send(Date.now().toString(), {mask: true});
}, 500);
});
```
### Other examples
For a full example with a browser client communicating with a ws server, see the
examples folder.
Note that the usage together with Express 3.0 is quite different from Express
2.x. The difference is expressed in the two different serverstats-examples.
Otherwise, see the test cases.
### Running the tests
```
make test
```
## API Docs
See [`/doc/ws.md`](https://github.com/websockets/ws/blob/master/doc/ws.md) for Node.js-like docs for the ws classes.
## Changelog
We're using the GitHub [`releases`](https://github.com/websockets/ws/releases) for changelog entries.
## License
(The MIT License)
Copyright (c) 2011 Einar Otto Stangvik &lt;einaros@gmail.com&gt;
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.
[archive]: http://web.archive.org/web/20130314230536/http://hobbycoding.posterous.com/the-fastest-websocket-module-for-nodejs

49
node_modules/engine.io-client/node_modules/ws/index.js generated vendored Normal file
View File

@ -0,0 +1,49 @@
'use strict';
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
var WS = module.exports = require('./lib/WebSocket');
WS.Server = require('./lib/WebSocketServer');
WS.Sender = require('./lib/Sender');
WS.Receiver = require('./lib/Receiver');
/**
* Create a new WebSocket server.
*
* @param {Object} options Server options
* @param {Function} fn Optional connection listener.
* @returns {WS.Server}
* @api public
*/
WS.createServer = function createServer(options, fn) {
var server = new WS.Server(options);
if (typeof fn === 'function') {
server.on('connection', fn);
}
return server;
};
/**
* Create a new WebSocket connection.
*
* @param {String} address The URL/address we need to connect to.
* @param {Function} fn Open listener.
* @returns {WS}
* @api public
*/
WS.connect = WS.createConnection = function connect(address, fn) {
var client = new WS(address);
if (typeof fn === 'function') {
client.on('open', fn);
}
return client;
};

Binary file not shown.

View File

@ -0,0 +1,63 @@
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
var util = require('util');
function BufferPool(initialSize, growStrategy, shrinkStrategy) {
if (this instanceof BufferPool === false) {
throw new TypeError("Classes can't be function-called");
}
if (typeof initialSize === 'function') {
shrinkStrategy = growStrategy;
growStrategy = initialSize;
initialSize = 0;
}
else if (typeof initialSize === 'undefined') {
initialSize = 0;
}
this._growStrategy = (growStrategy || function(db, size) {
return db.used + size;
}).bind(null, this);
this._shrinkStrategy = (shrinkStrategy || function(db) {
return initialSize;
}).bind(null, this);
this._buffer = initialSize ? new Buffer(initialSize) : null;
this._offset = 0;
this._used = 0;
this._changeFactor = 0;
this.__defineGetter__('size', function(){
return this._buffer == null ? 0 : this._buffer.length;
});
this.__defineGetter__('used', function(){
return this._used;
});
}
BufferPool.prototype.get = function(length) {
if (this._buffer == null || this._offset + length > this._buffer.length) {
var newBuf = new Buffer(this._growStrategy(length));
this._buffer = newBuf;
this._offset = 0;
}
this._used += length;
var buf = this._buffer.slice(this._offset, this._offset + length);
this._offset += length;
return buf;
}
BufferPool.prototype.reset = function(forceNewBuffer) {
var len = this._shrinkStrategy();
if (len < this.size) this._changeFactor -= 1;
if (forceNewBuffer || this._changeFactor < -2) {
this._changeFactor = 0;
this._buffer = len ? new Buffer(len) : null;
}
this._offset = 0;
this._used = 0;
}
module.exports = BufferPool;

View File

@ -0,0 +1,47 @@
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
exports.BufferUtil = {
merge: function(mergedBuffer, buffers) {
var offset = 0;
for (var i = 0, l = buffers.length; i < l; ++i) {
var buf = buffers[i];
buf.copy(mergedBuffer, offset);
offset += buf.length;
}
},
mask: function(source, mask, output, offset, length) {
var maskNum = mask.readUInt32LE(0, true);
var i = 0;
for (; i < length - 3; i += 4) {
var num = maskNum ^ source.readUInt32LE(i, true);
if (num < 0) num = 4294967296 + num;
output.writeUInt32LE(num, offset + i, true);
}
switch (length % 4) {
case 3: output[offset + i + 2] = source[i + 2] ^ mask[2];
case 2: output[offset + i + 1] = source[i + 1] ^ mask[1];
case 1: output[offset + i] = source[i] ^ mask[0];
case 0:;
}
},
unmask: function(data, mask) {
var maskNum = mask.readUInt32LE(0, true);
var length = data.length;
var i = 0;
for (; i < length - 3; i += 4) {
var num = maskNum ^ data.readUInt32LE(i, true);
if (num < 0) num = 4294967296 + num;
data.writeUInt32LE(num, i, true);
}
switch (length % 4) {
case 3: data[i + 2] = data[i + 2] ^ mask[2];
case 2: data[i + 1] = data[i + 1] ^ mask[1];
case 1: data[i] = data[i] ^ mask[0];
case 0:;
}
}
}

View File

@ -0,0 +1,17 @@
'use strict';
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
var bufferUtil;
try {
bufferUtil = require('bufferutil');
} catch (e) {
bufferUtil = require('./BufferUtil.fallback');
}
module.exports = bufferUtil.BufferUtil || bufferUtil;

View File

@ -0,0 +1,24 @@
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
module.exports = {
isValidErrorCode: function(code) {
return (code >= 1000 && code <= 1011 && code != 1004 && code != 1005 && code != 1006) ||
(code >= 3000 && code <= 4999);
},
1000: 'normal',
1001: 'going away',
1002: 'protocol error',
1003: 'unsupported data',
1004: 'reserved',
1005: 'reserved for extensions',
1006: 'reserved for extensions',
1007: 'inconsistent or invalid data',
1008: 'policy violation',
1009: 'message too big',
1010: 'extension handshake missing',
1011: 'an unexpected condition prevented the request from being fulfilled',
};

View File

@ -0,0 +1,81 @@
var util = require('util');
/**
* Module exports.
*/
exports.parse = parse;
exports.format = format;
/**
* Parse extensions header value
*/
function parse(value) {
value = value || '';
var extensions = {};
value.split(',').forEach(function(v) {
var params = v.split(';');
var token = params.shift().trim();
if (extensions[token] === undefined) {
extensions[token] = [];
} else if (!extensions.hasOwnProperty(token)) {
return;
}
var parsedParams = {};
params.forEach(function(param) {
var parts = param.trim().split('=');
var key = parts[0];
var value = parts[1];
if (typeof value === 'undefined') {
value = true;
} else {
// unquote value
if (value[0] === '"') {
value = value.slice(1);
}
if (value[value.length - 1] === '"') {
value = value.slice(0, value.length - 1);
}
}
if (parsedParams[key] === undefined) {
parsedParams[key] = [value];
} else if (parsedParams.hasOwnProperty(key)) {
parsedParams[key].push(value);
}
});
extensions[token].push(parsedParams);
});
return extensions;
}
/**
* Format extensions header value
*/
function format(value) {
return Object.keys(value).map(function(token) {
var paramsList = value[token];
if (!util.isArray(paramsList)) {
paramsList = [paramsList];
}
return paramsList.map(function(params) {
return [token].concat(Object.keys(params).map(function(k) {
var p = params[k];
if (!util.isArray(p)) p = [p];
return p.map(function(v) {
return v === true ? k : k + '=' + v;
}).join('; ');
})).join('; ');
}).join(', ');
}).join(', ');
}

View File

@ -0,0 +1,337 @@
var zlib = require('zlib');
var AVAILABLE_WINDOW_BITS = [8, 9, 10, 11, 12, 13, 14, 15];
var DEFAULT_WINDOW_BITS = 15;
var DEFAULT_MEM_LEVEL = 8;
PerMessageDeflate.extensionName = 'permessage-deflate';
/**
* Per-message Compression Extensions implementation
*/
function PerMessageDeflate(options, isServer,maxPayload) {
if (this instanceof PerMessageDeflate === false) {
throw new TypeError("Classes can't be function-called");
}
this._options = options || {};
this._isServer = !!isServer;
this._inflate = null;
this._deflate = null;
this.params = null;
this._maxPayload = maxPayload || 0;
}
/**
* Create extension parameters offer
*
* @api public
*/
PerMessageDeflate.prototype.offer = function() {
var 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 extension offer
*
* @api public
*/
PerMessageDeflate.prototype.accept = function(paramsList) {
paramsList = this.normalizeParams(paramsList);
var params;
if (this._isServer) {
params = this.acceptAsServer(paramsList);
} else {
params = this.acceptAsClient(paramsList);
}
this.params = params;
return params;
};
/**
* Releases all resources used by the extension
*
* @api public
*/
PerMessageDeflate.prototype.cleanup = function() {
if (this._inflate) {
if (this._inflate.writeInProgress) {
this._inflate.pendingClose = true;
} else {
if (this._inflate.close) this._inflate.close();
this._inflate = null;
}
}
if (this._deflate) {
if (this._deflate.writeInProgress) {
this._deflate.pendingClose = true;
} else {
if (this._deflate.close) this._deflate.close();
this._deflate = null;
}
}
};
/**
* Accept extension offer from client
*
* @api private
*/
PerMessageDeflate.prototype.acceptAsServer = function(paramsList) {
var accepted = {};
var result = paramsList.some(function(params) {
accepted = {};
if (this._options.serverNoContextTakeover === false && params.server_no_context_takeover) {
return;
}
if (this._options.serverMaxWindowBits === false && params.server_max_window_bits) {
return;
}
if (typeof this._options.serverMaxWindowBits === 'number' &&
typeof params.server_max_window_bits === 'number' &&
this._options.serverMaxWindowBits > params.server_max_window_bits) {
return;
}
if (typeof this._options.clientMaxWindowBits === 'number' && !params.client_max_window_bits) {
return;
}
if (this._options.serverNoContextTakeover || params.server_no_context_takeover) {
accepted.server_no_context_takeover = true;
}
if (this._options.clientNoContextTakeover) {
accepted.client_no_context_takeover = true;
}
if (this._options.clientNoContextTakeover !== false && params.client_no_context_takeover) {
accepted.client_no_context_takeover = true;
}
if (typeof this._options.serverMaxWindowBits === 'number') {
accepted.server_max_window_bits = this._options.serverMaxWindowBits;
} else if (typeof params.server_max_window_bits === 'number') {
accepted.server_max_window_bits = params.server_max_window_bits;
}
if (typeof this._options.clientMaxWindowBits === 'number') {
accepted.client_max_window_bits = this._options.clientMaxWindowBits;
} else if (this._options.clientMaxWindowBits !== false && typeof params.client_max_window_bits === 'number') {
accepted.client_max_window_bits = params.client_max_window_bits;
}
return true;
}, this);
if (!result) {
throw new Error('Doesn\'t support the offered configuration');
}
return accepted;
};
/**
* Accept extension response from server
*
* @api privaye
*/
PerMessageDeflate.prototype.acceptAsClient = function(paramsList) {
var params = paramsList[0];
if (this._options.clientNoContextTakeover != null) {
if (this._options.clientNoContextTakeover === false && params.client_no_context_takeover) {
throw new Error('Invalid value for "client_no_context_takeover"');
}
}
if (this._options.clientMaxWindowBits != null) {
if (this._options.clientMaxWindowBits === false && params.client_max_window_bits) {
throw new Error('Invalid value for "client_max_window_bits"');
}
if (typeof this._options.clientMaxWindowBits === 'number' &&
(!params.client_max_window_bits || params.client_max_window_bits > this._options.clientMaxWindowBits)) {
throw new Error('Invalid value for "client_max_window_bits"');
}
}
return params;
};
/**
* Normalize extensions parameters
*
* @api private
*/
PerMessageDeflate.prototype.normalizeParams = function(paramsList) {
return paramsList.map(function(params) {
Object.keys(params).forEach(function(key) {
var value = params[key];
if (value.length > 1) {
throw new Error('Multiple extension parameters for ' + key);
}
value = value[0];
switch (key) {
case 'server_no_context_takeover':
case 'client_no_context_takeover':
if (value !== true) {
throw new Error('invalid extension parameter value for ' + key + ' (' + value + ')');
}
params[key] = true;
break;
case 'server_max_window_bits':
case 'client_max_window_bits':
if (typeof value === 'string') {
value = parseInt(value, 10);
if (!~AVAILABLE_WINDOW_BITS.indexOf(value)) {
throw new Error('invalid extension parameter value for ' + key + ' (' + value + ')');
}
}
if (!this._isServer && value === true) {
throw new Error('Missing extension parameter value for ' + key);
}
params[key] = value;
break;
default:
throw new Error('Not defined extension parameter (' + key + ')');
}
}, this);
return params;
}, this);
};
/**
* Decompress message
*
* @api public
*/
PerMessageDeflate.prototype.decompress = function (data, fin, callback) {
var endpoint = this._isServer ? 'client' : 'server';
if (!this._inflate) {
var maxWindowBits = this.params[endpoint + '_max_window_bits'];
this._inflate = zlib.createInflateRaw({
windowBits: 'number' === typeof maxWindowBits ? maxWindowBits : DEFAULT_WINDOW_BITS
});
}
this._inflate.writeInProgress = true;
var self = this;
var buffers = [];
var cumulativeBufferLength=0;
this._inflate.on('error', onError).on('data', onData);
this._inflate.write(data);
if (fin) {
this._inflate.write(new Buffer([0x00, 0x00, 0xff, 0xff]));
}
this._inflate.flush(function() {
cleanup();
callback(null, Buffer.concat(buffers));
});
function onError(err) {
cleanup();
callback(err);
}
function onData(data) {
if(self._maxPayload!==undefined && self._maxPayload!==null && self._maxPayload>0){
cumulativeBufferLength+=data.length;
if(cumulativeBufferLength>self._maxPayload){
buffers=[];
cleanup();
var err={type:1009};
callback(err);
return;
}
}
buffers.push(data);
}
function cleanup() {
if (!self._inflate) return;
self._inflate.removeListener('error', onError);
self._inflate.removeListener('data', onData);
self._inflate.writeInProgress = false;
if ((fin && self.params[endpoint + '_no_context_takeover']) || self._inflate.pendingClose) {
if (self._inflate.close) self._inflate.close();
self._inflate = null;
}
}
};
/**
* Compress message
*
* @api public
*/
PerMessageDeflate.prototype.compress = function (data, fin, callback) {
var endpoint = this._isServer ? 'server' : 'client';
if (!this._deflate) {
var maxWindowBits = this.params[endpoint + '_max_window_bits'];
this._deflate = zlib.createDeflateRaw({
flush: zlib.Z_SYNC_FLUSH,
windowBits: 'number' === typeof maxWindowBits ? maxWindowBits : DEFAULT_WINDOW_BITS,
memLevel: this._options.memLevel || DEFAULT_MEM_LEVEL
});
}
this._deflate.writeInProgress = true;
var self = this;
var buffers = [];
this._deflate.on('error', onError).on('data', onData);
this._deflate.write(data);
this._deflate.flush(function() {
cleanup();
var data = Buffer.concat(buffers);
if (fin) {
data = data.slice(0, data.length - 4);
}
callback(null, data);
});
function onError(err) {
cleanup();
callback(err);
}
function onData(data) {
buffers.push(data);
}
function cleanup() {
if (!self._deflate) return;
self._deflate.removeListener('error', onError);
self._deflate.removeListener('data', onData);
self._deflate.writeInProgress = false;
if ((fin && self.params[endpoint + '_no_context_takeover']) || self._deflate.pendingClose) {
if (self._deflate.close) self._deflate.close();
self._deflate = null;
}
}
};
module.exports = PerMessageDeflate;

View File

@ -0,0 +1,194 @@
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
var util = require('util');
/**
* State constants
*/
var EMPTY = 0
, BODY = 1;
var BINARYLENGTH = 2
, BINARYBODY = 3;
/**
* Hixie Receiver implementation
*/
function Receiver () {
if (this instanceof Receiver === false) {
throw new TypeError("Classes can't be function-called");
}
this.state = EMPTY;
this.buffers = [];
this.messageEnd = -1;
this.spanLength = 0;
this.dead = false;
this.onerror = function() {};
this.ontext = function() {};
this.onbinary = function() {};
this.onclose = function() {};
this.onping = function() {};
this.onpong = function() {};
}
module.exports = Receiver;
/**
* Add new data to the parser.
*
* @api public
*/
Receiver.prototype.add = function(data) {
if (this.dead) return;
var self = this;
function doAdd() {
if (self.state === EMPTY) {
if (data.length == 2 && data[0] == 0xFF && data[1] == 0x00) {
self.reset();
self.onclose();
return;
}
if (data[0] === 0x80) {
self.messageEnd = 0;
self.state = BINARYLENGTH;
data = data.slice(1);
} else {
if (data[0] !== 0x00) {
self.error('payload must start with 0x00 byte', true);
return;
}
data = data.slice(1);
self.state = BODY;
}
}
if (self.state === BINARYLENGTH) {
var i = 0;
while ((i < data.length) && (data[i] & 0x80)) {
self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f);
++i;
}
if (i < data.length) {
self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f);
self.state = BINARYBODY;
++i;
}
if (i > 0)
data = data.slice(i);
}
if (self.state === BINARYBODY) {
var dataleft = self.messageEnd - self.spanLength;
if (data.length >= dataleft) {
// consume the whole buffer to finish the frame
self.buffers.push(data);
self.spanLength += dataleft;
self.messageEnd = dataleft;
return self.parse();
}
// frame's not done even if we consume it all
self.buffers.push(data);
self.spanLength += data.length;
return;
}
self.buffers.push(data);
if ((self.messageEnd = bufferIndex(data, 0xFF)) != -1) {
self.spanLength += self.messageEnd;
return self.parse();
}
else self.spanLength += data.length;
}
while(data) data = doAdd();
};
/**
* Releases all resources used by the receiver.
*
* @api public
*/
Receiver.prototype.cleanup = function() {
this.dead = true;
this.state = EMPTY;
this.buffers = [];
};
/**
* Process buffered data.
*
* @api public
*/
Receiver.prototype.parse = function() {
var output = new Buffer(this.spanLength);
var outputIndex = 0;
for (var bi = 0, bl = this.buffers.length; bi < bl - 1; ++bi) {
var buffer = this.buffers[bi];
buffer.copy(output, outputIndex);
outputIndex += buffer.length;
}
var lastBuffer = this.buffers[this.buffers.length - 1];
if (this.messageEnd > 0) lastBuffer.copy(output, outputIndex, 0, this.messageEnd);
if (this.state !== BODY) --this.messageEnd;
var tail = null;
if (this.messageEnd < lastBuffer.length - 1) {
tail = lastBuffer.slice(this.messageEnd + 1);
}
this.reset();
this.ontext(output.toString('utf8'));
return tail;
};
/**
* Handles an error
*
* @api private
*/
Receiver.prototype.error = function (reason, terminate) {
if (this.dead) return;
this.reset();
if(typeof reason == 'string'){
this.onerror(new Error(reason), terminate);
}
else if(reason.constructor == Error){
this.onerror(reason, terminate);
}
else{
this.onerror(new Error("An error occured"),terminate);
}
return this;
};
/**
* Reset parser state
*
* @api private
*/
Receiver.prototype.reset = function (reason) {
if (this.dead) return;
this.state = EMPTY;
this.buffers = [];
this.messageEnd = -1;
this.spanLength = 0;
};
/**
* Internal api
*/
function bufferIndex(buffer, byte) {
for (var i = 0, l = buffer.length; i < l; ++i) {
if (buffer[i] === byte) return i;
}
return -1;
}

View File

@ -0,0 +1,793 @@
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
var util = require('util')
, isValidUTF8 = require('./Validation')
, ErrorCodes = require('./ErrorCodes')
, BufferPool = require('./BufferPool')
, bufferUtil = require('./BufferUtil')
, PerMessageDeflate = require('./PerMessageDeflate');
/**
* HyBi Receiver implementation
*/
function Receiver (extensions,maxPayload) {
if (this instanceof Receiver === false) {
throw new TypeError("Classes can't be function-called");
}
if(typeof extensions==='number'){
maxPayload=extensions;
extensions={};
}
// memory pool for fragmented messages
var fragmentedPoolPrevUsed = -1;
this.fragmentedBufferPool = new BufferPool(1024, function(db, length) {
return db.used + length;
}, function(db) {
return fragmentedPoolPrevUsed = fragmentedPoolPrevUsed >= 0 ?
Math.ceil((fragmentedPoolPrevUsed + db.used) / 2) :
db.used;
});
// memory pool for unfragmented messages
var unfragmentedPoolPrevUsed = -1;
this.unfragmentedBufferPool = new BufferPool(1024, function(db, length) {
return db.used + length;
}, function(db) {
return unfragmentedPoolPrevUsed = unfragmentedPoolPrevUsed >= 0 ?
Math.ceil((unfragmentedPoolPrevUsed + db.used) / 2) :
db.used;
});
this.extensions = extensions || {};
this.maxPayload = maxPayload || 0;
this.currentPayloadLength = 0;
this.state = {
activeFragmentedOperation: null,
lastFragment: false,
masked: false,
opcode: 0,
fragmentedOperation: false
};
this.overflow = [];
this.headerBuffer = new Buffer(10);
this.expectOffset = 0;
this.expectBuffer = null;
this.expectHandler = null;
this.currentMessage = [];
this.currentMessageLength = 0;
this.messageHandlers = [];
this.expectHeader(2, this.processPacket);
this.dead = false;
this.processing = false;
this.onerror = function() {};
this.ontext = function() {};
this.onbinary = function() {};
this.onclose = function() {};
this.onping = function() {};
this.onpong = function() {};
}
module.exports = Receiver;
/**
* Add new data to the parser.
*
* @api public
*/
Receiver.prototype.add = function(data) {
if (this.dead) return;
var dataLength = data.length;
if (dataLength == 0) return;
if (this.expectBuffer == null) {
this.overflow.push(data);
return;
}
var toRead = Math.min(dataLength, this.expectBuffer.length - this.expectOffset);
fastCopy(toRead, data, this.expectBuffer, this.expectOffset);
this.expectOffset += toRead;
if (toRead < dataLength) {
this.overflow.push(data.slice(toRead));
}
while (this.expectBuffer && this.expectOffset == this.expectBuffer.length) {
var bufferForHandler = this.expectBuffer;
this.expectBuffer = null;
this.expectOffset = 0;
this.expectHandler.call(this, bufferForHandler);
}
};
/**
* Releases all resources used by the receiver.
*
* @api public
*/
Receiver.prototype.cleanup = function() {
this.dead = true;
this.overflow = null;
this.headerBuffer = null;
this.expectBuffer = null;
this.expectHandler = null;
this.unfragmentedBufferPool = null;
this.fragmentedBufferPool = null;
this.state = null;
this.currentMessage = null;
this.onerror = null;
this.ontext = null;
this.onbinary = null;
this.onclose = null;
this.onping = null;
this.onpong = null;
};
/**
* Waits for a certain amount of header bytes to be available, then fires a callback.
*
* @api private
*/
Receiver.prototype.expectHeader = function(length, handler) {
if (length == 0) {
handler(null);
return;
}
this.expectBuffer = this.headerBuffer.slice(this.expectOffset, this.expectOffset + length);
this.expectHandler = handler;
var toRead = length;
while (toRead > 0 && this.overflow.length > 0) {
var fromOverflow = this.overflow.pop();
if (toRead < fromOverflow.length) this.overflow.push(fromOverflow.slice(toRead));
var read = Math.min(fromOverflow.length, toRead);
fastCopy(read, fromOverflow, this.expectBuffer, this.expectOffset);
this.expectOffset += read;
toRead -= read;
}
};
/**
* Waits for a certain amount of data bytes to be available, then fires a callback.
*
* @api private
*/
Receiver.prototype.expectData = function(length, handler) {
if (length == 0) {
handler(null);
return;
}
this.expectBuffer = this.allocateFromPool(length, this.state.fragmentedOperation);
this.expectHandler = handler;
var toRead = length;
while (toRead > 0 && this.overflow.length > 0) {
var fromOverflow = this.overflow.pop();
if (toRead < fromOverflow.length) this.overflow.push(fromOverflow.slice(toRead));
var read = Math.min(fromOverflow.length, toRead);
fastCopy(read, fromOverflow, this.expectBuffer, this.expectOffset);
this.expectOffset += read;
toRead -= read;
}
};
/**
* Allocates memory from the buffer pool.
*
* @api private
*/
Receiver.prototype.allocateFromPool = function(length, isFragmented) {
return (isFragmented ? this.fragmentedBufferPool : this.unfragmentedBufferPool).get(length);
};
/**
* Start processing a new packet.
*
* @api private
*/
Receiver.prototype.processPacket = function (data) {
if (this.extensions[PerMessageDeflate.extensionName]) {
if ((data[0] & 0x30) != 0) {
this.error('reserved fields (2, 3) must be empty', 1002);
return;
}
} else {
if ((data[0] & 0x70) != 0) {
this.error('reserved fields must be empty', 1002);
return;
}
}
this.state.lastFragment = (data[0] & 0x80) == 0x80;
this.state.masked = (data[1] & 0x80) == 0x80;
var compressed = (data[0] & 0x40) == 0x40;
var opcode = data[0] & 0xf;
if (opcode === 0) {
if (compressed) {
this.error('continuation frame cannot have the Per-message Compressed bits', 1002);
return;
}
// continuation frame
this.state.fragmentedOperation = true;
this.state.opcode = this.state.activeFragmentedOperation;
if (!(this.state.opcode == 1 || this.state.opcode == 2)) {
this.error('continuation frame cannot follow current opcode', 1002);
return;
}
}
else {
if (opcode < 3 && this.state.activeFragmentedOperation != null) {
this.error('data frames after the initial data frame must have opcode 0', 1002);
return;
}
if (opcode >= 8 && compressed) {
this.error('control frames cannot have the Per-message Compressed bits', 1002);
return;
}
this.state.compressed = compressed;
this.state.opcode = opcode;
if (this.state.lastFragment === false) {
this.state.fragmentedOperation = true;
this.state.activeFragmentedOperation = opcode;
}
else this.state.fragmentedOperation = false;
}
var handler = opcodes[this.state.opcode];
if (typeof handler == 'undefined') this.error('no handler for opcode ' + this.state.opcode, 1002);
else {
handler.start.call(this, data);
}
};
/**
* Endprocessing a packet.
*
* @api private
*/
Receiver.prototype.endPacket = function() {
if (this.dead) return;
if (!this.state.fragmentedOperation) this.unfragmentedBufferPool.reset(true);
else if (this.state.lastFragment) this.fragmentedBufferPool.reset(true);
this.expectOffset = 0;
this.expectBuffer = null;
this.expectHandler = null;
if (this.state.lastFragment && this.state.opcode === this.state.activeFragmentedOperation) {
// end current fragmented operation
this.state.activeFragmentedOperation = null;
}
this.currentPayloadLength = 0;
this.state.lastFragment = false;
this.state.opcode = this.state.activeFragmentedOperation != null ? this.state.activeFragmentedOperation : 0;
this.state.masked = false;
this.expectHeader(2, this.processPacket);
};
/**
* Reset the parser state.
*
* @api private
*/
Receiver.prototype.reset = function() {
if (this.dead) return;
this.state = {
activeFragmentedOperation: null,
lastFragment: false,
masked: false,
opcode: 0,
fragmentedOperation: false
};
this.fragmentedBufferPool.reset(true);
this.unfragmentedBufferPool.reset(true);
this.expectOffset = 0;
this.expectBuffer = null;
this.expectHandler = null;
this.overflow = [];
this.currentMessage = [];
this.currentMessageLength = 0;
this.messageHandlers = [];
this.currentPayloadLength = 0;
};
/**
* Unmask received data.
*
* @api private
*/
Receiver.prototype.unmask = function (mask, buf, binary) {
if (mask != null && buf != null) bufferUtil.unmask(buf, mask);
if (binary) return buf;
return buf != null ? buf.toString('utf8') : '';
};
/**
* Handles an error
*
* @api private
*/
Receiver.prototype.error = function (reason, protocolErrorCode) {
if (this.dead) return;
this.reset();
if(typeof reason == 'string'){
this.onerror(new Error(reason), protocolErrorCode);
}
else if(reason.constructor == Error){
this.onerror(reason, protocolErrorCode);
}
else{
this.onerror(new Error("An error occured"),protocolErrorCode);
}
return this;
};
/**
* Execute message handler buffers
*
* @api private
*/
Receiver.prototype.flush = function() {
if (this.processing || this.dead) return;
var handler = this.messageHandlers.shift();
if (!handler) return;
this.processing = true;
var self = this;
handler(function() {
self.processing = false;
self.flush();
});
};
/**
* Apply extensions to message
*
* @api private
*/
Receiver.prototype.applyExtensions = function(messageBuffer, fin, compressed, callback) {
var self = this;
if (compressed) {
this.extensions[PerMessageDeflate.extensionName].decompress(messageBuffer, fin, function(err, buffer) {
if (self.dead) return;
if (err) {
callback(new Error('invalid compressed data'));
return;
}
callback(null, buffer);
});
} else {
callback(null, messageBuffer);
}
};
/**
* Checks payload size, disconnects socket when it exceeds maxPayload
*
* @api private
*/
Receiver.prototype.maxPayloadExceeded = function(length) {
if (this.maxPayload=== undefined || this.maxPayload === null || this.maxPayload < 1) {
return false;
}
var fullLength = this.currentPayloadLength + length;
if (fullLength < this.maxPayload) {
this.currentPayloadLength = fullLength;
return false;
}
this.error('payload cannot exceed ' + this.maxPayload + ' bytes', 1009);
this.messageBuffer=[];
this.cleanup();
return true;
};
/**
* Buffer utilities
*/
function readUInt16BE(start) {
return (this[start]<<8) +
this[start+1];
}
function readUInt32BE(start) {
return (this[start]<<24) +
(this[start+1]<<16) +
(this[start+2]<<8) +
this[start+3];
}
function fastCopy(length, srcBuffer, dstBuffer, dstOffset) {
switch (length) {
default: srcBuffer.copy(dstBuffer, dstOffset, 0, length); break;
case 16: dstBuffer[dstOffset+15] = srcBuffer[15];
case 15: dstBuffer[dstOffset+14] = srcBuffer[14];
case 14: dstBuffer[dstOffset+13] = srcBuffer[13];
case 13: dstBuffer[dstOffset+12] = srcBuffer[12];
case 12: dstBuffer[dstOffset+11] = srcBuffer[11];
case 11: dstBuffer[dstOffset+10] = srcBuffer[10];
case 10: dstBuffer[dstOffset+9] = srcBuffer[9];
case 9: dstBuffer[dstOffset+8] = srcBuffer[8];
case 8: dstBuffer[dstOffset+7] = srcBuffer[7];
case 7: dstBuffer[dstOffset+6] = srcBuffer[6];
case 6: dstBuffer[dstOffset+5] = srcBuffer[5];
case 5: dstBuffer[dstOffset+4] = srcBuffer[4];
case 4: dstBuffer[dstOffset+3] = srcBuffer[3];
case 3: dstBuffer[dstOffset+2] = srcBuffer[2];
case 2: dstBuffer[dstOffset+1] = srcBuffer[1];
case 1: dstBuffer[dstOffset] = srcBuffer[0];
}
}
function clone(obj) {
var cloned = {};
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
cloned[k] = obj[k];
}
}
return cloned;
}
/**
* Opcode handlers
*/
var opcodes = {
// text
'1': {
start: function(data) {
var self = this;
// decode length
var firstLength = data[1] & 0x7f;
if (firstLength < 126) {
if (self.maxPayloadExceeded(firstLength)){
self.error('Maximumpayload exceeded in compressed text message. Aborting...', 1009);
return;
}
opcodes['1'].getData.call(self, firstLength);
}
else if (firstLength == 126) {
self.expectHeader(2, function(data) {
var length = readUInt16BE.call(data, 0);
if (self.maxPayloadExceeded(length)){
self.error('Maximumpayload exceeded in compressed text message. Aborting...', 1009);
return;
}
opcodes['1'].getData.call(self, length);
});
}
else if (firstLength == 127) {
self.expectHeader(8, function(data) {
if (readUInt32BE.call(data, 0) != 0) {
self.error('packets with length spanning more than 32 bit is currently not supported', 1008);
return;
}
var length = readUInt32BE.call(data, 4);
if (self.maxPayloadExceeded(length)){
self.error('Maximumpayload exceeded in compressed text message. Aborting...', 1009);
return;
}
opcodes['1'].getData.call(self, readUInt32BE.call(data, 4));
});
}
},
getData: function(length) {
var self = this;
if (self.state.masked) {
self.expectHeader(4, function(data) {
var mask = data;
self.expectData(length, function(data) {
opcodes['1'].finish.call(self, mask, data);
});
});
}
else {
self.expectData(length, function(data) {
opcodes['1'].finish.call(self, null, data);
});
}
},
finish: function(mask, data) {
var self = this;
var packet = this.unmask(mask, data, true) || new Buffer(0);
var state = clone(this.state);
this.messageHandlers.push(function(callback) {
self.applyExtensions(packet, state.lastFragment, state.compressed, function(err, buffer) {
if (err) {
if(err.type===1009){
return self.error('Maximumpayload exceeded in compressed text message. Aborting...', 1009);
}
return self.error(err.message, 1007);
}
if (buffer != null) {
if( self.maxPayload==0 || (self.maxPayload > 0 && (self.currentMessageLength + buffer.length) < self.maxPayload) ){
self.currentMessage.push(buffer);
}
else{
self.currentMessage=null;
self.currentMessage = [];
self.currentMessageLength = 0;
self.error(new Error('Maximum payload exceeded. maxPayload: '+self.maxPayload), 1009);
return;
}
self.currentMessageLength += buffer.length;
}
if (state.lastFragment) {
var messageBuffer = Buffer.concat(self.currentMessage);
self.currentMessage = [];
self.currentMessageLength = 0;
if (!isValidUTF8(messageBuffer)) {
self.error('invalid utf8 sequence', 1007);
return;
}
self.ontext(messageBuffer.toString('utf8'), {masked: state.masked, buffer: messageBuffer});
}
callback();
});
});
this.flush();
this.endPacket();
}
},
// binary
'2': {
start: function(data) {
var self = this;
// decode length
var firstLength = data[1] & 0x7f;
if (firstLength < 126) {
if (self.maxPayloadExceeded(firstLength)){
self.error('Max payload exceeded in compressed text message. Aborting...', 1009);
return;
}
opcodes['2'].getData.call(self, firstLength);
}
else if (firstLength == 126) {
self.expectHeader(2, function(data) {
var length = readUInt16BE.call(data, 0);
if (self.maxPayloadExceeded(length)){
self.error('Max payload exceeded in compressed text message. Aborting...', 1009);
return;
}
opcodes['2'].getData.call(self, length);
});
}
else if (firstLength == 127) {
self.expectHeader(8, function(data) {
if (readUInt32BE.call(data, 0) != 0) {
self.error('packets with length spanning more than 32 bit is currently not supported', 1008);
return;
}
var length = readUInt32BE.call(data, 4, true);
if (self.maxPayloadExceeded(length)){
self.error('Max payload exceeded in compressed text message. Aborting...', 1009);
return;
}
opcodes['2'].getData.call(self, length);
});
}
},
getData: function(length) {
var self = this;
if (self.state.masked) {
self.expectHeader(4, function(data) {
var mask = data;
self.expectData(length, function(data) {
opcodes['2'].finish.call(self, mask, data);
});
});
}
else {
self.expectData(length, function(data) {
opcodes['2'].finish.call(self, null, data);
});
}
},
finish: function(mask, data) {
var self = this;
var packet = this.unmask(mask, data, true) || new Buffer(0);
var state = clone(this.state);
this.messageHandlers.push(function(callback) {
self.applyExtensions(packet, state.lastFragment, state.compressed, function(err, buffer) {
if (err) {
if(err.type===1009){
return self.error('Max payload exceeded in compressed binary message. Aborting...', 1009);
}
return self.error(err.message, 1007);
}
if (buffer != null) {
if( self.maxPayload==0 || (self.maxPayload > 0 && (self.currentMessageLength + buffer.length) < self.maxPayload) ){
self.currentMessage.push(buffer);
}
else{
self.currentMessage=null;
self.currentMessage = [];
self.currentMessageLength = 0;
self.error(new Error('Maximum payload exceeded'), 1009);
return;
}
self.currentMessageLength += buffer.length;
}
if (state.lastFragment) {
var messageBuffer = Buffer.concat(self.currentMessage);
self.currentMessage = [];
self.currentMessageLength = 0;
self.onbinary(messageBuffer, {masked: state.masked, buffer: messageBuffer});
}
callback();
});
});
this.flush();
this.endPacket();
}
},
// close
'8': {
start: function(data) {
var self = this;
if (self.state.lastFragment == false) {
self.error('fragmented close is not supported', 1002);
return;
}
// decode length
var firstLength = data[1] & 0x7f;
if (firstLength < 126) {
opcodes['8'].getData.call(self, firstLength);
}
else {
self.error('control frames cannot have more than 125 bytes of data', 1002);
}
},
getData: function(length) {
var self = this;
if (self.state.masked) {
self.expectHeader(4, function(data) {
var mask = data;
self.expectData(length, function(data) {
opcodes['8'].finish.call(self, mask, data);
});
});
}
else {
self.expectData(length, function(data) {
opcodes['8'].finish.call(self, null, data);
});
}
},
finish: function(mask, data) {
var self = this;
data = self.unmask(mask, data, true);
var state = clone(this.state);
this.messageHandlers.push(function() {
if (data && data.length == 1) {
self.error('close packets with data must be at least two bytes long', 1002);
return;
}
var code = data && data.length > 1 ? readUInt16BE.call(data, 0) : 1000;
if (!ErrorCodes.isValidErrorCode(code)) {
self.error('invalid error code', 1002);
return;
}
var message = '';
if (data && data.length > 2) {
var messageBuffer = data.slice(2);
if (!isValidUTF8(messageBuffer)) {
self.error('invalid utf8 sequence', 1007);
return;
}
message = messageBuffer.toString('utf8');
}
self.onclose(code, message, {masked: state.masked});
self.reset();
});
this.flush();
},
},
// ping
'9': {
start: function(data) {
var self = this;
if (self.state.lastFragment == false) {
self.error('fragmented ping is not supported', 1002);
return;
}
// decode length
var firstLength = data[1] & 0x7f;
if (firstLength < 126) {
opcodes['9'].getData.call(self, firstLength);
}
else {
self.error('control frames cannot have more than 125 bytes of data', 1002);
}
},
getData: function(length) {
var self = this;
if (self.state.masked) {
self.expectHeader(4, function(data) {
var mask = data;
self.expectData(length, function(data) {
opcodes['9'].finish.call(self, mask, data);
});
});
}
else {
self.expectData(length, function(data) {
opcodes['9'].finish.call(self, null, data);
});
}
},
finish: function(mask, data) {
var self = this;
data = this.unmask(mask, data, true);
var state = clone(this.state);
this.messageHandlers.push(function(callback) {
self.onping(data, {masked: state.masked, binary: true});
callback();
});
this.flush();
this.endPacket();
}
},
// pong
'10': {
start: function(data) {
var self = this;
if (self.state.lastFragment == false) {
self.error('fragmented pong is not supported', 1002);
return;
}
// decode length
var firstLength = data[1] & 0x7f;
if (firstLength < 126) {
opcodes['10'].getData.call(self, firstLength);
}
else {
self.error('control frames cannot have more than 125 bytes of data', 1002);
}
},
getData: function(length) {
var self = this;
if (this.state.masked) {
this.expectHeader(4, function(data) {
var mask = data;
self.expectData(length, function(data) {
opcodes['10'].finish.call(self, mask, data);
});
});
}
else {
this.expectData(length, function(data) {
opcodes['10'].finish.call(self, null, data);
});
}
},
finish: function(mask, data) {
var self = this;
data = self.unmask(mask, data, true);
var state = clone(this.state);
this.messageHandlers.push(function(callback) {
self.onpong(data, {masked: state.masked, binary: true});
callback();
});
this.flush();
this.endPacket();
}
}
}

View File

@ -0,0 +1,124 @@
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
var events = require('events')
, util = require('util')
, EventEmitter = events.EventEmitter;
/**
* Hixie Sender implementation
*/
function Sender(socket) {
if (this instanceof Sender === false) {
throw new TypeError("Classes can't be function-called");
}
events.EventEmitter.call(this);
this.socket = socket;
this.continuationFrame = false;
this.isClosed = false;
}
module.exports = Sender;
/**
* Inherits from EventEmitter.
*/
util.inherits(Sender, events.EventEmitter);
/**
* Frames and writes data.
*
* @api public
*/
Sender.prototype.send = function(data, options, cb) {
if (this.isClosed) return;
var isString = typeof data == 'string'
, length = isString ? Buffer.byteLength(data) : data.length
, lengthbytes = (length > 127) ? 2 : 1 // assume less than 2**14 bytes
, writeStartMarker = this.continuationFrame == false
, writeEndMarker = !options || !(typeof options.fin != 'undefined' && !options.fin)
, buffer = new Buffer((writeStartMarker ? ((options && options.binary) ? (1 + lengthbytes) : 1) : 0) + length + ((writeEndMarker && !(options && options.binary)) ? 1 : 0))
, offset = writeStartMarker ? 1 : 0;
if (writeStartMarker) {
if (options && options.binary) {
buffer.write('\x80', 'binary');
// assume length less than 2**14 bytes
if (lengthbytes > 1)
buffer.write(String.fromCharCode(128+length/128), offset++, 'binary');
buffer.write(String.fromCharCode(length&0x7f), offset++, 'binary');
} else
buffer.write('\x00', 'binary');
}
if (isString) buffer.write(data, offset, 'utf8');
else data.copy(buffer, offset, 0);
if (writeEndMarker) {
if (options && options.binary) {
// sending binary, not writing end marker
} else
buffer.write('\xff', offset + length, 'binary');
this.continuationFrame = false;
}
else this.continuationFrame = true;
try {
this.socket.write(buffer, 'binary', cb);
} catch (e) {
this.error(e.toString());
}
};
/**
* Sends a close instruction to the remote party.
*
* @api public
*/
Sender.prototype.close = function(code, data, mask, cb) {
if (this.isClosed) return;
this.isClosed = true;
try {
if (this.continuationFrame) this.socket.write(new Buffer([0xff], 'binary'));
this.socket.write(new Buffer([0xff, 0x00]), 'binary', cb);
} catch (e) {
this.error(e.toString());
}
};
/**
* Sends a ping message to the remote party. Not available for hixie.
*
* @api public
*/
Sender.prototype.ping = function(data, options) {};
/**
* Sends a pong message to the remote party. Not available for hixie.
*
* @api public
*/
Sender.prototype.pong = function(data, options) {};
/**
* Handles an error
*
* @api private
*/
Sender.prototype.error = function (reason) {
this.emit('error', reason);
return this;
};

View File

@ -0,0 +1,310 @@
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
var events = require('events')
, util = require('util')
, crypto = require('crypto')
, EventEmitter = events.EventEmitter
, ErrorCodes = require('./ErrorCodes')
, bufferUtil = require('./BufferUtil')
, PerMessageDeflate = require('./PerMessageDeflate');
/**
* HyBi Sender implementation
*/
function Sender(socket, extensions) {
if (this instanceof Sender === false) {
throw new TypeError("Classes can't be function-called");
}
events.EventEmitter.call(this);
this._socket = socket;
this.extensions = extensions || {};
this.firstFragment = true;
this.compress = false;
this.messageHandlers = [];
this.processing = false;
}
/**
* Inherits from EventEmitter.
*/
util.inherits(Sender, events.EventEmitter);
/**
* Sends a close instruction to the remote party.
*
* @api public
*/
Sender.prototype.close = function(code, data, mask, cb) {
if (typeof code !== 'undefined') {
if (typeof code !== 'number' ||
!ErrorCodes.isValidErrorCode(code)) throw new Error('first argument must be a valid error code number');
}
code = code || 1000;
var dataBuffer = new Buffer(2 + (data ? Buffer.byteLength(data) : 0));
writeUInt16BE.call(dataBuffer, code, 0);
if (dataBuffer.length > 2) dataBuffer.write(data, 2);
var self = this;
this.messageHandlers.push(function() {
self.frameAndSend(0x8, dataBuffer, true, mask);
if (typeof cb == 'function') cb();
});
this.flush();
};
/**
* Sends a ping message to the remote party.
*
* @api public
*/
Sender.prototype.ping = function(data, options) {
var mask = options && options.mask;
var self = this;
this.messageHandlers.push(function() {
self.frameAndSend(0x9, data || '', true, mask);
});
this.flush();
};
/**
* Sends a pong message to the remote party.
*
* @api public
*/
Sender.prototype.pong = function(data, options) {
var mask = options && options.mask;
var self = this;
this.messageHandlers.push(function() {
self.frameAndSend(0xa, data || '', true, mask);
});
this.flush();
};
/**
* Sends text or binary data to the remote party.
*
* @api public
*/
Sender.prototype.send = function(data, options, cb) {
var finalFragment = options && options.fin === false ? false : true;
var mask = options && options.mask;
var compress = options && options.compress;
var opcode = options && options.binary ? 2 : 1;
if (this.firstFragment === false) {
opcode = 0;
compress = false;
} else {
this.firstFragment = false;
this.compress = compress;
}
if (finalFragment) this.firstFragment = true
var compressFragment = this.compress;
var self = this;
this.messageHandlers.push(function() {
if (!data || !compressFragment) {
self.frameAndSend(opcode, data, finalFragment, mask, compress, cb);
return;
}
self.processing = true;
self.applyExtensions(data, finalFragment, compressFragment, function(err, data) {
if (err) {
if (typeof cb == 'function') cb(err);
else self.emit('error', err);
return;
}
self.frameAndSend(opcode, data, finalFragment, mask, compress, cb);
self.processing = false;
self.flush();
});
});
this.flush();
};
/**
* Frames and sends a piece of data according to the HyBi WebSocket protocol.
*
* @api private
*/
Sender.prototype.frameAndSend = function(opcode, data, finalFragment, maskData, compressed, cb) {
var canModifyData = false;
if (!data) {
try {
this._socket.write(new Buffer([opcode | (finalFragment ? 0x80 : 0), 0 | (maskData ? 0x80 : 0)].concat(maskData ? [0, 0, 0, 0] : [])), 'binary', cb);
}
catch (e) {
if (typeof cb == 'function') cb(e);
else this.emit('error', e);
}
return;
}
if (!Buffer.isBuffer(data)) {
canModifyData = true;
if (data && (typeof data.byteLength !== 'undefined' || typeof data.buffer !== 'undefined')) {
data = getArrayBuffer(data);
} else {
//
// If people want to send a number, this would allocate the number in
// bytes as memory size instead of storing the number as buffer value. So
// we need to transform it to string in order to prevent possible
// vulnerabilities / memory attacks.
//
if (typeof data === 'number') data = data.toString();
data = new Buffer(data);
}
}
var dataLength = data.length
, dataOffset = maskData ? 6 : 2
, secondByte = dataLength;
if (dataLength >= 65536) {
dataOffset += 8;
secondByte = 127;
}
else if (dataLength > 125) {
dataOffset += 2;
secondByte = 126;
}
var mergeBuffers = dataLength < 32768 || (maskData && !canModifyData);
var totalLength = mergeBuffers ? dataLength + dataOffset : dataOffset;
var outputBuffer = new Buffer(totalLength);
outputBuffer[0] = finalFragment ? opcode | 0x80 : opcode;
if (compressed) outputBuffer[0] |= 0x40;
switch (secondByte) {
case 126:
writeUInt16BE.call(outputBuffer, dataLength, 2);
break;
case 127:
writeUInt32BE.call(outputBuffer, 0, 2);
writeUInt32BE.call(outputBuffer, dataLength, 6);
}
if (maskData) {
outputBuffer[1] = secondByte | 0x80;
var mask = getRandomMask();
outputBuffer[dataOffset - 4] = mask[0];
outputBuffer[dataOffset - 3] = mask[1];
outputBuffer[dataOffset - 2] = mask[2];
outputBuffer[dataOffset - 1] = mask[3];
if (mergeBuffers) {
bufferUtil.mask(data, mask, outputBuffer, dataOffset, dataLength);
try {
this._socket.write(outputBuffer, 'binary', cb);
}
catch (e) {
if (typeof cb == 'function') cb(e);
else this.emit('error', e);
}
}
else {
bufferUtil.mask(data, mask, data, 0, dataLength);
try {
this._socket.write(outputBuffer, 'binary');
this._socket.write(data, 'binary', cb);
}
catch (e) {
if (typeof cb == 'function') cb(e);
else this.emit('error', e);
}
}
}
else {
outputBuffer[1] = secondByte;
if (mergeBuffers) {
data.copy(outputBuffer, dataOffset);
try {
this._socket.write(outputBuffer, 'binary', cb);
}
catch (e) {
if (typeof cb == 'function') cb(e);
else this.emit('error', e);
}
}
else {
try {
this._socket.write(outputBuffer, 'binary');
this._socket.write(data, 'binary', cb);
}
catch (e) {
if (typeof cb == 'function') cb(e);
else this.emit('error', e);
}
}
}
};
/**
* Execute message handler buffers
*
* @api private
*/
Sender.prototype.flush = function() {
while (!this.processing && this.messageHandlers.length) {
this.messageHandlers.shift()();
}
};
/**
* Apply extensions to message
*
* @api private
*/
Sender.prototype.applyExtensions = function(data, fin, compress, callback) {
if ((data.buffer || data) instanceof ArrayBuffer) {
data = getArrayBuffer(data);
}
this.extensions[PerMessageDeflate.extensionName].compress(data, fin, callback);
};
module.exports = Sender;
function writeUInt16BE(value, offset) {
this[offset] = (value & 0xff00)>>8;
this[offset+1] = value & 0xff;
}
function writeUInt32BE(value, offset) {
this[offset] = (value & 0xff000000)>>24;
this[offset+1] = (value & 0xff0000)>>16;
this[offset+2] = (value & 0xff00)>>8;
this[offset+3] = value & 0xff;
}
function getArrayBuffer(data) {
// data is either an ArrayBuffer or ArrayBufferView.
var array = new Uint8Array(data.buffer || data)
, l = data.byteLength || data.length
, o = data.byteOffset || 0
, buffer = new Buffer(l);
for (var i = 0; i < l; ++i) {
buffer[i] = array[o+i];
}
return buffer;
}
function getRandomMask() {
return crypto.randomBytes(4);
}

View File

@ -0,0 +1,11 @@
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
exports.Validation = {
isValidUTF8: function(buffer) {
return true;
}
};

View File

@ -0,0 +1,19 @@
'use strict';
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
var isValidUTF8;
try {
isValidUTF8 = require('utf-8-validate');
} catch (e) {
isValidUTF8 = require('./Validation.fallback');
}
module.exports = typeof isValidUTF8 === 'object'
? isValidUTF8.Validation.isValidUTF8
: isValidUTF8;

View File

@ -0,0 +1,987 @@
'use strict';
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
var url = require('url')
, util = require('util')
, http = require('http')
, https = require('https')
, crypto = require('crypto')
, stream = require('stream')
, Ultron = require('ultron')
, Options = require('options')
, Sender = require('./Sender')
, Receiver = require('./Receiver')
, SenderHixie = require('./Sender.hixie')
, ReceiverHixie = require('./Receiver.hixie')
, Extensions = require('./Extensions')
, PerMessageDeflate = require('./PerMessageDeflate')
, EventEmitter = require('events').EventEmitter;
/**
* Constants
*/
// Default protocol version
var protocolVersion = 13;
// Close timeout
var closeTimeout = 30 * 1000; // Allow 30 seconds to terminate the connection cleanly
/**
* WebSocket implementation
*
* @constructor
* @param {String} address Connection address.
* @param {String|Array} protocols WebSocket protocols.
* @param {Object} options Additional connection options.
* @api public
*/
function WebSocket(address, protocols, options) {
if (this instanceof WebSocket === false) {
return new WebSocket(address, protocols, options);
}
EventEmitter.call(this);
if (protocols && !Array.isArray(protocols) && 'object' === typeof protocols) {
// accept the "options" Object as the 2nd argument
options = protocols;
protocols = null;
}
if ('string' === typeof protocols) {
protocols = [ protocols ];
}
if (!Array.isArray(protocols)) {
protocols = [];
}
this._socket = null;
this._ultron = null;
this._closeReceived = false;
this.bytesReceived = 0;
this.readyState = null;
this.supports = {};
this.extensions = {};
this._binaryType = 'nodebuffer';
if (Array.isArray(address)) {
initAsServerClient.apply(this, address.concat(options));
} else {
initAsClient.apply(this, [address, protocols, options]);
}
}
/**
* Inherits from EventEmitter.
*/
util.inherits(WebSocket, EventEmitter);
/**
* Ready States
*/
["CONNECTING", "OPEN", "CLOSING", "CLOSED"].forEach(function each(state, index) {
WebSocket.prototype[state] = WebSocket[state] = index;
});
/**
* Gracefully closes the connection, after sending a description message to the server
*
* @param {Object} data to be sent to the server
* @api public
*/
WebSocket.prototype.close = function close(code, data) {
if (this.readyState === WebSocket.CLOSED) return;
if (this.readyState === WebSocket.CONNECTING) {
this.readyState = WebSocket.CLOSED;
return;
}
if (this.readyState === WebSocket.CLOSING) {
if (this._closeReceived && this._isServer) {
this.terminate();
}
return;
}
var self = this;
try {
this.readyState = WebSocket.CLOSING;
this._closeCode = code;
this._closeMessage = data;
var mask = !this._isServer;
this._sender.close(code, data, mask, function(err) {
if (err) self.emit('error', err);
if (self._closeReceived && self._isServer) {
self.terminate();
} else {
// ensure that the connection is cleaned up even when no response of closing handshake.
clearTimeout(self._closeTimer);
self._closeTimer = setTimeout(cleanupWebsocketResources.bind(self, true), closeTimeout);
}
});
} catch (e) {
this.emit('error', e);
}
};
/**
* Pause the client stream
*
* @api public
*/
WebSocket.prototype.pause = function pauser() {
if (this.readyState !== WebSocket.OPEN) throw new Error('not opened');
return this._socket.pause();
};
/**
* Sends a ping
*
* @param {Object} data to be sent to the server
* @param {Object} Members - mask: boolean, binary: boolean
* @param {boolean} dontFailWhenClosed indicates whether or not to throw if the connection isnt open
* @api public
*/
WebSocket.prototype.ping = function ping(data, options, dontFailWhenClosed) {
if (this.readyState !== WebSocket.OPEN) {
if (dontFailWhenClosed === true) return;
throw new Error('not opened');
}
options = options || {};
if (typeof options.mask === 'undefined') options.mask = !this._isServer;
this._sender.ping(data, options);
};
/**
* Sends a pong
*
* @param {Object} data to be sent to the server
* @param {Object} Members - mask: boolean, binary: boolean
* @param {boolean} dontFailWhenClosed indicates whether or not to throw if the connection isnt open
* @api public
*/
WebSocket.prototype.pong = function(data, options, dontFailWhenClosed) {
if (this.readyState !== WebSocket.OPEN) {
if (dontFailWhenClosed === true) return;
throw new Error('not opened');
}
options = options || {};
if (typeof options.mask === 'undefined') options.mask = !this._isServer;
this._sender.pong(data, options);
};
/**
* Resume the client stream
*
* @api public
*/
WebSocket.prototype.resume = function resume() {
if (this.readyState !== WebSocket.OPEN) throw new Error('not opened');
return this._socket.resume();
};
/**
* Sends a piece of data
*
* @param {Object} data to be sent to the server
* @param {Object} Members - mask: boolean, binary: boolean, compress: boolean
* @param {function} Optional callback which is executed after the send completes
* @api public
*/
WebSocket.prototype.send = function send(data, options, cb) {
if (typeof options === 'function') {
cb = options;
options = {};
}
if (this.readyState !== WebSocket.OPEN) {
if (typeof cb === 'function') cb(new Error('not opened'));
else throw new Error('not opened');
return;
}
if (!data) data = '';
if (this._queue) {
var self = this;
this._queue.push(function() { self.send(data, options, cb); });
return;
}
options = options || {};
options.fin = true;
if (typeof options.binary === 'undefined') {
options.binary = (data instanceof ArrayBuffer || data instanceof Buffer ||
data instanceof Uint8Array ||
data instanceof Uint16Array ||
data instanceof Uint32Array ||
data instanceof Int8Array ||
data instanceof Int16Array ||
data instanceof Int32Array ||
data instanceof Float32Array ||
data instanceof Float64Array);
}
if (typeof options.mask === 'undefined') options.mask = !this._isServer;
if (typeof options.compress === 'undefined') options.compress = true;
if (!this.extensions[PerMessageDeflate.extensionName]) {
options.compress = false;
}
var readable = typeof stream.Readable === 'function'
? stream.Readable
: stream.Stream;
if (data instanceof readable) {
startQueue(this);
var self = this;
sendStream(this, data, options, function send(error) {
process.nextTick(function tock() {
executeQueueSends(self);
});
if (typeof cb === 'function') cb(error);
});
} else {
this._sender.send(data, options, cb);
}
};
/**
* Streams data through calls to a user supplied function
*
* @param {Object} Members - mask: boolean, binary: boolean, compress: boolean
* @param {function} 'function (error, send)' which is executed on successive ticks of which send is 'function (data, final)'.
* @api public
*/
WebSocket.prototype.stream = function stream(options, cb) {
if (typeof options === 'function') {
cb = options;
options = {};
}
var self = this;
if (typeof cb !== 'function') throw new Error('callback must be provided');
if (this.readyState !== WebSocket.OPEN) {
if (typeof cb === 'function') cb(new Error('not opened'));
else throw new Error('not opened');
return;
}
if (this._queue) {
this._queue.push(function () { self.stream(options, cb); });
return;
}
options = options || {};
if (typeof options.mask === 'undefined') options.mask = !this._isServer;
if (typeof options.compress === 'undefined') options.compress = true;
if (!this.extensions[PerMessageDeflate.extensionName]) {
options.compress = false;
}
startQueue(this);
function send(data, final) {
try {
if (self.readyState !== WebSocket.OPEN) throw new Error('not opened');
options.fin = final === true;
self._sender.send(data, options);
if (!final) process.nextTick(cb.bind(null, null, send));
else executeQueueSends(self);
} catch (e) {
if (typeof cb === 'function') cb(e);
else {
delete self._queue;
self.emit('error', e);
}
}
}
process.nextTick(cb.bind(null, null, send));
};
/**
* Immediately shuts down the connection
*
* @api public
*/
WebSocket.prototype.terminate = function terminate() {
if (this.readyState === WebSocket.CLOSED) return;
if (this._socket) {
this.readyState = WebSocket.CLOSING;
// End the connection
try { this._socket.end(); }
catch (e) {
// Socket error during end() call, so just destroy it right now
cleanupWebsocketResources.call(this, true);
return;
}
// Add a timeout to ensure that the connection is completely
// cleaned up within 30 seconds, even if the clean close procedure
// fails for whatever reason
// First cleanup any pre-existing timeout from an earlier "terminate" call,
// if one exists. Otherwise terminate calls in quick succession will leak timeouts
// and hold the program open for `closeTimout` time.
if (this._closeTimer) { clearTimeout(this._closeTimer); }
this._closeTimer = setTimeout(cleanupWebsocketResources.bind(this, true), closeTimeout);
} else if (this.readyState === WebSocket.CONNECTING) {
cleanupWebsocketResources.call(this, true);
}
};
/**
* Expose bufferedAmount
*
* @api public
*/
Object.defineProperty(WebSocket.prototype, 'bufferedAmount', {
get: function get() {
var amount = 0;
if (this._socket) {
amount = this._socket.bufferSize || 0;
}
return amount;
}
});
/**
* Expose binaryType
*
* This deviates from the W3C interface since ws doesn't support the required
* default "blob" type (instead we define a custom "nodebuffer" type).
*
* @see http://dev.w3.org/html5/websockets/#the-websocket-interface
* @api public
*/
Object.defineProperty(WebSocket.prototype, 'binaryType', {
get: function get() {
return this._binaryType;
},
set: function set(type) {
if (type === 'arraybuffer' || type === 'nodebuffer')
this._binaryType = type;
else
throw new SyntaxError('unsupported binaryType: must be either "nodebuffer" or "arraybuffer"');
}
});
/**
* Emulates the W3C Browser based WebSocket interface using function members.
*
* @see http://dev.w3.org/html5/websockets/#the-websocket-interface
* @api public
*/
['open', 'error', 'close', 'message'].forEach(function(method) {
Object.defineProperty(WebSocket.prototype, 'on' + method, {
/**
* Returns the current listener
*
* @returns {Mixed} the set function or undefined
* @api public
*/
get: function get() {
var listener = this.listeners(method)[0];
return listener ? (listener._listener ? listener._listener : listener) : undefined;
},
/**
* Start listening for events
*
* @param {Function} listener the listener
* @returns {Mixed} the set function or undefined
* @api public
*/
set: function set(listener) {
this.removeAllListeners(method);
this.addEventListener(method, listener);
}
});
});
/**
* Emulates the W3C Browser based WebSocket interface using addEventListener.
*
* @see https://developer.mozilla.org/en/DOM/element.addEventListener
* @see http://dev.w3.org/html5/websockets/#the-websocket-interface
* @api public
*/
WebSocket.prototype.addEventListener = function(method, listener) {
var target = this;
function onMessage (data, flags) {
if (flags.binary && this.binaryType === 'arraybuffer')
data = new Uint8Array(data).buffer;
listener.call(target, new MessageEvent(data, !!flags.binary, target));
}
function onClose (code, message) {
listener.call(target, new CloseEvent(code, message, target));
}
function onError (event) {
event.type = 'error';
event.target = target;
listener.call(target, event);
}
function onOpen () {
listener.call(target, new OpenEvent(target));
}
if (typeof listener === 'function') {
if (method === 'message') {
// store a reference so we can return the original function from the
// addEventListener hook
onMessage._listener = listener;
this.on(method, onMessage);
} else if (method === 'close') {
// store a reference so we can return the original function from the
// addEventListener hook
onClose._listener = listener;
this.on(method, onClose);
} else if (method === 'error') {
// store a reference so we can return the original function from the
// addEventListener hook
onError._listener = listener;
this.on(method, onError);
} else if (method === 'open') {
// store a reference so we can return the original function from the
// addEventListener hook
onOpen._listener = listener;
this.on(method, onOpen);
} else {
this.on(method, listener);
}
}
};
module.exports = WebSocket;
module.exports.buildHostHeader = buildHostHeader
/**
* W3C MessageEvent
*
* @see http://www.w3.org/TR/html5/comms.html
* @constructor
* @api private
*/
function MessageEvent(dataArg, isBinary, target) {
this.type = 'message';
this.data = dataArg;
this.target = target;
this.binary = isBinary; // non-standard.
}
/**
* W3C CloseEvent
*
* @see http://www.w3.org/TR/html5/comms.html
* @constructor
* @api private
*/
function CloseEvent(code, reason, target) {
this.type = 'close';
this.wasClean = (typeof code === 'undefined' || code === 1000);
this.code = code;
this.reason = reason;
this.target = target;
}
/**
* W3C OpenEvent
*
* @see http://www.w3.org/TR/html5/comms.html
* @constructor
* @api private
*/
function OpenEvent(target) {
this.type = 'open';
this.target = target;
}
// Append port number to Host header, only if specified in the url
// and non-default
function buildHostHeader(isSecure, hostname, port) {
var headerHost = hostname;
if (hostname) {
if ((isSecure && (port != 443)) || (!isSecure && (port != 80))){
headerHost = headerHost + ':' + port;
}
}
return headerHost;
}
/**
* Entirely private apis,
* which may or may not be bound to a sepcific WebSocket instance.
*/
function initAsServerClient(req, socket, upgradeHead, options) {
options = new Options({
protocolVersion: protocolVersion,
protocol: null,
extensions: {},
maxPayload: 0
}).merge(options);
// expose state properties
this.protocol = options.value.protocol;
this.protocolVersion = options.value.protocolVersion;
this.extensions = options.value.extensions;
this.supports.binary = (this.protocolVersion !== 'hixie-76');
this.upgradeReq = req;
this.readyState = WebSocket.CONNECTING;
this._isServer = true;
this.maxPayload = options.value.maxPayload;
// establish connection
if (options.value.protocolVersion === 'hixie-76') {
establishConnection.call(this, ReceiverHixie, SenderHixie, socket, upgradeHead);
} else {
establishConnection.call(this, Receiver, Sender, socket, upgradeHead);
}
}
function initAsClient(address, protocols, options) {
options = new Options({
origin: null,
protocolVersion: protocolVersion,
host: null,
headers: null,
protocol: protocols.join(','),
agent: null,
// ssl-related options
pfx: null,
key: null,
passphrase: null,
cert: null,
ca: null,
ciphers: null,
rejectUnauthorized: null,
perMessageDeflate: true,
localAddress: null
}).merge(options);
if (options.value.protocolVersion !== 8 && options.value.protocolVersion !== 13) {
throw new Error('unsupported protocol version');
}
// verify URL and establish http class
var serverUrl = url.parse(address);
var isUnixSocket = serverUrl.protocol === 'ws+unix:';
if (!serverUrl.host && !isUnixSocket) throw new Error('invalid url');
var isSecure = serverUrl.protocol === 'wss:' || serverUrl.protocol === 'https:';
var httpObj = isSecure ? https : http;
var port = serverUrl.port || (isSecure ? 443 : 80);
var auth = serverUrl.auth;
// prepare extensions
var extensionsOffer = {};
var perMessageDeflate;
if (options.value.perMessageDeflate) {
perMessageDeflate = new PerMessageDeflate(typeof options.value.perMessageDeflate !== true ? options.value.perMessageDeflate : {}, false);
extensionsOffer[PerMessageDeflate.extensionName] = perMessageDeflate.offer();
}
// expose state properties
this._isServer = false;
this.url = address;
this.protocolVersion = options.value.protocolVersion;
this.supports.binary = (this.protocolVersion !== 'hixie-76');
// begin handshake
var key = new Buffer(options.value.protocolVersion + '-' + Date.now()).toString('base64');
var shasum = crypto.createHash('sha1');
shasum.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11');
var expectedServerKey = shasum.digest('base64');
var agent = options.value.agent;
var headerHost = buildHostHeader(isSecure, serverUrl.hostname, port)
var requestOptions = {
port: port,
host: serverUrl.hostname,
headers: {
'Connection': 'Upgrade',
'Upgrade': 'websocket',
'Host': headerHost,
'Sec-WebSocket-Version': options.value.protocolVersion,
'Sec-WebSocket-Key': key
}
};
// If we have basic auth.
if (auth) {
requestOptions.headers.Authorization = 'Basic ' + new Buffer(auth).toString('base64');
}
if (options.value.protocol) {
requestOptions.headers['Sec-WebSocket-Protocol'] = options.value.protocol;
}
if (options.value.host) {
requestOptions.headers.Host = options.value.host;
}
if (options.value.headers) {
for (var header in options.value.headers) {
if (options.value.headers.hasOwnProperty(header)) {
requestOptions.headers[header] = options.value.headers[header];
}
}
}
if (Object.keys(extensionsOffer).length) {
requestOptions.headers['Sec-WebSocket-Extensions'] = Extensions.format(extensionsOffer);
}
if (options.isDefinedAndNonNull('pfx')
|| options.isDefinedAndNonNull('key')
|| options.isDefinedAndNonNull('passphrase')
|| options.isDefinedAndNonNull('cert')
|| options.isDefinedAndNonNull('ca')
|| options.isDefinedAndNonNull('ciphers')
|| options.isDefinedAndNonNull('rejectUnauthorized')) {
if (options.isDefinedAndNonNull('pfx')) requestOptions.pfx = options.value.pfx;
if (options.isDefinedAndNonNull('key')) requestOptions.key = options.value.key;
if (options.isDefinedAndNonNull('passphrase')) requestOptions.passphrase = options.value.passphrase;
if (options.isDefinedAndNonNull('cert')) requestOptions.cert = options.value.cert;
if (options.isDefinedAndNonNull('ca')) requestOptions.ca = options.value.ca;
if (options.isDefinedAndNonNull('ciphers')) requestOptions.ciphers = options.value.ciphers;
if (options.isDefinedAndNonNull('rejectUnauthorized')) requestOptions.rejectUnauthorized = options.value.rejectUnauthorized;
if (!agent) {
// global agent ignores client side certificates
agent = new httpObj.Agent(requestOptions);
}
}
requestOptions.path = serverUrl.path || '/';
if (agent) {
requestOptions.agent = agent;
}
if (isUnixSocket) {
requestOptions.socketPath = serverUrl.pathname;
}
if (options.value.localAddress) {
requestOptions.localAddress = options.value.localAddress;
}
if (options.value.origin) {
if (options.value.protocolVersion < 13) requestOptions.headers['Sec-WebSocket-Origin'] = options.value.origin;
else requestOptions.headers.Origin = options.value.origin;
}
var self = this;
var req = httpObj.request(requestOptions);
req.on('error', function onerror(error) {
self.emit('error', error);
cleanupWebsocketResources.call(self, error);
});
req.once('response', function response(res) {
var error;
if (!self.emit('unexpected-response', req, res)) {
error = new Error('unexpected server response (' + res.statusCode + ')');
req.abort();
self.emit('error', error);
}
cleanupWebsocketResources.call(self, error);
});
req.once('upgrade', function upgrade(res, socket, upgradeHead) {
if (self.readyState === WebSocket.CLOSED) {
// client closed before server accepted connection
self.emit('close');
self.removeAllListeners();
socket.end();
return;
}
var serverKey = res.headers['sec-websocket-accept'];
if (typeof serverKey === 'undefined' || serverKey !== expectedServerKey) {
self.emit('error', 'invalid server key');
self.removeAllListeners();
socket.end();
return;
}
var serverProt = res.headers['sec-websocket-protocol'];
var protList = (options.value.protocol || "").split(/, */);
var protError = null;
if (!options.value.protocol && serverProt) {
protError = 'server sent a subprotocol even though none requested';
} else if (options.value.protocol && !serverProt) {
protError = 'server sent no subprotocol even though requested';
} else if (serverProt && protList.indexOf(serverProt) === -1) {
protError = 'server responded with an invalid protocol';
}
if (protError) {
self.emit('error', protError);
self.removeAllListeners();
socket.end();
return;
} else if (serverProt) {
self.protocol = serverProt;
}
var serverExtensions = Extensions.parse(res.headers['sec-websocket-extensions']);
if (perMessageDeflate && serverExtensions[PerMessageDeflate.extensionName]) {
try {
perMessageDeflate.accept(serverExtensions[PerMessageDeflate.extensionName]);
} catch (err) {
self.emit('error', 'invalid extension parameter');
self.removeAllListeners();
socket.end();
return;
}
self.extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
}
establishConnection.call(self, Receiver, Sender, socket, upgradeHead);
// perform cleanup on http resources
req.removeAllListeners();
req = null;
agent = null;
});
req.end();
this.readyState = WebSocket.CONNECTING;
}
function establishConnection(ReceiverClass, SenderClass, socket, upgradeHead) {
var ultron = this._ultron = new Ultron(socket)
, called = false
, self = this;
socket.setTimeout(0);
socket.setNoDelay(true);
this._receiver = new ReceiverClass(this.extensions,this.maxPayload);
this._socket = socket;
// socket cleanup handlers
ultron.on('end', cleanupWebsocketResources.bind(this));
ultron.on('close', cleanupWebsocketResources.bind(this));
ultron.on('error', cleanupWebsocketResources.bind(this));
// ensure that the upgradeHead is added to the receiver
function firstHandler(data) {
if (called || self.readyState === WebSocket.CLOSED) return;
called = true;
socket.removeListener('data', firstHandler);
ultron.on('data', realHandler);
if (upgradeHead && upgradeHead.length > 0) {
realHandler(upgradeHead);
upgradeHead = null;
}
if (data) realHandler(data);
}
// subsequent packets are pushed straight to the receiver
function realHandler(data) {
self.bytesReceived += data.length;
self._receiver.add(data);
}
ultron.on('data', firstHandler);
// if data was passed along with the http upgrade,
// this will schedule a push of that on to the receiver.
// this has to be done on next tick, since the caller
// hasn't had a chance to set event handlers on this client
// object yet.
process.nextTick(firstHandler);
// receiver event handlers
self._receiver.ontext = function ontext(data, flags) {
flags = flags || {};
self.emit('message', data, flags);
};
self._receiver.onbinary = function onbinary(data, flags) {
flags = flags || {};
flags.binary = true;
self.emit('message', data, flags);
};
self._receiver.onping = function onping(data, flags) {
flags = flags || {};
self.pong(data, {
mask: !self._isServer,
binary: flags.binary === true
}, true);
self.emit('ping', data, flags);
};
self._receiver.onpong = function onpong(data, flags) {
self.emit('pong', data, flags || {});
};
self._receiver.onclose = function onclose(code, data, flags) {
flags = flags || {};
self._closeReceived = true;
self.close(code, data);
};
self._receiver.onerror = function onerror(reason, errorCode) {
// close the connection when the receiver reports a HyBi error code
self.close(typeof errorCode !== 'undefined' ? errorCode : 1002, '');
self.emit('error', (reason instanceof Error) ? reason : (new Error(reason)));
};
// finalize the client
this._sender = new SenderClass(socket, this.extensions);
this._sender.on('error', function onerror(error) {
self.close(1002, '');
self.emit('error', error);
});
this.readyState = WebSocket.OPEN;
this.emit('open');
}
function startQueue(instance) {
instance._queue = instance._queue || [];
}
function executeQueueSends(instance) {
var queue = instance._queue;
if (typeof queue === 'undefined') return;
delete instance._queue;
for (var i = 0, l = queue.length; i < l; ++i) {
queue[i]();
}
}
function sendStream(instance, stream, options, cb) {
stream.on('data', function incoming(data) {
if (instance.readyState !== WebSocket.OPEN) {
if (typeof cb === 'function') cb(new Error('not opened'));
else {
delete instance._queue;
instance.emit('error', new Error('not opened'));
}
return;
}
options.fin = false;
instance._sender.send(data, options);
});
stream.on('end', function end() {
if (instance.readyState !== WebSocket.OPEN) {
if (typeof cb === 'function') cb(new Error('not opened'));
else {
delete instance._queue;
instance.emit('error', new Error('not opened'));
}
return;
}
options.fin = true;
instance._sender.send(null, options);
if (typeof cb === 'function') cb(null);
});
}
function cleanupWebsocketResources(error) {
if (this.readyState === WebSocket.CLOSED) return;
this.readyState = WebSocket.CLOSED;
clearTimeout(this._closeTimer);
this._closeTimer = null;
// If the connection was closed abnormally (with an error), or if
// the close control frame was not received then the close code
// must default to 1006.
if (error || !this._closeReceived) {
this._closeCode = 1006;
}
this.emit('close', this._closeCode || 1000, this._closeMessage || '');
if (this._socket) {
if (this._ultron) this._ultron.destroy();
this._socket.on('error', function onerror() {
try { this.destroy(); }
catch (e) {}
});
try {
if (!error) this._socket.end();
else this._socket.destroy();
} catch (e) { /* Ignore termination errors */ }
this._socket = null;
this._ultron = null;
}
if (this._sender) {
this._sender.removeAllListeners();
this._sender = null;
}
if (this._receiver) {
this._receiver.cleanup();
this._receiver = null;
}
if (this.extensions[PerMessageDeflate.extensionName]) {
this.extensions[PerMessageDeflate.extensionName].cleanup();
}
this.extensions = null;
this.removeAllListeners();
this.on('error', function onerror() {}); // catch all errors after this
delete this._queue;
}

View File

@ -0,0 +1,554 @@
/*!
* ws: a node.js websocket client
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* MIT Licensed
*/
var util = require('util')
, events = require('events')
, http = require('http')
, crypto = require('crypto')
, Options = require('options')
, WebSocket = require('./WebSocket')
, Extensions = require('./Extensions')
, PerMessageDeflate = require('./PerMessageDeflate')
, tls = require('tls')
, url = require('url');
/**
* WebSocket Server implementation
*/
function WebSocketServer(options, callback) {
if (this instanceof WebSocketServer === false) {
return new WebSocketServer(options, callback);
}
events.EventEmitter.call(this);
options = new Options({
host: '0.0.0.0',
port: null,
server: null,
verifyClient: null,
handleProtocols: null,
path: null,
noServer: false,
disableHixie: false,
clientTracking: true,
perMessageDeflate: true,
maxPayload: 100 * 1024 * 1024
}).merge(options);
if (!options.isDefinedAndNonNull('port') && !options.isDefinedAndNonNull('server') && !options.value.noServer) {
throw new TypeError('`port` or a `server` must be provided');
}
var self = this;
if (options.isDefinedAndNonNull('port')) {
this._server = http.createServer(function (req, res) {
var body = http.STATUS_CODES[426];
res.writeHead(426, {
'Content-Length': body.length,
'Content-Type': 'text/plain'
});
res.end(body);
});
this._server.allowHalfOpen = false;
this._server.listen(options.value.port, options.value.host, callback);
this._closeServer = function() { if (self._server) self._server.close(); };
}
else if (options.value.server) {
this._server = options.value.server;
if (options.value.path) {
// take note of the path, to avoid collisions when multiple websocket servers are
// listening on the same http server
if (this._server._webSocketPaths && options.value.server._webSocketPaths[options.value.path]) {
throw new Error('two instances of WebSocketServer cannot listen on the same http server path');
}
if (typeof this._server._webSocketPaths !== 'object') {
this._server._webSocketPaths = {};
}
this._server._webSocketPaths[options.value.path] = 1;
}
}
if (this._server) {
this._onceServerListening = function() { self.emit('listening'); };
this._server.once('listening', this._onceServerListening);
}
if (typeof this._server != 'undefined') {
this._onServerError = function(error) { self.emit('error', error) };
this._server.on('error', this._onServerError);
this._onServerUpgrade = function(req, socket, upgradeHead) {
//copy upgradeHead to avoid retention of large slab buffers used in node core
var head = new Buffer(upgradeHead.length);
upgradeHead.copy(head);
self.handleUpgrade(req, socket, head, function(client) {
self.emit('connection'+req.url, client);
self.emit('connection', client);
});
};
this._server.on('upgrade', this._onServerUpgrade);
}
this.options = options.value;
this.path = options.value.path;
this.clients = [];
}
/**
* Inherits from EventEmitter.
*/
util.inherits(WebSocketServer, events.EventEmitter);
/**
* Immediately shuts down the connection.
*
* @api public
*/
WebSocketServer.prototype.close = function(callback) {
// terminate all associated clients
var error = null;
try {
for (var i = 0, l = this.clients.length; i < l; ++i) {
this.clients[i].terminate();
}
}
catch (e) {
error = e;
}
// remove path descriptor, if any
if (this.path && this._server._webSocketPaths) {
delete this._server._webSocketPaths[this.path];
if (Object.keys(this._server._webSocketPaths).length == 0) {
delete this._server._webSocketPaths;
}
}
// close the http server if it was internally created
try {
if (typeof this._closeServer !== 'undefined') {
this._closeServer();
}
}
finally {
if (this._server) {
this._server.removeListener('listening', this._onceServerListening);
this._server.removeListener('error', this._onServerError);
this._server.removeListener('upgrade', this._onServerUpgrade);
}
delete this._server;
}
if(callback)
callback(error);
else if(error)
throw error;
}
/**
* Handle a HTTP Upgrade request.
*
* @api public
*/
WebSocketServer.prototype.handleUpgrade = function(req, socket, upgradeHead, cb) {
// check for wrong path
if (this.options.path) {
var u = url.parse(req.url);
if (u && u.pathname !== this.options.path) return;
}
if (typeof req.headers.upgrade === 'undefined' || req.headers.upgrade.toLowerCase() !== 'websocket') {
abortConnection(socket, 400, 'Bad Request');
return;
}
if (req.headers['sec-websocket-key1']) handleHixieUpgrade.apply(this, arguments);
else handleHybiUpgrade.apply(this, arguments);
}
module.exports = WebSocketServer;
/**
* Entirely private apis,
* which may or may not be bound to a sepcific WebSocket instance.
*/
function handleHybiUpgrade(req, socket, upgradeHead, cb) {
// handle premature socket errors
var errorHandler = function() {
try { socket.destroy(); } catch (e) {}
}
socket.on('error', errorHandler);
// verify key presence
if (!req.headers['sec-websocket-key']) {
abortConnection(socket, 400, 'Bad Request');
return;
}
// verify version
var version = parseInt(req.headers['sec-websocket-version']);
if ([8, 13].indexOf(version) === -1) {
abortConnection(socket, 400, 'Bad Request');
return;
}
// verify protocol
var protocols = req.headers['sec-websocket-protocol'];
// verify client
var origin = version < 13 ?
req.headers['sec-websocket-origin'] :
req.headers['origin'];
// handle extensions offer
var extensionsOffer = Extensions.parse(req.headers['sec-websocket-extensions']);
// handler to call when the connection sequence completes
var self = this;
var completeHybiUpgrade2 = function(protocol) {
// calc key
var key = req.headers['sec-websocket-key'];
var shasum = crypto.createHash('sha1');
shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
key = shasum.digest('base64');
var headers = [
'HTTP/1.1 101 Switching Protocols'
, 'Upgrade: websocket'
, 'Connection: Upgrade'
, 'Sec-WebSocket-Accept: ' + key
];
if (typeof protocol != 'undefined') {
headers.push('Sec-WebSocket-Protocol: ' + protocol);
}
var extensions = {};
try {
extensions = acceptExtensions.call(self, extensionsOffer);
} catch (err) {
abortConnection(socket, 400, 'Bad Request');
return;
}
if (Object.keys(extensions).length) {
var serverExtensions = {};
Object.keys(extensions).forEach(function(token) {
serverExtensions[token] = [extensions[token].params]
});
headers.push('Sec-WebSocket-Extensions: ' + Extensions.format(serverExtensions));
}
// allows external modification/inspection of handshake headers
self.emit('headers', headers);
socket.setTimeout(0);
socket.setNoDelay(true);
try {
socket.write(headers.concat('', '').join('\r\n'));
}
catch (e) {
// if the upgrade write fails, shut the connection down hard
try { socket.destroy(); } catch (e) {}
return;
}
var client = new WebSocket([req, socket, upgradeHead], {
protocolVersion: version,
protocol: protocol,
extensions: extensions,
maxPayload: self.options.maxPayload
});
if (self.options.clientTracking) {
self.clients.push(client);
client.on('close', function() {
var index = self.clients.indexOf(client);
if (index != -1) {
self.clients.splice(index, 1);
}
});
}
// signal upgrade complete
socket.removeListener('error', errorHandler);
cb(client);
}
// optionally call external protocol selection handler before
// calling completeHybiUpgrade2
var completeHybiUpgrade1 = function() {
// choose from the sub-protocols
if (typeof self.options.handleProtocols == 'function') {
var protList = (protocols || "").split(/, */);
var callbackCalled = false;
var res = self.options.handleProtocols(protList, function(result, protocol) {
callbackCalled = true;
if (!result) abortConnection(socket, 401, 'Unauthorized');
else completeHybiUpgrade2(protocol);
});
if (!callbackCalled) {
// the handleProtocols handler never called our callback
abortConnection(socket, 501, 'Could not process protocols');
}
return;
} else {
if (typeof protocols !== 'undefined') {
completeHybiUpgrade2(protocols.split(/, */)[0]);
}
else {
completeHybiUpgrade2();
}
}
}
// optionally call external client verification handler
if (typeof this.options.verifyClient == 'function') {
var info = {
origin: origin,
secure: typeof req.connection.authorized !== 'undefined' || typeof req.connection.encrypted !== 'undefined',
req: req
};
if (this.options.verifyClient.length == 2) {
this.options.verifyClient(info, function(result, code, name) {
if (typeof code === 'undefined') code = 401;
if (typeof name === 'undefined') name = http.STATUS_CODES[code];
if (!result) abortConnection(socket, code, name);
else completeHybiUpgrade1();
});
return;
}
else if (!this.options.verifyClient(info)) {
abortConnection(socket, 401, 'Unauthorized');
return;
}
}
completeHybiUpgrade1();
}
function handleHixieUpgrade(req, socket, upgradeHead, cb) {
// handle premature socket errors
var errorHandler = function() {
try { socket.destroy(); } catch (e) {}
}
socket.on('error', errorHandler);
// bail if options prevent hixie
if (this.options.disableHixie) {
abortConnection(socket, 401, 'Hixie support disabled');
return;
}
// verify key presence
if (!req.headers['sec-websocket-key2']) {
abortConnection(socket, 400, 'Bad Request');
return;
}
var origin = req.headers['origin']
, self = this;
// setup handshake completion to run after client has been verified
var onClientVerified = function() {
var wshost;
if (!req.headers['x-forwarded-host'])
wshost = req.headers.host;
else
wshost = req.headers['x-forwarded-host'];
var location = ((req.headers['x-forwarded-proto'] === 'https' || socket.encrypted) ? 'wss' : 'ws') + '://' + wshost + req.url
, protocol = req.headers['sec-websocket-protocol'];
// build the response header and return a Buffer
var buildResponseHeader = function() {
var headers = [
'HTTP/1.1 101 Switching Protocols'
, 'Upgrade: WebSocket'
, 'Connection: Upgrade'
, 'Sec-WebSocket-Location: ' + location
];
if (typeof protocol != 'undefined') headers.push('Sec-WebSocket-Protocol: ' + protocol);
if (typeof origin != 'undefined') headers.push('Sec-WebSocket-Origin: ' + origin);
return new Buffer(headers.concat('', '').join('\r\n'));
};
// send handshake response before receiving the nonce
var handshakeResponse = function() {
socket.setTimeout(0);
socket.setNoDelay(true);
var headerBuffer = buildResponseHeader();
try {
socket.write(headerBuffer, 'binary', function(err) {
// remove listener if there was an error
if (err) socket.removeListener('data', handler);
return;
});
} catch (e) {
try { socket.destroy(); } catch (e) {}
return;
};
};
// handshake completion code to run once nonce has been successfully retrieved
var completeHandshake = function(nonce, rest, headerBuffer) {
// calculate key
var k1 = req.headers['sec-websocket-key1']
, k2 = req.headers['sec-websocket-key2']
, md5 = crypto.createHash('md5');
[k1, k2].forEach(function (k) {
var n = parseInt(k.replace(/[^\d]/g, ''))
, spaces = k.replace(/[^ ]/g, '').length;
if (spaces === 0 || n % spaces !== 0){
abortConnection(socket, 400, 'Bad Request');
return;
}
n /= spaces;
md5.update(String.fromCharCode(
n >> 24 & 0xFF,
n >> 16 & 0xFF,
n >> 8 & 0xFF,
n & 0xFF));
});
md5.update(nonce.toString('binary'));
socket.setTimeout(0);
socket.setNoDelay(true);
try {
var hashBuffer = new Buffer(md5.digest('binary'), 'binary');
var handshakeBuffer = new Buffer(headerBuffer.length + hashBuffer.length);
headerBuffer.copy(handshakeBuffer, 0);
hashBuffer.copy(handshakeBuffer, headerBuffer.length);
// do a single write, which - upon success - causes a new client websocket to be setup
socket.write(handshakeBuffer, 'binary', function(err) {
if (err) return; // do not create client if an error happens
var client = new WebSocket([req, socket, rest], {
protocolVersion: 'hixie-76',
protocol: protocol
});
if (self.options.clientTracking) {
self.clients.push(client);
client.on('close', function() {
var index = self.clients.indexOf(client);
if (index != -1) {
self.clients.splice(index, 1);
}
});
}
// signal upgrade complete
socket.removeListener('error', errorHandler);
cb(client);
});
}
catch (e) {
try { socket.destroy(); } catch (e) {}
return;
}
}
// retrieve nonce
var nonceLength = 8;
if (upgradeHead && upgradeHead.length >= nonceLength) {
var nonce = upgradeHead.slice(0, nonceLength);
var rest = upgradeHead.length > nonceLength ? upgradeHead.slice(nonceLength) : null;
completeHandshake.call(self, nonce, rest, buildResponseHeader());
}
else {
// nonce not present in upgradeHead
var nonce = new Buffer(nonceLength);
upgradeHead.copy(nonce, 0);
var received = upgradeHead.length;
var rest = null;
var handler = function (data) {
var toRead = Math.min(data.length, nonceLength - received);
if (toRead === 0) return;
data.copy(nonce, received, 0, toRead);
received += toRead;
if (received == nonceLength) {
socket.removeListener('data', handler);
if (toRead < data.length) rest = data.slice(toRead);
// complete the handshake but send empty buffer for headers since they have already been sent
completeHandshake.call(self, nonce, rest, new Buffer(0));
}
}
// handle additional data as we receive it
socket.on('data', handler);
// send header response before we have the nonce to fix haproxy buffering
handshakeResponse();
}
}
// verify client
if (typeof this.options.verifyClient == 'function') {
var info = {
origin: origin,
secure: typeof req.connection.authorized !== 'undefined' || typeof req.connection.encrypted !== 'undefined',
req: req
};
if (this.options.verifyClient.length == 2) {
var self = this;
this.options.verifyClient(info, function(result, code, name) {
if (typeof code === 'undefined') code = 401;
if (typeof name === 'undefined') name = http.STATUS_CODES[code];
if (!result) abortConnection(socket, code, name);
else onClientVerified.apply(self);
});
return;
}
else if (!this.options.verifyClient(info)) {
abortConnection(socket, 401, 'Unauthorized');
return;
}
}
// no client verification required
onClientVerified();
}
function acceptExtensions(offer) {
var extensions = {};
var options = this.options.perMessageDeflate;
var maxPayload = this.options.maxPayload;
if (options && offer[PerMessageDeflate.extensionName]) {
var perMessageDeflate = new PerMessageDeflate(options !== true ? options : {}, true, maxPayload);
perMessageDeflate.accept(offer[PerMessageDeflate.extensionName]);
extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
}
return extensions;
}
function abortConnection(socket, code, name) {
try {
var response = [
'HTTP/1.1 ' + code + ' ' + name,
'Content-type: text/html'
];
socket.write(response.concat('', '').join('\r\n'));
}
catch (e) { /* ignore errors - we've aborted this connection */ }
finally {
// ensure that an early aborted connection is shut down completely
try { socket.destroy(); } catch (e) {}
}
}

View File

@ -0,0 +1,42 @@
{
"name": "ws",
"description": "Simple to use, blazing fast and thoroughly tested websocket client and server for Node.js",
"version": "1.1.5",
"keywords": [
"Hixie",
"HyBi",
"Push",
"RFC-6455",
"WebSocket",
"WebSockets",
"real-time"
],
"homepage": "https://github.com/websockets/ws",
"bugs": "https://github.com/websockets/ws/issues",
"repository": "websockets/ws",
"author": "Einar Otto Stangvik <einaros@gmail.com> (http://2x.io)",
"license": "MIT",
"main": "index.js",
"files": [
"index.js",
"lib"
],
"scripts": {
"test": "make test"
},
"dependencies": {
"options": ">=0.0.5",
"ultron": "1.0.x"
},
"devDependencies": {
"ansi": "0.3.x",
"benchmark": "0.3.x",
"bufferutil": "1.2.x",
"expect.js": "0.3.x",
"istanbul": "^0.4.1",
"mocha": "2.3.x",
"should": "8.0.x",
"tinycolor": "0.0.x",
"utf-8-validate": "1.2.x"
}
}

83
node_modules/engine.io-client/package.json generated vendored Normal file
View File

@ -0,0 +1,83 @@
{
"name": "engine.io-client",
"description": "Client for the realtime Engine",
"license": "MIT",
"version": "1.8.6",
"homepage": "https://github.com/socketio/engine.io-client",
"contributors": [
{
"name": "Guillermo Rauch",
"email": "rauchg@gmail.com"
},
{
"name": "Vladimir Dronnikov",
"email": "dronnikov@gmail.com"
},
{
"name": "Christoph Dorn",
"web": "https://github.com/cadorn"
},
{
"name": "Mark Mokryn",
"email": "mokesmokes@gmail.com"
}
],
"dependencies": {
"component-emitter": "1.2.1",
"component-inherit": "0.0.3",
"debug": "2.3.3",
"engine.io-parser": "1.3.2",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"parsejson": "0.0.3",
"parseqs": "0.0.5",
"parseuri": "0.0.5",
"ws": "~1.1.5",
"xmlhttprequest-ssl": "1.6.3",
"yeast": "0.1.2"
},
"devDependencies": {
"babel-core": "6.4.5",
"babel-eslint": "4.1.7",
"babel-loader": "6.2.1",
"babel-preset-es2015": "6.3.13",
"blob": "0.0.4",
"concat-stream": "1.4.6",
"del": "2.2.0",
"derequire": "1.2.0",
"eslint-config-standard": "4.4.0",
"eslint-plugin-standard": "1.3.1",
"engine.io": "1.8.5",
"expect.js": "0.2.0",
"express": "3.4.8",
"gulp": "3.9.0",
"gulp-eslint": "1.1.1",
"gulp-file": "0.2.0",
"gulp-istanbul": "0.10.3",
"gulp-mocha": "2.2.0",
"gulp-task-listing": "1.0.1",
"istanbul": "0.2.3",
"mocha": "1.16.2",
"webpack": "1.12.12",
"webpack-stream": "3.1.0",
"zuul": "3.11.0",
"zuul-builder-webpack": "1.1.0",
"zuul-ngrok": "4.0.0"
},
"scripts": {
"test": "gulp test"
},
"browser": {
"ws": false,
"xmlhttprequest-ssl": "./lib/xmlhttprequest.js"
},
"repository": {
"type": "git",
"url": "https://github.com/socketio/engine.io-client.git"
},
"files": [
"index.js",
"lib/",
"engine.io.js"
]
}