Comparing version 1.0.1 to 1.1.0
@@ -7,3 +7,3 @@ /*! | ||
module.exports.BufferUtil = { | ||
exports.BufferUtil = { | ||
merge: function(mergedBuffer, buffers) { | ||
@@ -10,0 +10,0 @@ var offset = 0; |
@@ -14,3 +14,3 @@ | ||
function PerMessageDeflate(options, isServer) { | ||
function PerMessageDeflate(options, isServer,maxPayload) { | ||
if (this instanceof PerMessageDeflate === false) { | ||
@@ -25,2 +25,3 @@ throw new TypeError("Classes can't be function-called"); | ||
this.params = null; | ||
this._maxPayload = maxPayload || 0; | ||
} | ||
@@ -241,2 +242,3 @@ | ||
var buffers = []; | ||
var cumulativeBufferLength=0; | ||
@@ -259,3 +261,13 @@ this._inflate.on('error', onError).on('data', onData); | ||
function onData(data) { | ||
buffers.push(data); | ||
if(self._maxPayload!==undefined && self._maxPayload!==null && self._maxPayload>0){ | ||
cumulativeBufferLength+=data.length; | ||
if(cumulativeBufferLength>self._maxPayload){ | ||
buffers=[]; | ||
cleanup(); | ||
var err={type:1009}; | ||
callback(err); | ||
return; | ||
} | ||
} | ||
buffers.push(data); | ||
} | ||
@@ -262,0 +274,0 @@ |
@@ -50,2 +50,3 @@ /*! | ||
Receiver.prototype.add = function(data) { | ||
if (this.dead) return; | ||
var self = this; | ||
@@ -157,4 +158,13 @@ function doAdd() { | ||
Receiver.prototype.error = function (reason, terminate) { | ||
if (this.dead) return; | ||
this.reset(); | ||
this.onerror(reason, terminate); | ||
if(typeof reason == 'string'){ | ||
this.onerror(new Error(reason), terminate); | ||
} | ||
else if(reason.constructor == Error){ | ||
this.onerror(reason, terminate); | ||
} | ||
else{ | ||
this.onerror(new Error("An error occured"),terminate); | ||
} | ||
return this; | ||
@@ -161,0 +171,0 @@ }; |
@@ -18,7 +18,12 @@ /*! | ||
function Receiver (extensions) { | ||
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 | ||
@@ -43,4 +48,5 @@ var fragmentedPoolPrevUsed = -1; | ||
}); | ||
this.extensions = extensions || {}; | ||
this.maxPayload = maxPayload || 0; | ||
this.currentPayloadLength = 0; | ||
this.state = { | ||
@@ -59,2 +65,3 @@ activeFragmentedOperation: null, | ||
this.currentMessage = []; | ||
this.currentMessageLength = 0; | ||
this.messageHandlers = []; | ||
@@ -82,2 +89,3 @@ this.expectHeader(2, this.processPacket); | ||
Receiver.prototype.add = function(data) { | ||
if (this.dead) return; | ||
var dataLength = data.length; | ||
@@ -251,2 +259,3 @@ if (dataLength == 0) return; | ||
Receiver.prototype.endPacket = function() { | ||
if (this.dead) return; | ||
if (!this.state.fragmentedOperation) this.unfragmentedBufferPool.reset(true); | ||
@@ -261,2 +270,3 @@ else if (this.state.lastFragment) this.fragmentedBufferPool.reset(true); | ||
} | ||
this.currentPayloadLength = 0; | ||
this.state.lastFragment = false; | ||
@@ -290,3 +300,5 @@ this.state.opcode = this.state.activeFragmentedOperation != null ? this.state.activeFragmentedOperation : 0; | ||
this.currentMessage = []; | ||
this.currentMessageLength = 0; | ||
this.messageHandlers = []; | ||
this.currentPayloadLength = 0; | ||
}; | ||
@@ -307,16 +319,2 @@ | ||
/** | ||
* Concatenates a list of buffers. | ||
* | ||
* @api private | ||
*/ | ||
Receiver.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); | ||
bufferUtil.merge(mergedBuffer, buffers); | ||
return mergedBuffer; | ||
}; | ||
/** | ||
* Handles an error | ||
@@ -328,4 +326,13 @@ * | ||
Receiver.prototype.error = function (reason, protocolErrorCode) { | ||
if (this.dead) return; | ||
this.reset(); | ||
this.onerror(reason, protocolErrorCode); | ||
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; | ||
@@ -378,2 +385,23 @@ }; | ||
/** | ||
* 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 | ||
@@ -438,2 +466,6 @@ */ | ||
if (firstLength < 126) { | ||
if (self.maxPayloadExceeded(firstLength)){ | ||
self.error('Maximumpayload exceeded in compressed text message. Aborting...', 1009); | ||
return; | ||
} | ||
opcodes['1'].getData.call(self, firstLength); | ||
@@ -443,3 +475,8 @@ } | ||
self.expectHeader(2, function(data) { | ||
opcodes['1'].getData.call(self, readUInt16BE.call(data, 0)); | ||
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); | ||
}); | ||
@@ -453,2 +490,7 @@ } | ||
} | ||
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)); | ||
@@ -480,8 +522,25 @@ }); | ||
self.applyExtensions(packet, state.lastFragment, state.compressed, function(err, buffer) { | ||
if (err) return self.error(err.message, 1007); | ||
if (buffer != null) self.currentMessage.push(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 = self.concatBuffers(self.currentMessage); | ||
var messageBuffer = Buffer.concat(self.currentMessage); | ||
self.currentMessage = []; | ||
self.currentMessageLength = 0; | ||
if (!Validation.isValidUTF8(messageBuffer)) { | ||
@@ -507,2 +566,6 @@ self.error('invalid utf8 sequence', 1007); | ||
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); | ||
@@ -512,3 +575,8 @@ } | ||
self.expectHeader(2, function(data) { | ||
opcodes['2'].getData.call(self, readUInt16BE.call(data, 0)); | ||
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); | ||
}); | ||
@@ -522,3 +590,8 @@ } | ||
} | ||
opcodes['2'].getData.call(self, readUInt32BE.call(data, 4, true)); | ||
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); | ||
}); | ||
@@ -549,7 +622,25 @@ } | ||
self.applyExtensions(packet, state.lastFragment, state.compressed, function(err, buffer) { | ||
if (err) return self.error(err.message, 1007); | ||
if (buffer != null) self.currentMessage.push(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 = self.concatBuffers(self.currentMessage); | ||
var messageBuffer = Buffer.concat(self.currentMessage); | ||
self.currentMessage = []; | ||
self.currentMessageLength = 0; | ||
self.onbinary(messageBuffer, {masked: state.masked, buffer: messageBuffer}); | ||
@@ -556,0 +647,0 @@ } |
@@ -200,3 +200,3 @@ /*! | ||
outputBuffer[1] = secondByte | 0x80; | ||
var mask = this._randomMask || (this._randomMask = getRandomMask()); | ||
var mask = getRandomMask(); | ||
outputBuffer[dataOffset - 4] = mask[0]; | ||
@@ -203,0 +203,0 @@ outputBuffer[dataOffset - 3] = mask[1]; |
@@ -6,4 +6,4 @@ /*! | ||
*/ | ||
module.exports.Validation = { | ||
exports.Validation = { | ||
isValidUTF8: function(buffer) { | ||
@@ -13,2 +13,1 @@ return true; | ||
}; | ||
@@ -74,2 +74,3 @@ 'use strict'; | ||
this.extensions = {}; | ||
this._binaryType = 'nodebuffer'; | ||
@@ -376,2 +377,23 @@ if (Array.isArray(address)) { | ||
/** | ||
* Expose binaryType | ||
* | ||
* This deviates from the W3C interface since ws doesn't support the required | ||
* default "blob" type (instead we define a custom "nodebuffer" type). | ||
* | ||
* @see http://dev.w3.org/html5/websockets/#the-websocket-interface | ||
* @api public | ||
*/ | ||
Object.defineProperty(WebSocket.prototype, 'binaryType', { | ||
get: function get() { | ||
return this._binaryType; | ||
}, | ||
set: function set(type) { | ||
if (type === 'arraybuffer' || type === 'nodebuffer') | ||
this._binaryType = type; | ||
else | ||
throw new SyntaxError('unsupported binaryType: must be either "nodebuffer" or "arraybuffer"'); | ||
} | ||
}); | ||
/** | ||
* Emulates the W3C Browser based WebSocket interface using function members. | ||
@@ -420,2 +442,4 @@ * | ||
function onMessage (data, flags) { | ||
if (flags.binary && this.binaryType === 'arraybuffer') | ||
data = new Uint8Array(data).buffer; | ||
listener.call(target, new MessageEvent(data, !!flags.binary, target)); | ||
@@ -529,3 +553,4 @@ } | ||
protocol: null, | ||
extensions: {} | ||
extensions: {}, | ||
maxPayload: 0 | ||
}).merge(options); | ||
@@ -541,3 +566,3 @@ | ||
this._isServer = true; | ||
this.maxPayload = options.value.maxPayload; | ||
// establish connection | ||
@@ -778,3 +803,3 @@ if (options.value.protocolVersion === 'hixie-76') { | ||
this._receiver = new ReceiverClass(this.extensions); | ||
this._receiver = new ReceiverClass(this.extensions,this.maxPayload); | ||
this._socket = socket; | ||
@@ -857,3 +882,3 @@ | ||
self.close(typeof errorCode !== 'undefined' ? errorCode : 1002, ''); | ||
self.emit('error', reason, errorCode); | ||
self.emit('error', (reason instanceof Error) ? reason : (new Error(reason))); | ||
}; | ||
@@ -921,3 +946,2 @@ | ||
var emitClose = this.readyState !== WebSocket.CONNECTING; | ||
this.readyState = WebSocket.CLOSED; | ||
@@ -928,11 +952,9 @@ | ||
if (emitClose) { | ||
// If the connection was closed abnormally (with an error), or if | ||
// the close control frame was not received then the close code | ||
// must default to 1006. | ||
if (error || !this._closeReceived) { | ||
this._closeCode = 1006; | ||
} | ||
this.emit('close', this._closeCode || 1000, this._closeMessage || ''); | ||
// If the connection was closed abnormally (with an error), or if | ||
// the close control frame was not received then the close code | ||
// must default to 1006. | ||
if (error || !this._closeReceived) { | ||
this._closeCode = 1006; | ||
} | ||
this.emit('close', this._closeCode || 1000, this._closeMessage || ''); | ||
@@ -939,0 +961,0 @@ if (this._socket) { |
@@ -39,3 +39,4 @@ /*! | ||
clientTracking: true, | ||
perMessageDeflate: true | ||
perMessageDeflate: true, | ||
maxPayload: null | ||
}).merge(options); | ||
@@ -76,9 +77,11 @@ | ||
} | ||
if (this._server) this._server.once('listening', function() { self.emit('listening'); }); | ||
if (this._server) { | ||
this._onceServerListening = function() { self.emit('listening'); }; | ||
this._server.once('listening', this._onceServerListening); | ||
} | ||
if (typeof this._server != 'undefined') { | ||
this._server.on('error', function(error) { | ||
self.emit('error', error) | ||
}); | ||
this._server.on('upgrade', function(req, socket, upgradeHead) { | ||
this._onServerError = function(error) { self.emit('error', error) }; | ||
this._server.on('error', this._onServerError); | ||
this._onServerUpgrade = function(req, socket, upgradeHead) { | ||
//copy upgradeHead to avoid retention of large slab buffers used in node core | ||
@@ -92,3 +95,4 @@ var head = new Buffer(upgradeHead.length); | ||
}); | ||
}); | ||
}; | ||
this._server.on('upgrade', this._onServerUpgrade); | ||
} | ||
@@ -140,2 +144,7 @@ | ||
finally { | ||
if (this._server) { | ||
this._server.removeListener('listening', this._onceServerListening); | ||
this._server.removeListener('error', this._onServerError); | ||
this._server.removeListener('upgrade', this._onServerUpgrade); | ||
} | ||
delete this._server; | ||
@@ -263,3 +272,4 @@ } | ||
protocol: protocol, | ||
extensions: extensions | ||
extensions: extensions, | ||
maxPayload: self.options.maxPayload | ||
}); | ||
@@ -367,4 +377,38 @@ | ||
// build the response header and return a Buffer | ||
var buildResponseHeader = function() { | ||
var headers = [ | ||
'HTTP/1.1 101 Switching Protocols' | ||
, 'Upgrade: WebSocket' | ||
, 'Connection: Upgrade' | ||
, 'Sec-WebSocket-Location: ' + location | ||
]; | ||
if (typeof protocol != 'undefined') headers.push('Sec-WebSocket-Protocol: ' + protocol); | ||
if (typeof origin != 'undefined') headers.push('Sec-WebSocket-Origin: ' + origin); | ||
return new Buffer(headers.concat('', '').join('\r\n')); | ||
}; | ||
// send handshake response before receiving the nonce | ||
var handshakeResponse = function() { | ||
socket.setTimeout(0); | ||
socket.setNoDelay(true); | ||
var headerBuffer = buildResponseHeader(); | ||
try { | ||
socket.write(headerBuffer, 'binary', function(err) { | ||
// remove listener if there was an error | ||
if (err) socket.removeListener('data', handler); | ||
return; | ||
}); | ||
} catch (e) { | ||
try { socket.destroy(); } catch (e) {} | ||
return; | ||
}; | ||
}; | ||
// handshake completion code to run once nonce has been successfully retrieved | ||
var completeHandshake = function(nonce, rest) { | ||
var completeHandshake = function(nonce, rest, headerBuffer) { | ||
// calculate key | ||
@@ -391,16 +435,6 @@ var k1 = req.headers['sec-websocket-key1'] | ||
var headers = [ | ||
'HTTP/1.1 101 Switching Protocols' | ||
, 'Upgrade: WebSocket' | ||
, 'Connection: Upgrade' | ||
, 'Sec-WebSocket-Location: ' + location | ||
]; | ||
if (typeof protocol != 'undefined') headers.push('Sec-WebSocket-Protocol: ' + protocol); | ||
if (typeof origin != 'undefined') headers.push('Sec-WebSocket-Origin: ' + origin); | ||
socket.setTimeout(0); | ||
socket.setNoDelay(true); | ||
try { | ||
// merge header and hash buffer | ||
var headerBuffer = new Buffer(headers.concat('', '').join('\r\n')); | ||
var hashBuffer = new Buffer(md5.digest('binary'), 'binary'); | ||
@@ -444,7 +478,6 @@ var handshakeBuffer = new Buffer(headerBuffer.length + hashBuffer.length); | ||
var rest = upgradeHead.length > nonceLength ? upgradeHead.slice(nonceLength) : null; | ||
completeHandshake.call(self, nonce, rest); | ||
completeHandshake.call(self, nonce, rest, buildResponseHeader()); | ||
} | ||
else { | ||
// nonce not present in upgradeHead, so we must wait for enough data | ||
// data to arrive before continuing | ||
// nonce not present in upgradeHead | ||
var nonce = new Buffer(nonceLength); | ||
@@ -462,6 +495,13 @@ upgradeHead.copy(nonce, 0); | ||
if (toRead < data.length) rest = data.slice(toRead); | ||
completeHandshake.call(self, nonce, rest); | ||
// complete the handshake but send empty buffer for headers since they have already been sent | ||
completeHandshake.call(self, nonce, rest, new Buffer(0)); | ||
} | ||
} | ||
// handle additional data as we receive it | ||
socket.on('data', handler); | ||
// send header response before we have the nonce to fix haproxy buffering | ||
handshakeResponse(); | ||
} | ||
@@ -501,4 +541,5 @@ } | ||
var options = this.options.perMessageDeflate; | ||
var maxPayload = this.options.maxPayload; | ||
if (options && offer[PerMessageDeflate.extensionName]) { | ||
var perMessageDeflate = new PerMessageDeflate(options !== true ? options : {}, true); | ||
var perMessageDeflate = new PerMessageDeflate(options !== true ? options : {}, true, maxPayload); | ||
perMessageDeflate.accept(offer[PerMessageDeflate.extensionName]); | ||
@@ -505,0 +546,0 @@ extensions[PerMessageDeflate.extensionName] = perMessageDeflate; |
@@ -5,4 +5,5 @@ { | ||
"description": "simple to use, blazing fast and thoroughly tested websocket client, server and console for node.js, up-to-date against RFC-6455", | ||
"version": "1.0.1", | ||
"version": "1.1.0", | ||
"license": "MIT", | ||
"main": "index.js", | ||
"keywords": [ | ||
@@ -33,2 +34,3 @@ "Hixie", | ||
"expect.js": "0.3.x", | ||
"istanbul": "^0.4.1", | ||
"mocha": "2.3.x", | ||
@@ -35,0 +37,0 @@ "should": "8.0.x", |
@@ -34,3 +34,3 @@ # ws: a node.js websocket library | ||
allows for faster processing of masked WebSocket frames and general buffer | ||
operations. | ||
operations. | ||
- `npm install --save utf-8-validate`: The specification requires validation of | ||
@@ -40,4 +40,4 @@ invalid UTF-8 chars, some of these validations could not be done in JavaScript | ||
validating the input that you receive for security purposes leading to double | ||
validation. But if you want to be 100% spec conform and fast validation of UTF-8 | ||
then this module is a must. | ||
validation. But if you want to be 100% spec-conforming and have fast | ||
validation of UTF-8 then this module is a must. | ||
@@ -115,3 +115,3 @@ ### Sending and receiving text data | ||
// or ws.upgradeReq.headers.cookie (see http://stackoverflow.com/a/16395220/151312) | ||
ws.on('message', function incoming(message) { | ||
@@ -167,3 +167,3 @@ console.log('received: %s', message); | ||
var ws = new WebSocket('ws://echo.websocket.org/', { | ||
protocolVersion: 8, | ||
protocolVersion: 8, | ||
origin: 'http://websocket.org' | ||
@@ -190,9 +190,2 @@ }); | ||
### Browserify users | ||
When including ws via a browserify bundle, ws returns global.WebSocket which has slightly different API. | ||
You should use the standard WebSockets API instead. | ||
https://developer.mozilla.org/en-US/docs/WebSockets/Writing_WebSocket_client_applications#Availability_of_WebSockets | ||
### Other examples | ||
@@ -199,0 +192,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
110506
21
3166
9
236