summaryrefslogtreecommitdiff
path: root/node_modules/ws
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/ws')
-rw-r--r--node_modules/ws/.npmignore6
-rw-r--r--node_modules/ws/.travis.yml6
-rw-r--r--node_modules/ws/History.md260
-rw-r--r--node_modules/ws/Makefile38
-rw-r--r--node_modules/ws/README.md159
-rw-r--r--node_modules/ws/bench/parser.benchmark.js115
-rw-r--r--node_modules/ws/bench/sender.benchmark.js66
-rw-r--r--node_modules/ws/bench/speed.js105
-rw-r--r--node_modules/ws/bench/util.js105
-rw-r--r--node_modules/ws/bin/wscat190
-rw-r--r--node_modules/ws/binding.gyp14
-rw-r--r--node_modules/ws/builderror.log11
-rw-r--r--node_modules/ws/doc/ws.md179
-rw-r--r--node_modules/ws/examples/fileapi/.npmignore1
-rw-r--r--node_modules/ws/examples/fileapi/package.json18
-rw-r--r--node_modules/ws/examples/fileapi/public/app.js39
-rw-r--r--node_modules/ws/examples/fileapi/public/index.html22
-rw-r--r--node_modules/ws/examples/fileapi/public/uploader.js55
-rw-r--r--node_modules/ws/examples/fileapi/server.js103
-rw-r--r--node_modules/ws/examples/serverstats-express_3/package.json17
-rw-r--r--node_modules/ws/examples/serverstats-express_3/public/index.html33
-rw-r--r--node_modules/ws/examples/serverstats-express_3/server.js21
-rw-r--r--node_modules/ws/examples/serverstats/package.json17
-rw-r--r--node_modules/ws/examples/serverstats/public/index.html33
-rw-r--r--node_modules/ws/examples/serverstats/server.js19
-rw-r--r--node_modules/ws/examples/ssl.js59
-rw-r--r--node_modules/ws/index.js26
-rw-r--r--node_modules/ws/lib/BufferPool.js59
-rw-r--r--node_modules/ws/lib/BufferUtil.fallback.js47
-rw-r--r--node_modules/ws/lib/BufferUtil.js16
-rw-r--r--node_modules/ws/lib/ErrorCodes.js24
-rw-r--r--node_modules/ws/lib/Receiver.hixie.js180
-rw-r--r--node_modules/ws/lib/Receiver.js591
-rw-r--r--node_modules/ws/lib/Sender.hixie.js123
-rw-r--r--node_modules/ws/lib/Sender.js226
-rw-r--r--node_modules/ws/lib/Validation.fallback.js12
-rw-r--r--node_modules/ws/lib/Validation.js16
-rw-r--r--node_modules/ws/lib/WebSocket.js766
-rw-r--r--node_modules/ws/lib/WebSocketServer.js460
-rw-r--r--node_modules/ws/lib/browser.js5
-rw-r--r--node_modules/ws/node_modules/commander/.npmignore4
-rw-r--r--node_modules/ws/node_modules/commander/.travis.yml4
-rw-r--r--node_modules/ws/node_modules/commander/History.md107
-rw-r--r--node_modules/ws/node_modules/commander/Makefile7
-rw-r--r--node_modules/ws/node_modules/commander/Readme.md262
-rw-r--r--node_modules/ws/node_modules/commander/index.js2
-rw-r--r--node_modules/ws/node_modules/commander/lib/commander.js1026
-rw-r--r--node_modules/ws/node_modules/commander/package.json42
-rw-r--r--node_modules/ws/node_modules/options/.npmignore5
-rw-r--r--node_modules/ws/node_modules/options/Makefile12
-rw-r--r--node_modules/ws/node_modules/options/README.md28
-rw-r--r--node_modules/ws/node_modules/options/lib/options.js86
-rw-r--r--node_modules/ws/node_modules/options/package.json36
-rw-r--r--node_modules/ws/node_modules/options/test/fixtures/test.conf4
-rw-r--r--node_modules/ws/node_modules/options/test/options.test.js140
-rw-r--r--node_modules/ws/node_modules/tinycolor/.npmignore5
-rw-r--r--node_modules/ws/node_modules/tinycolor/README.md3
-rw-r--r--node_modules/ws/node_modules/tinycolor/example.js3
-rw-r--r--node_modules/ws/node_modules/tinycolor/package.json31
-rw-r--r--node_modules/ws/node_modules/tinycolor/tinycolor.js31
-rw-r--r--node_modules/ws/package.json48
-rw-r--r--node_modules/ws/src/bufferutil.cc115
-rw-r--r--node_modules/ws/src/validation.cc143
-rw-r--r--node_modules/ws/test/BufferPool.test.js63
-rw-r--r--node_modules/ws/test/Receiver.hixie.test.js158
-rw-r--r--node_modules/ws/test/Receiver.test.js255
-rw-r--r--node_modules/ws/test/Sender.hixie.test.js134
-rw-r--r--node_modules/ws/test/Sender.test.js24
-rw-r--r--node_modules/ws/test/Validation.test.js23
-rw-r--r--node_modules/ws/test/WebSocket.integration.js42
-rw-r--r--node_modules/ws/test/WebSocket.test.js1609
-rw-r--r--node_modules/ws/test/WebSocketServer.test.js1103
-rw-r--r--node_modules/ws/test/autobahn-server.js29
-rw-r--r--node_modules/ws/test/autobahn.js52
-rw-r--r--node_modules/ws/test/fixtures/agent1-cert.pem16
-rw-r--r--node_modules/ws/test/fixtures/agent1-key.pem15
-rw-r--r--node_modules/ws/test/fixtures/ca1-cert.pem15
-rw-r--r--node_modules/ws/test/fixtures/ca1-key.pem17
-rw-r--r--node_modules/ws/test/fixtures/certificate.pem13
-rw-r--r--node_modules/ws/test/fixtures/key.pem15
-rw-r--r--node_modules/ws/test/fixtures/request.pem11
-rw-r--r--node_modules/ws/test/fixtures/textfile9
-rw-r--r--node_modules/ws/test/hybi-common.js99
-rw-r--r--node_modules/ws/test/testserver.js180
84 files changed, 10268 insertions, 0 deletions
diff --git a/node_modules/ws/.npmignore b/node_modules/ws/.npmignore
new file mode 100644
index 0000000..8233793
--- /dev/null
+++ b/node_modules/ws/.npmignore
@@ -0,0 +1,6 @@
+npm-debug.log
+node_modules
+.*.swp
+.lock-*
+build
+
diff --git a/node_modules/ws/.travis.yml b/node_modules/ws/.travis.yml
new file mode 100644
index 0000000..08e4dad
--- /dev/null
+++ b/node_modules/ws/.travis.yml
@@ -0,0 +1,6 @@
+language: node_js
+npm_args: --ws:native
+node_js:
+ - "0.6"
+ - "0.8"
+ - "0.10"
diff --git a/node_modules/ws/History.md b/node_modules/ws/History.md
new file mode 100644
index 0000000..97c6ec2
--- /dev/null
+++ b/node_modules/ws/History.md
@@ -0,0 +1,260 @@
+v0.4.25 - December 17th, 2012
+=====================
+* Removed install.js. [shtylman]
+* Added browser field to package.json. [shtylman]
+* Support overwriting host header. [Raynos]
+* Emit 'listening' also with custom http server. [sebiq]
+
+v0.4.24 - December 6th, 2012
+=====================
+* Yet another intermediate release, to not delay minor features any longer.
+* Native support installation issues further circumvented. [einaros]
+
+v0.4.23 - November 19th, 2012
+=====================
+* Service release - last before major upgrade.
+* Changes default host from 127.0.0.1 to 0.0.0.0. [einaros]
+
+v0.4.22 - October 3rd, 2012
+=====================
+* clear failsafe cleanup timeout once cleanup is called [AndreasMadsen]
+* added w3c compatible CloseEvent for onclose / addEventListener("close", ...). [einaros]
+* fix the sub protocol header handler [sonnyp]
+* fix unhandled exception if socket closes and 'error' is emitted [jmatthewsr-ms]
+
+v0.4.21 - July 14th, 2012
+=====================
+* Emit error if server reponds with anything other than status code 101. [einaros]
+* Added 'headers' event to server. [rauchg]
+* path.exists moved to fs.exists. [blakmatrix]
+
+v0.4.20 - June 26th, 2012
+=====================
+* node v0.8.0 compatibility release.
+
+v0.4.19 - June 19th, 2012
+=====================
+* Change sender to merge buffers for relatively small payloads, may improve perf in some cases [einaros]
+* Avoid EventEmitter for Receiver classes. As above this may improve perf. [einaros]
+* Renamed fallback files from the somewhat misleading '*Windows'. [einaros]
+
+v0.4.18 - June 14th 2012
+=====================
+* Fixed incorrect md5 digest encoding in Hixie handshake [nicokaiser]
+* Added example of use with Express 3 [einaros]
+* Change installation procedure to not require --ws:native to build native extensions. They will now build if a compiler is available. [einaros]
+
+v0.4.17 - June 13th 2012
+=====================
+* Improve error handling during connection handshaking [einaros]
+* Ensure that errors are caught also after connection teardown [nicokaiser]
+* Update 'mocha' version to 1.1.0. [einaros]
+* Stop showing 'undefined' for some error logs. [tricknotes]
+* Update 'should' version to 0.6.3 [tricknotes]
+
+v0.4.16 - June 1st 2012
+=====================
+* Build fix for Windows. [einaros]
+
+v0.4.15 - May 20th 2012
+=====================
+* Enable fauxe streaming for hixie tansport. [einaros]
+* Allow hixie sender to deal with buffers. [einaros/pigne]
+* Allow error code 1011. [einaros]
+* Fix framing for empty packets (empty pings and pongs might break). [einaros]
+* Improve error and close handling, to avoid connections lingering in CLOSING state. [einaros]
+
+v0.4.14 - Apr 30th 2012
+=====================
+* use node-gyp instead of node-waf [TooTallNate]
+* remove old windows compatibility makefile, and silently fall back to native modules [einaros]
+* ensure connection status [nicokaiser]
+* websocket client updated to use port 443 by default for wss:// connections [einaros]
+* support unix sockets [kschzt]
+
+v0.4.13 - Apr 12th 2012
+=====================
+
+* circumvent node 0.6+ related memory leak caused by Object.defineProperty [nicokaiser]
+* improved error handling, improving stability in massive load use cases [nicokaiser]
+
+v0.4.12 - Mar 30th 2012
+=====================
+
+* various memory leak / possible memory leak cleanups [einaros]
+* api documentation [nicokaiser]
+* add option to disable client tracking [nicokaiser]
+
+v0.4.11 - Mar 24th 2012
+=====================
+
+* node v0.7 compatibillity release
+* gyp support [TooTallNate]
+* commander dependency update [jwueller]
+* loadbalancer support [nicokaiser]
+
+v0.4.10 - Mar 22th 2012
+=====================
+
+* Final hixie close frame fixes. [nicokaiser]
+
+v0.4.9 - Mar 21st 2012
+=====================
+
+* Various hixie bugfixes (such as proper close frame handling). [einaros]
+
+v0.4.8 - Feb 29th 2012
+=====================
+
+* Allow verifyClient to run asynchronously [karlsequin]
+* Various bugfixes and cleanups. [einaros]
+
+v0.4.7 - Feb 21st 2012
+=====================
+
+* Exposed bytesReceived from websocket client object, which makes it possible to implement bandwidth sampling. [einaros]
+* Updated browser based file upload example to include and output per websocket channel bandwidth sampling. [einaros]
+* Changed build scripts to check which architecture is currently in use. Required after the node.js changes to have prebuilt packages target ia32 by default. [einaros]
+
+v0.4.6 - Feb 9th 2012
+=====================
+
+* Added browser based file upload example. [einaros]
+* Added server-to-browser status push example. [einaros]
+* Exposed pause() and resume() on WebSocket object, to enable client stream shaping. [einaros]
+
+v0.4.5 - Feb 7th 2012
+=====================
+
+* Corrected regression bug in handling of connections with the initial frame delivered across both http upgrade head and a standalone packet. This would lead to a race condition, which in some cases could cause message corruption. [einaros]
+
+v0.4.4 - Feb 6th 2012
+=====================
+
+* Pass original request object to verifyClient, for cookie or authentication verifications. [einaros]
+* Implemented addEventListener and slightly improved the emulation API by adding a MessageEvent with a readonly data attribute. [aslakhellesoy]
+* Rewrite parts of hybi receiver to avoid stack overflows for large amounts of packets bundled in the same buffer / packet. [einaros]
+
+v0.4.3 - Feb 4th 2012
+=====================
+
+* Prioritized update: Corrected issue which would cause sockets to stay open longer than necessary, and resource leakage because of this. [einaros]
+
+v0.4.2 - Feb 4th 2012
+=====================
+
+* Breaking change: WebSocketServer's verifyOrigin option has been renamed to verifyClient. [einaros]
+* verifyClient now receives { origin: 'origin header', secure: true/false }, where 'secure' will be true for ssl connections. [einaros]
+* Split benchmark, in preparation for more thorough case. [einaros]
+* Introduced hixie-76 draft support for server, since Safari (iPhone / iPad / OS X) and Opera still aren't updated to use Hybi. [einaros]
+* Expose 'supports' object from WebSocket, to indicate e.g. the underlying transport's support for binary data. [einaros]
+* Test and code cleanups. [einaros]
+
+v0.4.1 - Jan 25th 2012
+=====================
+
+* Use readline in wscat [tricknotes]
+* Refactor _state away, in favor of the new _readyState [tricknotes]
+* travis-ci integration [einaros]
+* Fixed race condition in testsuite, causing a few tests to fail (without actually indicating errors) on travis [einaros]
+* Expose pong event [paddybyers]
+* Enabled running of WebSocketServer in noServer-mode, meaning that upgrades are passed in manually. [einaros]
+* Reworked connection procedure for WebSocketServer, and cleaned up tests. [einaros]
+
+v0.4.0 - Jan 2nd 2012
+=====================
+
+* Windows compatibility [einaros]
+* Windows compatible test script [einaros]
+
+v0.3.9 - Jan 1st 2012
+======================
+
+* Improved protocol framing performance [einaros]
+* WSS support [kazuyukitanimura]
+* WSS tests [einaros]
+* readyState exposed [justinlatimer, tricknotes]
+* url property exposed [justinlatimer]
+* Removed old 'state' property [einaros]
+* Test cleanups [einaros]
+
+v0.3.8 - Dec 27th 2011
+======================
+
+* Made it possible to listen on specific paths, which is especially good to have for precreated http servers [einaros]
+* Extensive WebSocket / WebSocketServer cleanup, including changing all internal properties to unconfigurable, unenumerable properties [einaros]
+* Receiver modifications to ensure even better performance with fragmented sends [einaros]
+* Fixed issue in sender.js, which would cause SlowBuffer instances (such as returned from the crypto library's randomBytes) to be copied (and thus be dead slow) [einaros]
+* Removed redundant buffer copy in sender.js, which should improve server performance [einaros]
+
+v0.3.7 - Dec 25nd 2011
+======================
+
+* Added a browser based API which uses EventEmitters internally [3rd-Eden]
+* Expose request information from upgrade event for websocket server clients [mmalecki]
+
+v0.3.6 - Dec 19th 2011
+======================
+
+* Added option to let WebSocket.Server use an already existing http server [mmalecki]
+* Migrating various option structures to use options.js module [einaros]
+* Added a few more tests, options and handshake verifications to ensure that faulty connections are dealt with [einaros]
+* Code cleanups in Sender and Receiver, to ensure even faster parsing [einaros]
+
+v0.3.5 - Dec 13th 2011
+======================
+
+* Optimized Sender.js, Receiver.js and bufferutil.cc:
+ * Apply loop-unrolling-like small block copies rather than use node.js Buffer#copy() (which is slow).
+ * Mask blocks of data using combination of 32bit xor and loop-unrolling, instead of single bytes.
+ * Keep pre-made send buffer for small transfers.
+* Leak fixes and code cleanups.
+
+v0.3.3 - Dec 12th 2011
+======================
+
+* Compile fix for Linux.
+* Rewrote parts of WebSocket.js, to avoid try/catch and thus avoid optimizer bailouts.
+
+v0.3.2 - Dec 11th 2011
+======================
+
+* Further performance updates, including the additions of a native BufferUtil module, which deals with several of the cpu intensive WebSocket operations.
+
+v0.3.1 - Dec 8th 2011
+======================
+
+* Service release, fixing broken tests.
+
+v0.3.0 - Dec 8th 2011
+======================
+
+* Node.js v0.4.x compatibility.
+* Code cleanups and efficiency improvements.
+* WebSocket server added, although this will still mainly be a client library.
+* WebSocket server certified to pass the Autobahn test suite.
+* Protocol improvements and corrections - such as handling (redundant) masks for empty fragments.
+* 'wscat' command line utility added, which can act as either client or server.
+
+v0.2.6 - Dec 3rd 2011
+======================
+
+* Renamed to 'ws'. Big woop, right -- but easy-websocket really just doesn't cut it anymore!
+
+v0.2.5 - Dec 3rd 2011
+======================
+
+ * Rewrote much of the WebSocket parser, to ensure high speed for highly fragmented messages.
+ * Added a BufferPool, as a start to more efficiently deal with allocations for WebSocket connections. More work to come, in that area.
+ * Updated the Autobahn report, at http://einaros.github.com/easy-websocket, with comparisons against WebSocket-Node 1.0.2 and Chrome 16.
+
+v0.2.0 - Nov 25th 2011
+======================
+
+ * Major rework to make sure all the Autobahn test cases pass. Also updated the internal tests to cover more corner cases.
+
+v0.1.2 - Nov 14th 2011
+======================
+
+ * Back and forth, back and forth: now settled on keeping the api (event names, methods) closer to the websocket browser api. This will stick now.
+ * Started keeping this history record. Better late than never, right?
diff --git a/node_modules/ws/Makefile b/node_modules/ws/Makefile
new file mode 100644
index 0000000..a0b82a1
--- /dev/null
+++ b/node_modules/ws/Makefile
@@ -0,0 +1,38 @@
+ALL_TESTS = $(shell find test/ -name '*.test.js')
+ALL_INTEGRATION = $(shell find test/ -name '*.integration.js')
+
+all:
+ node-gyp configure build
+
+clean:
+ node-gyp clean
+
+run-tests:
+ @./node_modules/.bin/mocha \
+ -t 2000 \
+ $(TESTFLAGS) \
+ $(TESTS)
+
+run-integrationtests:
+ @./node_modules/.bin/mocha \
+ -t 5000 \
+ $(TESTFLAGS) \
+ $(TESTS)
+
+test:
+ @$(MAKE) NODE_TLS_REJECT_UNAUTHORIZED=0 NODE_PATH=lib TESTS="$(ALL_TESTS)" run-tests
+
+integrationtest:
+ @$(MAKE) NODE_TLS_REJECT_UNAUTHORIZED=0 NODE_PATH=lib TESTS="$(ALL_INTEGRATION)" run-integrationtests
+
+benchmark:
+ @node bench/sender.benchmark.js
+ @node bench/parser.benchmark.js
+
+autobahn:
+ @NODE_PATH=lib node test/autobahn.js
+
+autobahn-server:
+ @NODE_PATH=lib node test/autobahn-server.js
+
+.PHONY: test
diff --git a/node_modules/ws/README.md b/node_modules/ws/README.md
new file mode 100644
index 0000000..7eb1a47
--- /dev/null
+++ b/node_modules/ws/README.md
@@ -0,0 +1,159 @@
+[![Build Status](https://secure.travis-ci.org/einaros/ws.png)](http://travis-ci.org/einaros/ws)
+
+# ws: a node.js websocket library #
+
+`ws` is a simple to use websocket implementation, up-to-date against RFC-6455, and [probably the fastest WebSocket library for node.js](http://hobbycoding.posterous.com/the-fastest-websocket-module-for-nodejs).
+
+Passes the quite extensive Autobahn test suite. See http://einaros.github.com/ws for the full reports.
+
+Comes with a command line utility, `wscat`, which can either act as a server (--listen), or client (--connect); Use it to debug simple websocket services.
+
+## 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`, or argument `-p 8` for wscat)
+* **HyBi drafts 13-17** (Current default, alternatively option `protocolVersion: 13`, or argument `-p 13` for wscat)
+
+_See the echo.websocket.org example below for how to use the `protocolVersion` option._
+
+## Usage ##
+
+### Installing ###
+
+`npm install ws`
+
+### Sending and receiving text data ###
+
+```js
+var WebSocket = require('ws');
+var ws = new WebSocket('ws://www.host.com/path');
+ws.on('open', function() {
+ 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() {
+ 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(ws) {
+ ws.on('message', function(message) {
+ console.log('received: %s', message);
+ });
+ ws.send('something');
+});
+```
+
+### 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(error) {
+ // if error is null, 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() {
+ console.log('connected');
+ ws.send(Date.now().toString(), {mask: true});
+});
+ws.on('close', function() {
+ console.log('disconnected');
+});
+ws.on('message', function(data, flags) {
+ console.log('Roundtrip time: ' + (Date.now() - parseInt(data)) + 'ms', flags);
+ setTimeout(function() {
+ ws.send(Date.now().toString(), {mask: true});
+ }, 500);
+});
+```
+
+### wscat against echo.websocket.org ###
+
+ $ npm install -g ws
+ $ wscat -c ws://echo.websocket.org -p 8
+ connected (press CTRL+C to quit)
+ > hi there
+ < hi there
+ > are you a happy parrot?
+ < are you a happy parrot?
+
+### 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 the doc/ directory for Node.js-like docs for the ws classes.
+
+## 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.
diff --git a/node_modules/ws/bench/parser.benchmark.js b/node_modules/ws/bench/parser.benchmark.js
new file mode 100644
index 0000000..ff5f737
--- /dev/null
+++ b/node_modules/ws/bench/parser.benchmark.js
@@ -0,0 +1,115 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
+ * MIT Licensed
+ */
+
+/**
+ * Benchmark dependencies.
+ */
+
+var benchmark = require('benchmark')
+ , Receiver = require('../').Receiver
+ , suite = new benchmark.Suite('Receiver');
+require('tinycolor');
+require('./util');
+
+/**
+ * Setup receiver.
+ */
+
+suite.on('start', function () {
+ receiver = new Receiver();
+});
+
+suite.on('cycle', function () {
+ receiver = new Receiver();
+});
+
+/**
+ * Benchmarks.
+ */
+
+var pingMessage = 'Hello'
+ , pingPacket1 = getBufferFromHexString('89 ' + (pack(2, 0x80 | pingMessage.length)) +
+ ' 34 83 a8 68 '+ getHexStringFromBuffer(mask(pingMessage, '34 83 a8 68')));
+suite.add('ping message', function () {
+ receiver.add(pingPacket1);
+});
+
+var pingPacket2 = getBufferFromHexString('89 00')
+suite.add('ping with no data', function () {
+ receiver.add(pingPacket2);
+});
+
+var closePacket = getBufferFromHexString('88 00');
+suite.add('close message', function () {
+ receiver.add(closePacket);
+ receiver.endPacket();
+});
+
+var maskedTextPacket = getBufferFromHexString('81 93 34 83 a8 68 01 b9 92 52 4f a1 c6 09 59 e6 8a 52 16 e6 cb 00 5b a1 d5');
+suite.add('masked text message', function () {
+ receiver.add(maskedTextPacket);
+});
+
+binaryDataPacket = (function() {
+ var length = 125
+ , message = new Buffer(length)
+ for (var i = 0; i < length; ++i) message[i] = i % 10;
+ return getBufferFromHexString('82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 '
+ + getHexStringFromBuffer(mask(message), '34 83 a8 68'));
+})();
+suite.add('binary data (125 bytes)', function () {
+ try {
+ receiver.add(binaryDataPacket);
+
+ }
+ catch(e) {console.log(e)}
+});
+
+binaryDataPacket2 = (function() {
+ var length = 65535
+ , message = new Buffer(length)
+ for (var i = 0; i < length; ++i) message[i] = i % 10;
+ return getBufferFromHexString('82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 '
+ + getHexStringFromBuffer(mask(message), '34 83 a8 68'));
+})();
+suite.add('binary data (65535 bytes)', function () {
+ receiver.add(binaryDataPacket2);
+});
+
+binaryDataPacket3 = (function() {
+ var length = 200*1024
+ , message = new Buffer(length)
+ for (var i = 0; i < length; ++i) message[i] = i % 10;
+ return getBufferFromHexString('82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 '
+ + getHexStringFromBuffer(mask(message), '34 83 a8 68'));
+})();
+suite.add('binary data (200 kB)', function () {
+ receiver.add(binaryDataPacket3);
+});
+
+/**
+ * Output progress.
+ */
+
+suite.on('cycle', function (bench, details) {
+ console.log('\n ' + suite.name.grey, details.name.white.bold);
+ console.log(' ' + [
+ details.hz.toFixed(2).cyan + ' ops/sec'.grey
+ , details.count.toString().white + ' times executed'.grey
+ , 'benchmark took '.grey + details.times.elapsed.toString().white + ' sec.'.grey
+ ,
+ ].join(', '.grey));
+});
+
+/**
+ * Run/export benchmarks.
+ */
+
+if (!module.parent) {
+ suite.run();
+} else {
+ module.exports = suite;
+}
diff --git a/node_modules/ws/bench/sender.benchmark.js b/node_modules/ws/bench/sender.benchmark.js
new file mode 100644
index 0000000..20c171a
--- /dev/null
+++ b/node_modules/ws/bench/sender.benchmark.js
@@ -0,0 +1,66 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
+ * MIT Licensed
+ */
+
+/**
+ * Benchmark dependencies.
+ */
+
+var benchmark = require('benchmark')
+ , Sender = require('../').Sender
+ , suite = new benchmark.Suite('Sender');
+require('tinycolor');
+require('./util');
+
+/**
+ * Setup sender.
+ */
+
+suite.on('start', function () {
+ sender = new Sender();
+ sender._socket = { write: function() {} };
+});
+
+suite.on('cycle', function () {
+ sender = new Sender();
+ sender._socket = { write: function() {} };
+});
+
+/**
+ * Benchmarks
+ */
+
+framePacket = new Buffer(200*1024);
+framePacket.fill(99);
+suite.add('frameAndSend, unmasked (200 kB)', function () {
+ sender.frameAndSend(0x2, framePacket, true, false);
+});
+suite.add('frameAndSend, masked (200 kB)', function () {
+ sender.frameAndSend(0x2, framePacket, true, true);
+});
+
+/**
+ * Output progress.
+ */
+
+suite.on('cycle', function (bench, details) {
+ console.log('\n ' + suite.name.grey, details.name.white.bold);
+ console.log(' ' + [
+ details.hz.toFixed(2).cyan + ' ops/sec'.grey
+ , details.count.toString().white + ' times executed'.grey
+ , 'benchmark took '.grey + details.times.elapsed.toString().white + ' sec.'.grey
+ ,
+ ].join(', '.grey));
+});
+
+/**
+ * Run/export benchmarks.
+ */
+
+if (!module.parent) {
+ suite.run();
+} else {
+ module.exports = suite;
+}
diff --git a/node_modules/ws/bench/speed.js b/node_modules/ws/bench/speed.js
new file mode 100644
index 0000000..3ce6414
--- /dev/null
+++ b/node_modules/ws/bench/speed.js
@@ -0,0 +1,105 @@
+var cluster = require('cluster')
+ , WebSocket = require('../')
+ , WebSocketServer = WebSocket.Server
+ , crypto = require('crypto')
+ , util = require('util')
+ , ansi = require('ansi');
+require('tinycolor');
+
+function roundPrec(num, prec) {
+ var mul = Math.pow(10, prec);
+ return Math.round(num * mul) / mul;
+}
+
+function humanSize(bytes) {
+ if (bytes >= 1048576) return roundPrec(bytes / 1048576, 2) + ' MB';
+ if (bytes >= 1024) return roundPrec(bytes / 1024, 2) + ' kB';
+ return roundPrec(bytes, 2) + ' B';
+}
+
+function generateRandomData(size) {
+ var buffer = new Buffer(size);
+ for (var i = 0; i < size; ++i) {
+ buffer[i] = ~~(Math.random() * 127);
+ }
+ return buffer;
+}
+
+if (cluster.isMaster) {
+ var wss = new WebSocketServer({port: 8181}, function() {
+ cluster.fork();
+ });
+ wss.on('connection', function(ws) {
+ ws.on('message', function(data, flags) {
+ ws.send(data, {binary: flags&&flags.binary});
+ });
+ ws.on('close', function() {});
+ });
+ cluster.on('death', function(worker) {
+ wss.close();
+ });
+}
+else {
+ var cursor = ansi(process.stdout);
+
+ var configs = [
+ [true, 10000, 64],
+ [true, 5000, 16*1024],
+ [true, 1000, 128*1024],
+ [true, 100, 1024*1024],
+ [true, 1, 500*1024*1024],
+ [false, 10000, 64],
+ [false, 5000, 16*1024],
+ [false, 1000, 128*1024],
+ [false, 100, 1024*1024],
+ ];
+
+ var largest = configs[0][1];
+ for (var i = 0, l = configs.length; i < l; ++i) {
+ if (configs[i][2] > largest) largest = configs[i][2];
+ }
+
+ console.log('Generating %s of test data ...', humanSize(largest));
+ var randomBytes = generateRandomData(largest);
+
+ function roundtrip(useBinary, roundtrips, size, cb) {
+ var data = randomBytes.slice(0, size);
+ var prefix = util.format('Running %d roundtrips of %s %s data', roundtrips, humanSize(size), useBinary ? 'binary' : 'text');
+ console.log(prefix);
+ var client = new WebSocket('ws://localhost:' + '8181');
+ var dt;
+ var roundtrip = 0;
+ function send() {
+ client.send(data, {binary: useBinary});
+ }
+ client.on('error', function(e) {
+ console.error(e);
+ process.exit();
+ });
+ client.on('open', function() {
+ dt = Date.now();
+ send();
+ });
+ client.on('message', function(data, flags) {
+ if (++roundtrip == roundtrips) {
+ var elapsed = Date.now() - dt;
+ cursor.up();
+ console.log('%s:\t%ss\t%s'
+ , useBinary ? prefix.green : prefix.cyan
+ , roundPrec(elapsed / 1000, 1).toString().green.bold
+ , (humanSize((size * roundtrips) / elapsed * 1000) + '/s').blue.bold);
+ client.close();
+ cb();
+ return;
+ }
+ process.nextTick(send);
+ });
+ }
+
+ (function run() {
+ if (configs.length == 0) process.exit();
+ var config = configs.shift();
+ config.push(run);
+ roundtrip.apply(null, config);
+ })();
+} \ No newline at end of file
diff --git a/node_modules/ws/bench/util.js b/node_modules/ws/bench/util.js
new file mode 100644
index 0000000..5f01281
--- /dev/null
+++ b/node_modules/ws/bench/util.js
@@ -0,0 +1,105 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
+ * MIT Licensed
+ */
+
+/**
+ * Returns a Buffer from a "ff 00 ff"-type hex string.
+ */
+
+getBufferFromHexString = function(byteStr) {
+ var bytes = byteStr.split(' ');
+ var buf = new Buffer(bytes.length);
+ for (var i = 0; i < bytes.length; ++i) {
+ buf[i] = parseInt(bytes[i], 16);
+ }
+ return buf;
+}
+
+/**
+ * Returns a hex string from a Buffer.
+ */
+
+getHexStringFromBuffer = function(data) {
+ var s = '';
+ for (var i = 0; i < data.length; ++i) {
+ s += padl(data[i].toString(16), 2, '0') + ' ';
+ }
+ return s.trim();
+}
+
+/**
+ * Splits a buffer in two parts.
+ */
+
+splitBuffer = function(buffer) {
+ var b1 = new Buffer(Math.ceil(buffer.length / 2));
+ buffer.copy(b1, 0, 0, b1.length);
+ var b2 = new Buffer(Math.floor(buffer.length / 2));
+ buffer.copy(b2, 0, b1.length, b1.length + b2.length);
+ return [b1, b2];
+}
+
+/**
+ * Performs hybi07+ type masking on a hex string or buffer.
+ */
+
+mask = function(buf, maskString) {
+ if (typeof buf == 'string') buf = new Buffer(buf);
+ var mask = getBufferFromHexString(maskString || '34 83 a8 68');
+ for (var i = 0; i < buf.length; ++i) {
+ buf[i] ^= mask[i % 4];
+ }
+ return buf;
+}
+
+/**
+ * Returns a hex string representing the length of a message
+ */
+
+getHybiLengthAsHexString = function(len, masked) {
+ if (len < 126) {
+ var buf = new Buffer(1);
+ buf[0] = (masked ? 0x80 : 0) | len;
+ }
+ else if (len < 65536) {
+ var buf = new Buffer(3);
+ buf[0] = (masked ? 0x80 : 0) | 126;
+ getBufferFromHexString(pack(4, len)).copy(buf, 1);
+ }
+ else {
+ var buf = new Buffer(9);
+ buf[0] = (masked ? 0x80 : 0) | 127;
+ getBufferFromHexString(pack(16, len)).copy(buf, 1);
+ }
+ return getHexStringFromBuffer(buf);
+}
+
+/**
+ * Unpacks a Buffer into a number.
+ */
+
+unpack = function(buffer) {
+ var n = 0;
+ for (var i = 0; i < buffer.length; ++i) {
+ n = (i == 0) ? buffer[i] : (n * 256) + buffer[i];
+ }
+ return n;
+}
+
+/**
+ * Returns a hex string, representing a specific byte count 'length', from a number.
+ */
+
+pack = function(length, number) {
+ return padl(number.toString(16), length, '0').replace(/([0-9a-f][0-9a-f])/gi, '$1 ').trim();
+}
+
+/**
+ * Left pads the string 's' to a total length of 'n' with char 'c'.
+ */
+
+padl = function(s, n, c) {
+ return new Array(1 + n - s.length).join(c) + s;
+}
diff --git a/node_modules/ws/bin/wscat b/node_modules/ws/bin/wscat
new file mode 100644
index 0000000..0ec894d
--- /dev/null
+++ b/node_modules/ws/bin/wscat
@@ -0,0 +1,190 @@
+#!/usr/bin/env node
+
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module dependencies.
+ */
+
+var WebSocket = require('../')
+ , fs = require('fs')
+ , program = require('commander')
+ , util = require('util')
+ , events = require('events')
+ , readline = require('readline');
+
+/**
+ * InputReader - processes console input
+ */
+
+function Console() {
+ this.stdin = process.stdin;
+ this.stdout = process.stdout;
+
+ this.readlineInterface = readline.createInterface(this.stdin, this.stdout);
+
+ var self = this;
+ this.readlineInterface.on('line', function(data) {
+ self.emit('line', data);
+ });
+ this.readlineInterface.on('close', function() {
+ self.emit('close');
+ });
+
+ this._resetInput = function() {
+ self.clear();
+ }
+}
+util.inherits(Console, events.EventEmitter);
+
+Console.Colors = {
+ Red: '\033[31m',
+ Green: '\033[32m',
+ Yellow: '\033[33m',
+ Blue: '\033[34m',
+ Default: '\033[39m'
+};
+
+Console.prototype.prompt = function() {
+ this.readlineInterface.prompt();
+}
+
+Console.prototype.print = function(msg, color) {
+ this.clear();
+ color = color || Console.Colors.Default;
+ this.stdout.write(color + msg + Console.Colors.Default + '\n');
+ this.prompt();
+}
+
+Console.prototype.clear = function() {
+ this.stdout.write('\033[2K\033[E');
+}
+
+Console.prototype.pause = function() {
+ this.stdin.on('keypress', this._resetInput);
+}
+
+Console.prototype.resume = function() {
+ this.stdin.removeListener('keypress', this._resetInput);
+}
+
+/**
+ * The actual application
+ */
+
+var version = JSON.parse(fs.readFileSync(__dirname + '/../package.json', 'utf8')).version;
+program
+ .version(version)
+ .usage('[options] <url>')
+ .option('-l, --listen <port>', 'listen on port')
+ .option('-c, --connect <url>', 'connect to a websocket server')
+ .option('-p, --protocol <version>', 'optional protocol version')
+ .option('-o, --origin <origin>', 'optional origin')
+ .option('--host <host>', 'optional host')
+ .option('-s, --subprotocol <protocol>', 'optional subprotocol')
+ .parse(process.argv);
+
+if (program.listen && program.connect) {
+ console.error('\033[33merror: use either --listen or --connect\033[39m');
+ process.exit(-1);
+}
+else if (program.listen) {
+ var wsConsole = new Console();
+ wsConsole.pause();
+ var options = {};
+ if (program.protocol) options.protocolVersion = program.protocol;
+ if (program.origin) options.origin = program.origin;
+ if (program.subprotocol) options.protocol = program.subprotocol;
+ var ws = null;
+ var wss = new WebSocket.Server({port: program.listen}, function() {
+ wsConsole.print('listening on port ' + program.listen + ' (press CTRL+C to quit)', Console.Colors.Green);
+ wsConsole.clear();
+ });
+ wsConsole.on('close', function() {
+ if (ws) {
+ try {
+ ws.close();
+ }
+ catch (e) {}
+ }
+ process.exit(0);
+ });
+ wsConsole.on('line', function(data) {
+ if (ws) {
+ ws.send(data, {mask: false});
+ wsConsole.prompt();
+ }
+ });
+ wss.on('connection', function(newClient) {
+ if (ws) {
+ // limit to one client
+ newClient.terminate();
+ return;
+ };
+ ws = newClient;
+ wsConsole.resume();
+ wsConsole.prompt();
+ wsConsole.print('client connected', Console.Colors.Green);
+ ws.on('close', function() {
+ wsConsole.print('disconnected', Console.Colors.Green);
+ wsConsole.clear();
+ wsConsole.pause();
+ ws = null;
+ });
+ ws.on('error', function(code, description) {
+ wsConsole.print('error: ' + code + (description ? ' ' + description : ''), Console.Colors.Yellow);
+ });
+ ws.on('message', function(data, flags) {
+ wsConsole.print('< ' + data, Console.Colors.Blue);
+ });
+ });
+ wss.on('error', function(error) {
+ wsConsole.print('error: ' + error.toString(), Console.Colors.Yellow);
+ process.exit(-1);
+ });
+}
+else if (program.connect) {
+ var wsConsole = new Console();
+ var options = {};
+ if (program.protocol) options.protocolVersion = program.protocol;
+ if (program.origin) options.origin = program.origin;
+ if (program.subprotocol) options.protocol = program.subprotocol;
+ if (program.host) options.host = program.host;
+ var ws = new WebSocket(program.connect, options);
+ ws.on('open', function() {
+ wsConsole.print('connected (press CTRL+C to quit)', Console.Colors.Green);
+ wsConsole.on('line', function(data) {
+ ws.send(data, {mask: true});
+ wsConsole.prompt();
+ });
+ });
+ ws.on('close', function() {
+ wsConsole.print('disconnected', Console.Colors.Green);
+ wsConsole.clear();
+ process.exit();
+ });
+ ws.on('error', function(code, description) {
+ wsConsole.print('error: ' + code + (description ? ' ' + description : ''), Console.Colors.Yellow);
+ process.exit(-1);
+ });
+ ws.on('message', function(data, flags) {
+ wsConsole.print('< ' + data, Console.Colors.Blue);
+ });
+ wsConsole.on('close', function() {
+ if (ws) {
+ try {
+ ws.close();
+ }
+ catch(e) {}
+ process.exit();
+ }
+ });
+}
+else {
+ console.error('\033[33merror: use either --listen or --connect\033[39m');
+ process.exit(-1);
+}
diff --git a/node_modules/ws/binding.gyp b/node_modules/ws/binding.gyp
new file mode 100644
index 0000000..a83d279
--- /dev/null
+++ b/node_modules/ws/binding.gyp
@@ -0,0 +1,14 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'validation',
+ 'cflags': [ '-O3' ],
+ 'sources': [ 'src/validation.cc' ]
+ },
+ {
+ 'target_name': 'bufferutil',
+ 'cflags': [ '-O3' ],
+ 'sources': [ 'src/bufferutil.cc' ]
+ }
+ ]
+}
diff --git a/node_modules/ws/builderror.log b/node_modules/ws/builderror.log
new file mode 100644
index 0000000..a3b0a0c
--- /dev/null
+++ b/node_modules/ws/builderror.log
@@ -0,0 +1,11 @@
+gyp ERR! configure error
+gyp ERR! stack Error: Can't find Python executable "python", you can set the PYTHON env variable.
+gyp ERR! stack at failNoPython (C:\Program Files\nodejs\node_modules\npm\node_modules\node-gyp\lib\configure.js:118:14)
+gyp ERR! stack at C:\Program Files\nodejs\node_modules\npm\node_modules\node-gyp\lib\configure.js:81:11
+gyp ERR! stack at Object.oncomplete (fs.js:107:15)
+gyp ERR! System Windows_NT 6.1.7600
+gyp ERR! command "node" "C:\\Program Files\\nodejs\\node_modules\\npm\\node_modules\\node-gyp\\bin\\node-gyp.js" "rebuild"
+gyp ERR! cwd C:\Users\Administrator\Downloads\webRTCCopy-master\server\node_modules\ws
+gyp ERR! node -v v0.10.15
+gyp ERR! node-gyp -v v0.10.6
+gyp ERR! not ok
diff --git a/node_modules/ws/doc/ws.md b/node_modules/ws/doc/ws.md
new file mode 100644
index 0000000..c7c6fcb
--- /dev/null
+++ b/node_modules/ws/doc/ws.md
@@ -0,0 +1,179 @@
+# ws
+
+## Class: ws.Server
+
+This class is a WebSocket server. It is an `EventEmitter`.
+
+### new ws.Server([options], [callback])
+
+* `options` Object
+ * `host` String
+ * `port` Number
+ * `server` http.Server
+ * `verifyClient` Function
+ * `path` String
+ * `noServer` Boolean
+ * `disableHixie` Boolean
+ * `clientTracking` Boolean
+* `callback` Function
+
+Construct a new server object.
+
+Either `port` or `server` must be provided, otherwise you might enable
+`noServer` if you want to pass the requests directly. Please note that the
+`callback` is only used when you supply the a `port` number in the options.
+
+### server.close([code], [data])
+
+Close the server and terminate all clients
+
+### server.handleUpgrade(request, socket, upgradeHead, callback)
+
+Handles a HTTP Upgrade request. `request` is an instance of `http.ServerRequest`, `socket` is an instance of `net.Socket`.
+
+When the Upgrade was successfully, the `callback` will be called with a `ws.WebSocket` object as parameter.
+
+### Event: 'error'
+
+`function (error) { }`
+
+If the underlying server emits an error, it will be forwarded here.
+
+### Event: 'headers'
+
+`function (headers) { }`
+
+Emitted with the object of HTTP headers that are going to be written to the `Stream` as part of the handshake.
+
+### Event: 'connection'
+
+`function (socket) { }`
+
+When a new WebSocket connection is established. `socket` is an object of type `ws.WebSocket`.
+
+
+## Class: ws.WebSocket
+
+This class represents a WebSocket connection. It is an `EventEmitter`.
+
+### new ws.WebSocket(address, [options])
+
+* `address` String|Array
+* `options` Object
+ * `protocol` String
+ * `protocolVersion` Number|String
+ -- the following only apply if `address` is a String
+ * `host` String
+ * `origin` String
+ * `pfx` String|Buffer
+ * `key` String|Buffer
+ * `passphrase` String
+ * `cert` String|Buffer
+ * `ca` Array
+ * `ciphers` String
+ * `rejectUnauthorized` Boolean
+
+Instantiating with an `address` creates a new WebSocket client object. If `address` is an Array (request, socket, rest), it is instantiated as a Server client (e.g. called from the `ws.Server`).
+
+### websocket.bytesReceived
+
+Received bytes count.
+
+### websocket.readyState
+
+Possible states are `WebSocket.CONNECTING`, `WebSocket.OPEN`, `WebSocket.CLOSING`, `WebSocket.CLOSED`.
+
+### websocket.protocolVersion
+
+The WebSocket protocol version used for this connection, `8`, `13` or `hixie-76` (the latter only for server clients).
+
+### websocket.url
+
+The URL of the WebSocket server (only for clients)
+
+### websocket.supports
+
+Describes the feature of the used protocol version. E.g. `supports.binary` is a boolean that describes if the connection supports binary messages.
+
+### websocket.close([code], [data])
+
+Gracefully closes the connection, after sending a description message
+
+### websocket.pause()
+
+Pause the client stream
+
+### websocket.ping([data], [options], [dontFailWhenClosed])
+
+Sends a ping. `data` is sent, `options` is an object with members `mask` and `binary`. `dontFailWhenClosed` indicates whether or not to throw if the connection isnt open.
+
+### websocket.pong([data], [options], [dontFailWhenClosed])
+
+Sends a pong. `data` is sent, `options` is an object with members `mask` and `binary`. `dontFailWhenClosed` indicates whether or not to throw if the connection isnt open.
+
+
+### websocket.resume()
+
+Resume the client stream
+
+### websocket.send(data, [options], [callback])
+
+Sends `data` through the connection. `options` can be an object with members `mask` and `binary`. The optional `callback` is executed after the send completes.
+
+### websocket.stream([options], callback)
+
+Streams data through calls to a user supplied function. `options` can be an object with members `mask` and `binary`. `callback` is executed on successive ticks of which send is `function (data, final)`.
+
+### websocket.terminate()
+
+Immediately shuts down the connection
+
+### websocket.onopen
+### websocket.onerror
+### websocket.onclose
+### websocket.onmessage
+
+Emulates the W3C Browser based WebSocket interface using function members.
+
+### websocket.addEventListener(method, listener)
+
+Emulates the W3C Browser based WebSocket interface using addEventListener.
+
+### Event: 'error'
+
+`function (error) { }`
+
+If the client emits an error, this event is emitted (errors from the underlying `net.Socket` are forwarded here).
+
+### Event: 'close'
+
+`function (code, message) { }`
+
+Is emitted when the connection is closed. `code` is defined in the WebSocket specification.
+
+The `close` event is also emitted when then underlying `net.Socket` closes the connection (`end` or `close`).
+
+### Event: 'message'
+
+`function (data, flags) { }`
+
+Is emitted when data is received. `flags` is an object with member `binary`.
+
+### Event: 'ping'
+
+`function (data, flags) { }`
+
+Is emitted when a ping is received. `flags` is an object with member `binary`.
+
+### Event: 'pong'
+
+`function (data, flags) { }`
+
+Is emitted when a pong is received. `flags` is an object with member `binary`.
+
+### Event: 'open'
+
+`function () { }`
+
+Emitted when the connection is established.
+
diff --git a/node_modules/ws/examples/fileapi/.npmignore b/node_modules/ws/examples/fileapi/.npmignore
new file mode 100644
index 0000000..dcd5756
--- /dev/null
+++ b/node_modules/ws/examples/fileapi/.npmignore
@@ -0,0 +1 @@
+uploaded
diff --git a/node_modules/ws/examples/fileapi/package.json b/node_modules/ws/examples/fileapi/package.json
new file mode 100644
index 0000000..7816f27
--- /dev/null
+++ b/node_modules/ws/examples/fileapi/package.json
@@ -0,0 +1,18 @@
+{
+ "author": "",
+ "name": "fileapi",
+ "version": "0.0.0",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/einaros/ws.git"
+ },
+ "engines": {
+ "node": "~0.6.8"
+ },
+ "dependencies": {
+ "express": "latest",
+ "ansi": "https://github.com/einaros/ansi.js/tarball/master"
+ },
+ "devDependencies": {},
+ "optionalDependencies": {}
+}
diff --git a/node_modules/ws/examples/fileapi/public/app.js b/node_modules/ws/examples/fileapi/public/app.js
new file mode 100644
index 0000000..e812cc3
--- /dev/null
+++ b/node_modules/ws/examples/fileapi/public/app.js
@@ -0,0 +1,39 @@
+function onFilesSelected(e) {
+ var button = e.srcElement;
+ button.disabled = true;
+ var progress = document.querySelector('div#progress');
+ progress.innerHTML = '0%';
+ var files = e.target.files;
+ var totalFiles = files.length;
+ var filesSent = 0;
+ if (totalFiles) {
+ var uploader = new Uploader('ws://localhost:8080', function () {
+ Array.prototype.slice.call(files, 0).forEach(function(file) {
+ if (file.name == '.') {
+ --totalFiles;
+ return;
+ }
+ uploader.sendFile(file, function(error) {
+ if (error) {
+ console.log(error);
+ return;
+ }
+ ++filesSent;
+ progress.innerHTML = ~~(filesSent / totalFiles * 100) + '%';
+ console.log('Sent: ' + file.name);
+ });
+ });
+ });
+ }
+ uploader.ondone = function() {
+ uploader.close();
+ progress.innerHTML = '100% done, ' + totalFiles + ' files sent.';
+ }
+}
+
+window.onload = function() {
+ var importButtons = document.querySelectorAll('[type="file"]');
+ Array.prototype.slice.call(importButtons, 0).forEach(function(importButton) {
+ importButton.addEventListener('change', onFilesSelected, false);
+ });
+}
diff --git a/node_modules/ws/examples/fileapi/public/index.html b/node_modules/ws/examples/fileapi/public/index.html
new file mode 100644
index 0000000..0d463dd
--- /dev/null
+++ b/node_modules/ws/examples/fileapi/public/index.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <style>
+ body {
+ font-family: Tahoma, Geneva, sans-serif;
+ }
+ div {
+ display: inline;
+ }
+ </style>
+ <script src='uploader.js'></script>
+ <script src='app.js'></script>
+ </head>
+ <body>
+ <p>This example will upload an entire directory tree to the node.js server via a fast and persistent WebSocket connection.</p>
+ <p>Note that the example is Chrome only for now.</p>
+ <input type="file" webkitdirectory /><br/><br/>
+ Upload status:
+ <div id='progress'>Please select a directory to upload.</div>
+ </body>
+</html>
diff --git a/node_modules/ws/examples/fileapi/public/uploader.js b/node_modules/ws/examples/fileapi/public/uploader.js
new file mode 100644
index 0000000..0c34a7f
--- /dev/null
+++ b/node_modules/ws/examples/fileapi/public/uploader.js
@@ -0,0 +1,55 @@
+function Uploader(url, cb) {
+ this.ws = new WebSocket(url);
+ if (cb) this.ws.onopen = cb;
+ this.sendQueue = [];
+ this.sending = null;
+ this.sendCallback = null;
+ this.ondone = null;
+ var self = this;
+ this.ws.onmessage = function(event) {
+ var data = JSON.parse(event.data);
+ if (data.event == 'complete') {
+ if (data.path != self.sending.path) {
+ self.sendQueue = [];
+ self.sending = null;
+ self.sendCallback = null;
+ throw new Error('Got message for wrong file!');
+ }
+ self.sending = null;
+ var callback = self.sendCallback;
+ self.sendCallback = null;
+ if (callback) callback();
+ if (self.sendQueue.length === 0 && self.ondone) self.ondone(null);
+ if (self.sendQueue.length > 0) {
+ var args = self.sendQueue.pop();
+ setTimeout(function() { self.sendFile.apply(self, args); }, 0);
+ }
+ }
+ else if (data.event == 'error') {
+ self.sendQueue = [];
+ self.sending = null;
+ var callback = self.sendCallback;
+ self.sendCallback = null;
+ var error = new Error('Server reported send error for file ' + data.path);
+ if (callback) callback(error);
+ if (self.ondone) self.ondone(error);
+ }
+ }
+}
+
+Uploader.prototype.sendFile = function(file, cb) {
+ if (this.ws.readyState != WebSocket.OPEN) throw new Error('Not connected');
+ if (this.sending) {
+ this.sendQueue.push(arguments);
+ return;
+ }
+ var fileData = { name: file.name, path: file.webkitRelativePath };
+ this.sending = fileData;
+ this.sendCallback = cb;
+ this.ws.send(JSON.stringify(fileData));
+ this.ws.send(file);
+}
+
+Uploader.prototype.close = function() {
+ this.ws.close();
+}
diff --git a/node_modules/ws/examples/fileapi/server.js b/node_modules/ws/examples/fileapi/server.js
new file mode 100644
index 0000000..badfeba
--- /dev/null
+++ b/node_modules/ws/examples/fileapi/server.js
@@ -0,0 +1,103 @@
+var WebSocketServer = require('../../').Server
+ , express = require('express')
+ , fs = require('fs')
+ , http = require('http')
+ , util = require('util')
+ , path = require('path')
+ , app = express.createServer()
+ , events = require('events')
+ , ansi = require('ansi')
+ , cursor = ansi(process.stdout);
+
+function BandwidthSampler(ws, interval) {
+ interval = interval || 2000;
+ var previousByteCount = 0;
+ var self = this;
+ var intervalId = setInterval(function() {
+ var byteCount = ws.bytesReceived;
+ var bytesPerSec = (byteCount - previousByteCount) / (interval / 1000);
+ previousByteCount = byteCount;
+ self.emit('sample', bytesPerSec);
+ }, interval);
+ ws.on('close', function() {
+ clearInterval(intervalId);
+ });
+}
+util.inherits(BandwidthSampler, events.EventEmitter);
+
+function makePathForFile(filePath, prefix, cb) {
+ if (typeof cb !== 'function') throw new Error('callback is required');
+ filePath = path.dirname(path.normalize(filePath)).replace(/^(\/|\\)+/, '');
+ var pieces = filePath.split(/(\\|\/)/);
+ var incrementalPath = prefix;
+ function step(error) {
+ if (error) return cb(error);
+ if (pieces.length == 0) return cb(null, incrementalPath);
+ incrementalPath += '/' + pieces.shift();
+ fs.exists(incrementalPath, function(exists) {
+ if (!exists) fs.mkdir(incrementalPath, step);
+ else process.nextTick(step);
+ });
+ }
+ step();
+}
+
+cursor.eraseData(2).goto(1, 1);
+app.use(express.static(__dirname + '/public'));
+
+var clientId = 0;
+var wss = new WebSocketServer({server: app});
+wss.on('connection', function(ws) {
+ var thisId = ++clientId;
+ cursor.goto(1, 4 + thisId).eraseLine();
+ console.log('Client #%d connected', thisId);
+
+ var sampler = new BandwidthSampler(ws);
+ sampler.on('sample', function(bps) {
+ cursor.goto(1, 4 + thisId).eraseLine();
+ console.log('WebSocket #%d incoming bandwidth: %d MB/s', thisId, Math.round(bps / (1024*1024)));
+ });
+
+ var filesReceived = 0;
+ var currentFile = null;
+ ws.on('message', function(data, flags) {
+ if (!flags.binary) {
+ currentFile = JSON.parse(data);
+ // note: a real-world app would want to sanity check the data
+ }
+ else {
+ if (currentFile == null) return;
+ makePathForFile(currentFile.path, __dirname + '/uploaded', function(error, path) {
+ if (error) {
+ console.log(error);
+ ws.send(JSON.stringify({event: 'error', path: currentFile.path, message: error.message}));
+ return;
+ }
+ fs.writeFile(path + '/' + currentFile.name, data, function(error) {
+ ++filesReceived;
+ // console.log('received %d bytes long file, %s', data.length, currentFile.path);
+ ws.send(JSON.stringify({event: 'complete', path: currentFile.path}));
+ currentFile = null;
+ });
+ });
+ }
+ });
+
+ ws.on('close', function() {
+ cursor.goto(1, 4 + thisId).eraseLine();
+ console.log('Client #%d disconnected. %d files received.', thisId, filesReceived);
+ });
+
+ ws.on('error', function(e) {
+ cursor.goto(1, 4 + thisId).eraseLine();
+ console.log('Client #%d error: %s', thisId, e.message);
+ });
+});
+
+fs.mkdir(__dirname + '/uploaded', function(error) {
+ // ignore errors, most likely means directory exists
+ console.log('Uploaded files will be saved to %s/uploaded.', __dirname);
+ console.log('Remember to wipe this directory if you upload lots and lots.');
+ app.listen(8080);
+ console.log('Listening on http://localhost:8080');
+});
diff --git a/node_modules/ws/examples/serverstats-express_3/package.json b/node_modules/ws/examples/serverstats-express_3/package.json
new file mode 100644
index 0000000..99722c4
--- /dev/null
+++ b/node_modules/ws/examples/serverstats-express_3/package.json
@@ -0,0 +1,17 @@
+{
+ "author": "",
+ "name": "serverstats",
+ "version": "0.0.0",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/einaros/ws.git"
+ },
+ "engines": {
+ "node": ">0.4.0"
+ },
+ "dependencies": {
+ "express": "~3.0.0"
+ },
+ "devDependencies": {},
+ "optionalDependencies": {}
+}
diff --git a/node_modules/ws/examples/serverstats-express_3/public/index.html b/node_modules/ws/examples/serverstats-express_3/public/index.html
new file mode 100644
index 0000000..24d84e1
--- /dev/null
+++ b/node_modules/ws/examples/serverstats-express_3/public/index.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <style>
+ body {
+ font-family: Tahoma, Geneva, sans-serif;
+ }
+
+ div {
+ display: inline;
+ }
+ </style>
+ <script>
+ function updateStats(memuse) {
+ document.getElementById('rss').innerHTML = memuse.rss;
+ document.getElementById('heapTotal').innerHTML = memuse.heapTotal;
+ document.getElementById('heapUsed').innerHTML = memuse.heapUsed;
+ }
+
+ var host = window.document.location.host.replace(/:.*/, '');
+ var ws = new WebSocket('ws://' + host + ':8080');
+ ws.onmessage = function (event) {
+ updateStats(JSON.parse(event.data));
+ };
+ </script>
+ </head>
+ <body>
+ <strong>Server Stats</strong><br>
+ RSS: <div id='rss'></div><br>
+ Heap total: <div id='heapTotal'></div><br>
+ Heap used: <div id='heapUsed'></div><br>
+ </body>
+</html>
diff --git a/node_modules/ws/examples/serverstats-express_3/server.js b/node_modules/ws/examples/serverstats-express_3/server.js
new file mode 100644
index 0000000..88bbc9e
--- /dev/null
+++ b/node_modules/ws/examples/serverstats-express_3/server.js
@@ -0,0 +1,21 @@
+var WebSocketServer = require('../../').Server
+ , http = require('http')
+ , express = require('express')
+ , app = express();
+
+app.use(express.static(__dirname + '/public'));
+
+var server = http.createServer(app);
+server.listen(8080);
+
+var wss = new WebSocketServer({server: server});
+wss.on('connection', function(ws) {
+ var id = setInterval(function() {
+ ws.send(JSON.stringify(process.memoryUsage()), function() { /* ignore errors */ });
+ }, 100);
+ console.log('started client interval');
+ ws.on('close', function() {
+ console.log('stopping client interval');
+ clearInterval(id);
+ });
+});
diff --git a/node_modules/ws/examples/serverstats/package.json b/node_modules/ws/examples/serverstats/package.json
new file mode 100644
index 0000000..65c900a
--- /dev/null
+++ b/node_modules/ws/examples/serverstats/package.json
@@ -0,0 +1,17 @@
+{
+ "author": "",
+ "name": "serverstats",
+ "version": "0.0.0",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/einaros/ws.git"
+ },
+ "engines": {
+ "node": ">0.4.0"
+ },
+ "dependencies": {
+ "express": "2.x"
+ },
+ "devDependencies": {},
+ "optionalDependencies": {}
+}
diff --git a/node_modules/ws/examples/serverstats/public/index.html b/node_modules/ws/examples/serverstats/public/index.html
new file mode 100644
index 0000000..24d84e1
--- /dev/null
+++ b/node_modules/ws/examples/serverstats/public/index.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <style>
+ body {
+ font-family: Tahoma, Geneva, sans-serif;
+ }
+
+ div {
+ display: inline;
+ }
+ </style>
+ <script>
+ function updateStats(memuse) {
+ document.getElementById('rss').innerHTML = memuse.rss;
+ document.getElementById('heapTotal').innerHTML = memuse.heapTotal;
+ document.getElementById('heapUsed').innerHTML = memuse.heapUsed;
+ }
+
+ var host = window.document.location.host.replace(/:.*/, '');
+ var ws = new WebSocket('ws://' + host + ':8080');
+ ws.onmessage = function (event) {
+ updateStats(JSON.parse(event.data));
+ };
+ </script>
+ </head>
+ <body>
+ <strong>Server Stats</strong><br>
+ RSS: <div id='rss'></div><br>
+ Heap total: <div id='heapTotal'></div><br>
+ Heap used: <div id='heapUsed'></div><br>
+ </body>
+</html>
diff --git a/node_modules/ws/examples/serverstats/server.js b/node_modules/ws/examples/serverstats/server.js
new file mode 100644
index 0000000..0bbce36
--- /dev/null
+++ b/node_modules/ws/examples/serverstats/server.js
@@ -0,0 +1,19 @@
+var WebSocketServer = require('../../').Server
+ , http = require('http')
+ , express = require('express')
+ , app = express.createServer();
+
+app.use(express.static(__dirname + '/public'));
+app.listen(8080);
+
+var wss = new WebSocketServer({server: app});
+wss.on('connection', function(ws) {
+ var id = setInterval(function() {
+ ws.send(JSON.stringify(process.memoryUsage()), function() { /* ignore errors */ });
+ }, 100);
+ console.log('started client interval');
+ ws.on('close', function() {
+ console.log('stopping client interval');
+ clearInterval(id);
+ })
+});
diff --git a/node_modules/ws/examples/ssl.js b/node_modules/ws/examples/ssl.js
new file mode 100644
index 0000000..bf1bf53
--- /dev/null
+++ b/node_modules/ws/examples/ssl.js
@@ -0,0 +1,59 @@
+
+(function(){
+
+ "use strict";
+
+ var fs = require('fs');
+
+ // you'll probably load configuration from config
+ var cfg = {
+ ssl: true,
+ port: 8080,
+ ssl_key: '/path/to/you/ssl.key',
+ ssl_cert: '/path/to/you/ssl.crt'
+ };
+
+ var httpServ = ( cfg.ssl ) ? require('https') : require('http');
+
+ var WebSocketServer = require('../').Server;
+
+ var app = null;
+
+ // dummy request processing
+ var processRequest = function( req, res ) {
+
+ res.writeHead(200);
+ res.end("All glory to WebSockets!\n");
+ };
+
+ if ( cfg.ssl ) {
+
+ app = httpServ.createServer({
+
+ // providing server with SSL key/cert
+ key: fs.readFileSync( cfg.ssl_key ),
+ cert: fs.readFileSync( cfg.ssl_cert )
+
+ }, processRequest ).listen( cfg.port );
+
+ } else {
+
+ app = httpServ.createServer( processRequest ).listen( cfg.port );
+ }
+
+ // passing or reference to web server so WS would knew port and SSL capabilities
+ var wss = new WebSocketServer( { server: app } );
+
+
+ wss.on( 'connection', function ( wsConnect ) {
+
+ wsConnect.on( 'message', function ( message ) {
+
+ console.log( message );
+
+ });
+
+ });
+
+
+}()); \ No newline at end of file
diff --git a/node_modules/ws/index.js b/node_modules/ws/index.js
new file mode 100644
index 0000000..3423ff2
--- /dev/null
+++ b/node_modules/ws/index.js
@@ -0,0 +1,26 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
+ * MIT Licensed
+ */
+
+module.exports = require('./lib/WebSocket');
+module.exports.Server = require('./lib/WebSocketServer');
+module.exports.Sender = require('./lib/Sender');
+module.exports.Receiver = require('./lib/Receiver');
+
+module.exports.createServer = function (options, connectionListener) {
+ var server = new module.exports.Server(options);
+ if (typeof connectionListener === 'function') {
+ server.on('connection', connectionListener);
+ }
+ return server;
+};
+
+module.exports.connect = module.exports.createConnection = function (address, openListener) {
+ var client = new module.exports(address);
+ if (typeof openListener === 'function') {
+ client.on('open', openListener);
+ }
+ return client;
+};
diff --git a/node_modules/ws/lib/BufferPool.js b/node_modules/ws/lib/BufferPool.js
new file mode 100644
index 0000000..faf8637
--- /dev/null
+++ b/node_modules/ws/lib/BufferPool.js
@@ -0,0 +1,59 @@
+/*!
+ * 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 (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;
diff --git a/node_modules/ws/lib/BufferUtil.fallback.js b/node_modules/ws/lib/BufferUtil.fallback.js
new file mode 100644
index 0000000..508542c
--- /dev/null
+++ b/node_modules/ws/lib/BufferUtil.fallback.js
@@ -0,0 +1,47 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
+ * MIT Licensed
+ */
+
+module.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:;
+ }
+ }
+}
diff --git a/node_modules/ws/lib/BufferUtil.js b/node_modules/ws/lib/BufferUtil.js
new file mode 100644
index 0000000..15d35b9
--- /dev/null
+++ b/node_modules/ws/lib/BufferUtil.js
@@ -0,0 +1,16 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
+ * MIT Licensed
+ */
+
+try {
+ module.exports = require('../build/Release/bufferutil');
+} catch (e) { try {
+ module.exports = require('../build/default/bufferutil');
+} catch (e) { try {
+ module.exports = require('./BufferUtil.fallback');
+} catch (e) {
+ console.error('bufferutil.node seems to not have been built. Run npm install.');
+ throw e;
+}}}
diff --git a/node_modules/ws/lib/ErrorCodes.js b/node_modules/ws/lib/ErrorCodes.js
new file mode 100644
index 0000000..55ebd52
--- /dev/null
+++ b/node_modules/ws/lib/ErrorCodes.js
@@ -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',
+}; \ No newline at end of file
diff --git a/node_modules/ws/lib/Receiver.hixie.js b/node_modules/ws/lib/Receiver.hixie.js
new file mode 100644
index 0000000..f54ad96
--- /dev/null
+++ b/node_modules/ws/lib/Receiver.hixie.js
@@ -0,0 +1,180 @@
+/*!
+ * 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 () {
+ 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) {
+ 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) {
+ this.reset();
+ this.onerror(reason, 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;
+}
diff --git a/node_modules/ws/lib/Receiver.js b/node_modules/ws/lib/Receiver.js
new file mode 100644
index 0000000..2752726
--- /dev/null
+++ b/node_modules/ws/lib/Receiver.js
@@ -0,0 +1,591 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
+ * MIT Licensed
+ */
+
+var util = require('util')
+ , Validation = require('./Validation').Validation
+ , ErrorCodes = require('./ErrorCodes')
+ , BufferPool = require('./BufferPool')
+ , bufferUtil = require('./BufferUtil').BufferUtil;
+
+/**
+ * Node version 0.4 and 0.6 compatibility
+ */
+
+var isNodeV4 = /^v0\.4/.test(process.version);
+
+/**
+ * HyBi Receiver implementation
+ */
+
+function Receiver () {
+ // 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 ?
+ (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 ?
+ (unfragmentedPoolPrevUsed + db.used) / 2 :
+ db.used;
+ });
+
+ 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.expectHeader(2, this.processPacket);
+ 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) {
+ 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 = !isNodeV4
+ ? function(length, isFragmented) { return (isFragmented ? this.fragmentedBufferPool : this.unfragmentedBufferPool).get(length); }
+ : function(length) { return new Buffer(length); };
+
+/**
+ * Start processing a new packet.
+ *
+ * @api private
+ */
+
+Receiver.prototype.processPacket = function (data) {
+ 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 opcode = data[0] & 0xf;
+ if (opcode === 0) {
+ // 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;
+ }
+ 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.state.fragmentedOperation) this.unfragmentedBufferPool.reset(true);
+ else if (this.state.lastFragment) this.fragmentedBufferPool.reset(false);
+ 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.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 = [];
+}
+
+/**
+ * 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') : '';
+}
+
+/**
+ * Concatenates a list of buffers.
+ *
+ * @api private
+ */
+
+Receiver.prototype.concatBuffers = function(buffers) {
+ var length = 0;
+ for (var i = 0, l = buffers.length; i < l; ++i) length += buffers[i].length;
+ var mergedBuffer = new Buffer(length);
+ bufferUtil.merge(mergedBuffer, buffers);
+ return mergedBuffer;
+}
+
+/**
+ * Handles an error
+ *
+ * @api private
+ */
+
+Receiver.prototype.error = function (reason, protocolErrorCode) {
+ this.reset();
+ this.onerror(reason, protocolErrorCode);
+ return this;
+}
+
+/**
+ * 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];
+ }
+}
+
+/**
+ * Opcode handlers
+ */
+
+var opcodes = {
+ // text
+ '1': {
+ start: function(data) {
+ var self = this;
+ // decode length
+ var firstLength = data[1] & 0x7f;
+ if (firstLength < 126) {
+ opcodes['1'].getData.call(self, firstLength);
+ }
+ else if (firstLength == 126) {
+ self.expectHeader(2, function(data) {
+ opcodes['1'].getData.call(self, readUInt16BE.call(data, 0));
+ });
+ }
+ 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;
+ }
+ 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 packet = this.unmask(mask, data, true);
+ if (packet != null) this.currentMessage.push(packet);
+ if (this.state.lastFragment) {
+ var messageBuffer = this.concatBuffers(this.currentMessage);
+ if (!Validation.isValidUTF8(messageBuffer)) {
+ this.error('invalid utf8 sequence', 1007);
+ return;
+ }
+ this.ontext(messageBuffer.toString('utf8'), {masked: this.state.masked, buffer: messageBuffer});
+ this.currentMessage = [];
+ }
+ this.endPacket();
+ }
+ },
+ // binary
+ '2': {
+ start: function(data) {
+ var self = this;
+ // decode length
+ var firstLength = data[1] & 0x7f;
+ if (firstLength < 126) {
+ opcodes['2'].getData.call(self, firstLength);
+ }
+ else if (firstLength == 126) {
+ self.expectHeader(2, function(data) {
+ opcodes['2'].getData.call(self, readUInt16BE.call(data, 0));
+ });
+ }
+ 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;
+ }
+ opcodes['2'].getData.call(self, readUInt32BE.call(data, 4, true));
+ });
+ }
+ },
+ 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 packet = this.unmask(mask, data, true);
+ if (packet != null) this.currentMessage.push(packet);
+ if (this.state.lastFragment) {
+ var messageBuffer = this.concatBuffers(this.currentMessage);
+ this.onbinary(messageBuffer, {masked: this.state.masked, buffer: messageBuffer});
+ this.currentMessage = [];
+ }
+ 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);
+ 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 (!Validation.isValidUTF8(messageBuffer)) {
+ self.error('invalid utf8 sequence', 1007);
+ return;
+ }
+ message = messageBuffer.toString('utf8');
+ }
+ this.onclose(code, message, {masked: self.state.masked});
+ this.reset();
+ },
+ },
+ // 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) {
+ this.onping(this.unmask(mask, data, true), {masked: this.state.masked, binary: true});
+ 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) {
+ this.onpong(this.unmask(mask, data, true), {masked: this.state.masked, binary: true});
+ this.endPacket();
+ }
+ }
+}
diff --git a/node_modules/ws/lib/Sender.hixie.js b/node_modules/ws/lib/Sender.hixie.js
new file mode 100644
index 0000000..1754afb
--- /dev/null
+++ b/node_modules/ws/lib/Sender.hixie.js
@@ -0,0 +1,123 @@
+/*!
+ * 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) {
+ 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;
+/*
+ if (options && options.binary) {
+ this.error('hixie websockets do not support binary');
+ 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;
+}
diff --git a/node_modules/ws/lib/Sender.js b/node_modules/ws/lib/Sender.js
new file mode 100644
index 0000000..db109be
--- /dev/null
+++ b/node_modules/ws/lib/Sender.js
@@ -0,0 +1,226 @@
+/*!
+ * 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
+ , ErrorCodes = require('./ErrorCodes')
+ , bufferUtil = require('./BufferUtil').BufferUtil;
+
+/**
+ * HyBi Sender implementation
+ */
+
+function Sender(socket) {
+ this._socket = socket;
+ this.firstFragment = true;
+}
+
+/**
+ * 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) {
+ 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);
+ this.frameAndSend(0x8, dataBuffer, true, mask);
+}
+
+/**
+ * Sends a ping message to the remote party.
+ *
+ * @api public
+ */
+
+Sender.prototype.ping = function(data, options) {
+ var mask = options && options.mask;
+ this.frameAndSend(0x9, data || '', true, mask);
+}
+
+/**
+ * Sends a pong message to the remote party.
+ *
+ * @api public
+ */
+
+Sender.prototype.pong = function(data, options) {
+ var mask = options && options.mask;
+ this.frameAndSend(0xa, data || '', true, mask);
+}
+
+/**
+ * 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 opcode = options && options.binary ? 2 : 1;
+ if (this.firstFragment === false) opcode = 0;
+ else this.firstFragment = false;
+ if (finalFragment) this.firstFragment = true
+ this.frameAndSend(opcode, data, finalFragment, mask, cb);
+}
+
+/**
+ * Frames and sends a piece of data according to the HyBi WebSocket protocol.
+ *
+ * @api private
+ */
+
+Sender.prototype.frameAndSend = function(opcode, data, finalFragment, maskData, 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 {
+ 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;
+
+ 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 = this._randomMask || (this._randomMask = 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);
+ }
+ }
+ }
+}
+
+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 = data.buffer || data
+ , l = data.byteLength || data.length
+ , buffer = new Buffer(l);
+ for (var i = 0; i < l; ++i) {
+ buffer[i] = array[i];
+ }
+ return buffer;
+}
+
+function getRandomMask() {
+ return new Buffer([
+ ~~(Math.random() * 255),
+ ~~(Math.random() * 255),
+ ~~(Math.random() * 255),
+ ~~(Math.random() * 255)
+ ]);
+}
diff --git a/node_modules/ws/lib/Validation.fallback.js b/node_modules/ws/lib/Validation.fallback.js
new file mode 100644
index 0000000..2c7c4fd
--- /dev/null
+++ b/node_modules/ws/lib/Validation.fallback.js
@@ -0,0 +1,12 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
+ * MIT Licensed
+ */
+
+module.exports.Validation = {
+ isValidUTF8: function(buffer) {
+ return true;
+ }
+};
+
diff --git a/node_modules/ws/lib/Validation.js b/node_modules/ws/lib/Validation.js
new file mode 100644
index 0000000..0f3109a
--- /dev/null
+++ b/node_modules/ws/lib/Validation.js
@@ -0,0 +1,16 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
+ * MIT Licensed
+ */
+
+try {
+ module.exports = require('../build/Release/validation');
+} catch (e) { try {
+ module.exports = require('../build/default/validation');
+} catch (e) { try {
+ module.exports = require('./Validation.fallback');
+} catch (e) {
+ console.error('validation.node seems to not have been built. Run npm install.');
+ throw e;
+}}}
diff --git a/node_modules/ws/lib/WebSocket.js b/node_modules/ws/lib/WebSocket.js
new file mode 100644
index 0000000..5f40658
--- /dev/null
+++ b/node_modules/ws/lib/WebSocket.js
@@ -0,0 +1,766 @@
+/*!
+ * 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')
+ , https = require('https')
+ , crypto = require('crypto')
+ , url = require('url')
+ , fs = require('fs')
+ , Options = require('options')
+ , Sender = require('./Sender')
+ , Receiver = require('./Receiver')
+ , SenderHixie = require('./Sender.hixie')
+ , ReceiverHixie = require('./Receiver.hixie');
+
+/**
+ * Constants
+ */
+
+// Default protocol version
+
+var protocolVersion = 13;
+
+// Close timeout
+
+var closeTimeout = 30000; // Allow 5 seconds to terminate the connection cleanly
+
+/**
+ * Node version 0.4 and 0.6 compatibility
+ */
+
+var isNodeV4 = /^v0\.4/.test(process.version);
+
+/**
+ * WebSocket implementation
+ */
+
+function WebSocket(address, options) {
+ var self = this;
+
+ this._socket = null;
+ this.bytesReceived = 0;
+ this.readyState = null;
+ this.supports = {};
+
+ if (Object.prototype.toString.call(address) == '[object Array]') {
+ initAsServerClient.apply(this, address.concat(options));
+ }
+ else initAsClient.apply(this, arguments);
+}
+
+/**
+ * Inherits from EventEmitter.
+ */
+
+util.inherits(WebSocket, events.EventEmitter);
+
+/**
+ * Ready States
+ */
+
+["CONNECTING", "OPEN", "CLOSING", "CLOSED"].forEach(function (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(code, data) {
+ if (this.readyState == WebSocket.CLOSING || this.readyState == WebSocket.CLOSED) return;
+ if (this.readyState == WebSocket.CONNECTING) {
+ this.readyState = WebSocket.CLOSED;
+ return;
+ }
+ try {
+ this.readyState = WebSocket.CLOSING;
+ this._closeCode = code;
+ this._closeMessage = data;
+ var mask = !this._isServer;
+ this._sender.close(code, data, mask);
+ }
+ catch (e) {
+ this.emit('error', e);
+ }
+ finally {
+ this.terminate();
+ }
+}
+
+/**
+ * Pause the client stream
+ *
+ * @api public
+ */
+
+WebSocket.prototype.pause = function() {
+ 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(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() {
+ 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
+ * @param {function} Optional callback which is executed after the send completes
+ * @api public
+ */
+
+WebSocket.prototype.send = function(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);
+ }
+ if (typeof options.mask == 'undefined') options.mask = !this._isServer;
+ if (data instanceof fs.ReadStream) {
+ startQueue(this);
+ var self = this;
+ sendStream(this, data, options, function(error) {
+ process.nextTick(function() { 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
+ * @param {function} 'function (error, send)' which is executed on successive ticks of which send is 'function (data, final)'.
+ * @api public
+ */
+
+WebSocket.prototype.stream = function(options, cb) {
+ if (typeof options == 'function') {
+ cb = options;
+ options = {};
+ }
+ 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) {
+ var self = this;
+ this._queue.push(function() { self.stream(options, cb); });
+ return;
+ }
+ options = options || {};
+ if (typeof options.mask == 'undefined') options.mask = !this._isServer;
+ startQueue(this);
+ var self = this;
+ var send = function(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() {
+ if (this.readyState == WebSocket.CLOSED) return;
+ if (this._socket) {
+ try {
+ // End the connection
+ 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
+ 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() {
+ return this._socket ? this._socket.bufferSize : 0;
+ }
+});
+
+/**
+ * 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;
+ if (typeof listener === 'function') {
+ if (method === 'message') {
+ function onMessage (data, flags) {
+ listener.call(this, new MessageEvent(data, flags.binary ? 'Binary' : 'Text', target));
+ }
+ // 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') {
+ function onClose (code, message) {
+ listener.call(this, new CloseEvent(code, message, target));
+ }
+ // 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') {
+ function onError (event) {
+ event.target = target;
+ listener.call(this, event);
+ }
+ // 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') {
+ function onOpen () {
+ listener.call(this, new OpenEvent(target));
+ }
+ // 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;
+
+/**
+ * W3C MessageEvent
+ *
+ * @see http://www.w3.org/TR/html5/comms.html
+ * @api private
+ */
+
+function MessageEvent(dataArg, typeArg, target) {
+ this.data = dataArg;
+ this.type = typeArg;
+ this.target = target;
+}
+
+/**
+ * W3C CloseEvent
+ *
+ * @see http://www.w3.org/TR/html5/comms.html
+ * @api private
+ */
+
+function CloseEvent(code, reason, target) {
+ 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
+ * @api private
+ */
+
+function OpenEvent(target) {
+ this.target = target;
+}
+
+/**
+ * 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
+ }).merge(options);
+
+ // expose state properties
+ this.protocol = options.value.protocol;
+ this.protocolVersion = options.value.protocolVersion;
+ this.supports.binary = (this.protocolVersion != 'hixie-76');
+ this.upgradeReq = req;
+ this.readyState = WebSocket.CONNECTING;
+ this._isServer = true;
+
+ // 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, options) {
+ options = new Options({
+ origin: null,
+ protocolVersion: protocolVersion,
+ host: null,
+ protocol: null,
+
+ // ssl-related options
+ pfx: null,
+ key: null,
+ passphrase: null,
+ cert: null,
+ ca: null,
+ ciphers: null,
+ rejectUnauthorized: 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);
+
+ // 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');
+
+ // node<=v0.4.x compatibility
+ var agent;
+ if (isNodeV4) {
+ isNodeV4 = true;
+ agent = new httpObj.Agent({
+ host: serverUrl.hostname,
+ port: port
+ });
+ }
+
+ var headerHost = serverUrl.hostname + ':' + port;
+ var requestOptions = {
+ port: port,
+ host: serverUrl.hostname,
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'websocket',
+ 'Host': headerHost,
+ 'Origin': headerHost,
+ 'Sec-WebSocket-Version': options.value.protocolVersion,
+ 'Sec-WebSocket-Key': key
+ }
+ };
+ if (options.value.protocol) {
+ requestOptions.headers['Sec-WebSocket-Protocol'] = options.value.protocol;
+ }
+ if (options.value.host) {
+ requestOptions.headers['Host'] = options.value.host;
+ }
+
+ if (options.isDefinedAndNonNull('pfx')
+ || options.isDefinedAndNonNull('key')
+ || options.isDefinedAndNonNull('passphrase')
+ || options.isDefinedAndNonNull('cert')
+ || options.isDefinedAndNonNull('ca')
+ || options.isDefinedAndNonNull('ciphers')
+ || options.isDefinedAndNonNull('rejectUnauthorized')) {
+
+ if (isNodeV4) {
+ throw new Error('Client side certificates are not supported on Node 0.4.x');
+ }
+
+ 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;
+
+ // global agent ignores client side certificates
+ agent = new httpObj.Agent(requestOptions);
+ }
+
+ if (isNodeV4) {
+ requestOptions.path = (serverUrl.pathname || '/') + (serverUrl.search || '');
+ }
+ else requestOptions.path = serverUrl.path || '/';
+
+ if (agent) {
+ requestOptions.agent = agent;
+ }
+
+ if (isUnixSocket) {
+ requestOptions.socketPath = serverUrl.pathname;
+ }
+ 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);
+
+ (isNodeV4 ? agent : req).on('error', function(error) {
+ self.emit('error', error);
+ cleanupWebsocketResources.call(this, error);
+ });
+ (isNodeV4 ? agent : req).once('response', function(res) {
+ var error = new Error('unexpected server response (' + res.statusCode + ')');
+ self.emit('error', error);
+ cleanupWebsocketResources.call(this, error);
+ });
+ (isNodeV4 ? agent : req).once('upgrade', function(res, socket, upgradeHead) {
+ if (self.readyState == WebSocket.CLOSED) {
+ // client closed before server accepted connection
+ self.emit('close');
+ removeAllListeners(self);
+ socket.end();
+ return;
+ }
+ var serverKey = res.headers['sec-websocket-accept'];
+ if (typeof serverKey == 'undefined' || serverKey !== expectedServerKey) {
+ self.emit('error', 'invalid server key');
+ removeAllListeners(self);
+ 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);
+ removeAllListeners(self);
+ socket.end();
+ return;
+ } else if (serverProt) {
+ self.protocol = serverProt;
+ }
+
+ establishConnection.call(self, Receiver, Sender, socket, upgradeHead);
+
+ // perform cleanup on http resources
+ removeAllListeners(isNodeV4 ? agent : req);
+ req = null;
+ agent = null;
+ });
+
+ req.end();
+ this.readyState = WebSocket.CONNECTING;
+}
+
+function establishConnection(ReceiverClass, SenderClass, socket, upgradeHead) {
+ this._socket = socket;
+ socket.setTimeout(0);
+ socket.setNoDelay(true);
+ var self = this;
+ this._receiver = new ReceiverClass();
+
+ // socket cleanup handlers
+ socket.on('end', cleanupWebsocketResources.bind(this));
+ socket.on('close', cleanupWebsocketResources.bind(this));
+ socket.on('error', cleanupWebsocketResources.bind(this));
+
+ // ensure that the upgradeHead is added to the receiver
+ function firstHandler(data) {
+ if (self.readyState != WebSocket.OPEN) return;
+ if (upgradeHead && upgradeHead.length > 0) {
+ self.bytesReceived += upgradeHead.length;
+ var head = upgradeHead;
+ upgradeHead = null;
+ self._receiver.add(head);
+ }
+ dataHandler = realHandler;
+ if (data) {
+ self.bytesReceived += data.length;
+ self._receiver.add(data);
+ }
+ }
+ // subsequent packets are pushed straight to the receiver
+ function realHandler(data) {
+ if (data) self.bytesReceived += data.length;
+ self._receiver.add(data);
+ }
+ var dataHandler = firstHandler;
+ socket.on('data', dataHandler);
+ // 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 (data, flags) {
+ flags = flags || {};
+ self.emit('message', data, flags);
+ };
+ self._receiver.onbinary = function (data, flags) {
+ flags = flags || {};
+ flags.binary = true;
+ self.emit('message', data, flags);
+ };
+ self._receiver.onping = function(data, flags) {
+ flags = flags || {};
+ self.pong(data, {mask: !self._isServer, binary: flags.binary === true}, true);
+ self.emit('ping', data, flags);
+ };
+ self._receiver.onpong = function(data, flags) {
+ self.emit('pong', data, flags);
+ };
+ self._receiver.onclose = function(code, data, flags) {
+ flags = flags || {};
+ self.close(code, data);
+ };
+ self._receiver.onerror = function(reason, errorCode) {
+ // close the connection when the receiver reports a HyBi error code
+ self.close(typeof errorCode != 'undefined' ? errorCode : 1002, '');
+ self.emit('error', reason, errorCode);
+ };
+
+ // finalize the client
+ this._sender = new SenderClass(socket);
+ this._sender.on('error', function(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(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() {
+ 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;
+ var emitClose = this.readyState != WebSocket.CONNECTING;
+ this.readyState = WebSocket.CLOSED;
+
+ clearTimeout(this._closeTimer);
+ this._closeTimer = null;
+ if (emitClose) this.emit('close', this._closeCode || 1000, this._closeMessage || '');
+
+ if (this._socket) {
+ removeAllListeners(this._socket);
+ // catch all socket error after removing all standard handlers
+ var socket = this._socket;
+ this._socket.on('error', function() {
+ try { socket.destroy(); } catch (e) {}
+ });
+ try {
+ if (!error) this._socket.end();
+ else this._socket.destroy();
+ }
+ catch (e) { /* Ignore termination errors */ }
+ this._socket = null;
+ }
+ if (this._sender) {
+ removeAllListeners(this._sender);
+ this._sender = null;
+ }
+ if (this._receiver) {
+ this._receiver.cleanup();
+ this._receiver = null;
+ }
+ removeAllListeners(this);
+ this.on('error', function() {}); // catch all errors after this
+ delete this._queue;
+}
+
+function removeAllListeners(instance) {
+ if (isNodeV4) {
+ // node v4 doesn't *actually* remove all listeners globally,
+ // so we do that instead
+ instance._events = {};
+ }
+ else instance.removeAllListeners();
+}
diff --git a/node_modules/ws/lib/WebSocketServer.js b/node_modules/ws/lib/WebSocketServer.js
new file mode 100644
index 0000000..ee47f10
--- /dev/null
+++ b/node_modules/ws/lib/WebSocketServer.js
@@ -0,0 +1,460 @@
+/*!
+ * 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')
+ , url = require('url')
+ , Options = require('options')
+ , WebSocket = require('./WebSocket')
+ , tls = require('tls')
+ , url = require('url');
+
+/**
+ * WebSocket Server implementation
+ */
+
+function WebSocketServer(options, callback) {
+ options = new Options({
+ host: '0.0.0.0',
+ port: null,
+ server: null,
+ verifyClient: null,
+ handleProtocols: null,
+ path: null,
+ noServer: false,
+ disableHixie: false,
+ clientTracking: true
+ }).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) {
+ res.writeHead(200, {'Content-Type': 'text/plain'});
+ res.end('Not implemented');
+ });
+ this._server.listen(options.value.port, options.value.host, callback);
+ this._closeServer = function() { 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._server.once('listening', function() { self.emit('listening'); });
+
+ if (typeof this._server != 'undefined') {
+ this._server.on('error', function(error) {
+ self.emit('error', error)
+ });
+ this._server.on('upgrade', 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.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() {
+ // 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 {
+ delete this._server;
+ }
+ 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'];
+
+ // 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);
+ }
+
+ // 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
+ });
+
+ 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, 404, '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.encrypted !== 'undefined',
+ req: req
+ };
+ if (this.options.verifyClient.length == 2) {
+ this.options.verifyClient(info, function(result) {
+ if (!result) abortConnection(socket, 401, 'Unauthorized')
+ 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'];
+
+ // handshake completion code to run once nonce has been successfully retrieved
+ var completeHandshake = function(nonce, rest) {
+ // 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'));
+
+ 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);
+
+ socket.setTimeout(0);
+ socket.setNoDelay(true);
+ try {
+ // merge header and hash buffer
+ var headerBuffer = new Buffer(headers.concat('', '').join('\r\n'));
+ 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);
+ }
+ else {
+ // nonce not present in upgradeHead, so we must wait for enough data
+ // data to arrive before continuing
+ 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);
+ completeHandshake.call(self, nonce, rest);
+ }
+ }
+ socket.on('data', handler);
+ }
+ }
+
+ // verify client
+ if (typeof this.options.verifyClient == 'function') {
+ var info = {
+ origin: origin,
+ secure: typeof req.connection.encrypted !== 'undefined',
+ req: req
+ };
+ if (this.options.verifyClient.length == 2) {
+ var self = this;
+ this.options.verifyClient(info, function(result) {
+ if (!result) abortConnection(socket, 401, 'Unauthorized')
+ else onClientVerified.apply(self);
+ });
+ return;
+ }
+ else if (!this.options.verifyClient(info)) {
+ abortConnection(socket, 401, 'Unauthorized');
+ return;
+ }
+ }
+
+ // no client verification required
+ onClientVerified();
+}
+
+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) {}
+ }
+}
diff --git a/node_modules/ws/lib/browser.js b/node_modules/ws/lib/browser.js
new file mode 100644
index 0000000..37cafe1
--- /dev/null
+++ b/node_modules/ws/lib/browser.js
@@ -0,0 +1,5 @@
+/// shim for browser packaging
+
+module.exports = function() {
+ return global.WebSocket || global.MozWebSocket;
+}
diff --git a/node_modules/ws/node_modules/commander/.npmignore b/node_modules/ws/node_modules/commander/.npmignore
new file mode 100644
index 0000000..f1250e5
--- /dev/null
+++ b/node_modules/ws/node_modules/commander/.npmignore
@@ -0,0 +1,4 @@
+support
+test
+examples
+*.sock
diff --git a/node_modules/ws/node_modules/commander/.travis.yml b/node_modules/ws/node_modules/commander/.travis.yml
new file mode 100644
index 0000000..f1d0f13
--- /dev/null
+++ b/node_modules/ws/node_modules/commander/.travis.yml
@@ -0,0 +1,4 @@
+language: node_js
+node_js:
+ - 0.4
+ - 0.6
diff --git a/node_modules/ws/node_modules/commander/History.md b/node_modules/ws/node_modules/commander/History.md
new file mode 100644
index 0000000..4961d2e
--- /dev/null
+++ b/node_modules/ws/node_modules/commander/History.md
@@ -0,0 +1,107 @@
+
+0.6.1 / 2012-06-01
+==================
+
+ * Added: append (yes or no) on confirmation
+ * Added: allow node.js v0.7.x
+
+0.6.0 / 2012-04-10
+==================
+
+ * Added `.prompt(obj, callback)` support. Closes #49
+ * Added default support to .choose(). Closes #41
+ * Fixed the choice example
+
+0.5.1 / 2011-12-20
+==================
+
+ * Fixed `password()` for recent nodes. Closes #36
+
+0.5.0 / 2011-12-04
+==================
+
+ * Added sub-command option support [itay]
+
+0.4.3 / 2011-12-04
+==================
+
+ * Fixed custom help ordering. Closes #32
+
+0.4.2 / 2011-11-24
+==================
+
+ * Added travis support
+ * Fixed: line-buffered input automatically trimmed. Closes #31
+
+0.4.1 / 2011-11-18
+==================
+
+ * Removed listening for "close" on --help
+
+0.4.0 / 2011-11-15
+==================
+
+ * Added support for `--`. Closes #24
+
+0.3.3 / 2011-11-14
+==================
+
+ * Fixed: wait for close event when writing help info [Jerry Hamlet]
+
+0.3.2 / 2011-11-01
+==================
+
+ * Fixed long flag definitions with values [felixge]
+
+0.3.1 / 2011-10-31
+==================
+
+ * Changed `--version` short flag to `-V` from `-v`
+ * Changed `.version()` so it's configurable [felixge]
+
+0.3.0 / 2011-10-31
+==================
+
+ * Added support for long flags only. Closes #18
+
+0.2.1 / 2011-10-24
+==================
+
+ * "node": ">= 0.4.x < 0.7.0". Closes #20
+
+0.2.0 / 2011-09-26
+==================
+
+ * Allow for defaults that are not just boolean. Default peassignment only occurs for --no-*, optional, and required arguments. [Jim Isaacs]
+
+0.1.0 / 2011-08-24
+==================
+
+ * Added support for custom `--help` output
+
+0.0.5 / 2011-08-18
+==================
+
+ * Changed: when the user enters nothing prompt for password again
+ * Fixed issue with passwords beginning with numbers [NuckChorris]
+
+0.0.4 / 2011-08-15
+==================
+
+ * Fixed `Commander#args`
+
+0.0.3 / 2011-08-15
+==================
+
+ * Added default option value support
+
+0.0.2 / 2011-08-15
+==================
+
+ * Added mask support to `Command#password(str[, mask], fn)`
+ * Added `Command#password(str, fn)`
+
+0.0.1 / 2010-01-03
+==================
+
+ * Initial release
diff --git a/node_modules/ws/node_modules/commander/Makefile b/node_modules/ws/node_modules/commander/Makefile
new file mode 100644
index 0000000..0074625
--- /dev/null
+++ b/node_modules/ws/node_modules/commander/Makefile
@@ -0,0 +1,7 @@
+
+TESTS = $(shell find test/test.*.js)
+
+test:
+ @./test/run $(TESTS)
+
+.PHONY: test \ No newline at end of file
diff --git a/node_modules/ws/node_modules/commander/Readme.md b/node_modules/ws/node_modules/commander/Readme.md
new file mode 100644
index 0000000..b8328c3
--- /dev/null
+++ b/node_modules/ws/node_modules/commander/Readme.md
@@ -0,0 +1,262 @@
+# Commander.js
+
+ The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/visionmedia/commander).
+
+ [![Build Status](https://secure.travis-ci.org/visionmedia/commander.js.png)](http://travis-ci.org/visionmedia/commander.js)
+
+## Installation
+
+ $ npm install commander
+
+## Option parsing
+
+ Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options.
+
+```js
+#!/usr/bin/env node
+
+/**
+ * Module dependencies.
+ */
+
+var program = require('commander');
+
+program
+ .version('0.0.1')
+ .option('-p, --peppers', 'Add peppers')
+ .option('-P, --pineapple', 'Add pineapple')
+ .option('-b, --bbq', 'Add bbq sauce')
+ .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble')
+ .parse(process.argv);
+
+console.log('you ordered a pizza with:');
+if (program.peppers) console.log(' - peppers');
+if (program.pineapple) console.log(' - pineappe');
+if (program.bbq) console.log(' - bbq');
+console.log(' - %s cheese', program.cheese);
+```
+
+ Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as "--template-engine" are camel-cased, becoming `program.templateEngine` etc.
+
+## Automated --help
+
+ The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free:
+
+```
+ $ ./examples/pizza --help
+
+ Usage: pizza [options]
+
+ Options:
+
+ -V, --version output the version number
+ -p, --peppers Add peppers
+ -P, --pineapple Add pineappe
+ -b, --bbq Add bbq sauce
+ -c, --cheese <type> Add the specified type of cheese [marble]
+ -h, --help output usage information
+
+```
+
+## Coercion
+
+```js
+function range(val) {
+ return val.split('..').map(Number);
+}
+
+function list(val) {
+ return val.split(',');
+}
+
+program
+ .version('0.0.1')
+ .usage('[options] <file ...>')
+ .option('-i, --integer <n>', 'An integer argument', parseInt)
+ .option('-f, --float <n>', 'A float argument', parseFloat)
+ .option('-r, --range <a>..<b>', 'A range', range)
+ .option('-l, --list <items>', 'A list', list)
+ .option('-o, --optional [value]', 'An optional value')
+ .parse(process.argv);
+
+console.log(' int: %j', program.integer);
+console.log(' float: %j', program.float);
+console.log(' optional: %j', program.optional);
+program.range = program.range || [];
+console.log(' range: %j..%j', program.range[0], program.range[1]);
+console.log(' list: %j', program.list);
+console.log(' args: %j', program.args);
+```
+
+## Custom help
+
+ You can display arbitrary `-h, --help` information
+ by listening for "--help". Commander will automatically
+ exit once you are done so that the remainder of your program
+ does not execute causing undesired behaviours, for example
+ in the following executable "stuff" will not output when
+ `--help` is used.
+
+```js
+#!/usr/bin/env node
+
+/**
+ * Module dependencies.
+ */
+
+var program = require('../');
+
+function list(val) {
+ return val.split(',').map(Number);
+}
+
+program
+ .version('0.0.1')
+ .option('-f, --foo', 'enable some foo')
+ .option('-b, --bar', 'enable some bar')
+ .option('-B, --baz', 'enable some baz');
+
+// must be before .parse() since
+// node's emit() is immediate
+
+program.on('--help', function(){
+ console.log(' Examples:');
+ console.log('');
+ console.log(' $ custom-help --help');
+ console.log(' $ custom-help -h');
+ console.log('');
+});
+
+program.parse(process.argv);
+
+console.log('stuff');
+```
+
+yielding the following help output:
+
+```
+
+Usage: custom-help [options]
+
+Options:
+
+ -h, --help output usage information
+ -V, --version output the version number
+ -f, --foo enable some foo
+ -b, --bar enable some bar
+ -B, --baz enable some baz
+
+Examples:
+
+ $ custom-help --help
+ $ custom-help -h
+
+```
+
+## .prompt(msg, fn)
+
+ Single-line prompt:
+
+```js
+program.prompt('name: ', function(name){
+ console.log('hi %s', name);
+});
+```
+
+ Multi-line prompt:
+
+```js
+program.prompt('description:', function(name){
+ console.log('hi %s', name);
+});
+```
+
+ Coercion:
+
+```js
+program.prompt('Age: ', Number, function(age){
+ console.log('age: %j', age);
+});
+```
+
+```js
+program.prompt('Birthdate: ', Date, function(date){
+ console.log('date: %s', date);
+});
+```
+
+## .password(msg[, mask], fn)
+
+Prompt for password without echoing:
+
+```js
+program.password('Password: ', function(pass){
+ console.log('got "%s"', pass);
+ process.stdin.destroy();
+});
+```
+
+Prompt for password with mask char "*":
+
+```js
+program.password('Password: ', '*', function(pass){
+ console.log('got "%s"', pass);
+ process.stdin.destroy();
+});
+```
+
+## .confirm(msg, fn)
+
+ Confirm with the given `msg`:
+
+```js
+program.confirm('continue? ', function(ok){
+ console.log(' got %j', ok);
+});
+```
+
+## .choose(list, fn)
+
+ Let the user choose from a `list`:
+
+```js
+var list = ['tobi', 'loki', 'jane', 'manny', 'luna'];
+
+console.log('Choose the coolest pet:');
+program.choose(list, function(i){
+ console.log('you chose %d "%s"', i, list[i]);
+});
+```
+
+## Links
+
+ - [API documentation](http://visionmedia.github.com/commander.js/)
+ - [ascii tables](https://github.com/LearnBoost/cli-table)
+ - [progress bars](https://github.com/visionmedia/node-progress)
+ - [more progress bars](https://github.com/substack/node-multimeter)
+ - [examples](https://github.com/visionmedia/commander.js/tree/master/examples)
+
+## License
+
+(The MIT License)
+
+Copyright (c) 2011 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. \ No newline at end of file
diff --git a/node_modules/ws/node_modules/commander/index.js b/node_modules/ws/node_modules/commander/index.js
new file mode 100644
index 0000000..06ec1e4
--- /dev/null
+++ b/node_modules/ws/node_modules/commander/index.js
@@ -0,0 +1,2 @@
+
+module.exports = require('./lib/commander'); \ No newline at end of file
diff --git a/node_modules/ws/node_modules/commander/lib/commander.js b/node_modules/ws/node_modules/commander/lib/commander.js
new file mode 100644
index 0000000..5ba87eb
--- /dev/null
+++ b/node_modules/ws/node_modules/commander/lib/commander.js
@@ -0,0 +1,1026 @@
+
+/*!
+ * commander
+ * Copyright(c) 2011 TJ Holowaychuk <tj@vision-media.ca>
+ * MIT Licensed
+ */
+
+/**
+ * Module dependencies.
+ */
+
+var EventEmitter = require('events').EventEmitter
+ , path = require('path')
+ , tty = require('tty')
+ , basename = path.basename;
+
+/**
+ * Expose the root command.
+ */
+
+exports = module.exports = new Command;
+
+/**
+ * Expose `Command`.
+ */
+
+exports.Command = Command;
+
+/**
+ * Expose `Option`.
+ */
+
+exports.Option = Option;
+
+/**
+ * Initialize a new `Option` with the given `flags` and `description`.
+ *
+ * @param {String} flags
+ * @param {String} description
+ * @api public
+ */
+
+function Option(flags, description) {
+ this.flags = flags;
+ this.required = ~flags.indexOf('<');
+ this.optional = ~flags.indexOf('[');
+ this.bool = !~flags.indexOf('-no-');
+ flags = flags.split(/[ ,|]+/);
+ if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift();
+ this.long = flags.shift();
+ this.description = description;
+}
+
+/**
+ * Return option name.
+ *
+ * @return {String}
+ * @api private
+ */
+
+Option.prototype.name = function(){
+ return this.long
+ .replace('--', '')
+ .replace('no-', '');
+};
+
+/**
+ * Check if `arg` matches the short or long flag.
+ *
+ * @param {String} arg
+ * @return {Boolean}
+ * @api private
+ */
+
+Option.prototype.is = function(arg){
+ return arg == this.short
+ || arg == this.long;
+};
+
+/**
+ * Initialize a new `Command`.
+ *
+ * @param {String} name
+ * @api public
+ */
+
+function Command(name) {
+ this.commands = [];
+ this.options = [];
+ this.args = [];
+ this.name = name;
+}
+
+/**
+ * Inherit from `EventEmitter.prototype`.
+ */
+
+Command.prototype.__proto__ = EventEmitter.prototype;
+
+/**
+ * Add command `name`.
+ *
+ * The `.action()` callback is invoked when the
+ * command `name` is specified via __ARGV__,
+ * and the remaining arguments are applied to the
+ * function for access.
+ *
+ * When the `name` is "*" an un-matched command
+ * will be passed as the first arg, followed by
+ * the rest of __ARGV__ remaining.
+ *
+ * Examples:
+ *
+ * program
+ * .version('0.0.1')
+ * .option('-C, --chdir <path>', 'change the working directory')
+ * .option('-c, --config <path>', 'set config path. defaults to ./deploy.conf')
+ * .option('-T, --no-tests', 'ignore test hook')
+ *
+ * program
+ * .command('setup')
+ * .description('run remote setup commands')
+ * .action(function(){
+ * console.log('setup');
+ * });
+ *
+ * program
+ * .command('exec <cmd>')
+ * .description('run the given remote command')
+ * .action(function(cmd){
+ * console.log('exec "%s"', cmd);
+ * });
+ *
+ * program
+ * .command('*')
+ * .description('deploy the given env')
+ * .action(function(env){
+ * console.log('deploying "%s"', env);
+ * });
+ *
+ * program.parse(process.argv);
+ *
+ * @param {String} name
+ * @return {Command} the new command
+ * @api public
+ */
+
+Command.prototype.command = function(name){
+ var args = name.split(/ +/);
+ var cmd = new Command(args.shift());
+ this.commands.push(cmd);
+ cmd.parseExpectedArgs(args);
+ cmd.parent = this;
+ return cmd;
+};
+
+/**
+ * Parse expected `args`.
+ *
+ * For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`.
+ *
+ * @param {Array} args
+ * @return {Command} for chaining
+ * @api public
+ */
+
+Command.prototype.parseExpectedArgs = function(args){
+ if (!args.length) return;
+ var self = this;
+ args.forEach(function(arg){
+ switch (arg[0]) {
+ case '<':
+ self.args.push({ required: true, name: arg.slice(1, -1) });
+ break;
+ case '[':
+ self.args.push({ required: false, name: arg.slice(1, -1) });
+ break;
+ }
+ });
+ return this;
+};
+
+/**
+ * Register callback `fn` for the command.
+ *
+ * Examples:
+ *
+ * program
+ * .command('help')
+ * .description('display verbose help')
+ * .action(function(){
+ * // output help here
+ * });
+ *
+ * @param {Function} fn
+ * @return {Command} for chaining
+ * @api public
+ */
+
+Command.prototype.action = function(fn){
+ var self = this;
+ this.parent.on(this.name, function(args, unknown){
+ // Parse any so-far unknown options
+ unknown = unknown || [];
+ var parsed = self.parseOptions(unknown);
+
+ // Output help if necessary
+ outputHelpIfNecessary(self, parsed.unknown);
+
+ // If there are still any unknown options, then we simply
+ // die, unless someone asked for help, in which case we give it
+ // to them, and then we die.
+ if (parsed.unknown.length > 0) {
+ self.unknownOption(parsed.unknown[0]);
+ }
+
+ self.args.forEach(function(arg, i){
+ if (arg.required && null == args[i]) {
+ self.missingArgument(arg.name);
+ }
+ });
+
+ // Always append ourselves to the end of the arguments,
+ // to make sure we match the number of arguments the user
+ // expects
+ if (self.args.length) {
+ args[self.args.length] = self;
+ } else {
+ args.push(self);
+ }
+
+ fn.apply(this, args);
+ });
+ return this;
+};
+
+/**
+ * Define option with `flags`, `description` and optional
+ * coercion `fn`.
+ *
+ * The `flags` string should contain both the short and long flags,
+ * separated by comma, a pipe or space. The following are all valid
+ * all will output this way when `--help` is used.
+ *
+ * "-p, --pepper"
+ * "-p|--pepper"
+ * "-p --pepper"
+ *
+ * Examples:
+ *
+ * // simple boolean defaulting to false
+ * program.option('-p, --pepper', 'add pepper');
+ *
+ * --pepper
+ * program.pepper
+ * // => Boolean
+ *
+ * // simple boolean defaulting to false
+ * program.option('-C, --no-cheese', 'remove cheese');
+ *
+ * program.cheese
+ * // => true
+ *
+ * --no-cheese
+ * program.cheese
+ * // => true
+ *
+ * // required argument
+ * program.option('-C, --chdir <path>', 'change the working directory');
+ *
+ * --chdir /tmp
+ * program.chdir
+ * // => "/tmp"
+ *
+ * // optional argument
+ * program.option('-c, --cheese [type]', 'add cheese [marble]');
+ *
+ * @param {String} flags
+ * @param {String} description
+ * @param {Function|Mixed} fn or default
+ * @param {Mixed} defaultValue
+ * @return {Command} for chaining
+ * @api public
+ */
+
+Command.prototype.option = function(flags, description, fn, defaultValue){
+ var self = this
+ , option = new Option(flags, description)
+ , oname = option.name()
+ , name = camelcase(oname);
+
+ // default as 3rd arg
+ if ('function' != typeof fn) defaultValue = fn, fn = null;
+
+ // preassign default value only for --no-*, [optional], or <required>
+ if (false == option.bool || option.optional || option.required) {
+ // when --no-* we make sure default is true
+ if (false == option.bool) defaultValue = true;
+ // preassign only if we have a default
+ if (undefined !== defaultValue) self[name] = defaultValue;
+ }
+
+ // register the option
+ this.options.push(option);
+
+ // when it's passed assign the value
+ // and conditionally invoke the callback
+ this.on(oname, function(val){
+ // coercion
+ if (null != val && fn) val = fn(val);
+
+ // unassigned or bool
+ if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) {
+ // if no value, bool true, and we have a default, then use it!
+ if (null == val) {
+ self[name] = option.bool
+ ? defaultValue || true
+ : false;
+ } else {
+ self[name] = val;
+ }
+ } else if (null !== val) {
+ // reassign
+ self[name] = val;
+ }
+ });
+
+ return this;
+};
+
+/**
+ * Parse `argv`, settings options and invoking commands when defined.
+ *
+ * @param {Array} argv
+ * @return {Command} for chaining
+ * @api public
+ */
+
+Command.prototype.parse = function(argv){
+ // store raw args
+ this.rawArgs = argv;
+
+ // guess name
+ if (!this.name) this.name = basename(argv[1]);
+
+ // process argv
+ var parsed = this.parseOptions(this.normalize(argv.slice(2)));
+ this.args = parsed.args;
+ return this.parseArgs(this.args, parsed.unknown);
+};
+
+/**
+ * Normalize `args`, splitting joined short flags. For example
+ * the arg "-abc" is equivalent to "-a -b -c".
+ *
+ * @param {Array} args
+ * @return {Array}
+ * @api private
+ */
+
+Command.prototype.normalize = function(args){
+ var ret = []
+ , arg;
+
+ for (var i = 0, len = args.length; i < len; ++i) {
+ arg = args[i];
+ if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) {
+ arg.slice(1).split('').forEach(function(c){
+ ret.push('-' + c);
+ });
+ } else {
+ ret.push(arg);
+ }
+ }
+
+ return ret;
+};
+
+/**
+ * Parse command `args`.
+ *
+ * When listener(s) are available those
+ * callbacks are invoked, otherwise the "*"
+ * event is emitted and those actions are invoked.
+ *
+ * @param {Array} args
+ * @return {Command} for chaining
+ * @api private
+ */
+
+Command.prototype.parseArgs = function(args, unknown){
+ var cmds = this.commands
+ , len = cmds.length
+ , name;
+
+ if (args.length) {
+ name = args[0];
+ if (this.listeners(name).length) {
+ this.emit(args.shift(), args, unknown);
+ } else {
+ this.emit('*', args);
+ }
+ } else {
+ outputHelpIfNecessary(this, unknown);
+
+ // If there were no args and we have unknown options,
+ // then they are extraneous and we need to error.
+ if (unknown.length > 0) {
+ this.unknownOption(unknown[0]);
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Return an option matching `arg` if any.
+ *
+ * @param {String} arg
+ * @return {Option}
+ * @api private
+ */
+
+Command.prototype.optionFor = function(arg){
+ for (var i = 0, len = this.options.length; i < len; ++i) {
+ if (this.options[i].is(arg)) {
+ return this.options[i];
+ }
+ }
+};
+
+/**
+ * Parse options from `argv` returning `argv`
+ * void of these options.
+ *
+ * @param {Array} argv
+ * @return {Array}
+ * @api public
+ */
+
+Command.prototype.parseOptions = function(argv){
+ var args = []
+ , len = argv.length
+ , literal
+ , option
+ , arg;
+
+ var unknownOptions = [];
+
+ // parse options
+ for (var i = 0; i < len; ++i) {
+ arg = argv[i];
+
+ // literal args after --
+ if ('--' == arg) {
+ literal = true;
+ continue;
+ }
+
+ if (literal) {
+ args.push(arg);
+ continue;
+ }
+
+ // find matching Option
+ option = this.optionFor(arg);
+
+ // option is defined
+ if (option) {
+ // requires arg
+ if (option.required) {
+ arg = argv[++i];
+ if (null == arg) return this.optionMissingArgument(option);
+ if ('-' == arg[0]) return this.optionMissingArgument(option, arg);
+ this.emit(option.name(), arg);
+ // optional arg
+ } else if (option.optional) {
+ arg = argv[i+1];
+ if (null == arg || '-' == arg[0]) {
+ arg = null;
+ } else {
+ ++i;
+ }
+ this.emit(option.name(), arg);
+ // bool
+ } else {
+ this.emit(option.name());
+ }
+ continue;
+ }
+
+ // looks like an option
+ if (arg.length > 1 && '-' == arg[0]) {
+ unknownOptions.push(arg);
+
+ // If the next argument looks like it might be
+ // an argument for this option, we pass it on.
+ // If it isn't, then it'll simply be ignored
+ if (argv[i+1] && '-' != argv[i+1][0]) {
+ unknownOptions.push(argv[++i]);
+ }
+ continue;
+ }
+
+ // arg
+ args.push(arg);
+ }
+
+ return { args: args, unknown: unknownOptions };
+};
+
+/**
+ * Argument `name` is missing.
+ *
+ * @param {String} name
+ * @api private
+ */
+
+Command.prototype.missingArgument = function(name){
+ console.error();
+ console.error(" error: missing required argument `%s'", name);
+ console.error();
+ process.exit(1);
+};
+
+/**
+ * `Option` is missing an argument, but received `flag` or nothing.
+ *
+ * @param {String} option
+ * @param {String} flag
+ * @api private
+ */
+
+Command.prototype.optionMissingArgument = function(option, flag){
+ console.error();
+ if (flag) {
+ console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag);
+ } else {
+ console.error(" error: option `%s' argument missing", option.flags);
+ }
+ console.error();
+ process.exit(1);
+};
+
+/**
+ * Unknown option `flag`.
+ *
+ * @param {String} flag
+ * @api private
+ */
+
+Command.prototype.unknownOption = function(flag){
+ console.error();
+ console.error(" error: unknown option `%s'", flag);
+ console.error();
+ process.exit(1);
+};
+
+/**
+ * Set the program version to `str`.
+ *
+ * This method auto-registers the "-V, --version" flag
+ * which will print the version number when passed.
+ *
+ * @param {String} str
+ * @param {String} flags
+ * @return {Command} for chaining
+ * @api public
+ */
+
+Command.prototype.version = function(str, flags){
+ if (0 == arguments.length) return this._version;
+ this._version = str;
+ flags = flags || '-V, --version';
+ this.option(flags, 'output the version number');
+ this.on('version', function(){
+ console.log(str);
+ process.exit(0);
+ });
+ return this;
+};
+
+/**
+ * Set the description `str`.
+ *
+ * @param {String} str
+ * @return {String|Command}
+ * @api public
+ */
+
+Command.prototype.description = function(str){
+ if (0 == arguments.length) return this._description;
+ this._description = str;
+ return this;
+};
+
+/**
+ * Set / get the command usage `str`.
+ *
+ * @param {String} str
+ * @return {String|Command}
+ * @api public
+ */
+
+Command.prototype.usage = function(str){
+ var args = this.args.map(function(arg){
+ return arg.required
+ ? '<' + arg.name + '>'
+ : '[' + arg.name + ']';
+ });
+
+ var usage = '[options'
+ + (this.commands.length ? '] [command' : '')
+ + ']'
+ + (this.args.length ? ' ' + args : '');
+ if (0 == arguments.length) return this._usage || usage;
+ this._usage = str;
+
+ return this;
+};
+
+/**
+ * Return the largest option length.
+ *
+ * @return {Number}
+ * @api private
+ */
+
+Command.prototype.largestOptionLength = function(){
+ return this.options.reduce(function(max, option){
+ return Math.max(max, option.flags.length);
+ }, 0);
+};
+
+/**
+ * Return help for options.
+ *
+ * @return {String}
+ * @api private
+ */
+
+Command.prototype.optionHelp = function(){
+ var width = this.largestOptionLength();
+
+ // Prepend the help information
+ return [pad('-h, --help', width) + ' ' + 'output usage information']
+ .concat(this.options.map(function(option){
+ return pad(option.flags, width)
+ + ' ' + option.description;
+ }))
+ .join('\n');
+};
+
+/**
+ * Return command help documentation.
+ *
+ * @return {String}
+ * @api private
+ */
+
+Command.prototype.commandHelp = function(){
+ if (!this.commands.length) return '';
+ return [
+ ''
+ , ' Commands:'
+ , ''
+ , this.commands.map(function(cmd){
+ var args = cmd.args.map(function(arg){
+ return arg.required
+ ? '<' + arg.name + '>'
+ : '[' + arg.name + ']';
+ }).join(' ');
+
+ return cmd.name
+ + (cmd.options.length
+ ? ' [options]'
+ : '') + ' ' + args
+ + (cmd.description()
+ ? '\n' + cmd.description()
+ : '');
+ }).join('\n\n').replace(/^/gm, ' ')
+ , ''
+ ].join('\n');
+};
+
+/**
+ * Return program help documentation.
+ *
+ * @return {String}
+ * @api private
+ */
+
+Command.prototype.helpInformation = function(){
+ return [
+ ''
+ , ' Usage: ' + this.name + ' ' + this.usage()
+ , '' + this.commandHelp()
+ , ' Options:'
+ , ''
+ , '' + this.optionHelp().replace(/^/gm, ' ')
+ , ''
+ , ''
+ ].join('\n');
+};
+
+/**
+ * Prompt for a `Number`.
+ *
+ * @param {String} str
+ * @param {Function} fn
+ * @api private
+ */
+
+Command.prototype.promptForNumber = function(str, fn){
+ var self = this;
+ this.promptSingleLine(str, function parseNumber(val){
+ val = Number(val);
+ if (isNaN(val)) return self.promptSingleLine(str + '(must be a number) ', parseNumber);
+ fn(val);
+ });
+};
+
+/**
+ * Prompt for a `Date`.
+ *
+ * @param {String} str
+ * @param {Function} fn
+ * @api private
+ */
+
+Command.prototype.promptForDate = function(str, fn){
+ var self = this;
+ this.promptSingleLine(str, function parseDate(val){
+ val = new Date(val);
+ if (isNaN(val.getTime())) return self.promptSingleLine(str + '(must be a date) ', parseDate);
+ fn(val);
+ });
+};
+
+/**
+ * Single-line prompt.
+ *
+ * @param {String} str
+ * @param {Function} fn
+ * @api private
+ */
+
+Command.prototype.promptSingleLine = function(str, fn){
+ if ('function' == typeof arguments[2]) {
+ return this['promptFor' + (fn.name || fn)](str, arguments[2]);
+ }
+
+ process.stdout.write(str);
+ process.stdin.setEncoding('utf8');
+ process.stdin.once('data', function(val){
+ fn(val.trim());
+ }).resume();
+};
+
+/**
+ * Multi-line prompt.
+ *
+ * @param {String} str
+ * @param {Function} fn
+ * @api private
+ */
+
+Command.prototype.promptMultiLine = function(str, fn){
+ var buf = [];
+ console.log(str);
+ process.stdin.setEncoding('utf8');
+ process.stdin.on('data', function(val){
+ if ('\n' == val || '\r\n' == val) {
+ process.stdin.removeAllListeners('data');
+ fn(buf.join('\n'));
+ } else {
+ buf.push(val.trimRight());
+ }
+ }).resume();
+};
+
+/**
+ * Prompt `str` and callback `fn(val)`
+ *
+ * Commander supports single-line and multi-line prompts.
+ * To issue a single-line prompt simply add white-space
+ * to the end of `str`, something like "name: ", whereas
+ * for a multi-line prompt omit this "description:".
+ *
+ *
+ * Examples:
+ *
+ * program.prompt('Username: ', function(name){
+ * console.log('hi %s', name);
+ * });
+ *
+ * program.prompt('Description:', function(desc){
+ * console.log('description was "%s"', desc.trim());
+ * });
+ *
+ * @param {String|Object} str
+ * @param {Function} fn
+ * @api public
+ */
+
+Command.prototype.prompt = function(str, fn){
+ var self = this;
+
+ if ('string' == typeof str) {
+ if (/ $/.test(str)) return this.promptSingleLine.apply(this, arguments);
+ this.promptMultiLine(str, fn);
+ } else {
+ var keys = Object.keys(str)
+ , obj = {};
+
+ function next() {
+ var key = keys.shift()
+ , label = str[key];
+
+ if (!key) return fn(obj);
+ self.prompt(label, function(val){
+ obj[key] = val;
+ next();
+ });
+ }
+
+ next();
+ }
+};
+
+/**
+ * Prompt for password with `str`, `mask` char and callback `fn(val)`.
+ *
+ * The mask string defaults to '', aka no output is
+ * written while typing, you may want to use "*" etc.
+ *
+ * Examples:
+ *
+ * program.password('Password: ', function(pass){
+ * console.log('got "%s"', pass);
+ * process.stdin.destroy();
+ * });
+ *
+ * program.password('Password: ', '*', function(pass){
+ * console.log('got "%s"', pass);
+ * process.stdin.destroy();
+ * });
+ *
+ * @param {String} str
+ * @param {String} mask
+ * @param {Function} fn
+ * @api public
+ */
+
+Command.prototype.password = function(str, mask, fn){
+ var self = this
+ , buf = '';
+
+ // default mask
+ if ('function' == typeof mask) {
+ fn = mask;
+ mask = '';
+ }
+
+ process.stdin.resume();
+ tty.setRawMode(true);
+ process.stdout.write(str);
+
+ // keypress
+ process.stdin.on('keypress', function(c, key){
+ if (key && 'enter' == key.name) {
+ console.log();
+ process.stdin.removeAllListeners('keypress');
+ tty.setRawMode(false);
+ if (!buf.trim().length) return self.password(str, mask, fn);
+ fn(buf);
+ return;
+ }
+
+ if (key && key.ctrl && 'c' == key.name) {
+ console.log('%s', buf);
+ process.exit();
+ }
+
+ process.stdout.write(mask);
+ buf += c;
+ }).resume();
+};
+
+/**
+ * Confirmation prompt with `str` and callback `fn(bool)`
+ *
+ * Examples:
+ *
+ * program.confirm('continue? ', function(ok){
+ * console.log(' got %j', ok);
+ * process.stdin.destroy();
+ * });
+ *
+ * @param {String} str
+ * @param {Function} fn
+ * @api public
+ */
+
+
+Command.prototype.confirm = function(str, fn, verbose){
+ var self = this;
+ this.prompt(str, function(ok){
+ if (!ok.trim()) {
+ if (!verbose) str += '(yes or no) ';
+ return self.confirm(str, fn, true);
+ }
+ fn(parseBool(ok));
+ });
+};
+
+/**
+ * Choice prompt with `list` of items and callback `fn(index, item)`
+ *
+ * Examples:
+ *
+ * var list = ['tobi', 'loki', 'jane', 'manny', 'luna'];
+ *
+ * console.log('Choose the coolest pet:');
+ * program.choose(list, function(i){
+ * console.log('you chose %d "%s"', i, list[i]);
+ * process.stdin.destroy();
+ * });
+ *
+ * @param {Array} list
+ * @param {Number|Function} index or fn
+ * @param {Function} fn
+ * @api public
+ */
+
+Command.prototype.choose = function(list, index, fn){
+ var self = this
+ , hasDefault = 'number' == typeof index;
+
+ if (!hasDefault) {
+ fn = index;
+ index = null;
+ }
+
+ list.forEach(function(item, i){
+ if (hasDefault && i == index) {
+ console.log('* %d) %s', i + 1, item);
+ } else {
+ console.log(' %d) %s', i + 1, item);
+ }
+ });
+
+ function again() {
+ self.prompt(' : ', function(val){
+ val = parseInt(val, 10) - 1;
+ if (hasDefault && isNaN(val)) val = index;
+
+ if (null == list[val]) {
+ again();
+ } else {
+ fn(val, list[val]);
+ }
+ });
+ }
+
+ again();
+};
+
+/**
+ * Camel-case the given `flag`
+ *
+ * @param {String} flag
+ * @return {String}
+ * @api private
+ */
+
+function camelcase(flag) {
+ return flag.split('-').reduce(function(str, word){
+ return str + word[0].toUpperCase() + word.slice(1);
+ });
+}
+
+/**
+ * Parse a boolean `str`.
+ *
+ * @param {String} str
+ * @return {Boolean}
+ * @api private
+ */
+
+function parseBool(str) {
+ return /^y|yes|ok|true$/i.test(str);
+}
+
+/**
+ * Pad `str` to `width`.
+ *
+ * @param {String} str
+ * @param {Number} width
+ * @return {String}
+ * @api private
+ */
+
+function pad(str, width) {
+ var len = Math.max(0, width - str.length);
+ return str + Array(len + 1).join(' ');
+}
+
+/**
+ * Output help information if necessary
+ *
+ * @param {Command} command to output help for
+ * @param {Array} array of options to search for -h or --help
+ * @api private
+ */
+
+function outputHelpIfNecessary(cmd, options) {
+ options = options || [];
+ for (var i = 0; i < options.length; i++) {
+ if (options[i] == '--help' || options[i] == '-h') {
+ process.stdout.write(cmd.helpInformation());
+ cmd.emit('--help');
+ process.exit(0);
+ }
+ }
+}
diff --git a/node_modules/ws/node_modules/commander/package.json b/node_modules/ws/node_modules/commander/package.json
new file mode 100644
index 0000000..b82c5b1
--- /dev/null
+++ b/node_modules/ws/node_modules/commander/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "commander",
+ "version": "0.6.1",
+ "description": "the complete solution for node.js command-line programs",
+ "keywords": [
+ "command",
+ "option",
+ "parser",
+ "prompt",
+ "stdin"
+ ],
+ "author": {
+ "name": "TJ Holowaychuk",
+ "email": "tj@vision-media.ca"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/visionmedia/commander.js.git"
+ },
+ "dependencies": {},
+ "devDependencies": {
+ "should": ">= 0.0.1"
+ },
+ "scripts": {
+ "test": "make test"
+ },
+ "main": "index",
+ "engines": {
+ "node": ">= 0.4.x"
+ },
+ "readme": "# Commander.js\n\n The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/visionmedia/commander).\n\n [![Build Status](https://secure.travis-ci.org/visionmedia/commander.js.png)](http://travis-ci.org/visionmedia/commander.js)\n\n## Installation\n\n $ npm install commander\n\n## Option parsing\n\n Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options.\n\n```js\n#!/usr/bin/env node\n\n/**\n * Module dependencies.\n */\n\nvar program = require('commander');\n\nprogram\n .version('0.0.1')\n .option('-p, --peppers', 'Add peppers')\n .option('-P, --pineapple', 'Add pineapple')\n .option('-b, --bbq', 'Add bbq sauce')\n .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble')\n .parse(process.argv);\n\nconsole.log('you ordered a pizza with:');\nif (program.peppers) console.log(' - peppers');\nif (program.pineapple) console.log(' - pineappe');\nif (program.bbq) console.log(' - bbq');\nconsole.log(' - %s cheese', program.cheese);\n```\n\n Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as \"--template-engine\" are camel-cased, becoming `program.templateEngine` etc.\n\n## Automated --help\n\n The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free:\n\n``` \n $ ./examples/pizza --help\n\n Usage: pizza [options]\n\n Options:\n\n -V, --version output the version number\n -p, --peppers Add peppers\n -P, --pineapple Add pineappe\n -b, --bbq Add bbq sauce\n -c, --cheese <type> Add the specified type of cheese [marble]\n -h, --help output usage information\n\n```\n\n## Coercion\n\n```js\nfunction range(val) {\n return val.split('..').map(Number);\n}\n\nfunction list(val) {\n return val.split(',');\n}\n\nprogram\n .version('0.0.1')\n .usage('[options] <file ...>')\n .option('-i, --integer <n>', 'An integer argument', parseInt)\n .option('-f, --float <n>', 'A float argument', parseFloat)\n .option('-r, --range <a>..<b>', 'A range', range)\n .option('-l, --list <items>', 'A list', list)\n .option('-o, --optional [value]', 'An optional value')\n .parse(process.argv);\n\nconsole.log(' int: %j', program.integer);\nconsole.log(' float: %j', program.float);\nconsole.log(' optional: %j', program.optional);\nprogram.range = program.range || [];\nconsole.log(' range: %j..%j', program.range[0], program.range[1]);\nconsole.log(' list: %j', program.list);\nconsole.log(' args: %j', program.args);\n```\n\n## Custom help\n\n You can display arbitrary `-h, --help` information\n by listening for \"--help\". Commander will automatically\n exit once you are done so that the remainder of your program\n does not execute causing undesired behaviours, for example\n in the following executable \"stuff\" will not output when\n `--help` is used.\n\n```js\n#!/usr/bin/env node\n\n/**\n * Module dependencies.\n */\n\nvar program = require('../');\n\nfunction list(val) {\n return val.split(',').map(Number);\n}\n\nprogram\n .version('0.0.1')\n .option('-f, --foo', 'enable some foo')\n .option('-b, --bar', 'enable some bar')\n .option('-B, --baz', 'enable some baz');\n\n// must be before .parse() since\n// node's emit() is immediate\n\nprogram.on('--help', function(){\n console.log(' Examples:');\n console.log('');\n console.log(' $ custom-help --help');\n console.log(' $ custom-help -h');\n console.log('');\n});\n\nprogram.parse(process.argv);\n\nconsole.log('stuff');\n```\n\nyielding the following help output:\n\n```\n\nUsage: custom-help [options]\n\nOptions:\n\n -h, --help output usage information\n -V, --version output the version number\n -f, --foo enable some foo\n -b, --bar enable some bar\n -B, --baz enable some baz\n\nExamples:\n\n $ custom-help --help\n $ custom-help -h\n\n```\n\n## .prompt(msg, fn)\n\n Single-line prompt:\n\n```js\nprogram.prompt('name: ', function(name){\n console.log('hi %s', name);\n});\n```\n\n Multi-line prompt:\n\n```js\nprogram.prompt('description:', function(name){\n console.log('hi %s', name);\n});\n```\n\n Coercion:\n\n```js\nprogram.prompt('Age: ', Number, function(age){\n console.log('age: %j', age);\n});\n```\n\n```js\nprogram.prompt('Birthdate: ', Date, function(date){\n console.log('date: %s', date);\n});\n```\n\n## .password(msg[, mask], fn)\n\nPrompt for password without echoing:\n\n```js\nprogram.password('Password: ', function(pass){\n console.log('got \"%s\"', pass);\n process.stdin.destroy();\n});\n```\n\nPrompt for password with mask char \"*\":\n\n```js\nprogram.password('Password: ', '*', function(pass){\n console.log('got \"%s\"', pass);\n process.stdin.destroy();\n});\n```\n\n## .confirm(msg, fn)\n\n Confirm with the given `msg`:\n\n```js\nprogram.confirm('continue? ', function(ok){\n console.log(' got %j', ok);\n});\n```\n\n## .choose(list, fn)\n\n Let the user choose from a `list`:\n\n```js\nvar list = ['tobi', 'loki', 'jane', 'manny', 'luna'];\n\nconsole.log('Choose the coolest pet:');\nprogram.choose(list, function(i){\n console.log('you chose %d \"%s\"', i, list[i]);\n});\n```\n\n## Links\n\n - [API documentation](http://visionmedia.github.com/commander.js/)\n - [ascii tables](https://github.com/LearnBoost/cli-table)\n - [progress bars](https://github.com/visionmedia/node-progress)\n - [more progress bars](https://github.com/substack/node-multimeter)\n - [examples](https://github.com/visionmedia/commander.js/tree/master/examples)\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2011 TJ Holowaychuk &lt;tj@vision-media.ca&gt;\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.",
+ "readmeFilename": "Readme.md",
+ "bugs": {
+ "url": "https://github.com/visionmedia/commander.js/issues"
+ },
+ "_id": "commander@0.6.1",
+ "dist": {
+ "shasum": "41be1203fe9f03dfa7933bc2e023817bf091e9e3"
+ },
+ "_from": "commander@~0.6.1",
+ "_resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz"
+}
diff --git a/node_modules/ws/node_modules/options/.npmignore b/node_modules/ws/node_modules/options/.npmignore
new file mode 100644
index 0000000..6bfffbb
--- /dev/null
+++ b/node_modules/ws/node_modules/options/.npmignore
@@ -0,0 +1,5 @@
+npm-debug.log
+node_modules
+.*.swp
+.lock-*
+build/
diff --git a/node_modules/ws/node_modules/options/Makefile b/node_modules/ws/node_modules/options/Makefile
new file mode 100644
index 0000000..7496b6f
--- /dev/null
+++ b/node_modules/ws/node_modules/options/Makefile
@@ -0,0 +1,12 @@
+ALL_TESTS = $(shell find test/ -name '*.test.js')
+
+run-tests:
+ @./node_modules/.bin/mocha \
+ -t 2000 \
+ $(TESTFLAGS) \
+ $(TESTS)
+
+test:
+ @$(MAKE) NODE_PATH=lib TESTS="$(ALL_TESTS)" run-tests
+
+.PHONY: test
diff --git a/node_modules/ws/node_modules/options/README.md b/node_modules/ws/node_modules/options/README.md
new file mode 100644
index 0000000..4b39a2a
--- /dev/null
+++ b/node_modules/ws/node_modules/options/README.md
@@ -0,0 +1,28 @@
+# options.js #
+
+A very light-weight in-code option parsers for node.js.
+
+## License ##
+
+(The MIT License)
+
+Copyright (c) 2012 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.
diff --git a/node_modules/ws/node_modules/options/lib/options.js b/node_modules/ws/node_modules/options/lib/options.js
new file mode 100644
index 0000000..4fc45e9
--- /dev/null
+++ b/node_modules/ws/node_modules/options/lib/options.js
@@ -0,0 +1,86 @@
+/*!
+ * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
+ * MIT Licensed
+ */
+
+var fs = require('fs');
+
+function Options(defaults) {
+ var internalValues = {};
+ var values = this.value = {};
+ Object.keys(defaults).forEach(function(key) {
+ internalValues[key] = defaults[key];
+ Object.defineProperty(values, key, {
+ get: function() { return internalValues[key]; },
+ configurable: false,
+ enumerable: true
+ });
+ });
+ this.reset = function() {
+ Object.keys(defaults).forEach(function(key) {
+ internalValues[key] = defaults[key];
+ });
+ return this;
+ };
+ this.merge = function(options, required) {
+ options = options || {};
+ if (Object.prototype.toString.call(required) === '[object Array]') {
+ var missing = [];
+ for (var i = 0, l = required.length; i < l; ++i) {
+ var key = required[i];
+ if (!(key in options)) {
+ missing.push(key);
+ }
+ }
+ if (missing.length > 0) {
+ if (missing.length > 1) {
+ throw new Error('options ' +
+ missing.slice(0, missing.length - 1).join(', ') + ' and ' +
+ missing[missing.length - 1] + ' must be defined');
+ }
+ else throw new Error('option ' + missing[0] + ' must be defined');
+ }
+ }
+ Object.keys(options).forEach(function(key) {
+ if (key in internalValues) {
+ internalValues[key] = options[key];
+ }
+ });
+ return this;
+ };
+ this.copy = function(keys) {
+ var obj = {};
+ Object.keys(defaults).forEach(function(key) {
+ if (keys.indexOf(key) !== -1) {
+ obj[key] = values[key];
+ }
+ });
+ return obj;
+ };
+ this.read = function(filename, cb) {
+ if (typeof cb == 'function') {
+ var self = this;
+ fs.readFile(filename, function(error, data) {
+ if (error) return cb(error);
+ var conf = JSON.parse(data);
+ self.merge(conf);
+ cb();
+ });
+ }
+ else {
+ var conf = JSON.parse(fs.readFileSync(filename));
+ this.merge(conf);
+ }
+ return this;
+ };
+ this.isDefined = function(key) {
+ return typeof values[key] != 'undefined';
+ };
+ this.isDefinedAndNonNull = function(key) {
+ return typeof values[key] != 'undefined' && values[key] !== null;
+ };
+ Object.freeze(values);
+ Object.freeze(this);
+}
+
+module.exports = Options;
diff --git a/node_modules/ws/node_modules/options/package.json b/node_modules/ws/node_modules/options/package.json
new file mode 100644
index 0000000..6e5bdf8
--- /dev/null
+++ b/node_modules/ws/node_modules/options/package.json
@@ -0,0 +1,36 @@
+{
+ "author": {
+ "name": "Einar Otto Stangvik",
+ "email": "einaros@gmail.com",
+ "url": "http://2x.io"
+ },
+ "name": "options",
+ "description": "A very light-weight in-code option parsers for node.js.",
+ "version": "0.0.5",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/einaros/options.js.git"
+ },
+ "main": "lib/options",
+ "scripts": {
+ "test": "make test"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ },
+ "dependencies": {},
+ "devDependencies": {
+ "mocha": "latest"
+ },
+ "readme": "# options.js #\n\nA very light-weight in-code option parsers for node.js.\n\n## License ##\n\n(The MIT License)\n\nCopyright (c) 2012 Einar Otto Stangvik &lt;einaros@gmail.com&gt;\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n",
+ "readmeFilename": "README.md",
+ "bugs": {
+ "url": "https://github.com/einaros/options.js/issues"
+ },
+ "_id": "options@0.0.5",
+ "dist": {
+ "shasum": "4f79a4434f2d0703250db48ef2fa5f5050f08f15"
+ },
+ "_from": "options@>=0.0.5",
+ "_resolved": "https://registry.npmjs.org/options/-/options-0.0.5.tgz"
+}
diff --git a/node_modules/ws/node_modules/options/test/fixtures/test.conf b/node_modules/ws/node_modules/options/test/fixtures/test.conf
new file mode 100644
index 0000000..6e62444
--- /dev/null
+++ b/node_modules/ws/node_modules/options/test/fixtures/test.conf
@@ -0,0 +1,4 @@
+{
+ "a": "foobar",
+ "b": false
+} \ No newline at end of file
diff --git a/node_modules/ws/node_modules/options/test/options.test.js b/node_modules/ws/node_modules/options/test/options.test.js
new file mode 100644
index 0000000..6a1d9f5
--- /dev/null
+++ b/node_modules/ws/node_modules/options/test/options.test.js
@@ -0,0 +1,140 @@
+var Options = require('options')
+ , assert = require('assert');
+
+describe('Options', function() {
+ describe('#ctor', function() {
+ it('initializes options', function() {
+ var option = new Options({a: true, b: false});
+ assert.strictEqual(true, option.value.a);
+ assert.strictEqual(false, option.value.b);
+ });
+ });
+
+ describe('#merge', function() {
+ it('merges options from another object', function() {
+ var option = new Options({a: true, b: false});
+ option.merge({b: true});
+ assert.strictEqual(true, option.value.a);
+ assert.strictEqual(true, option.value.b);
+ });
+ it('does nothing when arguments are undefined', function() {
+ var option = new Options({a: true, b: false});
+ option.merge(undefined);
+ assert.strictEqual(true, option.value.a);
+ assert.strictEqual(false, option.value.b);
+ });
+ it('cannot set values that werent already there', function() {
+ var option = new Options({a: true, b: false});
+ option.merge({c: true});
+ assert.strictEqual('undefined', typeof option.value.c);
+ });
+ it('can require certain options to be defined', function() {
+ var option = new Options({a: true, b: false, c: 3});
+ var caughtException = false;
+ try {
+ option.merge({}, ['a', 'b', 'c']);
+ }
+ catch (e) {
+ caughtException = e.toString() == 'Error: options a, b and c must be defined';
+ }
+ assert.strictEqual(true, caughtException);
+ });
+ it('can require certain options to be defined, when options are undefined', function() {
+ var option = new Options({a: true, b: false, c: 3});
+ var caughtException = false;
+ try {
+ option.merge(undefined, ['a', 'b', 'c']);
+ }
+ catch (e) {
+ caughtException = e.toString() == 'Error: options a, b and c must be defined';
+ }
+ assert.strictEqual(true, caughtException);
+ });
+ it('returns "this"', function() {
+ var option = new Options({a: true, b: false, c: 3});
+ assert.strictEqual(option, option.merge());
+ });
+ });
+
+ describe('#copy', function() {
+ it('returns a new object with the indicated options', function() {
+ var option = new Options({a: true, b: false, c: 3});
+ option.merge({c: 4});
+ var obj = option.copy(['a', 'c']);
+ assert.strictEqual(true, obj.a);
+ assert.strictEqual(4, obj.c);
+ assert.strictEqual('undefined', typeof obj.b);
+ });
+ });
+
+ describe('#value', function() {
+ it('can be enumerated', function() {
+ var option = new Options({a: true, b: false});
+ assert.strictEqual(2, Object.keys(option.value).length);
+ });
+ it('can not be used to set values', function() {
+ var option = new Options({a: true, b: false});
+ option.value.b = true;
+ assert.strictEqual(false, option.value.b);
+ });
+ it('can not be used to add values', function() {
+ var option = new Options({a: true, b: false});
+ option.value.c = 3;
+ assert.strictEqual('undefined', typeof option.value.c);
+ });
+ });
+
+ describe('#isDefined', function() {
+ it('returns true if the named value is defined', function() {
+ var option = new Options({a: undefined});
+ assert.strictEqual(false, option.isDefined('a'));
+ option.merge({a: false});
+ assert.strictEqual(true, option.isDefined('a'));
+ });
+ });
+
+ describe('#isDefinedAndNonNull', function() {
+ it('returns true if the named value is defined and non-null', function() {
+ var option = new Options({a: undefined});
+ assert.strictEqual(false, option.isDefinedAndNonNull('a'));
+ option.merge({a: null});
+ assert.strictEqual(false, option.isDefinedAndNonNull('a'));
+ option.merge({a: 2});
+ assert.strictEqual(true, option.isDefinedAndNonNull('a'));
+ });
+ });
+
+ describe('#read', function() {
+ it('reads and merges config from a file', function() {
+ var option = new Options({a: true, b: true});
+ option.read(__dirname + '/fixtures/test.conf');
+ assert.strictEqual('foobar', option.value.a);
+ assert.strictEqual(false, option.value.b);
+ });
+
+ it('asynchronously reads and merges config from a file when a callback is passed', function(done) {
+ var option = new Options({a: true, b: true});
+ option.read(__dirname + '/fixtures/test.conf', function(error) {
+ assert.strictEqual('foobar', option.value.a);
+ assert.strictEqual(false, option.value.b);
+ done();
+ });
+ });
+ });
+
+ describe('#reset', function() {
+ it('resets options to defaults', function() {
+ var option = new Options({a: true, b: false});
+ option.merge({b: true});
+ assert.strictEqual(true, option.value.b);
+ option.reset();
+ assert.strictEqual(false, option.value.b);
+ });
+ });
+
+ it('is immutable', function() {
+ var option = new Options({a: true, b: false});
+ option.foo = 2;
+ assert.strictEqual('undefined', typeof option.foo);
+ });
+});
diff --git a/node_modules/ws/node_modules/tinycolor/.npmignore b/node_modules/ws/node_modules/tinycolor/.npmignore
new file mode 100644
index 0000000..6bfffbb
--- /dev/null
+++ b/node_modules/ws/node_modules/tinycolor/.npmignore
@@ -0,0 +1,5 @@
+npm-debug.log
+node_modules
+.*.swp
+.lock-*
+build/
diff --git a/node_modules/ws/node_modules/tinycolor/README.md b/node_modules/ws/node_modules/tinycolor/README.md
new file mode 100644
index 0000000..55eb3c1
--- /dev/null
+++ b/node_modules/ws/node_modules/tinycolor/README.md
@@ -0,0 +1,3 @@
+# tinycolor #
+
+This is a no-fuzz, barebone, zero muppetry color module for node.js. \ No newline at end of file
diff --git a/node_modules/ws/node_modules/tinycolor/example.js b/node_modules/ws/node_modules/tinycolor/example.js
new file mode 100644
index 0000000..f754046
--- /dev/null
+++ b/node_modules/ws/node_modules/tinycolor/example.js
@@ -0,0 +1,3 @@
+require('./tinycolor');
+console.log('this should be red and have an underline!'.grey.underline);
+console.log('this should have a blue background!'.bgBlue); \ No newline at end of file
diff --git a/node_modules/ws/node_modules/tinycolor/package.json b/node_modules/ws/node_modules/tinycolor/package.json
new file mode 100644
index 0000000..890d233
--- /dev/null
+++ b/node_modules/ws/node_modules/tinycolor/package.json
@@ -0,0 +1,31 @@
+{
+ "author": {
+ "name": "Einar Otto Stangvik",
+ "email": "einaros@gmail.com",
+ "url": "http://2x.io"
+ },
+ "name": "tinycolor",
+ "description": "a to-the-point color module for node",
+ "version": "0.0.1",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/einaros/tinycolor.git"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ },
+ "dependencies": {},
+ "devDependencies": {},
+ "main": "tinycolor",
+ "readme": "# tinycolor #\n\nThis is a no-fuzz, barebone, zero muppetry color module for node.js.",
+ "readmeFilename": "README.md",
+ "bugs": {
+ "url": "https://github.com/einaros/tinycolor/issues"
+ },
+ "_id": "tinycolor@0.0.1",
+ "dist": {
+ "shasum": "dc96ac2859b0dcf0a1cb0aec6aaf31d3883c0ecb"
+ },
+ "_from": "tinycolor@0.x",
+ "_resolved": "https://registry.npmjs.org/tinycolor/-/tinycolor-0.0.1.tgz"
+}
diff --git a/node_modules/ws/node_modules/tinycolor/tinycolor.js b/node_modules/ws/node_modules/tinycolor/tinycolor.js
new file mode 100644
index 0000000..36e552c
--- /dev/null
+++ b/node_modules/ws/node_modules/tinycolor/tinycolor.js
@@ -0,0 +1,31 @@
+var styles = {
+ 'bold': ['\033[1m', '\033[22m'],
+ 'italic': ['\033[3m', '\033[23m'],
+ 'underline': ['\033[4m', '\033[24m'],
+ 'inverse': ['\033[7m', '\033[27m'],
+ 'black': ['\033[30m', '\033[39m'],
+ 'red': ['\033[31m', '\033[39m'],
+ 'green': ['\033[32m', '\033[39m'],
+ 'yellow': ['\033[33m', '\033[39m'],
+ 'blue': ['\033[34m', '\033[39m'],
+ 'magenta': ['\033[35m', '\033[39m'],
+ 'cyan': ['\033[36m', '\033[39m'],
+ 'white': ['\033[37m', '\033[39m'],
+ 'default': ['\033[39m', '\033[39m'],
+ 'grey': ['\033[90m', '\033[39m'],
+ 'bgBlack': ['\033[40m', '\033[49m'],
+ 'bgRed': ['\033[41m', '\033[49m'],
+ 'bgGreen': ['\033[42m', '\033[49m'],
+ 'bgYellow': ['\033[43m', '\033[49m'],
+ 'bgBlue': ['\033[44m', '\033[49m'],
+ 'bgMagenta': ['\033[45m', '\033[49m'],
+ 'bgCyan': ['\033[46m', '\033[49m'],
+ 'bgWhite': ['\033[47m', '\033[49m'],
+ 'bgDefault': ['\033[49m', '\033[49m']
+}
+Object.keys(styles).forEach(function(style) {
+ Object.defineProperty(String.prototype, style, {
+ get: function() { return styles[style][0] + this + styles[style][1]; },
+ enumerable: false
+ });
+});
diff --git a/node_modules/ws/package.json b/node_modules/ws/package.json
new file mode 100644
index 0000000..5a1859c
--- /dev/null
+++ b/node_modules/ws/package.json
@@ -0,0 +1,48 @@
+{
+ "author": {
+ "name": "Einar Otto Stangvik",
+ "email": "einaros@gmail.com",
+ "url": "http://2x.io"
+ },
+ "name": "ws",
+ "description": "simple to use, blazing fast and thoroughly tested websocket client, server and console for node.js, up-to-date against RFC-6455",
+ "version": "0.4.27",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/einaros/ws.git"
+ },
+ "bin": {
+ "wscat": "./bin/wscat"
+ },
+ "scripts": {
+ "test": "make test",
+ "install": "(node-gyp rebuild 2> builderror.log) || (exit 0)"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ },
+ "dependencies": {
+ "commander": "~0.6.1",
+ "tinycolor": "0.x",
+ "options": ">=0.0.5"
+ },
+ "devDependencies": {
+ "mocha": "~1.2.1",
+ "should": "0.6.x",
+ "expect.js": "0.1.x",
+ "benchmark": "0.3.x",
+ "ansi": "latest"
+ },
+ "browser": "./lib/browser.js",
+ "readme": "[![Build Status](https://secure.travis-ci.org/einaros/ws.png)](http://travis-ci.org/einaros/ws)\n\n# ws: a node.js websocket library #\n\n`ws` is a simple to use websocket implementation, up-to-date against RFC-6455, and [probably the fastest WebSocket library for node.js](http://hobbycoding.posterous.com/the-fastest-websocket-module-for-nodejs).\n\nPasses the quite extensive Autobahn test suite. See http://einaros.github.com/ws for the full reports.\n\nComes with a command line utility, `wscat`, which can either act as a server (--listen), or client (--connect); Use it to debug simple websocket services.\n\n## Protocol support ##\n\n* **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.)\n* **HyBi drafts 07-12** (Use the option `protocolVersion: 8`, or argument `-p 8` for wscat)\n* **HyBi drafts 13-17** (Current default, alternatively option `protocolVersion: 13`, or argument `-p 13` for wscat)\n\n_See the echo.websocket.org example below for how to use the `protocolVersion` option._\n\n## Usage ##\n\n### Installing ###\n\n`npm install ws`\n\n### Sending and receiving text data ###\n\n```js\nvar WebSocket = require('ws');\nvar ws = new WebSocket('ws://www.host.com/path');\nws.on('open', function() {\n ws.send('something');\n});\nws.on('message', function(data, flags) {\n // flags.binary will be set if a binary data is received\n // flags.masked will be set if the data was masked\n});\n```\n\n### Sending binary data ###\n\n```js\nvar WebSocket = require('ws');\nvar ws = new WebSocket('ws://www.host.com/path');\nws.on('open', function() {\n var array = new Float32Array(5);\n for (var i = 0; i < array.length; ++i) array[i] = i / 2;\n ws.send(array, {binary: true, mask: true});\n});\n```\n\nSetting `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.\n\n### Server example ###\n\n```js\nvar WebSocketServer = require('ws').Server\n , wss = new WebSocketServer({port: 8080});\nwss.on('connection', function(ws) {\n ws.on('message', function(message) {\n console.log('received: %s', message);\n });\n ws.send('something');\n});\n```\n\n### Error handling best practices ###\n\n```js\n// If the WebSocket is closed before the following send is attempted\nws.send('something');\n\n// Errors (both immediate and async write errors) can be detected in an optional callback.\n// The callback is also the only way of being notified that data has actually been sent.\nws.send('something', function(error) {\n // if error is null, the send has been completed,\n // otherwise the error object will indicate what failed.\n});\n\n// Immediate errors can also be handled with try/catch-blocks, but **note**\n// that since sends are inherently asynchronous, socket write failures will *not*\n// be captured when this technique is used.\ntry {\n ws.send('something');\n}\ncatch (e) {\n // handle error\n}\n```\n\n### echo.websocket.org demo ###\n\n```js\nvar WebSocket = require('ws');\nvar ws = new WebSocket('ws://echo.websocket.org/', {protocolVersion: 8, origin: 'http://websocket.org'});\nws.on('open', function() {\n console.log('connected');\n ws.send(Date.now().toString(), {mask: true});\n});\nws.on('close', function() {\n console.log('disconnected');\n});\nws.on('message', function(data, flags) {\n console.log('Roundtrip time: ' + (Date.now() - parseInt(data)) + 'ms', flags);\n setTimeout(function() {\n ws.send(Date.now().toString(), {mask: true});\n }, 500);\n});\n```\n\n### wscat against echo.websocket.org ###\n\n $ npm install -g ws\n $ wscat -c ws://echo.websocket.org -p 8\n connected (press CTRL+C to quit)\n > hi there\n < hi there\n > are you a happy parrot?\n < are you a happy parrot?\n\n### Other examples ###\n\nFor a full example with a browser client communicating with a ws server, see the examples folder.\n\nNote 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.\n\nOtherwise, see the test cases.\n\n### Running the tests ###\n\n`make test`\n\n## API Docs ##\n\nSee the doc/ directory for Node.js-like docs for the ws classes.\n\n## License ##\n\n(The MIT License)\n\nCopyright (c) 2011 Einar Otto Stangvik &lt;einaros@gmail.com&gt;\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n",
+ "readmeFilename": "README.md",
+ "bugs": {
+ "url": "https://github.com/einaros/ws/issues"
+ },
+ "_id": "ws@0.4.27",
+ "dist": {
+ "shasum": "be50a5b1f71c0f2aa3e8ff49f4cf0a45f2d59811"
+ },
+ "_from": "ws@",
+ "_resolved": "https://registry.npmjs.org/ws/-/ws-0.4.27.tgz"
+}
diff --git a/node_modules/ws/src/bufferutil.cc b/node_modules/ws/src/bufferutil.cc
new file mode 100644
index 0000000..866c28b
--- /dev/null
+++ b/node_modules/ws/src/bufferutil.cc
@@ -0,0 +1,115 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
+ * MIT Licensed
+ */
+
+#include <v8.h>
+#include <node.h>
+#include <node_buffer.h>
+#include <node_object_wrap.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <stdio.h>
+
+using namespace v8;
+using namespace node;
+
+class BufferUtil : public ObjectWrap
+{
+public:
+
+ static void Initialize(v8::Handle<v8::Object> target)
+ {
+ HandleScope scope;
+ Local<FunctionTemplate> t = FunctionTemplate::New(New);
+ t->InstanceTemplate()->SetInternalFieldCount(1);
+ NODE_SET_METHOD(t->GetFunction(), "unmask", BufferUtil::Unmask);
+ NODE_SET_METHOD(t->GetFunction(), "mask", BufferUtil::Mask);
+ NODE_SET_METHOD(t->GetFunction(), "merge", BufferUtil::Merge);
+ target->Set(String::NewSymbol("BufferUtil"), t->GetFunction());
+ }
+
+protected:
+
+ static Handle<Value> New(const Arguments& args)
+ {
+ HandleScope scope;
+ BufferUtil* bufferUtil = new BufferUtil();
+ bufferUtil->Wrap(args.This());
+ return args.This();
+ }
+
+ static Handle<Value> Merge(const Arguments& args)
+ {
+ HandleScope scope;
+ Local<Object> bufferObj = args[0]->ToObject();
+ char* buffer = Buffer::Data(bufferObj);
+ Local<Array> array = Local<Array>::Cast(args[1]);
+ unsigned int arrayLength = array->Length();
+ unsigned int offset = 0;
+ unsigned int i;
+ for (i = 0; i < arrayLength; ++i) {
+ Local<Object> src = array->Get(i)->ToObject();
+ unsigned int length = Buffer::Length(src);
+ memcpy(buffer + offset, Buffer::Data(src), length);
+ offset += length;
+ }
+ return scope.Close(True());
+ }
+
+ static Handle<Value> Unmask(const Arguments& args)
+ {
+ HandleScope scope;
+ Local<Object> buffer_obj = args[0]->ToObject();
+ unsigned int length = Buffer::Length(buffer_obj);
+ Local<Object> mask_obj = args[1]->ToObject();
+ unsigned int *mask = (unsigned int*)Buffer::Data(mask_obj);
+ unsigned int* from = (unsigned int*)Buffer::Data(buffer_obj);
+ unsigned int len32 = length / 4;
+ unsigned int i;
+ for (i = 0; i < len32; ++i) *(from + i) ^= *mask;
+ from += i;
+ switch (length % 4) {
+ case 3: *((unsigned char*)from+2) = *((unsigned char*)from+2) ^ ((unsigned char*)mask)[2];
+ case 2: *((unsigned char*)from+1) = *((unsigned char*)from+1) ^ ((unsigned char*)mask)[1];
+ case 1: *((unsigned char*)from ) = *((unsigned char*)from ) ^ ((unsigned char*)mask)[0];
+ case 0:;
+ }
+ return True();
+ }
+
+ static Handle<Value> Mask(const Arguments& args)
+ {
+ HandleScope scope;
+ Local<Object> buffer_obj = args[0]->ToObject();
+ Local<Object> mask_obj = args[1]->ToObject();
+ unsigned int *mask = (unsigned int*)Buffer::Data(mask_obj);
+ Local<Object> output_obj = args[2]->ToObject();
+ unsigned int dataOffset = args[3]->Int32Value();
+ unsigned int length = args[4]->Int32Value();
+ unsigned int* to = (unsigned int*)(Buffer::Data(output_obj) + dataOffset);
+ unsigned int* from = (unsigned int*)Buffer::Data(buffer_obj);
+ unsigned int len32 = length / 4;
+ unsigned int i;
+ for (i = 0; i < len32; ++i) *(to + i) = *(from + i) ^ *mask;
+ to += i;
+ from += i;
+ switch (length % 4) {
+ case 3: *((unsigned char*)to+2) = *((unsigned char*)from+2) ^ *((unsigned char*)mask+2);
+ case 2: *((unsigned char*)to+1) = *((unsigned char*)from+1) ^ *((unsigned char*)mask+1);
+ case 1: *((unsigned char*)to ) = *((unsigned char*)from ) ^ *((unsigned char*)mask);
+ case 0:;
+ }
+ return True();
+ }
+};
+
+extern "C" void init (Handle<Object> target)
+{
+ HandleScope scope;
+ BufferUtil::Initialize(target);
+}
+
+NODE_MODULE(bufferutil, init)
diff --git a/node_modules/ws/src/validation.cc b/node_modules/ws/src/validation.cc
new file mode 100644
index 0000000..b900164
--- /dev/null
+++ b/node_modules/ws/src/validation.cc
@@ -0,0 +1,143 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
+ * MIT Licensed
+ */
+
+#include <v8.h>
+#include <node.h>
+#include <node_buffer.h>
+#include <node_object_wrap.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include <stdio.h>
+
+using namespace v8;
+using namespace node;
+
+#define UNI_SUR_HIGH_START (uint32_t) 0xD800
+#define UNI_SUR_LOW_END (uint32_t) 0xDFFF
+#define UNI_REPLACEMENT_CHAR (uint32_t) 0x0000FFFD
+#define UNI_MAX_LEGAL_UTF32 (uint32_t) 0x0010FFFF
+
+static const uint8_t trailingBytesForUTF8[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+};
+
+static const uint32_t offsetsFromUTF8[6] = {
+ 0x00000000, 0x00003080, 0x000E2080,
+ 0x03C82080, 0xFA082080, 0x82082080
+};
+
+static int isLegalUTF8(const uint8_t *source, const int length)
+{
+ uint8_t a;
+ const uint8_t *srcptr = source+length;
+ switch (length) {
+ default: return 0;
+ /* Everything else falls through when "true"... */
+ /* RFC3629 makes 5 & 6 bytes UTF-8 illegal
+ case 6: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
+ case 5: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0; */
+ case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
+ case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
+ case 2: if ((a = (*--srcptr)) > 0xBF) return 0;
+ switch (*source) {
+ /* no fall-through in this inner switch */
+ case 0xE0: if (a < 0xA0) return 0; break;
+ case 0xED: if (a > 0x9F) return 0; break;
+ case 0xF0: if (a < 0x90) return 0; break;
+ case 0xF4: if (a > 0x8F) return 0; break;
+ default: if (a < 0x80) return 0;
+ }
+
+ case 1: if (*source >= 0x80 && *source < 0xC2) return 0;
+ }
+ if (*source > 0xF4) return 0;
+ return 1;
+}
+
+int is_valid_utf8 (size_t len, char *value)
+{
+ /* is the string valid UTF-8? */
+ for (unsigned int i = 0; i < len; i++) {
+ uint32_t ch = 0;
+ uint8_t extrabytes = trailingBytesForUTF8[(uint8_t) value[i]];
+
+ if (extrabytes + i >= len)
+ return 0;
+
+ if (isLegalUTF8 ((uint8_t *) (value + i), extrabytes + 1) == 0) return 0;
+
+ switch (extrabytes) {
+ case 5 : ch += (uint8_t) value[i++]; ch <<= 6;
+ case 4 : ch += (uint8_t) value[i++]; ch <<= 6;
+ case 3 : ch += (uint8_t) value[i++]; ch <<= 6;
+ case 2 : ch += (uint8_t) value[i++]; ch <<= 6;
+ case 1 : ch += (uint8_t) value[i++]; ch <<= 6;
+ case 0 : ch += (uint8_t) value[i];
+ }
+
+ ch -= offsetsFromUTF8[extrabytes];
+
+ if (ch <= UNI_MAX_LEGAL_UTF32) {
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END)
+ return 0;
+ } else {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+class Validation : public ObjectWrap
+{
+public:
+
+ static void Initialize(v8::Handle<v8::Object> target)
+ {
+ HandleScope scope;
+ Local<FunctionTemplate> t = FunctionTemplate::New(New);
+ t->InstanceTemplate()->SetInternalFieldCount(1);
+ NODE_SET_METHOD(t->GetFunction(), "isValidUTF8", Validation::IsValidUTF8);
+ target->Set(String::NewSymbol("Validation"), t->GetFunction());
+ }
+
+protected:
+
+ static Handle<Value> New(const Arguments& args)
+ {
+ HandleScope scope;
+ Validation* validation = new Validation();
+ validation->Wrap(args.This());
+ return args.This();
+ }
+
+ static Handle<Value> IsValidUTF8(const Arguments& args)
+ {
+ HandleScope scope;
+ if (!Buffer::HasInstance(args[0])) {
+ return ThrowException(Exception::Error(String::New("First argument needs to be a buffer")));
+ }
+ Local<Object> buffer_obj = args[0]->ToObject();
+ char *buffer_data = Buffer::Data(buffer_obj);
+ size_t buffer_length = Buffer::Length(buffer_obj);
+ return is_valid_utf8(buffer_length, buffer_data) == 1 ? scope.Close(True()) : scope.Close(False());
+ }
+};
+
+extern "C" void init (Handle<Object> target)
+{
+ HandleScope scope;
+ Validation::Initialize(target);
+}
+
+NODE_MODULE(validation, init)
diff --git a/node_modules/ws/test/BufferPool.test.js b/node_modules/ws/test/BufferPool.test.js
new file mode 100644
index 0000000..1ee7ff0
--- /dev/null
+++ b/node_modules/ws/test/BufferPool.test.js
@@ -0,0 +1,63 @@
+var BufferPool = require('../lib/BufferPool');
+require('should');
+
+describe('BufferPool', function() {
+ describe('#ctor', function() {
+ it('allocates pool', function() {
+ var db = new BufferPool(1000);
+ db.size.should.eql(1000);
+ });
+ });
+ describe('#get', function() {
+ it('grows the pool if necessary', function() {
+ var db = new BufferPool(1000);
+ var buf = db.get(2000);
+ db.size.should.be.above(1000);
+ db.used.should.eql(2000);
+ buf.length.should.eql(2000);
+ });
+ it('grows the pool after the first call, if necessary', function() {
+ var db = new BufferPool(1000);
+ var buf = db.get(1000);
+ db.used.should.eql(1000);
+ db.size.should.eql(1000);
+ buf.length.should.eql(1000);
+ var buf2 = db.get(1000);
+ db.used.should.eql(2000);
+ db.size.should.be.above(1000);
+ buf2.length.should.eql(1000);
+ });
+ it('grows the pool according to the growStrategy if necessary', function() {
+ var db = new BufferPool(1000, function(db, length) {
+ return db.size + 2345;
+ });
+ var buf = db.get(2000);
+ db.size.should.eql(3345);
+ buf.length.should.eql(2000);
+ });
+ it('doesnt grow the pool if theres enough room available', function() {
+ var db = new BufferPool(1000);
+ var buf = db.get(1000);
+ db.size.should.eql(1000);
+ buf.length.should.eql(1000);
+ });
+ });
+ describe('#reset', function() {
+ it('shinks the pool', function() {
+ var db = new BufferPool(1000);
+ var buf = db.get(2000);
+ db.reset(true);
+ db.size.should.eql(1000);
+ });
+ it('shrinks the pool according to the shrinkStrategy', function() {
+ var db = new BufferPool(1000, function(db, length) {
+ return db.used + length;
+ }, function(db) {
+ return 0;
+ });
+ var buf = db.get(2000);
+ db.reset(true);
+ db.size.should.eql(0);
+ });
+ });
+});
diff --git a/node_modules/ws/test/Receiver.hixie.test.js b/node_modules/ws/test/Receiver.hixie.test.js
new file mode 100644
index 0000000..043d3bc
--- /dev/null
+++ b/node_modules/ws/test/Receiver.hixie.test.js
@@ -0,0 +1,158 @@
+var assert = require('assert')
+ , expect = require('expect.js')
+ , Receiver = require('../lib/Receiver.hixie');
+require('./hybi-common');
+
+describe('Receiver', function() {
+ it('can parse text message', function() {
+ var p = new Receiver();
+ var packet = '00 48 65 6c 6c 6f ff';
+
+ var gotData = false;
+ p.ontext = function(data) {
+ gotData = true;
+ assert.equal('Hello', data);
+ };
+
+ p.add(getBufferFromHexString(packet));
+ expect(gotData).to.equal(true);
+ });
+
+ it('can parse multiple text messages', function() {
+ var p = new Receiver();
+ var packet = '00 48 65 6c 6c 6f ff 00 48 65 6c 6c 6f ff';
+
+ var gotData = false;
+ var messages = [];
+ p.ontext = function(data) {
+ gotData = true;
+ messages.push(data);
+ };
+
+ p.add(getBufferFromHexString(packet));
+ expect(gotData).to.equal(true);
+ for (var i = 0; i < 2; ++i) {
+ expect(messages[i]).to.equal('Hello');
+ }
+ });
+
+ it('can parse empty message', function() {
+ var p = new Receiver();
+ var packet = '00 ff';
+
+ var gotData = false;
+ p.ontext = function(data) {
+ gotData = true;
+ assert.equal('', data);
+ };
+
+ p.add(getBufferFromHexString(packet));
+ expect(gotData).to.equal(true);
+ });
+
+ it('can parse text messages delivered over multiple frames', function() {
+ var p = new Receiver();
+ var packets = [
+ '00 48',
+ '65 6c 6c',
+ '6f ff 00 48',
+ '65',
+ '6c 6c 6f',
+ 'ff'
+ ];
+
+ var gotData = false;
+ var messages = [];
+ p.ontext = function(data) {
+ gotData = true;
+ messages.push(data);
+ };
+
+ for (var i = 0; i < packets.length; ++i) {
+ p.add(getBufferFromHexString(packets[i]));
+ }
+ expect(gotData).to.equal(true);
+ for (var i = 0; i < 2; ++i) {
+ expect(messages[i]).to.equal('Hello');
+ }
+ });
+
+ it('emits an error if a payload doesnt start with 0x00', function() {
+ var p = new Receiver();
+ var packets = [
+ '00 6c ff',
+ '00 6c ff ff',
+ 'ff 00 6c ff 00 6c ff',
+ '00',
+ '6c 6c 6f',
+ 'ff'
+ ];
+
+ var gotData = false;
+ var gotError = false;
+ var messages = [];
+ p.ontext = function(data) {
+ gotData = true;
+ messages.push(data);
+ };
+ p.onerror = function(reason, code) {
+ gotError = code == true;
+ };
+
+ for (var i = 0; i < packets.length && !gotError; ++i) {
+ p.add(getBufferFromHexString(packets[i]));
+ }
+ expect(gotError).to.equal(true);
+ expect(messages[0]).to.equal('l');
+ expect(messages[1]).to.equal('l');
+ expect(messages.length).to.equal(2);
+ });
+
+ it('can parse close messages', function() {
+ var p = new Receiver();
+ var packets = [
+ 'ff 00'
+ ];
+
+ var gotClose = false;
+ var gotError = false;
+ p.onclose = function() {
+ gotClose = true;
+ };
+ p.onerror = function(reason, code) {
+ gotError = code == true;
+ };
+
+ for (var i = 0; i < packets.length && !gotError; ++i) {
+ p.add(getBufferFromHexString(packets[i]));
+ }
+ expect(gotClose).to.equal(true);
+ expect(gotError).to.equal(false);
+ });
+
+ it('can parse binary messages delivered over multiple frames', function() {
+ var p = new Receiver();
+ var packets = [
+ '80 05 48',
+ '65 6c 6c',
+ '6f 80 80 05 48',
+ '65',
+ '6c 6c 6f'
+ ];
+
+ var gotData = false;
+ var messages = [];
+ p.ontext = function(data) {
+ gotData = true;
+ messages.push(data);
+ };
+
+ for (var i = 0; i < packets.length; ++i) {
+ p.add(getBufferFromHexString(packets[i]));
+ }
+ expect(gotData).to.equal(true);
+ for (var i = 0; i < 2; ++i) {
+ expect(messages[i]).to.equal('Hello');
+ }
+ });
+});
diff --git a/node_modules/ws/test/Receiver.test.js b/node_modules/ws/test/Receiver.test.js
new file mode 100644
index 0000000..b0b5c0a
--- /dev/null
+++ b/node_modules/ws/test/Receiver.test.js
@@ -0,0 +1,255 @@
+var assert = require('assert')
+ , Receiver = require('../lib/Receiver');
+require('should');
+require('./hybi-common');
+
+describe('Receiver', function() {
+ it('can parse unmasked text message', function() {
+ var p = new Receiver();
+ var packet = '81 05 48 65 6c 6c 6f';
+
+ var gotData = false;
+ p.ontext = function(data) {
+ gotData = true;
+ assert.equal('Hello', data);
+ };
+
+ p.add(getBufferFromHexString(packet));
+ gotData.should.be.ok;
+ });
+ it('can parse close message', function() {
+ var p = new Receiver();
+ var packet = '88 00';
+
+ var gotClose = false;
+ p.onclose = function(data) {
+ gotClose = true;
+ };
+
+ p.add(getBufferFromHexString(packet));
+ gotClose.should.be.ok;
+ });
+ it('can parse masked text message', function() {
+ var p = new Receiver();
+ var packet = '81 93 34 83 a8 68 01 b9 92 52 4f a1 c6 09 59 e6 8a 52 16 e6 cb 00 5b a1 d5';
+
+ var gotData = false;
+ p.ontext = function(data) {
+ gotData = true;
+ assert.equal('5:::{"name":"echo"}', data);
+ };
+
+ p.add(getBufferFromHexString(packet));
+ gotData.should.be.ok;
+ });
+ it('can parse a masked text message longer than 125 bytes', function() {
+ var p = new Receiver();
+ var message = 'A';
+ for (var i = 0; i < 300; ++i) message += (i % 5).toString();
+ var packet = '81 FE ' + pack(4, message.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68'));
+
+ var gotData = false;
+ p.ontext = function(data) {
+ gotData = true;
+ assert.equal(message, data);
+ };
+
+ p.add(getBufferFromHexString(packet));
+ gotData.should.be.ok;
+ });
+ it('can parse a really long masked text message', function() {
+ var p = new Receiver();
+ var message = 'A';
+ for (var i = 0; i < 64*1024; ++i) message += (i % 5).toString();
+ var packet = '81 FF ' + pack(16, message.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68'));
+
+ var gotData = false;
+ p.ontext = function(data) {
+ gotData = true;
+ assert.equal(message, data);
+ };
+
+ p.add(getBufferFromHexString(packet));
+ gotData.should.be.ok;
+ });
+ it('can parse a fragmented masked text message of 300 bytes', function() {
+ var p = new Receiver();
+ var message = 'A';
+ for (var i = 0; i < 300; ++i) message += (i % 5).toString();
+ var msgpiece1 = message.substr(0, 150);
+ var msgpiece2 = message.substr(150);
+ var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68'));
+ var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68'));
+
+ var gotData = false;
+ p.ontext = function(data) {
+ gotData = true;
+ assert.equal(message, data);
+ };
+
+ p.add(getBufferFromHexString(packet1));
+ p.add(getBufferFromHexString(packet2));
+ gotData.should.be.ok;
+ });
+ it('can parse a ping message', function() {
+ var p = new Receiver();
+ var message = 'Hello';
+ var packet = '89 ' + getHybiLengthAsHexString(message.length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68'));
+
+ var gotPing = false;
+ p.onping = function(data) {
+ gotPing = true;
+ assert.equal(message, data);
+ };
+
+ p.add(getBufferFromHexString(packet));
+ gotPing.should.be.ok;
+ });
+ it('can parse a ping with no data', function() {
+ var p = new Receiver();
+ var packet = '89 00';
+
+ var gotPing = false;
+ p.onping = function(data) {
+ gotPing = true;
+ };
+
+ p.add(getBufferFromHexString(packet));
+ gotPing.should.be.ok;
+ });
+ it('can parse a fragmented masked text message of 300 bytes with a ping in the middle', function() {
+ var p = new Receiver();
+ var message = 'A';
+ for (var i = 0; i < 300; ++i) message += (i % 5).toString();
+
+ var msgpiece1 = message.substr(0, 150);
+ var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68'));
+
+ var pingMessage = 'Hello';
+ var pingPacket = '89 ' + getHybiLengthAsHexString(pingMessage.length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(pingMessage, '34 83 a8 68'));
+
+ var msgpiece2 = message.substr(150);
+ var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68'));
+
+ var gotData = false;
+ p.ontext = function(data) {
+ gotData = true;
+ assert.equal(message, data);
+ };
+ var gotPing = false;
+ p.onping = function(data) {
+ gotPing = true;
+ assert.equal(pingMessage, data);
+ };
+
+ p.add(getBufferFromHexString(packet1));
+ p.add(getBufferFromHexString(pingPacket));
+ p.add(getBufferFromHexString(packet2));
+ gotData.should.be.ok;
+ gotPing.should.be.ok;
+ });
+ it('can parse a fragmented masked text message of 300 bytes with a ping in the middle, which is delievered over sevaral tcp packets', function() {
+ var p = new Receiver();
+ var message = 'A';
+ for (var i = 0; i < 300; ++i) message += (i % 5).toString();
+
+ var msgpiece1 = message.substr(0, 150);
+ var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68'));
+
+ var pingMessage = 'Hello';
+ var pingPacket = '89 ' + getHybiLengthAsHexString(pingMessage.length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(pingMessage, '34 83 a8 68'));
+
+ var msgpiece2 = message.substr(150);
+ var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68'));
+
+ var gotData = false;
+ p.ontext = function(data) {
+ gotData = true;
+ assert.equal(message, data);
+ };
+ var gotPing = false;
+ p.onping = function(data) {
+ gotPing = true;
+ assert.equal(pingMessage, data);
+ };
+
+ var buffers = [];
+ buffers = buffers.concat(splitBuffer(getBufferFromHexString(packet1)));
+ buffers = buffers.concat(splitBuffer(getBufferFromHexString(pingPacket)));
+ buffers = buffers.concat(splitBuffer(getBufferFromHexString(packet2)));
+ for (var i = 0; i < buffers.length; ++i) {
+ p.add(buffers[i]);
+ }
+ gotData.should.be.ok;
+ gotPing.should.be.ok;
+ });
+ it('can parse a 100 byte long masked binary message', function() {
+ var p = new Receiver();
+ var length = 100;
+ var message = new Buffer(length);
+ for (var i = 0; i < length; ++i) message[i] = i % 256;
+ var originalMessage = getHexStringFromBuffer(message);
+ var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68'));
+
+ var gotData = false;
+ p.onbinary = function(data) {
+ gotData = true;
+ assert.equal(originalMessage, getHexStringFromBuffer(data));
+ };
+
+ p.add(getBufferFromHexString(packet));
+ gotData.should.be.ok;
+ });
+ it('can parse a 256 byte long masked binary message', function() {
+ var p = new Receiver();
+ var length = 256;
+ var message = new Buffer(length);
+ for (var i = 0; i < length; ++i) message[i] = i % 256;
+ var originalMessage = getHexStringFromBuffer(message);
+ var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68'));
+
+ var gotData = false;
+ p.onbinary = function(data) {
+ gotData = true;
+ assert.equal(originalMessage, getHexStringFromBuffer(data));
+ };
+
+ p.add(getBufferFromHexString(packet));
+ gotData.should.be.ok;
+ });
+ it('can parse a 200kb long masked binary message', function() {
+ var p = new Receiver();
+ var length = 200 * 1024;
+ var message = new Buffer(length);
+ for (var i = 0; i < length; ++i) message[i] = i % 256;
+ var originalMessage = getHexStringFromBuffer(message);
+ var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68'));
+
+ var gotData = false;
+ p.onbinary = function(data) {
+ gotData = true;
+ assert.equal(originalMessage, getHexStringFromBuffer(data));
+ };
+
+ p.add(getBufferFromHexString(packet));
+ gotData.should.be.ok;
+ });
+ it('can parse a 200kb long unmasked binary message', function() {
+ var p = new Receiver();
+ var length = 200 * 1024;
+ var message = new Buffer(length);
+ for (var i = 0; i < length; ++i) message[i] = i % 256;
+ var originalMessage = getHexStringFromBuffer(message);
+ var packet = '82 ' + getHybiLengthAsHexString(length, false) + ' ' + getHexStringFromBuffer(message);
+
+ var gotData = false;
+ p.onbinary = function(data) {
+ gotData = true;
+ assert.equal(originalMessage, getHexStringFromBuffer(data));
+ };
+
+ p.add(getBufferFromHexString(packet));
+ gotData.should.be.ok;
+ });
+});
+
diff --git a/node_modules/ws/test/Sender.hixie.test.js b/node_modules/ws/test/Sender.hixie.test.js
new file mode 100644
index 0000000..783f892
--- /dev/null
+++ b/node_modules/ws/test/Sender.hixie.test.js
@@ -0,0 +1,134 @@
+var assert = require('assert')
+ , Sender = require('../lib/Sender.hixie');
+require('should');
+require('./hybi-common');
+
+describe('Sender', function() {
+ describe('#send', function() {
+ it('frames and sends a text message', function(done) {
+ var message = 'Hello world';
+ var received;
+ var socket = {
+ write: function(data, encoding, cb) {
+ received = data;
+ process.nextTick(cb);
+ }
+ };
+ var sender = new Sender(socket, {});
+ sender.send(message, {}, function() {
+ received.toString('utf8').should.eql('\u0000' + message + '\ufffd');
+ done();
+ });
+ });
+
+ it('frames and sends an empty message', function(done) {
+ var socket = {
+ write: function(data, encoding, cb) {
+ done();
+ }
+ };
+ var sender = new Sender(socket, {});
+ sender.send('', {}, function() {});
+ });
+
+ it('frames and sends a buffer', function(done) {
+ var received;
+ var socket = {
+ write: function(data, encoding, cb) {
+ received = data;
+ process.nextTick(cb);
+ }
+ };
+ var sender = new Sender(socket, {});
+ sender.send(new Buffer('foobar'), {}, function() {
+ received.toString('utf8').should.eql('\u0000foobar\ufffd');
+ done();
+ });
+ });
+
+ it('frames and sends a binary message', function(done) {
+ var message = 'Hello world';
+ var received;
+ var socket = {
+ write: function(data, encoding, cb) {
+ received = data;
+ process.nextTick(cb);
+ }
+ };
+ var sender = new Sender(socket, {});
+ sender.send(message, {binary: true}, function() {
+ received.toString('hex').should.eql(
+ // 0x80 0x0b H e l l o <sp> w o r l d
+ '800b48656c6c6f20776f726c64');
+ done();
+ });
+ });
+/*
+ it('throws an exception for binary data', function(done) {
+ var socket = {
+ write: function(data, encoding, cb) {
+ process.nextTick(cb);
+ }
+ };
+ var sender = new Sender(socket, {});
+ sender.on('error', function() {
+ done();
+ });
+ sender.send(new Buffer(100), {binary: true}, function() {});
+ });
+*/
+ it('can fauxe stream data', function(done) {
+ var received = [];
+ var socket = {
+ write: function(data, encoding, cb) {
+ received.push(data);
+ process.nextTick(cb);
+ }
+ };
+ var sender = new Sender(socket, {});
+ sender.send(new Buffer('foobar'), { fin: false }, function() {});
+ sender.send('bazbar', { fin: false }, function() {});
+ sender.send(new Buffer('end'), { fin: true }, function() {
+ received[0].toString('utf8').should.eql('\u0000foobar');
+ received[1].toString('utf8').should.eql('bazbar');
+ received[2].toString('utf8').should.eql('end\ufffd');
+ done();
+ });
+ });
+ });
+
+ describe('#close', function() {
+ it('sends a hixie close frame', function(done) {
+ var received;
+ var socket = {
+ write: function(data, encoding, cb) {
+ received = data;
+ process.nextTick(cb);
+ }
+ };
+ var sender = new Sender(socket, {});
+ sender.close(null, null, null, function() {
+ received.toString('utf8').should.eql('\ufffd\u0000');
+ done();
+ });
+ });
+
+ it('sends a message end marker if fauxe streaming has started, before hixie close frame', function(done) {
+ var received = [];
+ var socket = {
+ write: function(data, encoding, cb) {
+ received.push(data);
+ if (cb) process.nextTick(cb);
+ }
+ };
+ var sender = new Sender(socket, {});
+ sender.send(new Buffer('foobar'), { fin: false }, function() {});
+ sender.close(null, null, null, function() {
+ received[0].toString('utf8').should.eql('\u0000foobar');
+ received[1].toString('utf8').should.eql('\ufffd');
+ received[2].toString('utf8').should.eql('\ufffd\u0000');
+ done();
+ });
+ });
+ });
+});
diff --git a/node_modules/ws/test/Sender.test.js b/node_modules/ws/test/Sender.test.js
new file mode 100644
index 0000000..43b4864
--- /dev/null
+++ b/node_modules/ws/test/Sender.test.js
@@ -0,0 +1,24 @@
+var Sender = require('../lib/Sender');
+require('should');
+
+describe('Sender', function() {
+ describe('#frameAndSend', function() {
+ it('does not modify a masked binary buffer', function() {
+ var sender = new Sender({ write: function() {} });
+ var buf = new Buffer([1, 2, 3, 4, 5]);
+ sender.frameAndSend(2, buf, true, true);
+ buf[0].should.eql(1);
+ buf[1].should.eql(2);
+ buf[2].should.eql(3);
+ buf[3].should.eql(4);
+ buf[4].should.eql(5);
+ });
+
+ it('does not modify a masked text buffer', function() {
+ var sender = new Sender({ write: function() {} });
+ var text = 'hi there';
+ sender.frameAndSend(1, text, true, true);
+ text.should.eql('hi there');
+ });
+ });
+});
diff --git a/node_modules/ws/test/Validation.test.js b/node_modules/ws/test/Validation.test.js
new file mode 100644
index 0000000..37c3399
--- /dev/null
+++ b/node_modules/ws/test/Validation.test.js
@@ -0,0 +1,23 @@
+var Validation = require('../lib/Validation').Validation;
+require('should');
+
+describe('Validation', function() {
+ describe('isValidUTF8', function() {
+ it('should return true for a valid utf8 string', function() {
+ var validBuffer = new Buffer('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque gravida mattis rhoncus. Donec iaculis, metus quis varius accumsan, erat mauris condimentum diam, et egestas erat enim ut ligula. Praesent sollicitudin tellus eget dolor euismod euismod. Nullam ac augue nec neque varius luctus. Curabitur elit mi, consequat ultricies adipiscing mollis, scelerisque in erat. Phasellus facilisis fermentum ullamcorper. Nulla et sem eu arcu pharetra pellentesque. Praesent consectetur tempor justo, vel iaculis dui ullamcorper sit amet. Integer tristique viverra ullamcorper. Vivamus laoreet, nulla eget suscipit eleifend, lacus lectus feugiat libero, non fermentum erat nisi at risus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut pulvinar dignissim tellus, eu dignissim lorem vulputate quis. Morbi ut pulvinar augue.');
+ Validation.isValidUTF8(validBuffer).should.be.ok;
+ });
+ it('should return false for an erroneous string', function() {
+ var invalidBuffer = new Buffer([0xce, 0xba, 0xe1, 0xbd, 0xb9, 0xcf, 0x83, 0xce, 0xbc, 0xce, 0xb5, 0xed, 0xa0, 0x80, 0x65, 0x64, 0x69, 0x74, 0x65, 0x64]);
+ Validation.isValidUTF8(invalidBuffer).should.not.be.ok;
+ });
+ it('should return true for valid cases from the autobahn test suite', function() {
+ Validation.isValidUTF8(new Buffer('\xf0\x90\x80\x80')).should.be.ok;
+ Validation.isValidUTF8(new Buffer([0xf0, 0x90, 0x80, 0x80])).should.be.ok;
+ });
+ it('should return false for erroneous autobahn strings', function() {
+ Validation.isValidUTF8(new Buffer([0xce, 0xba, 0xe1, 0xbd])).should.not.be.ok;
+ });
+ });
+});
+
diff --git a/node_modules/ws/test/WebSocket.integration.js b/node_modules/ws/test/WebSocket.integration.js
new file mode 100644
index 0000000..51a7e3a
--- /dev/null
+++ b/node_modules/ws/test/WebSocket.integration.js
@@ -0,0 +1,42 @@
+var assert = require('assert')
+ , WebSocket = require('../')
+ , server = require('./testserver');
+
+var port = 20000;
+
+function getArrayBuffer(buf) {
+ var l = buf.length;
+ var arrayBuf = new ArrayBuffer(l);
+ for (var i = 0; i < l; ++i) {
+ arrayBuf[i] = buf[i];
+ }
+ return arrayBuf;
+}
+
+function areArraysEqual(x, y) {
+ if (x.length != y.length) return false;
+ for (var i = 0, l = x.length; i < l; ++i) {
+ if (x[i] !== y[i]) return false;
+ }
+ return true;
+}
+
+describe('WebSocket', function() {
+ it('communicates successfully with echo service', function(done) {
+ var ws = new WebSocket('ws://echo.websocket.org', {protocolVersion: 8, origin: 'http://websocket.org'});
+ var str = Date.now().toString();
+ var dataReceived = false;
+ ws.on('open', function() {
+ ws.send(str, {mask: true});
+ });
+ ws.on('close', function() {
+ assert.equal(true, dataReceived);
+ done();
+ });
+ ws.on('message', function(data, flags) {
+ assert.equal(str, data);
+ ws.terminate();
+ dataReceived = true;
+ });
+ });
+}); \ No newline at end of file
diff --git a/node_modules/ws/test/WebSocket.test.js b/node_modules/ws/test/WebSocket.test.js
new file mode 100644
index 0000000..93e95da
--- /dev/null
+++ b/node_modules/ws/test/WebSocket.test.js
@@ -0,0 +1,1609 @@
+var assert = require('assert')
+ , https = require('https')
+ , http = require('http')
+ , should = require('should')
+ , WebSocket = require('../')
+ , WebSocketServer = require('../').Server
+ , fs = require('fs')
+ , server = require('./testserver')
+ , crypto = require('crypto');
+
+var port = 20000;
+
+function getArrayBuffer(buf) {
+ var l = buf.length;
+ var arrayBuf = new ArrayBuffer(l);
+ for (var i = 0; i < l; ++i) {
+ arrayBuf[i] = buf[i];
+ }
+ return arrayBuf;
+}
+
+function areArraysEqual(x, y) {
+ if (x.length != y.length) return false;
+ for (var i = 0, l = x.length; i < l; ++i) {
+ if (x[i] !== y[i]) return false;
+ }
+ return true;
+}
+
+describe('WebSocket', function() {
+ describe('#ctor', function() {
+ it('throws exception for invalid url', function(done) {
+ try {
+ var ws = new WebSocket('echo.websocket.org');
+ }
+ catch (e) {
+ done();
+ }
+ });
+ });
+
+ describe('properties', function() {
+ it('#bytesReceived exposes number of bytes received', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('message', function() {
+ ws.bytesReceived.should.eql(8);
+ wss.close();
+ done();
+ });
+ });
+ wss.on('connection', function(ws) {
+ ws.send('foobar');
+ });
+ });
+
+ it('#url exposes the server url', function(done) {
+ server.createServer(++port, function(srv) {
+ var url = 'ws://localhost:' + port;
+ var ws = new WebSocket(url);
+ assert.equal(url, ws.url);
+ ws.terminate();
+ ws.on('close', function() {
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('#protocolVersion exposes the protocol version', function(done) {
+ server.createServer(++port, function(srv) {
+ var url = 'ws://localhost:' + port;
+ var ws = new WebSocket(url);
+ assert.equal(13, ws.protocolVersion);
+ ws.terminate();
+ ws.on('close', function() {
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ describe('#bufferedAmount', function() {
+ it('defaults to zero', function(done) {
+ server.createServer(++port, function(srv) {
+ var url = 'ws://localhost:' + port;
+ var ws = new WebSocket(url);
+ assert.equal(0, ws.bufferedAmount);
+ ws.terminate();
+ ws.on('close', function() {
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('stress kernel write buffer', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var ws = new WebSocket('ws://localhost:' + port);
+ });
+ wss.on('connection', function(ws) {
+ while (true) {
+ if (ws.bufferedAmount > 0) break;
+ ws.send((new Array(10000)).join('hello'));
+ }
+ ws.terminate();
+ ws.on('close', function() {
+ wss.close();
+ done();
+ });
+ });
+ });
+ });
+
+ describe('#readyState', function() {
+ it('defaults to connecting', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ assert.equal(WebSocket.CONNECTING, ws.readyState);
+ ws.terminate();
+ ws.on('close', function() {
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('set to open once connection is established', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ assert.equal(WebSocket.OPEN, ws.readyState);
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('set to closed once connection is closed', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.close(1001);
+ ws.on('close', function() {
+ assert.equal(WebSocket.CLOSED, ws.readyState);
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('set to closed once connection is terminated', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.terminate();
+ ws.on('close', function() {
+ assert.equal(WebSocket.CLOSED, ws.readyState);
+ srv.close();
+ done();
+ });
+ });
+ });
+ });
+
+ /*
+ * Ready state constants
+ */
+
+ var readyStates = {
+ CONNECTING: 0,
+ OPEN: 1,
+ CLOSING: 2,
+ CLOSED: 3
+ };
+
+ /*
+ * Ready state constant tests
+ */
+
+ Object.keys(readyStates).forEach(function(state) {
+ describe('.' + state, function() {
+ it('is enumerable property of class', function() {
+ var propertyDescripter = Object.getOwnPropertyDescriptor(WebSocket, state)
+ assert.equal(readyStates[state], propertyDescripter.value);
+ assert.equal(true, propertyDescripter.enumerable);
+ });
+ });
+ });
+
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ Object.keys(readyStates).forEach(function(state) {
+ describe('.' + state, function() {
+ it('is property of instance', function() {
+ assert.equal(readyStates[state], ws[state]);
+ });
+ });
+ });
+ });
+ });
+
+ describe('events', function() {
+ it('emits a ping event', function(done) {
+ var wss = new WebSocketServer({port: ++port});
+ wss.on('connection', function(client) {
+ client.ping();
+ });
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('ping', function() {
+ ws.terminate();
+ wss.close();
+ done();
+ });
+ });
+
+ it('emits a pong event', function(done) {
+ var wss = new WebSocketServer({port: ++port});
+ wss.on('connection', function(client) {
+ client.pong();
+ });
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('pong', function() {
+ ws.terminate();
+ wss.close();
+ done();
+ });
+ });
+ });
+
+ describe('connection establishing', function() {
+ it('can disconnect before connection is established', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.terminate();
+ ws.on('open', function() {
+ assert.fail('connect shouldnt be raised here');
+ });
+ ws.on('close', function() {
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('can close before connection is established', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.close(1001);
+ ws.on('open', function() {
+ assert.fail('connect shouldnt be raised here');
+ });
+ ws.on('close', function() {
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('invalid server key is denied', function(done) {
+ server.createServer(++port, server.handlers.invalidKey, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('error', function() {
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('close event is raised when server closes connection', function(done) {
+ server.createServer(++port, server.handlers.closeAfterConnect, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('close', function() {
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('error is emitted if server aborts connection', function(done) {
+ server.createServer(++port, server.handlers.return401, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ assert.fail('connect shouldnt be raised here');
+ });
+ ws.on('error', function() {
+ srv.close();
+ done();
+ });
+ });
+ });
+ });
+
+ describe('#pause and #resume', function() {
+ it('pauses the underlying stream', function(done) {
+ // this test is sort-of racecondition'y, since an unlikely slow connection
+ // to localhost can cause the test to succeed even when the stream pausing
+ // isn't working as intended. that is an extremely unlikely scenario, though
+ // and an acceptable risk for the test.
+ var client;
+ var serverClient;
+ var openCount = 0;
+ function onOpen() {
+ if (++openCount == 2) {
+ var paused = true;
+ serverClient.on('message', function() {
+ paused.should.not.be.ok;
+ wss.close();
+ done();
+ });
+ serverClient.pause();
+ setTimeout(function() {
+ paused = false;
+ serverClient.resume();
+ }, 200);
+ client.send('foo');
+ }
+ }
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var ws = new WebSocket('ws://localhost:' + port);
+ serverClient = ws;
+ serverClient.on('open', onOpen);
+ });
+ wss.on('connection', function(ws) {
+ client = ws;
+ onOpen();
+ });
+ });
+ });
+
+ describe('#ping', function() {
+ it('before connect should fail', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('error', function() {});
+ try {
+ ws.ping();
+ }
+ catch (e) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ });
+
+ it('before connect can silently fail', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('error', function() {});
+ ws.ping('', {}, true);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+
+ it('without message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.ping();
+ });
+ srv.on('ping', function(message) {
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('with message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.ping('hi');
+ });
+ srv.on('ping', function(message) {
+ assert.equal('hi', message);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('with encoded message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.ping('hi', {mask: true});
+ });
+ srv.on('ping', function(message, flags) {
+ assert.ok(flags.masked);
+ assert.equal('hi', message);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+ });
+
+ describe('#pong', function() {
+ it('before connect should fail', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('error', function() {});
+ try {
+ ws.pong();
+ }
+ catch (e) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ });
+
+ it('before connect can silently fail', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('error', function() {});
+ ws.pong('', {}, true);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+
+ it('without message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.pong();
+ });
+ srv.on('pong', function(message) {
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('with message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.pong('hi');
+ });
+ srv.on('pong', function(message) {
+ assert.equal('hi', message);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('with encoded message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.pong('hi', {mask: true});
+ });
+ srv.on('pong', function(message, flags) {
+ assert.ok(flags.masked);
+ assert.equal('hi', message);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+ });
+
+ describe('#send', function() {
+ it('very long binary data can be sent and received (with echoing server)', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var array = new Float32Array(5 * 1024 * 1024);
+ for (var i = 0; i < array.length; ++i) array[i] = i / 5;
+ ws.on('open', function() {
+ ws.send(array, {binary: true});
+ });
+ ws.on('message', function(message, flags) {
+ assert.ok(flags.binary);
+ assert.ok(areArraysEqual(array, new Float32Array(getArrayBuffer(message))));
+ ws.terminate();
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('can send and receive text data', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.send('hi');
+ });
+ ws.on('message', function(message, flags) {
+ assert.equal('hi', message);
+ ws.terminate();
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('send and receive binary data as an array', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var array = new Float32Array(5);
+ for (var i = 0; i < array.length; ++i) array[i] = i / 2;
+ ws.on('open', function() {
+ ws.send(array, {binary: true});
+ });
+ ws.on('message', function(message, flags) {
+ assert.ok(flags.binary);
+ assert.ok(areArraysEqual(array, new Float32Array(getArrayBuffer(message))));
+ ws.terminate();
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('binary data can be sent and received as buffer', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var buf = new Buffer('foobar');
+ ws.on('open', function() {
+ ws.send(buf, {binary: true});
+ });
+ ws.on('message', function(message, flags) {
+ assert.ok(flags.binary);
+ assert.ok(areArraysEqual(buf, message));
+ ws.terminate();
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('ArrayBuffer is auto-detected without binary flag', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var array = new Float32Array(5);
+ for (var i = 0; i < array.length; ++i) array[i] = i / 2;
+ ws.on('open', function() {
+ ws.send(array.buffer);
+ });
+ ws.onmessage = function (event) {
+ assert.ok(event.type = 'Binary');
+ assert.ok(areArraysEqual(array, new Float32Array(getArrayBuffer(event.data))));
+ ws.terminate();
+ srv.close();
+ done();
+ };
+ });
+ });
+
+ it('Buffer is auto-detected without binary flag', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var buf = new Buffer('foobar');
+ ws.on('open', function() {
+ ws.send(buf);
+ });
+ ws.onmessage = function (event) {
+ assert.ok(event.type = 'Binary');
+ assert.ok(areArraysEqual(event.data, buf));
+ ws.terminate();
+ srv.close();
+ done();
+ };
+ });
+ });
+
+ it('before connect should fail', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('error', function() {});
+ try {
+ ws.send('hi');
+ }
+ catch (e) {
+ ws.terminate();
+ srv.close();
+ done();
+ }
+ });
+ });
+
+ it('before connect should pass error through callback, if present', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('error', function() {});
+ ws.send('hi', function(error) {
+ assert.ok(error instanceof Error);
+ ws.terminate();
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('without data should be successful', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.send();
+ });
+ srv.on('message', function(message, flags) {
+ assert.equal('', message);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('calls optional callback when flushed', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.send('hi', function() {
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+ });
+
+ it('with unencoded message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.send('hi');
+ });
+ srv.on('message', function(message, flags) {
+ assert.equal('hi', message);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('with encoded message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.send('hi', {mask: true});
+ });
+ srv.on('message', function(message, flags) {
+ assert.ok(flags.masked);
+ assert.equal('hi', message);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('with unencoded binary message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var array = new Float32Array(5);
+ for (var i = 0; i < array.length; ++i) array[i] = i / 2;
+ ws.on('open', function() {
+ ws.send(array, {binary: true});
+ });
+ srv.on('message', function(message, flags) {
+ assert.ok(flags.binary);
+ assert.ok(areArraysEqual(array, new Float32Array(getArrayBuffer(message))));
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('with encoded binary message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var array = new Float32Array(5);
+ for (var i = 0; i < array.length; ++i) array[i] = i / 2;
+ ws.on('open', function() {
+ ws.send(array, {mask: true, binary: true});
+ });
+ srv.on('message', function(message, flags) {
+ assert.ok(flags.binary);
+ assert.ok(flags.masked);
+ assert.ok(areArraysEqual(array, new Float32Array(getArrayBuffer(message))));
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('with binary stream will send fragmented data', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var callbackFired = false;
+ ws.on('open', function() {
+ var fileStream = fs.createReadStream('test/fixtures/textfile');
+ fileStream.bufferSize = 100;
+ ws.send(fileStream, {binary: true}, function(error) {
+ assert.equal(null, error);
+ callbackFired = true;
+ });
+ });
+ srv.on('message', function(data, flags) {
+ assert.ok(flags.binary);
+ assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile'), data));
+ ws.terminate();
+ });
+ ws.on('close', function() {
+ assert.ok(callbackFired);
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('with text stream will send fragmented data', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var callbackFired = false;
+ ws.on('open', function() {
+ var fileStream = fs.createReadStream('test/fixtures/textfile');
+ fileStream.setEncoding('utf8');
+ fileStream.bufferSize = 100;
+ ws.send(fileStream, {binary: false}, function(error) {
+ assert.equal(null, error);
+ callbackFired = true;
+ });
+ });
+ srv.on('message', function(data, flags) {
+ assert.ok(!flags.binary);
+ assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data));
+ ws.terminate();
+ });
+ ws.on('close', function() {
+ assert.ok(callbackFired);
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('will cause intermittent send to be delayed in order', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ var fileStream = fs.createReadStream('test/fixtures/textfile');
+ fileStream.setEncoding('utf8');
+ fileStream.bufferSize = 100;
+ ws.send(fileStream);
+ ws.send('foobar');
+ ws.send('baz');
+ });
+ var receivedIndex = 0;
+ srv.on('message', function(data, flags) {
+ ++receivedIndex;
+ if (receivedIndex == 1) {
+ assert.ok(!flags.binary);
+ assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data));
+ }
+ else if (receivedIndex == 2) {
+ assert.ok(!flags.binary);
+ assert.equal('foobar', data);
+ }
+ else {
+ assert.ok(!flags.binary);
+ assert.equal('baz', data);
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ });
+ });
+
+ it('will cause intermittent stream to be delayed in order', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ var fileStream = fs.createReadStream('test/fixtures/textfile');
+ fileStream.setEncoding('utf8');
+ fileStream.bufferSize = 100;
+ ws.send(fileStream);
+ var i = 0;
+ ws.stream(function(error, send) {
+ assert.ok(!error);
+ if (++i == 1) send('foo');
+ else send('bar', true);
+ });
+ });
+ var receivedIndex = 0;
+ srv.on('message', function(data, flags) {
+ ++receivedIndex;
+ if (receivedIndex == 1) {
+ assert.ok(!flags.binary);
+ assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data));
+ }
+ else if (receivedIndex == 2) {
+ assert.ok(!flags.binary);
+ assert.equal('foobar', data);
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ });
+ });
+
+ it('will cause intermittent ping to be delivered', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ var fileStream = fs.createReadStream('test/fixtures/textfile');
+ fileStream.setEncoding('utf8');
+ fileStream.bufferSize = 100;
+ ws.send(fileStream);
+ ws.ping('foobar');
+ });
+ var receivedIndex = 0;
+ srv.on('message', function(data, flags) {
+ assert.ok(!flags.binary);
+ assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data));
+ if (++receivedIndex == 2) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ srv.on('ping', function(data) {
+ assert.equal('foobar', data);
+ if (++receivedIndex == 2) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ });
+ });
+
+ it('will cause intermittent pong to be delivered', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ var fileStream = fs.createReadStream('test/fixtures/textfile');
+ fileStream.setEncoding('utf8');
+ fileStream.bufferSize = 100;
+ ws.send(fileStream);
+ ws.pong('foobar');
+ });
+ var receivedIndex = 0;
+ srv.on('message', function(data, flags) {
+ assert.ok(!flags.binary);
+ assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data));
+ if (++receivedIndex == 2) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ srv.on('pong', function(data) {
+ assert.equal('foobar', data);
+ if (++receivedIndex == 2) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ });
+ });
+
+ it('will cause intermittent close to be delivered', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ var fileStream = fs.createReadStream('test/fixtures/textfile');
+ fileStream.setEncoding('utf8');
+ fileStream.bufferSize = 100;
+ ws.send(fileStream);
+ ws.close(1000, 'foobar');
+ });
+ ws.on('close', function() {
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ ws.on('error', function() { /* That's quite alright -- a send was attempted after close */ });
+ srv.on('message', function(data, flags) {
+ assert.ok(!flags.binary);
+ assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data));
+ });
+ srv.on('close', function(code, data) {
+ assert.equal(1000, code);
+ assert.equal('foobar', data);
+ });
+ });
+ });
+ });
+
+ describe('#stream', function() {
+ it('very long binary data can be streamed', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var buffer = new Buffer(10 * 1024);
+ for (var i = 0; i < buffer.length; ++i) buffer[i] = i % 0xff;
+ ws.on('open', function() {
+ var i = 0;
+ var blockSize = 800;
+ var bufLen = buffer.length;
+ ws.stream({binary: true}, function(error, send) {
+ assert.ok(!error);
+ var start = i * blockSize;
+ var toSend = Math.min(blockSize, bufLen - (i * blockSize));
+ var end = start + toSend;
+ var isFinal = toSend < blockSize;
+ send(buffer.slice(start, end), isFinal);
+ i += 1;
+ });
+ });
+ srv.on('message', function(data, flags) {
+ assert.ok(flags.binary);
+ assert.ok(areArraysEqual(buffer, data));
+ ws.terminate();
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('before connect should pass error through callback', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('error', function() {});
+ ws.stream(function(error) {
+ assert.ok(error instanceof Error);
+ ws.terminate();
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('without callback should fail', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var payload = 'HelloWorld';
+ ws.on('open', function() {
+ try {
+ ws.stream();
+ }
+ catch (e) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ });
+ });
+
+ it('will cause intermittent send to be delayed in order', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var payload = 'HelloWorld';
+ ws.on('open', function() {
+ var i = 0;
+ ws.stream(function(error, send) {
+ assert.ok(!error);
+ if (++i == 1) {
+ send(payload.substr(0, 5));
+ ws.send('foobar');
+ ws.send('baz');
+ }
+ else {
+ send(payload.substr(5, 5), true);
+ }
+ });
+ });
+ var receivedIndex = 0;
+ srv.on('message', function(data, flags) {
+ ++receivedIndex;
+ if (receivedIndex == 1) {
+ assert.ok(!flags.binary);
+ assert.equal(payload, data);
+ }
+ else if (receivedIndex == 2) {
+ assert.ok(!flags.binary);
+ assert.equal('foobar', data);
+ }
+ else {
+ assert.ok(!flags.binary);
+ assert.equal('baz', data);
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ });
+ });
+
+ it('will cause intermittent stream to be delayed in order', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var payload = 'HelloWorld';
+ ws.on('open', function() {
+ var i = 0;
+ ws.stream(function(error, send) {
+ assert.ok(!error);
+ if (++i == 1) {
+ send(payload.substr(0, 5));
+ var i2 = 0;
+ ws.stream(function(error, send) {
+ assert.ok(!error);
+ if (++i2 == 1) send('foo');
+ else send('bar', true);
+ });
+ ws.send('baz');
+ }
+ else send(payload.substr(5, 5), true);
+ });
+ });
+ var receivedIndex = 0;
+ srv.on('message', function(data, flags) {
+ ++receivedIndex;
+ if (receivedIndex == 1) {
+ assert.ok(!flags.binary);
+ assert.equal(payload, data);
+ }
+ else if (receivedIndex == 2) {
+ assert.ok(!flags.binary);
+ assert.equal('foobar', data);
+ }
+ else if (receivedIndex == 3){
+ assert.ok(!flags.binary);
+ assert.equal('baz', data);
+ setTimeout(function() {
+ srv.close();
+ ws.terminate();
+ done();
+ }, 1000);
+ }
+ else throw new Error('more messages than we actually sent just arrived');
+ });
+ });
+ });
+
+ it('will cause intermittent ping to be delivered', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var payload = 'HelloWorld';
+ ws.on('open', function() {
+ var i = 0;
+ ws.stream(function(error, send) {
+ assert.ok(!error);
+ if (++i == 1) {
+ send(payload.substr(0, 5));
+ ws.ping('foobar');
+ }
+ else {
+ send(payload.substr(5, 5), true);
+ }
+ });
+ });
+ var receivedIndex = 0;
+ srv.on('message', function(data, flags) {
+ assert.ok(!flags.binary);
+ assert.equal(payload, data);
+ if (++receivedIndex == 2) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ srv.on('ping', function(data) {
+ assert.equal('foobar', data);
+ if (++receivedIndex == 2) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ });
+ });
+
+ it('will cause intermittent pong to be delivered', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var payload = 'HelloWorld';
+ ws.on('open', function() {
+ var i = 0;
+ ws.stream(function(error, send) {
+ assert.ok(!error);
+ if (++i == 1) {
+ send(payload.substr(0, 5));
+ ws.pong('foobar');
+ }
+ else {
+ send(payload.substr(5, 5), true);
+ }
+ });
+ });
+ var receivedIndex = 0;
+ srv.on('message', function(data, flags) {
+ assert.ok(!flags.binary);
+ assert.equal(payload, data);
+ if (++receivedIndex == 2) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ srv.on('pong', function(data) {
+ assert.equal('foobar', data);
+ if (++receivedIndex == 2) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ });
+ });
+
+ it('will cause intermittent close to be delivered', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var payload = 'HelloWorld';
+ var errorGiven = false;
+ ws.on('open', function() {
+ var i = 0;
+ ws.stream(function(error, send) {
+ if (++i == 1) {
+ send(payload.substr(0, 5));
+ ws.close(1000, 'foobar');
+ }
+ else if(i == 2) {
+ send(payload.substr(5, 5), true);
+ }
+ else if (i == 3) {
+ assert.ok(error);
+ errorGiven = true;
+ }
+ });
+ });
+ ws.on('close', function() {
+ assert.ok(errorGiven);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ srv.on('message', function(data, flags) {
+ assert.ok(!flags.binary);
+ assert.equal(payload, data);
+ });
+ srv.on('close', function(code, data) {
+ assert.equal(1000, code);
+ assert.equal('foobar', data);
+ });
+ });
+ });
+ });
+
+ describe('#close', function() {
+ it('will raise error callback, if any, if called during send stream', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var errorGiven = false;
+ ws.on('open', function() {
+ var fileStream = fs.createReadStream('test/fixtures/textfile');
+ fileStream.setEncoding('utf8');
+ fileStream.bufferSize = 100;
+ ws.send(fileStream, function(error) {
+ errorGiven = error != null;
+ });
+ ws.close(1000, 'foobar');
+ });
+ ws.on('close', function() {
+ setTimeout(function() {
+ assert.ok(errorGiven);
+ srv.close();
+ ws.terminate();
+ done();
+ }, 1000);
+ });
+ });
+ });
+
+ it('without invalid first argument throws exception', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ try {
+ ws.close('error');
+ }
+ catch (e) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ });
+ });
+
+ it('without reserved error code 1004 throws exception', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ try {
+ ws.close(1004);
+ }
+ catch (e) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ });
+ });
+
+ it('without message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.close(1000);
+ });
+ srv.on('close', function(code, message, flags) {
+ assert.equal('', message);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('with message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.close(1000, 'some reason');
+ });
+ srv.on('close', function(code, message, flags) {
+ assert.ok(flags.masked);
+ assert.equal('some reason', message);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('with encoded message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.close(1000, 'some reason', {mask: true});
+ });
+ srv.on('close', function(code, message, flags) {
+ assert.ok(flags.masked);
+ assert.equal('some reason', message);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('ends connection to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var connectedOnce = false;
+ ws.on('open', function() {
+ connectedOnce = true;
+ ws.close(1000, 'some reason', {mask: true});
+ });
+ ws.on('close', function() {
+ assert.ok(connectedOnce);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+ });
+
+ describe('W3C API emulation', function() {
+ it('should not throw errors when getting and setting', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var listener = function () {};
+
+ ws.onmessage = listener;
+ ws.onerror = listener;
+ ws.onclose = listener;
+ ws.onopen = listener;
+
+ assert.ok(ws.onopen === listener);
+ assert.ok(ws.onmessage === listener);
+ assert.ok(ws.onclose === listener);
+ assert.ok(ws.onerror === listener);
+
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+
+ it('should work the same as the EventEmitter api', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var listener = function() {};
+ var message = 0;
+ var close = 0;
+ var open = 0;
+
+ ws.onmessage = function(messageEvent) {
+ assert.ok(!!messageEvent.data);
+ ++message;
+ ws.close();
+ };
+
+ ws.onopen = function() {
+ ++open;
+ }
+
+ ws.onclose = function() {
+ ++close;
+ }
+
+ ws.on('open', function() {
+ ws.send('foo');
+ });
+
+ ws.on('close', function() {
+ process.nextTick(function() {
+ assert.ok(message === 1);
+ assert.ok(open === 1);
+ assert.ok(close === 1);
+
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+ });
+
+ it('should receive text data wrapped in a MessageEvent when using addEventListener', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.addEventListener('open', function() {
+ ws.send('hi');
+ });
+ ws.addEventListener('message', function(messageEvent) {
+ assert.equal('hi', messageEvent.data);
+ ws.terminate();
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('should receive valid CloseEvent when server closes with code 1000', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.addEventListener('close', function(closeEvent) {
+ assert.equal(true, closeEvent.wasClean);
+ assert.equal(1000, closeEvent.code);
+ ws.terminate();
+ wss.close();
+ done();
+ });
+ });
+ wss.on('connection', function(client) {
+ client.close(1000);
+ });
+ });
+
+ it('should receive valid CloseEvent when server closes with code 1001', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.addEventListener('close', function(closeEvent) {
+ assert.equal(false, closeEvent.wasClean);
+ assert.equal(1001, closeEvent.code);
+ assert.equal('some daft reason', closeEvent.reason);
+ ws.terminate();
+ wss.close();
+ done();
+ });
+ });
+ wss.on('connection', function(client) {
+ client.close(1001, 'some daft reason');
+ });
+ });
+
+ it('should have target set on Events', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.addEventListener('open', function(openEvent) {
+ assert.equal(ws, openEvent.target);
+ });
+ ws.addEventListener('message', function(messageEvent) {
+ assert.equal(ws, messageEvent.target);
+ wss.close();
+ });
+ ws.addEventListener('close', function(closeEvent) {
+ assert.equal(ws, closeEvent.target);
+ ws.emit('error', new Error('forced'));
+ });
+ ws.addEventListener('error', function(errorEvent) {
+ assert.equal(errorEvent.message, 'forced');
+ assert.equal(ws, errorEvent.target);
+ ws.terminate();
+ done();
+ });
+ });
+ wss.on('connection', function(client) {
+ client.send('hi')
+ });
+ });
+ });
+
+ describe('ssl', function() {
+ it('can connect to secure websocket server', function(done) {
+ var options = {
+ key: fs.readFileSync('test/fixtures/key.pem'),
+ cert: fs.readFileSync('test/fixtures/certificate.pem')
+ };
+ var app = https.createServer(options, function (req, res) {
+ res.writeHead(200);
+ res.end();
+ });
+ var wss = new WebSocketServer({server: app});
+ app.listen(++port, function() {
+ var ws = new WebSocket('wss://localhost:' + port);
+ });
+ wss.on('connection', function(ws) {
+ app.close();
+ ws.terminate();
+ wss.close();
+ done();
+ });
+ });
+
+ it('can connect to secure websocket server with client side certificate', function(done) {
+ var options = {
+ key: fs.readFileSync('test/fixtures/key.pem'),
+ cert: fs.readFileSync('test/fixtures/certificate.pem'),
+ ca: [fs.readFileSync('test/fixtures/ca1-cert.pem')],
+ requestCert: true
+ };
+ var clientOptions = {
+ key: fs.readFileSync('test/fixtures/agent1-key.pem'),
+ cert: fs.readFileSync('test/fixtures/agent1-cert.pem')
+ };
+ var app = https.createServer(options, function (req, res) {
+ res.writeHead(200);
+ res.end();
+ });
+ var success = false;
+ var wss = new WebSocketServer({
+ server: app,
+ verifyClient: function(info) {
+ success = !!info.req.client.authorized;
+ return true;
+ }
+ });
+ app.listen(++port, function() {
+ var ws = new WebSocket('wss://localhost:' + port, clientOptions);
+ });
+ wss.on('connection', function(ws) {
+ app.close();
+ ws.terminate();
+ wss.close();
+ success.should.be.ok;
+ done();
+ });
+ });
+
+ it('cannot connect to secure websocket server via ws://', function(done) {
+ var options = {
+ key: fs.readFileSync('test/fixtures/key.pem'),
+ cert: fs.readFileSync('test/fixtures/certificate.pem')
+ };
+ var app = https.createServer(options, function (req, res) {
+ res.writeHead(200);
+ res.end();
+ });
+ var wss = new WebSocketServer({server: app});
+ app.listen(++port, function() {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('error', function() {
+ app.close();
+ ws.terminate();
+ wss.close();
+ done();
+ });
+ });
+ });
+
+ it('can send and receive text data', function(done) {
+ var options = {
+ key: fs.readFileSync('test/fixtures/key.pem'),
+ cert: fs.readFileSync('test/fixtures/certificate.pem')
+ };
+ var app = https.createServer(options, function (req, res) {
+ res.writeHead(200);
+ res.end();
+ });
+ var wss = new WebSocketServer({server: app});
+ app.listen(++port, function() {
+ var ws = new WebSocket('wss://localhost:' + port);
+ ws.on('open', function() {
+ ws.send('foobar');
+ });
+ });
+ wss.on('connection', function(ws) {
+ ws.on('message', function(message, flags) {
+ message.should.eql('foobar');
+ app.close();
+ ws.terminate();
+ wss.close();
+ done();
+ });
+ });
+ });
+
+ it('can send and receive very long binary data', function(done) {
+ var options = {
+ key: fs.readFileSync('test/fixtures/key.pem'),
+ cert: fs.readFileSync('test/fixtures/certificate.pem')
+ }
+ var app = https.createServer(options, function (req, res) {
+ res.writeHead(200);
+ res.end();
+ });
+ crypto.randomBytes(5 * 1024 * 1024, function(ex, buf) {
+ if (ex) throw ex;
+ var wss = new WebSocketServer({server: app});
+ app.listen(++port, function() {
+ var ws = new WebSocket('wss://localhost:' + port);
+ ws.on('open', function() {
+ ws.send(buf, {binary: true});
+ });
+ ws.on('message', function(message, flags) {
+ flags.binary.should.be.ok;
+ areArraysEqual(buf, message).should.be.ok;
+ app.close();
+ ws.terminate();
+ wss.close();
+ done();
+ });
+ });
+ wss.on('connection', function(ws) {
+ ws.on('message', function(message, flags) {
+ ws.send(message, {binary: true});
+ });
+ });
+ });
+ });
+ });
+
+ describe('protocol support discovery', function() {
+ describe('#supports', function() {
+ describe('#binary', function() {
+ it('returns true for hybi transport', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var ws = new WebSocket('ws://localhost:' + port);
+ });
+ wss.on('connection', function(client) {
+ assert.equal(true, client.supports.binary);
+ wss.close();
+ done();
+ });
+ });
+
+ it('returns false for hixie transport', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'WebSocket',
+ 'Sec-WebSocket-Key1': '3e6b263 4 17 80',
+ 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90'
+ }
+ };
+ var req = http.request(options);
+ req.write('WjN}|M(6');
+ req.end();
+ });
+ wss.on('connection', function(client) {
+ assert.equal(false, client.supports.binary);
+ wss.close();
+ done();
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/node_modules/ws/test/WebSocketServer.test.js b/node_modules/ws/test/WebSocketServer.test.js
new file mode 100644
index 0000000..c21fd97
--- /dev/null
+++ b/node_modules/ws/test/WebSocketServer.test.js
@@ -0,0 +1,1103 @@
+var http = require('http')
+ , https = require('https')
+ , WebSocket = require('../')
+ , WebSocketServer = WebSocket.Server
+ , fs = require('fs')
+ , should = require('should');
+
+var port = 8000;
+
+function getArrayBuffer(buf) {
+ var l = buf.length;
+ var arrayBuf = new ArrayBuffer(l);
+ for (var i = 0; i < l; ++i) {
+ arrayBuf[i] = buf[i];
+ }
+ return arrayBuf;
+}
+
+function areArraysEqual(x, y) {
+ if (x.length != y.length) return false;
+ for (var i = 0, l = x.length; i < l; ++i) {
+ if (x[i] !== y[i]) return false;
+ }
+ return true;
+}
+
+describe('WebSocketServer', function() {
+ describe('#ctor', function() {
+ it('throws an error if no option object is passed', function() {
+ var gotException = false;
+ try {
+ var wss = new WebSocketServer();
+ }
+ catch (e) {
+ gotException = true;
+ }
+ gotException.should.be.ok;
+ });
+
+ it('throws an error if no port or server is specified', function() {
+ var gotException = false;
+ try {
+ var wss = new WebSocketServer({});
+ }
+ catch (e) {
+ gotException = true;
+ }
+ gotException.should.be.ok;
+ });
+
+ it('does not throw an error if no port or server is specified, when the noServer option is true', function() {
+ var gotException = false;
+ try {
+ var wss = new WebSocketServer({noServer: true});
+ }
+ catch (e) {
+ gotException = true;
+ }
+ gotException.should.eql(false);
+ });
+
+ it('emits an error if http server bind fails', function(done) {
+ var wss = new WebSocketServer({port: 1});
+ wss.on('error', function() { done(); });
+ });
+
+ it('starts a server on a given port', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var ws = new WebSocket('ws://localhost:' + port);
+ });
+ wss.on('connection', function(client) {
+ wss.close();
+ done();
+ });
+ });
+
+ it('uses a precreated http server', function (done) {
+ var srv = http.createServer();
+ srv.listen(++port, function () {
+ var wss = new WebSocketServer({server: srv});
+ var ws = new WebSocket('ws://localhost:' + port);
+
+ wss.on('connection', function(client) {
+ wss.close();
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('uses a precreated http server listening on unix socket', function (done) {
+ var srv = http.createServer();
+ var sockPath = '/tmp/ws_socket_'+new Date().getTime()+'.'+Math.floor(Math.random() * 1000);
+ srv.listen(sockPath, function () {
+ var wss = new WebSocketServer({server: srv});
+ var ws = new WebSocket('ws+unix://'+sockPath);
+
+ wss.on('connection', function(client) {
+ wss.close();
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('emits path specific connection event', function (done) {
+ var srv = http.createServer();
+ srv.listen(++port, function () {
+ var wss = new WebSocketServer({server: srv});
+ var ws = new WebSocket('ws://localhost:' + port+'/endpointName');
+
+ wss.on('connection/endpointName', function(client) {
+ wss.close();
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('can have two different instances listening on the same http server with two different paths', function(done) {
+ var srv = http.createServer();
+ srv.listen(++port, function () {
+ var wss1 = new WebSocketServer({server: srv, path: '/wss1'})
+ , wss2 = new WebSocketServer({server: srv, path: '/wss2'});
+ var doneCount = 0;
+ wss1.on('connection', function(client) {
+ wss1.close();
+ if (++doneCount == 2) {
+ srv.close();
+ done();
+ }
+ });
+ wss2.on('connection', function(client) {
+ wss2.close();
+ if (++doneCount == 2) {
+ srv.close();
+ done();
+ }
+ });
+ var ws1 = new WebSocket('ws://localhost:' + port + '/wss1');
+ var ws2 = new WebSocket('ws://localhost:' + port + '/wss2?foo=1');
+ });
+ });
+
+ it('cannot have two different instances listening on the same http server with the same path', function(done) {
+ var srv = http.createServer();
+ srv.listen(++port, function () {
+ var wss1 = new WebSocketServer({server: srv, path: '/wss1'});
+ try {
+ var wss2 = new WebSocketServer({server: srv, path: '/wss1'});
+ }
+ catch (e) {
+ wss1.close();
+ srv.close();
+ done();
+ }
+ });
+ });
+ });
+
+ describe('#close', function() {
+ it('will close all clients', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('close', function() {
+ if (++closes == 2) done();
+ });
+ });
+ var closes = 0;
+ wss.on('connection', function(client) {
+ client.on('close', function() {
+ if (++closes == 2) done();
+ });
+ wss.close();
+ });
+ });
+
+ it('does not close a precreated server', function(done) {
+ var srv = http.createServer();
+ var realClose = srv.close;
+ srv.close = function() {
+ should.fail('must not close pre-created server');
+ }
+ srv.listen(++port, function () {
+ var wss = new WebSocketServer({server: srv});
+ var ws = new WebSocket('ws://localhost:' + port);
+ wss.on('connection', function(client) {
+ wss.close();
+ srv.close = realClose;
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('cleans up websocket data on a precreated server', function(done) {
+ var srv = http.createServer();
+ srv.listen(++port, function () {
+ var wss1 = new WebSocketServer({server: srv, path: '/wss1'})
+ , wss2 = new WebSocketServer({server: srv, path: '/wss2'});
+ (typeof srv._webSocketPaths).should.eql('object');
+ Object.keys(srv._webSocketPaths).length.should.eql(2);
+ wss1.close();
+ Object.keys(srv._webSocketPaths).length.should.eql(1);
+ wss2.close();
+ (typeof srv._webSocketPaths).should.eql('undefined');
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ describe('#clients', function() {
+ it('returns a list of connected clients', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ wss.clients.length.should.eql(0);
+ var ws = new WebSocket('ws://localhost:' + port);
+ });
+ wss.on('connection', function(client) {
+ wss.clients.length.should.eql(1);
+ wss.close();
+ done();
+ });
+ });
+
+ it('can be disabled', function(done) {
+ var wss = new WebSocketServer({port: ++port, clientTracking: false}, function() {
+ wss.clients.length.should.eql(0);
+ var ws = new WebSocket('ws://localhost:' + port);
+ });
+ wss.on('connection', function(client) {
+ wss.clients.length.should.eql(0);
+ wss.close();
+ done();
+ });
+ });
+
+ it('is updated when client terminates the connection', function(done) {
+ var ws;
+ var wss = new WebSocketServer({port: ++port}, function() {
+ ws = new WebSocket('ws://localhost:' + port);
+ });
+ wss.on('connection', function(client) {
+ client.on('close', function() {
+ wss.clients.length.should.eql(0);
+ wss.close();
+ done();
+ });
+ ws.terminate();
+ });
+ });
+
+ it('is updated when client closes the connection', function(done) {
+ var ws;
+ var wss = new WebSocketServer({port: ++port}, function() {
+ ws = new WebSocket('ws://localhost:' + port);
+ });
+ wss.on('connection', function(client) {
+ client.on('close', function() {
+ wss.clients.length.should.eql(0);
+ wss.close();
+ done();
+ });
+ ws.close();
+ });
+ });
+ });
+
+ describe('#options', function() {
+ it('exposes options passed to constructor', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ wss.options.port.should.eql(port);
+ wss.close();
+ done();
+ });
+ });
+ });
+
+ describe('#handleUpgrade', function() {
+ it('can be used for a pre-existing server', function (done) {
+ var srv = http.createServer();
+ srv.listen(++port, function () {
+ var wss = new WebSocketServer({noServer: true});
+ srv.on('upgrade', function(req, socket, upgradeHead) {
+ wss.handleUpgrade(req, socket, upgradeHead, function(client) {
+ client.send('hello');
+ });
+ });
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('message', function(message) {
+ message.should.eql('hello');
+ wss.close();
+ srv.close();
+ done();
+ });
+ });
+ });
+ });
+
+ describe('hybi mode', function() {
+ describe('connection establishing', function() {
+ it('does not accept connections with no sec-websocket-key', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'websocket'
+ }
+ };
+ var req = http.request(options);
+ req.end();
+ req.on('response', function(res) {
+ res.statusCode.should.eql(400);
+ wss.close();
+ done();
+ });
+ });
+ wss.on('connection', function(ws) {
+ done(new Error('connection must not be established'));
+ });
+ wss.on('error', function() {});
+ });
+
+ it('does not accept connections with no sec-websocket-version', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'websocket',
+ 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ=='
+ }
+ };
+ var req = http.request(options);
+ req.end();
+ req.on('response', function(res) {
+ res.statusCode.should.eql(400);
+ wss.close();
+ done();
+ });
+ });
+ wss.on('connection', function(ws) {
+ done(new Error('connection must not be established'));
+ });
+ wss.on('error', function() {});
+ });
+
+ it('does not accept connections with invalid sec-websocket-version', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'websocket',
+ 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
+ 'Sec-WebSocket-Version': 12
+ }
+ };
+ var req = http.request(options);
+ req.end();
+ req.on('response', function(res) {
+ res.statusCode.should.eql(400);
+ wss.close();
+ done();
+ });
+ });
+ wss.on('connection', function(ws) {
+ done(new Error('connection must not be established'));
+ });
+ wss.on('error', function() {});
+ });
+
+ it('client can be denied', function(done) {
+ var wss = new WebSocketServer({port: ++port, verifyClient: function(o) {
+ return false;
+ }}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'websocket',
+ 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
+ 'Sec-WebSocket-Version': 8,
+ 'Sec-WebSocket-Origin': 'http://foobar.com'
+ }
+ };
+ var req = http.request(options);
+ req.end();
+ req.on('response', function(res) {
+ res.statusCode.should.eql(401);
+ process.nextTick(function() {
+ wss.close();
+ done();
+ });
+ });
+ });
+ wss.on('connection', function(ws) {
+ done(new Error('connection must not be established'));
+ });
+ wss.on('error', function() {});
+ });
+
+ it('client can be accepted', function(done) {
+ var wss = new WebSocketServer({port: ++port, verifyClient: function(o) {
+ return true;
+ }}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'websocket',
+ 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
+ 'Sec-WebSocket-Version': 13,
+ 'Origin': 'http://foobar.com'
+ }
+ };
+ var req = http.request(options);
+ req.end();
+ });
+ wss.on('connection', function(ws) {
+ ws.terminate();
+ wss.close();
+ done();
+ });
+ wss.on('error', function() {});
+ });
+
+ it('verifyClient gets client origin', function(done) {
+ var verifyClientCalled = false;
+ var wss = new WebSocketServer({port: ++port, verifyClient: function(info) {
+ info.origin.should.eql('http://foobarbaz.com');
+ verifyClientCalled = true;
+ return false;
+ }}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'websocket',
+ 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
+ 'Sec-WebSocket-Version': 13,
+ 'Origin': 'http://foobarbaz.com'
+ }
+ };
+ var req = http.request(options);
+ req.end();
+ req.on('response', function(res) {
+ verifyClientCalled.should.be.ok;
+ wss.close();
+ done();
+ });
+ });
+ wss.on('error', function() {});
+ });
+
+ it('verifyClient gets original request', function(done) {
+ var verifyClientCalled = false;
+ var wss = new WebSocketServer({port: ++port, verifyClient: function(info) {
+ info.req.headers['sec-websocket-key'].should.eql('dGhlIHNhbXBsZSBub25jZQ==');
+ verifyClientCalled = true;
+ return false;
+ }}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'websocket',
+ 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
+ 'Sec-WebSocket-Version': 13,
+ 'Origin': 'http://foobarbaz.com'
+ }
+ };
+ var req = http.request(options);
+ req.end();
+ req.on('response', function(res) {
+ verifyClientCalled.should.be.ok;
+ wss.close();
+ done();
+ });
+ });
+ wss.on('error', function() {});
+ });
+
+ it('verifyClient has secure:true for ssl connections', function(done) {
+ var options = {
+ key: fs.readFileSync('test/fixtures/key.pem'),
+ cert: fs.readFileSync('test/fixtures/certificate.pem')
+ };
+ var app = https.createServer(options, function (req, res) {
+ res.writeHead(200);
+ res.end();
+ });
+ var success = false;
+ var wss = new WebSocketServer({
+ server: app,
+ verifyClient: function(info) {
+ success = info.secure === true;
+ return true;
+ }
+ });
+ app.listen(++port, function() {
+ var ws = new WebSocket('wss://localhost:' + port);
+ });
+ wss.on('connection', function(ws) {
+ app.close();
+ ws.terminate();
+ wss.close();
+ success.should.be.ok;
+ done();
+ });
+ });
+
+ it('verifyClient has secure:false for non-ssl connections', function(done) {
+ var app = http.createServer(function (req, res) {
+ res.writeHead(200);
+ res.end();
+ });
+ var success = false;
+ var wss = new WebSocketServer({
+ server: app,
+ verifyClient: function(info) {
+ success = info.secure === false;
+ return true;
+ }
+ });
+ app.listen(++port, function() {
+ var ws = new WebSocket('ws://localhost:' + port);
+ });
+ wss.on('connection', function(ws) {
+ app.close();
+ ws.terminate();
+ wss.close();
+ success.should.be.ok;
+ done();
+ });
+ });
+
+ it('client can be denied asynchronously', function(done) {
+ var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) {
+ process.nextTick(function() {
+ cb(false);
+ });
+ }}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'websocket',
+ 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
+ 'Sec-WebSocket-Version': 8,
+ 'Sec-WebSocket-Origin': 'http://foobar.com'
+ }
+ };
+ var req = http.request(options);
+ req.end();
+ req.on('response', function(res) {
+ res.statusCode.should.eql(401);
+ process.nextTick(function() {
+ wss.close();
+ done();
+ });
+ });
+ });
+ wss.on('connection', function(ws) {
+ done(new Error('connection must not be established'));
+ });
+ wss.on('error', function() {});
+ });
+
+ it('client can be accepted asynchronously', function(done) {
+ var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) {
+ process.nextTick(function() {
+ cb(true);
+ });
+ }}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'websocket',
+ 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
+ 'Sec-WebSocket-Version': 13,
+ 'Origin': 'http://foobar.com'
+ }
+ };
+ var req = http.request(options);
+ req.end();
+ });
+ wss.on('connection', function(ws) {
+ ws.terminate();
+ wss.close();
+ done();
+ });
+ wss.on('error', function() {});
+ });
+
+ it('handles messages passed along with the upgrade request (upgrade head)', function(done) {
+ var wss = new WebSocketServer({port: ++port, verifyClient: function(o) {
+ return true;
+ }}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'websocket',
+ 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
+ 'Sec-WebSocket-Version': 13,
+ 'Origin': 'http://foobar.com'
+ }
+ };
+ var req = http.request(options);
+ req.write(new Buffer([0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f], 'binary'));
+ req.end();
+ });
+ wss.on('connection', function(ws) {
+ ws.on('message', function(data) {
+ data.should.eql('Hello');
+ ws.terminate();
+ wss.close();
+ done();
+ });
+ });
+ wss.on('error', function() {});
+ });
+
+ it('selects the first protocol by default', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'});
+ ws.on('open', function(client) {
+ ws.protocol.should.eql('prot1');
+ wss.close();
+ done();
+ });
+ });
+ });
+
+ it('selects the last protocol via protocol handler', function(done) {
+ var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) {
+ cb(true, ps[ps.length-1]); }}, function() {
+ var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'});
+ ws.on('open', function(client) {
+ ws.protocol.should.eql('prot2');
+ wss.close();
+ done();
+ });
+ });
+ });
+
+ it('client detects invalid server protocol', function(done) {
+ var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) {
+ cb(true, 'prot3'); }}, function() {
+ var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'});
+ ws.on('open', function(client) {
+ done(new Error('connection must not be established'));
+ });
+ ws.on('error', function() {
+ done();
+ });
+ });
+ });
+
+ it('client detects no server protocol', function(done) {
+ var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) {
+ cb(true); }}, function() {
+ var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'});
+ ws.on('open', function(client) {
+ done(new Error('connection must not be established'));
+ });
+ ws.on('error', function() {
+ done();
+ });
+ });
+ });
+
+ it('client refuses server protocols', function(done) {
+ var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) {
+ cb(false); }}, function() {
+ var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'});
+ ws.on('open', function(client) {
+ done(new Error('connection must not be established'));
+ });
+ ws.on('error', function() {
+ done();
+ });
+ });
+ });
+
+ it('server detects invalid protocol handler', function(done) {
+ var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) {
+ // not calling callback is an error and shouldn't timeout
+ }}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'websocket',
+ 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
+ 'Sec-WebSocket-Version': 13,
+ 'Sec-WebSocket-Origin': 'http://foobar.com'
+ }
+ };
+ options.port = port;
+ var req = http.request(options);
+ req.end();
+ req.on('response', function(res) {
+ res.statusCode.should.eql(501);
+ wss.close();
+ done();
+ });
+ });
+ wss.on('connection', function(ws) {
+ done(new Error('connection must not be established'));
+ });
+ wss.on('error', function() {});
+ });
+ });
+
+ describe('messaging', function() {
+ it('can send and receive data', function(done) {
+ var data = new Array(65*1024);
+ for (var i = 0; i < data.length; ++i) {
+ data[i] = String.fromCharCode(65 + ~~(25 * Math.random()));
+ }
+ data = data.join('');
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('message', function(message, flags) {
+ ws.send(message);
+ });
+ });
+ wss.on('connection', function(client) {
+ client.on('message', function(message) {
+ message.should.eql(data);
+ wss.close();
+ done();
+ });
+ client.send(data);
+ });
+ });
+ });
+ });
+
+ describe('hixie mode', function() {
+ it('can be disabled', function(done) {
+ var wss = new WebSocketServer({port: ++port, disableHixie: true}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'WebSocket',
+ 'Sec-WebSocket-Key1': '3e6b263 4 17 80',
+ 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90'
+ }
+ };
+ var req = http.request(options);
+ req.write('WjN}|M(6');
+ req.end();
+ req.on('response', function(res) {
+ res.statusCode.should.eql(401);
+ process.nextTick(function() {
+ wss.close();
+ done();
+ });
+ });
+ });
+ wss.on('connection', function(ws) {
+ done(new Error('connection must not be established'));
+ });
+ wss.on('error', function() {});
+ });
+
+ describe('connection establishing', function() {
+ it('does not accept connections with no sec-websocket-key1', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'WebSocket',
+ 'Sec-WebSocket-Key1': '3e6b263 4 17 80'
+ }
+ };
+ var req = http.request(options);
+ req.end();
+ req.on('response', function(res) {
+ res.statusCode.should.eql(400);
+ wss.close();
+ done();
+ });
+ });
+ wss.on('connection', function(ws) {
+ done(new Error('connection must not be established'));
+ });
+ wss.on('error', function() {});
+ });
+
+ it('does not accept connections with no sec-websocket-key2', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'WebSocket',
+ 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90'
+ }
+ };
+ var req = http.request(options);
+ req.end();
+ req.on('response', function(res) {
+ res.statusCode.should.eql(400);
+ wss.close();
+ done();
+ });
+ });
+ wss.on('connection', function(ws) {
+ done(new Error('connection must not be established'));
+ });
+ wss.on('error', function() {});
+ });
+
+ it('accepts connections with valid handshake', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'WebSocket',
+ 'Sec-WebSocket-Key1': '3e6b263 4 17 80',
+ 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90'
+ }
+ };
+ var req = http.request(options);
+ req.write('WjN}|M(6');
+ req.end();
+ });
+ wss.on('connection', function(ws) {
+ ws.terminate();
+ wss.close();
+ done();
+ });
+ wss.on('error', function() {});
+ });
+
+ it('client can be denied', function(done) {
+ var wss = new WebSocketServer({port: ++port, verifyClient: function(o) {
+ return false;
+ }}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'WebSocket',
+ 'Sec-WebSocket-Key1': '3e6b263 4 17 80',
+ 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90'
+ }
+ };
+ var req = http.request(options);
+ req.write('WjN}|M(6');
+ req.end();
+ req.on('response', function(res) {
+ res.statusCode.should.eql(401);
+ process.nextTick(function() {
+ wss.close();
+ done();
+ });
+ });
+ });
+ wss.on('connection', function(ws) {
+ done(new Error('connection must not be established'));
+ });
+ wss.on('error', function() {});
+ });
+
+ it('client can be accepted', function(done) {
+ var wss = new WebSocketServer({port: ++port, verifyClient: function(o) {
+ return true;
+ }}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'WebSocket',
+ 'Sec-WebSocket-Key1': '3e6b263 4 17 80',
+ 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90'
+ }
+ };
+ var req = http.request(options);
+ req.write('WjN}|M(6');
+ req.end();
+ });
+ wss.on('connection', function(ws) {
+ ws.terminate();
+ wss.close();
+ done();
+ });
+ wss.on('error', function() {});
+ });
+
+ it('verifyClient gets client origin', function(done) {
+ var verifyClientCalled = false;
+ var wss = new WebSocketServer({port: ++port, verifyClient: function(info) {
+ info.origin.should.eql('http://foobarbaz.com');
+ verifyClientCalled = true;
+ return false;
+ }}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'WebSocket',
+ 'Origin': 'http://foobarbaz.com',
+ 'Sec-WebSocket-Key1': '3e6b263 4 17 80',
+ 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90'
+ }
+ };
+ var req = http.request(options);
+ req.write('WjN}|M(6');
+ req.end();
+ req.on('response', function(res) {
+ verifyClientCalled.should.be.ok;
+ wss.close();
+ done();
+ });
+ });
+ wss.on('error', function() {});
+ });
+
+ it('verifyClient gets original request', function(done) {
+ var verifyClientCalled = false;
+ var wss = new WebSocketServer({port: ++port, verifyClient: function(info) {
+ info.req.headers['sec-websocket-key1'].should.eql('3e6b263 4 17 80');
+ verifyClientCalled = true;
+ return false;
+ }}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'WebSocket',
+ 'Origin': 'http://foobarbaz.com',
+ 'Sec-WebSocket-Key1': '3e6b263 4 17 80',
+ 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90'
+ }
+ };
+ var req = http.request(options);
+ req.write('WjN}|M(6');
+ req.end();
+ req.on('response', function(res) {
+ verifyClientCalled.should.be.ok;
+ wss.close();
+ done();
+ });
+ });
+ wss.on('error', function() {});
+ });
+
+ it('client can be denied asynchronously', function(done) {
+ var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) {
+ cb(false);
+ }}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'WebSocket',
+ 'Origin': 'http://foobarbaz.com',
+ 'Sec-WebSocket-Key1': '3e6b263 4 17 80',
+ 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90'
+ }
+ };
+ var req = http.request(options);
+ req.write('WjN}|M(6');
+ req.end();
+ req.on('response', function(res) {
+ res.statusCode.should.eql(401);
+ process.nextTick(function() {
+ wss.close();
+ done();
+ });
+ });
+ });
+ wss.on('connection', function(ws) {
+ done(new Error('connection must not be established'));
+ });
+ wss.on('error', function() {});
+ });
+
+ it('client can be accepted asynchronously', function(done) {
+ var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) {
+ cb(true);
+ }}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'WebSocket',
+ 'Origin': 'http://foobarbaz.com',
+ 'Sec-WebSocket-Key1': '3e6b263 4 17 80',
+ 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90'
+ }
+ };
+ var req = http.request(options);
+ req.write('WjN}|M(6');
+ req.end();
+ });
+ wss.on('connection', function(ws) {
+ wss.close();
+ done();
+ });
+ wss.on('error', function() {});
+ });
+
+ it('handles messages passed along with the upgrade request (upgrade head)', function(done) {
+ var wss = new WebSocketServer({port: ++port, verifyClient: function(o) {
+ return true;
+ }}, function() {
+ var options = {
+ port: port,
+ host: '127.0.0.1',
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'WebSocket',
+ 'Sec-WebSocket-Key1': '3e6b263 4 17 80',
+ 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90',
+ 'Origin': 'http://foobar.com'
+ }
+ };
+ var req = http.request(options);
+ req.write('WjN}|M(6');
+ req.write(new Buffer([0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff], 'binary'));
+ req.end();
+ });
+ wss.on('connection', function(ws) {
+ ws.on('message', function(data) {
+ data.should.eql('Hello');
+ ws.terminate();
+ wss.close();
+ done();
+ });
+ });
+ wss.on('error', function() {});
+ });
+ });
+ });
+
+ describe('client properties', function() {
+ it('protocol is exposed', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var ws = new WebSocket('ws://localhost:' + port, {protocol: 'hi'});
+ });
+ wss.on('connection', function(client) {
+ client.protocol.should.eql('hi');
+ wss.close();
+ done();
+ });
+ });
+
+ it('protocolVersion is exposed', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var ws = new WebSocket('ws://localhost:' + port, {protocolVersion: 8});
+ });
+ wss.on('connection', function(client) {
+ client.protocolVersion.should.eql(8);
+ wss.close();
+ done();
+ });
+ });
+
+ it('upgradeReq is the original request object', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var ws = new WebSocket('ws://localhost:' + port, {protocolVersion: 8});
+ });
+ wss.on('connection', function(client) {
+ client.upgradeReq.httpVersion.should.eql('1.1');
+ wss.close();
+ done();
+ });
+ });
+ });
+
+});
diff --git a/node_modules/ws/test/autobahn-server.js b/node_modules/ws/test/autobahn-server.js
new file mode 100644
index 0000000..36fe0c2
--- /dev/null
+++ b/node_modules/ws/test/autobahn-server.js
@@ -0,0 +1,29 @@
+var WebSocketServer = require('../').Server;
+
+process.on('uncaughtException', function(err) {
+ console.log('Caught exception: ', err, err.stack);
+});
+
+process.on('SIGINT', function () {
+ try {
+ console.log('Updating reports and shutting down');
+ var ws = new WebSocket('ws://localhost:9001/updateReports?agent=ws');
+ ws.on('close', function() {
+ process.exit();
+ });
+ }
+ catch(e) {
+ process.exit();
+ }
+});
+
+var wss = new WebSocketServer({port: 8181});
+wss.on('connection', function(ws) {
+ console.log('new connection');
+ ws.on('message', function(data, flags) {
+ ws.send(flags.buffer, {binary: flags.binary === true});
+ });
+ ws.on('error', function() {
+ console.log('error', arguments);
+ });
+});
diff --git a/node_modules/ws/test/autobahn.js b/node_modules/ws/test/autobahn.js
new file mode 100644
index 0000000..048cc90
--- /dev/null
+++ b/node_modules/ws/test/autobahn.js
@@ -0,0 +1,52 @@
+var WebSocket = require('../');
+var currentTest = 1;
+var lastTest = -1;
+var testCount = null;
+
+process.on('uncaughtException', function(err) {
+ console.log('Caught exception: ', err, err.stack);
+});
+
+process.on('SIGINT', function () {
+ try {
+ console.log('Updating reports and shutting down');
+ var ws = new WebSocket('ws://localhost:9001/updateReports?agent=ws');
+ ws.on('close', function() {
+ process.exit();
+ });
+ }
+ catch(e) {
+ process.exit();
+ }
+});
+
+function nextTest() {
+ if (currentTest > testCount || (lastTest != -1 && currentTest > lastTest)) {
+ console.log('Updating reports and shutting down');
+ var ws = new WebSocket('ws://localhost:9001/updateReports?agent=ws');
+ ws.on('close', function() {
+ process.exit();
+ });
+ return;
+ };
+ console.log('Running test case ' + currentTest + '/' + testCount);
+ var ws = new WebSocket('ws://localhost:9001/runCase?case=' + currentTest + '&agent=ws');
+ ws.on('message', function(data, flags) {
+ ws.send(flags.buffer, {binary: flags.binary === true, mask: true});
+ });
+ ws.on('close', function(data) {
+ currentTest += 1;
+ process.nextTick(nextTest);
+ });
+ ws.on('error', function(e) {});
+}
+
+var ws = new WebSocket('ws://localhost:9001/getCaseCount');
+ws.on('message', function(data, flags) {
+ testCount = parseInt(data);
+});
+ws.on('close', function() {
+ if (testCount > 0) {
+ nextTest();
+ }
+}); \ No newline at end of file
diff --git a/node_modules/ws/test/fixtures/agent1-cert.pem b/node_modules/ws/test/fixtures/agent1-cert.pem
new file mode 100644
index 0000000..cccb9fb
--- /dev/null
+++ b/node_modules/ws/test/fixtures/agent1-cert.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICbjCCAdcCCQCVvok5oeLpqzANBgkqhkiG9w0BAQUFADB6MQswCQYDVQQGEwJV
+UzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO
+BgNVBAsTB05vZGUuanMxDDAKBgNVBAMTA2NhMTEgMB4GCSqGSIb3DQEJARYRcnlA
+dGlueWNsb3Vkcy5vcmcwHhcNMTMwMzA4MDAzMDIyWhcNNDAwNzIzMDAzMDIyWjB9
+MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQK
+EwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MTEgMB4G
+CSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwgZ8wDQYJKoZIhvcNAQEBBQAD
+gY0AMIGJAoGBAL6GwKosYb0Yc3Qo0OtQVlCJ4208Idw11ij+t2W5sfYbCil5tyQo
+jnhGM1CJhEXynQpXXwjKJuIeTQCkeUibTyFKa0bs8+li2FiGoKYbb4G81ovnqkmE
+2iDVb8Gw3rrM4zeZ0ZdFnjMsAZac8h6+C4sB/pS9BiMOo6qTl15RQlcJAgMBAAEw
+DQYJKoZIhvcNAQEFBQADgYEAOtmLo8DwTPnI4wfQbQ3hWlTS/9itww6IsxH2ODt9
+ggB7wi7N3uAdIWRZ54ke0NEAO5CW1xNTwsWcxQbiHrDOqX1vfVCjIenI76jVEEap
+/Ay53ydHNBKdsKkib61Me14Mu0bA3lUul57VXwmH4NUEFB3w973Q60PschUhOEXj
+7DY=
+-----END CERTIFICATE-----
diff --git a/node_modules/ws/test/fixtures/agent1-key.pem b/node_modules/ws/test/fixtures/agent1-key.pem
new file mode 100644
index 0000000..cbd5f0c
--- /dev/null
+++ b/node_modules/ws/test/fixtures/agent1-key.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQC+hsCqLGG9GHN0KNDrUFZQieNtPCHcNdYo/rdlubH2Gwopebck
+KI54RjNQiYRF8p0KV18IyibiHk0ApHlIm08hSmtG7PPpYthYhqCmG2+BvNaL56pJ
+hNog1W/BsN66zOM3mdGXRZ4zLAGWnPIevguLAf6UvQYjDqOqk5deUUJXCQIDAQAB
+AoGANu/CBA+SCyVOvRK70u4yRTzNMAUjukxnuSBhH1rg/pajYnwvG6T6F6IeT72n
+P0gKkh3JUE6B0bds+p9yPUZTFUXghxjcF33wlIY44H6gFE4K5WutsFJ9c450wtuu
+8rXZTsIg7lAXWjTFVmdtOEPetcGlO2Hpi1O7ZzkzHgB2w9ECQQDksCCYx78or1zY
+ZSokm8jmpIjG3VLKdvI9HAoJRN40ldnwFoigrFa1AHwsFtWNe8bKyVRPDoLDUjpB
+dkPWgweVAkEA1UfgqguQ2KIkbtp9nDBionu3QaajksrRHwIa8vdfRfLxszfHk2fh
+NGY3dkRZF8HUAbzYLrd9poVhCBAEjWekpQJASOM6AHfpnXYHCZF01SYx6hEW5wsz
+kARJQODm8f1ZNTlttO/5q/xBxn7ZFNRSTD3fJlL05B2j380ddC/Vf1FT4QJAP1BC
+GliqnBSuGhZUWYxni3KMeTm9rzL0F29pjpzutHYlWB2D6ndY/FQnvL0XcZ0Bka58
+womIDGnl3x3aLBwLXQJBAJv6h5CHbXHx7VyDJAcNfppAqZGcEaiVg8yf2F33iWy2
+FLthhJucx7df7SO2aw5h06bRDRAhb9br0R9/3mLr7RE=
+-----END RSA PRIVATE KEY-----
diff --git a/node_modules/ws/test/fixtures/ca1-cert.pem b/node_modules/ws/test/fixtures/ca1-cert.pem
new file mode 100644
index 0000000..1d0c0d6
--- /dev/null
+++ b/node_modules/ws/test/fixtures/ca1-cert.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICazCCAdQCCQC9/g69HtxXRzANBgkqhkiG9w0BAQUFADB6MQswCQYDVQQGEwJV
+UzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO
+BgNVBAsTB05vZGUuanMxDDAKBgNVBAMTA2NhMTEgMB4GCSqGSIb3DQEJARYRcnlA
+dGlueWNsb3Vkcy5vcmcwHhcNMTMwMzA4MDAzMDIyWhcNNDAwNzIzMDAzMDIyWjB6
+MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQK
+EwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDDAKBgNVBAMTA2NhMTEgMB4GCSqG
+SIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0A
+MIGJAoGBAKxr1mARUcv7zaqx5y4AxJPK6c1jdbSg7StcL4vg8klaPAlfNO6o+/Cl
+w5CdQD3ukaVUwUOJ4T/+b3Xf7785XcWBC33GdjVQkfbHATJYcka7j7JDw3qev5Jk
+1rAbRw48hF6rYlSGcx1mccAjoLoa3I8jgxCNAYHIjUQXgdmU893rAgMBAAEwDQYJ
+KoZIhvcNAQEFBQADgYEAis05yxjCtJRuv8uX/DK6TX/j9C9Lzp1rKDNFTaTZ0iRw
+KCw1EcNx4OXSj9gNblW4PWxpDvygrt1AmH9h2cb8K859NSHa9JOBFw6MA5C2A4Sj
+NQfNATqUl4T6cdORlcDEZwHtT8b6D4A6Er31G/eJF4Sen0TUFpjdjd+l9RBjHlo=
+-----END CERTIFICATE-----
diff --git a/node_modules/ws/test/fixtures/ca1-key.pem b/node_modules/ws/test/fixtures/ca1-key.pem
new file mode 100644
index 0000000..df14950
--- /dev/null
+++ b/node_modules/ws/test/fixtures/ca1-key.pem
@@ -0,0 +1,17 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIFeWxJE1BrRECAggA
+MBQGCCqGSIb3DQMHBAgu9PlMSQ+BOASCAoDEZN2tX0xWo/N+Jg+PrvCrFDk3P+3x
+5xG/PEDjtMCAWPBEwbnaYHDzYmhNcAmxzGqEHGMDiWYs46LbO560VS3uMvFbEWPo
+KYYVb13vkxl2poXdonCb5cHZA5GUYzTIVVJFptl4LHwBczHoMHtA4FqAhKlYvlWw
+EOrdLB8XcwMmGPFabbbGxno0+EWWM27uNjlogfoxj35mQqSW4rOlhZ460XjOB1Zx
+LjXMuZeONojkGYQRG5EUMchBoctQpCOM6cAi9r1B9BvtFCBpDV1c1zEZBzTEUd8o
+kLn6tjLmY+QpTdylFjEWc7U3ppLY/pkoTBv4r85a2sEMWqkhSJboLaTboWzDJcU3
+Ke61pMpovt/3yCUd3TKgwduVwwQtDVTlBe0p66aN9QVj3CrFy/bKAGO3vxlli24H
+aIjZf+OVoBY21ESlW3jLvNlBf7Ezf///2E7j4SCDLyZSFMTpFoAG/jDRyvi+wTKX
+Kh485Bptnip6DCSuoH4u2SkOqwz3gJS/6s02YKe4m311QT4Pzne5/FwOFaS/HhQg
+Xvyh2/d00OgJ0Y0PYQsHILPRgTUCKUXvj1O58opn3fxSacsPxIXwj6Z4FYAjUTaV
+2B85k1lpant/JJEilDqMjqzx4pHZ/Z3Uto1lSM1JZs9SNL/0UR+6F0TXZTULVU9V
+w8jYzz4sPr7LEyrrTbzmjQgnQFVbhAN/eKgRZK/SpLjxpmBV5MfpbPKsPUZqT4UC
+4nXa8a/NYUQ9e+QKK8enq9E599c2W442W7Z1uFRZTWReMx/lF8wwA6G8zOPG0bdj
+d+T5Gegzd5mvRiXMBklCo8RLxOOvgxun1n3PY4a63aH6mqBhdfhiLp5j
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/node_modules/ws/test/fixtures/certificate.pem b/node_modules/ws/test/fixtures/certificate.pem
new file mode 100644
index 0000000..0efc2ef
--- /dev/null
+++ b/node_modules/ws/test/fixtures/certificate.pem
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIICATCCAWoCCQDPufXH86n2QzANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJu
+bzETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMB4XDTEyMDEwMTE0NDQwMFoXDTIwMDMxOTE0NDQwMFowRTELMAkG
+A1UEBhMCbm8xEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0
+IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtrQ7
++r//2iV/B6F+4boH0XqFn7alcV9lpjvAmwRXNKnxAoa0f97AjYPGNLKrjpkNXXhB
+JROIdbRbZnCNeC5fzX1a+JCo7KStzBXuGSZr27TtFmcV4H+9gIRIcNHtZmJLnxbJ
+sIhkGR8yVYdmJZe4eT5ldk1zoB1adgPF1hZhCBMCAwEAATANBgkqhkiG9w0BAQUF
+AAOBgQCeWBEHYJ4mCB5McwSSUox0T+/mJ4W48L/ZUE4LtRhHasU9hiW92xZkTa7E
+QLcoJKQiWfiLX2ysAro0NX4+V8iqLziMqvswnPzz5nezaOLE/9U/QvH3l8qqNkXu
+rNbsW1h/IO6FV8avWFYVFoutUwOaZ809k7iMh2F2JMgXQ5EymQ==
+-----END CERTIFICATE-----
diff --git a/node_modules/ws/test/fixtures/key.pem b/node_modules/ws/test/fixtures/key.pem
new file mode 100644
index 0000000..176fe32
--- /dev/null
+++ b/node_modules/ws/test/fixtures/key.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQC2tDv6v//aJX8HoX7hugfReoWftqVxX2WmO8CbBFc0qfEChrR/
+3sCNg8Y0squOmQ1deEElE4h1tFtmcI14Ll/NfVr4kKjspK3MFe4ZJmvbtO0WZxXg
+f72AhEhw0e1mYkufFsmwiGQZHzJVh2Yll7h5PmV2TXOgHVp2A8XWFmEIEwIDAQAB
+AoGAAlVY8sHi/aE+9xT77twWX3mGHV0SzdjfDnly40fx6S1Gc7bOtVdd9DC7pk6l
+3ENeJVR02IlgU8iC5lMHq4JEHPE272jtPrLlrpWLTGmHEqoVFv9AITPqUDLhB9Kk
+Hjl7h8NYBKbr2JHKICr3DIPKOT+RnXVb1PD4EORbJ3ooYmkCQQDfknUnVxPgxUGs
+ouABw1WJIOVgcCY/IFt4Ihf6VWTsxBgzTJKxn3HtgvE0oqTH7V480XoH0QxHhjLq
+DrgobWU9AkEA0TRJ8/ouXGnFEPAXjWr9GdPQRZ1Use2MrFjneH2+Sxc0CmYtwwqL
+Kr5kS6mqJrxprJeluSjBd+3/ElxURrEXjwJAUvmlN1OPEhXDmRHd92mKnlkyKEeX
+OkiFCiIFKih1S5Y/sRJTQ0781nyJjtJqO7UyC3pnQu1oFEePL+UEniRztQJAMfav
+AtnpYKDSM+1jcp7uu9BemYGtzKDTTAYfoiNF42EzSJiGrWJDQn4eLgPjY0T0aAf/
+yGz3Z9ErbhMm/Ysl+QJBAL4kBxRT8gM4ByJw4sdOvSeCCANFq8fhbgm8pGWlCPb5
+JGmX3/GHFM8x2tbWMGpyZP1DLtiNEFz7eCGktWK5rqE=
+-----END RSA PRIVATE KEY-----
diff --git a/node_modules/ws/test/fixtures/request.pem b/node_modules/ws/test/fixtures/request.pem
new file mode 100644
index 0000000..51bc7f6
--- /dev/null
+++ b/node_modules/ws/test/fixtures/request.pem
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIBhDCB7gIBADBFMQswCQYDVQQGEwJubzETMBEGA1UECAwKU29tZS1TdGF0ZTEh
+MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQC2tDv6v//aJX8HoX7hugfReoWftqVxX2WmO8CbBFc0qfEC
+hrR/3sCNg8Y0squOmQ1deEElE4h1tFtmcI14Ll/NfVr4kKjspK3MFe4ZJmvbtO0W
+ZxXgf72AhEhw0e1mYkufFsmwiGQZHzJVh2Yll7h5PmV2TXOgHVp2A8XWFmEIEwID
+AQABoAAwDQYJKoZIhvcNAQEFBQADgYEAjsUXEARgfxZNkMjuUcudgU2w4JXS0gGI
+JQ0U1LmU0vMDSKwqndMlvCbKzEgPbJnGJDI8D4MeINCJHa5Ceyb8c+jaJYUcCabl
+lQW5Psn3+eWp8ncKlIycDRj1Qk615XuXtV0fhkrgQM2ZCm9LaQ1O1Gd/CzLihLjF
+W0MmgMKMMRk=
+-----END CERTIFICATE REQUEST-----
diff --git a/node_modules/ws/test/fixtures/textfile b/node_modules/ws/test/fixtures/textfile
new file mode 100644
index 0000000..a10483b
--- /dev/null
+++ b/node_modules/ws/test/fixtures/textfile
@@ -0,0 +1,9 @@
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam egestas, massa at aliquam luctus, sapien erat viverra elit, nec pulvinar turpis eros sagittis urna. Pellentesque imperdiet tempor varius. Pellentesque blandit, ipsum in imperdiet venenatis, mi elit faucibus odio, id condimentum ante enim sed lectus. Aliquam et odio non odio pellentesque pulvinar. Vestibulum a erat dolor. Integer pretium risus sit amet nisl volutpat nec venenatis magna egestas. Ut bibendum felis eu tellus laoreet eleifend. Nam pulvinar auctor tortor, eu iaculis leo vestibulum quis. In euismod risus ac purus vehicula et fermentum ligula consectetur. Vivamus condimentum tempus lacinia.
+
+Curabitur sodales condimentum urna id dictum. Sed quis justo sit amet quam ultrices tincidunt vel laoreet nulla. Nullam quis ipsum sed nisi mollis bibendum at sit amet nisi. Donec laoreet consequat velit sit amet mollis. Nam sed sapien a massa iaculis dapibus. Sed dui nunc, ultricies et pellentesque ullamcorper, aliquet vitae ligula. Integer eu velit in neque iaculis venenatis. Ut rhoncus cursus est, ac dignissim leo vehicula a. Nulla ullamcorper vulputate mauris id blandit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque eleifend, nisi a tempor sollicitudin, odio massa pretium urna, quis congue sapien elit at tortor. Curabitur ipsum orci, vehicula non commodo molestie, laoreet id enim. Pellentesque convallis ultrices congue. Pellentesque nec iaculis lorem. In sagittis pharetra ipsum eget sodales.
+
+Fusce id nulla odio. Nunc nibh justo, placerat vel tincidunt sed, ornare et enim. Nulla vel urna vel ante commodo bibendum in vitae metus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis erat nunc, semper eget sagittis sit amet, ullamcorper eget lacus. Donec hendrerit ipsum vitae eros vestibulum eu gravida neque tincidunt. Ut molestie lacinia nulla. Donec mattis odio at magna egestas at pellentesque eros accumsan. Praesent interdum sem sit amet nibh commodo dignissim. Duis laoreet, enim ultricies fringilla suscipit, enim libero cursus nulla, sollicitudin adipiscing erat velit ut dui. Nulla eleifend mauris at velit fringilla a molestie lorem venenatis.
+
+Donec sit amet scelerisque metus. Cras ac felis a nulla venenatis vulputate. Duis porttitor eros ac neque rhoncus eget aliquet neque egestas. Quisque sed nunc est, vitae dapibus quam. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In vehicula, est vitae posuere ultricies, diam purus pretium sapien, nec rhoncus dolor nisl eget arcu. Aliquam et nisi vitae risus tincidunt auctor. In vehicula, erat a cursus adipiscing, lorem orci congue est, nec ultricies elit dui in nunc. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+
+Duis congue tempus elit sit amet auctor. Duis dignissim, risus ut sollicitudin ultricies, dolor ligula gravida odio, nec congue orci purus ut ligula. Fusce pretium dictum lectus at volutpat. Sed non auctor mauris. Etiam placerat vestibulum massa id blandit. Quisque consequat lacus ut nulla euismod facilisis. Sed aliquet ipsum nec mi imperdiet viverra. Pellentesque ullamcorper, lectus nec varius gravida, odio justo cursus risus, eu sagittis metus arcu quis felis. Phasellus consectetur vehicula libero, at condimentum orci euismod vel. Nunc purus tortor, suscipit nec fringilla nec, vulputate et nibh. Nam porta vehicula neque. Praesent porttitor, sapien eu auctor euismod, arcu quam elementum urna, sed hendrerit magna augue sed quam. \ No newline at end of file
diff --git a/node_modules/ws/test/hybi-common.js b/node_modules/ws/test/hybi-common.js
new file mode 100644
index 0000000..006f9c6
--- /dev/null
+++ b/node_modules/ws/test/hybi-common.js
@@ -0,0 +1,99 @@
+/**
+ * Returns a Buffer from a "ff 00 ff"-type hex string.
+ */
+
+getBufferFromHexString = function(byteStr) {
+ var bytes = byteStr.split(' ');
+ var buf = new Buffer(bytes.length);
+ for (var i = 0; i < bytes.length; ++i) {
+ buf[i] = parseInt(bytes[i], 16);
+ }
+ return buf;
+}
+
+/**
+ * Returns a hex string from a Buffer.
+ */
+
+getHexStringFromBuffer = function(data) {
+ var s = '';
+ for (var i = 0; i < data.length; ++i) {
+ s += padl(data[i].toString(16), 2, '0') + ' ';
+ }
+ return s.trim();
+}
+
+/**
+ * Splits a buffer in two parts.
+ */
+
+splitBuffer = function(buffer) {
+ var b1 = new Buffer(Math.ceil(buffer.length / 2));
+ buffer.copy(b1, 0, 0, b1.length);
+ var b2 = new Buffer(Math.floor(buffer.length / 2));
+ buffer.copy(b2, 0, b1.length, b1.length + b2.length);
+ return [b1, b2];
+}
+
+/**
+ * Performs hybi07+ type masking on a hex string or buffer.
+ */
+
+mask = function(buf, maskString) {
+ if (typeof buf == 'string') buf = new Buffer(buf);
+ var mask = getBufferFromHexString(maskString || '34 83 a8 68');
+ for (var i = 0; i < buf.length; ++i) {
+ buf[i] ^= mask[i % 4];
+ }
+ return buf;
+}
+
+/**
+ * Returns a hex string representing the length of a message
+ */
+
+getHybiLengthAsHexString = function(len, masked) {
+ if (len < 126) {
+ var buf = new Buffer(1);
+ buf[0] = (masked ? 0x80 : 0) | len;
+ }
+ else if (len < 65536) {
+ var buf = new Buffer(3);
+ buf[0] = (masked ? 0x80 : 0) | 126;
+ getBufferFromHexString(pack(4, len)).copy(buf, 1);
+ }
+ else {
+ var buf = new Buffer(9);
+ buf[0] = (masked ? 0x80 : 0) | 127;
+ getBufferFromHexString(pack(16, len)).copy(buf, 1);
+ }
+ return getHexStringFromBuffer(buf);
+}
+
+/**
+ * Unpacks a Buffer into a number.
+ */
+
+unpack = function(buffer) {
+ var n = 0;
+ for (var i = 0; i < buffer.length; ++i) {
+ n = (i == 0) ? buffer[i] : (n * 256) + buffer[i];
+ }
+ return n;
+}
+
+/**
+ * Returns a hex string, representing a specific byte count 'length', from a number.
+ */
+
+pack = function(length, number) {
+ return padl(number.toString(16), length, '0').replace(/([0-9a-f][0-9a-f])/gi, '$1 ').trim();
+}
+
+/**
+ * Left pads the string 's' to a total length of 'n' with char 'c'.
+ */
+
+padl = function(s, n, c) {
+ return new Array(1 + n - s.length).join(c) + s;
+}
diff --git a/node_modules/ws/test/testserver.js b/node_modules/ws/test/testserver.js
new file mode 100644
index 0000000..3e7a966
--- /dev/null
+++ b/node_modules/ws/test/testserver.js
@@ -0,0 +1,180 @@
+var http = require('http')
+ , util = require('util')
+ , crypto = require('crypto')
+ , events = require('events')
+ , Sender = require('../lib/Sender')
+ , Receiver = require('../lib/Receiver');
+
+module.exports = {
+ handlers: {
+ valid: validServer,
+ invalidKey: invalidRequestHandler,
+ closeAfterConnect: closeAfterConnectHandler,
+ return401: return401
+ },
+ createServer: function(port, handler, cb) {
+ if (handler && !cb) {
+ cb = handler;
+ handler = null;
+ }
+ var webServer = http.createServer(function (req, res) {
+ res.writeHead(200, {'Content-Type': 'text/plain'});
+ res.end('okay');
+ });
+ var srv = new Server(webServer);
+ webServer.on('upgrade', function(req, socket) {
+ webServer._socket = socket;
+ (handler || validServer)(srv, req, socket);
+ });
+ webServer.listen(port, '127.0.0.1', function() { cb(srv); });
+ }
+};
+
+/**
+ * Test strategies
+ */
+
+function validServer(server, req, socket) {
+ if (typeof req.headers.upgrade === 'undefined' ||
+ req.headers.upgrade.toLowerCase() !== 'websocket') {
+ throw new Error('invalid headers');
+ return;
+ }
+
+ if (!req.headers['sec-websocket-key']) {
+ socket.end();
+ throw new Error('websocket key is missing');
+ }
+
+ // 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
+ ];
+
+ socket.write(headers.concat('', '').join('\r\n'));
+ socket.setTimeout(0);
+ socket.setNoDelay(true);
+
+ var sender = new Sender(socket);
+ var receiver = new Receiver();
+ receiver.ontext = function (message, flags) {
+ server.emit('message', message, flags);
+ sender.send(message);
+ };
+ receiver.onbinary = function (message, flags) {
+ flags = flags || {};
+ flags.binary = true;
+ server.emit('message', message, flags);
+ sender.send(message, {binary: true});
+ };
+ receiver.onping = function (message, flags) {
+ flags = flags || {};
+ server.emit('ping', message, flags);
+ };
+ receiver.onpong = function (message, flags) {
+ flags = flags || {};
+ server.emit('pong', message, flags);
+ };
+ receiver.onclose = function (code, message, flags) {
+ flags = flags || {};
+ server.emit('close', code, message, flags);
+ };
+ socket.on('data', function (data) {
+ receiver.add(data);
+ });
+ socket.on('end', function() {
+ socket.end();
+ });
+}
+
+function invalidRequestHandler(server, req, socket) {
+ if (typeof req.headers.upgrade === 'undefined' ||
+ req.headers.upgrade.toLowerCase() !== 'websocket') {
+ throw new Error('invalid headers');
+ return;
+ }
+
+ if (!req.headers['sec-websocket-key']) {
+ socket.end();
+ throw new Error('websocket key is missing');
+ }
+
+ // calc key
+ var key = req.headers['sec-websocket-key'];
+ var shasum = crypto.createHash('sha1');
+ shasum.update(key + "bogus");
+ key = shasum.digest('base64');
+
+ var headers = [
+ 'HTTP/1.1 101 Switching Protocols'
+ , 'Upgrade: websocket'
+ , 'Connection: Upgrade'
+ , 'Sec-WebSocket-Accept: ' + key
+ ];
+
+ socket.write(headers.concat('', '').join('\r\n'));
+ socket.end();
+}
+
+function closeAfterConnectHandler(server, req, socket) {
+ if (typeof req.headers.upgrade === 'undefined' ||
+ req.headers.upgrade.toLowerCase() !== 'websocket') {
+ throw new Error('invalid headers');
+ return;
+ }
+
+ if (!req.headers['sec-websocket-key']) {
+ socket.end();
+ throw new Error('websocket key is missing');
+ }
+
+ // 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
+ ];
+
+ socket.write(headers.concat('', '').join('\r\n'));
+ socket.end();
+}
+
+
+function return401(server, req, socket) {
+ var headers = [
+ 'HTTP/1.1 401 Unauthorized'
+ , 'Content-type: text/html'
+ ];
+
+ socket.write(headers.concat('', '').join('\r\n'));
+ socket.end();
+}
+
+/**
+ * Server object, which will do the actual emitting
+ */
+
+function Server(webServer) {
+ this.webServer = webServer;
+}
+
+util.inherits(Server, events.EventEmitter);
+
+Server.prototype.close = function() {
+ this.webServer.close();
+ if (this._socket) this._socket.end();
+}