aboutsummaryrefslogtreecommitdiffstats
path: root/signaling-server/node_modules/socket.io/lib
diff options
context:
space:
mode:
Diffstat (limited to 'signaling-server/node_modules/socket.io/lib')
-rw-r--r--signaling-server/node_modules/socket.io/lib/logger.js97
-rw-r--r--signaling-server/node_modules/socket.io/lib/manager.js1042
-rw-r--r--signaling-server/node_modules/socket.io/lib/namespace.js355
-rw-r--r--signaling-server/node_modules/socket.io/lib/parser.js249
-rw-r--r--signaling-server/node_modules/socket.io/lib/socket.io.js143
-rw-r--r--signaling-server/node_modules/socket.io/lib/socket.js369
-rw-r--r--signaling-server/node_modules/socket.io/lib/static.js395
-rw-r--r--signaling-server/node_modules/socket.io/lib/store.js98
-rw-r--r--signaling-server/node_modules/socket.io/lib/stores/memory.js143
-rw-r--r--signaling-server/node_modules/socket.io/lib/stores/redis.js269
-rw-r--r--signaling-server/node_modules/socket.io/lib/transport.js516
-rw-r--r--signaling-server/node_modules/socket.io/lib/transports/flashsocket.js129
-rw-r--r--signaling-server/node_modules/socket.io/lib/transports/htmlfile.js83
-rw-r--r--signaling-server/node_modules/socket.io/lib/transports/http-polling.js147
-rw-r--r--signaling-server/node_modules/socket.io/lib/transports/http.js122
-rw-r--r--signaling-server/node_modules/socket.io/lib/transports/index.js12
-rw-r--r--signaling-server/node_modules/socket.io/lib/transports/jsonp-polling.js97
-rw-r--r--signaling-server/node_modules/socket.io/lib/transports/websocket.js36
-rw-r--r--signaling-server/node_modules/socket.io/lib/transports/websocket/default.js376
-rw-r--r--signaling-server/node_modules/socket.io/lib/transports/websocket/hybi-07-12.js642
-rw-r--r--signaling-server/node_modules/socket.io/lib/transports/websocket/hybi-16.js642
-rw-r--r--signaling-server/node_modules/socket.io/lib/transports/websocket/index.js11
-rw-r--r--signaling-server/node_modules/socket.io/lib/transports/xhr-polling.js69
-rw-r--r--signaling-server/node_modules/socket.io/lib/util.js50
24 files changed, 6092 insertions, 0 deletions
diff --git a/signaling-server/node_modules/socket.io/lib/logger.js b/signaling-server/node_modules/socket.io/lib/logger.js
new file mode 100644
index 0000000..49d02c9
--- /dev/null
+++ b/signaling-server/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/signaling-server/node_modules/socket.io/lib/manager.js b/signaling-server/node_modules/socket.io/lib/manager.js
new file mode 100644
index 0000000..17ed9e4
--- /dev/null
+++ b/signaling-server/node_modules/socket.io/lib/manager.js
@@ -0,0 +1,1042 @@
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module dependencies.
+ */
+
+var fs = require('fs')
+ , url = require('url')
+ , tty = require('tty')
+ , crypto = require('crypto')
+ , 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
+ , jsonpolling_re = /^\d+$/;
+
+/**
+ * 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) {
+ if (options.hasOwnProperty(i)) {
+ 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').splice(0);
+ 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.hasOwnProperty(i)) {
+ 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.sequenceNumber = Date.now() | 0;
+
+ 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);
+ });
+
+ // 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)
+
+ //use persistent channel for these, don't add and remove 5 channels for every connection
+ //eg. for 10,000 concurrent users this creates 50,000 channels in redis, which kind of slows things down
+ //we only need 5 (extra) total channels at all times
+ this.store.subscribe('message-remote',function (id, packet) {
+ self.onClientMessage(id, packet);
+ });
+
+ this.store.subscribe('disconnect-remote', function (id, reason) {
+ self.onClientDisconnect(id, reason);
+ });
+
+ this.store.subscribe('dispatch-remote', function (id, packet, volatile) {
+ var transport = self.transports[id];
+ if (transport) {
+ transport.onDispatch(packet, volatile);
+ }
+
+ if (!volatile) {
+ self.onClientDispatch(id, packet);
+ }
+ });
+
+ this.store.subscribe('heartbeat-clear', function (id) {
+ var transport = self.transports[id];
+ if (transport) {
+ transport.onHeartbeatClear();
+ }
+ });
+
+ this.store.subscribe('disconnect-force', function (id) {
+ var transport = self.transports[id];
+ if (transport) {
+ transport.onForcedDisconnect();
+ }
+ });
+};
+/**
+ * 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 (this.closed[id]) {
+ var self = this;
+
+ var transport = self.transports[id];
+ if (self.closed[id] && self.closed[id].length && transport) {
+
+ // if we have buffered messages that accumulate between calling
+ // onOpen an this async callback, send them if the transport is
+ // still open, otherwise leave them buffered
+ if (transport.open) {
+ transport.payload(self.closed[id]);
+ 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];
+ }
+
+ if (this.roomClients[id]) {
+ 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;
+};
+
+/**
+ * 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) {
+ if (this.namespaces.hasOwnProperty(name)) {
+ 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) {
+ 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]) {
+ if (this.roomClients[id].hasOwnProperty(room)) {
+ this.onLeave(id, room);
+ }
+ }
+ delete this.roomClients[id]
+ }
+
+ this.store.destroyClient(id, this.get('client store expiration'));
+};
+
+/**
+ * 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);
+ req.head = null;
+};
+
+/**
+ * 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;
+
+ // handle sync disconnect xhrs
+ 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);
+ }
+ req.res.writeHead(200);
+ req.res.end();
+ 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) {
+ if (this.namespaces.hasOwnProperty(i)) {
+ 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' });
+ }
+ }
+ }
+ }
+ } else {
+ if (transport.open) {
+ transport.error('client not handshaken', 'reconnect');
+ }
+
+ transport.discard();
+ }
+};
+
+/**
+ * Generates a session id.
+ *
+ * @api private
+ */
+
+Manager.prototype.generateId = function () {
+ var rand = new Buffer(15); // multiple of 3 for base64
+ if (!rand.writeInt32BE) {
+ return Math.abs(Math.random() * Math.random() * Date.now() | 0).toString()
+ + Math.abs(Math.random() * Math.random() * Date.now() | 0).toString();
+ }
+ this.sequenceNumber = (this.sequenceNumber + 1) | 0;
+ rand.writeInt32BE(this.sequenceNumber, 11);
+ if (crypto.randomBytes) {
+ crypto.randomBytes(12).copy(rand);
+ } else {
+ // not secure for node 0.4
+ [0, 4, 8].forEach(function(i) {
+ rand.writeInt32BE(Math.random() * Math.pow(2, 32) | 0, i);
+ });
+ }
+ return rand.toString('base64').replace(/\//g, '_').replace(/\+/g, '-');
+};
+
+/**
+ * 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 && jsonpolling_re.test(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 && jsonpolling_re.test(data.query.jsonp)) {
+ hs = 'io.j[' + data.query.jsonp + '](' + JSON.stringify(hs) + ');';
+ res.writeHead(200, { 'Content-Type': 'application/javascript' });
+ } else {
+ res.writeHead(200, headers);
+ }
+
+ self.onHandshake(id, newData || handshakeData);
+ self.store.publish('handshake', id, newData || handshakeData);
+
+ res.end(hs);
+
+ 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/signaling-server/node_modules/socket.io/lib/namespace.js b/signaling-server/node_modules/socket.io/lib/namespace.js
new file mode 100644
index 0000000..6e1e1c9
--- /dev/null
+++ b/signaling-server/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/signaling-server/node_modules/socket.io/lib/parser.js b/signaling-server/node_modules/socket.io/lib/parser.js
new file mode 100644
index 0000000..d56b550
--- /dev/null
+++ b/signaling-server/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/signaling-server/node_modules/socket.io/lib/socket.io.js b/signaling-server/node_modules/socket.io/lib/socket.io.js
new file mode 100644
index 0000000..d9d1c1f
--- /dev/null
+++ b/signaling-server/node_modules/socket.io/lib/socket.io.js
@@ -0,0 +1,143 @@
+
+/*!
+ * 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.16';
+
+/**
+ * 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 server) {
+ console.warn('Socket.IO\'s `listen()` method expects an `http.Server` instance\n'
+ + 'as its first parameter. Are you migrating from Express 2.x to 3.x?\n'
+ + 'If so, check out the "Socket.IO compatibility" section at:\n'
+ + 'https://github.com/visionmedia/express/wiki/Migrating-from-2.x-to-3.x');
+ }
+
+ 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/signaling-server/node_modules/socket.io/lib/socket.js b/signaling-server/node_modules/socket.io/lib/socket.js
new file mode 100644
index 0000000..cd39635
--- /dev/null
+++ b/signaling-server/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-remote', 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-remote', 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/signaling-server/node_modules/socket.io/lib/static.js b/signaling-server/node_modules/socket.io/lib/static.js
new file mode 100644
index 0000000..fe50593
--- /dev/null
+++ b/signaling-server/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('close', 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/signaling-server/node_modules/socket.io/lib/store.js b/signaling-server/node_modules/socket.io/lib/store.js
new file mode 100644
index 0000000..06c0389
--- /dev/null
+++ b/signaling-server/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/signaling-server/node_modules/socket.io/lib/stores/memory.js b/signaling-server/node_modules/socket.io/lib/stores/memory.js
new file mode 100644
index 0000000..8b731a7
--- /dev/null
+++ b/signaling-server/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/signaling-server/node_modules/socket.io/lib/stores/redis.js b/signaling-server/node_modules/socket.io/lib/stores/redis.js
new file mode 100644
index 0000000..8fea235
--- /dev/null
+++ b/signaling-server/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/signaling-server/node_modules/socket.io/lib/transport.js b/signaling-server/node_modules/socket.io/lib/transport.js
new file mode 100644
index 0000000..e04d2ae
--- /dev/null
+++ b/signaling-server/node_modules/socket.io/lib/transport.js
@@ -0,0 +1,516 @@
+
+/*!
+ * 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;
+
+ 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.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-remote', this.id, ack);
+ }
+ }
+
+ // handle packet locally or publish it
+ if (current) {
+ this.manager.onClientMessage(this.id, packet);
+ } else {
+ this.store.publish('message-remote', 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 (' + reason + ')');
+
+ var local = this.manager.transports[this.id];
+
+ this.close();
+ this.clearTimeouts();
+ this.disconnected = true;
+
+ if (local) {
+ this.manager.onClientDisconnect(this.id, reason);
+ }
+
+ this.store.publish('disconnect-remote', 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/signaling-server/node_modules/socket.io/lib/transports/flashsocket.js b/signaling-server/node_modules/socket.io/lib/transports/flashsocket.js
new file mode 100644
index 0000000..dc2d78b
--- /dev/null
+++ b/signaling-server/node_modules/socket.io/lib/transports/flashsocket.js
@@ -0,0 +1,129 @@
+/*!
+ * 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 () {
+
+ // Drop out immediately if the user has
+ // disabled the flash policy server
+ if (!manager.get('flash policy server')) {
+ return;
+ }
+
+ server = require('policyfile').createServer({
+ log: function(msg){
+ manager.log.info(msg);
+ }
+ }, 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();
+ }
+ });
+
+ // create or destroy the server
+ manager.on('set:flash policy server', function (value, key) {
+ var transports = manager.get('transports');
+ if (~transports.indexOf('flashsocket')) {
+ if (server && !value) {
+ // destroy the server
+ try {
+ server.close();
+ }
+ catch (e) { /* ignore exception. could e.g. be that the server isn't started yet */ }
+ }
+ } else if (!server && value) {
+ // create the server
+ 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/signaling-server/node_modules/socket.io/lib/transports/htmlfile.js b/signaling-server/node_modules/socket.io/lib/transports/htmlfile.js
new file mode 100644
index 0000000..fce0c0e
--- /dev/null
+++ b/signaling-server/node_modules/socket.io/lib/transports/htmlfile.js
@@ -0,0 +1,83 @@
+
+/*!
+ * 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) {
+ // escape all forward slashes. see GH-1251
+ data = '<script>_(' + JSON.stringify(data).replace(/\//g, '\\/') + ');</script>';
+
+ if (this.response.write(data)) {
+ this.drained = true;
+ }
+
+ this.log.debug(this.name + ' writing', data);
+};
diff --git a/signaling-server/node_modules/socket.io/lib/transports/http-polling.js b/signaling-server/node_modules/socket.io/lib/transports/http-polling.js
new file mode 100644
index 0000000..89b7e04
--- /dev/null
+++ b/signaling-server/node_modules/socket.io/lib/transports/http-polling.js
@@ -0,0 +1,147 @@
+
+/*!
+ * 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';
+
+/**
+ * Override setHandlers
+ *
+ * @api private
+ */
+
+HTTPPolling.prototype.setHandlers = function () {
+ HTTPTransport.prototype.setHandlers.call(this);
+ this.socket.removeListener('end', this.bound.end);
+ this.socket.removeListener('close', this.bound.close);
+};
+
+/**
+ * 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 (reason) {
+ this.clearPollTimeout();
+ return HTTPTransport.prototype.end.call(this, reason);
+};
+
diff --git a/signaling-server/node_modules/socket.io/lib/transports/http.js b/signaling-server/node_modules/socket.io/lib/transports/http.js
new file mode 100644
index 0000000..fa94b59
--- /dev/null
+++ b/signaling-server/node_modules/socket.io/lib/transports/http.js
@@ -0,0 +1,122 @@
+
+/*!
+ * 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) {
+
+ // Always set the response in case an error is returned to the client
+ this.response = req.res;
+
+ 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 = '';
+ self.onClose();
+ });
+
+ if (origin) {
+ // https://developer.mozilla.org/En/HTTP_Access_Control
+ headers['Access-Control-Allow-Origin'] = origin;
+ headers['Access-Control-Allow-Credentials'] = 'true';
+ headers['X-XSS-Protection'] = '0';
+ }
+ } else {
+ 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/signaling-server/node_modules/socket.io/lib/transports/index.js b/signaling-server/node_modules/socket.io/lib/transports/index.js
new file mode 100644
index 0000000..b865559
--- /dev/null
+++ b/signaling-server/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/signaling-server/node_modules/socket.io/lib/transports/jsonp-polling.js b/signaling-server/node_modules/socket.io/lib/transports/jsonp-polling.js
new file mode 100644
index 0000000..ad7d5af
--- /dev/null
+++ b/signaling-server/node_modules/socket.io/lib/transports/jsonp-polling.js
@@ -0,0 +1,97 @@
+
+/*!
+ * socket.io-node
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module requirements.
+ */
+
+var HTTPPolling = require('./http-polling');
+var jsonpolling_re = /^\d+$/
+
+/**
+ * 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 && jsonpolling_re.test(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/signaling-server/node_modules/socket.io/lib/transports/websocket.js b/signaling-server/node_modules/socket.io/lib/transports/websocket.js
new file mode 100644
index 0000000..78a4304
--- /dev/null
+++ b/signaling-server/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/signaling-server/node_modules/socket.io/lib/transports/websocket/default.js b/signaling-server/node_modules/socket.io/lib/transports/websocket/default.js
new file mode 100644
index 0000000..cf3b8af
--- /dev/null
+++ b/signaling-server/node_modules/socket.io/lib/transports/websocket/default.js
@@ -0,0 +1,376 @@
+/*!
+ * 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({maxBuffer: mng.get('destroy buffer size')});
+ 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();
+ });
+ this.parser.on('kick', function (reason) {
+ self.log.warn(self.name + ' parser forced user kick: ' + reason);
+ self.onMessage({type: 'disconnect', endpoint: ''});
+ 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']
+ , waitingForNonce = false;
+ if(this.manager.settings['match origin protocol']){
+ location = (origin.indexOf('https')>-1 ? 'wss' : 'ws') + '://' + this.req.headers.host + this.req.url;
+ }else if(this.socket.encrypted){
+ location = 'wss://' + this.req.headers.host + this.req.url;
+ }else{
+ location = 'ws://' + this.req.headers.host + this.req.url;
+ }
+
+ 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 (opts) {
+ this._maxBuffer = (opts && opts.maxBuffer) || 10E7;
+ this._dataLength = 0;
+ 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._dataLength += data.length;
+ if(this._dataLength > this._maxBuffer) {
+ this.buffer = ''; //Clear buffer
+ this.emit('kick', 'max buffer size reached');
+ return;
+ }
+
+ 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/signaling-server/node_modules/socket.io/lib/transports/websocket/hybi-07-12.js b/signaling-server/node_modules/socket.io/lib/transports/websocket/hybi-07-12.js
new file mode 100644
index 0000000..8f0759d
--- /dev/null
+++ b/signaling-server/node_modules/socket.io/lib/transports/websocket/hybi-07-12.js
@@ -0,0 +1,642 @@
+
+/*!
+ * 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({maxBuffer: mng.get('destroy buffer size')});
+ 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();
+ });
+ this.parser.on('kick', function (reason) {
+ self.log.warn(self.name + ' parser forced user kick: ' + reason);
+ self.onMessage({type: 'disconnect', endpoint: ''});
+ 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 (opts) {
+ this.state = {
+ activeFragmentedOperation: null,
+ lastFragment: false,
+ masked: false,
+ opcode: 0
+ };
+ this.overflow = null;
+ this.expectOffset = 0;
+ this.expectBuffer = null;
+ this.expectHandler = null;
+ this.currentMessage = '';
+ this._maxBuffer = (opts && opts.maxBuffer) || 10E7;
+ this._dataLength = 0;
+
+ 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) {
+ this._dataLength += data.length;
+ if (this._dataLength > this._maxBuffer) {
+ // Clear data
+ this.overflow = null;
+ this.expectBuffer = null;
+ // Kick client
+ this.emit('kick', 'max buffer size reached');
+ return;
+ }
+ 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) {
+ if (length > this._maxBuffer) {
+ this.emit('kick', 'expected input larger than max buffer');
+ return;
+ }
+ 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/signaling-server/node_modules/socket.io/lib/transports/websocket/hybi-16.js b/signaling-server/node_modules/socket.io/lib/transports/websocket/hybi-16.js
new file mode 100644
index 0000000..2074fa1
--- /dev/null
+++ b/signaling-server/node_modules/socket.io/lib/transports/websocket/hybi-16.js
@@ -0,0 +1,642 @@
+/*!
+ * 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({maxBuffer: mng.get('destroy buffer size')});
+ 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();
+ });
+ this.parser.on('kick', function (reason) {
+ self.log.warn(self.name + ' parser forced user kick: ' + reason);
+ self.onMessage({type: 'disconnect', endpoint: ''});
+ 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 (opts) {
+ this.state = {
+ activeFragmentedOperation: null,
+ lastFragment: false,
+ masked: false,
+ opcode: 0
+ };
+ this.overflow = null;
+ this.expectOffset = 0;
+ this.expectBuffer = null;
+ this.expectHandler = null;
+ this.currentMessage = '';
+ this._maxBuffer = (opts && opts.maxBuffer) || 10E7;
+ this._dataLength = 0;
+
+ 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) {
+ this._dataLength += data.length;
+ if (this._dataLength > this._maxBuffer) {
+ // Clear data
+ this.overflow = null;
+ this.expectBuffer = null;
+ // Kick client
+ this.emit('kick', 'max buffer size reached');
+ return;
+ }
+ 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) {
+ if (length > this._maxBuffer) {
+ this.emit('kick', 'expected input larger than max buffer');
+ return;
+ }
+ 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/signaling-server/node_modules/socket.io/lib/transports/websocket/index.js b/signaling-server/node_modules/socket.io/lib/transports/websocket/index.js
new file mode 100644
index 0000000..3a952b7
--- /dev/null
+++ b/signaling-server/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/signaling-server/node_modules/socket.io/lib/transports/xhr-polling.js b/signaling-server/node_modules/socket.io/lib/transports/xhr-polling.js
new file mode 100644
index 0000000..1db5aee
--- /dev/null
+++ b/signaling-server/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/signaling-server/node_modules/socket.io/lib/util.js b/signaling-server/node_modules/socket.io/lib/util.js
new file mode 100644
index 0000000..f7d9f2b
--- /dev/null
+++ b/signaling-server/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;
+}
+