summaryrefslogtreecommitdiff
path: root/node_modules/socket.io
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/socket.io')
-rw-r--r--node_modules/socket.io/.npmignore3
-rw-r--r--node_modules/socket.io/.travis.yml6
-rw-r--r--node_modules/socket.io/History.md246
-rw-r--r--node_modules/socket.io/Makefile31
-rw-r--r--node_modules/socket.io/Readme.md345
-rw-r--r--node_modules/socket.io/benchmarks/decode.bench.js64
-rw-r--r--node_modules/socket.io/benchmarks/encode.bench.js90
-rw-r--r--node_modules/socket.io/benchmarks/runner.js55
-rw-r--r--node_modules/socket.io/examples/chat/app.js80
-rw-r--r--node_modules/socket.io/examples/chat/index.jade83
-rw-r--r--node_modules/socket.io/examples/chat/package.json11
-rw-r--r--node_modules/socket.io/examples/chat/public/stylesheets/mixins.styl96
-rw-r--r--node_modules/socket.io/examples/chat/public/stylesheets/style.css188
-rw-r--r--node_modules/socket.io/examples/chat/public/stylesheets/style.styl118
-rw-r--r--node_modules/socket.io/examples/irc-output/app.js74
-rw-r--r--node_modules/socket.io/examples/irc-output/index.jade28
-rw-r--r--node_modules/socket.io/examples/irc-output/irc.js164
-rw-r--r--node_modules/socket.io/examples/irc-output/package.json10
-rw-r--r--node_modules/socket.io/examples/irc-output/public/stylesheets/style.styl69
-rw-r--r--node_modules/socket.io/index.js8
-rw-r--r--node_modules/socket.io/lib/client.js167
-rw-r--r--node_modules/socket.io/lib/logger.js97
-rw-r--r--node_modules/socket.io/lib/manager.js983
-rw-r--r--node_modules/socket.io/lib/namespace.js355
-rw-r--r--node_modules/socket.io/lib/parser.js249
-rw-r--r--node_modules/socket.io/lib/socket.io.js136
-rw-r--r--node_modules/socket.io/lib/socket.js369
-rw-r--r--node_modules/socket.io/lib/static.js395
-rw-r--r--node_modules/socket.io/lib/store.js98
-rw-r--r--node_modules/socket.io/lib/stores/memory.js143
-rw-r--r--node_modules/socket.io/lib/stores/redis.js269
-rw-r--r--node_modules/socket.io/lib/transport.js534
-rw-r--r--node_modules/socket.io/lib/transports/flashsocket.js106
-rw-r--r--node_modules/socket.io/lib/transports/htmlfile.js82
-rw-r--r--node_modules/socket.io/lib/transports/http-polling.js135
-rw-r--r--node_modules/socket.io/lib/transports/http.js118
-rw-r--r--node_modules/socket.io/lib/transports/index.js12
-rw-r--r--node_modules/socket.io/lib/transports/jsonp-polling.js96
-rw-r--r--node_modules/socket.io/lib/transports/websocket.js36
-rw-r--r--node_modules/socket.io/lib/transports/websocket/default.js360
-rw-r--r--node_modules/socket.io/lib/transports/websocket/hybi-07-12.js622
-rw-r--r--node_modules/socket.io/lib/transports/websocket/hybi-16.js623
-rw-r--r--node_modules/socket.io/lib/transports/websocket/index.js11
-rw-r--r--node_modules/socket.io/lib/transports/xhr-polling.js69
-rw-r--r--node_modules/socket.io/lib/util.js50
-rw-r--r--node_modules/socket.io/package.json69
-rw-r--r--node_modules/socket.io/support/node-websocket-client/LICENSE27
-rw-r--r--node_modules/socket.io/support/node-websocket-client/Makefile22
-rw-r--r--node_modules/socket.io/support/node-websocket-client/README.md41
-rw-r--r--node_modules/socket.io/support/node-websocket-client/examples/client-unix.js12
-rw-r--r--node_modules/socket.io/support/node-websocket-client/examples/client.js10
-rw-r--r--node_modules/socket.io/support/node-websocket-client/examples/server-unix.js13
-rw-r--r--node_modules/socket.io/support/node-websocket-client/lib/websocket.js617
-rw-r--r--node_modules/socket.io/support/node-websocket-client/package.json22
-rw-r--r--node_modules/socket.io/support/node-websocket-client/test/test-basic.js68
-rw-r--r--node_modules/socket.io/support/node-websocket-client/test/test-client-close.js43
-rw-r--r--node_modules/socket.io/support/node-websocket-client/test/test-readonly-attrs.js43
-rw-r--r--node_modules/socket.io/support/node-websocket-client/test/test-ready-state.js26
-rw-r--r--node_modules/socket.io/support/node-websocket-client/test/test-server-close.js41
-rw-r--r--node_modules/socket.io/support/node-websocket-client/test/test-unix-send-fd.js63
-rw-r--r--node_modules/socket.io/support/node-websocket-client/test/test-unix-sockets.js46
61 files changed, 9047 insertions, 0 deletions
diff --git a/node_modules/socket.io/.npmignore b/node_modules/socket.io/.npmignore
new file mode 100644
index 0000000..39e9864
--- /dev/null
+++ b/node_modules/socket.io/.npmignore
@@ -0,0 +1,3 @@
+support
+test
+examples
diff --git a/node_modules/socket.io/.travis.yml b/node_modules/socket.io/.travis.yml
new file mode 100644
index 0000000..56eca03
--- /dev/null
+++ b/node_modules/socket.io/.travis.yml
@@ -0,0 +1,6 @@
+language: node_js
+node_js:
+ - 0.6
+
+notifications:
+ irc: "irc.freenode.org#socket.io"
diff --git a/node_modules/socket.io/History.md b/node_modules/socket.io/History.md
new file mode 100644
index 0000000..e0b954a
--- /dev/null
+++ b/node_modules/socket.io/History.md
@@ -0,0 +1,246 @@
+
+0.9.4 / 2012-04-01
+==================
+
+ * Disconnecting from namespace improvement (#795) [DanielBaulig]
+ * Bumped client with polling reconnection loop (#438)
+
+0.9.3 / 2012-03-28
+==================
+
+ * Fix "Syntax error" on FF Web Console with XHR Polling [mikito]
+
+0.9.2 / 2012-03-13
+==================
+
+ * More sensible close `timeout default` (fixes disconnect issue)
+
+0.9.1-1 / 2012-03-02
+====================
+
+ * Bumped client with NPM dependency fix.
+
+0.9.1 / 2012-03-02
+==================
+
+ * Changed heartbeat timeout and interval defaults (60 and 25 seconds)
+ * Make tests work both on 0.4 and 0.6
+ * Updated client (improvements + bug fixes).
+
+0.9.0 / 2012-02-26
+==================
+
+ * Make it possible to use a regexp to match the socket.io resource URL.
+ We need this because we have to prefix the socket.io URL with a variable ID.
+ * Supplemental fix to gavinuhma/authfix, it looks like the same Access-Control-Origin logic is needed in the http and xhr-polling transports
+ * Updated express dep for windows compatibility.
+ * Combine two substr calls into one in decodePayload to improve performance
+ * Minor documentation fix
+ * Minor. Conform to style of other files.
+ * Switching setting to 'match origin protocol'
+ * Revert "Fixes leaking Redis subscriptions for #663. The local flag was not getting passed through onClientDisconnect()."
+ * Revert "Handle leaked dispatch:[id] subscription."
+ * Merge pull request #667 from dshaw/patch/redis-disconnect
+ * Handle leaked dispatch:[id] subscription.
+ * Fixes leaking Redis subscriptions for #663. The local flag was not getting passed through onClientDisconnect().
+ * Prevent memory leaking on uncompleted requests & add max post size limitation
+ * Fix for testcase
+ * Set Access-Control-Allow-Credentials true, regardless of cookie
+ * Remove assertvarnish from package as it breaks on 0.6
+ * Correct irc channel
+ * Added proper return after reserved field error
+ * Fixes manager.js failure to close connection after transport error has happened
+ * Added implicit port 80 for origin checks. fixes #638
+ * Fixed bug #432 in 0.8.7
+ * Set Access-Control-Allow-Origin header to origin to enable withCredentials
+ * Adding configuration variable matchOriginProtocol
+ * Fixes location mismatch error in Safari.
+ * Use tty to detect if we should add colors or not by default.
+ * Updated the package location.
+
+0.8.7 / 2011-11-05
+==================
+
+ * Fixed memory leaks in closed clients.
+ * Fixed memory leaks in namespaces.
+ * Fixed websocket handling for malformed requests from proxies. [einaros]
+ * Node 0.6 compatibility. [einaros] [3rd-Eden]
+ * Adapted tests and examples.
+
+0.8.6 / 2011-10-27
+==================
+
+ * Added JSON decoding on jsonp-polling transport.
+ * Fixed README example.
+ * Major speed optimizations [3rd-Eden] [einaros] [visionmedia]
+ * Added decode/encode benchmarks [visionmedia]
+ * Added support for black-listing client sent events.
+ * Fixed logging options, closes #540 [3rd-Eden]
+ * Added vary header for gzip [3rd-Eden]
+ * Properly cleaned up async websocket / flashsocket tests, after patching node-websocket-client
+ * Patched to properly shut down when a finishClose call is made during connection establishment
+ * Added support for socket.io version on url and far-future Expires [3rd-Eden] [getify]
+ * Began IE10 compatibility [einaros] [tbranyen]
+ * Misc WebSocket fixes [einaros]
+ * Added UTF8 to respone headers for htmlfile [3rd-Eden]
+
+0.8.5 / 2011-10-07
+==================
+
+ * Added websocket draft HyBi-16 support. [einaros]
+ * Fixed websocket continuation bugs. [einaros]
+ * Fixed flashsocket transport name.
+ * Fixed websocket tests.
+ * Ensured `parser#decodePayload` doesn't choke.
+ * Added http referrer verification to manager verifyOrigin.
+ * Added access control for cross domain xhr handshakes [3rd-Eden]
+ * Added support for automatic generation of socket.io files [3rd-Eden]
+ * Added websocket binary support [einaros]
+ * Added gzip support for socket.io.js [3rd-Eden]
+ * Expose socket.transport [3rd-Eden]
+ * Updated client.
+
+0.8.4 / 2011-09-06
+==================
+
+ * Client build
+
+0.8.3 / 2011-09-03
+==================
+
+ * Fixed `\n` parsing for non-JSON packets (fixes #479).
+ * Fixed parsing of certain unicode characters (fixes #451).
+ * Fixed transport message packet logging.
+ * Fixed emission of `error` event resulting in an uncaught exception if unhandled (fixes #476).
+ * Fixed; allow for falsy values as the configuration value of `log level` (fixes #491).
+ * Fixed repository URI in `package.json`. Fixes #504.
+ * Added text/plain content-type to handshake responses [einaros]
+ * Improved single byte writes [einaros]
+ * Updated socket.io-flashsocket default port from 843 to 10843 [3rd-Eden]
+ * Updated client.
+
+0.8.2 / 2011-08-29
+==================
+
+ * Updated client.
+
+0.8.1 / 2011-08-29
+==================
+
+ * Fixed utf8 bug in send framing in websocket [einaros]
+ * Fixed typo in docs [Znarkus]
+ * Fixed bug in send framing for over 64kB of data in websocket [einaros]
+ * Corrected ping handling in websocket transport [einaros]
+
+0.8.0 / 2011-08-28
+==================
+
+ * Updated to work with two-level websocket versioning. [einaros]
+ * Added hybi07 support. [einaros]
+ * Added hybi10 support. [einaros]
+ * Added http referrer verification to manager.js verifyOrigin. [einaors]
+
+0.7.11 / 2011-08-27
+===================
+
+ * Updated socket.io-client.
+
+0.7.10 / 2011-08-27
+===================
+
+ * Updated socket.io-client.
+
+0.7.9 / 2011-08-12
+==================
+
+ * Updated socket.io-client.
+ * Make sure we only do garbage collection when the server we receive is actually run.
+
+0.7.8 / 2011-08-08
+==================
+
+ * Changed; make sure sio#listen passes options to both HTTP server and socket.io manager.
+ * Added docs for sio#listen.
+ * Added options parameter support for Manager constructor.
+ * Added memory leaks tests and test-leaks Makefile task.
+ * Removed auto npm-linking from make test.
+ * Make sure that you can disable heartbeats. [3rd-Eden]
+ * Fixed rooms memory leak [3rd-Eden]
+ * Send response once we got all POST data, not immediately [Pita]
+ * Fixed onLeave behavior with missing clientsk [3rd-Eden]
+ * Prevent duplicate references in rooms.
+ * Added alias for `to` to `in` and `in` to `to`.
+ * Fixed roomClients definition.
+ * Removed dependency on redis for installation without npm [3rd-Eden]
+ * Expose path and querystring in handshakeData [3rd-Eden]
+
+0.7.7 / 2011-07-12
+==================
+
+ * Fixed double dispatch handling with emit to closed clients.
+ * Added test for emitting to closed clients to prevent regression.
+ * Fixed race condition in redis test.
+ * Changed Transport#end instrumentation.
+ * Leveraged $emit instead of emit internally.
+ * Made tests faster.
+ * Fixed double disconnect events.
+ * Fixed disconnect logic
+ * Simplified remote events handling in Socket.
+ * Increased testcase timeout.
+ * Fixed unknown room emitting (GH-291). [3rd-Eden]
+ * Fixed `address` in handshakeData. [3rd-Eden]
+ * Removed transports definition in chat example.
+ * Fixed room cleanup
+ * Fixed; make sure the client is cleaned up after booting.
+ * Make sure to mark the client as non-open if the connection is closed.
+ * Removed unneeded `buffer` declarations.
+ * Fixed; make sure to clear socket handlers and subscriptions upon transport close.
+
+0.7.6 / 2011-06-30
+==================
+
+ * Fixed general dispatching when a client has closed.
+
+0.7.5 / 2011-06-30
+==================
+
+ * Fixed dispatching to clients that are disconnected.
+
+0.7.4 / 2011-06-30
+==================
+
+ * Fixed; only clear handlers if they were set. [level09]
+
+0.7.3 / 2011-06-30
+==================
+
+ * Exposed handshake data to clients.
+ * Refactored dispatcher interface.
+ * Changed; Moved id generation method into the manager.
+ * Added sub-namespace authorization. [3rd-Eden]
+ * Changed; normalized SocketNamespace local eventing [dvv]
+ * Changed; Use packet.reason or default to 'packet' [3rd-Eden]
+ * Changed console.error to console.log.
+ * Fixed; bind both servers at the same time do that the test never times out.
+ * Added 304 support.
+ * Removed `Transport#name` for abstract interface.
+ * Changed; lazily require http and https module only when needed. [3rd-Eden]
+
+0.7.2 / 2011-06-22
+==================
+
+ * Make sure to write a packet (of type `noop`) when closing a poll.
+ This solves a problem with cross-domain requests being flagged as aborted and
+ reconnection being triggered.
+ * Added `noop` message type.
+
+0.7.1 / 2011-06-21
+==================
+
+ * Fixed cross-domain XHR.
+ * Added CORS test to xhr-polling suite.
+
+0.7.0 / 2010-06-21
+==================
+
+ * http://socket.io/announcement.html
diff --git a/node_modules/socket.io/Makefile b/node_modules/socket.io/Makefile
new file mode 100644
index 0000000..832cba8
--- /dev/null
+++ b/node_modules/socket.io/Makefile
@@ -0,0 +1,31 @@
+
+ALL_TESTS = $(shell find test/ -name '*.test.js')
+ALL_BENCH = $(shell find benchmarks -name '*.bench.js')
+
+run-tests:
+ @./node_modules/.bin/expresso \
+ -t 3000 \
+ -I support \
+ --serial \
+ $(TESTFLAGS) \
+ $(TESTS)
+
+test:
+ @$(MAKE) NODE_PATH=lib TESTS="$(ALL_TESTS)" run-tests
+
+test-cov:
+ @TESTFLAGS=--cov $(MAKE) test
+
+test-leaks:
+ @ls test/leaks/* | xargs node --expose_debug_as=debug --expose_gc
+
+run-bench:
+ @node $(PROFILEFLAGS) benchmarks/runner.js
+
+bench:
+ @$(MAKE) BENCHMARKS="$(ALL_BENCH)" run-bench
+
+profile:
+ @PROFILEFLAGS='--prof --trace-opt --trace-bailout --trace-deopt' $(MAKE) bench
+
+.PHONY: test bench profile
diff --git a/node_modules/socket.io/Readme.md b/node_modules/socket.io/Readme.md
new file mode 100644
index 0000000..2a03a12
--- /dev/null
+++ b/node_modules/socket.io/Readme.md
@@ -0,0 +1,345 @@
+# Socket.IO
+
+Socket.IO is a Node.JS project that makes WebSockets and realtime possible in
+all browsers. It also enhances WebSockets by providing built-in multiplexing,
+horizontal scalability, automatic JSON encoding/decoding, and more.
+
+## How to Install
+
+```bash
+npm install socket.io
+```
+
+## How to use
+
+First, require `socket.io`:
+
+```js
+var io = require('socket.io');
+```
+
+Next, attach it to a HTTP/HTTPS server. If you're using the fantastic `express`
+web framework:
+
+```js
+var app = express.createServer()
+ , io = io.listen(app);
+
+app.listen(80);
+
+io.sockets.on('connection', function (socket) {
+ socket.emit('news', { hello: 'world' });
+ socket.on('my other event', function (data) {
+ console.log(data);
+ });
+});
+```
+
+Finally, load it from the client side code:
+
+```html
+<script src="/socket.io/socket.io.js"></script>
+<script>
+ var socket = io.connect('http://localhost');
+ socket.on('news', function (data) {
+ console.log(data);
+ socket.emit('my other event', { my: 'data' });
+ });
+</script>
+```
+
+For more thorough examples, look at the `examples/` directory.
+
+## Short recipes
+
+### Sending and receiving events.
+
+Socket.IO allows you to emit and receive custom events.
+Besides `connect`, `message` and `disconnect`, you can emit custom events:
+
+```js
+// note, io.listen(<port>) will create a http server for you
+var io = require('socket.io').listen(80);
+
+io.sockets.on('connection', function (socket) {
+ io.sockets.emit('this', { will: 'be received by everyone' });
+
+ socket.on('private message', function (from, msg) {
+ console.log('I received a private message by ', from, ' saying ', msg);
+ });
+
+ socket.on('disconnect', function () {
+ io.sockets.emit('user disconnected');
+ });
+});
+```
+
+### Storing data associated to a client
+
+Sometimes it's necessary to store data associated with a client that's
+necessary for the duration of the session.
+
+#### Server side
+
+```js
+var io = require('socket.io').listen(80);
+
+io.sockets.on('connection', function (socket) {
+ socket.on('set nickname', function (name) {
+ socket.set('nickname', name, function () { socket.emit('ready'); });
+ });
+
+ socket.on('msg', function () {
+ socket.get('nickname', function (err, name) {
+ console.log('Chat message by ', name);
+ });
+ });
+});
+```
+
+#### Client side
+
+```html
+<script>
+ var socket = io.connect('http://localhost');
+
+ socket.on('connect', function () {
+ socket.emit('set nickname', confirm('What is your nickname?'));
+ socket.on('ready', function () {
+ console.log('Connected !');
+ socket.emit('msg', confirm('What is your message?'));
+ });
+ });
+</script>
+```
+
+### Restricting yourself to a namespace
+
+If you have control over all the messages and events emitted for a particular
+application, using the default `/` namespace works.
+
+If you want to leverage 3rd-party code, or produce code to share with others,
+socket.io provides a way of namespacing a `socket`.
+
+This has the benefit of `multiplexing` a single connection. Instead of
+socket.io using two `WebSocket` connections, it'll use one.
+
+The following example defines a socket that listens on '/chat' and one for
+'/news':
+
+#### Server side
+
+```js
+var io = require('socket.io').listen(80);
+
+var chat = io
+ .of('/chat')
+ .on('connection', function (socket) {
+ socket.emit('a message', { that: 'only', '/chat': 'will get' });
+ chat.emit('a message', { everyone: 'in', '/chat': 'will get' });
+ });
+
+var news = io
+ .of('/news');
+ .on('connection', function (socket) {
+ socket.emit('item', { news: 'item' });
+ });
+```
+
+#### Client side:
+
+```html
+<script>
+ var chat = io.connect('http://localhost/chat')
+ , news = io.connect('http://localhost/news');
+
+ chat.on('connect', function () {
+ chat.emit('hi!');
+ });
+
+ news.on('news', function () {
+ news.emit('woot');
+ });
+</script>
+```
+
+### Sending volatile messages.
+
+Sometimes certain messages can be dropped. Let's say you have an app that
+shows realtime tweets for the keyword `bieber`.
+
+If a certain client is not ready to receive messages (because of network slowness
+or other issues, or because he's connected through long polling and is in the
+middle of a request-response cycle), if he doesn't receive ALL the tweets related
+to bieber your application won't suffer.
+
+In that case, you might want to send those messages as volatile messages.
+
+#### Server side
+
+```js
+var io = require('socket.io').listen(80);
+
+io.sockets.on('connection', function (socket) {
+ var tweets = setInterval(function () {
+ getBieberTweet(function (tweet) {
+ socket.volatile.emit('bieber tweet', tweet);
+ });
+ }, 100);
+
+ socket.on('disconnect', function () {
+ clearInterval(tweets);
+ });
+});
+```
+
+#### Client side
+
+In the client side, messages are received the same way whether they're volatile
+or not.
+
+### Getting acknowledgements
+
+Sometimes, you might want to get a callback when the client confirmed the message
+reception.
+
+To do this, simply pass a function as the last parameter of `.send` or `.emit`.
+What's more, when you use `.emit`, the acknowledgement is done by you, which
+means you can also pass data along:
+
+#### Server side
+
+```js
+var io = require('socket.io').listen(80);
+
+io.sockets.on('connection', function (socket) {
+ socket.on('ferret', function (name, fn) {
+ fn('woot');
+ });
+});
+```
+
+#### Client side
+
+```html
+<script>
+ var socket = io.connect(); // TIP: .connect with no args does auto-discovery
+ socket.on('connect', function () { // TIP: you can avoid listening on `connect` and listen on events directly too!
+ socket.emit('ferret', 'tobi', function (data) {
+ console.log(data); // data will be 'woot'
+ });
+ });
+</script>
+```
+
+### Broadcasting messages
+
+To broadcast, simply add a `broadcast` flag to `emit` and `send` method calls.
+Broadcasting means sending a message to everyone else except for the socket
+that starts it.
+
+#### Server side
+
+```js
+var io = require('socket.io').listen(80);
+
+io.sockets.on('connection', function (socket) {
+ socket.broadcast.emit('user connected');
+ socket.broadcast.json.send({ a: 'message' });
+});
+```
+
+### Rooms
+
+Sometimes you want to put certain sockets in the same room, so that it's easy
+to broadcast to all of them together.
+
+Think of this as built-in channels for sockets. Sockets `join` and `leave`
+rooms in each socket.
+
+#### Server side
+
+```js
+var io = require('socket.io').listen(80);
+
+io.sockets.on('connection', function (socket) {
+ socket.join('justin bieber fans');
+ socket.broadcast.to('justin bieber fans').emit('new fan');
+ io.sockets.in('rammstein fans').emit('new non-fan');
+});
+```
+
+### Using it just as a cross-browser WebSocket
+
+If you just want the WebSocket semantics, you can do that too.
+Simply leverage `send` and listen on the `message` event:
+
+#### Server side
+
+```js
+var io = require('socket.io').listen(80);
+
+io.sockets.on('connection', function (socket) {
+ socket.on('message', function () { });
+ socket.on('disconnect', function () { });
+});
+```
+
+#### Client side
+
+```html
+<script>
+ var socket = io.connect('http://localhost/');
+ socket.on('connect', function () {
+ socket.send('hi');
+
+ socket.on('message', function (msg) {
+ // my msg
+ });
+ });
+</script>
+```
+
+### Changing configuration
+
+Configuration in socket.io is TJ-style:
+
+#### Server side
+
+```js
+var io = require('socket.io').listen(80);
+
+io.configure(function () {
+ io.set('transports', ['websocket', 'flashsocket', 'xhr-polling']);
+});
+
+io.configure('development', function () {
+ io.set('transports', ['websocket', 'xhr-polling']);
+ io.enable('log');
+});
+```
+
+## License
+
+(The MIT License)
+
+Copyright (c) 2011 Guillermo Rauch &lt;guillermo@learnboost.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/socket.io/benchmarks/decode.bench.js b/node_modules/socket.io/benchmarks/decode.bench.js
new file mode 100644
index 0000000..4855d80
--- /dev/null
+++ b/node_modules/socket.io/benchmarks/decode.bench.js
@@ -0,0 +1,64 @@
+
+/**
+ * Module dependencies.
+ */
+
+var benchmark = require('benchmark')
+ , colors = require('colors')
+ , io = require('../')
+ , parser = io.parser
+ , suite = new benchmark.Suite('Decode packet');
+
+suite.add('string', function () {
+ parser.decodePacket('4:::"2"');
+});
+
+suite.add('event', function () {
+ parser.decodePacket('5:::{"name":"woot"}');
+});
+
+suite.add('event+ack', function () {
+ parser.decodePacket('5:1+::{"name":"tobi"}');
+});
+
+suite.add('event+data', function () {
+ parser.decodePacket('5:::{"name":"edwald","args":[{"a": "b"},2,"3"]}');
+});
+
+suite.add('heartbeat', function () {
+ parser.decodePacket('2:::');
+});
+
+suite.add('error', function () {
+ parser.decodePacket('7:::2+0');
+});
+
+var payload = parser.encodePayload([
+ parser.encodePacket({ type: 'message', data: '5', endpoint: '' })
+ , parser.encodePacket({ type: 'message', data: '53d', endpoint: '' })
+ , parser.encodePacket({ type: 'message', data: 'foobar', endpoint: '' })
+ , parser.encodePacket({ type: 'message', data: 'foobarbaz', endpoint: '' })
+ , parser.encodePacket({ type: 'message', data: 'foobarbazfoobarbaz', endpoint: '' })
+ , parser.encodePacket({ type: 'message', data: 'foobarbaz', endpoint: '' })
+ , parser.encodePacket({ type: 'message', data: 'foobar', endpoint: '' })
+]);
+
+suite.add('payload', function () {
+ parser.decodePayload(payload);
+});
+
+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));
+});
+
+if (!module.parent) {
+ suite.run();
+} else {
+ module.exports = suite;
+}
diff --git a/node_modules/socket.io/benchmarks/encode.bench.js b/node_modules/socket.io/benchmarks/encode.bench.js
new file mode 100644
index 0000000..5037702
--- /dev/null
+++ b/node_modules/socket.io/benchmarks/encode.bench.js
@@ -0,0 +1,90 @@
+
+/**
+ * Module dependencies.
+ */
+
+var benchmark = require('benchmark')
+ , colors = require('colors')
+ , io = require('../')
+ , parser = io.parser
+ , suite = new benchmark.Suite('Encode packet');
+
+suite.add('string', function () {
+ parser.encodePacket({
+ type: 'json'
+ , endpoint: ''
+ , data: '2'
+ });
+});
+
+suite.add('event', function () {
+ parser.encodePacket({
+ type: 'event'
+ , name: 'woot'
+ , endpoint: ''
+ , args: []
+ });
+});
+
+suite.add('event+ack', function () {
+ parser.encodePacket({
+ type: 'json'
+ , id: 1
+ , ack: 'data'
+ , endpoint: ''
+ , data: { a: 'b' }
+ });
+});
+
+suite.add('event+data', function () {
+ parser.encodePacket({
+ type: 'event'
+ , name: 'edwald'
+ , endpoint: ''
+ , args: [{a: 'b'}, 2, '3']
+ });
+});
+
+suite.add('heartbeat', function () {
+ parser.encodePacket({
+ type: 'heartbeat'
+ , endpoint: ''
+ })
+});
+
+suite.add('error', function () {
+ parser.encodePacket({
+ type: 'error'
+ , reason: 'unauthorized'
+ , advice: 'reconnect'
+ , endpoint: ''
+ })
+})
+
+suite.add('payload', function () {
+ parser.encodePayload([
+ parser.encodePacket({ type: 'message', data: '5', endpoint: '' })
+ , parser.encodePacket({ type: 'message', data: '53d', endpoint: '' })
+ , parser.encodePacket({ type: 'message', data: 'foobar', endpoint: '' })
+ , parser.encodePacket({ type: 'message', data: 'foobarbaz', endpoint: '' })
+ , parser.encodePacket({ type: 'message', data: 'foobarbazfoobarbaz', endpoint: '' })
+ , parser.encodePacket({ type: 'message', data: 'foobarbaz', endpoint: '' })
+ , parser.encodePacket({ type: 'message', data: 'foobar', endpoint: '' })
+ ]);
+});
+
+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));
+});
+
+if (!module.parent) {
+ suite.run();
+} else {
+ module.exports = suite;
+}
diff --git a/node_modules/socket.io/benchmarks/runner.js b/node_modules/socket.io/benchmarks/runner.js
new file mode 100644
index 0000000..81e55ca
--- /dev/null
+++ b/node_modules/socket.io/benchmarks/runner.js
@@ -0,0 +1,55 @@
+/**
+ * Benchmark runner dependencies
+ */
+
+var colors = require('colors')
+ , path = require('path');
+
+/**
+ * Find all the benchmarks
+ */
+
+var benchmarks_files = process.env.BENCHMARKS.split(' ')
+ , all = [].concat(benchmarks_files)
+ , first = all.shift()
+ , benchmarks = {};
+
+// find the benchmarks and load them all in our obj
+benchmarks_files.forEach(function (file) {
+ benchmarks[file] = require(path.join(__dirname, '..', file));
+});
+
+// setup the complete listeners
+benchmarks_files.forEach(function (file) {
+ var benchmark = benchmarks[file]
+ , next_file = all.shift()
+ , next = benchmarks[next_file];
+
+ /**
+ * Generate a oncomplete function for the tests, either we are done or we
+ * have more benchmarks to process.
+ */
+
+ function complete () {
+ if (!next) {
+ console.log(
+ '\n\nBenchmark completed in'.grey
+ , (Date.now() - start).toString().green + ' ms'.grey
+ );
+ } else {
+ console.log('\nStarting benchmark '.grey + next_file.yellow);
+ next.run();
+ }
+ }
+
+ // attach the listener
+ benchmark.on('complete', complete);
+});
+
+/**
+ * Start the benchmark
+ */
+
+var start = Date.now();
+console.log('Starting benchmark '.grey + first.yellow);
+benchmarks[first].run();
diff --git a/node_modules/socket.io/examples/chat/app.js b/node_modules/socket.io/examples/chat/app.js
new file mode 100644
index 0000000..bdfad2a
--- /dev/null
+++ b/node_modules/socket.io/examples/chat/app.js
@@ -0,0 +1,80 @@
+/**
+ * Module dependencies.
+ */
+
+var express = require('express')
+ , stylus = require('stylus')
+ , nib = require('nib')
+ , sio = require('../../lib/socket.io');
+
+/**
+ * App.
+ */
+
+var app = express.createServer();
+
+/**
+ * App configuration.
+ */
+
+app.configure(function () {
+ app.use(stylus.middleware({ src: __dirname + '/public', compile: compile }));
+ app.use(express.static(__dirname + '/public'));
+ app.set('views', __dirname);
+ app.set('view engine', 'jade');
+
+ function compile (str, path) {
+ return stylus(str)
+ .set('filename', path)
+ .use(nib());
+ };
+});
+
+/**
+ * App routes.
+ */
+
+app.get('/', function (req, res) {
+ res.render('index', { layout: false });
+});
+
+/**
+ * App listen.
+ */
+
+app.listen(3000, function () {
+ var addr = app.address();
+ console.log(' app listening on http://' + addr.address + ':' + addr.port);
+});
+
+/**
+ * Socket.IO server (single process only)
+ */
+
+var io = sio.listen(app)
+ , nicknames = {};
+
+io.sockets.on('connection', function (socket) {
+ socket.on('user message', function (msg) {
+ socket.broadcast.emit('user message', socket.nickname, msg);
+ });
+
+ socket.on('nickname', function (nick, fn) {
+ if (nicknames[nick]) {
+ fn(true);
+ } else {
+ fn(false);
+ nicknames[nick] = socket.nickname = nick;
+ socket.broadcast.emit('announcement', nick + ' connected');
+ io.sockets.emit('nicknames', nicknames);
+ }
+ });
+
+ socket.on('disconnect', function () {
+ if (!socket.nickname) return;
+
+ delete nicknames[socket.nickname];
+ socket.broadcast.emit('announcement', socket.nickname + ' disconnected');
+ socket.broadcast.emit('nicknames', nicknames);
+ });
+});
diff --git a/node_modules/socket.io/examples/chat/index.jade b/node_modules/socket.io/examples/chat/index.jade
new file mode 100644
index 0000000..0633d8b
--- /dev/null
+++ b/node_modules/socket.io/examples/chat/index.jade
@@ -0,0 +1,83 @@
+doctype 5
+html
+ head
+ link(href='/stylesheets/style.css', rel='stylesheet')
+ script(src='http://code.jquery.com/jquery-1.6.1.min.js')
+ script(src='/socket.io/socket.io.js')
+ script
+ // socket.io specific code
+ var socket = io.connect();
+
+ socket.on('connect', function () {
+ $('#chat').addClass('connected');
+ });
+
+ socket.on('announcement', function (msg) {
+ $('#lines').append($('<p>').append($('<em>').text(msg)));
+ });
+
+ socket.on('nicknames', function (nicknames) {
+ $('#nicknames').empty().append($('<span>Online: </span>'));
+ for (var i in nicknames) {
+ $('#nicknames').append($('<b>').text(nicknames[i]));
+ }
+ });
+
+ socket.on('user message', message);
+ socket.on('reconnect', function () {
+ $('#lines').remove();
+ message('System', 'Reconnected to the server');
+ });
+
+ socket.on('reconnecting', function () {
+ message('System', 'Attempting to re-connect to the server');
+ });
+
+ socket.on('error', function (e) {
+ message('System', e ? e : 'A unknown error occurred');
+ });
+
+ function message (from, msg) {
+ $('#lines').append($('<p>').append($('<b>').text(from), msg));
+ }
+
+ // dom manipulation
+ $(function () {
+ $('#set-nickname').submit(function (ev) {
+ socket.emit('nickname', $('#nick').val(), function (set) {
+ if (!set) {
+ clear();
+ return $('#chat').addClass('nickname-set');
+ }
+ $('#nickname-err').css('visibility', 'visible');
+ });
+ return false;
+ });
+
+ $('#send-message').submit(function () {
+ message('me', $('#message').val());
+ socket.emit('user message', $('#message').val());
+ clear();
+ $('#lines').get(0).scrollTop = 10000000;
+ return false;
+ });
+
+ function clear () {
+ $('#message').val('').focus();
+ };
+ });
+ body
+ #chat
+ #nickname
+ form.wrap#set-nickname
+ p Please type in your nickname and press enter.
+ input#nick
+ p#nickname-err Nickname already in use
+ #connecting
+ .wrap Connecting to socket.io server
+ #messages
+ #nicknames
+ #lines
+ form#send-message
+ input#message
+ button Send
diff --git a/node_modules/socket.io/examples/chat/package.json b/node_modules/socket.io/examples/chat/package.json
new file mode 100644
index 0000000..6b375b0
--- /dev/null
+++ b/node_modules/socket.io/examples/chat/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "chat.io"
+ , "description": "example chat application with socket.io"
+ , "version": "0.0.1"
+ , "dependencies": {
+ "express": "2.5.5"
+ , "jade": "0.16.4"
+ , "stylus": "0.19.0"
+ , "nib": "0.2.0"
+ }
+}
diff --git a/node_modules/socket.io/examples/chat/public/stylesheets/mixins.styl b/node_modules/socket.io/examples/chat/public/stylesheets/mixins.styl
new file mode 100644
index 0000000..fb5644c
--- /dev/null
+++ b/node_modules/socket.io/examples/chat/public/stylesheets/mixins.styl
@@ -0,0 +1,96 @@
+border-radius(n)
+ -webkit-border-radius n
+ -moz-border-radius n
+ border-radius n
+
+// replace str with val
+
+replace(expr, str, val)
+ expr = clone(expr)
+ for e, i in expr
+ if str == e
+ expr[i] = val
+ expr
+
+// normalize gradient point (webkit)
+
+grad-point(pos)
+ if length(pos) == 1
+ return left pos if pos in (top bottom)
+ return pos top if pos in (left right)
+ else if pos[0] in (top bottom)
+ pos[1] pos[0]
+ else
+ pos
+
+// implicit color stop position
+
+pos-in-stops(i, stops)
+ len = length(stops)
+ if len - 1 == i
+ 100%
+ else if i
+ unit(i / len * 100, '%')
+ else
+ 0%
+
+// normalize color stops
+// - (color pos) -> (pos color)
+// - (color) -> (implied-pos color)
+
+normalize-stops(stops)
+ stops = clone(stops)
+ for stop, i in stops
+ if length(stop) == 1
+ color = stop[0]
+ stop[0] = pos-in-stops(i, stops)
+ stop[1] = color
+ else if typeof(stop[1]) == 'unit'
+ pos = stop[1]
+ stop[1] = stop[0]
+ stop[0] = pos
+ stops
+
+// join color stops with the given translation function
+
+join-stops(stops, translate)
+ str = ''
+ len = length(stops)
+ for stop, i in stops
+ str += ', ' if i
+ pos = stop[0]
+ color = stop[1]
+ str += translate(color, pos)
+ unquote(str)
+
+// webkit translation function
+
+webkit-stop(color, pos)
+ s('color-stop(%d, %s)', pos / 100, color)
+
+// mozilla translation function
+
+moz-stop(color, pos)
+ s('%s %s', color, pos)
+
+// create a linear gradient with the given start
+// position, followed by color stops
+
+linear-gradient(start, stops...)
+ error('color stops required') unless length(stops)
+ prop = current-property[0]
+ val = current-property[1]
+ stops = normalize-stops(stops)
+
+ // webkit
+ end = grad-point(opposite-position(start))
+ webkit = s('-webkit-gradient(linear, %s, %s, %s)', grad-point(start), end, join-stops(stops, webkit-stop))
+ add-property(prop, replace(val, '__CALL__', webkit))
+
+ // moz
+ stops = join-stops(stops, moz-stop)
+ moz = s('-moz-linear-gradient(%s, %s)', start, stops)
+ add-property(prop, replace(val, '__CALL__', moz))
+
+ // literal
+ s('linear-gradient(%s, %s)', start, stops)
diff --git a/node_modules/socket.io/examples/chat/public/stylesheets/style.css b/node_modules/socket.io/examples/chat/public/stylesheets/style.css
new file mode 100644
index 0000000..42cf98f
--- /dev/null
+++ b/node_modules/socket.io/examples/chat/public/stylesheets/style.css
@@ -0,0 +1,188 @@
+#chat,
+#nickname,
+#messages {
+ width: 600px;
+}
+#chat {
+ position: relative;
+ border: 1px solid #ccc;
+}
+#nickname,
+#connecting {
+ position: absolute;
+ height: 410px;
+ z-index: 100;
+ left: 0;
+ top: 0;
+ background: #fff;
+ text-align: center;
+ width: 600px;
+ font: 15px Georgia;
+ color: #666;
+ display: block;
+}
+#nickname .wrap,
+#connecting .wrap {
+ padding-top: 150px;
+}
+#nickname input {
+ border: 1px solid #ccc;
+ padding: 10px;
+}
+#nickname input:focus {
+ border-color: #999;
+ outline: 0;
+}
+#nickname #nickname-err {
+ color: #8b0000;
+ font-size: 12px;
+ visibility: hidden;
+}
+.connected #connecting {
+ display: none;
+}
+.nickname-set #nickname {
+ display: none;
+}
+#messages {
+ height: 380px;
+ background: #eee;
+}
+#messages em {
+ text-shadow: 0 1px 0 #fff;
+ color: #999;
+}
+#messages p {
+ padding: 0;
+ margin: 0;
+ font: 12px Helvetica, Arial;
+ padding: 5px 10px;
+}
+#messages p b {
+ display: inline-block;
+ padding-right: 10px;
+}
+#messages p:nth-child(even) {
+ background: #fafafa;
+}
+#messages #nicknames {
+ background: #ccc;
+ padding: 2px 4px 4px;
+ font: 11px Helvetica;
+}
+#messages #nicknames span {
+ color: #000;
+}
+#messages #nicknames b {
+ display: inline-block;
+ color: #fff;
+ background: #999;
+ padding: 3px 6px;
+ margin-right: 5px;
+ -webkit-border-radius: 10px;
+ -moz-border-radius: 10px;
+ border-radius: 10px;
+ text-shadow: 0 1px 0 #666;
+}
+#messages #lines {
+ height: 355px;
+ overflow: auto;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+#messages #lines::-webkit-scrollbar {
+ width: 6px;
+ height: 6px;
+}
+#messages #lines::-webkit-scrollbar-button:start:decrement,
+#messages #lines ::-webkit-scrollbar-button:end:increment {
+ display: block;
+ height: 10px;
+}
+#messages #lines::-webkit-scrollbar-button:vertical:increment {
+ background-color: #fff;
+}
+#messages #lines::-webkit-scrollbar-track-piece {
+ background-color: #fff;
+ -webkit-border-radius: 3px;
+}
+#messages #lines::-webkit-scrollbar-thumb:vertical {
+ height: 50px;
+ background-color: #ccc;
+ -webkit-border-radius: 3px;
+}
+#messages #lines::-webkit-scrollbar-thumb:horizontal {
+ width: 50px;
+ background-color: #fff;
+ -webkit-border-radius: 3px;
+}
+#send-message {
+ background: #fff;
+ position: relative;
+}
+#send-message input {
+ border: none;
+ height: 30px;
+ padding: 0 10px;
+ line-height: 30px;
+ vertical-align: middle;
+ width: 580px;
+}
+#send-message input:focus {
+ outline: 0;
+}
+#send-message button {
+ position: absolute;
+ top: 5px;
+ right: 5px;
+}
+button {
+ margin: 0;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+ display: inline-block;
+ text-decoration: none;
+ background: #43a1f7;
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #43a1f7), color-stop(1, #377ad0));
+ background: -webkit-linear-gradient(top, #43a1f7 0%, #377ad0 100%);
+ background: -moz-linear-gradient(top, #43a1f7 0%, #377ad0 100%);
+ background: linear-gradient(top, #43a1f7 0%, #377ad0 100%);
+ border: 1px solid #2e70c4;
+ -webkit-border-radius: 16px;
+ -moz-border-radius: 16px;
+ border-radius: 16px;
+ color: #fff;
+ font-family: "lucida grande", sans-serif;
+ font-size: 11px;
+ font-weight: normal;
+ line-height: 1;
+ padding: 3px 10px 5px 10px;
+ text-align: center;
+ text-shadow: 0 -1px 1px #2d6dc0;
+}
+button:hover,
+button.hover {
+ background: darker;
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #43a1f7), color-stop(1, #2e70c4));
+ background: -webkit-linear-gradient(top, #43a1f7 0%, #2e70c4 100%);
+ background: -moz-linear-gradient(top, #43a1f7 0%, #2e70c4 100%);
+ background: linear-gradient(top, #43a1f7 0%, #2e70c4 100%);
+ border: 1px solid #2e70c4;
+ cursor: pointer;
+ text-shadow: 0 -1px 1px #2c6bbb;
+}
+button:active,
+button.active {
+ background: #2e70c4;
+ border: 1px solid #2e70c4;
+ border-bottom: 1px solid #2861aa;
+ text-shadow: 0 -1px 1px #2b67b5;
+}
+button:focus,
+button.focus {
+ outline: none;
+ -webkit-box-shadow: 0 1px 0 0 rgba(255,255,255,0.4), 0 0 4px 0 #377ad0;
+ -moz-box-shadow: 0 1px 0 0 rgba(255,255,255,0.4), 0 0 4px 0 #377ad0;
+ box-shadow: 0 1px 0 0 rgba(255,255,255,0.4), 0 0 4px 0 #377ad0;
+}
diff --git a/node_modules/socket.io/examples/chat/public/stylesheets/style.styl b/node_modules/socket.io/examples/chat/public/stylesheets/style.styl
new file mode 100644
index 0000000..6289c94
--- /dev/null
+++ b/node_modules/socket.io/examples/chat/public/stylesheets/style.styl
@@ -0,0 +1,118 @@
+@import 'nib'
+
+#chat, #nickname, #messages
+ width 600px
+
+#chat
+ position relative
+ border 1px solid #ccc
+
+#nickname, #connecting
+ position absolute
+ height 410px
+ z-index 100
+ left 0
+ top 0
+ background #fff
+ text-align center
+ width 600px
+ font 15px Georgia
+ color #666
+ display block
+ .wrap
+ padding-top 150px
+
+#nickname
+ input
+ border 1px solid #ccc
+ padding 10px
+ &:focus
+ border-color #999
+ outline 0
+ #nickname-err
+ color darkred
+ font-size 12px
+ visibility hidden
+
+.connected
+ #connecting
+ display none
+
+.nickname-set
+ #nickname
+ display none
+
+#messages
+ height 380px
+ background #eee
+ em
+ text-shadow 0 1px 0 #fff
+ color #999
+ p
+ padding 0
+ margin 0
+ font 12px Helvetica, Arial
+ padding 5px 10px
+ b
+ display inline-block
+ padding-right 10px
+ p:nth-child(even)
+ background #fafafa
+ #nicknames
+ background #ccc
+ padding 2px 4px 4px
+ font 11px Helvetica
+ span
+ color black
+ b
+ display inline-block
+ color #fff
+ background #999
+ padding 3px 6px
+ margin-right 5px
+ border-radius 10px
+ text-shadow 0 1px 0 #666
+ #lines
+ height 355px
+ overflow auto
+ overflow-x hidden
+ overflow-y auto
+ &::-webkit-scrollbar
+ width 6px
+ height 6px
+ &::-webkit-scrollbar-button:start:decrement, ::-webkit-scrollbar-button:end:increment
+ display block
+ height 10px
+ &::-webkit-scrollbar-button:vertical:increment
+ background-color #fff
+ &::-webkit-scrollbar-track-piece
+ background-color #fff
+ -webkit-border-radius 3px
+ &::-webkit-scrollbar-thumb:vertical
+ height 50px
+ background-color #ccc
+ -webkit-border-radius 3px
+ &::-webkit-scrollbar-thumb:horizontal
+ width 50px
+ background-color #fff
+ -webkit-border-radius 3px
+
+#send-message
+ background #fff
+ position relative
+ input
+ border none
+ height 30px
+ padding 0 10px
+ line-height 30px
+ vertical-align middle
+ width 580px
+ &:focus
+ outline 0
+ button
+ position absolute
+ top 5px
+ right 5px
+
+button
+ download-button()
diff --git a/node_modules/socket.io/examples/irc-output/app.js b/node_modules/socket.io/examples/irc-output/app.js
new file mode 100644
index 0000000..784886a
--- /dev/null
+++ b/node_modules/socket.io/examples/irc-output/app.js
@@ -0,0 +1,74 @@
+/**
+ * Module dependencies.
+ */
+
+var express = require('express')
+ , stylus = require('stylus')
+ , nib = require('nib')
+ , sio = require('../../lib/socket.io')
+ , irc = require('./irc');
+
+/**
+ * App.
+ */
+
+var app = express.createServer();
+
+/**
+ * App configuration.
+ */
+
+app.configure(function () {
+ app.use(stylus.middleware({ src: __dirname + '/public', compile: compile }))
+ app.use(express.static(__dirname + '/public'));
+ app.set('views', __dirname);
+ app.set('view engine', 'jade');
+
+ function compile (str, path) {
+ return stylus(str)
+ .set('filename', path)
+ .use(nib());
+ };
+});
+
+/**
+ * App routes.
+ */
+
+app.get('/', function (req, res) {
+ res.render('index', { layout: false });
+});
+
+/**
+ * App listen.
+ */
+
+app.listen(3000, function () {
+ var addr = app.address();
+ console.log(' app listening on http://' + addr.address + ':' + addr.port);
+});
+
+/**
+ * Socket.IO server
+ */
+
+var io = sio.listen(app)
+
+/**
+ * Connect to IRC.
+ */
+
+var client = new irc.Client('irc.freenode.net', 6667);
+client.connect('socketio\\test\\' + String(Math.random()).substr(-3));
+client.on('001', function () {
+ this.send('JOIN', '#node.js');
+});
+client.on('PART', function (prefix) {
+ io.sockets.emit('announcement', irc.user(prefix) + ' left the channel');
+});
+client.on('JOIN', function (prefix) {
+ io.sockets.emit('announcement', irc.user(prefix) + ' joined the channel');
+});
+client.on('PRIVMSG', function (prefix, channel, text) {
+ io.sockets.emit('irc message', irc.user(prefix), text);
+});
diff --git a/node_modules/socket.io/examples/irc-output/index.jade b/node_modules/socket.io/examples/irc-output/index.jade
new file mode 100644
index 0000000..5468632
--- /dev/null
+++ b/node_modules/socket.io/examples/irc-output/index.jade
@@ -0,0 +1,28 @@
+doctype 5
+html
+ head
+ link(href='/stylesheets/style.css', rel='stylesheet')
+ script(src='http://code.jquery.com/jquery-1.6.1.min.js')
+ script(src='/socket.io/socket.io.js')
+ script
+ var socket = io.connect();
+
+ socket.on('connect', function () {
+ $('#irc').addClass('connected');
+ });
+
+ socket.on('announcement', function (msg) {
+ $('#messages').append($('<p>').append($('<em>').text(msg)));
+ $('#messages').get(0).scrollTop = 10000000;
+ });
+
+ socket.on('irc message', function (user, msg) {
+ $('#messages').append($('<p>').append($('<b>').text(user), msg));
+ $('#messages').get(0).scrollTop = 10000000;
+ });
+ body
+ h2 Node.JS IRC
+ #irc
+ #connecting
+ .wrap Connecting to socket.io server
+ #messages
diff --git a/node_modules/socket.io/examples/irc-output/irc.js b/node_modules/socket.io/examples/irc-output/irc.js
new file mode 100644
index 0000000..cc679d5
--- /dev/null
+++ b/node_modules/socket.io/examples/irc-output/irc.js
@@ -0,0 +1,164 @@
+/**
+ * From https://github.com/felixge/nodelog/
+ */
+
+var sys = require('util');
+var tcp = require('net');
+var irc = exports;
+
+function bind(fn, scope) {
+ var bindArgs = Array.prototype.slice.call(arguments);
+ bindArgs.shift();
+ bindArgs.shift();
+
+ return function() {
+ var args = Array.prototype.slice.call(arguments);
+ fn.apply(scope, bindArgs.concat(args));
+ };
+}
+
+function each(set, iterator) {
+ for (var i = 0; i < set.length; i++) {
+ var r = iterator(set[i], i);
+ if (r === false) {
+ return;
+ }
+ }
+}
+
+var Client = irc.Client = function(host, port) {
+ this.host = host || 'localhost';
+ this.port = port || 6667;
+
+ this.connection = null;
+ this.buffer = '';
+ this.encoding = 'utf8';
+ this.timeout = 10 * 60 * 60 * 1000;
+
+ this.nick = null;
+ this.user = null;
+ this.real = null;
+}
+sys.inherits(Client, process.EventEmitter);
+
+Client.prototype.connect = function(nick, user, real) {
+ var connection = tcp.createConnection(this.port, this.host);
+ connection.setEncoding(this.encoding);
+ connection.setTimeout(this.timeout);
+ connection.addListener('connect', bind(this.onConnect, this));
+ connection.addListener('data', bind(this.onReceive, this));
+ connection.addListener('end', bind(this.onEof, this));
+ connection.addListener('timeout', bind(this.onTimeout, this));
+ connection.addListener('close', bind(this.onClose, this));
+
+ this.nick = nick;
+ this.user = user || 'guest';
+ this.real = real || 'Guest';
+
+ this.connection = connection;
+};
+
+Client.prototype.disconnect = function(why) {
+ if (this.connection.readyState !== 'closed') {
+ this.connection.close();
+ sys.puts('disconnected (reason: '+why+')');
+ this.emit('DISCONNECT', why);
+ }
+};
+
+Client.prototype.send = function(arg1) {
+ if (this.connection.readyState !== 'open') {
+ return this.disconnect('cannot send with readyState: '+this.connection.readyState);
+ }
+
+ var message = [];
+ for (var i = 0; i< arguments.length; i++) {
+ if (arguments[i]) {
+ message.push(arguments[i]);
+ }
+ }
+ message = message.join(' ');
+
+ sys.puts('> '+message);
+ message = message + "\r\n";
+ this.connection.write(message, this.encoding);
+};
+
+Client.prototype.parse = function(message) {
+ var match = message.match(/(?:(:[^\s]+) )?([^\s]+) (.+)/);
+ var parsed = {
+ prefix: match[1],
+ command: match[2]
+ };
+
+ var params = match[3].match(/(.*?) ?:(.*)/);
+ if (params) {
+ // Params before :
+ params[1] = (params[1])
+ ? params[1].split(' ')
+ : [];
+ // Rest after :
+ params[2] = params[2]
+ ? [params[2]]
+ : [];
+
+ params = params[1].concat(params[2]);
+ } else {
+ params = match[3].split(' ');
+ }
+
+ parsed.params = params;
+ return parsed;
+};
+
+Client.prototype.onConnect = function() {
+ this.send('NICK', this.nick);
+ this.send('USER', this.user, '0', '*', ':'+this.real);
+};
+
+Client.prototype.onReceive = function(chunk) {
+ this.buffer = this.buffer + chunk;
+
+ while (this.buffer) {
+ var offset = this.buffer.indexOf("\r\n");
+ if (offset < 0) {
+ return;
+ }
+
+ var message = this.buffer.substr(0, offset);
+ this.buffer = this.buffer.substr(offset + 2);
+ sys.puts('< '+message);
+
+ message = this.parse(message);
+
+ this.emit.apply(this, [message.command, message.prefix].concat(message.params));
+
+ if (message !== false) {
+ this.onMessage(message);
+ }
+ }
+};
+
+Client.prototype.onMessage = function(message) {
+ switch (message.command) {
+ case 'PING':
+ this.send('PONG', ':'+message.params[0]);
+ break;
+ }
+};
+
+Client.prototype.onEof = function() {
+ this.disconnect('eof');
+};
+
+Client.prototype.onTimeout = function() {
+ this.disconnect('timeout');
+};
+
+Client.prototype.onClose = function() {
+ this.disconnect('close');
+};
+
+exports.user = function(prefix) {
+ return prefix.match(/:([^!]+)!/)[1]
+};
diff --git a/node_modules/socket.io/examples/irc-output/package.json b/node_modules/socket.io/examples/irc-output/package.json
new file mode 100644
index 0000000..665ee33
--- /dev/null
+++ b/node_modules/socket.io/examples/irc-output/package.json
@@ -0,0 +1,10 @@
+{
+ "name": "socket.io-irc"
+ , "version": "0.0.1"
+ , "dependencies": {
+ "express": "2.5.5"
+ , "jade": "0.16.4"
+ , "stylus": "0.19.0"
+ , "nib": "0.2.0"
+ }
+}
diff --git a/node_modules/socket.io/examples/irc-output/public/stylesheets/style.styl b/node_modules/socket.io/examples/irc-output/public/stylesheets/style.styl
new file mode 100644
index 0000000..2713512
--- /dev/null
+++ b/node_modules/socket.io/examples/irc-output/public/stylesheets/style.styl
@@ -0,0 +1,69 @@
+@import 'nib'
+
+h2
+ font bold 18px Helvetica Neue, Arial
+
+#irc, #messages
+ width 600px
+
+#irc
+ position relative
+ border 1px solid #ccc
+
+#connecting
+ position absolute
+ height 410px
+ z-index 100
+ left 0
+ top 0
+ background #fff
+ text-align center
+ width 600px
+ font 15px Georgia
+ color #666
+ display block
+ .wrap
+ padding-top 150px
+
+.connected
+ #connecting
+ display none
+
+#messages
+ height 380px
+ background #eee
+ overflow auto
+ overflow-x hidden
+ overflow-y auto
+ &::-webkit-scrollbar
+ width 6px
+ height 6px
+ &::-webkit-scrollbar-button:start:decrement, ::-webkit-scrollbar-button:end:increment
+ display block
+ height 10px
+ &::-webkit-scrollbar-button:vertical:increment
+ background-color #fff
+ &::-webkit-scrollbar-track-piece
+ background-color #fff
+ -webkit-border-radius 3px
+ &::-webkit-scrollbar-thumb:vertical
+ height 50px
+ background-color #ccc
+ -webkit-border-radius 3px
+ &::-webkit-scrollbar-thumb:horizontal
+ width 50px
+ background-color #fff
+ -webkit-border-radius 3px
+ em
+ text-shadow 0 1px 0 #fff
+ color #999
+ p
+ padding 0
+ margin 0
+ font 12px Helvetica, Arial
+ padding 5px 10px
+ b
+ display inline-block
+ padding-right 10px
+ p:nth-child(even)
+ background #fafafa
diff --git a/node_modules/socket.io/index.js b/node_modules/socket.io/index.js
new file mode 100644
index 0000000..cc00c10
--- /dev/null
+++ b/node_modules/socket.io/index.js
@@ -0,0 +1,8 @@
+
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+module.exports = require('./lib/socket.io');
diff --git a/node_modules/socket.io/lib/client.js b/node_modules/socket.io/lib/client.js
new file mode 100644
index 0000000..d80067b
--- /dev/null
+++ b/node_modules/socket.io/lib/client.js
@@ -0,0 +1,167 @@
+
+/**
+ * Module dependencies.
+ */
+
+var parser = require('socket.io-client').parser
+ , EventEmitter = require('events').EventEmitter
+
+/**
+ * Client constructor.
+ *
+ * @api public
+ */
+
+function Client (id, server) {
+ this.id = id;
+ this.acks = {};
+ this.store = server.store;
+
+ var self = this;
+
+ store.subscribe(id, function (packet) {
+
+ });
+
+ store.subscribe(id + '.disconect', function () {
+ self.onDisconnect();
+ });
+}
+
+/**
+ * Inherits from EventEmitter.
+ */
+
+Client.prototype.__proto__ = EventEmitter.prototype;
+
+/**
+ * Save reference to original `emit`.
+ *
+ * @api private
+ */
+
+Client.prototype._emit = Client.prototype.emit;
+
+/**
+ * Broadcast flag.
+ *
+ * @api public
+ */
+
+Client.prototype.__defineGetter__('broadcast', function () {
+ this.flags.broadcast = true;
+});
+
+/**
+ * JSON flag (deprecated)
+ *
+ * @api public
+ */
+
+Client.prototype.__defineGetter__('json', function () {
+ this.flags.broadcast = true;
+});
+
+/**
+ * Joins a group.
+ *
+ * @param {String} group
+ * @return {Client} for chaining
+ * @api public
+ */
+
+Client.prototype.join = function (group, fn) {
+ if (!~this.subscriptions.indexOf(group)) {
+ var self = this;
+ this.subscriptions.push(group);
+ this.store.addToGroup(group, this.sid, function (ev, args) {
+ self.onGroupEvent(ev, args);
+ }, fn);
+ } else {
+ fn && fn();
+ }
+
+ return this;
+};
+
+/**
+ * Leaves a group.
+ *
+ * @return {Client} for chaining
+ * @api public
+ */
+
+Client.prototype.leave = function (group) {
+ var index = this.subscriptions.indexOf(group);
+ if (~index) {
+ this.subscriptions.splice(index, 1);
+ }
+ return this;
+};
+
+Client.prototype.disconnect = function () {
+ if (this.socket) {
+ this.socket.disconnect();
+ } else {
+ this.publish('disconnect');
+ }
+}
+
+/**
+ * Called upon disconnect.
+ *
+ * @api private
+ */
+
+Client.prototype.onDisconnect = function () {
+ for (var i = 0, l = this.subscriptions; i < l; i++) {
+ this.store.removeFromGroup(id, group, fn);
+ }
+};
+
+/**
+ * Registers ACK.
+ */
+
+Client.prototype.ack = function (fn, callback) {
+ this.subscribe('ack');
+};
+
+/**
+ * Emits an event.
+ */
+
+Client.prototype.emit = function () {
+ var args = toArray(arguments), fn;
+
+ if ('function' == typeof args[args.length - 1]) {
+ fn = args.pop();
+ }
+
+ var data = args.shift();
+ if (args.length) {
+ data += '\n' + JSON.stringify(args);
+ }
+
+ if (fn) {
+ this.ack(fn, function (id) {
+ self.sendPacket('event', data, id);
+ });
+ } else {
+ this.sendPacket('event', data);
+ }
+
+ return this;
+};
+
+/**
+ * Sends a packet.
+ */
+
+Client.prototype.sendPacket = function (type, data, id) {
+ var data = parser.encode({ type: type, data: data, id: id });
+
+ if (this.server.sockets[id]) {
+ this.server.sockets[id].write(data);
+ }
+};
diff --git a/node_modules/socket.io/lib/logger.js b/node_modules/socket.io/lib/logger.js
new file mode 100644
index 0000000..49d02c9
--- /dev/null
+++ b/node_modules/socket.io/lib/logger.js
@@ -0,0 +1,97 @@
+
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module dependencies.
+ */
+
+var util = require('./util')
+ , toArray = util.toArray;
+
+/**
+ * Log levels.
+ */
+
+var levels = [
+ 'error'
+ , 'warn'
+ , 'info'
+ , 'debug'
+];
+
+/**
+ * Colors for log levels.
+ */
+
+var colors = [
+ 31
+ , 33
+ , 36
+ , 90
+];
+
+/**
+ * Pads the nice output to the longest log level.
+ */
+
+function pad (str) {
+ var max = 0;
+
+ for (var i = 0, l = levels.length; i < l; i++)
+ max = Math.max(max, levels[i].length);
+
+ if (str.length < max)
+ return str + new Array(max - str.length + 1).join(' ');
+
+ return str;
+};
+
+/**
+ * Logger (console).
+ *
+ * @api public
+ */
+
+var Logger = module.exports = function (opts) {
+ opts = opts || {}
+ this.colors = false !== opts.colors;
+ this.level = 3;
+ this.enabled = true;
+};
+
+/**
+ * Log method.
+ *
+ * @api public
+ */
+
+Logger.prototype.log = function (type) {
+ var index = levels.indexOf(type);
+
+ if (index > this.level || !this.enabled)
+ return this;
+
+ console.log.apply(
+ console
+ , [this.colors
+ ? ' \033[' + colors[index] + 'm' + pad(type) + ' -\033[39m'
+ : type + ':'
+ ].concat(toArray(arguments).slice(1))
+ );
+
+ return this;
+};
+
+/**
+ * Generate methods.
+ */
+
+levels.forEach(function (name) {
+ Logger.prototype[name] = function () {
+ this.log.apply(this, [name].concat(toArray(arguments)));
+ };
+});
diff --git a/node_modules/socket.io/lib/manager.js b/node_modules/socket.io/lib/manager.js
new file mode 100644
index 0000000..d136a90
--- /dev/null
+++ b/node_modules/socket.io/lib/manager.js
@@ -0,0 +1,983 @@
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module dependencies.
+ */
+
+var fs = require('fs')
+ , url = require('url')
+ , tty = require('tty')
+ , util = require('./util')
+ , store = require('./store')
+ , client = require('socket.io-client')
+ , transports = require('./transports')
+ , Logger = require('./logger')
+ , Socket = require('./socket')
+ , MemoryStore = require('./stores/memory')
+ , SocketNamespace = require('./namespace')
+ , Static = require('./static')
+ , EventEmitter = process.EventEmitter;
+
+/**
+ * Export the constructor.
+ */
+
+exports = module.exports = Manager;
+
+/**
+ * Default transports.
+ */
+
+var defaultTransports = exports.defaultTransports = [
+ 'websocket'
+ , 'htmlfile'
+ , 'xhr-polling'
+ , 'jsonp-polling'
+];
+
+/**
+ * Inherited defaults.
+ */
+
+var parent = module.parent.exports
+ , protocol = parent.protocol;
+
+/**
+ * Manager constructor.
+ *
+ * @param {HTTPServer} server
+ * @param {Object} options, optional
+ * @api public
+ */
+
+function Manager (server, options) {
+ this.server = server;
+ this.namespaces = {};
+ this.sockets = this.of('');
+ this.settings = {
+ origins: '*:*'
+ , log: true
+ , store: new MemoryStore
+ , logger: new Logger
+ , static: new Static(this)
+ , heartbeats: true
+ , resource: '/socket.io'
+ , transports: defaultTransports
+ , authorization: false
+ , blacklist: ['disconnect']
+ , 'log level': 3
+ , 'log colors': tty.isatty(process.stdout.fd)
+ , 'close timeout': 60
+ , 'heartbeat interval': 25
+ , 'heartbeat timeout': 60
+ , 'polling duration': 20
+ , 'flash policy server': true
+ , 'flash policy port': 10843
+ , 'destroy upgrade': true
+ , 'destroy buffer size': 10E7
+ , 'browser client': true
+ , 'browser client cache': true
+ , 'browser client minification': false
+ , 'browser client etag': false
+ , 'browser client expires': 315360000
+ , 'browser client gzip': false
+ , 'browser client handler': false
+ , 'client store expiration': 15
+ , 'match origin protocol': false
+ };
+
+ for (var i in options) {
+ this.settings[i] = options[i];
+ }
+
+ var self = this;
+
+ // default error handler
+ server.on('error', function(err) {
+ self.log.warn('error raised: ' + err);
+ });
+
+ this.initStore();
+
+ this.on('set:store', function() {
+ self.initStore();
+ });
+
+ // reset listeners
+ this.oldListeners = server.listeners('request');
+ server.removeAllListeners('request');
+
+ server.on('request', function (req, res) {
+ self.handleRequest(req, res);
+ });
+
+ server.on('upgrade', function (req, socket, head) {
+ self.handleUpgrade(req, socket, head);
+ });
+
+ server.on('close', function () {
+ clearInterval(self.gc);
+ });
+
+ server.once('listening', function () {
+ self.gc = setInterval(self.garbageCollection.bind(self), 10000);
+ });
+
+ for (var i in transports) {
+ if (transports[i].init) {
+ transports[i].init(this);
+ }
+ }
+
+ // forward-compatibility with 1.0
+ var self = this;
+ this.sockets.on('connection', function (conn) {
+ self.emit('connection', conn);
+ });
+
+ this.log.info('socket.io started');
+};
+
+Manager.prototype.__proto__ = EventEmitter.prototype
+
+/**
+ * Store accessor shortcut.
+ *
+ * @api public
+ */
+
+Manager.prototype.__defineGetter__('store', function () {
+ var store = this.get('store');
+ store.manager = this;
+ return store;
+});
+
+/**
+ * Logger accessor.
+ *
+ * @api public
+ */
+
+Manager.prototype.__defineGetter__('log', function () {
+ var logger = this.get('logger');
+
+ logger.level = this.get('log level') || -1;
+ logger.colors = this.get('log colors');
+ logger.enabled = this.enabled('log');
+
+ return logger;
+});
+
+/**
+ * Static accessor.
+ *
+ * @api public
+ */
+
+Manager.prototype.__defineGetter__('static', function () {
+ return this.get('static');
+});
+
+/**
+ * Get settings.
+ *
+ * @api public
+ */
+
+Manager.prototype.get = function (key) {
+ return this.settings[key];
+};
+
+/**
+ * Set settings
+ *
+ * @api public
+ */
+
+Manager.prototype.set = function (key, value) {
+ if (arguments.length == 1) return this.get(key);
+ this.settings[key] = value;
+ this.emit('set:' + key, this.settings[key], key);
+ return this;
+};
+
+/**
+ * Enable a setting
+ *
+ * @api public
+ */
+
+Manager.prototype.enable = function (key) {
+ this.settings[key] = true;
+ this.emit('set:' + key, this.settings[key], key);
+ return this;
+};
+
+/**
+ * Disable a setting
+ *
+ * @api public
+ */
+
+Manager.prototype.disable = function (key) {
+ this.settings[key] = false;
+ this.emit('set:' + key, this.settings[key], key);
+ return this;
+};
+
+/**
+ * Checks if a setting is enabled
+ *
+ * @api public
+ */
+
+Manager.prototype.enabled = function (key) {
+ return !!this.settings[key];
+};
+
+/**
+ * Checks if a setting is disabled
+ *
+ * @api public
+ */
+
+Manager.prototype.disabled = function (key) {
+ return !this.settings[key];
+};
+
+/**
+ * Configure callbacks.
+ *
+ * @api public
+ */
+
+Manager.prototype.configure = function (env, fn) {
+ if ('function' == typeof env) {
+ env.call(this);
+ } else if (env == (process.env.NODE_ENV || 'development')) {
+ fn.call(this);
+ }
+
+ return this;
+};
+
+/**
+ * Initializes everything related to the message dispatcher.
+ *
+ * @api private
+ */
+
+Manager.prototype.initStore = function () {
+ this.handshaken = {};
+ this.connected = {};
+ this.open = {};
+ this.closed = {};
+ this.rooms = {};
+ this.roomClients = {};
+
+ var self = this;
+
+ this.store.subscribe('handshake', function (id, data) {
+ self.onHandshake(id, data);
+ });
+
+ this.store.subscribe('connect', function (id) {
+ self.onConnect(id);
+ });
+
+ this.store.subscribe('open', function (id) {
+ self.onOpen(id);
+ });
+
+ this.store.subscribe('join', function (id, room) {
+ self.onJoin(id, room);
+ });
+
+ this.store.subscribe('leave', function (id, room) {
+ self.onLeave(id, room);
+ });
+
+ this.store.subscribe('close', function (id) {
+ self.onClose(id);
+ });
+
+ this.store.subscribe('dispatch', function (room, packet, volatile, exceptions) {
+ self.onDispatch(room, packet, volatile, exceptions);
+ });
+
+ this.store.subscribe('disconnect', function (id) {
+ self.onDisconnect(id);
+ });
+};
+
+/**
+ * Called when a client handshakes.
+ *
+ * @param text
+ */
+
+Manager.prototype.onHandshake = function (id, data) {
+ this.handshaken[id] = data;
+};
+
+/**
+ * Called when a client connects (ie: transport first opens)
+ *
+ * @api private
+ */
+
+Manager.prototype.onConnect = function (id) {
+ this.connected[id] = true;
+};
+
+/**
+ * Called when a client opens a request in a different node.
+ *
+ * @api private
+ */
+
+Manager.prototype.onOpen = function (id) {
+ this.open[id] = true;
+
+ // if we were buffering messages for the client, clear them
+ if (this.closed[id]) {
+ var self = this;
+
+ this.store.unsubscribe('dispatch:' + id, function () {
+ delete self.closed[id];
+ });
+ }
+
+ // clear the current transport
+ if (this.transports[id]) {
+ this.transports[id].discard();
+ this.transports[id] = null;
+ }
+};
+
+/**
+ * Called when a message is sent to a namespace and/or room.
+ *
+ * @api private
+ */
+
+Manager.prototype.onDispatch = function (room, packet, volatile, exceptions) {
+ if (this.rooms[room]) {
+ for (var i = 0, l = this.rooms[room].length; i < l; i++) {
+ var id = this.rooms[room][i];
+
+ if (!~exceptions.indexOf(id)) {
+ if (this.transports[id] && this.transports[id].open) {
+ this.transports[id].onDispatch(packet, volatile);
+ } else if (!volatile) {
+ this.onClientDispatch(id, packet);
+ }
+ }
+ }
+ }
+};
+
+/**
+ * Called when a client joins a nsp / room.
+ *
+ * @api private
+ */
+
+Manager.prototype.onJoin = function (id, name) {
+ if (!this.roomClients[id]) {
+ this.roomClients[id] = {};
+ }
+
+ if (!this.rooms[name]) {
+ this.rooms[name] = [];
+ }
+
+ if (!~this.rooms[name].indexOf(id)) {
+ this.rooms[name].push(id);
+ this.roomClients[id][name] = true;
+ }
+};
+
+/**
+ * Called when a client leaves a nsp / room.
+ *
+ * @param private
+ */
+
+Manager.prototype.onLeave = function (id, room) {
+ if (this.rooms[room]) {
+ var index = this.rooms[room].indexOf(id);
+
+ if (index >= 0) {
+ this.rooms[room].splice(index, 1);
+ }
+
+ if (!this.rooms[room].length) {
+ delete this.rooms[room];
+ }
+ delete this.roomClients[id][room];
+ }
+};
+
+/**
+ * Called when a client closes a request in different node.
+ *
+ * @api private
+ */
+
+Manager.prototype.onClose = function (id) {
+ if (this.open[id]) {
+ delete this.open[id];
+ }
+
+ this.closed[id] = [];
+
+ var self = this;
+
+ this.store.subscribe('dispatch:' + id, function (packet, volatile) {
+ if (!volatile) {
+ self.onClientDispatch(id, packet);
+ }
+ });
+};
+
+/**
+ * Dispatches a message for a closed client.
+ *
+ * @api private
+ */
+
+Manager.prototype.onClientDispatch = function (id, packet) {
+ if (this.closed[id]) {
+ this.closed[id].push(packet);
+ }
+};
+
+/**
+ * Receives a message for a client.
+ *
+ * @api private
+ */
+
+Manager.prototype.onClientMessage = function (id, packet) {
+ if (this.namespaces[packet.endpoint]) {
+ this.namespaces[packet.endpoint].handlePacket(id, packet);
+ }
+};
+
+/**
+ * Fired when a client disconnects (not triggered).
+ *
+ * @api private
+ */
+
+Manager.prototype.onClientDisconnect = function (id, reason) {
+ for (var name in this.namespaces) {
+ this.namespaces[name].handleDisconnect(id, reason, typeof this.roomClients[id] !== 'undefined' &&
+ typeof this.roomClients[id][name] !== 'undefined');
+ }
+
+ this.onDisconnect(id);
+};
+
+/**
+ * Called when a client disconnects.
+ *
+ * @param text
+ */
+
+Manager.prototype.onDisconnect = function (id, local) {
+ delete this.handshaken[id];
+
+ if (this.open[id]) {
+ delete this.open[id];
+ }
+
+ if (this.connected[id]) {
+ delete this.connected[id];
+ }
+
+ if (this.transports[id]) {
+ this.transports[id].discard();
+ delete this.transports[id];
+ }
+
+ if (this.closed[id]) {
+ delete this.closed[id];
+ }
+
+ if (this.roomClients[id]) {
+ for (var room in this.roomClients[id]) {
+ this.onLeave(id, room);
+ }
+ delete this.roomClients[id]
+ }
+
+ this.store.destroyClient(id, this.get('client store expiration'));
+
+ this.store.unsubscribe('dispatch:' + id);
+
+ if (local) {
+ this.store.unsubscribe('message:' + id);
+ this.store.unsubscribe('disconnect:' + id);
+ }
+};
+
+/**
+ * Handles an HTTP request.
+ *
+ * @api private
+ */
+
+Manager.prototype.handleRequest = function (req, res) {
+ var data = this.checkRequest(req);
+
+ if (!data) {
+ for (var i = 0, l = this.oldListeners.length; i < l; i++) {
+ this.oldListeners[i].call(this.server, req, res);
+ }
+
+ return;
+ }
+
+ if (data.static || !data.transport && !data.protocol) {
+ if (data.static && this.enabled('browser client')) {
+ this.static.write(data.path, req, res);
+ } else {
+ res.writeHead(200);
+ res.end('Welcome to socket.io.');
+
+ this.log.info('unhandled socket.io url');
+ }
+
+ return;
+ }
+
+ if (data.protocol != protocol) {
+ res.writeHead(500);
+ res.end('Protocol version not supported.');
+
+ this.log.info('client protocol version unsupported');
+ } else {
+ if (data.id) {
+ this.handleHTTPRequest(data, req, res);
+ } else {
+ this.handleHandshake(data, req, res);
+ }
+ }
+};
+
+/**
+ * Handles an HTTP Upgrade.
+ *
+ * @api private
+ */
+
+Manager.prototype.handleUpgrade = function (req, socket, head) {
+ var data = this.checkRequest(req)
+ , self = this;
+
+ if (!data) {
+ if (this.enabled('destroy upgrade')) {
+ socket.end();
+ this.log.debug('destroying non-socket.io upgrade');
+ }
+
+ return;
+ }
+
+ req.head = head;
+ this.handleClient(data, req);
+};
+
+/**
+ * Handles a normal handshaken HTTP request (eg: long-polling)
+ *
+ * @api private
+ */
+
+Manager.prototype.handleHTTPRequest = function (data, req, res) {
+ req.res = res;
+ this.handleClient(data, req);
+};
+
+/**
+ * Intantiantes a new client.
+ *
+ * @api private
+ */
+
+Manager.prototype.handleClient = function (data, req) {
+ var socket = req.socket
+ , store = this.store
+ , self = this;
+
+ if (undefined != data.query.disconnect) {
+ if (this.transports[data.id] && this.transports[data.id].open) {
+ this.transports[data.id].onForcedDisconnect();
+ } else {
+ this.store.publish('disconnect-force:' + data.id);
+ }
+ return;
+ }
+
+ if (!~this.get('transports').indexOf(data.transport)) {
+ this.log.warn('unknown transport: "' + data.transport + '"');
+ req.connection.end();
+ return;
+ }
+
+ var transport = new transports[data.transport](this, data, req)
+ , handshaken = this.handshaken[data.id];
+
+ if (transport.disconnected) {
+ // failed during transport setup
+ req.connection.end();
+ return;
+ }
+ if (handshaken) {
+ if (transport.open) {
+ if (this.closed[data.id] && this.closed[data.id].length) {
+ transport.payload(this.closed[data.id]);
+ this.closed[data.id] = [];
+ }
+
+ this.onOpen(data.id);
+ this.store.publish('open', data.id);
+ this.transports[data.id] = transport;
+ }
+
+ if (!this.connected[data.id]) {
+ this.onConnect(data.id);
+ this.store.publish('connect', data.id);
+
+ // flag as used
+ delete handshaken.issued;
+ this.onHandshake(data.id, handshaken);
+ this.store.publish('handshake', data.id, handshaken);
+
+ // initialize the socket for all namespaces
+ for (var i in this.namespaces) {
+ var socket = this.namespaces[i].socket(data.id, true);
+
+ // echo back connect packet and fire connection event
+ if (i === '') {
+ this.namespaces[i].handlePacket(data.id, { type: 'connect' });
+ }
+ }
+
+ this.store.subscribe('message:' + data.id, function (packet) {
+ self.onClientMessage(data.id, packet);
+ });
+
+ this.store.subscribe('disconnect:' + data.id, function (reason) {
+ self.onClientDisconnect(data.id, reason);
+ });
+ }
+ } else {
+ if (transport.open) {
+ transport.error('client not handshaken', 'reconnect');
+ }
+
+ transport.discard();
+ }
+};
+
+/**
+ * Generates a session id.
+ *
+ * @api private
+ */
+
+Manager.prototype.generateId = function () {
+ return Math.abs(Math.random() * Math.random() * Date.now() | 0).toString()
+ + Math.abs(Math.random() * Math.random() * Date.now() | 0).toString();
+};
+
+/**
+ * Handles a handshake request.
+ *
+ * @api private
+ */
+
+Manager.prototype.handleHandshake = function (data, req, res) {
+ var self = this
+ , origin = req.headers.origin
+ , headers = {
+ 'Content-Type': 'text/plain'
+ };
+
+ function writeErr (status, message) {
+ if (data.query.jsonp) {
+ res.writeHead(200, { 'Content-Type': 'application/javascript' });
+ res.end('io.j[' + data.query.jsonp + '](new Error("' + message + '"));');
+ } else {
+ res.writeHead(status, headers);
+ res.end(message);
+ }
+ };
+
+ function error (err) {
+ writeErr(500, 'handshake error');
+ self.log.warn('handshake error ' + err);
+ };
+
+ if (!this.verifyOrigin(req)) {
+ writeErr(403, 'handshake bad origin');
+ return;
+ }
+
+ var handshakeData = this.handshakeData(data);
+
+ if (origin) {
+ // https://developer.mozilla.org/En/HTTP_Access_Control
+ headers['Access-Control-Allow-Origin'] = origin;
+ headers['Access-Control-Allow-Credentials'] = 'true';
+ }
+
+ this.authorize(handshakeData, function (err, authorized, newData) {
+ if (err) return error(err);
+
+ if (authorized) {
+ var id = self.generateId()
+ , hs = [
+ id
+ , self.enabled('heartbeats') ? self.get('heartbeat timeout') || '' : ''
+ , self.get('close timeout') || ''
+ , self.transports(data).join(',')
+ ].join(':');
+
+ if (data.query.jsonp) {
+ hs = 'io.j[' + data.query.jsonp + '](' + JSON.stringify(hs) + ');';
+ res.writeHead(200, { 'Content-Type': 'application/javascript' });
+ } else {
+ res.writeHead(200, headers);
+ }
+
+ res.end(hs);
+
+ self.onHandshake(id, newData || handshakeData);
+ self.store.publish('handshake', id, newData || handshakeData);
+
+ self.log.info('handshake authorized', id);
+ } else {
+ writeErr(403, 'handshake unauthorized');
+ self.log.info('handshake unauthorized');
+ }
+ })
+};
+
+/**
+ * Gets normalized handshake data
+ *
+ * @api private
+ */
+
+Manager.prototype.handshakeData = function (data) {
+ var connection = data.request.connection
+ , connectionAddress
+ , date = new Date;
+
+ if (connection.remoteAddress) {
+ connectionAddress = {
+ address: connection.remoteAddress
+ , port: connection.remotePort
+ };
+ } else if (connection.socket && connection.socket.remoteAddress) {
+ connectionAddress = {
+ address: connection.socket.remoteAddress
+ , port: connection.socket.remotePort
+ };
+ }
+
+ return {
+ headers: data.headers
+ , address: connectionAddress
+ , time: date.toString()
+ , query: data.query
+ , url: data.request.url
+ , xdomain: !!data.request.headers.origin
+ , secure: data.request.connection.secure
+ , issued: +date
+ };
+};
+
+/**
+ * Verifies the origin of a request.
+ *
+ * @api private
+ */
+
+Manager.prototype.verifyOrigin = function (request) {
+ var origin = request.headers.origin || request.headers.referer
+ , origins = this.get('origins');
+
+ if (origin === 'null') origin = '*';
+
+ if (origins.indexOf('*:*') !== -1) {
+ return true;
+ }
+
+ if (origin) {
+ try {
+ var parts = url.parse(origin);
+ parts.port = parts.port || 80;
+ var ok =
+ ~origins.indexOf(parts.hostname + ':' + parts.port) ||
+ ~origins.indexOf(parts.hostname + ':*') ||
+ ~origins.indexOf('*:' + parts.port);
+ if (!ok) this.log.warn('illegal origin: ' + origin);
+ return ok;
+ } catch (ex) {
+ this.log.warn('error parsing origin');
+ }
+ }
+ else {
+ this.log.warn('origin missing from handshake, yet required by config');
+ }
+ return false;
+};
+
+/**
+ * Handles an incoming packet.
+ *
+ * @api private
+ */
+
+Manager.prototype.handlePacket = function (sessid, packet) {
+ this.of(packet.endpoint || '').handlePacket(sessid, packet);
+};
+
+/**
+ * Performs authentication.
+ *
+ * @param Object client request data
+ * @api private
+ */
+
+Manager.prototype.authorize = function (data, fn) {
+ if (this.get('authorization')) {
+ var self = this;
+
+ this.get('authorization').call(this, data, function (err, authorized) {
+ self.log.debug('client ' + authorized ? 'authorized' : 'unauthorized');
+ fn(err, authorized);
+ });
+ } else {
+ this.log.debug('client authorized');
+ fn(null, true);
+ }
+
+ return this;
+};
+
+/**
+ * Retrieves the transports adviced to the user.
+ *
+ * @api private
+ */
+
+Manager.prototype.transports = function (data) {
+ var transp = this.get('transports')
+ , ret = [];
+
+ for (var i = 0, l = transp.length; i < l; i++) {
+ var transport = transp[i];
+
+ if (transport) {
+ if (!transport.checkClient || transport.checkClient(data)) {
+ ret.push(transport);
+ }
+ }
+ }
+
+ return ret;
+};
+
+/**
+ * Checks whether a request is a socket.io one.
+ *
+ * @return {Object} a client request data object or `false`
+ * @api private
+ */
+
+var regexp = /^\/([^\/]+)\/?([^\/]+)?\/?([^\/]+)?\/?$/
+
+Manager.prototype.checkRequest = function (req) {
+ var resource = this.get('resource');
+
+ var match;
+ if (typeof resource === 'string') {
+ match = req.url.substr(0, resource.length);
+ if (match !== resource) match = null;
+ } else {
+ match = resource.exec(req.url);
+ if (match) match = match[0];
+ }
+
+ if (match) {
+ var uri = url.parse(req.url.substr(match.length), true)
+ , path = uri.pathname || ''
+ , pieces = path.match(regexp);
+
+ // client request data
+ var data = {
+ query: uri.query || {}
+ , headers: req.headers
+ , request: req
+ , path: path
+ };
+
+ if (pieces) {
+ data.protocol = Number(pieces[1]);
+ data.transport = pieces[2];
+ data.id = pieces[3];
+ data.static = !!this.static.has(path);
+ };
+
+ return data;
+ }
+
+ return false;
+};
+
+/**
+ * Declares a socket namespace
+ *
+ * @api public
+ */
+
+Manager.prototype.of = function (nsp) {
+ if (this.namespaces[nsp]) {
+ return this.namespaces[nsp];
+ }
+
+ return this.namespaces[nsp] = new SocketNamespace(this, nsp);
+};
+
+/**
+ * Perform garbage collection on long living objects and properties that cannot
+ * be removed automatically.
+ *
+ * @api private
+ */
+
+Manager.prototype.garbageCollection = function () {
+ // clean up unused handshakes
+ var ids = Object.keys(this.handshaken)
+ , i = ids.length
+ , now = Date.now()
+ , handshake;
+
+ while (i--) {
+ handshake = this.handshaken[ids[i]];
+
+ if ('issued' in handshake && (now - handshake.issued) >= 3E4) {
+ this.onDisconnect(ids[i]);
+ }
+ }
+};
diff --git a/node_modules/socket.io/lib/namespace.js b/node_modules/socket.io/lib/namespace.js
new file mode 100644
index 0000000..6e1e1c9
--- /dev/null
+++ b/node_modules/socket.io/lib/namespace.js
@@ -0,0 +1,355 @@
+/**
+ * Module dependencies.
+ */
+
+var Socket = require('./socket')
+ , EventEmitter = process.EventEmitter
+ , parser = require('./parser')
+ , util = require('./util');
+
+/**
+ * Exports the constructor.
+ */
+
+exports = module.exports = SocketNamespace;
+
+/**
+ * Constructor.
+ *
+ * @api public.
+ */
+
+function SocketNamespace (mgr, name) {
+ this.manager = mgr;
+ this.name = name || '';
+ this.sockets = {};
+ this.auth = false;
+ this.setFlags();
+};
+
+/**
+ * Inherits from EventEmitter.
+ */
+
+SocketNamespace.prototype.__proto__ = EventEmitter.prototype;
+
+/**
+ * Copies emit since we override it.
+ *
+ * @api private
+ */
+
+SocketNamespace.prototype.$emit = EventEmitter.prototype.emit;
+
+/**
+ * Retrieves all clients as Socket instances as an array.
+ *
+ * @api public
+ */
+
+SocketNamespace.prototype.clients = function (room) {
+ var room = this.name + (room !== undefined ?
+ '/' + room : '');
+
+ if (!this.manager.rooms[room]) {
+ return [];
+ }
+
+ return this.manager.rooms[room].map(function (id) {
+ return this.socket(id);
+ }, this);
+};
+
+/**
+ * Access logger interface.
+ *
+ * @api public
+ */
+
+SocketNamespace.prototype.__defineGetter__('log', function () {
+ return this.manager.log;
+});
+
+/**
+ * Access store.
+ *
+ * @api public
+ */
+
+SocketNamespace.prototype.__defineGetter__('store', function () {
+ return this.manager.store;
+});
+
+/**
+ * JSON message flag.
+ *
+ * @api public
+ */
+
+SocketNamespace.prototype.__defineGetter__('json', function () {
+ this.flags.json = true;
+ return this;
+});
+
+/**
+ * Volatile message flag.
+ *
+ * @api public
+ */
+
+SocketNamespace.prototype.__defineGetter__('volatile', function () {
+ this.flags.volatile = true;
+ return this;
+});
+
+/**
+ * Overrides the room to relay messages to (flag).
+ *
+ * @api public
+ */
+
+SocketNamespace.prototype.in = SocketNamespace.prototype.to = function (room) {
+ this.flags.endpoint = this.name + (room ? '/' + room : '');
+ return this;
+};
+
+/**
+ * Adds a session id we should prevent relaying messages to (flag).
+ *
+ * @api public
+ */
+
+SocketNamespace.prototype.except = function (id) {
+ this.flags.exceptions.push(id);
+ return this;
+};
+
+/**
+ * Sets the default flags.
+ *
+ * @api private
+ */
+
+SocketNamespace.prototype.setFlags = function () {
+ this.flags = {
+ endpoint: this.name
+ , exceptions: []
+ };
+ return this;
+};
+
+/**
+ * Sends out a packet.
+ *
+ * @api private
+ */
+
+SocketNamespace.prototype.packet = function (packet) {
+ packet.endpoint = this.name;
+
+ var store = this.store
+ , log = this.log
+ , volatile = this.flags.volatile
+ , exceptions = this.flags.exceptions
+ , packet = parser.encodePacket(packet);
+
+ this.manager.onDispatch(this.flags.endpoint, packet, volatile, exceptions);
+ this.store.publish('dispatch', this.flags.endpoint, packet, volatile, exceptions);
+
+ this.setFlags();
+
+ return this;
+};
+
+/**
+ * Sends to everyone.
+ *
+ * @api public
+ */
+
+SocketNamespace.prototype.send = function (data) {
+ return this.packet({
+ type: this.flags.json ? 'json' : 'message'
+ , data: data
+ });
+};
+
+/**
+ * Emits to everyone (override).
+ *
+ * @api public
+ */
+
+SocketNamespace.prototype.emit = function (name) {
+ if (name == 'newListener') {
+ return this.$emit.apply(this, arguments);
+ }
+
+ return this.packet({
+ type: 'event'
+ , name: name
+ , args: util.toArray(arguments).slice(1)
+ });
+};
+
+/**
+ * Retrieves or creates a write-only socket for a client, unless specified.
+ *
+ * @param {Boolean} whether the socket will be readable when initialized
+ * @api public
+ */
+
+SocketNamespace.prototype.socket = function (sid, readable) {
+ if (!this.sockets[sid]) {
+ this.sockets[sid] = new Socket(this.manager, sid, this, readable);
+ }
+
+ return this.sockets[sid];
+};
+
+/**
+ * Sets authorization for this namespace.
+ *
+ * @api public
+ */
+
+SocketNamespace.prototype.authorization = function (fn) {
+ this.auth = fn;
+ return this;
+};
+
+/**
+ * Called when a socket disconnects entirely.
+ *
+ * @api private
+ */
+
+SocketNamespace.prototype.handleDisconnect = function (sid, reason, raiseOnDisconnect) {
+ if (this.sockets[sid] && this.sockets[sid].readable) {
+ if (raiseOnDisconnect) this.sockets[sid].onDisconnect(reason);
+ delete this.sockets[sid];
+ }
+};
+
+/**
+ * Performs authentication.
+ *
+ * @param Object client request data
+ * @api private
+ */
+
+SocketNamespace.prototype.authorize = function (data, fn) {
+ if (this.auth) {
+ var self = this;
+
+ this.auth.call(this, data, function (err, authorized) {
+ self.log.debug('client ' +
+ (authorized ? '' : 'un') + 'authorized for ' + self.name);
+ fn(err, authorized);
+ });
+ } else {
+ this.log.debug('client authorized for ' + this.name);
+ fn(null, true);
+ }
+
+ return this;
+};
+
+/**
+ * Handles a packet.
+ *
+ * @api private
+ */
+
+SocketNamespace.prototype.handlePacket = function (sessid, packet) {
+ var socket = this.socket(sessid)
+ , dataAck = packet.ack == 'data'
+ , manager = this.manager
+ , self = this;
+
+ function ack () {
+ self.log.debug('sending data ack packet');
+ socket.packet({
+ type: 'ack'
+ , args: util.toArray(arguments)
+ , ackId: packet.id
+ });
+ };
+
+ function error (err) {
+ self.log.warn('handshake error ' + err + ' for ' + self.name);
+ socket.packet({ type: 'error', reason: err });
+ };
+
+ function connect () {
+ self.manager.onJoin(sessid, self.name);
+ self.store.publish('join', sessid, self.name);
+
+ // packet echo
+ socket.packet({ type: 'connect' });
+
+ // emit connection event
+ self.$emit('connection', socket);
+ };
+
+ switch (packet.type) {
+ case 'connect':
+ if (packet.endpoint == '') {
+ connect();
+ } else {
+ var handshakeData = manager.handshaken[sessid];
+
+ this.authorize(handshakeData, function (err, authorized, newData) {
+ if (err) return error(err);
+
+ if (authorized) {
+ manager.onHandshake(sessid, newData || handshakeData);
+ self.store.publish('handshake', sessid, newData || handshakeData);
+ connect();
+ } else {
+ error('unauthorized');
+ }
+ });
+ }
+ break;
+
+ case 'ack':
+ if (socket.acks[packet.ackId]) {
+ socket.acks[packet.ackId].apply(socket, packet.args);
+ } else {
+ this.log.info('unknown ack packet');
+ }
+ break;
+
+ case 'event':
+ // check if the emitted event is not blacklisted
+ if (-~manager.get('blacklist').indexOf(packet.name)) {
+ this.log.debug('ignoring blacklisted event `' + packet.name + '`');
+ } else {
+ var params = [packet.name].concat(packet.args);
+
+ if (dataAck) {
+ params.push(ack);
+ }
+
+ socket.$emit.apply(socket, params);
+ }
+ break;
+
+ case 'disconnect':
+ this.manager.onLeave(sessid, this.name);
+ this.store.publish('leave', sessid, this.name);
+
+ socket.$emit('disconnect', packet.reason || 'packet');
+ break;
+
+ case 'json':
+ case 'message':
+ var params = ['message', packet.data];
+
+ if (dataAck)
+ params.push(ack);
+
+ socket.$emit.apply(socket, params);
+ };
+};
diff --git a/node_modules/socket.io/lib/parser.js b/node_modules/socket.io/lib/parser.js
new file mode 100644
index 0000000..d56b550
--- /dev/null
+++ b/node_modules/socket.io/lib/parser.js
@@ -0,0 +1,249 @@
+
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module dependencies.
+ */
+
+/**
+ * Packet types.
+ */
+
+var packets = exports.packets = {
+ 'disconnect': 0
+ , 'connect': 1
+ , 'heartbeat': 2
+ , 'message': 3
+ , 'json': 4
+ , 'event': 5
+ , 'ack': 6
+ , 'error': 7
+ , 'noop': 8
+ }
+ , packetslist = Object.keys(packets);
+
+/**
+ * Errors reasons.
+ */
+
+var reasons = exports.reasons = {
+ 'transport not supported': 0
+ , 'client not handshaken': 1
+ , 'unauthorized': 2
+ }
+ , reasonslist = Object.keys(reasons);
+
+/**
+ * Errors advice.
+ */
+
+var advice = exports.advice = {
+ 'reconnect': 0
+ }
+ , advicelist = Object.keys(advice);
+
+/**
+ * Encodes a packet.
+ *
+ * @api private
+ */
+
+exports.encodePacket = function (packet) {
+ var type = packets[packet.type]
+ , id = packet.id || ''
+ , endpoint = packet.endpoint || ''
+ , ack = packet.ack
+ , data = null;
+
+ switch (packet.type) {
+ case 'message':
+ if (packet.data !== '')
+ data = packet.data;
+ break;
+
+ case 'event':
+ var ev = { name: packet.name };
+
+ if (packet.args && packet.args.length) {
+ ev.args = packet.args;
+ }
+
+ data = JSON.stringify(ev);
+ break;
+
+ case 'json':
+ data = JSON.stringify(packet.data);
+ break;
+
+ case 'ack':
+ data = packet.ackId
+ + (packet.args && packet.args.length
+ ? '+' + JSON.stringify(packet.args) : '');
+ break;
+
+ case 'connect':
+ if (packet.qs)
+ data = packet.qs;
+ break;
+
+ case 'error':
+ var reason = packet.reason ? reasons[packet.reason] : ''
+ , adv = packet.advice ? advice[packet.advice] : ''
+
+ if (reason !== '' || adv !== '')
+ data = reason + (adv !== '' ? ('+' + adv) : '')
+
+ break;
+ }
+
+ // construct packet with required fragments
+ var encoded = type + ':' + id + (ack == 'data' ? '+' : '') + ':' + endpoint;
+
+ // data fragment is optional
+ if (data !== null && data !== undefined)
+ encoded += ':' + data;
+
+ return encoded;
+};
+
+/**
+ * Encodes multiple messages (payload).
+ *
+ * @param {Array} messages
+ * @api private
+ */
+
+exports.encodePayload = function (packets) {
+ var decoded = '';
+
+ if (packets.length == 1)
+ return packets[0];
+
+ for (var i = 0, l = packets.length; i < l; i++) {
+ var packet = packets[i];
+ decoded += '\ufffd' + packet.length + '\ufffd' + packets[i]
+ }
+
+ return decoded;
+};
+
+/**
+ * Decodes a packet
+ *
+ * @api private
+ */
+
+var regexp = /([^:]+):([0-9]+)?(\+)?:([^:]+)?:?([\s\S]*)?/;
+
+/**
+ * Wrap the JSON.parse in a seperate function the crankshaft optimizer will
+ * only punish this function for the usage for try catch
+ *
+ * @api private
+ */
+
+function parse (data) {
+ try { return JSON.parse(data) }
+ catch (e) { return false }
+}
+
+exports.decodePacket = function (data) {
+ var pieces = data.match(regexp);
+
+ if (!pieces) return {};
+
+ var id = pieces[2] || ''
+ , data = pieces[5] || ''
+ , packet = {
+ type: packetslist[pieces[1]]
+ , endpoint: pieces[4] || ''
+ };
+
+ // whether we need to acknowledge the packet
+ if (id) {
+ packet.id = id;
+ if (pieces[3])
+ packet.ack = 'data';
+ else
+ packet.ack = true;
+ }
+
+ // handle different packet types
+ switch (packet.type) {
+ case 'message':
+ packet.data = data || '';
+ break;
+
+ case 'event':
+ pieces = parse(data);
+ if (pieces) {
+ packet.name = pieces.name;
+ packet.args = pieces.args;
+ }
+
+ packet.args = packet.args || [];
+ break;
+
+ case 'json':
+ packet.data = parse(data);
+ break;
+
+ case 'connect':
+ packet.qs = data || '';
+ break;
+
+ case 'ack':
+ pieces = data.match(/^([0-9]+)(\+)?(.*)/);
+ if (pieces) {
+ packet.ackId = pieces[1];
+ packet.args = [];
+
+ if (pieces[3]) {
+ packet.args = parse(pieces[3]) || [];
+ }
+ }
+ break;
+
+ case 'error':
+ pieces = data.split('+');
+ packet.reason = reasonslist[pieces[0]] || '';
+ packet.advice = advicelist[pieces[1]] || '';
+ }
+
+ return packet;
+};
+
+/**
+ * Decodes data payload. Detects multiple messages
+ *
+ * @return {Array} messages
+ * @api public
+ */
+
+exports.decodePayload = function (data) {
+ if (undefined == data || null == data) {
+ return [];
+ }
+
+ if (data[0] == '\ufffd') {
+ var ret = [];
+
+ for (var i = 1, length = ''; i < data.length; i++) {
+ if (data[i] == '\ufffd') {
+ ret.push(exports.decodePacket(data.substr(i + 1, length)));
+ i += Number(length) + 1;
+ length = '';
+ } else {
+ length += data[i];
+ }
+ }
+
+ return ret;
+ } else {
+ return [exports.decodePacket(data)];
+ }
+};
diff --git a/node_modules/socket.io/lib/socket.io.js b/node_modules/socket.io/lib/socket.io.js
new file mode 100644
index 0000000..c67c9ba
--- /dev/null
+++ b/node_modules/socket.io/lib/socket.io.js
@@ -0,0 +1,136 @@
+
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module dependencies.
+ */
+
+var client = require('socket.io-client');
+
+/**
+ * Version.
+ */
+
+exports.version = '0.9.4';
+
+/**
+ * Supported protocol version.
+ */
+
+exports.protocol = 1;
+
+/**
+ * Client that we serve.
+ */
+
+exports.clientVersion = client.version;
+
+/**
+ * Attaches a manager
+ *
+ * @param {HTTPServer/Number} a HTTP/S server or a port number to listen on.
+ * @param {Object} opts to be passed to Manager and/or http server
+ * @param {Function} callback if a port is supplied
+ * @api public
+ */
+
+exports.listen = function (server, options, fn) {
+ if ('function' == typeof options) {
+ fn = options;
+ options = {};
+ }
+
+ if ('undefined' == typeof server) {
+ // create a server that listens on port 80
+ server = 80;
+ }
+
+ if ('number' == typeof server) {
+ // if a port number is passed
+ var port = server;
+
+ if (options && options.key)
+ server = require('https').createServer(options);
+ else
+ server = require('http').createServer();
+
+ // default response
+ server.on('request', function (req, res) {
+ res.writeHead(200);
+ res.end('Welcome to socket.io.');
+ });
+
+ server.listen(port, fn);
+ }
+
+ // otherwise assume a http/s server
+ return new exports.Manager(server, options);
+};
+
+/**
+ * Manager constructor.
+ *
+ * @api public
+ */
+
+exports.Manager = require('./manager');
+
+/**
+ * Transport constructor.
+ *
+ * @api public
+ */
+
+exports.Transport = require('./transport');
+
+/**
+ * Socket constructor.
+ *
+ * @api public
+ */
+
+exports.Socket = require('./socket');
+
+/**
+ * Static constructor.
+ *
+ * @api public
+ */
+
+exports.Static = require('./static');
+
+/**
+ * Store constructor.
+ *
+ * @api public
+ */
+
+exports.Store = require('./store');
+
+/**
+ * Memory Store constructor.
+ *
+ * @api public
+ */
+
+exports.MemoryStore = require('./stores/memory');
+
+/**
+ * Redis Store constructor.
+ *
+ * @api public
+ */
+
+exports.RedisStore = require('./stores/redis');
+
+/**
+ * Parser.
+ *
+ * @api public
+ */
+
+exports.parser = require('./parser');
diff --git a/node_modules/socket.io/lib/socket.js b/node_modules/socket.io/lib/socket.js
new file mode 100644
index 0000000..d9807f6
--- /dev/null
+++ b/node_modules/socket.io/lib/socket.js
@@ -0,0 +1,369 @@
+
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module dependencies.
+ */
+
+var parser = require('./parser')
+ , util = require('./util')
+ , EventEmitter = process.EventEmitter
+
+/**
+ * Export the constructor.
+ */
+
+exports = module.exports = Socket;
+
+/**
+ * Default error event listener to prevent uncaught exceptions.
+ */
+
+var defaultError = function () {};
+
+/**
+ * Socket constructor.
+ *
+ * @param {Manager} manager instance
+ * @param {String} session id
+ * @param {Namespace} namespace the socket belongs to
+ * @param {Boolean} whether the
+ * @api public
+ */
+
+function Socket (manager, id, nsp, readable) {
+ this.id = id;
+ this.namespace = nsp;
+ this.manager = manager;
+ this.disconnected = false;
+ this.ackPackets = 0;
+ this.acks = {};
+ this.setFlags();
+ this.readable = readable;
+ this.store = this.manager.store.client(this.id);
+ this.on('error', defaultError);
+};
+
+/**
+ * Inherits from EventEmitter.
+ */
+
+Socket.prototype.__proto__ = EventEmitter.prototype;
+
+/**
+ * Accessor shortcut for the handshake data
+ *
+ * @api private
+ */
+
+Socket.prototype.__defineGetter__('handshake', function () {
+ return this.manager.handshaken[this.id];
+});
+
+/**
+ * Accessor shortcut for the transport type
+ *
+ * @api private
+ */
+
+Socket.prototype.__defineGetter__('transport', function () {
+ return this.manager.transports[this.id].name;
+});
+
+/**
+ * Accessor shortcut for the logger.
+ *
+ * @api private
+ */
+
+Socket.prototype.__defineGetter__('log', function () {
+ return this.manager.log;
+});
+
+/**
+ * JSON message flag.
+ *
+ * @api public
+ */
+
+Socket.prototype.__defineGetter__('json', function () {
+ this.flags.json = true;
+ return this;
+});
+
+/**
+ * Volatile message flag.
+ *
+ * @api public
+ */
+
+Socket.prototype.__defineGetter__('volatile', function () {
+ this.flags.volatile = true;
+ return this;
+});
+
+/**
+ * Broadcast message flag.
+ *
+ * @api public
+ */
+
+Socket.prototype.__defineGetter__('broadcast', function () {
+ this.flags.broadcast = true;
+ return this;
+});
+
+/**
+ * Overrides the room to broadcast messages to (flag)
+ *
+ * @api public
+ */
+
+Socket.prototype.to = Socket.prototype.in = function (room) {
+ this.flags.room = room;
+ return this;
+};
+
+/**
+ * Resets flags
+ *
+ * @api private
+ */
+
+Socket.prototype.setFlags = function () {
+ this.flags = {
+ endpoint: this.namespace.name
+ , room: ''
+ };
+ return this;
+};
+
+/**
+ * Triggered on disconnect
+ *
+ * @api private
+ */
+
+Socket.prototype.onDisconnect = function (reason) {
+ if (!this.disconnected) {
+ this.$emit('disconnect', reason);
+ this.disconnected = true;
+ }
+};
+
+/**
+ * Joins a user to a room.
+ *
+ * @api public
+ */
+
+Socket.prototype.join = function (name, fn) {
+ var nsp = this.namespace.name
+ , name = (nsp + '/') + name;
+
+ this.manager.onJoin(this.id, name);
+ this.manager.store.publish('join', this.id, name);
+
+ if (fn) {
+ this.log.warn('Client#join callback is deprecated');
+ fn();
+ }
+
+ return this;
+};
+
+/**
+ * Un-joins a user from a room.
+ *
+ * @api public
+ */
+
+Socket.prototype.leave = function (name, fn) {
+ var nsp = this.namespace.name
+ , name = (nsp + '/') + name;
+
+ this.manager.onLeave(this.id, name);
+ this.manager.store.publish('leave', this.id, name);
+
+ if (fn) {
+ this.log.warn('Client#leave callback is deprecated');
+ fn();
+ }
+
+ return this;
+};
+
+/**
+ * Transmits a packet.
+ *
+ * @api private
+ */
+
+Socket.prototype.packet = function (packet) {
+ if (this.flags.broadcast) {
+ this.log.debug('broadcasting packet');
+ this.namespace.in(this.flags.room).except(this.id).packet(packet);
+ } else {
+ packet.endpoint = this.flags.endpoint;
+ packet = parser.encodePacket(packet);
+
+ this.dispatch(packet, this.flags.volatile);
+ }
+
+ this.setFlags();
+
+ return this;
+};
+
+/**
+ * Dispatches a packet
+ *
+ * @api private
+ */
+
+Socket.prototype.dispatch = function (packet, volatile) {
+ if (this.manager.transports[this.id] && this.manager.transports[this.id].open) {
+ this.manager.transports[this.id].onDispatch(packet, volatile);
+ } else {
+ if (!volatile) {
+ this.manager.onClientDispatch(this.id, packet, volatile);
+ }
+
+ this.manager.store.publish('dispatch:' + this.id, packet, volatile);
+ }
+};
+
+/**
+ * Stores data for the client.
+ *
+ * @api public
+ */
+
+Socket.prototype.set = function (key, value, fn) {
+ this.store.set(key, value, fn);
+ return this;
+};
+
+/**
+ * Retrieves data for the client
+ *
+ * @api public
+ */
+
+Socket.prototype.get = function (key, fn) {
+ this.store.get(key, fn);
+ return this;
+};
+
+/**
+ * Checks data for the client
+ *
+ * @api public
+ */
+
+Socket.prototype.has = function (key, fn) {
+ this.store.has(key, fn);
+ return this;
+};
+
+/**
+ * Deletes data for the client
+ *
+ * @api public
+ */
+
+Socket.prototype.del = function (key, fn) {
+ this.store.del(key, fn);
+ return this;
+};
+
+/**
+ * Kicks client
+ *
+ * @api public
+ */
+
+Socket.prototype.disconnect = function () {
+ if (!this.disconnected) {
+ this.log.info('booting client');
+
+ if ('' === this.namespace.name) {
+ if (this.manager.transports[this.id] && this.manager.transports[this.id].open) {
+ this.manager.transports[this.id].onForcedDisconnect();
+ } else {
+ this.manager.onClientDisconnect(this.id);
+ this.manager.store.publish('disconnect:' + this.id);
+ }
+ } else {
+ this.packet({type: 'disconnect'});
+ this.manager.onLeave(this.id, this.namespace.name);
+ this.$emit('disconnect', 'booted');
+ }
+
+ }
+
+ return this;
+};
+
+/**
+ * Send a message.
+ *
+ * @api public
+ */
+
+Socket.prototype.send = function (data, fn) {
+ var packet = {
+ type: this.flags.json ? 'json' : 'message'
+ , data: data
+ };
+
+ if (fn) {
+ packet.id = ++this.ackPackets;
+ packet.ack = true;
+ this.acks[packet.id] = fn;
+ }
+
+ return this.packet(packet);
+};
+
+/**
+ * Original emit function.
+ *
+ * @api private
+ */
+
+Socket.prototype.$emit = EventEmitter.prototype.emit;
+
+/**
+ * Emit override for custom events.
+ *
+ * @api public
+ */
+
+Socket.prototype.emit = function (ev) {
+ if (ev == 'newListener') {
+ return this.$emit.apply(this, arguments);
+ }
+
+ var args = util.toArray(arguments).slice(1)
+ , lastArg = args[args.length - 1]
+ , packet = {
+ type: 'event'
+ , name: ev
+ };
+
+ if ('function' == typeof lastArg) {
+ packet.id = ++this.ackPackets;
+ packet.ack = lastArg.length ? 'data' : true;
+ this.acks[packet.id] = lastArg;
+ args = args.slice(0, args.length - 1);
+ }
+
+ packet.args = args;
+
+ return this.packet(packet);
+};
diff --git a/node_modules/socket.io/lib/static.js b/node_modules/socket.io/lib/static.js
new file mode 100644
index 0000000..e3117ed
--- /dev/null
+++ b/node_modules/socket.io/lib/static.js
@@ -0,0 +1,395 @@
+
+/*!
+* socket.io-node
+* Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+* MIT Licensed
+*/
+
+/**
+ * Module dependencies.
+ */
+
+var client = require('socket.io-client')
+ , cp = require('child_process')
+ , fs = require('fs')
+ , util = require('./util');
+
+/**
+ * File type details.
+ *
+ * @api private
+ */
+
+var mime = {
+ js: {
+ type: 'application/javascript'
+ , encoding: 'utf8'
+ , gzip: true
+ }
+ , swf: {
+ type: 'application/x-shockwave-flash'
+ , encoding: 'binary'
+ , gzip: false
+ }
+};
+
+/**
+ * Regexp for matching custom transport patterns. Users can configure their own
+ * socket.io bundle based on the url structure. Different transport names are
+ * concatinated using the `+` char. /socket.io/socket.io+websocket.js should
+ * create a bundle that only contains support for the websocket.
+ *
+ * @api private
+ */
+
+var bundle = /\+((?:\+)?[\w\-]+)*(?:\.v\d+\.\d+\.\d+)?(?:\.js)$/
+ , versioning = /\.v\d+\.\d+\.\d+(?:\.js)$/;
+
+/**
+ * Export the constructor
+ */
+
+exports = module.exports = Static;
+
+/**
+ * Static constructor
+ *
+ * @api public
+ */
+
+function Static (manager) {
+ this.manager = manager;
+ this.cache = {};
+ this.paths = {};
+
+ this.init();
+}
+
+/**
+ * Initialize the Static by adding default file paths.
+ *
+ * @api public
+ */
+
+Static.prototype.init = function () {
+ /**
+ * Generates a unique id based the supplied transports array
+ *
+ * @param {Array} transports The array with transport types
+ * @api private
+ */
+ function id (transports) {
+ var id = transports.join('').split('').map(function (char) {
+ return ('' + char.charCodeAt(0)).split('').pop();
+ }).reduce(function (char, id) {
+ return char +id;
+ });
+
+ return client.version + ':' + id;
+ }
+
+ /**
+ * Generates a socket.io-client file based on the supplied transports.
+ *
+ * @param {Array} transports The array with transport types
+ * @param {Function} callback Callback for the static.write
+ * @api private
+ */
+
+ function build (transports, callback) {
+ client.builder(transports, {
+ minify: self.manager.enabled('browser client minification')
+ }, function (err, content) {
+ callback(err, content ? new Buffer(content) : null, id(transports));
+ }
+ );
+ }
+
+ var self = this;
+
+ // add our default static files
+ this.add('/static/flashsocket/WebSocketMain.swf', {
+ file: client.dist + '/WebSocketMain.swf'
+ });
+
+ this.add('/static/flashsocket/WebSocketMainInsecure.swf', {
+ file: client.dist + '/WebSocketMainInsecure.swf'
+ });
+
+ // generates dedicated build based on the available transports
+ this.add('/socket.io.js', function (path, callback) {
+ build(self.manager.get('transports'), callback);
+ });
+
+ this.add('/socket.io.v', { mime: mime.js }, function (path, callback) {
+ build(self.manager.get('transports'), callback);
+ });
+
+ // allow custom builds based on url paths
+ this.add('/socket.io+', { mime: mime.js }, function (path, callback) {
+ var available = self.manager.get('transports')
+ , matches = path.match(bundle)
+ , transports = [];
+
+ if (!matches) return callback('No valid transports');
+
+ // make sure they valid transports
+ matches[0].split('.')[0].split('+').slice(1).forEach(function (transport) {
+ if (!!~available.indexOf(transport)) {
+ transports.push(transport);
+ }
+ });
+
+ if (!transports.length) return callback('No valid transports');
+ build(transports, callback);
+ });
+
+ // clear cache when transports change
+ this.manager.on('set:transports', function (key, value) {
+ delete self.cache['/socket.io.js'];
+ Object.keys(self.cache).forEach(function (key) {
+ if (bundle.test(key)) {
+ delete self.cache[key];
+ }
+ });
+ });
+};
+
+/**
+ * Gzip compress buffers.
+ *
+ * @param {Buffer} data The buffer that needs gzip compression
+ * @param {Function} callback
+ * @api public
+ */
+
+Static.prototype.gzip = function (data, callback) {
+ var gzip = cp.spawn('gzip', ['-9', '-c', '-f', '-n'])
+ , encoding = Buffer.isBuffer(data) ? 'binary' : 'utf8'
+ , buffer = []
+ , err;
+
+ gzip.stdout.on('data', function (data) {
+ buffer.push(data);
+ });
+
+ gzip.stderr.on('data', function (data) {
+ err = data +'';
+ buffer.length = 0;
+ });
+
+ gzip.on('exit', function () {
+ if (err) return callback(err);
+
+ var size = 0
+ , index = 0
+ , i = buffer.length
+ , content;
+
+ while (i--) {
+ size += buffer[i].length;
+ }
+
+ content = new Buffer(size);
+ i = buffer.length;
+
+ buffer.forEach(function (buffer) {
+ var length = buffer.length;
+
+ buffer.copy(content, index, 0, length);
+ index += length;
+ });
+
+ buffer.length = 0;
+ callback(null, content);
+ });
+
+ gzip.stdin.end(data, encoding);
+};
+
+/**
+ * Is the path a static file?
+ *
+ * @param {String} path The path that needs to be checked
+ * @api public
+ */
+
+Static.prototype.has = function (path) {
+ // fast case
+ if (this.paths[path]) return this.paths[path];
+
+ var keys = Object.keys(this.paths)
+ , i = keys.length;
+
+ while (i--) {
+ if (-~path.indexOf(keys[i])) return this.paths[keys[i]];
+ }
+
+ return false;
+};
+
+/**
+ * Add new paths new paths that can be served using the static provider.
+ *
+ * @param {String} path The path to respond to
+ * @param {Options} options Options for writing out the response
+ * @param {Function} [callback] Optional callback if no options.file is
+ * supplied this would be called instead.
+ * @api public
+ */
+
+Static.prototype.add = function (path, options, callback) {
+ var extension = /(?:\.(\w{1,4}))$/.exec(path);
+
+ if (!callback && typeof options == 'function') {
+ callback = options;
+ options = {};
+ }
+
+ options.mime = options.mime || (extension ? mime[extension[1]] : false);
+
+ if (callback) options.callback = callback;
+ if (!(options.file || options.callback) || !options.mime) return false;
+
+ this.paths[path] = options;
+
+ return true;
+};
+
+/**
+ * Writes a static response.
+ *
+ * @param {String} path The path for the static content
+ * @param {HTTPRequest} req The request object
+ * @param {HTTPResponse} res The response object
+ * @api public
+ */
+
+Static.prototype.write = function (path, req, res) {
+ /**
+ * Write a response without throwing errors because can throw error if the
+ * response is no longer writable etc.
+ *
+ * @api private
+ */
+
+ function write (status, headers, content, encoding) {
+ try {
+ res.writeHead(status, headers || undefined);
+
+ // only write content if it's not a HEAD request and we actually have
+ // some content to write (304's doesn't have content).
+ res.end(
+ req.method !== 'HEAD' && content ? content : ''
+ , encoding || undefined
+ );
+ } catch (e) {}
+ }
+
+ /**
+ * Answers requests depending on the request properties and the reply object.
+ *
+ * @param {Object} reply The details and content to reply the response with
+ * @api private
+ */
+
+ function answer (reply) {
+ var cached = req.headers['if-none-match'] === reply.etag;
+ if (cached && self.manager.enabled('browser client etag')) {
+ return write(304);
+ }
+
+ var accept = req.headers['accept-encoding'] || ''
+ , gzip = !!~accept.toLowerCase().indexOf('gzip')
+ , mime = reply.mime
+ , versioned = reply.versioned
+ , headers = {
+ 'Content-Type': mime.type
+ };
+
+ // check if we can add a etag
+ if (self.manager.enabled('browser client etag') && reply.etag && !versioned) {
+ headers['Etag'] = reply.etag;
+ }
+
+ // see if we need to set Expire headers because the path is versioned
+ if (versioned) {
+ var expires = self.manager.get('browser client expires');
+ headers['Cache-Control'] = 'private, x-gzip-ok="", max-age=' + expires;
+ headers['Date'] = new Date().toUTCString();
+ headers['Expires'] = new Date(Date.now() + (expires * 1000)).toUTCString();
+ }
+
+ if (gzip && reply.gzip) {
+ headers['Content-Length'] = reply.gzip.length;
+ headers['Content-Encoding'] = 'gzip';
+ headers['Vary'] = 'Accept-Encoding';
+ write(200, headers, reply.gzip.content, mime.encoding);
+ } else {
+ headers['Content-Length'] = reply.length;
+ write(200, headers, reply.content, mime.encoding);
+ }
+
+ self.manager.log.debug('served static content ' + path);
+ }
+
+ var self = this
+ , details;
+
+ // most common case first
+ if (this.manager.enabled('browser client cache') && this.cache[path]) {
+ return answer(this.cache[path]);
+ } else if (this.manager.get('browser client handler')) {
+ return this.manager.get('browser client handler').call(this, req, res);
+ } else if ((details = this.has(path))) {
+ /**
+ * A small helper function that will let us deal with fs and dynamic files
+ *
+ * @param {Object} err Optional error
+ * @param {Buffer} content The data
+ * @api private
+ */
+
+ function ready (err, content, etag) {
+ if (err) {
+ self.manager.log.warn('Unable to serve file. ' + (err.message || err));
+ return write(500, null, 'Error serving static ' + path);
+ }
+
+ // store the result in the cache
+ var reply = self.cache[path] = {
+ content: content
+ , length: content.length
+ , mime: details.mime
+ , etag: etag || client.version
+ , versioned: versioning.test(path)
+ };
+
+ // check if gzip is enabled
+ if (details.mime.gzip && self.manager.enabled('browser client gzip')) {
+ self.gzip(content, function (err, content) {
+ if (!err) {
+ reply.gzip = {
+ content: content
+ , length: content.length
+ }
+ }
+
+ answer(reply);
+ });
+ } else {
+ answer(reply);
+ }
+ }
+
+ if (details.file) {
+ fs.readFile(details.file, ready);
+ } else if(details.callback) {
+ details.callback.call(this, path, ready);
+ } else {
+ write(404, null, 'File handle not found');
+ }
+ } else {
+ write(404, null, 'File not found');
+ }
+};
diff --git a/node_modules/socket.io/lib/store.js b/node_modules/socket.io/lib/store.js
new file mode 100644
index 0000000..06c0389
--- /dev/null
+++ b/node_modules/socket.io/lib/store.js
@@ -0,0 +1,98 @@
+
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Expose the constructor.
+ */
+
+exports = module.exports = Store;
+
+/**
+ * Module dependencies.
+ */
+
+var EventEmitter = process.EventEmitter;
+
+/**
+ * Store interface
+ *
+ * @api public
+ */
+
+function Store (options) {
+ this.options = options;
+ this.clients = {};
+};
+
+/**
+ * Inherit from EventEmitter.
+ */
+
+Store.prototype.__proto__ = EventEmitter.prototype;
+
+/**
+ * Initializes a client store
+ *
+ * @param {String} id
+ * @api public
+ */
+
+Store.prototype.client = function (id) {
+ if (!this.clients[id]) {
+ this.clients[id] = new (this.constructor.Client)(this, id);
+ }
+
+ return this.clients[id];
+};
+
+/**
+ * Destroys a client
+ *
+ * @api {String} sid
+ * @param {Number} number of seconds to expire client data
+ * @api private
+ */
+
+Store.prototype.destroyClient = function (id, expiration) {
+ if (this.clients[id]) {
+ this.clients[id].destroy(expiration);
+ delete this.clients[id];
+ }
+
+ return this;
+};
+
+/**
+ * Destroys the store
+ *
+ * @param {Number} number of seconds to expire client data
+ * @api private
+ */
+
+Store.prototype.destroy = function (clientExpiration) {
+ var keys = Object.keys(this.clients)
+ , count = keys.length;
+
+ for (var i = 0, l = count; i < l; i++) {
+ this.destroyClient(keys[i], clientExpiration);
+ }
+
+ this.clients = {};
+
+ return this;
+};
+
+/**
+ * Client.
+ *
+ * @api public
+ */
+
+Store.Client = function (store, id) {
+ this.store = store;
+ this.id = id;
+};
diff --git a/node_modules/socket.io/lib/stores/memory.js b/node_modules/socket.io/lib/stores/memory.js
new file mode 100644
index 0000000..8b731a7
--- /dev/null
+++ b/node_modules/socket.io/lib/stores/memory.js
@@ -0,0 +1,143 @@
+
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module dependencies.
+ */
+
+var crypto = require('crypto')
+ , Store = require('../store');
+
+/**
+ * Exports the constructor.
+ */
+
+exports = module.exports = Memory;
+Memory.Client = Client;
+
+/**
+ * Memory store
+ *
+ * @api public
+ */
+
+function Memory (opts) {
+ Store.call(this, opts);
+};
+
+/**
+ * Inherits from Store.
+ */
+
+Memory.prototype.__proto__ = Store.prototype;
+
+/**
+ * Publishes a message.
+ *
+ * @api private
+ */
+
+Memory.prototype.publish = function () { };
+
+/**
+ * Subscribes to a channel
+ *
+ * @api private
+ */
+
+Memory.prototype.subscribe = function () { };
+
+/**
+ * Unsubscribes
+ *
+ * @api private
+ */
+
+Memory.prototype.unsubscribe = function () { };
+
+/**
+ * Client constructor
+ *
+ * @api private
+ */
+
+function Client () {
+ Store.Client.apply(this, arguments);
+ this.data = {};
+};
+
+/**
+ * Inherits from Store.Client
+ */
+
+Client.prototype.__proto__ = Store.Client;
+
+/**
+ * Gets a key
+ *
+ * @api public
+ */
+
+Client.prototype.get = function (key, fn) {
+ fn(null, this.data[key] === undefined ? null : this.data[key]);
+ return this;
+};
+
+/**
+ * Sets a key
+ *
+ * @api public
+ */
+
+Client.prototype.set = function (key, value, fn) {
+ this.data[key] = value;
+ fn && fn(null);
+ return this;
+};
+
+/**
+ * Has a key
+ *
+ * @api public
+ */
+
+Client.prototype.has = function (key, fn) {
+ fn(null, key in this.data);
+};
+
+/**
+ * Deletes a key
+ *
+ * @api public
+ */
+
+Client.prototype.del = function (key, fn) {
+ delete this.data[key];
+ fn && fn(null);
+ return this;
+};
+
+/**
+ * Destroys the client.
+ *
+ * @param {Number} number of seconds to expire data
+ * @api private
+ */
+
+Client.prototype.destroy = function (expiration) {
+ if ('number' != typeof expiration) {
+ this.data = {};
+ } else {
+ var self = this;
+
+ setTimeout(function () {
+ self.data = {};
+ }, expiration * 1000);
+ }
+
+ return this;
+};
diff --git a/node_modules/socket.io/lib/stores/redis.js b/node_modules/socket.io/lib/stores/redis.js
new file mode 100644
index 0000000..8fea235
--- /dev/null
+++ b/node_modules/socket.io/lib/stores/redis.js
@@ -0,0 +1,269 @@
+
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module dependencies.
+ */
+
+var crypto = require('crypto')
+ , Store = require('../store')
+ , assert = require('assert');
+
+/**
+ * Exports the constructor.
+ */
+
+exports = module.exports = Redis;
+Redis.Client = Client;
+
+/**
+ * Redis store.
+ * Options:
+ * - nodeId (fn) gets an id that uniquely identifies this node
+ * - redis (fn) redis constructor, defaults to redis
+ * - redisPub (object) options to pass to the pub redis client
+ * - redisSub (object) options to pass to the sub redis client
+ * - redisClient (object) options to pass to the general redis client
+ * - pack (fn) custom packing, defaults to JSON or msgpack if installed
+ * - unpack (fn) custom packing, defaults to JSON or msgpack if installed
+ *
+ * @api public
+ */
+
+function Redis (opts) {
+ opts = opts || {};
+
+ // node id to uniquely identify this node
+ var nodeId = opts.nodeId || function () {
+ // by default, we generate a random id
+ return Math.abs(Math.random() * Math.random() * Date.now() | 0);
+ };
+
+ this.nodeId = nodeId();
+
+ // packing / unpacking mechanism
+ if (opts.pack) {
+ this.pack = opts.pack;
+ this.unpack = opts.unpack;
+ } else {
+ try {
+ var msgpack = require('msgpack');
+ this.pack = msgpack.pack;
+ this.unpack = msgpack.unpack;
+ } catch (e) {
+ this.pack = JSON.stringify;
+ this.unpack = JSON.parse;
+ }
+ }
+
+ var redis = opts.redis || require('redis')
+ , RedisClient = redis.RedisClient;
+
+ // initialize a pubsub client and a regular client
+ if (opts.redisPub instanceof RedisClient) {
+ this.pub = opts.redisPub;
+ } else {
+ opts.redisPub || (opts.redisPub = {});
+ this.pub = redis.createClient(opts.redisPub.port, opts.redisPub.host, opts.redisPub);
+ }
+ if (opts.redisSub instanceof RedisClient) {
+ this.sub = opts.redisSub;
+ } else {
+ opts.redisSub || (opts.redisSub = {});
+ this.sub = redis.createClient(opts.redisSub.port, opts.redisSub.host, opts.redisSub);
+ }
+ if (opts.redisClient instanceof RedisClient) {
+ this.cmd = opts.redisClient;
+ } else {
+ opts.redisClient || (opts.redisClient = {});
+ this.cmd = redis.createClient(opts.redisClient.port, opts.redisClient.host, opts.redisClient);
+ }
+
+ Store.call(this, opts);
+
+ this.sub.setMaxListeners(0);
+ this.setMaxListeners(0);
+};
+
+/**
+ * Inherits from Store.
+ */
+
+Redis.prototype.__proto__ = Store.prototype;
+
+/**
+ * Publishes a message.
+ *
+ * @api private
+ */
+
+Redis.prototype.publish = function (name) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ this.pub.publish(name, this.pack({ nodeId: this.nodeId, args: args }));
+ this.emit.apply(this, ['publish', name].concat(args));
+};
+
+/**
+ * Subscribes to a channel
+ *
+ * @api private
+ */
+
+Redis.prototype.subscribe = function (name, consumer, fn) {
+ this.sub.subscribe(name);
+
+ if (consumer || fn) {
+ var self = this;
+
+ self.sub.on('subscribe', function subscribe (ch) {
+ if (name == ch) {
+ function message (ch, msg) {
+ if (name == ch) {
+ msg = self.unpack(msg);
+
+ // we check that the message consumed wasnt emitted by this node
+ if (self.nodeId != msg.nodeId) {
+ consumer.apply(null, msg.args);
+ }
+ }
+ };
+
+ self.sub.on('message', message);
+
+ self.on('unsubscribe', function unsubscribe (ch) {
+ if (name == ch) {
+ self.sub.removeListener('message', message);
+ self.removeListener('unsubscribe', unsubscribe);
+ }
+ });
+
+ self.sub.removeListener('subscribe', subscribe);
+
+ fn && fn();
+ }
+ });
+ }
+
+ this.emit('subscribe', name, consumer, fn);
+};
+
+/**
+ * Unsubscribes
+ *
+ * @api private
+ */
+
+Redis.prototype.unsubscribe = function (name, fn) {
+ this.sub.unsubscribe(name);
+
+ if (fn) {
+ var client = this.sub;
+
+ client.on('unsubscribe', function unsubscribe (ch) {
+ if (name == ch) {
+ fn();
+ client.removeListener('unsubscribe', unsubscribe);
+ }
+ });
+ }
+
+ this.emit('unsubscribe', name, fn);
+};
+
+/**
+ * Destroys the store
+ *
+ * @api public
+ */
+
+Redis.prototype.destroy = function () {
+ Store.prototype.destroy.call(this);
+
+ this.pub.end();
+ this.sub.end();
+ this.cmd.end();
+};
+
+/**
+ * Client constructor
+ *
+ * @api private
+ */
+
+function Client (store, id) {
+ Store.Client.call(this, store, id);
+};
+
+/**
+ * Inherits from Store.Client
+ */
+
+Client.prototype.__proto__ = Store.Client;
+
+/**
+ * Redis hash get
+ *
+ * @api private
+ */
+
+Client.prototype.get = function (key, fn) {
+ this.store.cmd.hget(this.id, key, fn);
+ return this;
+};
+
+/**
+ * Redis hash set
+ *
+ * @api private
+ */
+
+Client.prototype.set = function (key, value, fn) {
+ this.store.cmd.hset(this.id, key, value, fn);
+ return this;
+};
+
+/**
+ * Redis hash del
+ *
+ * @api private
+ */
+
+Client.prototype.del = function (key, fn) {
+ this.store.cmd.hdel(this.id, key, fn);
+ return this;
+};
+
+/**
+ * Redis hash has
+ *
+ * @api private
+ */
+
+Client.prototype.has = function (key, fn) {
+ this.store.cmd.hexists(this.id, key, function (err, has) {
+ if (err) return fn(err);
+ fn(null, !!has);
+ });
+ return this;
+};
+
+/**
+ * Destroys client
+ *
+ * @param {Number} number of seconds to expire data
+ * @api private
+ */
+
+Client.prototype.destroy = function (expiration) {
+ if ('number' != typeof expiration) {
+ this.store.cmd.del(this.id);
+ } else {
+ this.store.cmd.expire(this.id, expiration);
+ }
+
+ return this;
+};
diff --git a/node_modules/socket.io/lib/transport.js b/node_modules/socket.io/lib/transport.js
new file mode 100644
index 0000000..61f456f
--- /dev/null
+++ b/node_modules/socket.io/lib/transport.js
@@ -0,0 +1,534 @@
+
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module dependencies.
+ */
+
+var parser = require('./parser');
+
+/**
+ * Expose the constructor.
+ */
+
+exports = module.exports = Transport;
+
+/**
+ * Transport constructor.
+ *
+ * @api public
+ */
+
+function Transport (mng, data, req) {
+ this.manager = mng;
+ this.id = data.id;
+ this.disconnected = false;
+ this.drained = true;
+ this.handleRequest(req);
+};
+
+/**
+ * Access the logger.
+ *
+ * @api public
+ */
+
+Transport.prototype.__defineGetter__('log', function () {
+ return this.manager.log;
+});
+
+/**
+ * Access the store.
+ *
+ * @api public
+ */
+
+Transport.prototype.__defineGetter__('store', function () {
+ return this.manager.store;
+});
+
+/**
+ * Handles a request when it's set.
+ *
+ * @api private
+ */
+
+Transport.prototype.handleRequest = function (req) {
+ this.log.debug('setting request', req.method, req.url);
+ this.req = req;
+
+ if (req.method == 'GET') {
+ this.socket = req.socket;
+ this.open = true;
+ this.drained = true;
+ this.setHeartbeatInterval();
+
+ this.setHandlers();
+ this.onSocketConnect();
+ }
+};
+
+/**
+ * Called when a connection is first set.
+ *
+ * @api private
+ */
+
+Transport.prototype.onSocketConnect = function () { };
+
+/**
+ * Sets transport handlers
+ *
+ * @api private
+ */
+
+Transport.prototype.setHandlers = function () {
+ var self = this;
+
+ // we need to do this in a pub/sub way since the client can POST the message
+ // over a different socket (ie: different Transport instance)
+ this.store.subscribe('heartbeat-clear:' + this.id, function () {
+ self.onHeartbeatClear();
+ });
+
+ this.store.subscribe('disconnect-force:' + this.id, function () {
+ self.onForcedDisconnect();
+ });
+
+ this.store.subscribe('dispatch:' + this.id, function (packet, volatile) {
+ self.onDispatch(packet, volatile);
+ });
+
+ this.bound = {
+ end: this.onSocketEnd.bind(this)
+ , close: this.onSocketClose.bind(this)
+ , error: this.onSocketError.bind(this)
+ , drain: this.onSocketDrain.bind(this)
+ };
+
+ this.socket.on('end', this.bound.end);
+ this.socket.on('close', this.bound.close);
+ this.socket.on('error', this.bound.error);
+ this.socket.on('drain', this.bound.drain);
+
+ this.handlersSet = true;
+};
+
+/**
+ * Removes transport handlers
+ *
+ * @api private
+ */
+
+Transport.prototype.clearHandlers = function () {
+ if (this.handlersSet) {
+ this.store.unsubscribe('disconnect-force:' + this.id);
+ this.store.unsubscribe('heartbeat-clear:' + this.id);
+ this.store.unsubscribe('dispatch:' + this.id);
+
+ this.socket.removeListener('end', this.bound.end);
+ this.socket.removeListener('close', this.bound.close);
+ this.socket.removeListener('error', this.bound.error);
+ this.socket.removeListener('drain', this.bound.drain);
+ }
+};
+
+/**
+ * Called when the connection dies
+ *
+ * @api private
+ */
+
+Transport.prototype.onSocketEnd = function () {
+ this.end('socket end');
+};
+
+/**
+ * Called when the connection dies
+ *
+ * @api private
+ */
+
+Transport.prototype.onSocketClose = function (error) {
+ this.end(error ? 'socket error' : 'socket close');
+};
+
+/**
+ * Called when the connection has an error.
+ *
+ * @api private
+ */
+
+Transport.prototype.onSocketError = function (err) {
+ if (this.open) {
+ this.socket.destroy();
+ this.onClose();
+ }
+
+ this.log.info('socket error ' + err.stack);
+};
+
+/**
+ * Called when the connection is drained.
+ *
+ * @api private
+ */
+
+Transport.prototype.onSocketDrain = function () {
+ this.drained = true;
+};
+
+/**
+ * Called upon receiving a heartbeat packet.
+ *
+ * @api private
+ */
+
+Transport.prototype.onHeartbeatClear = function () {
+ this.clearHeartbeatTimeout();
+ this.setHeartbeatInterval();
+};
+
+/**
+ * Called upon a forced disconnection.
+ *
+ * @api private
+ */
+
+Transport.prototype.onForcedDisconnect = function () {
+ if (!this.disconnected) {
+ this.log.info('transport end by forced client disconnection');
+ if (this.open) {
+ this.packet({ type: 'disconnect' });
+ }
+ this.end('booted');
+ }
+};
+
+/**
+ * Dispatches a packet.
+ *
+ * @api private
+ */
+
+Transport.prototype.onDispatch = function (packet, volatile) {
+ if (volatile) {
+ this.writeVolatile(packet);
+ } else {
+ this.write(packet);
+ }
+};
+
+/**
+ * Sets the close timeout.
+ */
+
+Transport.prototype.setCloseTimeout = function () {
+ if (!this.closeTimeout) {
+ var self = this;
+
+ this.closeTimeout = setTimeout(function () {
+ self.log.debug('fired close timeout for client', self.id);
+ self.closeTimeout = null;
+ self.end('close timeout');
+ }, this.manager.get('close timeout') * 1000);
+
+ this.log.debug('set close timeout for client', this.id);
+ }
+};
+
+/**
+ * Clears the close timeout.
+ */
+
+Transport.prototype.clearCloseTimeout = function () {
+ if (this.closeTimeout) {
+ clearTimeout(this.closeTimeout);
+ this.closeTimeout = null;
+
+ this.log.debug('cleared close timeout for client', this.id);
+ }
+};
+
+/**
+ * Sets the heartbeat timeout
+ */
+
+Transport.prototype.setHeartbeatTimeout = function () {
+ if (!this.heartbeatTimeout && this.manager.enabled('heartbeats')) {
+ var self = this;
+
+ this.heartbeatTimeout = setTimeout(function () {
+ self.log.debug('fired heartbeat timeout for client', self.id);
+ self.heartbeatTimeout = null;
+ self.end('heartbeat timeout');
+ }, this.manager.get('heartbeat timeout') * 1000);
+
+ this.log.debug('set heartbeat timeout for client', this.id);
+ }
+};
+
+/**
+ * Clears the heartbeat timeout
+ *
+ * @param text
+ */
+
+Transport.prototype.clearHeartbeatTimeout = function () {
+ if (this.heartbeatTimeout && this.manager.enabled('heartbeats')) {
+ clearTimeout(this.heartbeatTimeout);
+ this.heartbeatTimeout = null;
+ this.log.debug('cleared heartbeat timeout for client', this.id);
+ }
+};
+
+/**
+ * Sets the heartbeat interval. To be called when a connection opens and when
+ * a heartbeat is received.
+ *
+ * @api private
+ */
+
+Transport.prototype.setHeartbeatInterval = function () {
+ if (!this.heartbeatInterval && this.manager.enabled('heartbeats')) {
+ var self = this;
+
+ this.heartbeatInterval = setTimeout(function () {
+ self.heartbeat();
+ self.heartbeatInterval = null;
+ }, this.manager.get('heartbeat interval') * 1000);
+
+ this.log.debug('set heartbeat interval for client', this.id);
+ }
+};
+
+/**
+ * Clears all timeouts.
+ *
+ * @api private
+ */
+
+Transport.prototype.clearTimeouts = function () {
+ this.clearCloseTimeout();
+ this.clearHeartbeatTimeout();
+ this.clearHeartbeatInterval();
+};
+
+/**
+ * Sends a heartbeat
+ *
+ * @api private
+ */
+
+Transport.prototype.heartbeat = function () {
+ if (this.open) {
+ this.log.debug('emitting heartbeat for client', this.id);
+ this.packet({ type: 'heartbeat' });
+ this.setHeartbeatTimeout();
+ }
+
+ return this;
+};
+
+/**
+ * Handles a message.
+ *
+ * @param {Object} packet object
+ * @api private
+ */
+
+Transport.prototype.onMessage = function (packet) {
+ var current = this.manager.transports[this.id];
+
+ if ('heartbeat' == packet.type) {
+ this.log.debug('got heartbeat packet');
+
+ if (current && current.open) {
+ current.onHeartbeatClear();
+ } else {
+ this.store.publish('heartbeat-clear:' + this.id);
+ }
+ } else {
+ if ('disconnect' == packet.type && packet.endpoint == '') {
+ this.log.debug('got disconnection packet');
+
+ if (current) {
+ current.onForcedDisconnect();
+ } else {
+ this.store.publish('disconnect-force:' + this.id);
+ }
+
+ return;
+ }
+
+ if (packet.id && packet.ack != 'data') {
+ this.log.debug('acknowledging packet automatically');
+
+ var ack = parser.encodePacket({
+ type: 'ack'
+ , ackId: packet.id
+ , endpoint: packet.endpoint || ''
+ });
+
+ if (current && current.open) {
+ current.onDispatch(ack);
+ } else {
+ this.manager.onClientDispatch(this.id, ack);
+ this.store.publish('dispatch:' + this.id, ack);
+ }
+ }
+
+ // handle packet locally or publish it
+ if (current) {
+ this.manager.onClientMessage(this.id, packet);
+ } else {
+ this.store.publish('message:' + this.id, packet);
+ }
+ }
+};
+
+/**
+ * Clears the heartbeat interval
+ *
+ * @api private
+ */
+
+Transport.prototype.clearHeartbeatInterval = function () {
+ if (this.heartbeatInterval && this.manager.enabled('heartbeats')) {
+ clearTimeout(this.heartbeatInterval);
+ this.heartbeatInterval = null;
+ this.log.debug('cleared heartbeat interval for client', this.id);
+ }
+};
+
+/**
+ * Finishes the connection and makes sure client doesn't reopen
+ *
+ * @api private
+ */
+
+Transport.prototype.disconnect = function (reason) {
+ this.packet({ type: 'disconnect' });
+ this.end(reason);
+
+ return this;
+};
+
+/**
+ * Closes the connection.
+ *
+ * @api private
+ */
+
+Transport.prototype.close = function () {
+ if (this.open) {
+ this.doClose();
+ this.onClose();
+ }
+};
+
+/**
+ * Called upon a connection close.
+ *
+ * @api private
+ */
+
+Transport.prototype.onClose = function () {
+ if (this.open) {
+ this.setCloseTimeout();
+ this.clearHandlers();
+ this.open = false;
+ this.manager.onClose(this.id);
+ this.store.publish('close', this.id);
+ }
+};
+
+/**
+ * Cleans up the connection, considers the client disconnected.
+ *
+ * @api private
+ */
+
+Transport.prototype.end = function (reason) {
+ if (!this.disconnected) {
+ this.log.info('transport end');
+
+ var local = this.manager.transports[this.id];
+
+ this.close();
+ this.clearTimeouts();
+ this.disconnected = true;
+
+ if (local) {
+ this.manager.onClientDisconnect(this.id, reason, true);
+ } else {
+ this.store.publish('disconnect:' + this.id, reason);
+ }
+ }
+};
+
+/**
+ * Signals that the transport should pause and buffer data.
+ *
+ * @api public
+ */
+
+Transport.prototype.discard = function () {
+ this.log.debug('discarding transport');
+ this.discarded = true;
+ this.clearTimeouts();
+ this.clearHandlers();
+
+ return this;
+};
+
+/**
+ * Writes an error packet with the specified reason and advice.
+ *
+ * @param {Number} advice
+ * @param {Number} reason
+ * @api public
+ */
+
+Transport.prototype.error = function (reason, advice) {
+ this.packet({
+ type: 'error'
+ , reason: reason
+ , advice: advice
+ });
+
+ this.log.warn(reason, advice ? ('client should ' + advice) : '');
+ this.end('error');
+};
+
+/**
+ * Write a packet.
+ *
+ * @api public
+ */
+
+Transport.prototype.packet = function (obj) {
+ return this.write(parser.encodePacket(obj));
+};
+
+/**
+ * Writes a volatile message.
+ *
+ * @api private
+ */
+
+Transport.prototype.writeVolatile = function (msg) {
+ if (this.open) {
+ if (this.drained) {
+ this.write(msg);
+ } else {
+ this.log.debug('ignoring volatile packet, buffer not drained');
+ }
+ } else {
+ this.log.debug('ignoring volatile packet, transport not open');
+ }
+};
diff --git a/node_modules/socket.io/lib/transports/flashsocket.js b/node_modules/socket.io/lib/transports/flashsocket.js
new file mode 100644
index 0000000..d79363d
--- /dev/null
+++ b/node_modules/socket.io/lib/transports/flashsocket.js
@@ -0,0 +1,106 @@
+
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module requirements.
+ */
+var WebSocket = require('./websocket');
+
+/**
+ * Export the constructor.
+ */
+
+exports = module.exports = FlashSocket;
+
+/**
+ * The FlashSocket transport is just a proxy
+ * for WebSocket connections.
+ *
+ * @api public
+ */
+
+function FlashSocket (mng, data, req) {
+ return WebSocket.call(this, mng, data, req);
+}
+
+/**
+ * Inherits from WebSocket.
+ */
+
+FlashSocket.prototype.__proto__ = WebSocket.prototype;
+
+/**
+ * Transport name
+ *
+ * @api public
+ */
+
+FlashSocket.prototype.name = 'flashsocket';
+
+/**
+ * Listens for new configuration changes of the Manager
+ * this way we can enable and disable the flash server.
+ *
+ * @param {Manager} Manager instance.
+ * @api private
+ */
+
+
+FlashSocket.init = function (manager) {
+ var server;
+ function create () {
+ server = require('policyfile').createServer({
+ log: function(msg){
+ manager.log.info(msg.toLowerCase());
+ }
+ }, manager.get('origins'));
+
+ server.on('close', function (e) {
+ server = null;
+ });
+
+ server.listen(manager.get('flash policy port'), manager.server);
+
+ manager.flashPolicyServer = server;
+ }
+
+ // listen for origin changes, so we can update the server
+ manager.on('set:origins', function (value, key) {
+ if (!server) return;
+
+ // update the origins and compile a new response buffer
+ server.origins = Array.isArray(value) ? value : [value];
+ server.compile();
+ });
+
+ // destory the server and create a new server
+ manager.on('set:flash policy port', function (value, key) {
+ var transports = manager.get('transports');
+ if (~transports.indexOf('flashsocket')) {
+ if (server) {
+ if (server.port === value) return;
+ // destroy the server and rebuild it on a new port
+ try {
+ server.close();
+ }
+ catch (e) { /* ignore exception. could e.g. be that the server isn't started yet */ }
+ }
+ create();
+ }
+ });
+
+ // only start the server
+ manager.on('set:transports', function (value, key){
+ if (!server && ~manager.get('transports').indexOf('flashsocket')) {
+ create();
+ }
+ });
+ // check if we need to initialize at start
+ if (~manager.get('transports').indexOf('flashsocket')){
+ create();
+ }
+};
diff --git a/node_modules/socket.io/lib/transports/htmlfile.js b/node_modules/socket.io/lib/transports/htmlfile.js
new file mode 100644
index 0000000..e8709a3
--- /dev/null
+++ b/node_modules/socket.io/lib/transports/htmlfile.js
@@ -0,0 +1,82 @@
+
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module requirements.
+ */
+
+var HTTPTransport = require('./http');
+
+/**
+ * Export the constructor.
+ */
+
+exports = module.exports = HTMLFile;
+
+/**
+ * HTMLFile transport constructor.
+ *
+ * @api public
+ */
+
+function HTMLFile (mng, data, req) {
+ HTTPTransport.call(this, mng, data, req);
+};
+
+/**
+ * Inherits from Transport.
+ */
+
+HTMLFile.prototype.__proto__ = HTTPTransport.prototype;
+
+/**
+ * Transport name
+ *
+ * @api public
+ */
+
+HTMLFile.prototype.name = 'htmlfile';
+
+/**
+ * Handles the request.
+ *
+ * @api private
+ */
+
+HTMLFile.prototype.handleRequest = function (req) {
+ HTTPTransport.prototype.handleRequest.call(this, req);
+
+ if (req.method == 'GET') {
+ req.res.writeHead(200, {
+ 'Content-Type': 'text/html; charset=UTF-8'
+ , 'Connection': 'keep-alive'
+ , 'Transfer-Encoding': 'chunked'
+ });
+
+ req.res.write(
+ '<html><body>'
+ + '<script>var _ = function (msg) { parent.s._(msg, document); };</script>'
+ + new Array(174).join(' ')
+ );
+ }
+};
+
+/**
+ * Performs the write.
+ *
+ * @api private
+ */
+
+HTMLFile.prototype.write = function (data) {
+ data = '<script>_(' + JSON.stringify(data) + ');</script>';
+
+ if (this.response.write(data)) {
+ this.drained = true;
+ }
+
+ this.log.debug(this.name + ' writing', data);
+};
diff --git a/node_modules/socket.io/lib/transports/http-polling.js b/node_modules/socket.io/lib/transports/http-polling.js
new file mode 100644
index 0000000..c71fc9c
--- /dev/null
+++ b/node_modules/socket.io/lib/transports/http-polling.js
@@ -0,0 +1,135 @@
+
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module requirements.
+ */
+
+var HTTPTransport = require('./http');
+
+/**
+ * Exports the constructor.
+ */
+
+exports = module.exports = HTTPPolling;
+
+/**
+ * HTTP polling constructor.
+ *
+ * @api public.
+ */
+
+function HTTPPolling (mng, data, req) {
+ HTTPTransport.call(this, mng, data, req);
+};
+
+/**
+ * Inherits from HTTPTransport.
+ *
+ * @api public.
+ */
+
+HTTPPolling.prototype.__proto__ = HTTPTransport.prototype;
+
+/**
+ * Transport name
+ *
+ * @api public
+ */
+
+HTTPPolling.prototype.name = 'httppolling';
+
+/**
+ * Removes heartbeat timeouts for polling.
+ */
+
+HTTPPolling.prototype.setHeartbeatInterval = function () {
+ return this;
+};
+
+/**
+ * Handles a request
+ *
+ * @api private
+ */
+
+HTTPPolling.prototype.handleRequest = function (req) {
+ HTTPTransport.prototype.handleRequest.call(this, req);
+
+ if (req.method == 'GET') {
+ var self = this;
+
+ this.pollTimeout = setTimeout(function () {
+ self.packet({ type: 'noop' });
+ self.log.debug(self.name + ' closed due to exceeded duration');
+ }, this.manager.get('polling duration') * 1000);
+
+ this.log.debug('setting poll timeout');
+ }
+};
+
+/**
+ * Clears polling timeout
+ *
+ * @api private
+ */
+
+HTTPPolling.prototype.clearPollTimeout = function () {
+ if (this.pollTimeout) {
+ clearTimeout(this.pollTimeout);
+ this.pollTimeout = null;
+ this.log.debug('clearing poll timeout');
+ }
+
+ return this;
+};
+
+/**
+ * Override clear timeouts to clear the poll timeout
+ *
+ * @api private
+ */
+
+HTTPPolling.prototype.clearTimeouts = function () {
+ HTTPTransport.prototype.clearTimeouts.call(this);
+
+ this.clearPollTimeout();
+};
+
+/**
+ * doWrite to clear poll timeout
+ *
+ * @api private
+ */
+
+HTTPPolling.prototype.doWrite = function () {
+ this.clearPollTimeout();
+};
+
+/**
+ * Performs a write.
+ *
+ * @api private.
+ */
+
+HTTPPolling.prototype.write = function (data, close) {
+ this.doWrite(data);
+ this.response.end();
+ this.onClose();
+};
+
+/**
+ * Override end.
+ *
+ * @api private
+ */
+
+HTTPPolling.prototype.end = function () {
+ this.clearPollTimeout();
+ return HTTPTransport.prototype.end.call(this);
+};
+
diff --git a/node_modules/socket.io/lib/transports/http.js b/node_modules/socket.io/lib/transports/http.js
new file mode 100644
index 0000000..5144db8
--- /dev/null
+++ b/node_modules/socket.io/lib/transports/http.js
@@ -0,0 +1,118 @@
+
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module requirements.
+ */
+
+var Transport = require('../transport')
+ , parser = require('../parser')
+ , qs = require('querystring');
+
+/**
+ * Export the constructor.
+ */
+
+exports = module.exports = HTTPTransport;
+
+/**
+ * HTTP interface constructor. For all non-websocket transports.
+ *
+ * @api public
+ */
+
+function HTTPTransport (mng, data, req) {
+ Transport.call(this, mng, data, req);
+};
+
+/**
+ * Inherits from Transport.
+ */
+
+HTTPTransport.prototype.__proto__ = Transport.prototype;
+
+/**
+ * Handles a request.
+ *
+ * @api private
+ */
+
+HTTPTransport.prototype.handleRequest = function (req) {
+ if (req.method == 'POST') {
+ var buffer = ''
+ , res = req.res
+ , origin = req.headers.origin
+ , headers = { 'Content-Length': 1, 'Content-Type': 'text/plain; charset=UTF-8' }
+ , self = this;
+
+ req.on('data', function (data) {
+ buffer += data;
+
+ if (Buffer.byteLength(buffer) >= self.manager.get('destroy buffer size')) {
+ buffer = '';
+ req.connection.destroy();
+ }
+ });
+
+ req.on('end', function () {
+ res.writeHead(200, headers);
+ res.end('1');
+
+ self.onData(self.postEncoded ? qs.parse(buffer).d : buffer);
+ });
+
+ // prevent memory leaks for uncompleted requests
+ req.on('close', function () {
+ buffer = '';
+ });
+
+ if (origin) {
+ // https://developer.mozilla.org/En/HTTP_Access_Control
+ headers['Access-Control-Allow-Origin'] = origin;
+ headers['Access-Control-Allow-Credentials'] = 'true';
+ }
+ } else {
+ this.response = req.res;
+
+ Transport.prototype.handleRequest.call(this, req);
+ }
+};
+
+/**
+ * Handles data payload.
+ *
+ * @api private
+ */
+
+HTTPTransport.prototype.onData = function (data) {
+ var messages = parser.decodePayload(data);
+ this.log.debug(this.name + ' received data packet', data);
+
+ for (var i = 0, l = messages.length; i < l; i++) {
+ this.onMessage(messages[i]);
+ }
+};
+
+/**
+ * Closes the request-response cycle
+ *
+ * @api private
+ */
+
+HTTPTransport.prototype.doClose = function () {
+ this.response.end();
+};
+
+/**
+ * Writes a payload of messages
+ *
+ * @api private
+ */
+
+HTTPTransport.prototype.payload = function (msgs) {
+ this.write(parser.encodePayload(msgs));
+};
diff --git a/node_modules/socket.io/lib/transports/index.js b/node_modules/socket.io/lib/transports/index.js
new file mode 100644
index 0000000..b865559
--- /dev/null
+++ b/node_modules/socket.io/lib/transports/index.js
@@ -0,0 +1,12 @@
+
+/**
+ * Export transports.
+ */
+
+module.exports = {
+ websocket: require('./websocket')
+ , flashsocket: require('./flashsocket')
+ , htmlfile: require('./htmlfile')
+ , 'xhr-polling': require('./xhr-polling')
+ , 'jsonp-polling': require('./jsonp-polling')
+};
diff --git a/node_modules/socket.io/lib/transports/jsonp-polling.js b/node_modules/socket.io/lib/transports/jsonp-polling.js
new file mode 100644
index 0000000..83d11b8
--- /dev/null
+++ b/node_modules/socket.io/lib/transports/jsonp-polling.js
@@ -0,0 +1,96 @@
+
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module requirements.
+ */
+
+var HTTPPolling = require('./http-polling');
+
+/**
+ * Export the constructor.
+ */
+
+exports = module.exports = JSONPPolling;
+
+/**
+ * JSON-P polling transport.
+ *
+ * @api public
+ */
+
+function JSONPPolling (mng, data, req) {
+ HTTPPolling.call(this, mng, data, req);
+
+ this.head = 'io.j[0](';
+ this.foot = ');';
+
+ if (data.query.i) {
+ this.head = 'io.j[' + data.query.i + '](';
+ }
+};
+
+/**
+ * Inherits from Transport.
+ */
+
+JSONPPolling.prototype.__proto__ = HTTPPolling.prototype;
+
+/**
+ * Transport name
+ *
+ * @api public
+ */
+
+JSONPPolling.prototype.name = 'jsonppolling';
+
+/**
+ * Make sure POST are decoded.
+ */
+
+JSONPPolling.prototype.postEncoded = true;
+
+/**
+ * Handles incoming data.
+ * Due to a bug in \n handling by browsers, we expect a JSONified string.
+ *
+ * @api private
+ */
+
+JSONPPolling.prototype.onData = function (data) {
+ try {
+ data = JSON.parse(data);
+ } catch (e) {
+ this.error('parse', 'reconnect');
+ return;
+ }
+
+ HTTPPolling.prototype.onData.call(this, data);
+};
+
+/**
+ * Performs the write.
+ *
+ * @api private
+ */
+
+JSONPPolling.prototype.doWrite = function (data) {
+ HTTPPolling.prototype.doWrite.call(this);
+
+ var data = data === undefined
+ ? '' : this.head + JSON.stringify(data) + this.foot;
+
+ this.response.writeHead(200, {
+ 'Content-Type': 'text/javascript; charset=UTF-8'
+ , 'Content-Length': Buffer.byteLength(data)
+ , 'Connection': 'Keep-Alive'
+ , 'X-XSS-Protection': '0'
+ });
+
+ this.response.write(data);
+ this.log.debug(this.name + ' writing', data);
+};
diff --git a/node_modules/socket.io/lib/transports/websocket.js b/node_modules/socket.io/lib/transports/websocket.js
new file mode 100644
index 0000000..78a4304
--- /dev/null
+++ b/node_modules/socket.io/lib/transports/websocket.js
@@ -0,0 +1,36 @@
+
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module requirements.
+ */
+
+var protocolVersions = require('./websocket/');
+
+/**
+ * Export the constructor.
+ */
+
+exports = module.exports = WebSocket;
+
+/**
+ * HTTP interface constructor. Interface compatible with all transports that
+ * depend on request-response cycles.
+ *
+ * @api public
+ */
+
+function WebSocket (mng, data, req) {
+ var transport
+ , version = req.headers['sec-websocket-version'];
+ if (typeof version !== 'undefined' && typeof protocolVersions[version] !== 'undefined') {
+ transport = new protocolVersions[version](mng, data, req);
+ }
+ else transport = new protocolVersions['default'](mng, data, req);
+ if (typeof this.name !== 'undefined') transport.name = this.name;
+ return transport;
+};
diff --git a/node_modules/socket.io/lib/transports/websocket/default.js b/node_modules/socket.io/lib/transports/websocket/default.js
new file mode 100644
index 0000000..2e861a7
--- /dev/null
+++ b/node_modules/socket.io/lib/transports/websocket/default.js
@@ -0,0 +1,360 @@
+
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module requirements.
+ */
+
+var Transport = require('../../transport')
+ , EventEmitter = process.EventEmitter
+ , crypto = require('crypto')
+ , parser = require('../../parser');
+
+/**
+ * Export the constructor.
+ */
+
+exports = module.exports = WebSocket;
+
+/**
+ * HTTP interface constructor. Interface compatible with all transports that
+ * depend on request-response cycles.
+ *
+ * @api public
+ */
+
+function WebSocket (mng, data, req) {
+ // parser
+ var self = this;
+
+ this.parser = new Parser();
+ this.parser.on('data', function (packet) {
+ self.log.debug(self.name + ' received data packet', packet);
+ self.onMessage(parser.decodePacket(packet));
+ });
+ this.parser.on('close', function () {
+ self.end();
+ });
+ this.parser.on('error', function () {
+ self.end();
+ });
+
+ Transport.call(this, mng, data, req);
+};
+
+/**
+ * Inherits from Transport.
+ */
+
+WebSocket.prototype.__proto__ = Transport.prototype;
+
+/**
+ * Transport name
+ *
+ * @api public
+ */
+
+WebSocket.prototype.name = 'websocket';
+
+/**
+ * Websocket draft version
+ *
+ * @api public
+ */
+
+WebSocket.prototype.protocolVersion = 'hixie-76';
+
+/**
+ * Called when the socket connects.
+ *
+ * @api private
+ */
+
+WebSocket.prototype.onSocketConnect = function () {
+ var self = this;
+
+ this.socket.setNoDelay(true);
+
+ this.buffer = true;
+ this.buffered = [];
+
+ if (this.req.headers.upgrade !== 'WebSocket') {
+ this.log.warn(this.name + ' connection invalid');
+ this.end();
+ return;
+ }
+
+ var origin = this.req.headers['origin']
+ , location = ((this.manager.settings['match origin protocol'] ?
+ origin.match(/^https/) : this.socket.encrypted) ?
+ 'wss' : 'ws')
+ + '://' + this.req.headers.host + this.req.url
+ , waitingForNonce = false;
+
+ if (this.req.headers['sec-websocket-key1']) {
+ // If we don't have the nonce yet, wait for it (HAProxy compatibility).
+ if (! (this.req.head && this.req.head.length >= 8)) {
+ waitingForNonce = true;
+ }
+
+ var headers = [
+ 'HTTP/1.1 101 WebSocket Protocol Handshake'
+ , 'Upgrade: WebSocket'
+ , 'Connection: Upgrade'
+ , 'Sec-WebSocket-Origin: ' + origin
+ , 'Sec-WebSocket-Location: ' + location
+ ];
+
+ if (this.req.headers['sec-websocket-protocol']){
+ headers.push('Sec-WebSocket-Protocol: '
+ + this.req.headers['sec-websocket-protocol']);
+ }
+ } else {
+ var headers = [
+ 'HTTP/1.1 101 Web Socket Protocol Handshake'
+ , 'Upgrade: WebSocket'
+ , 'Connection: Upgrade'
+ , 'WebSocket-Origin: ' + origin
+ , 'WebSocket-Location: ' + location
+ ];
+ }
+
+ try {
+ this.socket.write(headers.concat('', '').join('\r\n'));
+ this.socket.setTimeout(0);
+ this.socket.setNoDelay(true);
+ this.socket.setEncoding('utf8');
+ } catch (e) {
+ this.end();
+ return;
+ }
+
+ if (waitingForNonce) {
+ this.socket.setEncoding('binary');
+ } else if (this.proveReception(headers)) {
+ self.flush();
+ }
+
+ var headBuffer = '';
+
+ this.socket.on('data', function (data) {
+ if (waitingForNonce) {
+ headBuffer += data;
+
+ if (headBuffer.length < 8) {
+ return;
+ }
+
+ // Restore the connection to utf8 encoding after receiving the nonce
+ self.socket.setEncoding('utf8');
+ waitingForNonce = false;
+
+ // Stuff the nonce into the location where it's expected to be
+ self.req.head = headBuffer.substr(0, 8);
+ headBuffer = '';
+
+ if (self.proveReception(headers)) {
+ self.flush();
+ }
+
+ return;
+ }
+
+ self.parser.add(data);
+ });
+};
+
+/**
+ * Writes to the socket.
+ *
+ * @api private
+ */
+
+WebSocket.prototype.write = function (data) {
+ if (this.open) {
+ this.drained = false;
+
+ if (this.buffer) {
+ this.buffered.push(data);
+ return this;
+ }
+
+ var length = Buffer.byteLength(data)
+ , buffer = new Buffer(2 + length);
+
+ buffer.write('\x00', 'binary');
+ buffer.write(data, 1, 'utf8');
+ buffer.write('\xff', 1 + length, 'binary');
+
+ try {
+ if (this.socket.write(buffer)) {
+ this.drained = true;
+ }
+ } catch (e) {
+ this.end();
+ }
+
+ this.log.debug(this.name + ' writing', data);
+ }
+};
+
+/**
+ * Flushes the internal buffer
+ *
+ * @api private
+ */
+
+WebSocket.prototype.flush = function () {
+ this.buffer = false;
+
+ for (var i = 0, l = this.buffered.length; i < l; i++) {
+ this.write(this.buffered.splice(0, 1)[0]);
+ }
+};
+
+/**
+ * Finishes the handshake.
+ *
+ * @api private
+ */
+
+WebSocket.prototype.proveReception = function (headers) {
+ var self = this
+ , k1 = this.req.headers['sec-websocket-key1']
+ , k2 = this.req.headers['sec-websocket-key2'];
+
+ if (k1 && k2){
+ var 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){
+ self.log.warn('Invalid ' + self.name + ' key: "' + k + '".');
+ self.end();
+ return false;
+ }
+
+ n /= spaces;
+
+ md5.update(String.fromCharCode(
+ n >> 24 & 0xFF,
+ n >> 16 & 0xFF,
+ n >> 8 & 0xFF,
+ n & 0xFF));
+ });
+
+ md5.update(this.req.head.toString('binary'));
+
+ try {
+ this.socket.write(md5.digest('binary'), 'binary');
+ } catch (e) {
+ this.end();
+ }
+ }
+
+ return true;
+};
+
+/**
+ * Writes a payload.
+ *
+ * @api private
+ */
+
+WebSocket.prototype.payload = function (msgs) {
+ for (var i = 0, l = msgs.length; i < l; i++) {
+ this.write(msgs[i]);
+ }
+
+ return this;
+};
+
+/**
+ * Closes the connection.
+ *
+ * @api private
+ */
+
+WebSocket.prototype.doClose = function () {
+ this.socket.end();
+};
+
+/**
+ * WebSocket parser
+ *
+ * @api public
+ */
+
+function Parser () {
+ this.buffer = '';
+ this.i = 0;
+};
+
+/**
+ * Inherits from EventEmitter.
+ */
+
+Parser.prototype.__proto__ = EventEmitter.prototype;
+
+/**
+ * Adds data to the buffer.
+ *
+ * @api public
+ */
+
+Parser.prototype.add = function (data) {
+ this.buffer += data;
+ this.parse();
+};
+
+/**
+ * Parses the buffer.
+ *
+ * @api private
+ */
+
+Parser.prototype.parse = function () {
+ for (var i = this.i, chr, l = this.buffer.length; i < l; i++){
+ chr = this.buffer[i];
+
+ if (this.buffer.length == 2 && this.buffer[1] == '\u0000') {
+ this.emit('close');
+ this.buffer = '';
+ this.i = 0;
+ return;
+ }
+
+ if (i === 0){
+ if (chr != '\u0000')
+ this.error('Bad framing. Expected null byte as first frame');
+ else
+ continue;
+ }
+
+ if (chr == '\ufffd'){
+ this.emit('data', this.buffer.substr(1, i - 1));
+ this.buffer = this.buffer.substr(i + 1);
+ this.i = 0;
+ return this.parse();
+ }
+ }
+};
+
+/**
+ * Handles an error
+ *
+ * @api private
+ */
+
+Parser.prototype.error = function (reason) {
+ this.buffer = '';
+ this.i = 0;
+ this.emit('error', reason);
+ return this;
+};
diff --git a/node_modules/socket.io/lib/transports/websocket/hybi-07-12.js b/node_modules/socket.io/lib/transports/websocket/hybi-07-12.js
new file mode 100644
index 0000000..44f666a
--- /dev/null
+++ b/node_modules/socket.io/lib/transports/websocket/hybi-07-12.js
@@ -0,0 +1,622 @@
+
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module requirements.
+ */
+
+var Transport = require('../../transport')
+ , EventEmitter = process.EventEmitter
+ , crypto = require('crypto')
+ , url = require('url')
+ , parser = require('../../parser')
+ , util = require('../../util');
+
+/**
+ * Export the constructor.
+ */
+
+exports = module.exports = WebSocket;
+exports.Parser = Parser;
+
+/**
+ * HTTP interface constructor. Interface compatible with all transports that
+ * depend on request-response cycles.
+ *
+ * @api public
+ */
+
+function WebSocket (mng, data, req) {
+ // parser
+ var self = this;
+
+ this.manager = mng;
+ this.parser = new Parser();
+ this.parser.on('data', function (packet) {
+ self.onMessage(parser.decodePacket(packet));
+ });
+ this.parser.on('ping', function () {
+ // version 8 ping => pong
+ try {
+ self.socket.write('\u008a\u0000');
+ }
+ catch (e) {
+ self.end();
+ return;
+ }
+ });
+ this.parser.on('close', function () {
+ self.end();
+ });
+ this.parser.on('error', function (reason) {
+ self.log.warn(self.name + ' parser error: ' + reason);
+ self.end();
+ });
+
+ Transport.call(this, mng, data, req);
+};
+
+/**
+ * Inherits from Transport.
+ */
+
+WebSocket.prototype.__proto__ = Transport.prototype;
+
+/**
+ * Transport name
+ *
+ * @api public
+ */
+
+WebSocket.prototype.name = 'websocket';
+
+/**
+ * Websocket draft version
+ *
+ * @api public
+ */
+
+WebSocket.prototype.protocolVersion = '07-12';
+
+/**
+ * Called when the socket connects.
+ *
+ * @api private
+ */
+
+WebSocket.prototype.onSocketConnect = function () {
+ var self = this;
+
+ if (typeof this.req.headers.upgrade === 'undefined' ||
+ this.req.headers.upgrade.toLowerCase() !== 'websocket') {
+ this.log.warn(this.name + ' connection invalid');
+ this.end();
+ return;
+ }
+
+ var origin = this.req.headers['sec-websocket-origin']
+ , location = ((this.manager.settings['match origin protocol'] ?
+ origin.match(/^https/) : this.socket.encrypted) ?
+ 'wss' : 'ws')
+ + '://' + this.req.headers.host + this.req.url;
+
+ if (!this.verifyOrigin(origin)) {
+ this.log.warn(this.name + ' connection invalid: origin mismatch');
+ this.end();
+ return;
+ }
+
+ if (!this.req.headers['sec-websocket-key']) {
+ this.log.warn(this.name + ' connection invalid: received no key');
+ this.end();
+ return;
+ }
+
+ // calc key
+ var key = this.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
+ ];
+
+ try {
+ this.socket.write(headers.concat('', '').join('\r\n'));
+ this.socket.setTimeout(0);
+ this.socket.setNoDelay(true);
+ } catch (e) {
+ this.end();
+ return;
+ }
+
+ this.socket.on('data', function (data) {
+ self.parser.add(data);
+ });
+};
+
+/**
+ * Verifies the origin of a request.
+ *
+ * @api private
+ */
+
+WebSocket.prototype.verifyOrigin = function (origin) {
+ var origins = this.manager.get('origins');
+
+ if (origin === 'null') origin = '*';
+
+ if (origins.indexOf('*:*') !== -1) {
+ return true;
+ }
+
+ if (origin) {
+ try {
+ var parts = url.parse(origin);
+ parts.port = parts.port || 80;
+ var ok =
+ ~origins.indexOf(parts.hostname + ':' + parts.port) ||
+ ~origins.indexOf(parts.hostname + ':*') ||
+ ~origins.indexOf('*:' + parts.port);
+ if (!ok) this.log.warn('illegal origin: ' + origin);
+ return ok;
+ } catch (ex) {
+ this.log.warn('error parsing origin');
+ }
+ }
+ else {
+ this.log.warn('origin missing from websocket call, yet required by config');
+ }
+ return false;
+};
+
+/**
+ * Writes to the socket.
+ *
+ * @api private
+ */
+
+WebSocket.prototype.write = function (data) {
+ if (this.open) {
+ var buf = this.frame(0x81, data);
+ try {
+ this.socket.write(buf, 'binary');
+ }
+ catch (e) {
+ this.end();
+ return;
+ }
+ this.log.debug(this.name + ' writing', data);
+ }
+};
+
+/**
+ * Writes a payload.
+ *
+ * @api private
+ */
+
+WebSocket.prototype.payload = function (msgs) {
+ for (var i = 0, l = msgs.length; i < l; i++) {
+ this.write(msgs[i]);
+ }
+
+ return this;
+};
+
+/**
+ * Frame server-to-client output as a text packet.
+ *
+ * @api private
+ */
+
+WebSocket.prototype.frame = function (opcode, str) {
+ var dataBuffer = new Buffer(str)
+ , dataLength = dataBuffer.length
+ , startOffset = 2
+ , secondByte = dataLength;
+ if (dataLength > 65536) {
+ startOffset = 10;
+ secondByte = 127;
+ }
+ else if (dataLength > 125) {
+ startOffset = 4;
+ secondByte = 126;
+ }
+ var outputBuffer = new Buffer(dataLength + startOffset);
+ outputBuffer[0] = opcode;
+ outputBuffer[1] = secondByte;
+ dataBuffer.copy(outputBuffer, startOffset);
+ switch (secondByte) {
+ case 126:
+ outputBuffer[2] = dataLength >>> 8;
+ outputBuffer[3] = dataLength % 256;
+ break;
+ case 127:
+ var l = dataLength;
+ for (var i = 1; i <= 8; ++i) {
+ outputBuffer[startOffset - i] = l & 0xff;
+ l >>>= 8;
+ }
+ }
+ return outputBuffer;
+};
+
+/**
+ * Closes the connection.
+ *
+ * @api private
+ */
+
+WebSocket.prototype.doClose = function () {
+ this.socket.end();
+};
+
+/**
+ * WebSocket parser
+ *
+ * @api public
+ */
+
+function Parser () {
+ this.state = {
+ activeFragmentedOperation: null,
+ lastFragment: false,
+ masked: false,
+ opcode: 0
+ };
+ this.overflow = null;
+ this.expectOffset = 0;
+ this.expectBuffer = null;
+ this.expectHandler = null;
+ this.currentMessage = '';
+
+ var self = this;
+ this.opcodeHandlers = {
+ // text
+ '1': function(data) {
+ var finish = function(mask, data) {
+ self.currentMessage += self.unmask(mask, data);
+ if (self.state.lastFragment) {
+ self.emit('data', self.currentMessage);
+ self.currentMessage = '';
+ }
+ self.endPacket();
+ }
+
+ var expectData = function(length) {
+ if (self.state.masked) {
+ self.expect('Mask', 4, function(data) {
+ var mask = data;
+ self.expect('Data', length, function(data) {
+ finish(mask, data);
+ });
+ });
+ }
+ else {
+ self.expect('Data', length, function(data) {
+ finish(null, data);
+ });
+ }
+ }
+
+ // decode length
+ var firstLength = data[1] & 0x7f;
+ if (firstLength < 126) {
+ expectData(firstLength);
+ }
+ else if (firstLength == 126) {
+ self.expect('Length', 2, function(data) {
+ expectData(util.unpack(data));
+ });
+ }
+ else if (firstLength == 127) {
+ self.expect('Length', 8, function(data) {
+ if (util.unpack(data.slice(0, 4)) != 0) {
+ self.error('packets with length spanning more than 32 bit is currently not supported');
+ return;
+ }
+ var lengthBytes = data.slice(4); // note: cap to 32 bit length
+ expectData(util.unpack(data));
+ });
+ }
+ },
+ // binary
+ '2': function(data) {
+ var finish = function(mask, data) {
+ if (typeof self.currentMessage == 'string') self.currentMessage = []; // build a buffer list
+ self.currentMessage.push(self.unmask(mask, data, true));
+ if (self.state.lastFragment) {
+ self.emit('binary', self.concatBuffers(self.currentMessage));
+ self.currentMessage = '';
+ }
+ self.endPacket();
+ }
+
+ var expectData = function(length) {
+ if (self.state.masked) {
+ self.expect('Mask', 4, function(data) {
+ var mask = data;
+ self.expect('Data', length, function(data) {
+ finish(mask, data);
+ });
+ });
+ }
+ else {
+ self.expect('Data', length, function(data) {
+ finish(null, data);
+ });
+ }
+ }
+
+ // decode length
+ var firstLength = data[1] & 0x7f;
+ if (firstLength < 126) {
+ expectData(firstLength);
+ }
+ else if (firstLength == 126) {
+ self.expect('Length', 2, function(data) {
+ expectData(util.unpack(data));
+ });
+ }
+ else if (firstLength == 127) {
+ self.expect('Length', 8, function(data) {
+ if (util.unpack(data.slice(0, 4)) != 0) {
+ self.error('packets with length spanning more than 32 bit is currently not supported');
+ return;
+ }
+ var lengthBytes = data.slice(4); // note: cap to 32 bit length
+ expectData(util.unpack(data));
+ });
+ }
+ },
+ // close
+ '8': function(data) {
+ self.emit('close');
+ self.reset();
+ },
+ // ping
+ '9': function(data) {
+ if (self.state.lastFragment == false) {
+ self.error('fragmented ping is not supported');
+ return;
+ }
+
+ var finish = function(mask, data) {
+ self.emit('ping', self.unmask(mask, data));
+ self.endPacket();
+ }
+
+ var expectData = function(length) {
+ if (self.state.masked) {
+ self.expect('Mask', 4, function(data) {
+ var mask = data;
+ self.expect('Data', length, function(data) {
+ finish(mask, data);
+ });
+ });
+ }
+ else {
+ self.expect('Data', length, function(data) {
+ finish(null, data);
+ });
+ }
+ }
+
+ // decode length
+ var firstLength = data[1] & 0x7f;
+ if (firstLength == 0) {
+ finish(null, null);
+ }
+ else if (firstLength < 126) {
+ expectData(firstLength);
+ }
+ else if (firstLength == 126) {
+ self.expect('Length', 2, function(data) {
+ expectData(util.unpack(data));
+ });
+ }
+ else if (firstLength == 127) {
+ self.expect('Length', 8, function(data) {
+ expectData(util.unpack(data));
+ });
+ }
+ }
+ }
+
+ this.expect('Opcode', 2, this.processPacket);
+};
+
+/**
+ * Inherits from EventEmitter.
+ */
+
+Parser.prototype.__proto__ = EventEmitter.prototype;
+
+/**
+ * Add new data to the parser.
+ *
+ * @api public
+ */
+
+Parser.prototype.add = function(data) {
+ if (this.expectBuffer == null) {
+ this.addToOverflow(data);
+ return;
+ }
+ var toRead = Math.min(data.length, this.expectBuffer.length - this.expectOffset);
+ data.copy(this.expectBuffer, this.expectOffset, 0, toRead);
+ this.expectOffset += toRead;
+ if (toRead < data.length) {
+ // at this point the overflow buffer shouldn't at all exist
+ this.overflow = new Buffer(data.length - toRead);
+ data.copy(this.overflow, 0, toRead, toRead + this.overflow.length);
+ }
+ if (this.expectOffset == this.expectBuffer.length) {
+ var bufferForHandler = this.expectBuffer;
+ this.expectBuffer = null;
+ this.expectOffset = 0;
+ this.expectHandler.call(this, bufferForHandler);
+ }
+}
+
+/**
+ * Adds a piece of data to the overflow.
+ *
+ * @api private
+ */
+
+Parser.prototype.addToOverflow = function(data) {
+ if (this.overflow == null) this.overflow = data;
+ else {
+ var prevOverflow = this.overflow;
+ this.overflow = new Buffer(this.overflow.length + data.length);
+ prevOverflow.copy(this.overflow, 0);
+ data.copy(this.overflow, prevOverflow.length);
+ }
+}
+
+/**
+ * Waits for a certain amount of bytes to be available, then fires a callback.
+ *
+ * @api private
+ */
+
+Parser.prototype.expect = function(what, length, handler) {
+ this.expectBuffer = new Buffer(length);
+ this.expectOffset = 0;
+ this.expectHandler = handler;
+ if (this.overflow != null) {
+ var toOverflow = this.overflow;
+ this.overflow = null;
+ this.add(toOverflow);
+ }
+}
+
+/**
+ * Start processing a new packet.
+ *
+ * @api private
+ */
+
+Parser.prototype.processPacket = function (data) {
+ if ((data[0] & 0x70) != 0) {
+ this.error('reserved fields must be empty');
+ }
+ 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.opcode = this.state.activeFragmentedOperation;
+ if (!(this.state.opcode == 1 || this.state.opcode == 2)) {
+ this.error('continuation frame cannot follow current opcode')
+ return;
+ }
+ }
+ else {
+ this.state.opcode = opcode;
+ if (this.state.lastFragment === false) {
+ this.state.activeFragmentedOperation = opcode;
+ }
+ }
+ var handler = this.opcodeHandlers[this.state.opcode];
+ if (typeof handler == 'undefined') this.error('no handler for opcode ' + this.state.opcode);
+ else handler(data);
+}
+
+/**
+ * Endprocessing a packet.
+ *
+ * @api private
+ */
+
+Parser.prototype.endPacket = function() {
+ 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.expect('Opcode', 2, this.processPacket);
+}
+
+/**
+ * Reset the parser state.
+ *
+ * @api private
+ */
+
+Parser.prototype.reset = function() {
+ this.state = {
+ activeFragmentedOperation: null,
+ lastFragment: false,
+ masked: false,
+ opcode: 0
+ };
+ this.expectOffset = 0;
+ this.expectBuffer = null;
+ this.expectHandler = null;
+ this.overflow = null;
+ this.currentMessage = '';
+}
+
+/**
+ * Unmask received data.
+ *
+ * @api private
+ */
+
+Parser.prototype.unmask = function (mask, buf, binary) {
+ if (mask != null) {
+ for (var i = 0, ll = buf.length; i < ll; i++) {
+ buf[i] ^= mask[i % 4];
+ }
+ }
+ if (binary) return buf;
+ return buf != null ? buf.toString('utf8') : '';
+}
+
+/**
+ * Concatenates a list of buffers.
+ *
+ * @api private
+ */
+
+Parser.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);
+ var offset = 0;
+ for (var i = 0, l = buffers.length; i < l; ++i) {
+ buffers[i].copy(mergedBuffer, offset);
+ offset += buffers[i].length;
+ }
+ return mergedBuffer;
+}
+
+/**
+ * Handles an error
+ *
+ * @api private
+ */
+
+Parser.prototype.error = function (reason) {
+ this.reset();
+ this.emit('error', reason);
+ return this;
+};
diff --git a/node_modules/socket.io/lib/transports/websocket/hybi-16.js b/node_modules/socket.io/lib/transports/websocket/hybi-16.js
new file mode 100644
index 0000000..d84e10c
--- /dev/null
+++ b/node_modules/socket.io/lib/transports/websocket/hybi-16.js
@@ -0,0 +1,623 @@
+
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module requirements.
+ */
+
+var Transport = require('../../transport')
+ , EventEmitter = process.EventEmitter
+ , crypto = require('crypto')
+ , url = require('url')
+ , parser = require('../../parser')
+ , util = require('../../util');
+
+/**
+ * Export the constructor.
+ */
+
+exports = module.exports = WebSocket;
+exports.Parser = Parser;
+
+/**
+ * HTTP interface constructor. Interface compatible with all transports that
+ * depend on request-response cycles.
+ *
+ * @api public
+ */
+
+function WebSocket (mng, data, req) {
+ // parser
+ var self = this;
+
+ this.manager = mng;
+ this.parser = new Parser();
+ this.parser.on('data', function (packet) {
+ self.onMessage(parser.decodePacket(packet));
+ });
+ this.parser.on('ping', function () {
+ // version 8 ping => pong
+ try {
+ self.socket.write('\u008a\u0000');
+ }
+ catch (e) {
+ self.end();
+ return;
+ }
+ });
+ this.parser.on('close', function () {
+ self.end();
+ });
+ this.parser.on('error', function (reason) {
+ self.log.warn(self.name + ' parser error: ' + reason);
+ self.end();
+ });
+
+ Transport.call(this, mng, data, req);
+};
+
+/**
+ * Inherits from Transport.
+ */
+
+WebSocket.prototype.__proto__ = Transport.prototype;
+
+/**
+ * Transport name
+ *
+ * @api public
+ */
+
+WebSocket.prototype.name = 'websocket';
+
+/**
+ * Websocket draft version
+ *
+ * @api public
+ */
+
+WebSocket.prototype.protocolVersion = '16';
+
+/**
+ * Called when the socket connects.
+ *
+ * @api private
+ */
+
+WebSocket.prototype.onSocketConnect = function () {
+ var self = this;
+
+ if (typeof this.req.headers.upgrade === 'undefined' ||
+ this.req.headers.upgrade.toLowerCase() !== 'websocket') {
+ this.log.warn(this.name + ' connection invalid');
+ this.end();
+ return;
+ }
+
+ var origin = this.req.headers['origin']
+ , location = ((this.manager.settings['match origin protocol'] ?
+ origin.match(/^https/) : this.socket.encrypted) ?
+ 'wss' : 'ws')
+ + '://' + this.req.headers.host + this.req.url;
+
+ if (!this.verifyOrigin(origin)) {
+ this.log.warn(this.name + ' connection invalid: origin mismatch');
+ this.end();
+ return;
+ }
+
+ if (!this.req.headers['sec-websocket-key']) {
+ this.log.warn(this.name + ' connection invalid: received no key');
+ this.end();
+ return;
+ }
+
+ // calc key
+ var key = this.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
+ ];
+
+ try {
+ this.socket.write(headers.concat('', '').join('\r\n'));
+ this.socket.setTimeout(0);
+ this.socket.setNoDelay(true);
+ } catch (e) {
+ this.end();
+ return;
+ }
+
+ this.socket.on('data', function (data) {
+ self.parser.add(data);
+ });
+};
+
+/**
+ * Verifies the origin of a request.
+ *
+ * @api private
+ */
+
+WebSocket.prototype.verifyOrigin = function (origin) {
+ var origins = this.manager.get('origins');
+
+ if (origin === 'null') origin = '*';
+
+ if (origins.indexOf('*:*') !== -1) {
+ return true;
+ }
+
+ if (origin) {
+ try {
+ var parts = url.parse(origin);
+ parts.port = parts.port || 80;
+ var ok =
+ ~origins.indexOf(parts.hostname + ':' + parts.port) ||
+ ~origins.indexOf(parts.hostname + ':*') ||
+ ~origins.indexOf('*:' + parts.port);
+ if (!ok) this.log.warn('illegal origin: ' + origin);
+ return ok;
+ } catch (ex) {
+ this.log.warn('error parsing origin');
+ }
+ }
+ else {
+ this.log.warn('origin missing from websocket call, yet required by config');
+ }
+ return false;
+};
+
+/**
+ * Writes to the socket.
+ *
+ * @api private
+ */
+
+WebSocket.prototype.write = function (data) {
+ if (this.open) {
+ var buf = this.frame(0x81, data);
+ try {
+ this.socket.write(buf, 'binary');
+ }
+ catch (e) {
+ this.end();
+ return;
+ }
+ this.log.debug(this.name + ' writing', data);
+ }
+};
+
+/**
+ * Writes a payload.
+ *
+ * @api private
+ */
+
+WebSocket.prototype.payload = function (msgs) {
+ for (var i = 0, l = msgs.length; i < l; i++) {
+ this.write(msgs[i]);
+ }
+
+ return this;
+};
+
+/**
+ * Frame server-to-client output as a text packet.
+ *
+ * @api private
+ */
+
+WebSocket.prototype.frame = function (opcode, str) {
+ var dataBuffer = new Buffer(str)
+ , dataLength = dataBuffer.length
+ , startOffset = 2
+ , secondByte = dataLength;
+ if (dataLength > 65536) {
+ startOffset = 10;
+ secondByte = 127;
+ }
+ else if (dataLength > 125) {
+ startOffset = 4;
+ secondByte = 126;
+ }
+ var outputBuffer = new Buffer(dataLength + startOffset);
+ outputBuffer[0] = opcode;
+ outputBuffer[1] = secondByte;
+ dataBuffer.copy(outputBuffer, startOffset);
+ switch (secondByte) {
+ case 126:
+ outputBuffer[2] = dataLength >>> 8;
+ outputBuffer[3] = dataLength % 256;
+ break;
+ case 127:
+ var l = dataLength;
+ for (var i = 1; i <= 8; ++i) {
+ outputBuffer[startOffset - i] = l & 0xff;
+ l >>>= 8;
+ }
+ }
+ return outputBuffer;
+};
+
+/**
+ * Closes the connection.
+ *
+ * @api private
+ */
+
+WebSocket.prototype.doClose = function () {
+ this.socket.end();
+};
+
+/**
+ * WebSocket parser
+ *
+ * @api public
+ */
+
+function Parser () {
+ this.state = {
+ activeFragmentedOperation: null,
+ lastFragment: false,
+ masked: false,
+ opcode: 0
+ };
+ this.overflow = null;
+ this.expectOffset = 0;
+ this.expectBuffer = null;
+ this.expectHandler = null;
+ this.currentMessage = '';
+
+ var self = this;
+ this.opcodeHandlers = {
+ // text
+ '1': function(data) {
+ var finish = function(mask, data) {
+ self.currentMessage += self.unmask(mask, data);
+ if (self.state.lastFragment) {
+ self.emit('data', self.currentMessage);
+ self.currentMessage = '';
+ }
+ self.endPacket();
+ }
+
+ var expectData = function(length) {
+ if (self.state.masked) {
+ self.expect('Mask', 4, function(data) {
+ var mask = data;
+ self.expect('Data', length, function(data) {
+ finish(mask, data);
+ });
+ });
+ }
+ else {
+ self.expect('Data', length, function(data) {
+ finish(null, data);
+ });
+ }
+ }
+
+ // decode length
+ var firstLength = data[1] & 0x7f;
+ if (firstLength < 126) {
+ expectData(firstLength);
+ }
+ else if (firstLength == 126) {
+ self.expect('Length', 2, function(data) {
+ expectData(util.unpack(data));
+ });
+ }
+ else if (firstLength == 127) {
+ self.expect('Length', 8, function(data) {
+ if (util.unpack(data.slice(0, 4)) != 0) {
+ self.error('packets with length spanning more than 32 bit is currently not supported');
+ return;
+ }
+ var lengthBytes = data.slice(4); // note: cap to 32 bit length
+ expectData(util.unpack(data));
+ });
+ }
+ },
+ // binary
+ '2': function(data) {
+ var finish = function(mask, data) {
+ if (typeof self.currentMessage == 'string') self.currentMessage = []; // build a buffer list
+ self.currentMessage.push(self.unmask(mask, data, true));
+ if (self.state.lastFragment) {
+ self.emit('binary', self.concatBuffers(self.currentMessage));
+ self.currentMessage = '';
+ }
+ self.endPacket();
+ }
+
+ var expectData = function(length) {
+ if (self.state.masked) {
+ self.expect('Mask', 4, function(data) {
+ var mask = data;
+ self.expect('Data', length, function(data) {
+ finish(mask, data);
+ });
+ });
+ }
+ else {
+ self.expect('Data', length, function(data) {
+ finish(null, data);
+ });
+ }
+ }
+
+ // decode length
+ var firstLength = data[1] & 0x7f;
+ if (firstLength < 126) {
+ expectData(firstLength);
+ }
+ else if (firstLength == 126) {
+ self.expect('Length', 2, function(data) {
+ expectData(util.unpack(data));
+ });
+ }
+ else if (firstLength == 127) {
+ self.expect('Length', 8, function(data) {
+ if (util.unpack(data.slice(0, 4)) != 0) {
+ self.error('packets with length spanning more than 32 bit is currently not supported');
+ return;
+ }
+ var lengthBytes = data.slice(4); // note: cap to 32 bit length
+ expectData(util.unpack(data));
+ });
+ }
+ },
+ // close
+ '8': function(data) {
+ self.emit('close');
+ self.reset();
+ },
+ // ping
+ '9': function(data) {
+ if (self.state.lastFragment == false) {
+ self.error('fragmented ping is not supported');
+ return;
+ }
+
+ var finish = function(mask, data) {
+ self.emit('ping', self.unmask(mask, data));
+ self.endPacket();
+ }
+
+ var expectData = function(length) {
+ if (self.state.masked) {
+ self.expect('Mask', 4, function(data) {
+ var mask = data;
+ self.expect('Data', length, function(data) {
+ finish(mask, data);
+ });
+ });
+ }
+ else {
+ self.expect('Data', length, function(data) {
+ finish(null, data);
+ });
+ }
+ }
+
+ // decode length
+ var firstLength = data[1] & 0x7f;
+ if (firstLength == 0) {
+ finish(null, null);
+ }
+ else if (firstLength < 126) {
+ expectData(firstLength);
+ }
+ else if (firstLength == 126) {
+ self.expect('Length', 2, function(data) {
+ expectData(util.unpack(data));
+ });
+ }
+ else if (firstLength == 127) {
+ self.expect('Length', 8, function(data) {
+ expectData(util.unpack(data));
+ });
+ }
+ }
+ }
+
+ this.expect('Opcode', 2, this.processPacket);
+};
+
+/**
+ * Inherits from EventEmitter.
+ */
+
+Parser.prototype.__proto__ = EventEmitter.prototype;
+
+/**
+ * Add new data to the parser.
+ *
+ * @api public
+ */
+
+Parser.prototype.add = function(data) {
+ if (this.expectBuffer == null) {
+ this.addToOverflow(data);
+ return;
+ }
+ var toRead = Math.min(data.length, this.expectBuffer.length - this.expectOffset);
+ data.copy(this.expectBuffer, this.expectOffset, 0, toRead);
+ this.expectOffset += toRead;
+ if (toRead < data.length) {
+ // at this point the overflow buffer shouldn't at all exist
+ this.overflow = new Buffer(data.length - toRead);
+ data.copy(this.overflow, 0, toRead, toRead + this.overflow.length);
+ }
+ if (this.expectOffset == this.expectBuffer.length) {
+ var bufferForHandler = this.expectBuffer;
+ this.expectBuffer = null;
+ this.expectOffset = 0;
+ this.expectHandler.call(this, bufferForHandler);
+ }
+}
+
+/**
+ * Adds a piece of data to the overflow.
+ *
+ * @api private
+ */
+
+Parser.prototype.addToOverflow = function(data) {
+ if (this.overflow == null) this.overflow = data;
+ else {
+ var prevOverflow = this.overflow;
+ this.overflow = new Buffer(this.overflow.length + data.length);
+ prevOverflow.copy(this.overflow, 0);
+ data.copy(this.overflow, prevOverflow.length);
+ }
+}
+
+/**
+ * Waits for a certain amount of bytes to be available, then fires a callback.
+ *
+ * @api private
+ */
+
+Parser.prototype.expect = function(what, length, handler) {
+ this.expectBuffer = new Buffer(length);
+ this.expectOffset = 0;
+ this.expectHandler = handler;
+ if (this.overflow != null) {
+ var toOverflow = this.overflow;
+ this.overflow = null;
+ this.add(toOverflow);
+ }
+}
+
+/**
+ * Start processing a new packet.
+ *
+ * @api private
+ */
+
+Parser.prototype.processPacket = function (data) {
+ if ((data[0] & 0x70) != 0) {
+ this.error('reserved fields must be empty');
+ 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.opcode = this.state.activeFragmentedOperation;
+ if (!(this.state.opcode == 1 || this.state.opcode == 2)) {
+ this.error('continuation frame cannot follow current opcode')
+ return;
+ }
+ }
+ else {
+ this.state.opcode = opcode;
+ if (this.state.lastFragment === false) {
+ this.state.activeFragmentedOperation = opcode;
+ }
+ }
+ var handler = this.opcodeHandlers[this.state.opcode];
+ if (typeof handler == 'undefined') this.error('no handler for opcode ' + this.state.opcode);
+ else handler(data);
+}
+
+/**
+ * Endprocessing a packet.
+ *
+ * @api private
+ */
+
+Parser.prototype.endPacket = function() {
+ 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.expect('Opcode', 2, this.processPacket);
+}
+
+/**
+ * Reset the parser state.
+ *
+ * @api private
+ */
+
+Parser.prototype.reset = function() {
+ this.state = {
+ activeFragmentedOperation: null,
+ lastFragment: false,
+ masked: false,
+ opcode: 0
+ };
+ this.expectOffset = 0;
+ this.expectBuffer = null;
+ this.expectHandler = null;
+ this.overflow = null;
+ this.currentMessage = '';
+}
+
+/**
+ * Unmask received data.
+ *
+ * @api private
+ */
+
+Parser.prototype.unmask = function (mask, buf, binary) {
+ if (mask != null) {
+ for (var i = 0, ll = buf.length; i < ll; i++) {
+ buf[i] ^= mask[i % 4];
+ }
+ }
+ if (binary) return buf;
+ return buf != null ? buf.toString('utf8') : '';
+}
+
+/**
+ * Concatenates a list of buffers.
+ *
+ * @api private
+ */
+
+Parser.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);
+ var offset = 0;
+ for (var i = 0, l = buffers.length; i < l; ++i) {
+ buffers[i].copy(mergedBuffer, offset);
+ offset += buffers[i].length;
+ }
+ return mergedBuffer;
+}
+
+/**
+ * Handles an error
+ *
+ * @api private
+ */
+
+Parser.prototype.error = function (reason) {
+ this.reset();
+ this.emit('error', reason);
+ return this;
+};
diff --git a/node_modules/socket.io/lib/transports/websocket/index.js b/node_modules/socket.io/lib/transports/websocket/index.js
new file mode 100644
index 0000000..3a952b7
--- /dev/null
+++ b/node_modules/socket.io/lib/transports/websocket/index.js
@@ -0,0 +1,11 @@
+
+/**
+ * Export websocket versions.
+ */
+
+module.exports = {
+ 7: require('./hybi-07-12'),
+ 8: require('./hybi-07-12'),
+ 13: require('./hybi-16'),
+ default: require('./default')
+};
diff --git a/node_modules/socket.io/lib/transports/xhr-polling.js b/node_modules/socket.io/lib/transports/xhr-polling.js
new file mode 100644
index 0000000..1db5aee
--- /dev/null
+++ b/node_modules/socket.io/lib/transports/xhr-polling.js
@@ -0,0 +1,69 @@
+
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module requirements.
+ */
+
+var HTTPPolling = require('./http-polling');
+
+/**
+ * Export the constructor.
+ */
+
+exports = module.exports = XHRPolling;
+
+/**
+ * Ajax polling transport.
+ *
+ * @api public
+ */
+
+function XHRPolling (mng, data, req) {
+ HTTPPolling.call(this, mng, data, req);
+};
+
+/**
+ * Inherits from Transport.
+ */
+
+XHRPolling.prototype.__proto__ = HTTPPolling.prototype;
+
+/**
+ * Transport name
+ *
+ * @api public
+ */
+
+XHRPolling.prototype.name = 'xhr-polling';
+
+/**
+ * Frames data prior to write.
+ *
+ * @api private
+ */
+
+XHRPolling.prototype.doWrite = function (data) {
+ HTTPPolling.prototype.doWrite.call(this);
+
+ var origin = this.req.headers.origin
+ , headers = {
+ 'Content-Type': 'text/plain; charset=UTF-8'
+ , 'Content-Length': data === undefined ? 0 : Buffer.byteLength(data)
+ , 'Connection': 'Keep-Alive'
+ };
+
+ if (origin) {
+ // https://developer.mozilla.org/En/HTTP_Access_Control
+ headers['Access-Control-Allow-Origin'] = origin;
+ headers['Access-Control-Allow-Credentials'] = 'true';
+ }
+
+ this.response.writeHead(200, headers);
+ this.response.write(data);
+ this.log.debug(this.name + ' writing', data);
+};
diff --git a/node_modules/socket.io/lib/util.js b/node_modules/socket.io/lib/util.js
new file mode 100644
index 0000000..f7d9f2b
--- /dev/null
+++ b/node_modules/socket.io/lib/util.js
@@ -0,0 +1,50 @@
+
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module dependencies.
+ */
+
+/**
+ * Converts an enumerable to an array.
+ *
+ * @api public
+ */
+
+exports.toArray = function (enu) {
+ var arr = [];
+
+ for (var i = 0, l = enu.length; i < l; i++)
+ arr.push(enu[i]);
+
+ return arr;
+};
+
+/**
+ * Unpacks a buffer to a number.
+ *
+ * @api public
+ */
+
+exports.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;
+}
+
+/**
+ * Left pads a string.
+ *
+ * @api public
+ */
+
+exports.padl = function (s,n,c) {
+ return new Array(1 + n - s.length).join(c) + s;
+}
+
diff --git a/node_modules/socket.io/package.json b/node_modules/socket.io/package.json
new file mode 100644
index 0000000..13e7c6e
--- /dev/null
+++ b/node_modules/socket.io/package.json
@@ -0,0 +1,69 @@
+{
+ "name": "socket.io",
+ "version": "0.9.4",
+ "description": "Real-time apps made cross-browser & easy with a WebSocket-like API",
+ "homepage": "http://socket.io",
+ "keywords": [
+ "websocket",
+ "socket",
+ "realtime",
+ "socket.io",
+ "comet",
+ "ajax"
+ ],
+ "author": {
+ "name": "Guillermo Rauch",
+ "email": "guillermo@learnboost.com"
+ },
+ "contributors": [
+ {
+ "name": "Guillermo Rauch",
+ "email": "rauchg@gmail.com"
+ },
+ {
+ "name": "Arnout Kazemier",
+ "email": "info@3rd-eden.com"
+ },
+ {
+ "name": "Vladimir Dronnikov",
+ "email": "dronnikov@gmail.com"
+ },
+ {
+ "name": "Einar Otto Stangvik",
+ "email": "einaros@gmail.com"
+ }
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/LearnBoost/socket.io.git"
+ },
+ "dependencies": {
+ "socket.io-client": "0.9.4",
+ "policyfile": "0.0.4",
+ "redis": "0.6.7"
+ },
+ "devDependencies": {
+ "expresso": "0.9.2",
+ "should": "0.0.4",
+ "benchmark": "0.2.2",
+ "microtime": "0.1.3-1",
+ "colors": "0.5.1"
+ },
+ "main": "index",
+ "engines": {
+ "node": ">= 0.4.0"
+ },
+ "scripts": {
+ "test": "make test"
+ },
+ "_id": "socket.io@0.9.4",
+ "optionalDependencies": {},
+ "_engineSupported": true,
+ "_npmVersion": "1.1.12",
+ "_nodeVersion": "v0.6.14",
+ "_defaultsLoaded": true,
+ "dist": {
+ "shasum": "ad1649320fe4b1f98ff3d24e74d50ac27bc6ac24"
+ },
+ "_from": "socket.io@0.9.4"
+}
diff --git a/node_modules/socket.io/support/node-websocket-client/LICENSE b/node_modules/socket.io/support/node-websocket-client/LICENSE
new file mode 100644
index 0000000..f3c2eae
--- /dev/null
+++ b/node_modules/socket.io/support/node-websocket-client/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2010, Peter Griess <pg@std.in>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of node-websocket-client nor the names of its
+ contributors may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/node_modules/socket.io/support/node-websocket-client/Makefile b/node_modules/socket.io/support/node-websocket-client/Makefile
new file mode 100644
index 0000000..e7c849a
--- /dev/null
+++ b/node_modules/socket.io/support/node-websocket-client/Makefile
@@ -0,0 +1,22 @@
+# This makefile exists to help run tests.
+#
+# If TEST_UNIX is a non-empty value, runs tests for UNIX sockets. This
+# functionality is not in node-websocket-server at the moment.
+
+.PHONY: test
+
+all: test test-unix
+
+test:
+ for f in `ls -1 test/test-*.js | grep -v unix` ; do \
+ echo $$f ; \
+ node $$f ; \
+ done
+
+test-unix:
+ if [[ -n "$$TEST_UNIX" ]] ; then \
+ for f in `ls -1 test/test-*.js | grep unix` ; do \
+ echo $$f ; \
+ node $$f ; \
+ done \
+ fi
diff --git a/node_modules/socket.io/support/node-websocket-client/README.md b/node_modules/socket.io/support/node-websocket-client/README.md
new file mode 100644
index 0000000..8823a5c
--- /dev/null
+++ b/node_modules/socket.io/support/node-websocket-client/README.md
@@ -0,0 +1,41 @@
+A prototype [Web Socket](http://www.whatwg.org/specs/web-socket-protocol/)
+client implementation for [node.js](http://nodejs.org).
+
+Tested with
+[miksago/node-websocket-server](http://github.com/miksago/node-websocket-server)
+v1.2.00.
+
+Requires [nodejs](http://nodejs.org) 0.1.98 or later.
+
+## Installation
+
+Install this using `npm` as follows
+
+ npm install websocket-client
+
+... or just dump `lib/websocket.js` in your `$NODE_PATH`.
+
+## Usage
+
+ var sys = require('sys');
+ var WebSocket = require('websocket').WebSocket;
+
+ var ws = new WebSocket('ws://localhost:8000/biff', 'borf');
+ ws.addListener('data', function(buf) {
+ sys.debug('Got data: ' + sys.inspect(buf));
+ });
+ ws.onmessage = function(m) {
+ sys.debug('Got message: ' + m);
+ }
+
+## API
+
+This supports the `send()` and `onmessage()` APIs. The `WebSocket` object will
+also emit `data` events that are node `Buffer` objects, in case you want to
+work with something lower-level than strings.
+
+## Transports
+
+Multiple transports are supported, indicated by the scheme provided to the
+`WebSocket` constructor. `ws://` is a standard TCP-based Web Socket;
+`ws+unix://` allows connection to a UNIX socket at the given path.
diff --git a/node_modules/socket.io/support/node-websocket-client/examples/client-unix.js b/node_modules/socket.io/support/node-websocket-client/examples/client-unix.js
new file mode 100644
index 0000000..3bb23ba
--- /dev/null
+++ b/node_modules/socket.io/support/node-websocket-client/examples/client-unix.js
@@ -0,0 +1,12 @@
+var sys = require('sys');
+var WebSocket = require('../lib/websocket').WebSocket;
+
+var ws = new WebSocket('ws+unix://' + process.argv[2], 'boffo');
+
+ws.addListener('message', function(d) {
+ sys.debug('Received message: ' + d.toString('utf8'));
+});
+
+ws.addListener('open', function() {
+ ws.send('This is a message', 1);
+});
diff --git a/node_modules/socket.io/support/node-websocket-client/examples/client.js b/node_modules/socket.io/support/node-websocket-client/examples/client.js
new file mode 100644
index 0000000..259bf6e
--- /dev/null
+++ b/node_modules/socket.io/support/node-websocket-client/examples/client.js
@@ -0,0 +1,10 @@
+var sys = require('sys');
+var WebSocket = require('../lib/websocket').WebSocket;
+
+var ws = new WebSocket('ws://localhost:8000/biff', 'borf');
+ws.addListener('data', function(buf) {
+ sys.debug('Got data: ' + sys.inspect(buf));
+});
+ws.onmessage = function(m) {
+ sys.debug('Got message: ' + m);
+}
diff --git a/node_modules/socket.io/support/node-websocket-client/examples/server-unix.js b/node_modules/socket.io/support/node-websocket-client/examples/server-unix.js
new file mode 100644
index 0000000..912be0e
--- /dev/null
+++ b/node_modules/socket.io/support/node-websocket-client/examples/server-unix.js
@@ -0,0 +1,13 @@
+var sys = require('sys');
+var ws = require('websocket-server/ws');
+
+var srv = ws.createServer({ debug : true});
+srv.addListener('connection', function(s) {
+ sys.debug('Got a connection!');
+
+ s._req.socket.addListener('fd', function(fd) {
+ sys.debug('Got an fd: ' + fd);
+ });
+});
+
+srv.listen(process.argv[2]);
diff --git a/node_modules/socket.io/support/node-websocket-client/lib/websocket.js b/node_modules/socket.io/support/node-websocket-client/lib/websocket.js
new file mode 100644
index 0000000..4f7f734
--- /dev/null
+++ b/node_modules/socket.io/support/node-websocket-client/lib/websocket.js
@@ -0,0 +1,617 @@
+var assert = require('assert');
+var buffer = require('buffer');
+var crypto = require('crypto');
+var events = require('events');
+var http = require('http');
+var net = require('net');
+var urllib = require('url');
+var sys = require('util');
+
+var FRAME_NO = 0;
+var FRAME_LO = 1;
+var FRAME_HI = 2;
+
+// Values for readyState as per the W3C spec
+var CONNECTING = 0;
+var OPEN = 1;
+var CLOSING = 2;
+var CLOSED = 3;
+
+var debugLevel = parseInt(process.env.NODE_DEBUG, 16);
+var debug = (debugLevel & 0x4) ?
+ function() { sys.error.apply(this, arguments); } :
+ function() { };
+
+// Generate a Sec-WebSocket-* value
+var createSecretKey = function() {
+ // How many spaces will we be inserting?
+ var numSpaces = 1 + Math.floor(Math.random() * 12);
+ assert.ok(1 <= numSpaces && numSpaces <= 12);
+
+ // What is the numerical value of our key?
+ var keyVal = (Math.floor(
+ Math.random() * (4294967295 / numSpaces)
+ ) * numSpaces);
+
+ // Our string starts with a string representation of our key
+ var s = keyVal.toString();
+
+ // Insert 'numChars' worth of noise in the character ranges
+ // [0x21, 0x2f] (14 characters) and [0x3a, 0x7e] (68 characters)
+ var numChars = 1 + Math.floor(Math.random() * 12);
+ assert.ok(1 <= numChars && numChars <= 12);
+
+ for (var i = 0; i < numChars; i++) {
+ var pos = Math.floor(Math.random() * s.length + 1);
+
+ var c = Math.floor(Math.random() * (14 + 68));
+ c = (c <= 14) ?
+ String.fromCharCode(c + 0x21) :
+ String.fromCharCode((c - 14) + 0x3a);
+
+ s = s.substring(0, pos) + c + s.substring(pos, s.length);
+ }
+
+ // We shoudln't have any spaces in our value until we insert them
+ assert.equal(s.indexOf(' '), -1);
+
+ // Insert 'numSpaces' worth of spaces
+ for (var i = 0; i < numSpaces; i++) {
+ var pos = Math.floor(Math.random() * (s.length - 1)) + 1;
+ s = s.substring(0, pos) + ' ' + s.substring(pos, s.length);
+ }
+
+ assert.notEqual(s.charAt(0), ' ');
+ assert.notEqual(s.charAt(s.length), ' ');
+
+ return s;
+};
+
+// Generate a challenge sequence
+var createChallenge = function() {
+ var c = '';
+ for (var i = 0; i < 8; i++) {
+ c += String.fromCharCode(Math.floor(Math.random() * 255));
+ }
+
+ return c;
+};
+
+// Get the value of a secret key string
+//
+// This strips non-digit values and divides the result by the number of
+// spaces found.
+var secretKeyValue = function(sk) {
+ var ns = 0;
+ var v = 0;
+
+ for (var i = 0; i < sk.length; i++) {
+ var cc = sk.charCodeAt(i);
+
+ if (cc == 0x20) {
+ ns++;
+ } else if (0x30 <= cc && cc <= 0x39) {
+ v = v * 10 + cc - 0x30;
+ }
+ }
+
+ return Math.floor(v / ns);
+}
+
+// Get the to-be-hashed value of a secret key string
+//
+// This takes the result of secretKeyValue() and encodes it in a big-endian
+// byte string
+var secretKeyHashValue = function(sk) {
+ var skv = secretKeyValue(sk);
+
+ var hv = '';
+ hv += String.fromCharCode((skv >> 24) & 0xff);
+ hv += String.fromCharCode((skv >> 16) & 0xff);
+ hv += String.fromCharCode((skv >> 8) & 0xff);
+ hv += String.fromCharCode((skv >> 0) & 0xff);
+
+ return hv;
+};
+
+// Compute the secret key signature based on two secret key strings and some
+// handshaking data.
+var computeSecretKeySignature = function(s1, s2, hs) {
+ assert.equal(hs.length, 8);
+
+ var hash = crypto.createHash('md5');
+
+ hash.update(secretKeyHashValue(s1));
+ hash.update(secretKeyHashValue(s2));
+ hash.update(hs);
+
+ return hash.digest('binary');
+};
+
+// Return a hex representation of the given binary string; used for debugging
+var str2hex = function(str) {
+ var hexChars = [
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'a', 'b', 'c', 'd', 'e', 'f'
+ ];
+
+ var out = '';
+ for (var i = 0; i < str.length; i++) {
+ var c = str.charCodeAt(i);
+ out += hexChars[(c & 0xf0) >>> 4];
+ out += hexChars[c & 0x0f];
+ out += ' ';
+ }
+
+ return out.trim();
+};
+
+// Get the scheme for a URL, undefined if none is found
+var getUrlScheme = function(url) {
+ var i = url.indexOf(':');
+ if (i == -1) {
+ return undefined;
+ }
+
+ return url.substring(0, i);
+};
+
+// Set a constant on the given object
+var setConstant = function(obj, name, value) {
+ Object.defineProperty(obj, name, {
+ get : function() {
+ return value;
+ }
+ });
+};
+
+// WebSocket object
+//
+// This is intended to conform (mostly) to http://dev.w3.org/html5/websockets/
+//
+// N.B. Arguments are parsed in the anonymous function at the bottom of the
+// constructor.
+var WebSocket = function(url, proto, opts) {
+ events.EventEmitter.call(this);
+
+ // Retain a reference to our object
+ var self = this;
+
+ // State of our end of the connection
+ var readyState = CONNECTING;
+
+ // Whether or not the server has sent a close handshake
+ var serverClosed = false;
+
+ // Our underlying net.Stream instance
+ var stream = undefined;
+
+ opts = opts || {
+ origin : 'http://www.example.com'
+ };
+
+ // Frame parsing functions
+ //
+ // These read data from the given buffer starting at the given offset,
+ // looking for the end of the current frame. If found, the current frame is
+ // emitted and the function returns. Only a single frame is processed at a
+ // time.
+ //
+ // The number of bytes read to complete a frame is returned, which the
+ // caller is to use to advance along its buffer. If 0 is returned, no
+ // completed frame bytes were found, and the caller should probably enqueue
+ // the buffer as a continuation of the current message. If a complete frame
+ // is read, the function is responsible for resting 'frameType'.
+
+ // Framing data
+ var frameType = FRAME_NO;
+ var bufs = [];
+ var bufsBytes = 0;
+
+ // Frame-parsing functions
+ var frameFuncs = [
+ // FRAME_NO
+ function(buf, off) {
+ if (buf[off] & 0x80) {
+ frameType = FRAME_HI;
+ } else {
+ frameType = FRAME_LO;
+ }
+
+ return 1;
+ },
+
+ // FRAME_LO
+ function(buf, off) {
+ debug('frame_lo(' + sys.inspect(buf) + ', ' + off + ')');
+
+ // Find the first instance of 0xff, our terminating byte
+ for (var i = off; i < buf.length && buf[i] != 0xff; i++)
+ ;
+
+ // We didn't find a terminating byte
+ if (i >= buf.length) {
+ return 0;
+ }
+
+ // We found a terminating byte; collect all bytes into a single buffer
+ // and emit it
+ var mb = null;
+ if (bufs.length == 0) {
+ mb = buf.slice(off, i);
+ } else {
+ mb = new buffer.Buffer(bufsBytes + i);
+
+ var mbOff = 0;
+ bufs.forEach(function(b) {
+ b.copy(mb, mbOff, 0, b.length);
+ mbOff += b.length;
+ });
+
+ assert.equal(mbOff, bufsBytes);
+
+ // Don't call Buffer.copy() if we're coping 0 bytes. Rather
+ // than being a no-op, this will trigger a range violation on
+ // the destination.
+ if (i > 0) {
+ buf.copy(mb, mbOff, off, i);
+ }
+
+ // We consumed all of the buffers that we'd been saving; clear
+ // things out
+ bufs = [];
+ bufsBytes = 0;
+ }
+
+ process.nextTick(function() {
+ var b = mb;
+ return function() {
+ var m = b.toString('utf8');
+
+ self.emit('data', b);
+ self.emit('message', m); // wss compat
+
+ if (self.onmessage) {
+ self.onmessage({data: m});
+ }
+ };
+ }());
+
+ frameType = FRAME_NO;
+ return i - off + 1;
+ },
+
+ // FRAME_HI
+ function(buf, off) {
+ debug('frame_hi(' + sys.inspect(buf) + ', ' + off + ')');
+
+ if (buf[off] !== 0) {
+ throw new Error('High-byte framing not supported.');
+ }
+
+ serverClosed = true;
+ return 1;
+ }
+ ];
+
+ // Handle data coming from our socket
+ var dataListener = function(buf) {
+ if (buf.length <= 0 || serverClosed) {
+ return;
+ }
+
+ debug('dataListener(' + sys.inspect(buf) + ')');
+
+ var off = 0;
+ var consumed = 0;
+
+ do {
+ if (frameType < 0 || frameFuncs.length <= frameType) {
+ throw new Error('Unexpected frame type: ' + frameType);
+ }
+
+ assert.equal(bufs.length === 0, bufsBytes === 0);
+ assert.ok(off < buf.length);
+
+ consumed = frameFuncs[frameType](buf, off);
+ off += consumed;
+ } while (!serverClosed && consumed > 0 && off < buf.length);
+
+ if (serverClosed) {
+ serverCloseHandler();
+ }
+
+ if (consumed == 0) {
+ bufs.push(buf.slice(off, buf.length));
+ bufsBytes += buf.length - off;
+ }
+ };
+
+ // Handle incoming file descriptors
+ var fdListener = function(fd) {
+ self.emit('fd', fd);
+ };
+
+ // Handle errors from any source (HTTP client, stream, etc)
+ var errorListener = function(e) {
+ process.nextTick(function() {
+ self.emit('wserror', e);
+
+ if (self.onerror) {
+ self.onerror(e);
+ }
+ });
+ };
+
+ // Finish the closing process; destroy the socket and tell the application
+ // that we've closed.
+ var finishClose = self.finishClose = function() {
+ readyState = CLOSED;
+ if (stream) {
+ stream.end();
+ stream.destroy();
+ stream = undefined;
+ }
+
+ process.nextTick(function() {
+ self.emit('close');
+ if (self.onclose) {
+ self.onclose();
+ }
+ });
+ };
+
+ // Send a close frame to the server
+ var sendClose = function() {
+ assert.equal(OPEN, readyState);
+
+ readyState = CLOSING;
+ stream.write('\xff\x00', 'binary');
+ };
+
+ // Handle a close packet sent from the server
+ var serverCloseHandler = function() {
+ assert.ok(serverClosed);
+ assert.ok(readyState === OPEN || readyState === CLOSING);
+
+ bufs = [];
+ bufsBytes = 0;
+
+ // Handle state transitions asynchronously so that we don't change
+ // readyState before the application has had a chance to process data
+ // events which are already in the delivery pipeline. For example, a
+ // 'data' event could be delivered with a readyState of CLOSING if we
+ // received both frames in the same packet.
+ process.nextTick(function() {
+ if (readyState === OPEN) {
+ sendClose();
+ }
+
+ finishClose();
+ });
+ };
+
+ // External API
+ self.close = function(timeout) {
+ if (readyState === CONNECTING) {
+ // If we're still in the process of connecting, the server is not
+ // in a position to understand our close frame. Just nuke the
+ // connection and call it a day.
+ finishClose();
+ } else if (readyState === OPEN) {
+ sendClose();
+
+ if (timeout) {
+ setTimeout(finishClose, timeout * 1000);
+ }
+ }
+ };
+
+ self.send = function(str, fd) {
+ if (readyState != OPEN) {
+ return;
+ }
+
+ stream.write('\x00', 'binary');
+ stream.write(str, 'utf8', fd);
+ stream.write('\xff', 'binary');
+ };
+
+ // wss compat
+ self.write = self.send;
+
+ setConstant(self, 'url', url);
+
+ Object.defineProperty(self, 'readyState', {
+ get : function() {
+ return readyState;
+ }
+ });
+
+ // Connect and perform handshaking with the server
+ (function() {
+ // Parse constructor arguments
+ if (!url) {
+ throw new Error('Url and must be specified.');
+ }
+
+ // Secrets used for handshaking
+ var key1 = createSecretKey();
+ var key2 = createSecretKey();
+ var challenge = createChallenge();
+
+ debug(
+ 'key1=\'' + str2hex(key1) + '\'; ' +
+ 'key2=\'' + str2hex(key2) + '\'; ' +
+ 'challenge=\'' + str2hex(challenge) + '\''
+ );
+
+ var httpHeaders = {
+ 'Connection' : 'Upgrade',
+ 'Upgrade' : 'WebSocket',
+ 'Sec-WebSocket-Key1' : key1,
+ 'Sec-WebSocket-Key2' : key2
+ };
+ if (opts.origin) {
+ httpHeaders['Origin'] = opts.origin;
+ }
+ if (proto) {
+ httpHeaders['Sec-WebSocket-Protocol'] = proto;
+ }
+
+ var httpPath = '/';
+
+ // Create the HTTP client that we'll use for handshaking. We'll cannabalize
+ // its socket via the 'upgrade' event and leave it to rot.
+ //
+ // N.B. The ws+unix:// scheme makes use of the implementation detail
+ // that http.Client passes its constructor arguments through,
+ // un-inspected to net.Stream.connect(). The latter accepts a
+ // string as its first argument to connect to a UNIX socket.
+ var opt = {};
+ var agent = null;
+ switch (getUrlScheme(url)) {
+ case 'ws':
+ var u = urllib.parse(url);
+ agent = new http.Agent({
+ host: u.hostname,
+ port: u.port || 80
+ });
+ opt.agent = agent;
+ opt.host = u.hostname;
+ opt.port = u.port || 80;
+ opt.path = (u.pathname || '/') + (u.search || '');
+ opt.headers = httpHeaders;
+ break;
+
+ case 'ws+unix':
+ var sockPath = url.substring('ws+unix://'.length, url.length);
+ var u = urllib.parse(url);
+ agent = new http.Agent({
+ host: 'localhost',
+ port: sockPath
+ });
+ opt.agent = agent;
+ opt.host = 'localhost';
+ opt.path = sockPath;
+ opt.headers = httpHeaders;
+ break;
+
+ default:
+ throw new Error('Invalid URL scheme \'' + urlScheme + '\' specified.');
+ }
+
+ var httpReq = http.request(opt, function() { });
+ var upgradeHandler = (function() {
+ var data = undefined;
+
+ return function(req, s, head) {
+ req.socket.setNoDelay(true);
+ stream = s;
+
+ if (readyState == CLOSED) {
+ stream.end();
+ stream.destroy();
+ stream = undefined;
+ return;
+ }
+
+ stream.on('data', function(d) {
+ if (d.length <= 0) {
+ return;
+ }
+
+ if (!data) {
+ data = d;
+ } else {
+ var data2 = new buffer.Buffer(data.length + d.length);
+
+ data.copy(data2, 0, 0, data.length);
+ d.copy(data2, data.length, 0, d.length);
+
+ data = data2;
+ }
+
+ if (data.length >= 16) {
+ var expected = computeSecretKeySignature(key1, key2, challenge);
+ var actual = data.slice(0, 16).toString('binary');
+
+ // Handshaking fails; we're donezo
+ if (actual != expected) {
+ debug(
+ 'expected=\'' + str2hex(expected) + '\'; ' +
+ 'actual=\'' + str2hex(actual) + '\''
+ );
+
+ process.nextTick(function() {
+ // N.B. Emit 'wserror' here, as 'error' is a reserved word in the
+ // EventEmitter world, and gets thrown.
+ self.emit(
+ 'wserror',
+ new Error('Invalid handshake from server:' +
+ 'expected \'' + str2hex(expected) + '\', ' +
+ 'actual \'' + str2hex(actual) + '\''
+ )
+ );
+
+ if (self.onerror) {
+ self.onerror();
+ }
+
+ finishClose();
+ });
+ }
+
+ // Un-register our data handler and add the one to be used
+ // for the normal, non-handshaking case. If we have extra
+ // data left over, manually fire off the handler on
+ // whatever remains.
+ //
+ // XXX: This is lame. We should only remove the listeners
+ // that we added.
+ httpReq.removeAllListeners('upgrade');
+ stream.removeAllListeners('data');
+ stream.on('data', dataListener);
+
+ readyState = OPEN;
+
+ process.nextTick(function() {
+ self.emit('open');
+
+ if (self.onopen) {
+ self.onopen();
+ }
+ });
+
+ // Consume any leftover data
+ if (data.length > 16) {
+ stream.emit('data', data.slice(16, data.length));
+ }
+ }
+ });
+ stream.on('fd', fdListener);
+ stream.on('error', errorListener);
+ stream.on('close', function() {
+ errorListener(new Error('Stream closed unexpectedly.'));
+ });
+
+ stream.emit('data', head);
+ };
+ })();
+ agent.on('upgrade', upgradeHandler); // node v0.4
+ httpReq.on('upgrade', upgradeHandler); // node v0.5+
+
+ httpReq.write(challenge, 'binary');
+ httpReq.end();
+ })();
+};
+sys.inherits(WebSocket, events.EventEmitter);
+exports.WebSocket = WebSocket;
+
+// Add some constants to the WebSocket object
+setConstant(WebSocket.prototype, 'CONNECTING', CONNECTING);
+setConstant(WebSocket.prototype, 'OPEN', OPEN);
+setConstant(WebSocket.prototype, 'CLOSING', CLOSING);
+setConstant(WebSocket.prototype, 'CLOSED', CLOSED);
+
+// vim:ts=4 sw=4 et
diff --git a/node_modules/socket.io/support/node-websocket-client/package.json b/node_modules/socket.io/support/node-websocket-client/package.json
new file mode 100644
index 0000000..c6e221f
--- /dev/null
+++ b/node_modules/socket.io/support/node-websocket-client/package.json
@@ -0,0 +1,22 @@
+{
+ "name" : "websocket-client",
+ "version" : "1.0.0",
+ "description" : "An HTML5 Web Sockets client",
+ "author" : "Peter Griess <pg@std.in>",
+ "engines" : {
+ "node" : ">=0.1.98"
+ },
+ "repositories" : [
+ {
+ "type" : "git",
+ "url" : "http://github.com/pgriess/node-websocket-client.git"
+ }
+ ],
+ "licenses" : [
+ {
+ "type" : "BSD",
+ "url" : "http://github.com/pgriess/node-websocket-client/blob/master/LICENSE"
+ }
+ ],
+ "main" : "./lib/websocket"
+}
diff --git a/node_modules/socket.io/support/node-websocket-client/test/test-basic.js b/node_modules/socket.io/support/node-websocket-client/test/test-basic.js
new file mode 100644
index 0000000..f010424
--- /dev/null
+++ b/node_modules/socket.io/support/node-websocket-client/test/test-basic.js
@@ -0,0 +1,68 @@
+// Verify that we can connect to a WebSocket server, exchange messages, and
+// shut down cleanly.
+
+var assert = require('assert');
+var WebSocket = require('../lib/websocket').WebSocket;
+var WebSocketServer = require('websocket-server/ws/server').Server;
+
+var PORT = 1024 + Math.floor(Math.random() * 4096);
+var C_MSG = 'Client test: ' + (Math.random() * 100);
+var S_MSG = 'Server test: ' + (Math.random() * 100);
+
+var serverGotConnection = false;
+var clientGotOpen = false;
+var clientGotData = false;
+var clientGotMessage = false;
+var serverGotMessage = false;
+var serverGotClose = false;
+var clientGotClose = false;
+
+var wss = new WebSocketServer();
+wss.listen(PORT, 'localhost');
+wss.on('connection', function(c) {
+ serverGotConnection = true;
+
+ c.on('message', function(m) {
+ assert.equal(m, C_MSG);
+ serverGotMessage = true;
+
+ c.close();
+ });
+
+ c.on('close', function() {
+ serverGotClose = true;
+ wss.close();
+ });
+
+ c.write(S_MSG);
+});
+
+var ws = new WebSocket('ws://localhost:' + PORT + '/', 'biff');
+ws.on('open', function() {
+ clientGotOpen = true;
+});
+ws.on('data', function(buf) {
+ assert.equal(typeof buf, 'object');
+ assert.equal(buf.toString('utf8'), S_MSG);
+
+ clientGotData = true;
+
+ ws.send(C_MSG);
+});
+ws.onmessage = function(m) {
+ assert.deepEqual(m, {data : S_MSG});
+ clientGotMessage = true;
+};
+ws.onclose = function() {
+ clientGotClose = true;
+};
+
+process.on('exit', function() {
+ assert.ok(serverGotConnection);
+ assert.ok(clientGotOpen);
+ assert.ok(clientGotData);
+ assert.ok(clientGotMessage);
+ assert.ok(serverGotMessage);
+ assert.ok(serverGotClose);
+ assert.ok(clientGotClose);
+});
diff --git a/node_modules/socket.io/support/node-websocket-client/test/test-client-close.js b/node_modules/socket.io/support/node-websocket-client/test/test-client-close.js
new file mode 100644
index 0000000..76fb81f
--- /dev/null
+++ b/node_modules/socket.io/support/node-websocket-client/test/test-client-close.js
@@ -0,0 +1,43 @@
+// Verify that a connection can be closed gracefully from the client.
+
+var assert = require('assert');
+var WebSocket = require('../lib/websocket').WebSocket;
+var WebSocketServer = require('websocket-server/ws/server').Server;
+
+var PORT = 1024 + Math.floor(Math.random() * 4096);
+var C_MSG = 'Client test: ' + (Math.random() * 100);
+
+var serverGotClientMessage = false;
+var clientGotServerClose = false;
+var serverGotClientClose = false;
+
+var wss = new WebSocketServer();
+wss.listen(PORT, 'localhost');
+wss.on('connection', function(c) {
+ c.on('message', function(m) {
+ assert.equal(m, C_MSG);
+ serverGotClientMessage = true;
+ });
+ c.on('close', function() {
+ serverGotClientClose = true;
+ wss.close();
+ });
+});
+
+var ws = new WebSocket('ws://localhost:' + PORT);
+ws.onopen = function() {
+ ws.send(C_MSG);
+
+ // XXX: Add a timeout here
+ ws.close(5);
+};
+ws.onclose = function() {
+ assert.equal(ws.CLOSED, ws.readyState);
+ clientGotServerClose = true;
+};
+
+process.on('exit', function() {
+ assert.ok(serverGotClientMessage);
+ assert.ok(clientGotServerClose);
+ assert.ok(serverGotClientClose);
+});
diff --git a/node_modules/socket.io/support/node-websocket-client/test/test-readonly-attrs.js b/node_modules/socket.io/support/node-websocket-client/test/test-readonly-attrs.js
new file mode 100644
index 0000000..de896b3
--- /dev/null
+++ b/node_modules/socket.io/support/node-websocket-client/test/test-readonly-attrs.js
@@ -0,0 +1,43 @@
+// Verify that some attributes of a WebSocket object are read-only.
+
+var assert = require('assert');
+var sys = require('sys');
+var WebSocket = require('../lib/websocket').WebSocket;
+var WebSocketServer = require('websocket-server/ws/server').Server;
+
+var PORT = 1024 + Math.floor(Math.random() * 4096);
+
+var wss = new WebSocketServer();
+wss.listen(PORT, 'localhost');
+wss.on('connection', function(c) {
+ c.close();
+ wss.close();
+});
+var ws = new WebSocket('ws://localhost:' + PORT + '/', 'biff');
+ws.on('open', function() {
+ assert.equal(ws.CONNECTING, 0);
+ try {
+ ws.CONNECTING = 13;
+ assert.equal(
+ ws.CONNECTING, 0,
+ 'Should not have been able to set read-only CONNECTING attribute'
+ );
+ } catch (e) {
+ assert.equal(e.type, 'no_setter_in_callback');
+ }
+
+ assert.equal(ws.OPEN, 1);
+ assert.equal(ws.CLOSING, 2);
+ assert.equal(ws.CLOSED, 3);
+
+ assert.equal(ws.url, 'ws://localhost:' + PORT + '/');
+ try {
+ ws.url = 'foobar';
+ assert.equal(
+ ws.url, 'ws://localhost:' + PORT + '/',
+ 'Should not have been able to set read-only url attribute'
+ );
+ } catch (e) {
+ assert.equal(e.type, 'no_setter_in_callback');
+ }
+});
diff --git a/node_modules/socket.io/support/node-websocket-client/test/test-ready-state.js b/node_modules/socket.io/support/node-websocket-client/test/test-ready-state.js
new file mode 100644
index 0000000..8fcbd4c
--- /dev/null
+++ b/node_modules/socket.io/support/node-websocket-client/test/test-ready-state.js
@@ -0,0 +1,26 @@
+// Verify that readyState transitions are implemented correctly
+
+var assert = require('assert');
+var WebSocket = require('../lib/websocket').WebSocket;
+var WebSocketServer = require('websocket-server/ws/server').Server;
+
+var PORT = 1024 + Math.floor(Math.random() * 4096);
+
+var wss = new WebSocketServer();
+wss.listen(PORT, 'localhost');
+wss.on('connection', function(c) {
+ c.close();
+});
+
+var ws = new WebSocket('ws://localhost:' + PORT);
+assert.equal(ws.readyState, ws.CONNECTING);
+ws.onopen = function() {
+ assert.equal(ws.readyState, ws.OPEN);
+
+ ws.close();
+ assert.ok(ws.readyState == ws.CLOSING);
+};
+ws.onclose = function() {
+ assert.equal(ws.readyState, ws.CLOSED);
+ wss.close();
+};
diff --git a/node_modules/socket.io/support/node-websocket-client/test/test-server-close.js b/node_modules/socket.io/support/node-websocket-client/test/test-server-close.js
new file mode 100644
index 0000000..a286429
--- /dev/null
+++ b/node_modules/socket.io/support/node-websocket-client/test/test-server-close.js
@@ -0,0 +1,41 @@
+// Verify that a connection can be closed gracefully from the server.
+
+var assert = require('assert');
+var WebSocket = require('../lib/websocket').WebSocket;
+var WebSocketServer = require('websocket-server/ws/server').Server;
+
+var PORT = 1024 + Math.floor(Math.random() * 4096);
+var S_MSG = 'Server test: ' + (Math.random() * 100);
+
+var clientGotServerMessage = false;
+var clientGotServerClose = false;
+var serverGotClientClose = false;
+
+var wss = new WebSocketServer();
+wss.listen(PORT, 'localhost');
+wss.on('connection', function(c) {
+ c.on('close', function() {
+ serverGotClientClose = true;
+ wss.close();
+ });
+
+ c.write(S_MSG);
+ c.close();
+});
+
+var ws = new WebSocket('ws://localhost:' + PORT);
+ws.onmessage = function(m) {
+ assert.deepEqual(m, {data: S_MSG});
+
+ clientGotServerMessage = true;
+};
+ws.onclose = function() {
+ assert.equal(ws.CLOSED, ws.readyState);
+ clientGotServerClose = true;
+};
+
+process.on('exit', function() {
+ assert.ok(clientGotServerMessage);
+ assert.ok(clientGotServerClose);
+ assert.ok(serverGotClientClose);
+});
diff --git a/node_modules/socket.io/support/node-websocket-client/test/test-unix-send-fd.js b/node_modules/socket.io/support/node-websocket-client/test/test-unix-send-fd.js
new file mode 100644
index 0000000..8f1c28d
--- /dev/null
+++ b/node_modules/socket.io/support/node-websocket-client/test/test-unix-send-fd.js
@@ -0,0 +1,63 @@
+// Verify that both sides of the WS connection can both send and receive file
+// descriptors.
+
+var assert = require('assert');
+var fs = require('fs');
+var path = require('path');
+var sys = require('sys');
+var WebSocket = require('../lib/websocket').WebSocket;
+var WebSocketServer = require('websocket-server/ws/server').Server;
+
+var PATH = path.join(__dirname, 'sock.' + process.pid);
+var C_MSG = 'Client test: ' + (Math.random() * 100);
+var S_MSG = 'Server test: ' + (Math.random() * 100);
+
+var clientReceivedData = false;
+var clientReceivedFD = false;
+var serverReceivedData = false;
+var serverReceivedFD = false;
+
+var wss = new WebSocketServer();
+wss.on('listening', function() {
+ var ws = new WebSocket('ws+unix://' + PATH);
+ ws.on('data', function(d) {
+ assert.equal(d.toString('utf8'), S_MSG);
+
+ clientReceivedData = true;
+
+ ws.send(C_MSG, 1);
+ ws.close();
+ });
+ ws.on('fd', function(fd) {
+ assert.ok(fd >= 0);
+
+ clientReceivedFD = true;
+ });
+});
+wss.on('connection', function(c) {
+ c.write(S_MSG, 0);
+ c._req.socket.on('fd', function(fd) {
+ assert.ok(fd >= 0);
+
+ serverReceivedFD = true;
+ });
+ c.on('message', function(d) {
+ assert.equal(d, C_MSG);
+
+ serverReceivedData = true;
+
+ wss.close();
+ });
+});
+wss.listen(PATH);
+
+process.on('exit', function() {
+ assert.ok(clientReceivedFD);
+ assert.ok(clientReceivedData);
+ assert.ok(serverReceivedFD);
+ assert.ok(serverReceivedData);
+
+ try {
+ fs.unlinkSync(PATH);
+ } catch (e) { }
+});
diff --git a/node_modules/socket.io/support/node-websocket-client/test/test-unix-sockets.js b/node_modules/socket.io/support/node-websocket-client/test/test-unix-sockets.js
new file mode 100644
index 0000000..5cbf094
--- /dev/null
+++ b/node_modules/socket.io/support/node-websocket-client/test/test-unix-sockets.js
@@ -0,0 +1,46 @@
+// Verify that we can connect to a server over UNIX domain sockets.
+
+var assert = require('assert');
+var fs = require('fs');
+var path = require('path');
+var sys = require('sys');
+var WebSocket = require('../lib/websocket').WebSocket;
+var WebSocketServer = require('websocket-server/ws/server').Server;
+
+var PATH = path.join(__dirname, 'sock.' + process.pid);
+var S_MSG = 'Server test: ' + (Math.random() * 100);
+
+var serverGotConnection = false;
+var clientGotOpen = false;
+var clientGotData = false;
+
+var wss = new WebSocketServer();
+wss.on('listening', function() {
+ var ws = new WebSocket('ws+unix://' + PATH);
+ ws.on('open', function() {
+ clientGotOpen = true;
+
+ ws.close();
+ });
+ ws.on('data', function(d) {
+ assert.equal(d.toString('utf8'), S_MSG);
+ clientGotData = true;
+ });
+});
+wss.on('connection', function(c) {
+ serverGotConnection = true;
+
+ c.write(S_MSG);
+ wss.close();
+});
+wss.listen(PATH);
+
+process.on('exit', function() {
+ assert.ok(serverGotConnection);
+ assert.ok(clientGotOpen);
+ assert.ok(clientGotData);
+
+ try {
+ fs.unlinkSync(PATH);
+ } catch(e) { }
+});