diff options
Diffstat (limited to 'node_modules/ws/lib/Receiver.js')
-rw-r--r-- | node_modules/ws/lib/Receiver.js | 793 |
1 files changed, 0 insertions, 793 deletions
diff --git a/node_modules/ws/lib/Receiver.js b/node_modules/ws/lib/Receiver.js deleted file mode 100644 index 0bf29d800..000000000 --- a/node_modules/ws/lib/Receiver.js +++ /dev/null @@ -1,793 +0,0 @@ -/*! - * ws: a node.js websocket client - * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com> - * MIT Licensed - */ - -var util = require('util') - , Validation = require('./Validation').Validation - , ErrorCodes = require('./ErrorCodes') - , BufferPool = require('./BufferPool') - , bufferUtil = require('./BufferUtil').BufferUtil - , PerMessageDeflate = require('./PerMessageDeflate'); - -/** - * HyBi Receiver implementation - */ - -function Receiver (extensions,maxPayload) { - if (this instanceof Receiver === false) { - throw new TypeError("Classes can't be function-called"); - } - if(typeof extensions==='number'){ - maxPayload=extensions; - extensions={}; - } - - - // memory pool for fragmented messages - var fragmentedPoolPrevUsed = -1; - this.fragmentedBufferPool = new BufferPool(1024, function(db, length) { - return db.used + length; - }, function(db) { - return fragmentedPoolPrevUsed = fragmentedPoolPrevUsed >= 0 ? - Math.ceil((fragmentedPoolPrevUsed + db.used) / 2) : - db.used; - }); - - // memory pool for unfragmented messages - var unfragmentedPoolPrevUsed = -1; - this.unfragmentedBufferPool = new BufferPool(1024, function(db, length) { - return db.used + length; - }, function(db) { - return unfragmentedPoolPrevUsed = unfragmentedPoolPrevUsed >= 0 ? - Math.ceil((unfragmentedPoolPrevUsed + db.used) / 2) : - db.used; - }); - this.extensions = extensions || {}; - this.maxPayload = maxPayload || 0; - this.currentPayloadLength = 0; - this.state = { - activeFragmentedOperation: null, - lastFragment: false, - masked: false, - opcode: 0, - fragmentedOperation: false - }; - this.overflow = []; - this.headerBuffer = new Buffer(10); - this.expectOffset = 0; - this.expectBuffer = null; - this.expectHandler = null; - this.currentMessage = []; - this.currentMessageLength = 0; - this.messageHandlers = []; - this.expectHeader(2, this.processPacket); - this.dead = false; - this.processing = false; - - this.onerror = function() {}; - this.ontext = function() {}; - this.onbinary = function() {}; - this.onclose = function() {}; - this.onping = function() {}; - this.onpong = function() {}; -} - -module.exports = Receiver; - -/** - * Add new data to the parser. - * - * @api public - */ - -Receiver.prototype.add = function(data) { - if (this.dead) return; - var dataLength = data.length; - if (dataLength == 0) return; - if (this.expectBuffer == null) { - this.overflow.push(data); - return; - } - var toRead = Math.min(dataLength, this.expectBuffer.length - this.expectOffset); - fastCopy(toRead, data, this.expectBuffer, this.expectOffset); - this.expectOffset += toRead; - if (toRead < dataLength) { - this.overflow.push(data.slice(toRead)); - } - while (this.expectBuffer && this.expectOffset == this.expectBuffer.length) { - var bufferForHandler = this.expectBuffer; - this.expectBuffer = null; - this.expectOffset = 0; - this.expectHandler.call(this, bufferForHandler); - } -}; - -/** - * Releases all resources used by the receiver. - * - * @api public - */ - -Receiver.prototype.cleanup = function() { - this.dead = true; - this.overflow = null; - this.headerBuffer = null; - this.expectBuffer = null; - this.expectHandler = null; - this.unfragmentedBufferPool = null; - this.fragmentedBufferPool = null; - this.state = null; - this.currentMessage = null; - this.onerror = null; - this.ontext = null; - this.onbinary = null; - this.onclose = null; - this.onping = null; - this.onpong = null; -}; - -/** - * Waits for a certain amount of header bytes to be available, then fires a callback. - * - * @api private - */ - -Receiver.prototype.expectHeader = function(length, handler) { - if (length == 0) { - handler(null); - return; - } - this.expectBuffer = this.headerBuffer.slice(this.expectOffset, this.expectOffset + length); - this.expectHandler = handler; - var toRead = length; - while (toRead > 0 && this.overflow.length > 0) { - var fromOverflow = this.overflow.pop(); - if (toRead < fromOverflow.length) this.overflow.push(fromOverflow.slice(toRead)); - var read = Math.min(fromOverflow.length, toRead); - fastCopy(read, fromOverflow, this.expectBuffer, this.expectOffset); - this.expectOffset += read; - toRead -= read; - } -}; - -/** - * Waits for a certain amount of data bytes to be available, then fires a callback. - * - * @api private - */ - -Receiver.prototype.expectData = function(length, handler) { - if (length == 0) { - handler(null); - return; - } - this.expectBuffer = this.allocateFromPool(length, this.state.fragmentedOperation); - this.expectHandler = handler; - var toRead = length; - while (toRead > 0 && this.overflow.length > 0) { - var fromOverflow = this.overflow.pop(); - if (toRead < fromOverflow.length) this.overflow.push(fromOverflow.slice(toRead)); - var read = Math.min(fromOverflow.length, toRead); - fastCopy(read, fromOverflow, this.expectBuffer, this.expectOffset); - this.expectOffset += read; - toRead -= read; - } -}; - -/** - * Allocates memory from the buffer pool. - * - * @api private - */ - -Receiver.prototype.allocateFromPool = function(length, isFragmented) { - return (isFragmented ? this.fragmentedBufferPool : this.unfragmentedBufferPool).get(length); -}; - -/** - * Start processing a new packet. - * - * @api private - */ - -Receiver.prototype.processPacket = function (data) { - if (this.extensions[PerMessageDeflate.extensionName]) { - if ((data[0] & 0x30) != 0) { - this.error('reserved fields (2, 3) must be empty', 1002); - return; - } - } else { - if ((data[0] & 0x70) != 0) { - this.error('reserved fields must be empty', 1002); - return; - } - } - this.state.lastFragment = (data[0] & 0x80) == 0x80; - this.state.masked = (data[1] & 0x80) == 0x80; - var compressed = (data[0] & 0x40) == 0x40; - var opcode = data[0] & 0xf; - if (opcode === 0) { - if (compressed) { - this.error('continuation frame cannot have the Per-message Compressed bits', 1002); - return; - } - // continuation frame - this.state.fragmentedOperation = true; - this.state.opcode = this.state.activeFragmentedOperation; - if (!(this.state.opcode == 1 || this.state.opcode == 2)) { - this.error('continuation frame cannot follow current opcode', 1002); - return; - } - } - else { - if (opcode < 3 && this.state.activeFragmentedOperation != null) { - this.error('data frames after the initial data frame must have opcode 0', 1002); - return; - } - if (opcode >= 8 && compressed) { - this.error('control frames cannot have the Per-message Compressed bits', 1002); - return; - } - this.state.compressed = compressed; - this.state.opcode = opcode; - if (this.state.lastFragment === false) { - this.state.fragmentedOperation = true; - this.state.activeFragmentedOperation = opcode; - } - else this.state.fragmentedOperation = false; - } - var handler = opcodes[this.state.opcode]; - if (typeof handler == 'undefined') this.error('no handler for opcode ' + this.state.opcode, 1002); - else { - handler.start.call(this, data); - } -}; - -/** - * Endprocessing a packet. - * - * @api private - */ - -Receiver.prototype.endPacket = function() { - if (this.dead) return; - if (!this.state.fragmentedOperation) this.unfragmentedBufferPool.reset(true); - else if (this.state.lastFragment) this.fragmentedBufferPool.reset(true); - 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.currentPayloadLength = 0; - this.state.lastFragment = false; - this.state.opcode = this.state.activeFragmentedOperation != null ? this.state.activeFragmentedOperation : 0; - this.state.masked = false; - this.expectHeader(2, this.processPacket); -}; - -/** - * Reset the parser state. - * - * @api private - */ - -Receiver.prototype.reset = function() { - if (this.dead) return; - this.state = { - activeFragmentedOperation: null, - lastFragment: false, - masked: false, - opcode: 0, - fragmentedOperation: false - }; - this.fragmentedBufferPool.reset(true); - this.unfragmentedBufferPool.reset(true); - this.expectOffset = 0; - this.expectBuffer = null; - this.expectHandler = null; - this.overflow = []; - this.currentMessage = []; - this.currentMessageLength = 0; - this.messageHandlers = []; - this.currentPayloadLength = 0; -}; - -/** - * Unmask received data. - * - * @api private - */ - -Receiver.prototype.unmask = function (mask, buf, binary) { - if (mask != null && buf != null) bufferUtil.unmask(buf, mask); - if (binary) return buf; - return buf != null ? buf.toString('utf8') : ''; -}; - -/** - * Handles an error - * - * @api private - */ - -Receiver.prototype.error = function (reason, protocolErrorCode) { - if (this.dead) return; - this.reset(); - if(typeof reason == 'string'){ - this.onerror(new Error(reason), protocolErrorCode); - } - else if(reason.constructor == Error){ - this.onerror(reason, protocolErrorCode); - } - else{ - this.onerror(new Error("An error occured"),protocolErrorCode); - } - return this; -}; - -/** - * Execute message handler buffers - * - * @api private - */ - -Receiver.prototype.flush = function() { - if (this.processing || this.dead) return; - - var handler = this.messageHandlers.shift(); - if (!handler) return; - - this.processing = true; - var self = this; - - handler(function() { - self.processing = false; - self.flush(); - }); -}; - -/** - * Apply extensions to message - * - * @api private - */ - -Receiver.prototype.applyExtensions = function(messageBuffer, fin, compressed, callback) { - var self = this; - if (compressed) { - this.extensions[PerMessageDeflate.extensionName].decompress(messageBuffer, fin, function(err, buffer) { - if (self.dead) return; - if (err) { - callback(new Error('invalid compressed data')); - return; - } - callback(null, buffer); - }); - } else { - callback(null, messageBuffer); - } -}; - -/** -* Checks payload size, disconnects socket when it exceeds maxPayload -* -* @api private -*/ -Receiver.prototype.maxPayloadExceeded = function(length) { - if (this.maxPayload=== undefined || this.maxPayload === null || this.maxPayload < 1) { - return false; - } - var fullLength = this.currentPayloadLength + length; - if (fullLength < this.maxPayload) { - this.currentPayloadLength = fullLength; - return false; - } - this.error('payload cannot exceed ' + this.maxPayload + ' bytes', 1009); - this.messageBuffer=[]; - this.cleanup(); - - return true; -}; - -/** - * Buffer utilities - */ - -function readUInt16BE(start) { - return (this[start]<<8) + - this[start+1]; -} - -function readUInt32BE(start) { - return (this[start]<<24) + - (this[start+1]<<16) + - (this[start+2]<<8) + - this[start+3]; -} - -function fastCopy(length, srcBuffer, dstBuffer, dstOffset) { - switch (length) { - default: srcBuffer.copy(dstBuffer, dstOffset, 0, length); break; - case 16: dstBuffer[dstOffset+15] = srcBuffer[15]; - case 15: dstBuffer[dstOffset+14] = srcBuffer[14]; - case 14: dstBuffer[dstOffset+13] = srcBuffer[13]; - case 13: dstBuffer[dstOffset+12] = srcBuffer[12]; - case 12: dstBuffer[dstOffset+11] = srcBuffer[11]; - case 11: dstBuffer[dstOffset+10] = srcBuffer[10]; - case 10: dstBuffer[dstOffset+9] = srcBuffer[9]; - case 9: dstBuffer[dstOffset+8] = srcBuffer[8]; - case 8: dstBuffer[dstOffset+7] = srcBuffer[7]; - case 7: dstBuffer[dstOffset+6] = srcBuffer[6]; - case 6: dstBuffer[dstOffset+5] = srcBuffer[5]; - case 5: dstBuffer[dstOffset+4] = srcBuffer[4]; - case 4: dstBuffer[dstOffset+3] = srcBuffer[3]; - case 3: dstBuffer[dstOffset+2] = srcBuffer[2]; - case 2: dstBuffer[dstOffset+1] = srcBuffer[1]; - case 1: dstBuffer[dstOffset] = srcBuffer[0]; - } -} - -function clone(obj) { - var cloned = {}; - for (var k in obj) { - if (obj.hasOwnProperty(k)) { - cloned[k] = obj[k]; - } - } - return cloned; -} - -/** - * Opcode handlers - */ - -var opcodes = { - // text - '1': { - start: function(data) { - var self = this; - // decode length - var firstLength = data[1] & 0x7f; - if (firstLength < 126) { - if (self.maxPayloadExceeded(firstLength)){ - self.error('Maximumpayload exceeded in compressed text message. Aborting...', 1009); - return; - } - opcodes['1'].getData.call(self, firstLength); - } - else if (firstLength == 126) { - self.expectHeader(2, function(data) { - var length = readUInt16BE.call(data, 0); - if (self.maxPayloadExceeded(length)){ - self.error('Maximumpayload exceeded in compressed text message. Aborting...', 1009); - return; - } - opcodes['1'].getData.call(self, length); - }); - } - else if (firstLength == 127) { - self.expectHeader(8, function(data) { - if (readUInt32BE.call(data, 0) != 0) { - self.error('packets with length spanning more than 32 bit is currently not supported', 1008); - return; - } - var length = readUInt32BE.call(data, 4); - if (self.maxPayloadExceeded(length)){ - self.error('Maximumpayload exceeded in compressed text message. Aborting...', 1009); - return; - } - opcodes['1'].getData.call(self, readUInt32BE.call(data, 4)); - }); - } - }, - getData: function(length) { - var self = this; - if (self.state.masked) { - self.expectHeader(4, function(data) { - var mask = data; - self.expectData(length, function(data) { - opcodes['1'].finish.call(self, mask, data); - }); - }); - } - else { - self.expectData(length, function(data) { - opcodes['1'].finish.call(self, null, data); - }); - } - }, - finish: function(mask, data) { - var self = this; - var packet = this.unmask(mask, data, true) || new Buffer(0); - var state = clone(this.state); - this.messageHandlers.push(function(callback) { - self.applyExtensions(packet, state.lastFragment, state.compressed, function(err, buffer) { - if (err) { - if(err.type===1009){ - return self.error('Maximumpayload exceeded in compressed text message. Aborting...', 1009); - } - return self.error(err.message, 1007); - } - if (buffer != null) { - if( self.maxPayload==0 || (self.maxPayload > 0 && (self.currentMessageLength + buffer.length) < self.maxPayload) ){ - self.currentMessage.push(buffer); - } - else{ - self.currentMessage=null; - self.currentMessage = []; - self.currentMessageLength = 0; - self.error(new Error('Maximum payload exceeded. maxPayload: '+self.maxPayload), 1009); - return; - } - self.currentMessageLength += buffer.length; - } - if (state.lastFragment) { - var messageBuffer = Buffer.concat(self.currentMessage); - self.currentMessage = []; - self.currentMessageLength = 0; - if (!Validation.isValidUTF8(messageBuffer)) { - self.error('invalid utf8 sequence', 1007); - return; - } - self.ontext(messageBuffer.toString('utf8'), {masked: state.masked, buffer: messageBuffer}); - } - callback(); - }); - }); - this.flush(); - this.endPacket(); - } - }, - // binary - '2': { - start: function(data) { - var self = this; - // decode length - var firstLength = data[1] & 0x7f; - if (firstLength < 126) { - if (self.maxPayloadExceeded(firstLength)){ - self.error('Max payload exceeded in compressed text message. Aborting...', 1009); - return; - } - opcodes['2'].getData.call(self, firstLength); - } - else if (firstLength == 126) { - self.expectHeader(2, function(data) { - var length = readUInt16BE.call(data, 0); - if (self.maxPayloadExceeded(length)){ - self.error('Max payload exceeded in compressed text message. Aborting...', 1009); - return; - } - opcodes['2'].getData.call(self, length); - }); - } - else if (firstLength == 127) { - self.expectHeader(8, function(data) { - if (readUInt32BE.call(data, 0) != 0) { - self.error('packets with length spanning more than 32 bit is currently not supported', 1008); - return; - } - var length = readUInt32BE.call(data, 4, true); - if (self.maxPayloadExceeded(length)){ - self.error('Max payload exceeded in compressed text message. Aborting...', 1009); - return; - } - opcodes['2'].getData.call(self, length); - }); - } - }, - getData: function(length) { - var self = this; - if (self.state.masked) { - self.expectHeader(4, function(data) { - var mask = data; - self.expectData(length, function(data) { - opcodes['2'].finish.call(self, mask, data); - }); - }); - } - else { - self.expectData(length, function(data) { - opcodes['2'].finish.call(self, null, data); - }); - } - }, - finish: function(mask, data) { - var self = this; - var packet = this.unmask(mask, data, true) || new Buffer(0); - var state = clone(this.state); - this.messageHandlers.push(function(callback) { - self.applyExtensions(packet, state.lastFragment, state.compressed, function(err, buffer) { - if (err) { - if(err.type===1009){ - return self.error('Max payload exceeded in compressed binary message. Aborting...', 1009); - } - return self.error(err.message, 1007); - } - if (buffer != null) { - if( self.maxPayload==0 || (self.maxPayload > 0 && (self.currentMessageLength + buffer.length) < self.maxPayload) ){ - self.currentMessage.push(buffer); - } - else{ - self.currentMessage=null; - self.currentMessage = []; - self.currentMessageLength = 0; - self.error(new Error('Maximum payload exceeded'), 1009); - return; - } - self.currentMessageLength += buffer.length; - } - if (state.lastFragment) { - var messageBuffer = Buffer.concat(self.currentMessage); - self.currentMessage = []; - self.currentMessageLength = 0; - self.onbinary(messageBuffer, {masked: state.masked, buffer: messageBuffer}); - } - callback(); - }); - }); - this.flush(); - this.endPacket(); - } - }, - // close - '8': { - start: function(data) { - var self = this; - if (self.state.lastFragment == false) { - self.error('fragmented close is not supported', 1002); - return; - } - - // decode length - var firstLength = data[1] & 0x7f; - if (firstLength < 126) { - opcodes['8'].getData.call(self, firstLength); - } - else { - self.error('control frames cannot have more than 125 bytes of data', 1002); - } - }, - getData: function(length) { - var self = this; - if (self.state.masked) { - self.expectHeader(4, function(data) { - var mask = data; - self.expectData(length, function(data) { - opcodes['8'].finish.call(self, mask, data); - }); - }); - } - else { - self.expectData(length, function(data) { - opcodes['8'].finish.call(self, null, data); - }); - } - }, - finish: function(mask, data) { - var self = this; - data = self.unmask(mask, data, true); - - var state = clone(this.state); - this.messageHandlers.push(function() { - if (data && data.length == 1) { - self.error('close packets with data must be at least two bytes long', 1002); - return; - } - var code = data && data.length > 1 ? readUInt16BE.call(data, 0) : 1000; - if (!ErrorCodes.isValidErrorCode(code)) { - self.error('invalid error code', 1002); - return; - } - var message = ''; - if (data && data.length > 2) { - var messageBuffer = data.slice(2); - if (!Validation.isValidUTF8(messageBuffer)) { - self.error('invalid utf8 sequence', 1007); - return; - } - message = messageBuffer.toString('utf8'); - } - self.onclose(code, message, {masked: state.masked}); - self.reset(); - }); - this.flush(); - }, - }, - // ping - '9': { - start: function(data) { - var self = this; - if (self.state.lastFragment == false) { - self.error('fragmented ping is not supported', 1002); - return; - } - - // decode length - var firstLength = data[1] & 0x7f; - if (firstLength < 126) { - opcodes['9'].getData.call(self, firstLength); - } - else { - self.error('control frames cannot have more than 125 bytes of data', 1002); - } - }, - getData: function(length) { - var self = this; - if (self.state.masked) { - self.expectHeader(4, function(data) { - var mask = data; - self.expectData(length, function(data) { - opcodes['9'].finish.call(self, mask, data); - }); - }); - } - else { - self.expectData(length, function(data) { - opcodes['9'].finish.call(self, null, data); - }); - } - }, - finish: function(mask, data) { - var self = this; - data = this.unmask(mask, data, true); - var state = clone(this.state); - this.messageHandlers.push(function(callback) { - self.onping(data, {masked: state.masked, binary: true}); - callback(); - }); - this.flush(); - this.endPacket(); - } - }, - // pong - '10': { - start: function(data) { - var self = this; - if (self.state.lastFragment == false) { - self.error('fragmented pong is not supported', 1002); - return; - } - - // decode length - var firstLength = data[1] & 0x7f; - if (firstLength < 126) { - opcodes['10'].getData.call(self, firstLength); - } - else { - self.error('control frames cannot have more than 125 bytes of data', 1002); - } - }, - getData: function(length) { - var self = this; - if (this.state.masked) { - this.expectHeader(4, function(data) { - var mask = data; - self.expectData(length, function(data) { - opcodes['10'].finish.call(self, mask, data); - }); - }); - } - else { - this.expectData(length, function(data) { - opcodes['10'].finish.call(self, null, data); - }); - } - }, - finish: function(mask, data) { - var self = this; - data = self.unmask(mask, data, true); - var state = clone(this.state); - this.messageHandlers.push(function(callback) { - self.onpong(data, {masked: state.masked, binary: true}); - callback(); - }); - this.flush(); - this.endPacket(); - } - } -} |