From b60df56157ee1fd0bd4938799bac05a62fda91a1 Mon Sep 17 00:00:00 2001 From: lookshe Date: Sat, 14 Mar 2015 20:45:20 +0100 Subject: initial commit from working version --- .../socket.io/lib/transports/flashsocket.js | 129 +++++ .../socket.io/lib/transports/htmlfile.js | 83 +++ .../socket.io/lib/transports/http-polling.js | 147 +++++ .../node_modules/socket.io/lib/transports/http.js | 122 ++++ .../node_modules/socket.io/lib/transports/index.js | 12 + .../socket.io/lib/transports/jsonp-polling.js | 97 ++++ .../socket.io/lib/transports/websocket.js | 36 ++ .../socket.io/lib/transports/websocket/default.js | 376 ++++++++++++ .../lib/transports/websocket/hybi-07-12.js | 642 +++++++++++++++++++++ .../socket.io/lib/transports/websocket/hybi-16.js | 642 +++++++++++++++++++++ .../socket.io/lib/transports/websocket/index.js | 11 + .../socket.io/lib/transports/xhr-polling.js | 69 +++ 12 files changed, 2366 insertions(+) create mode 100644 signaling-server/node_modules/socket.io/lib/transports/flashsocket.js create mode 100644 signaling-server/node_modules/socket.io/lib/transports/htmlfile.js create mode 100644 signaling-server/node_modules/socket.io/lib/transports/http-polling.js create mode 100644 signaling-server/node_modules/socket.io/lib/transports/http.js create mode 100644 signaling-server/node_modules/socket.io/lib/transports/index.js create mode 100644 signaling-server/node_modules/socket.io/lib/transports/jsonp-polling.js create mode 100644 signaling-server/node_modules/socket.io/lib/transports/websocket.js create mode 100644 signaling-server/node_modules/socket.io/lib/transports/websocket/default.js create mode 100644 signaling-server/node_modules/socket.io/lib/transports/websocket/hybi-07-12.js create mode 100644 signaling-server/node_modules/socket.io/lib/transports/websocket/hybi-16.js create mode 100644 signaling-server/node_modules/socket.io/lib/transports/websocket/index.js create mode 100644 signaling-server/node_modules/socket.io/lib/transports/xhr-polling.js (limited to 'signaling-server/node_modules/socket.io/lib/transports') 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 + * 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 + * 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( + '' + + '' + + new Array(174).join(' ') + ); + } +}; + +/** + * Performs the write. + * + * @api private + */ + +HTMLFile.prototype.write = function (data) { + // escape all forward slashes. see GH-1251 + data = ''; + + 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 + * 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 + * 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 + * 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 + * 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 + * 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 + * 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 + * 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 + * 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); +}; -- cgit v1.2.3