├── index.js ├── .gitignore ├── package.json ├── examples ├── simple.js └── simple.html ├── Readme.md ├── Todo.md ├── test ├── store.redis.test.js ├── common.js ├── transports.htmlfile.test.js └── transports.websocket.test.js └── lib └── redis-store.js /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/redis-store.js'); 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | .DS_Store 3 | .idea 4 | nohup.out 5 | npm-debug.log 6 | logs 7 | node_modules 8 | pids 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "socket.io-redis" 3 | , "version": "0.1.0-pre" 4 | , "description": "Redis Store for Socket.IO" 5 | , "keywords": ["socket.io", "store", "redis"] 6 | , "author": "Daniel D. Shaw (http://dshaw.com)" 7 | , "repository":{ 8 | "type": "git" 9 | , "url": "https://github.com/dshaw/Socket.IO-redis.git" 10 | } 11 | , "dependencies": { 12 | "socket.io": ">= 0.7.2" 13 | , "redis": ">= 0.5.11" 14 | , "eventemitter2": ">= 0.1.3" 15 | } 16 | , "devDependencies": { 17 | "expresso": "0.7.7" 18 | , "should": "0.2.1" 19 | } 20 | , "scripts" : { 21 | "test" : "expresso test/transports.websocket.test.js --serial" 22 | } 23 | , "main": "index" 24 | , "engines": { "node": ">= 0.4.0" } 25 | } -------------------------------------------------------------------------------- /examples/simple.js: -------------------------------------------------------------------------------- 1 | var simple = require('fs').readFileSync(__dirname + '/simple.html') 2 | , server = require('http').createServer(function(req, res) { 3 | res.writeHead(200, {"Content-Type": "text/html"}); 4 | res.end(simple); 5 | }) 6 | , io = require('socket.io').listen(server) 7 | , RedisStore = require('..'); 8 | 9 | server.listen(8124); 10 | 11 | //io.set('store', new RedisStore({ namespace: 'test:socket.io' }));; 12 | io.set('store', new RedisStore()); 13 | 14 | io.sockets.on('connection', function (socket) { 15 | console.log('new connection', socket.id); 16 | socket.send('>> simple socket.io server'); 17 | socket.on('message', function (data) { 18 | console.log('message:', data); 19 | // echo it back to the client 20 | socket.send(data); 21 | }); 22 | socket.on('disconnect', function () { 23 | console.log('disconnect', arguments); 24 | }); 25 | }); -------------------------------------------------------------------------------- /examples/simple.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Socket.IO-redis 5 | 14 | 15 | 16 |
17 | 18 |
19 |

20 | 
21 |   
22 |   
49 | 
50 | 


--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
 1 | Redis Implementation of Socket.IO Store
 2 | =======================================
 3 | 
 4 | # STATUS: Deprecated. No longer under development.
 5 | 
 6 | [RedisStore](https://github.com/LearnBoost/socket.io/blob/0.9/lib/socket.io.js#L129-L135) was added to Socket.io core up through v1.0. Please use that instead.
 7 | 
 8 | I'm leaving this up for historical reference and because I think there are some fundamentally better approaches in the way this version of a Redis-based Socket.io Store was designed to handle sessions. However, to make this compatible with Socket.io core would take another major internal architectural re-write of core.
 9 | 
10 | ## More Socket.io goodness
11 | 
12 | If you like [Socket.io RedisStore](https://github.com/LearnBoost/socket.io/blob/0.9/lib/stores/redis.js), you might also be interested in my other Socket.io modules:
13 | 
14 | * [socket.io-announce](https://github.com/dshaw/socket.io-announce) - Lightweight infrastructure broadcasting for use with Socket.io RedisStore.
15 | * [socket.io-zero](https://github.com/dshaw/socket.io-zero) - ZeroMQ dispatch with Redis distributed persistance Socket.io Store. 
16 | 
17 | 
18 | --------------------------------
19 | 
20 | #socket.io-redis
21 | 
22 | Setup
23 | ----------------
24 | 
25 | ```bash
26 | cd Socket.IO-redis
27 | npm install
28 | ```
29 | 
30 | Run the Examples
31 | ----------------
32 | 
33 | ```bash
34 | redis-server
35 | redis-cli
36 | > monitor
37 | node examples/simple.js
38 | ```
39 | 
40 | open [http://localhost:8124](http://localhost:8124)
41 | 
42 | Run the Tests
43 | ----------------
44 | 
45 | ```bash
46 | npm test
47 | ```
48 | 


--------------------------------------------------------------------------------
/Todo.md:
--------------------------------------------------------------------------------
 1 | External Storage / Redis Store
 2 | ===============================
 3 | 
 4 | Patched
 5 | ------------------
 6 | 
 7 |   -
 8 | 
 9 | Dispatch Events
10 | ------------------
11 | 
12 |   - parser.js
13 |     * packets
14 | 
15 | Add to Store
16 | ------------------
17 | 
18 |   - manager.js
19 |     ? namespaces
20 |         - manager.js
21 |         - Manager.prototype.of = function (namespace) {};
22 |     ? enable/disable settings
23 |   - namespace.js
24 |     * SocketNamespace
25 |       - this.sockets = {}
26 |     * SocketNamespace.prototype.packet = function (packet) {};
27 |   - socket.js
28 |     * this.store.once('disconnect:' + id, function (reason) {};
29 |     * Socket.prototype.packet = function (packet) {};
30 |     * Socket.prototype.set = function (key, value, fn) {};
31 |     * Socket.prototype.get = function (key, fn) {};
32 |     * Socket.prototype.send = function (data, fn) {};
33 |     * Socket.prototype.emit = function (ev) {};
34 |   - transport.js
35 |     * Transport.prototype.handleRequest = function (req) {};
36 |       `this.store.publish`
37 |     * Transport.prototype.setHandlers = function () {}
38 |       - this.store.once('disconnect-force:' + this.id, function () {};
39 |       - this.store.on('heartbeat-clear:' + this.id, function () {};
40 |       - this.store.on('volatile:' + this.id, function (packet) {};
41 |     * Transport.prototype.clearHandlers = function () {}
42 |       - // probably can actually be ignored.
43 | 
44 | 
45 | Handle listeners
46 | ------------------
47 | 
48 |   - manager.js
49 |     * self.store.on('message:' + data.id, function (packet) { self.handlePacket(data.id, packet); });
50 |   - namespace.js
51 |     * SocketNamespace.prototype.handlePacket = function (sessid, packet) {};
52 | 


--------------------------------------------------------------------------------
/test/store.redis.test.js:
--------------------------------------------------------------------------------
  1 | /*!
  2 |  * Socket.IO Redis Store
  3 |  * Copyright(c) 2011 Daniel D. Shaw 
  4 |  * MIT Licenced
  5 |  */
  6 | 
  7 | /**
  8 |  * Module Dependencies
  9 |  */
 10 | 
 11 | var sio = require('socket.io') // npm link socket.io@v0.7.0
 12 |   , parser = sio.parser
 13 |   , RedisStore = require('..')
 14 |   , should = require('./common')
 15 |   , HTTPClient = should.HTTPClient;
 16 | 
 17 | /**
 18 |  * Configuration
 19 |  */
 20 | 
 21 | var ports = 15500
 22 |   , opts = { namespace: 'test:socket.io' };
 23 | 
 24 | /**
 25 |  * Test.
 26 |  */
 27 | 
 28 | module.exports = {
 29 | 
 30 |   'redisStore is an Instance of Socket.IO Store': function (done) {
 31 |     var Store = sio.Store
 32 |       , redisStore = new RedisStore(opts);
 33 | 
 34 |     redisStore.should.be.an.instanceof(Store);
 35 |     redisStore.should.be.an.instanceof(RedisStore);
 36 | 
 37 |     done();
 38 |   },
 39 | 
 40 |   // from socket.io manager.test.js
 41 |   'test handshake': function (done) {
 42 |     var port = ++ports
 43 |       , cl = client(port)
 44 |       , io = create(cl);
 45 | 
 46 |     cl.get('/socket.io/{protocol}/', function (res, data) {
 47 |       res.statusCode.should.eql(200);
 48 |       data.should.match(/([^:]+):([0-9]+)?:([0-9]+)?:(.+)/);
 49 | 
 50 |       cl.end();
 51 |       io.server.close();
 52 | 
 53 |       done();
 54 |     });
 55 |   },
 56 | 
 57 |   // from socket.io manager.test.js
 58 |   'test authorization failure in handshake': function (done) {
 59 |     var port = ++ports
 60 |       , cl = client(port)
 61 |       , io = create(cl);
 62 | 
 63 |     io.configure(function () {
 64 |       function auth (data, fn) {
 65 |         fn(null, false);
 66 |       };
 67 | 
 68 |       io.set('authorization', auth);
 69 |     });
 70 | 
 71 |     cl.get('/socket.io/{protocol}/', function (res, data) {
 72 |       res.statusCode.should.eql(403);
 73 |       data.should.match(/handshake unauthorized/);
 74 | 
 75 |       cl.end();
 76 |       io.server.close();
 77 |       done();
 78 |     });
 79 |   },
 80 | 
 81 |   // from socket.io manager.test.js
 82 |   'test a handshake error': function (done) {
 83 |     var port = ++ports
 84 |       , cl = client(port)
 85 |       , io = create(cl);
 86 | 
 87 |     io.configure(function () {
 88 |       function auth (data, fn) {
 89 |         fn(new Error);
 90 |       };
 91 | 
 92 |       io.set('authorization', auth);
 93 |     });
 94 | 
 95 |     cl.get('/socket.io/{protocol}/', function (res, data) {
 96 |       res.statusCode.should.eql(500);
 97 |       data.should.match(/handshake error/);
 98 | 
 99 |       cl.end();
100 |       io.server.close();
101 |       done();
102 |     });
103 |   },
104 | 
105 |   // from socket.io manager.test.js
106 |   'test setting a custom heartbeat timeout': function (done) {
107 |     var port = ++ports
108 |       , cl = client(port)
109 |       , io = create(cl);
110 | 
111 |     io.configure(function () {
112 |       io.set('heartbeat timeout', 33);
113 |     });
114 | 
115 |     cl.get('/socket.io/{protocol}/', function (res, data) {
116 |       res.statusCode.should.eql(200);
117 |       data.should.match(/([^:]+):33:([0-9]+)?:(.*)/);
118 | 
119 |       cl.end();
120 |       io.server.close();
121 |       done();
122 |     });
123 |   }
124 | 
125 | };
126 | 


--------------------------------------------------------------------------------
/test/common.js:
--------------------------------------------------------------------------------
  1 | 
  2 | /*!
  3 |  * socket.io-node
  4 |  * Copyright(c) 2011 LearnBoost 
  5 |  * MIT Licensed
  6 |  */
  7 | 
  8 | /**
  9 |  * Test dependencies.
 10 |  */
 11 | 
 12 | var io = require('socket.io')
 13 |   , parser = io.parser
 14 |   , http = require('http')
 15 |   , https = require('https')
 16 |   , RedisStore = require('..');
 17 | 
 18 | /**
 19 |  * Exports.
 20 |  */
 21 | 
 22 | var should = module.exports = require('should');
 23 | 
 24 | should.HTTPClient = HTTPClient;
 25 | 
 26 | /**
 27 |  * Client utility.
 28 |  *
 29 |  * @api publiC
 30 |  */
 31 | 
 32 | function HTTPClient (port) {
 33 |   this.port = port;
 34 |   this.agent = new http.Agent({
 35 |       host: 'localhost'
 36 |     , port: port
 37 |   });
 38 | };
 39 | 
 40 | /**
 41 |  * Issue a request
 42 |  *
 43 |  * @api private
 44 |  */
 45 | 
 46 | HTTPClient.prototype.request = function (path, opts, fn) {
 47 |   if ('function' == typeof opts) {
 48 |     fn = opts;
 49 |     opts = {};
 50 |   }
 51 | 
 52 |   opts = opts || {};
 53 |   opts.agent = this.agent;
 54 |   opts.host = 'localhost';
 55 |   opts.port = this.port;
 56 |   opts.path = path.replace(/{protocol}/g, io.protocol);
 57 |   opts.headers = {
 58 |       'Host': 'localhost'
 59 |     , 'Connection': 'keep-alive'
 60 |   };
 61 | 
 62 |   var req = http.request(opts, function (res) {
 63 |     if (false === opts.buffer)
 64 |       return fn && fn(res);
 65 | 
 66 |     var buf = '';
 67 | 
 68 |     res.on('data', function (chunk) {
 69 |       buf += chunk;
 70 |     });
 71 | 
 72 |     res.on('end', function () {
 73 |       fn && fn(res, opts.parse ? opts.parse(buf) : buf);
 74 |     });
 75 |   });
 76 | 
 77 |   req.on('error', function (err) { });
 78 | 
 79 |   if (undefined !== opts.data)
 80 |     req.write(opts.data);
 81 | 
 82 |   req.end();
 83 | 
 84 |   return req;
 85 | };
 86 | 
 87 | /**
 88 |  * Terminates the client and associated connections.
 89 |  *
 90 |  * @api public
 91 |  */
 92 | 
 93 | HTTPClient.prototype.end = function () {
 94 |   this.agent.sockets.forEach(function (socket) {
 95 |     socket.end();
 96 |   });
 97 | };
 98 | 
 99 | /**
100 |  * Issue a GET request
101 |  *
102 |  * @api public
103 |  */
104 | 
105 | HTTPClient.prototype.get = function (path, opts, fn) {
106 |   if ('function' == typeof opts) {
107 |     fn = opts;
108 |     opts = {};
109 |   }
110 | 
111 |   opts = opts || {};
112 |   opts.method = 'GET';
113 | 
114 |   // override the parser for transport requests
115 |   if (/\/(xhr-polling|htmlfile|jsonp-polling)\//.test(path)) {
116 |     // parser that might be necessary for transport-specific framing
117 |     var transportParse = opts.parse;
118 |     opts.parse = function (data) {
119 |       if (data === '') return data;
120 | 
121 |       data = transportParse ? transportParse(data) : data;
122 |       return parser.decodePayload(data);
123 |     };
124 |   } else {
125 |     opts.parse = undefined;
126 |   }
127 | 
128 |   return this.request(path, opts, fn);
129 | };
130 | 
131 | /**
132 |  * Issue a POST request
133 |  *
134 |  * @api private
135 |  */
136 | 
137 | HTTPClient.prototype.post = function (path, data, opts, fn) {
138 |   if ('function' == typeof opts) {
139 |     fn = opts;
140 |     opts = {};
141 |   }
142 | 
143 |   opts = opts || {};
144 |   opts.method = 'POST';
145 |   opts.data = data;
146 | 
147 |   return this.request(path, opts, fn);
148 | };
149 | 
150 | /**
151 |  * Performs a handshake (GET) request
152 |  *
153 |  * @api private
154 |  */
155 | 
156 | HTTPClient.prototype.handshake = function (opts, fn) {
157 |   if ('function' == typeof opts) {
158 |     fn = opts;
159 |     opts = {};
160 |   }
161 | 
162 |   return this.get('/socket.io/{protocol}', opts, function (res, data) {
163 |     fn && fn.apply(null, data.split(':'));
164 |   });
165 | };
166 | 
167 | /**
168 |  * Generates a new client for the given port.
169 |  *
170 |  * @api private
171 |  */
172 | 
173 | client = function (port) {
174 |   return new HTTPClient(port);
175 | };
176 | 
177 | /**
178 |  * Create a socket.io server.
179 |  *
180 |  * Modified to always use RedisStore
181 |  */
182 | 
183 | create = function (cl) {
184 |   var rio = io.listen(cl.port);
185 |   var options = { namespace: 'test:socket.io' };
186 |   rio.set('store', new RedisStore(options));
187 |   return rio;
188 | };
189 | 
190 | 


--------------------------------------------------------------------------------
/lib/redis-store.js:
--------------------------------------------------------------------------------
  1 | /*!
  2 |  * Socket.IO Redis Store
  3 |  * Copyright(c) 2011 Daniel D. Shaw 
  4 |  * MIT Licenced
  5 |  */
  6 | 
  7 | /**
  8 |  * Module Dependencies
  9 |  */
 10 | 
 11 | var crypto = require('crypto')
 12 |   , util = require('util')
 13 |   , sio = require('socket.io')
 14 |   , Store = sio.Store
 15 |   , redis = require('redis')
 16 |   , EventEmitter2 = require('eventemitter2');
 17 | 
 18 | /**
 19 |  * Export the constructors
 20 |  */
 21 | 
 22 | exports = module.exports = RedisStore;
 23 | RedisStore.Client = Client;
 24 | 
 25 | /**
 26 |  * Redis Store
 27 |  *
 28 |  * @api public
 29 |  */
 30 | 
 31 | function RedisStore(opts) {
 32 |   console.log('initializing redis store with', opts || 'default options');
 33 |   var self = this;
 34 | 
 35 |   this.clientsMap = {};
 36 |   this.rooms = {};
 37 |   opts || (opts = {});
 38 |   opts.namespace || (opts.namespace = 'socket.io');
 39 |   this.namespace = opts.namespace;
 40 |   this.opts = opts;
 41 | 
 42 |   this.redisClient = new redis.createClient(opts.port, opts.host, opts);
 43 |   this.redisSubscriber = new redis.createClient(opts.port, opts.host, opts);
 44 |   if (opts.db) {
 45 |     self.redisClient.on('connect', function() {
 46 |       self.redisClient.select(opts.db);
 47 |     });
 48 |     self.redisSubscriber.on('connect', function() {
 49 |       self.redisSubscriber.select(opts.db);
 50 |     });
 51 |   }
 52 |   
 53 |   this.redisSubscriber.psubscribe('*');
 54 |   this.redisSubscriber.on('pmessage', function (pattern, event, data) {
 55 |     if (data === 'undefined') {
 56 |       data = undefined;
 57 |     } else {
 58 |       try {
 59 |         data = JSON.parse(data);
 60 |       } catch (e) { /* then data is what it is */ }
 61 |     }
 62 |     console.log('redis subscriber', event, data);
 63 |     self.emit(event, data);
 64 |   });
 65 | }
 66 | 
 67 | /**
 68 |  * Monkey patch Socket.IO Store to use EventEmitter2 instead of events.EventEmitter
 69 |  */
 70 | 
 71 | Store.prototype.__proto__ = EventEmitter2.prototype;
 72 | 
 73 | /**
 74 |  * Inherits from Socket.IO Store
 75 |  */
 76 | 
 77 | util.inherits(RedisStore, Store);
 78 | 
 79 | 
 80 | /**
 81 |  * Namespaced keys.
 82 |  *
 83 |  * @param {String} key
 84 |  *
 85 |  * @api private
 86 |  */
 87 | 
 88 | RedisStore.prototype.key = function (key /* , multi element key */) {
 89 |   return [this.namespace].concat(Array.prototype.slice.call(arguments)).join(':')
 90 | };
 91 | 
 92 | /**
 93 |  * Handshake a client.
 94 |  *
 95 |  * @param {Object} client request object
 96 |  * @param {Function} callback
 97 |  * @api public
 98 |  */
 99 | 
100 | RedisStore.prototype.handshake = function (data, fn) {
101 |   var id = this.generateId()
102 |     , key = this.key('client', id);
103 |   this.redisClient.hset(key, 'handshaken', 1, function(err, res) {
104 |     fn && fn(null, id);
105 |   });
106 |   return this;
107 | };
108 | 
109 | /**
110 |  * Checks if a client is handshaken.
111 |  *
112 |  * @api public
113 |  */
114 | 
115 | RedisStore.prototype.isHandshaken = function (id, fn) {
116 |   var key = this.key('client', id);
117 |   this.redisClient.hexists(key, 'handshaken', function(err, res) {
118 |     fn(null, !!res);
119 |   });
120 |   return this;
121 | };
122 | 
123 | /**
124 |  * Generates a random id.
125 |  *
126 |  * @api private
127 |  */
128 | 
129 | RedisStore.prototype.generateId = function () {
130 |   var rand = String(Math.random() * Math.random() * Date.now());
131 |   return crypto.createHash('md5').update(rand).digest('hex');
132 | };
133 | 
134 | /**
135 |  * Retrieves a client store instance.
136 |  *
137 |  * NOTE: this need to be differs from Memory store. Needs to be async, but the internal APIs depend on it being sync.
138 |  *
139 |  * @api public
140 |  */
141 | 
142 | RedisStore.prototype.client = function (id /*, fn */) {
143 |   if (!this.clientsMap[id]) {
144 |     var key = this.key('client', id);
145 |     this.redisClient.hset(key, 'id', id);
146 |     this.clientsMap[id] = new RedisStore.Client(this, id);
147 |     this.log.info('initializing redis client store for', id);
148 |   }
149 | 
150 |   return this.clientsMap[id];
151 | };
152 | 
153 | /**
154 |  * Called when a client disconnects.
155 |  *
156 |  * @param {String} id
157 |  * @param {Boolean} force
158 |  * @param {String} reason
159 |  *
160 |  * @api public
161 |  */
162 | 
163 | RedisStore.prototype.disconnect = function (id, force, reason) {
164 |   var key = this.key('client', id);
165 |   this.log.debug('destroying dispatcher for', id);
166 |   this.redisClient.del(key, function (err, res) {});
167 |   
168 |   if (force) {
169 |     this.publish('disconnect-force:' + id, reason);
170 |   }
171 |   this.publish('disconnect:' + id, reason);
172 | 
173 |   return this;
174 | };
175 | 
176 | /**
177 |  * Relays a heartbeat message.
178 |  *
179 |  * @param {String} id
180 |  *
181 |  * @api private
182 |  */
183 | 
184 | RedisStore.prototype.heartbeat = function (id) {
185 |   return this.publish('heartbeat-clear:' + id);
186 | };
187 | 
188 | /**
189 |  * Relays a packet
190 |  *
191 |  * @param {String} id
192 |  * @param {Object} packet
193 |  *
194 |  * @api private
195 |  */
196 | 
197 | RedisStore.prototype.message = function (id, packet) {
198 |   return this.publish('message:' + id, packet);
199 | };
200 | 
201 | /**
202 |  * Returns client ids in a particular room
203 |  *
204 |  * @param {String} room
205 |  * @param {Function} callback
206 |  *
207 |  * @api public
208 |  */
209 | 
210 | RedisStore.prototype.clients = function (room, fn) {
211 |   if ('function' == typeof room) {
212 |     fn = room;
213 |     room = '';
214 |   }
215 | 
216 |   var key = this.key('rooms', room);
217 |   this.redisClient.smembers(key, function(err, res) {
218 |     fn && fn(res);
219 |   });
220 | };
221 | 
222 | /**
223 |  * Joins a user to a room
224 |  *
225 |  * @param {String} sid
226 |  * @param {String} room
227 |  * @param {Function} callback
228 |  *
229 |  * @api private
230 |  */
231 | 
232 | RedisStore.prototype.join = function (sid, room, fn) {
233 |   this.log.info('join room', sid, room);
234 |   var roomKey = this.key('rooms', room)
235 |     , clientKey = this.key('client', sid, 'rooms');
236 | 
237 |   this.log.info('rooms key', roomKey, 'client key', clientKey);
238 |   this.redisClient.multi()
239 |       .sadd(roomKey, sid)
240 |       // TODO: integrate with core client data structure
241 |       .sadd(clientKey, room)
242 |       .exec(function (err, replies) {
243 |         fn && fn();
244 |       });
245 |   return this;
246 | };
247 | 
248 | /**
249 |  * Removes a user from a room
250 |  *
251 |  * @api private
252 |  */
253 | 
254 | RedisStore.prototype.leave = function (sid, room, fn) {
255 |   var key = this.key('rooms', room);
256 |   this.redisClient.srem(key, sid, function(err, res) {
257 |     fn && fn();
258 |   });
259 |   return this;
260 | };
261 | 
262 | /**
263 |  * Simple publish
264 |  *
265 |  * @param {String} event
266 |  * @param {Object} data
267 |  * @param {Function} callback
268 |  *
269 |  * @api public
270 |  */
271 | 
272 | RedisStore.prototype.publish = function (ev, data, fn) {
273 |   if ('function' == typeof data) {
274 |     fn = data;
275 |     data = undefined;
276 |   }
277 | 
278 |   if (!data) {
279 |     data = ev;
280 |     ev = this.namespace;
281 |   }
282 |   
283 |   this.log.debug('publish to redis', ev, data);
284 |   this.redisClient.publish(ev, JSON.stringify(data));
285 |   
286 |   if (fn) fn();
287 | 
288 |   return this;
289 | };
290 | 
291 | /**
292 |  * Simple subscribe
293 |  *
294 |  * @param {String} channel
295 |  * @param {Function} callback
296 |  *
297 |  * @api public
298 |  */
299 | 
300 | RedisStore.prototype.subscribe = function (chn, fn) {
301 |   this.log.debug('redis store channel subscribe', chn);
302 |   this.on(chn, fn);
303 |   return this;
304 | };
305 | 
306 | /**
307 |  * Simple unsubscribe
308 |  *
309 |  * @param {String} channel
310 |  *
311 |  * @api public
312 |  */
313 | 
314 | RedisStore.prototype.unsubscribe = function (chn) {
315 |   this.log.debug('redis store channel unsubscribe', chn);
316 |   this.redisSubscriber.unsubscribe(chn);
317 | };
318 | 
319 | /**
320 |  * Client constructor
321 |  *
322 |  * @api private
323 |  */
324 | 
325 | function Client (store, id) {
326 |   Store.Client.apply(this, arguments);
327 |   this.reqs = 0;
328 |   this.paused = true;
329 |   this.rooms = {};
330 | }
331 | 
332 | /**
333 |  * Inherits from Store.Client
334 |  */
335 | 
336 | util.inherits(Client, Store.Client);
337 | 
338 | /**
339 |  * Counts transport requests.
340 |  *
341 |  * @param {Function} callback
342 |  *
343 |  * @api public
344 |  */
345 | 
346 | Client.prototype.count = function (fn) {
347 |   var key = this.store.key('client', this.id);
348 |   this.store.redisClient.hincrby(key, 'count', 1, function(err, res) {
349 |     fn(null, res);
350 |   });
351 |   return this;
352 | };
353 | 
354 | /**
355 |  * Sets up queue consumption
356 |  *
357 |  * @param {Function} callback
358 |  *
359 |  * @api public
360 |  */
361 | 
362 | Client.prototype.consume = function (fn) {
363 |   this.consumer = fn;
364 |   this.paused = false;
365 | 
366 |   if (this.buffer.length) {
367 |     fn(this.buffer, null);
368 |     this.buffer = [];
369 |   }
370 | 
371 |   return this;
372 | };
373 | 
374 | /**
375 |  * Publishes a message to be sent to the client.
376 |  *
377 |  * @String encoded message
378 |  * @api public
379 |  */
380 | 
381 | Client.prototype.publish = function (msg) {
382 |   this.store.log.info('Client publish', msg);
383 |   if (this.paused) {
384 |     this.buffer.push(msg);
385 |   } else {
386 |     this.consumer(null, msg);
387 |   }
388 | 
389 |   return this;
390 | };
391 | 
392 | /**
393 |  * Pauses the stream.
394 |  *
395 |  * @api public
396 |  */
397 | 
398 | Client.prototype.pause = function () {
399 |   this.paused = true;
400 |   return this;
401 | };
402 | 
403 | /**
404 |  * Destroys the client.
405 |  *
406 |  * @api public
407 |  */
408 | 
409 | Client.prototype.destroy = function () {
410 |   this.buffer = null;
411 | };
412 | 
413 | /**
414 |  * Gets a key
415 |  *
416 |  * @param {String} key
417 |  * @param {Function} callback
418 |  *
419 |  * @api public
420 |  */
421 | 
422 | Client.prototype.get = function (key, fn) {
423 |   var clientKey = this.store.key('client', this.id, 'dict');
424 |   this.store.redisClient.hget(clientKey, key, function(err, res) {
425 |     fn && fn(null, res);
426 |   });
427 |   return this;
428 | };
429 | 
430 | /**
431 |  * Sets a key
432 |  *
433 |  * @param {String} key
434 |  * @param {String} value
435 |  * @param {Function} callback
436 |  *
437 |  * @api public
438 |  */
439 | 
440 | Client.prototype.set = function (key, value, fn) {
441 |   var clientKey = this.store.key('client', this.id, 'dict');
442 |   this.store.redisClient.hset(clientKey, key, value, function(err, res) {
443 |     fn && fn(null);
444 |   });
445 |   return this;
446 | };
447 | 
448 | /**
449 |  * Emits a message incoming from client.
450 |  *
451 |  * TODO: this should probably publish to redis instead of emitting
452 |  *
453 |  * @param {String} message
454 |  *
455 |  * @api private
456 |  */
457 | 
458 | Client.prototype.onMessage = function (msg) {
459 |   this.store.emit('message:' + this.id, msg);
460 | };
461 | 


--------------------------------------------------------------------------------
/test/transports.htmlfile.test.js:
--------------------------------------------------------------------------------
  1 | 
  2 | /*!
  3 |  * socket.io-node
  4 |  * Copyright(c) 2011 LearnBoost 
  5 |  * MIT Licensed
  6 |  */
  7 | 
  8 | /**
  9 |  * Test dependencies.
 10 |  */
 11 | 
 12 | var sio = require('socket.io')
 13 |   , should = require('./common')
 14 |   , HTTPClient = should.HTTPClient
 15 |   , parser = sio.parser
 16 |   , ports = 15300;
 17 | 
 18 | /**
 19 |  * HTTPClient for htmlfile transport.
 20 |  */
 21 | 
 22 | function HTMLFile (port) {
 23 |   HTTPClient.call(this, port);
 24 | };
 25 | 
 26 | /**
 27 |  * Inherts from HTTPClient.
 28 |  */
 29 | 
 30 | HTMLFile.prototype.__proto__ = HTTPClient.prototype;
 31 | 
 32 | /**
 33 |  * Override GET request with streaming parser.
 34 |  *
 35 |  * @api public
 36 |  */
 37 | 
 38 | var head = ''
 40 |   , initial = ''
 41 |       + ''
 42 |       + new Array(174).join(' ')
 43 | 
 44 | HTMLFile.prototype.data = function (path, opts, fn) {
 45 |   if ('function' == typeof opts) {
 46 |     fn = opts;
 47 |     opts = {};
 48 |   }
 49 | 
 50 |   opts.buffer = false;
 51 | 
 52 |   return this.request(path, opts, function (res) {
 53 |     var buf = ''
 54 |       , messages = 0
 55 |       , state = 0;
 56 | 
 57 |     res.on('data', function (chunk) {
 58 |       buf += chunk;
 59 | 
 60 |       function parse () {
 61 |         switch (state) {
 62 |           case 0:
 63 |             if (buf.indexOf(initial) === 0) {
 64 |               buf = buf.substr(initial.length);
 65 |               state = 1;
 66 |             } else {
 67 |               break;
 68 |             }
 69 | 
 70 |           case 1:
 71 |             if (buf.indexOf(head) === 0) {
 72 |               buf = buf.substr(head.length);
 73 |               state = 2;
 74 |             } else {
 75 |               break;
 76 |             }
 77 | 
 78 |           case 2:
 79 |             if (buf.indexOf(foot) != -1) {
 80 |               var data = buf.slice(0, buf.indexOf(foot))
 81 |                 , obj = JSON.parse(data);
 82 | 
 83 |               fn(obj === '' ? obj : parser.decodePayload(obj), ++messages);
 84 | 
 85 |               buf = buf.substr(data.length + foot.length);
 86 |               state = 1;
 87 | 
 88 |               parse();
 89 |             }
 90 |         };
 91 |       };
 92 | 
 93 |       parse();
 94 |     });
 95 |   });
 96 | };
 97 | 
 98 | /**
 99 |  * Create client for this transport.
100 |  *
101 |  * @api public
102 |  */
103 | 
104 | function client (port) {
105 |   return new HTMLFile(port);
106 | };
107 | 
108 | /**
109 |  * Tests.
110 |  */
111 | 
112 | module.exports = {
113 | 
114 | // NOT PASSING
115 | //  'test that not responding to a heartbeat drops client': function (done) {
116 | //    var port = ++ports
117 | //      , cl = client(port)
118 | //      , io = create(cl)
119 | //      , beat = false;
120 | //
121 | //    io.configure(function () {
122 | //      io.set('heartbeat interval', .05);
123 | //      io.set('heartbeat timeout', .05);
124 | //      io.set('close timeout', 0);
125 | //    });
126 | //
127 | //    io.sockets.on('connection', function (socket) {
128 | //      socket.on('disconnect', function (reason) {
129 | //        beat.should.be.true;
130 | //        reason.should.eql('heartbeat timeout');
131 | //
132 | //        cl.end();
133 | //        io.server.close();
134 | //        done();
135 | //      });
136 | //    });
137 | //
138 | //    cl.handshake(function (sid) {
139 | //      cl.data('/socket.io/{protocol}/htmlfile/' + sid, function (msgs, i) {
140 | //        switch (i) {
141 | //          case 1:
142 | //            msgs.should.have.length(1);
143 | //            msgs[0].type.should.eql('connect');
144 | //            msgs[0].endpoint.should.eql('');
145 | //            break;
146 | //
147 | //          case 2:
148 | //            msgs.should.have.length(1);
149 | //            msgs[0].type.should.eql('heartbeat');
150 | //            beat = true;
151 | //        };
152 | //      });
153 | //    });
154 | //  },
155 | 
156 | //  'test that responding to a heartbeat maintains session': function (done) {
157 | //    var port = ++ports
158 | //      , cl = client(port)
159 | //      , io = create(cl)
160 | //      , heartbeats = 0;
161 | //
162 | //    io.configure(function () {
163 | //      io.set('heartbeat interval', .05);
164 | //      io.set('heartbeat timeout', .05);
165 | //      io.set('close timeout', 0);
166 | //    });
167 | //
168 | //    io.sockets.on('connection', function (socket) {
169 | //      socket.on('disconnect', function (reason) {
170 | //        heartbeats.should.eql(2);
171 | //        reason.should.eql('heartbeat timeout');
172 | //
173 | //        cl.end();
174 | //        io.server.close();
175 | //        done();
176 | //      });
177 | //    });
178 | //
179 | //    cl.handshake(function (sid) {
180 | //      cl.data('/socket.io/{protocol}/htmlfile/' + sid, function (msgs, i) {
181 | //        switch (i) {
182 | //          case 1:
183 | //            msgs.should.have.length(1);
184 | //            msgs[0].type.should.eql('connect');
185 | //            msgs[0].endpoint.should.eql('');
186 | //            break;
187 | //
188 | //          default:
189 | //            msgs.should.have.length(1);
190 | //            msgs[0].type.should.eql('heartbeat');
191 | //
192 | //            heartbeats++;
193 | //
194 | //            if (heartbeats == 1) {
195 | //              cl.post('/socket.io/{protocol}/htmlfile/' + sid, parser.encodePacket({
196 | //                type: 'heartbeat'
197 | //              }));
198 | //            }
199 | //        }
200 | //      });
201 | //    });
202 | //  },
203 | 
204 | //  'test sending undeliverable volatile messages': function (done) {
205 | //    var port = ++ports
206 | //      , cl = client(port)
207 | //      , io = create(cl)
208 | //      , messaged = false
209 | //      , s;
210 | //
211 | //    io.configure(function () {
212 | //      io.set('close timeout', 0);
213 | //    });
214 | //
215 | //    io.sockets.on('connection', function (socket) {
216 | //      s = socket;
217 | //
218 | //      socket.on('disconnect', function () {
219 | //        messaged.should.be.false;
220 | //        io.server.close();
221 | //        done();
222 | //      });
223 | //    });
224 | //
225 | //    cl.handshake(function (sid) {
226 | //      cl.data('/socket.io/{protocol}/htmlfile/' + sid, function () { });
227 | //
228 | //      setTimeout(function () {
229 | //        cl.end();
230 | //
231 | //        setTimeout(function () {
232 | //          s.volatile.send('wooooot');
233 | //          cl = client(port);
234 | //          cl.data('/socket.io/{protocol}/htmlfile/' + sid, function (msgs) {
235 | //            if (msgs && msgs.length)
236 | //              messaged = true;
237 | //          });
238 | //
239 | //          setTimeout(function () {
240 | //            cl.end();
241 | //          }, 20);
242 | //        }, 20);
243 | //      }, 20);
244 | //    });
245 | //  },
246 | //
247 | //  'test sending undeliverable volatile json': function (done) {
248 | //    var port = ++ports
249 | //      , cl = client(port)
250 | //      , io = create(cl)
251 | //      , messaged = false
252 | //      , s;
253 | //
254 | //    io.configure(function () {
255 | //      io.set('close timeout', 0);
256 | //    });
257 | //
258 | //    io.sockets.on('connection', function (socket) {
259 | //      s = socket;
260 | //
261 | //      socket.on('disconnect', function () {
262 | //        messaged.should.be.false;
263 | //        io.server.close();
264 | //        done();
265 | //      });
266 | //    });
267 | //
268 | //    cl.handshake(function (sid) {
269 | //      cl.data('/socket.io/{protocol}/htmlfile/' + sid, function () { });
270 | //
271 | //      setTimeout(function () {
272 | //        cl.end();
273 | //
274 | //        setTimeout(function () {
275 | //          s.volatile.json.send(123);
276 | //
277 | //          cl = client(port);
278 | //          cl.data('/socket.io/{protocol}/htmlfile/' + sid, function (msgs) {
279 | //            if (msgs && msgs.length)
280 | //              messaged = true;
281 | //          });
282 | //
283 | //          setTimeout(function () {
284 | //            cl.end();
285 | //          }, 20);
286 | //        }, 20);
287 | //      }, 20);
288 | //    });
289 | //  },
290 | //
291 | //  'test sending undeliverable volatile events': function (done) {
292 | //    var port = ++ports
293 | //      , cl = client(port)
294 | //      , io = create(cl)
295 | //      , messaged = false
296 | //      , s;
297 | //
298 | //    io.configure(function () {
299 | //      io.set('close timeout', 0);
300 | //    });
301 | //
302 | //    io.sockets.on('connection', function (socket) {
303 | //      s = socket;
304 | //
305 | //      socket.on('disconnect', function () {
306 | //        messaged.should.be.false;
307 | //        io.server.close();
308 | //        done();
309 | //      });
310 | //    });
311 | //
312 | //    cl.handshake(function (sid) {
313 | //      cl.data('/socket.io/{protocol}/htmlfile/' + sid, function () { });
314 | //
315 | //      setTimeout(function () {
316 | //        cl.end();
317 | //
318 | //        setTimeout(function () {
319 | //          s.volatile.emit('tobi');
320 | //
321 | //          cl = client(port);
322 | //          cl.data('/socket.io/{protocol}/htmlfile/' + sid, function (msgs) {
323 | //            if (msgs && msgs.length)
324 | //              messaged = true;
325 | //          });
326 | //
327 | //          setTimeout(function () {
328 | //            cl.end();
329 | //          }, 20);
330 | //        }, 20);
331 | //      }, 20);
332 | //    });
333 | //  },
334 | 
335 | //  'test sending deliverable volatile messages': function (done) {
336 | //    var port = ++ports
337 | //      , cl = client(port)
338 | //      , io = create(cl)
339 | //      , messaged = false;
340 | //
341 | //    io.configure(function () {
342 | //      io.set('close timeout', 0);
343 | //    });
344 | //
345 | //    io.sockets.on('connection', function (socket) {
346 | //      socket.volatile.send('woot');
347 | //
348 | //      socket.on('disconnect', function () {
349 | //        io.server.close();
350 | //        done();
351 | //      });
352 | //    });
353 | //
354 | //    cl.handshake(function (sid) {
355 | //      cl.data('/socket.io/{protocol}/htmlfile/' + sid, function (msgs, i) {
356 | //        switch (i) {
357 | //          case 1:
358 | //            msgs.should.have.length(1);
359 | //            msgs[0].type.should.eql('connect');
360 | //            msgs[0].endpoint.should.eql('');
361 | //            break;
362 | //
363 | //          case 2:
364 | //            msgs.should.have.length(1);
365 | //            msgs[0].should.eql({
366 | //                type: 'message'
367 | //              , data: 'woot'
368 | //              , endpoint: ''
369 | //            });
370 | //            cl.end();
371 | //        }
372 | //      });
373 | //    });
374 | //  },
375 | //
376 | //  'test sending deliverable volatile json': function (done) {
377 | //    var port = ++ports
378 | //      , cl = client(port)
379 | //      , io = create(cl)
380 | //      , messaged = false;
381 | //
382 | //    io.configure(function () {
383 | //      io.set('close timeout', 0);
384 | //    });
385 | //
386 | //    io.sockets.on('connection', function (socket) {
387 | //      socket.volatile.json.send(['woot']);
388 | //
389 | //      socket.on('disconnect', function () {
390 | //        io.server.close();
391 | //        done();
392 | //      });
393 | //    });
394 | //
395 | //    cl.handshake(function (sid) {
396 | //      cl.data('/socket.io/{protocol}/htmlfile/' + sid, function (msgs, i) {
397 | //        switch (i) {
398 | //          case 1:
399 | //            msgs.should.have.length(1);
400 | //            msgs[0].type.should.eql('connect');
401 | //            msgs[0].endpoint.should.eql('');
402 | //            break;
403 | //
404 | //          case 2:
405 | //            msgs.should.have.length(1);
406 | //            msgs[0].should.eql({
407 | //                type: 'json'
408 | //              , data: ['woot']
409 | //              , endpoint: ''
410 | //            });
411 | //            cl.end();
412 | //        }
413 | //      });
414 | //    });
415 | //  },
416 | 
417 |   'test sending deliverable volatile events': function (done) {
418 |     var port = ++ports
419 |       , cl = client(port)
420 |       , io = create(cl)
421 |       , messaged = false;
422 | 
423 |     io.configure(function () {
424 |       io.set('close timeout', 0);
425 |     });
426 | 
427 |     io.sockets.on('connection', function (socket) {
428 |       console.log('connnected!!!!! Attempting to volitile emit.');
429 | //      socket.volatile.emit('aaa');
430 |       socket.emit('aaa');
431 | 
432 |       socket.on('disconnect', function () {
433 |         io.server.close();
434 |         done();
435 |       });
436 |     });
437 | 
438 |     cl.handshake(function (sid) {
439 |       console.error('client sid', sid);
440 |       cl.end();
441 |       cl.data('/socket.io/{protocol}/htmlfile/' + sid, function (msgs, i) {
442 |         console.log('~~~~~~~~~ messages', msgs, i);
443 |         switch (i) {
444 |           case 1:
445 |             msgs.should.have.length(1);
446 |             msgs[0].type.should.eql('connect');
447 |             msgs[0].endpoint.should.eql('');
448 |             break;
449 | 
450 |           case 2:
451 |             msgs.should.have.length(1);
452 |             msgs[0].should.eql({
453 |                 type: 'event'
454 |               , name: 'aaa'
455 |               , endpoint: ''
456 |               , args: []
457 |             });
458 |             console.log('^^^^^^^^ Client attempting to disconnect.');
459 |             cl.end();
460 |         }
461 |       });
462 |     });
463 |   }
464 | 
465 | };
466 | 


--------------------------------------------------------------------------------
/test/transports.websocket.test.js:
--------------------------------------------------------------------------------
   1 | 
   2 | /*!
   3 |  * socket.io-node
   4 |  * Copyright(c) 2011 LearnBoost 
   5 |  * MIT Licensed
   6 |  */
   7 | 
   8 | /**
   9 |  * Test dependencies.
  10 |  */
  11 | 
  12 | var sio = require('socket.io')
  13 |   , parser = sio.parser
  14 |   , should = require('./common')
  15 |   , HTTPClient = should.HTTPClient
  16 |   , WebSocket = require('websocket-client').WebSocket
  17 |   , ports = 15400;
  18 | 
  19 | /**
  20 |  * WebSocket socket.io client.
  21 |  *
  22 |  * @api private
  23 |  */
  24 | 
  25 | function WSClient (port, sid) {
  26 |   this.sid = sid;
  27 |   this.port = port;
  28 | 
  29 |   WebSocket.call(
  30 |       this
  31 |     , 'ws://localhost:' + port + '/socket.io/' 
  32 |         + sio.protocol + '/websocket/' + sid
  33 |   );
  34 | }
  35 | 
  36 | /**
  37 |  * Inherits from WebSocket.
  38 |  */
  39 | 
  40 | WSClient.prototype.__proto__ = WebSocket.prototype;
  41 | 
  42 | /**
  43 |  * Overrides message event emission.
  44 |  *
  45 |  * @api private
  46 |  */
  47 | 
  48 | WSClient.prototype.emit = function (name) {
  49 |   var args = arguments;
  50 | 
  51 |   if (name == 'message' || name == 'data') {
  52 |     args[1] = parser.decodePacket(args[1].toString());
  53 |   }
  54 | 
  55 |   return WebSocket.prototype.emit.apply(this, arguments);
  56 | };
  57 | 
  58 | /**
  59 |  * Writes a packet
  60 |  */
  61 | 
  62 | WSClient.prototype.packet = function (pack) {
  63 |   this.write(parser.encodePacket(pack));
  64 |   return this;
  65 | };
  66 | 
  67 | /**
  68 |  * Creates a websocket client.
  69 |  *
  70 |  * @api public
  71 |  */
  72 | 
  73 | function websocket (cl, sid) {
  74 |   return new WSClient(cl.port, sid);
  75 | };
  76 | 
  77 | /**
  78 |  * Tests.
  79 |  */
  80 | 
  81 | module.exports = {
  82 | 
  83 |   'test that not responding to a heartbeat drops client': function (done) {
  84 |     var cl = client(++ports)
  85 |       , io = create(cl)
  86 |       , messages = 0
  87 |       , beat;
  88 | 
  89 |     io.configure(function () {
  90 |       io.set('heartbeat interval', .05);
  91 |       io.set('heartbeat timeout', .05);
  92 |       io.set('close timeout', 0);
  93 |     });
  94 | 
  95 |     io.sockets.on('connection', function (socket) {
  96 |       socket.on('disconnect', function (reason) {
  97 | //        beat.should.be.true;
  98 | //        reason.should.eql('heartbeat timeout');
  99 | 
 100 |         cl.end();
 101 |         io.server.close();
 102 |         done();
 103 |       });
 104 |     });
 105 | 
 106 |     cl.handshake(function (sid) {
 107 |       var ws = websocket(cl, sid);
 108 |       ws.on('message', function (packet) {
 109 |         if (++messages == 1) {
 110 |           packet.type.should.eql('connect');
 111 |         } else {
 112 |           packet.type.should.eql('heartbeat');
 113 |           beat = true;
 114 |         }
 115 |       });
 116 |     });
 117 |   },
 118 | 
 119 | //  'test that responding to a heartbeat maintains session': function (done) {
 120 | //    var cl = client(++ports)
 121 | //      , io = create(cl)
 122 | //      , messages = 0
 123 | //      , heartbeats = 0;
 124 | //
 125 | //    io.configure(function () {
 126 | //      io.set('heartbeat interval', .05);
 127 | //      io.set('heartbeat timeout', .05);
 128 | //      io.set('close timeout', 0);
 129 | //    });
 130 | //
 131 | //    io.sockets.on('connection', function (socket) {
 132 | //      socket.on('disconnect', function (reason) {
 133 | //        heartbeats.should.eql(2);
 134 | //        reason.should.eql('heartbeat timeout');
 135 | //
 136 | //        cl.end();
 137 | //        io.server.close();
 138 | //        done();
 139 | //      });
 140 | //    });
 141 | //
 142 | //    cl.handshake(function (sid) {
 143 | //      var ws = websocket(cl, sid);
 144 | //      ws.on('message', function (packet) {
 145 | //        if (++messages == 1) {
 146 | //          packet.type.should.eql('connect');
 147 | //        } else {
 148 | //          packet.type.should.eql('heartbeat');
 149 | //          heartbeats++;
 150 | //
 151 | //          if (heartbeats == 1) {
 152 | //            ws.packet({ type: 'heartbeat' });
 153 | //          }
 154 | //        }
 155 | //      });
 156 | //    });
 157 | //  },
 158 | 
 159 |   'test sending undeliverable volatile messages': function (done) {
 160 |     var cl = client(++ports)
 161 |       , io = create(cl)
 162 |       , messages = 0
 163 |       , messaged = false
 164 |       , s;
 165 | 
 166 |     io.configure(function () {
 167 |       io.set('close timeout', .05);
 168 |     });
 169 | 
 170 |     io.sockets.on('connection', function (socket) {
 171 |       s = socket;
 172 | 
 173 |       socket.on('disconnect', function () {
 174 |         messaged.should.be.false;
 175 |         cl.end();
 176 |         io.server.close();
 177 |         done();
 178 |       });
 179 |     });
 180 | 
 181 |     cl.handshake(function (sid) {
 182 |       var ws = websocket(cl, sid);
 183 |       ws.on('message', function (msg) {
 184 |         msg.type.should.eql('connect');
 185 |         ws.finishClose();
 186 | 
 187 |         setTimeout(function () {
 188 |           s.volatile.send('ah wha wha');
 189 | 
 190 |           ws = websocket(cl, sid);
 191 |           ws.on('message', function () {
 192 |             messaged = true;
 193 |           });
 194 | 
 195 |           setTimeout(function () {
 196 |             ws.finishClose();
 197 |           }, 10);
 198 |         }, 10);
 199 |       });
 200 |     });
 201 |   },
 202 | 
 203 |   'test sending undeliverable volatile json': function (done) {
 204 |     var cl = client(++ports)
 205 |       , io = create(cl)
 206 |       , messaged = false
 207 |       , s;
 208 | 
 209 |     io.configure(function () {
 210 |       io.set('close timeout', .05);
 211 |     });
 212 | 
 213 |     io.sockets.on('connection', function (socket) {
 214 |       s = socket;
 215 | 
 216 |       socket.on('disconnect', function () {
 217 |         messaged.should.be.false;
 218 |         cl.end();
 219 |         io.server.close();
 220 |         done();
 221 |       });
 222 |     });
 223 | 
 224 |     cl.handshake(function (sid) {
 225 |       var ws = websocket(cl, sid);
 226 |       ws.on('message', function () {
 227 |         ws.finishClose();
 228 | 
 229 |         setTimeout(function () {
 230 |           s.volatile.json.send({ a: 'b' });
 231 | 
 232 |           ws = websocket(cl, sid);
 233 |           ws.on('message', function () {
 234 |             messaged = true;
 235 |           });
 236 | 
 237 |           setTimeout(function () {
 238 |             ws.finishClose();
 239 |           }, 10);
 240 |         }, 10);
 241 |       });
 242 |     });
 243 |   },
 244 | 
 245 |   'test sending undeliverable volatile events': function (done) {
 246 |     var cl = client(++ports)
 247 |       , io = create(cl)
 248 |       , messaged = false
 249 |       , s;
 250 | 
 251 |     io.configure(function () {
 252 |       io.set('close timeout', .05);
 253 |     });
 254 | 
 255 |     io.sockets.on('connection', function (socket) {
 256 |       s = socket;
 257 | 
 258 |       socket.on('disconnect', function () {
 259 |         messaged.should.be.false;
 260 |         cl.end();
 261 |         io.server.close();
 262 |         done();
 263 |       });
 264 |     });
 265 | 
 266 |     cl.handshake(function (sid) {
 267 |       var ws = websocket(cl, sid);
 268 |       ws.on('message', function () {
 269 |         ws.finishClose();
 270 | 
 271 |         setTimeout(function () {
 272 |           s.volatile.emit({ a: 'b' });
 273 | 
 274 |           ws = websocket(cl, sid);
 275 |           ws.on('message', function () {
 276 |             messaged = true;
 277 |           });
 278 | 
 279 |           setTimeout(function () {
 280 |             ws.finishClose();
 281 |           }, 10);
 282 |         }, 10);
 283 |       });
 284 |     });
 285 |   },
 286 | 
 287 |   'test sending deliverable volatile messages': function (done) {
 288 |     var cl = client(++ports)
 289 |       , io = create(cl)
 290 |       , messages = 0
 291 |       , messaged = false;
 292 | 
 293 |     io.configure(function () {
 294 |       io.set('close timeout', .05);
 295 |     });
 296 | 
 297 |     io.sockets.on('connection', function (socket) {
 298 |       socket.volatile.send('tobi');
 299 | 
 300 |       socket.on('disconnect', function () {
 301 |         messaged.should.be.true;
 302 |         cl.end();
 303 |         io.server.close();
 304 |         done();
 305 |       });
 306 |     });
 307 | 
 308 |     cl.handshake(function (sid) {
 309 |       var ws = websocket(cl, sid);
 310 |       ws.on('message', function (msg) {
 311 |         if (++messages == 1) {
 312 |           msg.type.should.eql('connect');
 313 |         } else {
 314 |           msg.should.eql({
 315 |               type: 'message'
 316 |             , data: 'tobi'
 317 |             , endpoint: ''
 318 |           });
 319 |           messaged = true;
 320 |           ws.finishClose();
 321 |         }
 322 |       });
 323 |     });
 324 |   },
 325 | 
 326 |   'test sending deliverable volatile json': function (done) {
 327 |     var cl = client(++ports)
 328 |       , io = create(cl)
 329 |       , messaged = false;
 330 | 
 331 |     io.configure(function () {
 332 |       io.set('close timeout', .05);
 333 |     });
 334 | 
 335 |     io.sockets.on('connection', function (socket) {
 336 |       socket.volatile.json.send([1, 2, 3]);
 337 | 
 338 |       socket.on('disconnect', function () {
 339 |         messaged.should.be.true;
 340 |         cl.end();
 341 |         io.server.close();
 342 |         done();
 343 |       });
 344 |     });
 345 | 
 346 |     cl.handshake(function (sid) {
 347 |       var ws = websocket(cl, sid);
 348 |       ws.on('message', function (msg) {
 349 |         if (!ws.connected) {
 350 |           msg.type.should.eql('connect');
 351 |           ws.connected = true;
 352 |         } else {
 353 |           msg.should.eql({
 354 |               type: 'json'
 355 |             , data: [1, 2, 3]
 356 |             , endpoint: ''
 357 |           });
 358 |           messaged = true;
 359 |           ws.finishClose();
 360 |         }
 361 |       });
 362 |     });
 363 |   },
 364 | 
 365 |   'test sending deliverable volatile events': function (done) {
 366 |     var cl = client(++ports)
 367 |       , io = create(cl)
 368 |       , messaged = false;
 369 | 
 370 |     io.configure(function () {
 371 |       io.set('close timeout', .05);
 372 |     });
 373 | 
 374 |     io.sockets.on('connection', function (socket) {
 375 |       socket.volatile.emit('tobi');
 376 | 
 377 |       socket.on('disconnect', function () {
 378 |         messaged.should.be.true;
 379 |         cl.end();
 380 |         io.server.close();
 381 |         done();
 382 |       });
 383 |     });
 384 | 
 385 |     cl.handshake(function (sid) {
 386 |       var ws = websocket(cl, sid);
 387 |       ws.on('message', function (msg) {
 388 |         if (!ws.connected) {
 389 |           msg.type.should.eql('connect');
 390 |           ws.connected = true;
 391 |         } else {
 392 |           msg.should.eql({
 393 |               type: 'event'
 394 |             , name: 'tobi'
 395 |             , endpoint: ''
 396 |             , args: []
 397 |           });
 398 |           messaged = true;
 399 |           ws.finishClose();
 400 |         }
 401 |       });
 402 |     });
 403 |   },
 404 | 
 405 |   'test sending to all clients in a namespace': function (done) {
 406 |     var port = ++ports
 407 |       , cl1 = client(port)
 408 |       , cl2 = client(port)
 409 |       , io = create(cl1)
 410 |       , messages = 0
 411 |       , connections = 0
 412 |       , disconnections = 0;
 413 | 
 414 |     io.configure(function () {
 415 |       io.set('close timeout', 0);
 416 |     });
 417 | 
 418 |     io.sockets.on('connection', function (socket) {
 419 |       connections++;
 420 | 
 421 |       if (connections == 2) {
 422 |         io.sockets.send('yup');
 423 |       }
 424 | 
 425 |       socket.on('disconnect', function () {
 426 |         disconnections++;
 427 | 
 428 |         if (disconnections == 2) {
 429 |           messages.should.eql(2);
 430 |           cl1.end();
 431 |           cl2.end();
 432 |           io.server.close();
 433 |           done();
 434 |         }
 435 |       });
 436 |     });
 437 | 
 438 |     cl1.handshake(function (sid) {
 439 |       var ws1 = websocket(cl1, sid);
 440 |       ws1.on('message', function (msg) {
 441 |         if (!ws1.connected) {
 442 |           msg.type.should.eql('connect');
 443 |           ws1.connected = true;
 444 |         } else {
 445 |           msg.should.eql({
 446 |               type: 'message'
 447 |             , data: 'yup'
 448 |             , endpoint: ''
 449 |           });
 450 | 
 451 |           messages++;
 452 |           ws1.finishClose();
 453 |         }
 454 |       });
 455 |     });
 456 | 
 457 |     cl2.handshake(function (sid) {
 458 |       var ws2 = websocket(cl2, sid);
 459 |       ws2.on('message', function (msg) {
 460 |         if (!ws2.connected) {
 461 |           msg.type.should.eql('connect');
 462 |           ws2.connected = true;
 463 |         } else {
 464 |           msg.should.eql({
 465 |               type: 'message'
 466 |             , data: 'yup'
 467 |             , endpoint: ''
 468 |           });
 469 | 
 470 |           messages++;
 471 |           ws2.finishClose();
 472 |         }
 473 |       });
 474 |     });
 475 |   },
 476 | 
 477 |   'test sending json to all clients in a namespace': function (done) {
 478 |     var port = ++ports
 479 |       , cl1 = client(port)
 480 |       , cl2 = client(port)
 481 |       , io = create(cl1)
 482 |       , messages = 0
 483 |       , connections = 0
 484 |       , disconnections = 0;
 485 | 
 486 |     io.configure(function () {
 487 |       io.set('close timeout', 0);
 488 |     });
 489 | 
 490 |     io.sockets.on('connection', function (socket) {
 491 |       connections++;
 492 | 
 493 |       if (connections == 2) {
 494 |         io.sockets.json.send({ a: 'b' });
 495 |       }
 496 | 
 497 |       socket.on('disconnect', function () {
 498 |         disconnections++;
 499 | 
 500 |         if (disconnections == 2) {
 501 |           messages.should.eql(2);
 502 |           cl1.end();
 503 |           cl2.end();
 504 |           io.server.close();
 505 |           done();
 506 |         }
 507 |       });
 508 |     });
 509 | 
 510 |     cl1.handshake(function (sid) {
 511 |       var ws1 = websocket(cl1, sid);
 512 |       ws1.on('message', function (msg) {
 513 |         if (!ws1.connected) {
 514 |           msg.type.should.eql('connect');
 515 |           ws1.connected = true;
 516 |         } else {
 517 |           msg.should.eql({
 518 |               type: 'json'
 519 |             , data: { a: 'b' }
 520 |             , endpoint: ''
 521 |           });
 522 | 
 523 |           messages++;
 524 |           ws1.finishClose();
 525 |         }
 526 |       });
 527 |     });
 528 | 
 529 |     cl2.handshake(function (sid) {
 530 |       var ws2 = websocket(cl2, sid);
 531 |       ws2.on('message', function (msg) {
 532 |         if (!ws2.connected) {
 533 |           msg.type.should.eql('connect');
 534 |           ws2.connected = true;
 535 |         } else {
 536 |           msg.should.eql({
 537 |               type: 'json'
 538 |             , data: { a: 'b' }
 539 |             , endpoint: ''
 540 |           });
 541 | 
 542 |           messages++;
 543 |           ws2.finishClose();
 544 |         }
 545 |       });
 546 |     });
 547 |   },
 548 | 
 549 | //  'test emitting to all clients in a namespace': function (done) {
 550 | //    var port = ++ports
 551 | //      , cl1 = client(port)
 552 | //      , cl2 = client(port)
 553 | //      , io = create(cl1)
 554 | //      , messages = 0
 555 | //      , connections = 0
 556 | //      , disconnections = 0;
 557 | //
 558 | //    io.configure(function () {
 559 | //      io.set('close timeout', 0);
 560 | //    });
 561 | //
 562 | //    io.sockets.on('connection', function (socket) {
 563 | //      connections++;
 564 | //
 565 | //      if (connections == 2) {
 566 | //        io.sockets.emit('tobi', 'rapture');
 567 | //      }
 568 | //
 569 | //      socket.on('disconnect', function () {
 570 | //        disconnections++;
 571 | //
 572 | //        if (disconnections == 2) {
 573 | //          messages.should.eql(2);
 574 | //          cl1.end();
 575 | //          cl2.end();
 576 | //          io.server.close();
 577 | //          done();
 578 | //        }
 579 | //      });
 580 | //    });
 581 | //
 582 | //    cl1.handshake(function (sid) {
 583 | //      var ws1 = websocket(cl1, sid);
 584 | //      ws1.on('message', function (msg) {
 585 | //        if (!ws1.connected) {
 586 | //          msg.type.should.eql('connect');
 587 | //          ws1.connected = true;
 588 | //        } else {
 589 | //          msg.should.eql({
 590 | //              type: 'event'
 591 | //            , name: 'tobi'
 592 | //            , args: ['rapture']
 593 | //            , endpoint: ''
 594 | //          });
 595 | //
 596 | //          messages++;
 597 | //          ws1.finishClose();
 598 | //        }
 599 | //      });
 600 | //    });
 601 | //
 602 | //    cl2.handshake(function (sid) {
 603 | //      var ws2 = websocket(cl2, sid);
 604 | //      ws2.on('message', function (msg) {
 605 | //        if (!ws2.connected) {
 606 | //          msg.type.should.eql('connect');
 607 | //          ws2.connected = true;
 608 | //        } else {
 609 | //          msg.should.eql({
 610 | //              type: 'event'
 611 | //            , name: 'tobi'
 612 | //            , args: ['rapture']
 613 | //            , endpoint: ''
 614 | //          });
 615 | //
 616 | //          messages++;
 617 | //          ws2.finishClose();
 618 | //        }
 619 | //      });
 620 | //    });
 621 | //  },
 622 | 
 623 |   'test sending to all clients in a room': function (done) {
 624 |     var port = ++ports
 625 |       , cl1 = client(port)
 626 |       , cl2 = client(port)
 627 |       , cl3 = client(port)
 628 |       , io = create(cl1)
 629 |       , messages = 0
 630 |       , joins = 0
 631 |       , connections = 0
 632 |       , disconnections = 0;
 633 | 
 634 |     io.configure(function () {
 635 |       io.set('close timeout', 0);
 636 |     });
 637 | 
 638 |     io.sockets.on('connection', function (socket) {
 639 |       connections++;
 640 | 
 641 |       if (connections != 3) {
 642 |         socket.join('woot', function () {
 643 |           joins++;
 644 | 
 645 |           if (joins == 2) {
 646 |             setTimeout(function () {
 647 |               connections.should.eql(3);
 648 |               io.sockets.in('woot').send('hahaha');
 649 |             }, 20);
 650 |           }
 651 |         });
 652 |       }
 653 | 
 654 |       socket.on('disconnect', function () {
 655 |         disconnections++;
 656 | 
 657 |         if (disconnections == 3) {
 658 |           messages.should.eql(2);
 659 |           cl1.end();
 660 |           cl2.end();
 661 |           cl3.end();
 662 |           io.server.close();
 663 |           done();
 664 |         }
 665 |       });
 666 |     });
 667 | 
 668 |     cl1.handshake(function (sid) {
 669 |       var ws1 = websocket(cl1, sid);
 670 |       ws1.on('message', function (msg) {
 671 |         if (!ws1.connected) {
 672 |           msg.type.should.eql('connect');
 673 |           ws1.connected = true;
 674 |         } else {
 675 |           msg.should.eql({
 676 |               type: 'message'
 677 |             , data: 'hahaha'
 678 |             , endpoint: ''
 679 |           });
 680 | 
 681 |           messages++;
 682 |         }
 683 |       });
 684 | 
 685 |       setTimeout(function () {
 686 |         ws1.finishClose();
 687 |       }, 50);
 688 |     });
 689 | 
 690 |     cl2.handshake(function (sid) {
 691 |       var ws2 = websocket(cl2, sid);
 692 |       ws2.on('message', function (msg) {
 693 |         if (!ws2.connected) {
 694 |           msg.type.should.eql('connect');
 695 |           ws2.connected = true;
 696 |         } else {
 697 |           msg.should.eql({
 698 |               type: 'message'
 699 |             , data: 'hahaha'
 700 |             , endpoint: ''
 701 |           });
 702 | 
 703 |           messages++;
 704 |         }
 705 |       });
 706 | 
 707 |       setTimeout(function () {
 708 |         ws2.finishClose();
 709 |       }, 50);
 710 |     });
 711 | 
 712 |     cl3.handshake(function (sid) {
 713 |       var ws3 = websocket(cl3, sid);
 714 |       ws3.on('message', function (msg) {
 715 |         if (!ws3.connected) {
 716 |           msg.type.should.eql('connect');
 717 |           ws3.connected = true;
 718 |         } else {
 719 |           msg.should.eql({
 720 |               type: 'message'
 721 |             , data: 'hahaha'
 722 |             , endpoint: ''
 723 |           });
 724 | 
 725 |           messages++;
 726 |         }
 727 |       });
 728 | 
 729 |       setTimeout(function () {
 730 |         ws3.finishClose();
 731 |       }, 50);
 732 |     });
 733 |   },
 734 | 
 735 |   'test sending json to all clients in a room': function (done) {
 736 |     var port = ++ports
 737 |       , cl1 = client(port)
 738 |       , cl2 = client(port)
 739 |       , cl3 = client(port)
 740 |       , io = create(cl1)
 741 |       , messages = 0
 742 |       , joins = 0
 743 |       , connections = 0
 744 |       , disconnections = 0;
 745 | 
 746 |     io.configure(function () {
 747 |       io.set('close timeout', 0);
 748 |     });
 749 | 
 750 |     io.sockets.on('connection', function (socket) {
 751 |       connections++;
 752 | 
 753 |       if (connections != 3) {
 754 |         socket.join('woot', function () {
 755 |           joins++;
 756 | 
 757 |           if (joins == 2) {
 758 |             setTimeout(function () {
 759 |               connections.should.eql(3);
 760 |               io.sockets.in('woot').json.send(123);
 761 |             }, 20);
 762 |           }
 763 |         });
 764 |       }
 765 | 
 766 |       socket.on('disconnect', function () {
 767 |         disconnections++;
 768 | 
 769 |         if (disconnections == 3) {
 770 |           messages.should.eql(2);
 771 |           cl1.end();
 772 |           cl2.end();
 773 |           cl3.end();
 774 |           io.server.close();
 775 |           done();
 776 |         }
 777 |       });
 778 |     });
 779 | 
 780 |     cl1.handshake(function (sid) {
 781 |       var ws1 = websocket(cl1, sid);
 782 |       ws1.on('message', function (msg) {
 783 |         if (!ws1.connected) {
 784 |           msg.type.should.eql('connect');
 785 |           ws1.connected = true;
 786 |         } else {
 787 |           msg.should.eql({
 788 |               type: 'json'
 789 |             , data: 123
 790 |             , endpoint: ''
 791 |           });
 792 | 
 793 |           messages++;
 794 |         }
 795 |       });
 796 | 
 797 |       setTimeout(function () {
 798 |         ws1.finishClose();
 799 |       }, 50);
 800 |     });
 801 | 
 802 |     cl2.handshake(function (sid) {
 803 |       var ws2 = websocket(cl2, sid);
 804 |       ws2.on('message', function (msg) {
 805 |         if (!ws2.connected) {
 806 |           msg.type.should.eql('connect');
 807 |           ws2.connected = true;
 808 |         } else {
 809 |           msg.should.eql({
 810 |               type: 'json'
 811 |             , data: 123
 812 |             , endpoint: ''
 813 |           });
 814 | 
 815 |           messages++;
 816 |         }
 817 |       });
 818 | 
 819 |       setTimeout(function () {
 820 |         ws2.finishClose();
 821 |       }, 50);
 822 |     });
 823 | 
 824 |     cl3.handshake(function (sid) {
 825 |       var ws3 = websocket(cl3, sid);
 826 |       ws3.on('message', function (msg) {
 827 |         if (!ws3.connected) {
 828 |           msg.type.should.eql('connect');
 829 |           ws3.connected = true;
 830 |         } else {
 831 |           msg.should.eql({
 832 |               type: 'json'
 833 |             , data: 123
 834 |             , endpoint: ''
 835 |           });
 836 | 
 837 |           messages++;
 838 |         }
 839 |       });
 840 | 
 841 |       setTimeout(function () {
 842 |         ws3.finishClose();
 843 |       }, 50);
 844 |     });
 845 |   },
 846 | 
 847 |   'test emitting to all clients in a room': function (done) {
 848 |     var port = ++ports
 849 |       , cl1 = client(port)
 850 |       , cl2 = client(port)
 851 |       , cl3 = client(port)
 852 |       , io = create(cl1)
 853 |       , messages = 0
 854 |       , joins = 0
 855 |       , connections = 0
 856 |       , disconnections = 0;
 857 | 
 858 |     io.configure(function () {
 859 |       io.set('close timeout', 0);
 860 |     });
 861 | 
 862 |     io.sockets.on('connection', function (socket) {
 863 |       connections++;
 864 | 
 865 |       if (connections != 3) {
 866 |         socket.join('woot', function () {
 867 |           joins++;
 868 | 
 869 |           if (joins == 2) {
 870 |             setTimeout(function () {
 871 |               connections.should.eql(3);
 872 |               io.sockets.in('woot').emit('locki');
 873 |             }, 20);
 874 |           }
 875 |         });
 876 |       }
 877 | 
 878 |       socket.on('disconnect', function () {
 879 |         disconnections++;
 880 | 
 881 |         if (disconnections == 3) {
 882 |           messages.should.eql(2);
 883 |           cl1.end();
 884 |           cl2.end();
 885 |           cl3.end();
 886 |           io.server.close();
 887 |           done();
 888 |         }
 889 |       });
 890 |     });
 891 | 
 892 |     cl1.handshake(function (sid) {
 893 |       var ws1 = websocket(cl1, sid);
 894 |       ws1.on('message', function (msg) {
 895 |         if (!ws1.connected) {
 896 |           msg.type.should.eql('connect');
 897 |           ws1.connected = true;
 898 |         } else {
 899 |           msg.should.eql({
 900 |               type: 'event'
 901 |             , name: 'locki'
 902 |             , args: []
 903 |             , endpoint: ''
 904 |           });
 905 | 
 906 |           messages++;
 907 |         }
 908 |       });
 909 | 
 910 |       setTimeout(function () {
 911 |         ws1.finishClose();
 912 |       }, 50);
 913 |     });
 914 | 
 915 |     cl2.handshake(function (sid) {
 916 |       var ws2 = websocket(cl2, sid);
 917 |       ws2.on('message', function (msg) {
 918 |         if (!ws2.connected) {
 919 |           msg.type.should.eql('connect');
 920 |           ws2.connected = true;
 921 |         } else {
 922 |           msg.should.eql({
 923 |               type: 'event'
 924 |             , name: 'locki'
 925 |             , args: []
 926 |             , endpoint: ''
 927 |           });
 928 | 
 929 |           messages++;
 930 |         }
 931 |       });
 932 | 
 933 |       setTimeout(function () {
 934 |         ws2.finishClose();
 935 |       }, 50);
 936 |     });
 937 | 
 938 |     cl3.handshake(function (sid) {
 939 |       var ws3 = websocket(cl3, sid);
 940 |       ws3.on('message', function (msg) {
 941 |         if (!ws3.connected) {
 942 |           msg.type.should.eql('connect');
 943 |           ws3.connected = true;
 944 |         } else {
 945 |           msg.should.eql({
 946 |               type: 'event'
 947 |             , name: 'locki'
 948 |             , args: []
 949 |             , endpoint: ''
 950 |           });
 951 | 
 952 |           messages++;
 953 |         }
 954 |       });
 955 | 
 956 |       setTimeout(function () {
 957 |         ws3.finishClose();
 958 |       }, 50);
 959 |     });
 960 |   },
 961 | 
 962 |   'test message with broadcast flag': function (done) {
 963 |     var port = ++ports
 964 |       , cl1 = client(port)
 965 |       , cl2 = client(port)
 966 |       , cl3 = client(port)
 967 |       , io = create(cl1)
 968 |       , messages = 0
 969 |       , disconnections = 0;
 970 | 
 971 |     io.configure(function () {
 972 |       io.set('close timeout', 0);
 973 |     });
 974 | 
 975 |     io.sockets.on('connection', function (socket) {
 976 |       socket.on('trigger broadcast', function () {
 977 |         socket.broadcast.send('boom');
 978 |       });
 979 | 
 980 |       socket.on('disconnect', function () {
 981 |         disconnections++;
 982 | 
 983 |         if (disconnections == 3) {
 984 |           messages.should.eql(2);
 985 |           cl1.end();
 986 |           cl2.end();
 987 |           cl3.end();
 988 |           io.server.close();
 989 |           done();
 990 |         }
 991 |       });
 992 |     });
 993 | 
 994 |     cl1.handshake(function (sid) {
 995 |       var ws1 = websocket(cl1, sid);
 996 |       ws1.on('message', function (msg) {
 997 |         if (!ws1.connected) {
 998 |           msg.type.should.eql('connect');
 999 |           ws1.connected = true;
1000 |         } else {
1001 |           msg.should.eql({
1002 |               type: 'message'
1003 |             , data: 'boom'
1004 |             , endpoint: ''
1005 |           });
1006 | 
1007 |           messages++;
1008 |           ws1.finishClose();
1009 |         }
1010 |       });
1011 |     });
1012 | 
1013 |     cl2.handshake(function (sid) {
1014 |       var ws2 = websocket(cl2, sid);
1015 |       ws2.on('message', function (msg) {
1016 |         if (!ws2.connected) {
1017 |           msg.type.should.eql('connect');
1018 |           ws2.connected = true;
1019 |         } else {
1020 |           msg.should.eql({
1021 |               type: 'message'
1022 |             , data: 'boom'
1023 |             , endpoint: ''
1024 |           });
1025 | 
1026 |           messages++;
1027 |           ws2.finishClose();
1028 |         }
1029 |       });
1030 |     });
1031 | 
1032 |     cl3.handshake(function (sid) {
1033 |       var ws3 = websocket(cl3, sid);
1034 |       ws3.on('open', function () {
1035 |         ws3.packet({
1036 |             type: 'event'
1037 |           , name: 'trigger broadcast'
1038 |           , endpoint: ''
1039 |         });
1040 | 
1041 |         setTimeout(function () {
1042 |           ws3.finishClose();
1043 |         }, 50);
1044 |       });
1045 | 
1046 |       ws3.on('message', function (msg) {
1047 |         if (!ws3.connected) {
1048 |           msg.type.should.eql('connect');
1049 |           ws3.connected = true;
1050 |         } else {
1051 |           throw new Error('we shouldnt get a message here');
1052 |         }
1053 |       });
1054 |     });
1055 |   },
1056 | 
1057 |   'test json with broadcast flag': function (done) {
1058 |     var port = ++ports
1059 |       , cl1 = client(port)
1060 |       , cl2 = client(port)
1061 |       , cl3 = client(port)
1062 |       , io = create(cl1)
1063 |       , messages = 0
1064 |       , disconnections = 0;
1065 | 
1066 |     io.configure(function () {
1067 |       io.set('close timeout', 0);
1068 |     });
1069 | 
1070 |     io.sockets.on('connection', function (socket) {
1071 |       socket.on('trigger broadcast', function () {
1072 |         socket.broadcast.json.send([1, 2, 3]);
1073 |       });
1074 | 
1075 |       socket.on('disconnect', function () {
1076 |         disconnections++;
1077 | 
1078 |         if (disconnections == 3) {
1079 |           messages.should.eql(2);
1080 |           cl1.end();
1081 |           cl2.end();
1082 |           cl3.end();
1083 |           io.server.close();
1084 |           done();
1085 |         }
1086 |       });
1087 |     });
1088 | 
1089 |     cl1.handshake(function (sid) {
1090 |       var ws1 = websocket(cl1, sid);
1091 |       ws1.on('message', function (msg) {
1092 |         if (!ws1.connected) {
1093 |           msg.type.should.eql('connect');
1094 |           ws1.connected = true;
1095 |         } else {
1096 |           msg.should.eql({
1097 |               type: 'json'
1098 |             , data: [1, 2, 3]
1099 |             , endpoint: ''
1100 |           });
1101 | 
1102 |           messages++;
1103 |           ws1.finishClose();
1104 |         }
1105 |       });
1106 |     });
1107 | 
1108 |     cl2.handshake(function (sid) {
1109 |       var ws2 = websocket(cl2, sid);
1110 |       ws2.on('message', function (msg) {
1111 |         if (!ws2.connected) {
1112 |           msg.type.should.eql('connect');
1113 |           ws2.connected = true;
1114 |         } else {
1115 |           msg.should.eql({
1116 |               type: 'json'
1117 |             , data: [1, 2, 3]
1118 |             , endpoint: ''
1119 |           });
1120 | 
1121 |           messages++;
1122 |           ws2.finishClose();
1123 |         }
1124 |       });
1125 |     });
1126 | 
1127 |     cl3.handshake(function (sid) {
1128 |       var ws3 = websocket(cl3, sid);
1129 |       ws3.on('open', function () {
1130 |         ws3.packet({
1131 |             type: 'event'
1132 |           , name: 'trigger broadcast'
1133 |           , endpoint: ''
1134 |         });
1135 | 
1136 |         setTimeout(function () {
1137 |           ws3.finishClose();
1138 |         }, 50);
1139 |       });
1140 | 
1141 |       ws3.on('message', function (msg) {
1142 |         if (!ws3.connected) {
1143 |           msg.type.should.eql('connect');
1144 |           ws3.connected = true;
1145 |         } else {
1146 |           throw new Error('we shouldnt get a message here');
1147 |         }
1148 |       });
1149 |     });
1150 |   },
1151 | 
1152 |   'test event with broadcast flag': function (done) {
1153 |     var port = ++ports
1154 |       , cl1 = client(port)
1155 |       , cl2 = client(port)
1156 |       , cl3 = client(port)
1157 |       , io = create(cl1)
1158 |       , messages = 0
1159 |       , disconnections = 0;
1160 | 
1161 |     io.configure(function () {
1162 |       io.set('close timeout', 0);
1163 |     });
1164 | 
1165 |     io.sockets.on('connection', function (socket) {
1166 |       socket.on('trigger broadcast', function () {
1167 |         socket.broadcast.emit('hey', 'arnold');
1168 |       });
1169 | 
1170 |       socket.on('disconnect', function () {
1171 |         disconnections++;
1172 | 
1173 |         if (disconnections == 3) {
1174 |           messages.should.eql(2);
1175 |           cl1.end();
1176 |           cl2.end();
1177 |           cl3.end();
1178 |           io.server.close();
1179 |           done();
1180 |         }
1181 |       });
1182 |     });
1183 | 
1184 |     cl1.handshake(function (sid) {
1185 |       var ws1 = websocket(cl1, sid);
1186 |       ws1.on('message', function (msg) {
1187 |         if (!ws1.connected) {
1188 |           msg.type.should.eql('connect');
1189 |           ws1.connected = true;
1190 |         } else {
1191 |           msg.should.eql({
1192 |               type: 'event'
1193 |             , name: 'hey'
1194 |             , args: ['arnold']
1195 |             , endpoint: ''
1196 |           });
1197 | 
1198 |           messages++;
1199 |           ws1.finishClose();
1200 |         }
1201 |       });
1202 |     });
1203 | 
1204 |     cl2.handshake(function (sid) {
1205 |       var ws2 = websocket(cl2, sid);
1206 |       ws2.on('message', function (msg) {
1207 |         if (!ws2.connected) {
1208 |           msg.type.should.eql('connect');
1209 |           ws2.connected = true;
1210 |         } else {
1211 |           msg.should.eql({
1212 |               type: 'event'
1213 |             , name: 'hey'
1214 |             , args: ['arnold']
1215 |             , endpoint: ''
1216 |           });
1217 | 
1218 |           messages++;
1219 |           ws2.finishClose();
1220 |         }
1221 |       });
1222 |     });
1223 | 
1224 |     cl3.handshake(function (sid) {
1225 |       var ws3 = websocket(cl3, sid);
1226 |       ws3.on('open', function () {
1227 |         ws3.packet({
1228 |             type: 'event'
1229 |           , name: 'trigger broadcast'
1230 |           , endpoint: ''
1231 |         });
1232 | 
1233 |         setTimeout(function () {
1234 |           ws3.finishClose();
1235 |         }, 50);
1236 |       });
1237 | 
1238 |       ws3.on('message', function (msg) {
1239 |         if (!ws3.connected) {
1240 |           msg.type.should.eql('connect');
1241 |           ws3.connected = true;
1242 |         } else {
1243 |           throw new Error('we shouldnt get a message here');
1244 |         }
1245 |       });
1246 |     });
1247 |   },
1248 | 
1249 |   'test message with broadcast flag and to()': function (done) {
1250 |     var port = ++ports
1251 |       , cl1 = client(port)
1252 |       , cl2 = client(port)
1253 |       , cl3 = client(port)
1254 |       , io = create(cl1)
1255 |       , messages = 0
1256 |       , connections = 0
1257 |       , disconnections = 0;
1258 | 
1259 |     io.configure(function () {
1260 |       io.set('close timeout', 0);
1261 |     });
1262 | 
1263 |     io.sockets.on('connection', function (socket) {
1264 |       connections++;
1265 | 
1266 |       if (connections == 1)
1267 |         socket.join('losers');
1268 | 
1269 |       socket.on('trigger broadcast', function () {
1270 |         socket.broadcast.to('losers').send('boom');
1271 |       });
1272 | 
1273 |       socket.on('disconnect', function () {
1274 |         disconnections++;
1275 | 
1276 |         if (disconnections == 3) {
1277 |           messages.should.eql(1);
1278 |           cl1.end();
1279 |           cl2.end();
1280 |           cl3.end();
1281 |           io.server.close();
1282 |           done();
1283 |         }
1284 |       });
1285 |     });
1286 | 
1287 |     cl1.handshake(function (sid) {
1288 |       var ws1 = websocket(cl1, sid);
1289 |       ws1.on('message', function (msg) {
1290 |         if (!ws1.connected) {
1291 |           msg.type.should.eql('connect');
1292 |           ws1.connected = true;
1293 |         } else {
1294 |           msg.should.eql({
1295 |               type: 'message'
1296 |             , data: 'boom'
1297 |             , endpoint: ''
1298 |           });
1299 | 
1300 |           messages++;
1301 |         }
1302 |       });
1303 | 
1304 |       ws1.on('open', function () {
1305 |         cl2.handshake(function (sid) {
1306 |           var ws2 = websocket(cl2, sid);
1307 |           ws2.on('message', function (msg) {
1308 |             if (!ws2.connected) {
1309 |               msg.type.should.eql('connect');
1310 |               ws2.connected = true;
1311 |             } else {
1312 |               throw new Error('This socket shouldnt get a message');
1313 |             }
1314 |           });
1315 | 
1316 |           ws2.on('open', function () {
1317 |             cl3.handshake(function (sid) {
1318 |               var ws3 = websocket(cl3, sid);
1319 |               ws3.on('open', function () {
1320 |                 ws3.packet({
1321 |                     type: 'event'
1322 |                   , name: 'trigger broadcast'
1323 |                   , endpoint: ''
1324 |                 });
1325 | 
1326 |                 setTimeout(function () {
1327 |                   ws1.finishClose();
1328 |                   ws2.finishClose();
1329 |                   ws3.finishClose();
1330 |                 }, 50);
1331 |               });
1332 | 
1333 |               ws3.on('message', function (msg) {
1334 |                 if (!ws3.connected) {
1335 |                   msg.type.should.eql('connect');
1336 |                   ws3.connected = true;
1337 |                 } else {
1338 |                   throw new Error('we shouldnt get a message here');
1339 |                 }
1340 |               });
1341 |             });
1342 |           });
1343 |         });
1344 |       });
1345 |     });
1346 |   },
1347 | 
1348 |   'test json with broadcast flag and to()': function (done) {
1349 |     var port = ++ports
1350 |       , cl1 = client(port)
1351 |       , cl2 = client(port)
1352 |       , cl3 = client(port)
1353 |       , io = create(cl1)
1354 |       , messages = 0
1355 |       , connections = 0
1356 |       , disconnections = 0;
1357 | 
1358 |     io.configure(function () {
1359 |       io.set('close timeout', 0);
1360 |     });
1361 | 
1362 |     io.sockets.on('connection', function (socket) {
1363 |       connections++;
1364 | 
1365 |       if (connections == 1)
1366 |         socket.join('losers');
1367 | 
1368 |       socket.on('trigger broadcast', function () {
1369 |         socket.broadcast.json.to('losers').send({ hello: 'world' });
1370 |       });
1371 | 
1372 |       socket.on('disconnect', function () {
1373 |         disconnections++;
1374 | 
1375 |         if (disconnections == 3) {
1376 |           messages.should.eql(1);
1377 |           cl1.end();
1378 |           cl2.end();
1379 |           cl3.end();
1380 |           io.server.close();
1381 |           done();
1382 |         }
1383 |       });
1384 |     });
1385 | 
1386 |     cl1.handshake(function (sid) {
1387 |       var ws1 = websocket(cl1, sid);
1388 |       ws1.on('message', function (msg) {
1389 |         if (!ws1.connected) {
1390 |           msg.type.should.eql('connect');
1391 |           ws1.connected = true;
1392 |         } else {
1393 |           msg.should.eql({
1394 |               type: 'json'
1395 |             , data: { hello: 'world' }
1396 |             , endpoint: ''
1397 |           });
1398 | 
1399 |           messages++;
1400 |         }
1401 |       });
1402 | 
1403 |       ws1.on('open', function () {
1404 |         cl2.handshake(function (sid) {
1405 |           var ws2 = websocket(cl2, sid);
1406 |           ws2.on('message', function (msg) {
1407 |             if (!ws2.connected) {
1408 |               msg.type.should.eql('connect');
1409 |               ws2.connected = true;
1410 |             } else {
1411 |               throw new Error('This socket shouldnt get a message');
1412 |             }
1413 |           });
1414 | 
1415 |           ws2.on('open', function () {
1416 |             cl3.handshake(function (sid) {
1417 |               var ws3 = websocket(cl3, sid);
1418 |               ws3.on('open', function () {
1419 |                 ws3.packet({
1420 |                     type: 'event'
1421 |                   , name: 'trigger broadcast'
1422 |                   , endpoint: ''
1423 |                 });
1424 | 
1425 |                 setTimeout(function () {
1426 |                   ws1.finishClose();
1427 |                   ws2.finishClose();
1428 |                   ws3.finishClose();
1429 |                 }, 50);
1430 |               });
1431 | 
1432 |               ws3.on('message', function (msg) {
1433 |                 if (!ws3.connected) {
1434 |                   msg.type.should.eql('connect');
1435 |                   ws3.connected = true;
1436 |                 } else {
1437 |                   throw new Error('we shouldnt get a message here');
1438 |                 }
1439 |               });
1440 |             });
1441 |           });
1442 |         });
1443 |       });
1444 |     });
1445 |   },
1446 | 
1447 |   'test event with broadcast flag and to()': function (done) {
1448 |     var port = ++ports
1449 |       , cl1 = client(port)
1450 |       , cl2 = client(port)
1451 |       , cl3 = client(port)
1452 |       , io = create(cl1)
1453 |       , messages = 0
1454 |       , connections = 0
1455 |       , disconnections = 0;
1456 | 
1457 |     io.configure(function () {
1458 |       io.set('close timeout', 0);
1459 |     });
1460 | 
1461 |     io.sockets.on('connection', function (socket) {
1462 |       connections++;
1463 | 
1464 |       if (connections == 1) {
1465 |         socket.join('losers');
1466 |       }
1467 | 
1468 |       socket.on('trigger broadcast', function () {
1469 |         socket.broadcast.to('losers').emit('victory');
1470 |       });
1471 | 
1472 |       socket.on('disconnect', function () {
1473 |         disconnections++;
1474 | 
1475 |         if (disconnections == 3) {
1476 |           messages.should.eql(1);
1477 |           cl1.end();
1478 |           cl2.end();
1479 |           cl3.end();
1480 |           io.server.close();
1481 |           done();
1482 |         }
1483 |       });
1484 |     });
1485 | 
1486 |     cl1.handshake(function (sid) {
1487 |       var ws1 = websocket(cl1, sid);
1488 |       ws1.on('message', function (msg) {
1489 |         if (!ws1.connected) {
1490 |           msg.type.should.eql('connect');
1491 |           ws1.connected = true;
1492 |         } else {
1493 |           msg.should.eql({
1494 |               type: 'event'
1495 |             , name: 'victory'
1496 |             , args: []
1497 |             , endpoint: ''
1498 |           });
1499 | 
1500 |           messages++;
1501 |         }
1502 |       });
1503 | 
1504 |       ws1.on('open', function () {
1505 |         cl2.handshake(function (sid) {
1506 |           var ws2 = websocket(cl2, sid);
1507 |           ws2.on('message', function (msg) {
1508 |             if (!ws2.connected) {
1509 |               msg.type.should.eql('connect');
1510 |               ws2.connected = true;
1511 |             } else {
1512 |               throw new Error('This socket shouldnt get a message');
1513 |             };
1514 |           });
1515 | 
1516 |           ws2.on('open', function () {
1517 |             cl3.handshake(function (sid) {
1518 |               var ws3 = websocket(cl3, sid);
1519 |               ws3.on('open', function () {
1520 |                 ws3.packet({
1521 |                     type: 'event'
1522 |                   , name: 'trigger broadcast'
1523 |                   , endpoint: ''
1524 |                 });
1525 | 
1526 |                 setTimeout(function () {
1527 |                   ws1.finishClose();
1528 |                   ws2.finishClose();
1529 |                   ws3.finishClose();
1530 |                 }, 50);
1531 |               });
1532 | 
1533 |               ws3.on('message', function (msg) {
1534 |                 if (!ws3.connected) {
1535 |                   msg.type.should.eql('connect');
1536 |                   ws3.connected = true;
1537 |                 } else {
1538 |                   throw new Error('we shouldnt get a message here');
1539 |                 }
1540 |               });
1541 |             });
1542 |           });
1543 |         });
1544 |       });
1545 |     });
1546 |   }
1547 | 
1548 | };
1549 | 


--------------------------------------------------------------------------------