From b21bfad86bc0c5283de233f1518455d583073918 Mon Sep 17 00:00:00 2001 From: gdvandongen Date: Sat, 28 May 2016 00:25:27 +0200 Subject: [PATCH] v 1.2.1 added timeout default timeout 30 secs --- README.md | 5 +- app.js | 1 + app.json | 2 +- node_modules/http.min/README.md | 19 ++- node_modules/http.min/index.js | 16 ++- node_modules/http.min/package.json | 35 +++-- node_modules/ws/Makefile | 19 ++- node_modules/ws/README.md | 17 +-- node_modules/ws/SECURITY.md | 33 +++++ node_modules/ws/lib/BufferUtil.fallback.js | 2 +- node_modules/ws/lib/PerMessageDeflate.js | 16 ++- node_modules/ws/lib/Receiver.hixie.js | 12 +- node_modules/ws/lib/Receiver.js | 145 +++++++++++++++++---- node_modules/ws/lib/Sender.js | 2 +- node_modules/ws/lib/Validation.fallback.js | 5 +- node_modules/ws/lib/WebSocket.js | 48 +++++-- node_modules/ws/lib/WebSocketServer.js | 91 +++++++++---- node_modules/ws/package.json | 27 ++-- 18 files changed, 365 insertions(+), 130 deletions(-) create mode 100644 node_modules/ws/SECURITY.md diff --git a/README.md b/README.md index cef5c58..b305b93 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,9 @@ condition or execute HTTP requests as a flow action with this app. ### Action cards - HTTP Delete - HTTP Get -- HTTP Get JSON (for query parameters like ?a=1&b=zz use `{"a":1,"b":"zz"}`) +- DEPRICATED: HTTP Get JSON (for query parameters like ?a=1&b=zz use `{"a":1,"b":"zz"}`, will be removed in a next version. Queryparameters can now be used with the HTTP Get card) - HTTP Put JSON -- HTTP Post Form (content-type 'application/x-www-form-urlencoded') +- HTTP Post Form (content-type 'application/x-www-form-urlencoded', JSON formatted) - HTTP Post JSON (content-type 'application/json') - WebSocket Send (message to ws://x.x.x.x:y endpoint) - GET variable step 1 (read 'get variable and trigger flow') @@ -38,6 +38,7 @@ When this cards executes succesfull, it will start flows with the 'GET variable Happy hacking! #### Notes + Requests will time-out after 30 seconds. Passing a valid JSON string (at least `{}` ) is obligatory for cards with a JSON parameter. ###### Advanced HTTP options diff --git a/app.js b/app.js index 160b6e3..e616166 100644 --- a/app.js +++ b/app.js @@ -33,6 +33,7 @@ function createUrlOptions (input, mergeOptions) { result = {uri: input} } if (mergeOptions) mergeOptional(result, mergeOptions) + mergeOptional(result, {timeout: 30000}) return result } // function createUrlOptions diff --git a/app.json b/app.json index 78a67b6..43cebc3 100644 --- a/app.json +++ b/app.json @@ -1,6 +1,6 @@ { "id":"com.internet", - "version":"1.2.0", + "version":"1.2.1", "compatibility":">=0.8.20", "name":{ "en":"HTTP request flow cards", diff --git a/node_modules/http.min/README.md b/node_modules/http.min/README.md index 4ba06d7..fafbf5e 100644 --- a/node_modules/http.min/README.md +++ b/node_modules/http.min/README.md @@ -2,8 +2,9 @@ [![NPM][npm-image]][npm-url] [![Build status][travis-image]][travis-url] [![Coverage Status][coverage-image]][coverage-url] [![js-standard-style][standard-image]][standard-url] [![Dependencies][david-image]][david-url] [![devDependencies][david-dev-image]][david-dev-url] Minimal simple HTTP client with promises. -Supports common methods GET, POST, PUT, PATCH, DELETE and JSON support. -No dependencies, under 100 SLOC. +Supports HTTP, HTTPS and common methods GET, POST, PUT, PATCH, DELETE. +Includes timeout, query string, form data, JSON helpers. +No dependencies, under 100 SLOC. ## Install @@ -14,7 +15,7 @@ No dependencies, under 100 SLOC. ### GET ```javascript var http = require('http.min') -http.get('https://httpbin.org/get').then(function (result) { +http('https://httpbin.org/get').then(function (result) { console.log('Code: ' + result.response.statusCode) console.log('Response: ' + result.data) }) @@ -75,6 +76,8 @@ http.json(options).then(function (data) { var http = require('http.min') var options = { uri: 'https://httpbin.org/post', + // timeout in ms + timeout: 1000, // query string helper query: { hello: 'test' @@ -85,6 +88,16 @@ var options = { }, // handle JSON data json: true + // send JSON data and expect JSON response + json: { + this: 'is data' + }, + request: function (req) { + // hook to manipulate request before being sent + // instace of node http.ClientRequest + req.setTimeout(2000) + req.on('upgrade', ...) + } } http.post(options).then(function (result) { console.log('Code: ' + result.response.statusCode) diff --git a/node_modules/http.min/index.js b/node_modules/http.min/index.js index 40025ff..1c26c3a 100644 --- a/node_modules/http.min/index.js +++ b/node_modules/http.min/index.js @@ -55,12 +55,20 @@ METHODS.forEach(function (method) { response: response }) }) - }).on('error', function (err) { - reject(err) + }).on('error', reject) + if (options.timeout) { + req.setTimeout(options.timeout) + } + req.on('timeout', function () { + req.destroy() + reject('timeout') }) if (data) { req.write(data) } + if (options.request) { + options.request(req) + } req.end() }) if (options.json) { @@ -93,4 +101,6 @@ function parseJSON (promise) { }) } -module.exports = HTTP +// make http.get() default exported function +merge(HTTP.get, HTTP) +module.exports = HTTP.get diff --git a/node_modules/http.min/package.json b/node_modules/http.min/package.json index 537dee9..d9ceca5 100644 --- a/node_modules/http.min/package.json +++ b/node_modules/http.min/package.json @@ -1,13 +1,13 @@ { "name": "http.min", - "version": "1.2.4", + "version": "1.3.0", "description": "Minimalistic & simplistic HTTP client. No dependencies, under 100 SLOC.", "main": "index.js", "scripts": { - "test": "npm run standard --silent && npm run coverage --silent", + "pretest": "standard", + "test": "npm run coverage --silent", "mocha": "mocha", - "coverage": "istanbul cover _mocha", - "standard": "standard" + "coverage": "istanbul cover _mocha" }, "keywords": [ "http", @@ -27,19 +27,19 @@ "license": "ISC", "devDependencies": { "expect.js": "^0.3.1", - "istanbul": "^0.4.2", - "mocha": "^2.4.5", - "nock": "^7.3.0", - "standard": "^6.0.7" + "istanbul": "^0.4.3", + "mocha": "^2.5.3", + "nock": "^8.0.0", + "standard": "^7.0.0" }, - "gitHead": "4fea110112b6f55b95723ae2abc254106be3b5c1", + "gitHead": "313f9e355d9ce5d09e7c9e58eca7252cb52dc366", "bugs": { "url": "https://github.com/matjaz/node-http.min/issues" }, "homepage": "https://github.com/matjaz/node-http.min#readme", - "_id": "http.min@1.2.4", - "_shasum": "97c35b487015660cb1d147cf5adaa9cee241debd", - "_from": "http.min@*", + "_id": "http.min@1.3.0", + "_shasum": "e7b5131c8561d3e276e6ffba73d96e911c7b1028", + "_from": "http.min@1.3.0", "_npmVersion": "3.7.3", "_nodeVersion": "0.12.9", "_npmUser": { @@ -47,8 +47,8 @@ "email": "me@matjaz.info" }, "dist": { - "shasum": "97c35b487015660cb1d147cf5adaa9cee241debd", - "tarball": "http://registry.npmjs.org/http.min/-/http.min-1.2.4.tgz" + "shasum": "e7b5131c8561d3e276e6ffba73d96e911c7b1028", + "tarball": "https://registry.npmjs.org/http.min/-/http.min-1.3.0.tgz" }, "maintainers": [ { @@ -57,10 +57,9 @@ } ], "_npmOperationalInternal": { - "host": "packages-13-west.internal.npmjs.com", - "tmp": "tmp/http.min-1.2.4.tgz_1457553094472_0.3418956636451185" + "host": "packages-16-east.internal.npmjs.com", + "tmp": "tmp/http.min-1.3.0.tgz_1464336035262_0.8792552393861115" }, "directories": {}, - "_resolved": "https://registry.npmjs.org/http.min/-/http.min-1.2.4.tgz", - "readme": "ERROR: No README data found!" + "_resolved": "https://registry.npmjs.org/http.min/-/http.min-1.3.0.tgz" } diff --git a/node_modules/ws/Makefile b/node_modules/ws/Makefile index 00f19fa..94612c5 100644 --- a/node_modules/ws/Makefile +++ b/node_modules/ws/Makefile @@ -1,12 +1,6 @@ ALL_TESTS = $(shell find test/ -name '*.test.js') ALL_INTEGRATION = $(shell find test/ -name '*.integration.js') -all: - node-gyp configure build - -clean: - node-gyp clean - run-tests: @./node_modules/.bin/mocha \ -t 5000 \ @@ -21,12 +15,23 @@ run-integrationtests: $(TESTFLAGS) \ $(TESTS) +run-coverage: + @./node_modules/.bin/istanbul cover --report html \ + ./node_modules/.bin/_mocha -- \ + -t 5000 \ + -s 6000 \ + $(TESTFLAGS) \ + $(TESTS) + test: @$(MAKE) NODE_TLS_REJECT_UNAUTHORIZED=0 NODE_PATH=lib TESTS="$(ALL_TESTS)" run-tests integrationtest: @$(MAKE) NODE_TLS_REJECT_UNAUTHORIZED=0 NODE_PATH=lib TESTS="$(ALL_INTEGRATION)" run-integrationtests +coverage: + @$(MAKE) NODE_TLS_REJECT_UNAUTHORIZED=0 NODE_PATH=lib TESTS="$(ALL_TESTS)" run-coverage + benchmark: @node bench/sender.benchmark.js @node bench/parser.benchmark.js @@ -37,4 +42,4 @@ autobahn: autobahn-server: @NODE_PATH=lib node test/autobahn-server.js -.PHONY: test +.PHONY: test coverage diff --git a/node_modules/ws/README.md b/node_modules/ws/README.md index 9be2e51..93106d7 100644 --- a/node_modules/ws/README.md +++ b/node_modules/ws/README.md @@ -31,13 +31,13 @@ compiler is installed on the host system. - `npm install --save bufferutil`: Improves internal buffer operations which allows for faster processing of masked WebSocket frames and general buffer - operations. + operations. - `npm install --save utf-8-validate`: The specification requires validation of invalid UTF-8 chars, some of these validations could not be done in JavaScript hence the need for a binary addon. In most cases you will already be 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. ### Sending and receiving text data @@ -110,7 +110,7 @@ wss.on('connection', function connection(ws) { var location = url.parse(ws.upgradeReq.url, true); // you might use location.query.access_token to authenticate or share sessions // or ws.upgradeReq.headers.cookie (see http://stackoverflow.com/a/16395220/151312) - + ws.on('message', function incoming(message) { console.log('received: %s', message); }); @@ -161,7 +161,7 @@ catch (e) { /* handle error */ } ```js var WebSocket = require('ws'); var ws = new WebSocket('ws://echo.websocket.org/', { - protocolVersion: 8, + protocolVersion: 8, origin: 'http://websocket.org' }); @@ -183,13 +183,6 @@ ws.on('message', function message(data, flags) { }); ``` -### 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 For a full example with a browser client communicating with a ws server, see the diff --git a/node_modules/ws/SECURITY.md b/node_modules/ws/SECURITY.md new file mode 100644 index 0000000..fd8e07b --- /dev/null +++ b/node_modules/ws/SECURITY.md @@ -0,0 +1,33 @@ +# Security Guidelines + +Please contact us directly at **security@3rd-Eden.com** for any bug that might +impact the security of this project. Please prefix the subject of your email +with `[security]` in lowercase and square brackets. Our email filters will +automatically prevent these messages from being moved to our spam box. + +You will receive an acknowledgement of your report within **24 hours**. + +All emails that do not include security vulnerabilities will be removed and +blocked instantly. + +## Exceptions + +If you do not receive an acknowledgement within the said time frame please give +us the benefit of the doubt as it's possible that we haven't seen it yet. In +this case please send us a message **without details** using one of the +following methods: + +- Contact the lead developers of this project on their personal e-mails. You + can find the e-mails in the git logs, for example using the following command: + `git --no-pager show -s --format='%an <%ae>' ` where `` is the + SHA1 of their latest commit in the project. +- Create a GitHub issue stating contact details and the severity of the issue. + +Once we have acknowledged receipt of your report and confirmed the bug +ourselves we will work with you to fix the vulnerability and publicly acknowledge +your responsible disclosure, if you wish. In addition to that we will report +all vulnerabilities to the [Node Security Project](https://nodesecurity.io/). + +## History + +04 Jan 2016: [Buffer vulnerablity](https://github.com/websockets/ws/releases/tag/1.0.1) diff --git a/node_modules/ws/lib/BufferUtil.fallback.js b/node_modules/ws/lib/BufferUtil.fallback.js index 508542c..7abd0d8 100644 --- a/node_modules/ws/lib/BufferUtil.fallback.js +++ b/node_modules/ws/lib/BufferUtil.fallback.js @@ -4,7 +4,7 @@ * MIT Licensed */ -module.exports.BufferUtil = { +exports.BufferUtil = { merge: function(mergedBuffer, buffers) { var offset = 0; for (var i = 0, l = buffers.length; i < l; ++i) { diff --git a/node_modules/ws/lib/PerMessageDeflate.js b/node_modules/ws/lib/PerMessageDeflate.js index 5324bd8..00a6ea6 100644 --- a/node_modules/ws/lib/PerMessageDeflate.js +++ b/node_modules/ws/lib/PerMessageDeflate.js @@ -11,7 +11,7 @@ PerMessageDeflate.extensionName = 'permessage-deflate'; * Per-message Compression Extensions implementation */ -function PerMessageDeflate(options, isServer) { +function PerMessageDeflate(options, isServer,maxPayload) { if (this instanceof PerMessageDeflate === false) { throw new TypeError("Classes can't be function-called"); } @@ -21,6 +21,7 @@ function PerMessageDeflate(options, isServer) { this._inflate = null; this._deflate = null; this.params = null; + this._maxPayload = maxPayload || 0; } /** @@ -236,6 +237,7 @@ PerMessageDeflate.prototype.decompress = function (data, fin, callback) { var self = this; var buffers = []; + var cumulativeBufferLength=0; this._inflate.on('error', onError).on('data', onData); this._inflate.write(data); @@ -253,7 +255,17 @@ PerMessageDeflate.prototype.decompress = function (data, fin, callback) { } 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); } function cleanup() { diff --git a/node_modules/ws/lib/Receiver.hixie.js b/node_modules/ws/lib/Receiver.hixie.js index 66bc561..598ccbd 100644 --- a/node_modules/ws/lib/Receiver.hixie.js +++ b/node_modules/ws/lib/Receiver.hixie.js @@ -47,6 +47,7 @@ module.exports = Receiver; */ Receiver.prototype.add = function(data) { + if (this.dead) return; var self = this; function doAdd() { if (self.state === EMPTY) { @@ -153,8 +154,17 @@ Receiver.prototype.parse = function() { */ 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; }; diff --git a/node_modules/ws/lib/Receiver.js b/node_modules/ws/lib/Receiver.js index b3183bf..0bf29d8 100644 --- a/node_modules/ws/lib/Receiver.js +++ b/node_modules/ws/lib/Receiver.js @@ -15,10 +15,15 @@ var util = require('util') * HyBi Receiver implementation */ -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 var fragmentedPoolPrevUsed = -1; @@ -39,8 +44,9 @@ function Receiver (extensions) { Math.ceil((unfragmentedPoolPrevUsed + db.used) / 2) : db.used; }); - this.extensions = extensions || {}; + this.maxPayload = maxPayload || 0; + this.currentPayloadLength = 0; this.state = { activeFragmentedOperation: null, lastFragment: false, @@ -54,6 +60,7 @@ function Receiver (extensions) { this.expectBuffer = null; this.expectHandler = null; this.currentMessage = []; + this.currentMessageLength = 0; this.messageHandlers = []; this.expectHeader(2, this.processPacket); this.dead = false; @@ -76,6 +83,7 @@ module.exports = Receiver; */ Receiver.prototype.add = function(data) { + if (this.dead) return; var dataLength = data.length; if (dataLength == 0) return; if (this.expectBuffer == null) { @@ -244,6 +252,7 @@ Receiver.prototype.processPacket = function (data) { */ 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; @@ -253,6 +262,7 @@ Receiver.prototype.endPacket = function() { // 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; @@ -281,7 +291,9 @@ Receiver.prototype.reset = function() { this.expectHandler = null; this.overflow = []; this.currentMessage = []; + this.currentMessageLength = 0; this.messageHandlers = []; + this.currentPayloadLength = 0; }; /** @@ -296,20 +308,6 @@ Receiver.prototype.unmask = function (mask, buf, binary) { return buf != null ? buf.toString('utf8') : ''; }; -/** - * 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 * @@ -317,8 +315,17 @@ Receiver.prototype.concatBuffers = function(buffers) { */ 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; }; @@ -365,6 +372,27 @@ Receiver.prototype.applyExtensions = function(messageBuffer, fin, compressed, ca } }; +/** +* 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 */ @@ -425,11 +453,20 @@ var opcodes = { // 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) { - 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); }); } else if (firstLength == 127) { @@ -438,6 +475,11 @@ var opcodes = { 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)); }); } @@ -464,12 +506,29 @@ var opcodes = { var state = clone(this.state); this.messageHandlers.push(function(callback) { 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)) { self.error('invalid utf8 sequence', 1007); return; @@ -490,11 +549,20 @@ var opcodes = { // 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) { - 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); }); } else if (firstLength == 127) { @@ -503,7 +571,12 @@ var opcodes = { self.error('packets with length spanning more than 32 bit is currently not supported', 1008); return; } - 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); }); } }, @@ -529,11 +602,29 @@ var opcodes = { var state = clone(this.state); this.messageHandlers.push(function(callback) { 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}); } callback(); diff --git a/node_modules/ws/lib/Sender.js b/node_modules/ws/lib/Sender.js index d34061e..6ef2ea2 100644 --- a/node_modules/ws/lib/Sender.js +++ b/node_modules/ws/lib/Sender.js @@ -197,7 +197,7 @@ Sender.prototype.frameAndSend = function(opcode, data, finalFragment, maskData, if (maskData) { outputBuffer[1] = secondByte | 0x80; - var mask = this._randomMask || (this._randomMask = getRandomMask()); + var mask = getRandomMask(); outputBuffer[dataOffset - 4] = mask[0]; outputBuffer[dataOffset - 3] = mask[1]; outputBuffer[dataOffset - 2] = mask[2]; diff --git a/node_modules/ws/lib/Validation.fallback.js b/node_modules/ws/lib/Validation.fallback.js index 2c7c4fd..639b0d3 100644 --- a/node_modules/ws/lib/Validation.fallback.js +++ b/node_modules/ws/lib/Validation.fallback.js @@ -3,10 +3,9 @@ * Copyright(c) 2011 Einar Otto Stangvik * MIT Licensed */ - -module.exports.Validation = { + +exports.Validation = { isValidUTF8: function(buffer) { return true; } }; - diff --git a/node_modules/ws/lib/WebSocket.js b/node_modules/ws/lib/WebSocket.js index 4e06c80..bb09e85 100644 --- a/node_modules/ws/lib/WebSocket.js +++ b/node_modules/ws/lib/WebSocket.js @@ -71,6 +71,7 @@ function WebSocket(address, protocols, options) { this.readyState = null; this.supports = {}; this.extensions = {}; + this._binaryType = 'nodebuffer'; if (Array.isArray(address)) { initAsServerClient.apply(this, address.concat(options)); @@ -371,6 +372,27 @@ Object.defineProperty(WebSocket.prototype, 'bufferedAmount', { } }); +/** + * 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. * @@ -415,6 +437,8 @@ WebSocket.prototype.addEventListener = function(method, listener) { var target = this; function onMessage (data, flags) { + if (flags.binary && this.binaryType === 'arraybuffer') + data = new Uint8Array(data).buffer; listener.call(target, new MessageEvent(data, !!flags.binary, target)); } @@ -523,7 +547,8 @@ function initAsServerClient(req, socket, upgradeHead, options) { options = new Options({ protocolVersion: protocolVersion, protocol: null, - extensions: {} + extensions: {}, + maxPayload: 0 }).merge(options); // expose state properties @@ -534,7 +559,7 @@ function initAsServerClient(req, socket, upgradeHead, options) { this.upgradeReq = req; this.readyState = WebSocket.CONNECTING; this._isServer = true; - + this.maxPayload = options.value.maxPayload; // establish connection if (options.value.protocolVersion === 'hixie-76') { establishConnection.call(this, ReceiverHixie, SenderHixie, socket, upgradeHead); @@ -770,7 +795,7 @@ function establishConnection(ReceiverClass, SenderClass, socket, upgradeHead) { socket.setTimeout(0); socket.setNoDelay(true); - this._receiver = new ReceiverClass(this.extensions); + this._receiver = new ReceiverClass(this.extensions,this.maxPayload); this._socket = socket; // socket cleanup handlers @@ -848,7 +873,7 @@ function establishConnection(ReceiverClass, SenderClass, socket, upgradeHead) { self._receiver.onerror = function onerror(reason, errorCode) { // close the connection when the receiver reports a HyBi error code self.close(typeof errorCode !== 'undefined' ? errorCode : 1002, ''); - self.emit('error', reason, errorCode); + self.emit('error', (reason instanceof Error) ? reason : (new Error(reason))); }; // finalize the client @@ -911,21 +936,18 @@ function sendStream(instance, stream, options, cb) { function cleanupWebsocketResources(error) { if (this.readyState === WebSocket.CLOSED) return; - var emitClose = this.readyState !== WebSocket.CONNECTING; this.readyState = WebSocket.CLOSED; clearTimeout(this._closeTimer); this._closeTimer = null; - 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 || ''); if (this._socket) { if (this._ultron) this._ultron.destroy(); diff --git a/node_modules/ws/lib/WebSocketServer.js b/node_modules/ws/lib/WebSocketServer.js index ba0e4c0..476cf71 100644 --- a/node_modules/ws/lib/WebSocketServer.js +++ b/node_modules/ws/lib/WebSocketServer.js @@ -36,7 +36,8 @@ function WebSocketServer(options, callback) { noServer: false, disableHixie: false, clientTracking: true, - perMessageDeflate: true + perMessageDeflate: true, + maxPayload: null }).merge(options); if (!options.isDefinedAndNonNull('port') && !options.isDefinedAndNonNull('server') && !options.value.noServer) { @@ -72,13 +73,15 @@ function WebSocketServer(options, callback) { this._server._webSocketPaths[options.value.path] = 1; } } - 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 var head = new Buffer(upgradeHead.length); upgradeHead.copy(head); @@ -87,7 +90,8 @@ function WebSocketServer(options, callback) { self.emit('connection'+req.url, client); self.emit('connection', client); }); - }); + }; + this._server.on('upgrade', this._onServerUpgrade); } this.options = options.value; @@ -134,6 +138,11 @@ WebSocketServer.prototype.close = function(callback) { } } 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; } if(callback) @@ -256,7 +265,8 @@ function handleHybiUpgrade(req, socket, upgradeHead, cb) { var client = new WebSocket([req, socket, upgradeHead], { protocolVersion: version, protocol: protocol, - extensions: extensions + extensions: extensions, + maxPayload: self.options.maxPayload }); if (self.options.clientTracking) { @@ -359,8 +369,42 @@ function handleHixieUpgrade(req, socket, upgradeHead, cb) { var location = ((req.headers['x-forwarded-proto'] === 'https' || socket.encrypted) ? 'wss' : 'ws') + '://' + wshost + req.url , protocol = req.headers['sec-websocket-protocol']; + // 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 var k1 = req.headers['sec-websocket-key1'] , k2 = req.headers['sec-websocket-key2'] @@ -382,20 +426,10 @@ function handleHixieUpgrade(req, socket, upgradeHead, cb) { }); md5.update(nonce.toString('binary')); - 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'); var handshakeBuffer = new Buffer(headerBuffer.length + hashBuffer.length); headerBuffer.copy(handshakeBuffer, 0); @@ -434,11 +468,10 @@ function handleHixieUpgrade(req, socket, upgradeHead, cb) { if (upgradeHead && upgradeHead.length >= nonceLength) { var nonce = upgradeHead.slice(0, nonceLength); 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); upgradeHead.copy(nonce, 0); var received = upgradeHead.length; @@ -451,10 +484,17 @@ function handleHixieUpgrade(req, socket, upgradeHead, cb) { if (received == nonceLength) { socket.removeListener('data', handler); 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(); } } @@ -489,8 +529,9 @@ function handleHixieUpgrade(req, socket, upgradeHead, cb) { function acceptExtensions(offer) { var extensions = {}; 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]); extensions[PerMessageDeflate.extensionName] = perMessageDeflate; } diff --git a/node_modules/ws/package.json b/node_modules/ws/package.json index 8fc7d29..087c590 100644 --- a/node_modules/ws/package.json +++ b/node_modules/ws/package.json @@ -6,8 +6,9 @@ }, "name": "ws", "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": [ "Hixie", "HyBi", @@ -33,22 +34,23 @@ "benchmark": "0.3.x", "bufferutil": "1.2.x", "expect.js": "0.3.x", + "istanbul": "^0.4.1", "mocha": "2.3.x", "should": "8.0.x", "tinycolor": "0.0.x", "utf-8-validate": "1.2.x" }, "gypfile": true, - "gitHead": "40a9d686288b5d0be13f2bf2f3f5da07afc8cda2", + "gitHead": "4263f26d4dbe27e781c41a1ddfe3dab87dd9e1dc", "bugs": { "url": "https://github.com/websockets/ws/issues" }, "homepage": "https://github.com/websockets/ws#readme", - "_id": "ws@1.0.1", - "_shasum": "7d0b2a2e58cddd819039c29c9de65045e1b310e9", - "_from": "ws@*", - "_npmVersion": "3.5.1", - "_nodeVersion": "4.2.3", + "_id": "ws@1.1.0", + "_shasum": "c1d6fd1515d3ceff1f0ae2759bf5fd77030aad1d", + "_from": "ws@1.1.0", + "_npmVersion": "3.8.0", + "_nodeVersion": "4.3.1", "_npmUser": { "name": "3rdeden", "email": "npm@3rd-Eden.com" @@ -68,10 +70,13 @@ } ], "dist": { - "shasum": "7d0b2a2e58cddd819039c29c9de65045e1b310e9", - "tarball": "http://registry.npmjs.org/ws/-/ws-1.0.1.tgz" + "shasum": "c1d6fd1515d3ceff1f0ae2759bf5fd77030aad1d", + "tarball": "https://registry.npmjs.org/ws/-/ws-1.1.0.tgz" + }, + "_npmOperationalInternal": { + "host": "packages-16-east.internal.npmjs.com", + "tmp": "tmp/ws-1.1.0.tgz_1460376022305_0.992860296042636" }, "directories": {}, - "_resolved": "https://registry.npmjs.org/ws/-/ws-1.0.1.tgz", - "readme": "ERROR: No README data found!" + "_resolved": "https://registry.npmjs.org/ws/-/ws-1.1.0.tgz" }