commit 962764c034e410eddc69dfa5cb2dac4f7ea650ac Author: jefklak Date: Thu Feb 6 19:23:06 2014 +0100 initial commit diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/.DS_Store differ diff --git a/cam/index.html b/cam/index.html new file mode 100644 index 0000000..f1cf60c --- /dev/null +++ b/cam/index.html @@ -0,0 +1,67 @@ + + + + + + + + + + + +

Cam capture live feed

+ cam +

+ DO NOT REFRESH page, happens automatically every 60 seconds.
+ Last update:

+

+
+

HOURLY Cam history

+ +
+ Fetching... Please wait. +
+
+ + + + diff --git a/cam/index.json b/cam/index.json new file mode 100644 index 0000000..6765dbb --- /dev/null +++ b/cam/index.json @@ -0,0 +1 @@ +{"files":[{"name":"image.jpg","type":0,"time":1381927500000,"size":"490610","owner":"ftp","group":"ftp","userPermissions":{"read":true,"write":true,"exec":false},"groupPermissions":{"read":true,"write":false,"exec":false},"otherPermissions":{"read":true,"write":false,"exec":false}}]} \ No newline at end of file diff --git a/cam/lazyload.js b/cam/lazyload.js new file mode 100644 index 0000000..26cc801 --- /dev/null +++ b/cam/lazyload.js @@ -0,0 +1,239 @@ +/* + * Lazy Load - jQuery plugin for lazy loading images + * + * Copyright (c) 2007-2013 Mika Tuupola + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/mit-license.php + * + * Project home: + * http://www.appelsiini.net/projects/lazyload + * + * Version: 1.9.0 + * + */ + +(function($, window, document, undefined) { + var $window = $(window); + + $.fn.lazyload = function(options) { + var elements = this; + var $container; + var settings = { + threshold : 0, + failure_limit : 0, + event : "scroll", + effect : "show", + container : window, + data_attribute : "original", + skip_invisible : true, + appear : null, + load : null, + placeholder : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" + }; + + function update() { + var counter = 0; + + elements.each(function() { + var $this = $(this); + if (settings.skip_invisible && !$this.is(":visible")) { + return; + } + if ($.abovethetop(this, settings) || + $.leftofbegin(this, settings)) { + /* Nothing. */ + } else if (!$.belowthefold(this, settings) && + !$.rightoffold(this, settings)) { + $this.trigger("appear"); + /* if we found an image we'll load, reset the counter */ + counter = 0; + } else { + if (++counter > settings.failure_limit) { + return false; + } + } + }); + + } + + if(options) { + /* Maintain BC for a couple of versions. */ + if (undefined !== options.failurelimit) { + options.failure_limit = options.failurelimit; + delete options.failurelimit; + } + if (undefined !== options.effectspeed) { + options.effect_speed = options.effectspeed; + delete options.effectspeed; + } + + $.extend(settings, options); + } + + /* Cache container as jQuery as object. */ + $container = (settings.container === undefined || + settings.container === window) ? $window : $(settings.container); + + /* Fire one scroll event per scroll. Not one scroll event per image. */ + if (0 === settings.event.indexOf("scroll")) { + $container.bind(settings.event, function() { + return update(); + }); + } + + this.each(function() { + var self = this; + var $self = $(self); + + self.loaded = false; + + /* If no src attribute given use data:uri. */ + if ($self.attr("src") === undefined || $self.attr("src") === false) { + $self.attr("src", settings.placeholder); + } + + /* When appear is triggered load original image. */ + $self.one("appear", function() { + if (!this.loaded) { + if (settings.appear) { + var elements_left = elements.length; + settings.appear.call(self, elements_left, settings); + } + $("") + .bind("load", function() { + var original = $self.data(settings.data_attribute); + $self.hide(); + if ($self.is("img")) { + $self.attr("src", original); + } else { + $self.css("background-image", "url('" + original + "')"); + } + $self[settings.effect](settings.effect_speed); + + self.loaded = true; + + /* Remove image from array so it is not looped next time. */ + var temp = $.grep(elements, function(element) { + return !element.loaded; + }); + elements = $(temp); + + if (settings.load) { + var elements_left = elements.length; + settings.load.call(self, elements_left, settings); + } + }) + .attr("src", $self.data(settings.data_attribute)); + } + }); + + /* When wanted event is triggered load original image */ + /* by triggering appear. */ + if (0 !== settings.event.indexOf("scroll")) { + $self.bind(settings.event, function() { + if (!self.loaded) { + $self.trigger("appear"); + } + }); + } + }); + + /* Check if something appears when window is resized. */ + $window.bind("resize", function() { + update(); + }); + + /* With IOS5 force loading images when navigating with back button. */ + /* Non optimal workaround. */ + if ((/iphone|ipod|ipad.*os 5/gi).test(navigator.appVersion)) { + $window.bind("pageshow", function(event) { + if (event.originalEvent && event.originalEvent.persisted) { + elements.each(function() { + $(this).trigger("appear"); + }); + } + }); + } + + /* Force initial check if images should appear. */ + $(document).ready(function() { + update(); + }); + + return this; + }; + + /* Convenience methods in jQuery namespace. */ + /* Use as $.belowthefold(element, {threshold : 100, container : window}) */ + + $.belowthefold = function(element, settings) { + var fold; + + if (settings.container === undefined || settings.container === window) { + fold = (window.innerHeight ? window.innerHeight : $window.height()) + $window.scrollTop(); + } else { + fold = $(settings.container).offset().top + $(settings.container).height(); + } + + return fold <= $(element).offset().top - settings.threshold; + }; + + $.rightoffold = function(element, settings) { + var fold; + + if (settings.container === undefined || settings.container === window) { + fold = $window.width() + $window.scrollLeft(); + } else { + fold = $(settings.container).offset().left + $(settings.container).width(); + } + + return fold <= $(element).offset().left - settings.threshold; + }; + + $.abovethetop = function(element, settings) { + var fold; + + if (settings.container === undefined || settings.container === window) { + fold = $window.scrollTop(); + } else { + fold = $(settings.container).offset().top; + } + + return fold >= $(element).offset().top + settings.threshold + $(element).height(); + }; + + $.leftofbegin = function(element, settings) { + var fold; + + if (settings.container === undefined || settings.container === window) { + fold = $window.scrollLeft(); + } else { + fold = $(settings.container).offset().left; + } + + return fold >= $(element).offset().left + settings.threshold + $(element).width(); + }; + + $.inviewport = function(element, settings) { + return !$.rightoffold(element, settings) && !$.leftofbegin(element, settings) && + !$.belowthefold(element, settings) && !$.abovethetop(element, settings); + }; + + /* Custom selectors for your convenience. */ + /* Use as $("img:below-the-fold").something() or */ + /* $("img").filter(":below-the-fold").something() which is faster */ + + $.extend($.expr[":"], { + "below-the-fold" : function(a) { return $.belowthefold(a, {threshold : 0}); }, + "above-the-top" : function(a) { return !$.belowthefold(a, {threshold : 0}); }, + "right-of-screen": function(a) { return $.rightoffold(a, {threshold : 0}); }, + "left-of-screen" : function(a) { return !$.rightoffold(a, {threshold : 0}); }, + "in-viewport" : function(a) { return $.inviewport(a, {threshold : 0}); }, + /* Maintain BC for couple of versions. */ + "above-the-fold" : function(a) { return !$.belowthefold(a, {threshold : 0}); }, + "right-of-fold" : function(a) { return $.rightoffold(a, {threshold : 0}); }, + "left-of-fold" : function(a) { return !$.rightoffold(a, {threshold : 0}); } + }); + +})(jQuery, window, document); diff --git a/clientcam.html b/clientcam.html new file mode 100755 index 0000000..7f1d18b --- /dev/null +++ b/clientcam.html @@ -0,0 +1,73 @@ + + + + + + + + +

hello!

+ +klik hier om video te capturen.
+ +
+ +

Streamed image:

+ + \ No newline at end of file diff --git a/compress.js b/compress.js new file mode 100644 index 0000000..7ed67fd --- /dev/null +++ b/compress.js @@ -0,0 +1,6 @@ +var gm = require("gm").subClass({ ImageMagick : true }); + +gm("image.jpg").quality(60).compress().write("compressed.jpg", function(e) { + if(e) throw e; // Error: spawn ENOENT ? ImageMagick niet op PATH. + console.log("done"); +}); diff --git a/ftpindex.js b/ftpindex.js new file mode 100644 index 0000000..e671e5b --- /dev/null +++ b/ftpindex.js @@ -0,0 +1,32 @@ +var Ftp = require("jsftp"), fs = require("fs"); + +var user = "FILL ME IN"; +var pass = "FILL ME IN"; +var root = "/domains/brainbaking.com/public_html"; + +var ftp = new Ftp({ + host: "ftp.brainbaking.com", + port: 21 +}); + +ftp.auth(user, pass, function(err, res) { + var images = { files: [] }; + ftp.ls(root + "/cam", function(err, list) { + + images.files = list.filter(function(file) { + return file.name.indexOf('.jpg') > 0; + }); + + console.log("writing " + images); + fs.writeFile("index.json", JSON.stringify(images), function(e) { + if(e) throw e; + + ftp.put("index.json", root + "/cam/index.json", function(e) { + if(e) throw e; + console.log("written index JSON file."); + }); + }); + + }); + +}); diff --git a/image.jpg b/image.jpg new file mode 100644 index 0000000..e893490 Binary files /dev/null and b/image.jpg differ diff --git a/index.json b/index.json new file mode 100644 index 0000000..04abe9d --- /dev/null +++ b/index.json @@ -0,0 +1 @@ +{"files":[{"name":"1381937362359_image.jpg","type":0,"time":1381937340000,"size":"550409","owner":"ftp","group":"ftp","userPermissions":{"read":true,"write":true,"exec":false},"groupPermissions":{"read":true,"write":false,"exec":false},"otherPermissions":{"read":true,"write":false,"exec":false}},{"name":"1381937569375_image.jpg","type":0,"time":1381937520000,"size":"586258","owner":"ftp","group":"ftp","userPermissions":{"read":true,"write":true,"exec":false},"groupPermissions":{"read":true,"write":false,"exec":false},"otherPermissions":{"read":true,"write":false,"exec":false}},{"name":"1381989036320_image.jpg","type":0,"time":1381989000000,"size":"533100","owner":"ftp","group":"ftp","userPermissions":{"read":true,"write":true,"exec":false},"groupPermissions":{"read":true,"write":false,"exec":false},"otherPermissions":{"read":true,"write":false,"exec":false}},{"name":"1381989636744_image.jpg","type":0,"time":1381989600000,"size":"633357","owner":"ftp","group":"ftp","userPermissions":{"read":true,"write":true,"exec":false},"groupPermissions":{"read":true,"write":false,"exec":false},"otherPermissions":{"read":true,"write":false,"exec":false}},{"name":"1381993235977_image.jpg","type":0,"time":1381993200000,"size":"545913","owner":"ftp","group":"ftp","userPermissions":{"read":true,"write":true,"exec":false},"groupPermissions":{"read":true,"write":false,"exec":false},"otherPermissions":{"read":true,"write":false,"exec":false}},{"name":"1381996835944_image.jpg","type":0,"time":1381996800000,"size":"543081","owner":"ftp","group":"ftp","userPermissions":{"read":true,"write":true,"exec":false},"groupPermissions":{"read":true,"write":false,"exec":false},"otherPermissions":{"read":true,"write":false,"exec":false}},{"name":"1382000435066_image.jpg","type":0,"time":1382000400000,"size":"534940","owner":"ftp","group":"ftp","userPermissions":{"read":true,"write":true,"exec":false},"groupPermissions":{"read":true,"write":false,"exec":false},"otherPermissions":{"read":true,"write":false,"exec":false}},{"name":"1382004035158_image.jpg","type":0,"time":1382004000000,"size":"543900","owner":"ftp","group":"ftp","userPermissions":{"read":true,"write":true,"exec":false},"groupPermissions":{"read":true,"write":false,"exec":false},"otherPermissions":{"read":true,"write":false,"exec":false}},{"name":"1382007635067_image.jpg","type":0,"time":1382007600000,"size":"534479","owner":"ftp","group":"ftp","userPermissions":{"read":true,"write":true,"exec":false},"groupPermissions":{"read":true,"write":false,"exec":false},"otherPermissions":{"read":true,"write":false,"exec":false}},{"name":"1382011234993_image.jpg","type":0,"time":1382011200000,"size":"535949","owner":"ftp","group":"ftp","userPermissions":{"read":true,"write":true,"exec":false},"groupPermissions":{"read":true,"write":false,"exec":false},"otherPermissions":{"read":true,"write":false,"exec":false}},{"name":"1382014835242_image.jpg","type":0,"time":1382014800000,"size":"550919","owner":"ftp","group":"ftp","userPermissions":{"read":true,"write":true,"exec":false},"groupPermissions":{"read":true,"write":false,"exec":false},"otherPermissions":{"read":true,"write":false,"exec":false}},{"name":"1382018434860_image.jpg","type":0,"time":1382018400000,"size":"523887","owner":"ftp","group":"ftp","userPermissions":{"read":true,"write":true,"exec":false},"groupPermissions":{"read":true,"write":false,"exec":false},"otherPermissions":{"read":true,"write":false,"exec":false}},{"name":"1382022034866_image.jpg","type":0,"time":1382022000000,"size":"520789","owner":"ftp","group":"ftp","userPermissions":{"read":true,"write":true,"exec":false},"groupPermissions":{"read":true,"write":false,"exec":false},"otherPermissions":{"read":true,"write":false,"exec":false}}]} \ No newline at end of file diff --git a/node_modules/.bin/wscat b/node_modules/.bin/wscat new file mode 120000 index 0000000..06c3b74 --- /dev/null +++ b/node_modules/.bin/wscat @@ -0,0 +1 @@ +../ws/bin/wscat \ No newline at end of file diff --git a/node_modules/jsftp/.idea/.name b/node_modules/jsftp/.idea/.name new file mode 100644 index 0000000..2d9be14 --- /dev/null +++ b/node_modules/jsftp/.idea/.name @@ -0,0 +1 @@ +jsftp \ No newline at end of file diff --git a/node_modules/jsftp/.idea/codeStyleSettings.xml b/node_modules/jsftp/.idea/codeStyleSettings.xml new file mode 100644 index 0000000..9178b38 --- /dev/null +++ b/node_modules/jsftp/.idea/codeStyleSettings.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/node_modules/jsftp/.idea/encodings.xml b/node_modules/jsftp/.idea/encodings.xml new file mode 100644 index 0000000..e206d70 --- /dev/null +++ b/node_modules/jsftp/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/node_modules/jsftp/.idea/inspectionProfiles/Project_Default.xml b/node_modules/jsftp/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..d036fa9 --- /dev/null +++ b/node_modules/jsftp/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/node_modules/jsftp/.idea/inspectionProfiles/profiles_settings.xml b/node_modules/jsftp/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..3b31283 --- /dev/null +++ b/node_modules/jsftp/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/node_modules/jsftp/.idea/jsLibraryMappings.xml b/node_modules/jsftp/.idea/jsLibraryMappings.xml new file mode 100644 index 0000000..2350bfd --- /dev/null +++ b/node_modules/jsftp/.idea/jsLibraryMappings.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/node_modules/jsftp/.idea/jsftp.iml b/node_modules/jsftp/.idea/jsftp.iml new file mode 100644 index 0000000..bd9a540 --- /dev/null +++ b/node_modules/jsftp/.idea/jsftp.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/node_modules/jsftp/.idea/libraries/Node_js_Dependencies_for_jsftp.xml b/node_modules/jsftp/.idea/libraries/Node_js_Dependencies_for_jsftp.xml new file mode 100644 index 0000000..493fb38 --- /dev/null +++ b/node_modules/jsftp/.idea/libraries/Node_js_Dependencies_for_jsftp.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/node_modules/jsftp/.idea/misc.xml b/node_modules/jsftp/.idea/misc.xml new file mode 100644 index 0000000..d16ceba --- /dev/null +++ b/node_modules/jsftp/.idea/misc.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/node_modules/jsftp/.idea/modules.xml b/node_modules/jsftp/.idea/modules.xml new file mode 100644 index 0000000..4b6a084 --- /dev/null +++ b/node_modules/jsftp/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/node_modules/jsftp/.idea/scopes/scope_settings.xml b/node_modules/jsftp/.idea/scopes/scope_settings.xml new file mode 100644 index 0000000..922003b --- /dev/null +++ b/node_modules/jsftp/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/node_modules/jsftp/.idea/vcs.xml b/node_modules/jsftp/.idea/vcs.xml new file mode 100644 index 0000000..c80f219 --- /dev/null +++ b/node_modules/jsftp/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/node_modules/jsftp/.idea/workspace.xml b/node_modules/jsftp/.idea/workspace.xml new file mode 100644 index 0000000..f4b7292 --- /dev/null +++ b/node_modules/jsftp/.idea/workspace.xml @@ -0,0 +1,588 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bitwise operation issuesJavaScript + + + Control flow issuesJavaScript + + + GeneralJavaScript + + + JavaScript + + + Potentially confusing code constructsJavaScript + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1366213834349 + 1366213834349 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/node_modules/jsftp/.npmignore b/node_modules/jsftp/.npmignore new file mode 100644 index 0000000..eb33546 --- /dev/null +++ b/node_modules/jsftp/.npmignore @@ -0,0 +1,5 @@ +coverage.html +*.un~ +lib-cov +reports +.c9revisions diff --git a/node_modules/jsftp/.travis.yml b/node_modules/jsftp/.travis.yml new file mode 100644 index 0000000..27aafa2 --- /dev/null +++ b/node_modules/jsftp/.travis.yml @@ -0,0 +1,9 @@ +language: node_js +node_js: + - "0.10" + - "0.8" + - "0.6" + +before_script: + - "npm install --dev" + diff --git a/node_modules/jsftp/LICENSE b/node_modules/jsftp/LICENSE new file mode 100644 index 0000000..8a34f6d --- /dev/null +++ b/node_modules/jsftp/LICENSE @@ -0,0 +1,22 @@ +The MIT License + +Copyright(c) 2011 Ajax.org B.V. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/node_modules/jsftp/Makefile b/node_modules/jsftp/Makefile new file mode 100644 index 0000000..78f4696 --- /dev/null +++ b/node_modules/jsftp/Makefile @@ -0,0 +1,31 @@ +# use the tools as dev dependencies rather than installing them globaly +# it lets you handle specific versions of the tooling for each of your projects +MOCHA=node_modules/.bin/mocha +ISTANBUL=node_modules/.bin/istanbul +JSHINT=node_modules/.bin/jshint + +# test files must end with ".test.js" +TESTS=$(shell find test/ -name "*.test.js") + +clean: + rm -rf reports + +test: + $(MOCHA) -R spec $(TESTS) + +_MOCHA="node_modules/.bin/_mocha" +coverage: + @# check if reports folder exists, if not create it + @test -d reports || mkdir reports + $(ISTANBUL) cover --report lcovonly --dir ./reports $(_MOCHA) -- -R spec $(TESTS) + genhtml reports/lcov.info --output-directory reports/ + +jshint: + $(JSHINT) lib test --show-non-errors + +checkstyle: + @# check if reports folder exists, if not create it + @test -d reports || mkdir reports + $(JSHINT) lib test --reporter=checkstyle > reports/checkstyle.xml + +.PHONY: clean test coverage jshint checkstyle diff --git a/node_modules/jsftp/README.md b/node_modules/jsftp/README.md new file mode 100644 index 0000000..51d9fae --- /dev/null +++ b/node_modules/jsftp/README.md @@ -0,0 +1,235 @@ +jsftp Flattr this +===== + +A client FTP library for NodeJS that focuses on correctness, clarity +and conciseness. It doesn't get in the way and plays nice with streaming APIs. + +[![NPM](https://nodei.co/npm/jsftp.png)](https://nodei.co/npm/jsftp/) + +**Warning: The latest version (1.0.0) of jsftp breaks API compatibility with previous +versions, it is NOT a drop-in replacement. Please be careful when upgrading. The +API changes are not drastic at all and it is all documented below. If you do not +want to upgrade yet you should stay with version 0.6.0, the last one before the +upgrade. The API docs below are updated for 1.0.** + +Starting it up +-------------- + +```javascript +var JSFtp = require("jsftp"); + +var Ftp = new JSFtp({ + host: "myserver.com", + port: 3331, // defaults to 21 + user: "user", // defaults to "anonymous" + pass: "1234" // defaults to "@anonymous" +}; +``` + +jsftp gives you access to all the raw commands of the FTP protocol in form of +methods in the `Ftp` object. It also provides several convenience methods for +actions that require complex chains of commands (e.g. uploading and retrieving +files, passive operations), as shown below. + +When raw commands succeed they always pass the response of the server to the +callback, in the form of an object that contains two properties: `code`, which +is the response code of the FTP operation, and `text`, which is the complete +text of the response. + +Raw (or native) commands are accessible in the form `Ftp.raw["command"](params, callback)` + +Thus, a command like `QUIT` will be called like this: + +```javascript +Ftp.raw.quit(function(err, data) { + if (err) return console.error(err); + + console.log("Bye!"); +}); +``` + +and a command like `MKD` (make directory), which accepts parameters, looks like this: + +```javascript +Ftp.raw.mkd("/new_dir", function(err, data) { + if (err) return console.error(err); + + console.log(data.text); // Show the FTP response text to the user + console.log(data.code); // Show the FTP response code to the user +}); +``` + +API and examples +---------------- + +#### new Ftp(options) + - `options` is an object with the following properties: + + ```javascript + { + host: 'localhost', // Host name for the current FTP server. + port: 3333, // Port number for the current FTP server (defaults to 21). + user: 'user', // Username + pass: 'pass', // Password + } + ``` + +Creates a new Ftp instance. + + +#### Ftp.host + +Host name for the current FTP server. + +#### Ftp.port + +Port number for the current FTP server (defaults to 21). + +#### Ftp.socket + +NodeJS socket for the current FTP server. + +#### Ftp.features + +Array of feature names for the current FTP server. It is +generated when the user authenticates with the `auth` method. + +#### Ftp.system + +Contains the system identification string for the remote FTP server. + + +### Methods + +#### Ftp.raw.FTP_COMMAND([params], callback) +All the standard FTP commands are available under the `raw` namespace. These +commands might accept parameters or not, but they always accept a callback +with the signature `err, data`, in which `err` is the error response coming +from the server (usually a 4xx or 5xx error code) and the data is an object +that contains two properties: `code` and `text`. `code` is an integer indicating +the response code of the response and `text` is the response string itself. + +#### Ftp.auth(username, password, callback) +Authenticates the user with the given username and password. If null or empty +values are passed for those, `auth` will use anonymous credentials. `callback` +will be called with the response text in case of successful login or with an +error as a first parameter, in normal Node fashion. + +#### Ftp.ls(filePath, callback) +Lists information about files or directories and yields an array of file objects +with parsed file properties to the `callback`. You should use this function +instead of `stat` or `list` in case you need to do something with the individual +file properties. + +```javascript +ftp.ls(".", function(err, res) { + res.forEach(function(file) { + console.log(file.name); + }); +}); +``` + +#### Ftp.list(filePath, callback) +Lists `filePath` contents using a passive connection. Calls callback with an +array of strings with complete file information. + +```javascript +ftp.list(remoteCWD, function(err, res) { + res.forEach(function(file) { + console.log(file.name); + }); + // Prints something like + // -rw-r--r-- 1 sergi staff 4 Jun 03 09:32 testfile1.txt + // -rw-r--r-- 1 sergi staff 4 Jun 03 09:31 testfile2.txt + // -rw-r--r-- 1 sergi staff 0 May 29 13:05 testfile3.txt + // ... +}); +``` + +#### Ftp.get(remotePath, callback) +Gives back a paused socket with the file contents ready to be streamed, +or calls the callback with an error if not successful. + +```javascript + var str = ""; // Will store the contents of the file + ftp.get('remote/path/file.txt', function(err, socket) { + if (err) return; + + socket.on("data", function(d) { str += d.toString(); }) + socket.on("close", function(hadErr) { + if (hadErr) + console.error('There was an error retrieving the file.'); + }); + socket.resume(); + }); +``` + +#### Ftp.get(remotePath, localPath, callback) +Stores the remote file directly in the given local path. + +```javascript + ftp.get('remote/file.txt, 'local/file.txt, function(hadErr) { + if (hadErr) + console.error('There was an error retrieving the file.'); + else + console.log('File copied successfully!'); + }); +``` + +#### Ftp.put(source, remotePath, callback) +Uploads a file to `filePath`. It accepts a string with the local path for the +file or a `Buffer` as a `source` parameter. + +```javascript +ftp.put(buffer, 'path/to/remote/file.txt', function(hadError) { + if (!hadError) + console.log("File transferred successfully!"); +}); +``` + +#### Ftp.rename(from, to, callback) +Renames a file in the server. `from` and `to` are both filepaths. + +```javascript +ftp.rename(from, to, function(err, res) { + if (!err) + console.log("Renaming successful!"); +}); +``` + +#### Ftp.keepAlive() +Refreshes the interval thats keep the server connection active. + +You can find more usage examples in the [unit tests](https://github.com/sergi/jsftp/blob/master/test/jsftp_test.js). This documentation +will grow as jsftp evolves. + +Installation +------------ + + npm install jsftp + +Test coverage +------------- + +In order to run coverage reports: + + npm install --dev + make coverage + + Current overall coverage rate: + lines......: 92.1% (316 of 343 lines) + functions..: 91.0% (71 of 78 functions) + + +Tests +----- + +To run tests: + + npm install --dev + make test + +License +------- + +See LICENSE. diff --git a/node_modules/jsftp/index.js b/node_modules/jsftp/index.js new file mode 100644 index 0000000..f03f905 --- /dev/null +++ b/node_modules/jsftp/index.js @@ -0,0 +1,2 @@ +var libpath = process.env['VFS_FTP_COV'] ? './lib-cov' : './lib'; +module.exports = require(libpath + "/jsftp"); diff --git a/node_modules/jsftp/lib/jsftp.js b/node_modules/jsftp/lib/jsftp.js new file mode 100644 index 0000000..eee2505 --- /dev/null +++ b/node_modules/jsftp/lib/jsftp.js @@ -0,0 +1,650 @@ +/* vim:set ts=2 sw=2 sts=2 expandtab */ +/*global require: true module: true */ +/* + * @package jsftp + * @copyright Copyright(c) 2012 Ajax.org B.V. + * @author Sergi Mansilla + * @license https://github.com/sergi/jsFTP/blob/master/LICENSE MIT License + */ + +var Net = require("net"); +var EventEmitter = require("events").EventEmitter; +var es = require("event-stream"); +var responseHandler = require("./response"); +var Utils = require("./utils"); +var util = require("util"); +var fs = require("fs"); + +var FTP_PORT = 21; +var DEBUG_MODE = false; +var TIMEOUT = 10 * 60 * 1000; +var IDLE_TIME = 30000; +var COMMANDS = [ + // Commands without parameters + "abor", "pwd", "cdup", "feat", "noop", "quit", "pasv", "syst", + // Commands with one or more parameters + "cwd", "dele", "list", "mdtm", "mkd", "mode", "nlst", "pass", "retr", "rmd", + "rnfr", "rnto", "site", "stat", "stor", "type", "user", "pass", "xrmd", "opts", + // Extended features + "chmod", "size" +]; + +var Cmds = {}; +COMMANDS.forEach(function(cmd) { + cmd = cmd.toLowerCase(); + Cmds[cmd] = function() { + var callback = function() {}; + var completeCmd = cmd; + if (arguments.length) { + var args = Array.prototype.slice.call(arguments); + if (typeof args[args.length - 1] === "function") + callback = args.pop(); + + completeCmd += " " + args.join(" "); + } + this.execute(completeCmd.trim(), callback); + }; +}); + +function once(fn) { + var returnValue, called = false; + return function() { + if (!called) { + called = true; + returnValue = fn.apply(this, arguments); + } + return returnValue; + }; +} + +var Ftp = module.exports = function(cfg) { + "use strict"; + + Object.keys(cfg).forEach(function(opt) { + if (!this[opt]) this[opt] = cfg[opt]; + }, this); + + EventEmitter.call(this); + + // True if the server doesn't support the `stat` command. Since listing a + // directory or retrieving file properties is quite a common operation, it is + // more efficient to avoid the round-trip to the server. + this.useList = false; + this.port = this.port || FTP_PORT; + this.pending = []; // Pending requests + this.cmdBuffer_ = []; + this.responseHandler = responseHandler(); + + // Generate generic methods from parameter names. they can easily be + // overriden if we need special behavior. they accept any parameters given, + // it is the responsability of the user to validate the parameters. + var raw = this.raw = {}; + COMMANDS.forEach(function(cmd) { raw[cmd] = Cmds[cmd].bind(this); }, this); + + this.socket = this._createSocket(this.port, this.host); +}; + +util.inherits(Ftp, EventEmitter); + +Ftp.prototype.reemit = function(event) { + var self = this; + return function(data) { self.emit(event, data); } +}; + +Ftp.prototype._createSocket = function(port, host, firstAction) { + if (this.socket && this.socket.destroy) this.socket.destroy(); + + this.authenticated = false; + var socket = Net.createConnection(port, host); + socket.on("connect", this.reemit("connect")); + socket.on("timeout", this.reemit("timeout")); + + if (firstAction) + socket.once("connect", firstAction); + + this._createStreams(socket); + + return socket; +}; + +Ftp.prototype._createStreams = function(socket) { + this.pipeline = es.pipeline( + socket, + es.split(), + es.mapSync(this.responseHandler)); + + var self = this; + this.pipeline.on('data', function(data) { + self.emit('data', data); + self.parseResponse.call(self, data) + }); + this.pipeline.on("error", this.reemit("error")); +}; + +Ftp.prototype.parseResponse = function(data) { + if (!this.cmdBuffer_.length) + return; + + if ([220].indexOf(data.code) > -1) + return; + + var next = this.cmdBuffer_[0][1]; + if (Utils.isMark(data.code)) { + // If we receive a Mark and it is not expected, we ignore + // that command + if (!next.expectsMark || next.expectsMark.marks.indexOf(data.code) === -1) + return; + // We might have to ignore the command that comes after the + // mark. + if (next.expectsMark.ignore) + this.ignoreCmdCode = next.expectsMark.ignore; + } + + if (this.ignoreCmdCode && this.ignoreCmdCode === data.code) { + this.ignoreCmdCode = null; + return; + } + + this.parse(data, this.cmdBuffer_.shift()); +}; + +/** + * Writes a new command to the server. + * + * @param {String} command Command to write in the FTP socket + * @returns void + */ +Ftp.prototype.send = function(command) { + if (!command || typeof command !== "string") + return; + + this.emit("cmdSend", command); + this.pipeline.write(command + "\r\n"); +}; + +Ftp.prototype.nextCmd = function() { + if (!this.inProgress && this.cmdBuffer_[0]) { + this.send(this.cmdBuffer_[0][0]); + this.inProgress = true; + } +}; + +/** + * Check whether the ftp user is authenticated at the moment of the + * enqueing. ideally this should happen in the `push` method, just + * before writing to the socket, but that would be complicated, + * since we would have to 'unshift' the auth chain into the queue + * or play the raw auth commands (that is, without enqueuing in + * order to not mess up the queue order. ideally, that would be + * built into the queue object. all this explanation to justify a + * slight slopiness in the code flow. + * + * @param {string} action + * @param {function} callback + * @return void + */ +Ftp.prototype.execute = function(action, callback) { + if (!callback) callback = function() {}; + + if (this.socket && this.socket.writable) { + this._executeCommand(action, callback); + } else { + var self = this; + this.authenticated = false; + this.socket = this._createSocket(this.port, this.host, function() { + self._executeCommand(action, callback); + }); + } +}; + +Ftp.prototype._executeCommand = function(action, callback) { + var self = this; + + function executeCmd() { + self.cmdBuffer_.push([action, callback]); + self.nextCmd(); + } + + if (self.authenticated || /feat|syst|user|pass/.test(action)) { + executeCmd(); + } else { + this.getFeatures(function() { + self.auth(self.user, self.pass, executeCmd); + }); + } +}; + +/** + * Parse is called each time that a comand and a request are paired + * together. That is, each time that there is a round trip of actions + * between the client and the server. The `action` param contains an array + * with the response from the server as a first element (text) and an array + * with the command executed and the callback (if any) as the second + * element. + * + * @param action {Array} Contains server response and client command info. + */ +Ftp.prototype.parse = function(response, command) { + // In FTP every response code above 399 means error in some way. + // Since the RFC is not respected by many servers, we are going to + // overgeneralize and consider every value above 399 as an error. + var err = null; + if (response.code > 399) { + err = new Error(response.text || "Unknown FTP error."); + err.code = response.code; + } + + command[1](err, response); + this.inProgress = false; + this.nextCmd(); +}; + +/** + * Returns true if the current server has the requested feature. False otherwise. + * + * @param {String} feature Feature to look for + * @returns {Boolean} Whether the current server has the feature + */ +Ftp.prototype.hasFeat = function(feature) { + if (feature) + return this.features.indexOf(feature.toLowerCase()) > -1; +}; + +/** + * Returns an array of features supported by the current FTP server + * + * @param {String} features Server response for the 'FEAT' command + * @returns {String[]} Array of feature names + */ +Ftp.prototype._parseFeats = function(features) { + // Ignore header and footer + return features.split(/\r\n|\n/).slice(1, -1).map(function(feat) { + return (/^\s*(\w*)\s*/).exec(feat)[1].trim().toLowerCase(); + }); +}; + + +// Below this point all the methods are action helpers for FTP that compose +// several actions in one command + +Ftp.prototype.getFeatures = function(callback) { + var self = this; + if (!this.features) + this.raw.feat(function(err, response) { + self.features = err ? [] : self._parseFeats(response.text); + self.raw.syst(function(err, res) { + if (!err && res.code === 215) + self.system = res.text.toLowerCase(); + + callback(null, self.features); + }); + }); + else + callback(null, self.features); +}; + +/** + * Authenticates the user. + * + * @param user {String} Username + * @param pass {String} Password + * @param callback {Function} Follow-up function. + */ +Ftp.prototype.auth = function(user, pass, callback) { + this.pending.push(callback); + + var self = this; + + function notifyAll(err, res) { + var cb; + while (cb = self.pending.shift()) + cb(err, res); + } + + if (this.authenticating) return; + + if (!user) user = "anonymous"; + if (!pass) pass = "@anonymous"; + + this.authenticating = true; + self.raw.user(user, function(err, res) { + if (!err && [230, 331, 332].indexOf(res.code) > -1) { + self.raw.pass(pass, function(err, res) { + self.authenticating = false; + + if (err) + notifyAll(new Error("Login not accepted")); + + if ([230, 202].indexOf(res.code) > -1) { + self.authenticated = true; + self.user = user; + self.pass = pass; + self.raw.type("I", function() { + notifyAll(null, res); + }); + } else if (res.code === 332) { + self.raw.acct(""); // ACCT not really supported + } + }); + } else { + self.authenticating = false; + notifyAll(new Error("Login not accepted")); + } + }); +}; + +Ftp.prototype.setType = function(type, callback) { + if (this.type === type) + callback(null); + + var self = this; + this.raw.type(type, function(err, data) { + if (!err) self.type = type; + + callback(err, data); + }); +}; + +/** + * Lists a folder's contents using a passive connection. + * + * @param {String} [path] Remote path for the file/folder to retrieve + * @param {Function} callback Function to call with errors or results + */ +Ftp.prototype.list = function(path, callback) { + if (arguments.length === 1) { + callback = arguments[0]; + path = ""; + } + + var self = this; + var cb = function(err, listing) { + self.setType("I", once(function() { + callback(err, listing); + })); + }; + cb.expectsMark = { + marks: [125, 150], + ignore: 226 + }; + + var listing = ""; + this.setType("A", function() { + self.getPasvSocket(function(err, socket) { + socket.on("data", function(data) { + listing += data; + }); + socket.on("close", function(err) { + cb(err || null, listing); + }); + socket.on("error", cb); + + self.send("list " + (path || "")); + }); + }); +}; + +Ftp.prototype.emitProgress = function(data) { + this.emit('progress', { + filename: data.filename, + action: data.action, + total: data.totalSize || 0, + transferred: data.socket[ + data.action === 'get' ? 'bytesRead' : 'bytesWritten'] + }); +}; + +/** + * Depending on the number of parameters, returns the content of the specified + * file or directly saves a file into the specified destination. In the latter + * case, an optional callback can be provided, which will receive the error in + * case the operation was not successful. + * + * @param {String} remotePath File to be retrieved from the FTP server + * @param {String} localPath Local path where the new file will be created + * @param {Function} [callback] Gets called on either success or failure + */ +Ftp.prototype.get = function(remotePath, localPath, callback) { + var self = this; + if (arguments.length === 2) { + callback = once(localPath || function() {}); + this.getGetSocket(remotePath, callback); + } else { + callback = once(callback || function() {}); + this.getGetSocket(remotePath, function(err, socket) { + if (err) { + callback(err); + } + + var writeStream = fs.createWriteStream(localPath); + writeStream.on('error', callback); + + socket.on('readable', function() { + self.emitProgress({ + filename: remotePath, + action: 'get', + socket: this + }); + }); + socket.on('end', callback); + socket.pipe(writeStream); + socket.resume(); + }) + } +}; + +/** + * Returns a socket for a get (RETR) on a path. The socket is ready to be + * streamed, but it is returned in a paused state. It is left to the user to + * resume it. + * + * @param path {String} Path to the file to be retrieved + * @param callback {Function} Function to call when finalized, with the socket as a parameter + */ +Ftp.prototype.getGetSocket = function(path, callback) { + var self = this; + callback = once(callback); + this.getPasvSocket(function(err, socket) { + if (err) return cmdCallback(err); + + socket.pause(); + + function cmdCallback(err, res) { + if (err) return callback(err); + + if (res.code === 150) + callback(null, socket); + else + callback(new Error("Unexpected command " + res.text)); + } + + cmdCallback.expectsMark = { + marks: [125, 150], + ignore: 226 + }; + self.execute("retr " + path, cmdCallback); + }); +}; + +/** + * Uploads contents on a FTP server. The `from` parameter can be a Buffer or the + * path for a local file to be uploaded. + * + * @param {String|Buffer} from Contents to be uploaded. + * @param {String} to path for the remote destination. + * @param {Function} callback Function to execute on error or success. + */ +Ftp.prototype.put = function(from, to, callback) { + if (from instanceof Buffer) { + this.getPutSocket(to, function(err, socket) { + if (!err) socket.end(from); + }, callback); + } else { + var self = this; + fs.exists(from, function(exists) { + if (!exists) + return callback(new Error("Local file doesn't exist.")); + + self.getPutSocket(to, function(err, socket) { + if (err) return; + + fs.stat(from, function(err, stats) { + var totalSize = err ? 0 : stats.size; + var read = fs.createReadStream(from, { + bufferSize: 4 * 1024 + }); + read.pipe(socket); + read.on('readable', function() { + self.emitProgress({ + filename: to, + action: 'put', + socket: read, + totalSize: totalSize + }); + }); + }); + }, callback); + }); + } +}; + +Ftp.prototype.getPutSocket = function(path, callback, doneCallback) { + if (!callback) throw new Error("A callback argument is required."); + + doneCallback = once(doneCallback || function() {}); + var _callback = once(function(err, _socket) { + if (err) { + callback(err); + return doneCallback(err); + } + return callback(err, _socket); + }); + + var self = this; + this.getPasvSocket(function(err, socket) { + if (err) return _callback(err); + + var putCallback = once(function putCallback(err, res) { + if (err) return _callback(err); + + // Mark 150 indicates that the 'STOR' socket is ready to receive data. + // Anything else is not relevant. + if (res.code === 150) { + socket.on('close', doneCallback); + socket.on('error', doneCallback); + _callback(null, socket); + } else { + return _callback(new Error("Unexpected command " + res.text)); + } + }); + putCallback.expectsMark = { + marks: [125, 150], + ignore: 226 + }; + self.execute("stor " + path, putCallback); + }); +}; + +Ftp.prototype.getPasvSocket = function(callback) { + var timeout = this.timeout; + callback = once(callback || function() {}); + this.execute("pasv", function(err, res) { + if (err) return callback(err); + + var pasvRes = Utils.getPasvPort(res.text); + if (pasvRes === false) + return callback(new Error("PASV: Bad host/port combination")); + + var host = pasvRes[0]; + var port = pasvRes[1]; + var socket = Net.createConnection(port, host); + socket.setTimeout(timeout || TIMEOUT); + callback(null, socket); + }); +}; + +/** + * Provides information about files. It lists a directory contents or + * a single file and yields an array of file objects. The file objects + * contain several properties. The main difference between this method and + * 'list' or 'stat' is that it returns objects with the file properties + * already parsed. + * + * Example of file object: + * + * { + * name: 'README.txt', + * type: 0, + * time: 996052680000, + * size: '2582', + * owner: 'sergi', + * group: 'staff', + * userPermissions: { read: true, write: true, exec: false }, + * groupPermissions: { read: true, write: false, exec: false }, + * otherPermissions: { read: true, write: false, exec: false } + * } + * + * The constants used in the object are defined in ftpParser.js + * + * @param filePath {String} Path to the file or directory to list + * @param callback {Function} Function to call with the proper data when + * the listing is finished. + */ +Ftp.prototype.ls = function(filePath, callback) { + function entriesToList(err, entries) { + if (err) { + return callback(err); + } + callback(null, Utils.parseEntry(entries.text || entries)); + } + + if (this.useList) { + this.list(filePath, entriesToList); + } else { + var self = this; + this.raw.stat(filePath, function(err, data) { + // We might be connected to a server that doesn't support the + // 'STAT' command, which is set as default. We use 'LIST' instead, + // and we set the variable `useList` to true, to avoid extra round + // trips to the server to check. + if ((err && (err.code === 502 || err.code === 500)) || + (self.system && self.system.indexOf("hummingbird") > -1)) + // Not sure if the "hummingbird" system check ^^^ is still + // necessary. If they support any standards, the 500 error + // should have us covered. Let's leave it for now. + { + self.useList = true; + self.list(filePath, entriesToList); + } else { + entriesToList(err, data); + } + }); + } +}; + +Ftp.prototype.rename = function(from, to, callback) { + var self = this; + this.raw.rnfr(from, function(err, res) { + if (err) return callback(err); + self.raw.rnto(to, function(err, res) { + callback(err, res); + }); + }); +}; + +Ftp.prototype.keepAlive = function() { + var self = this; + if (this._keepAliveInterval) + clearInterval(this._keepAliveInterval); + + this._keepAliveInterval = setInterval(self.raw.noop, IDLE_TIME); +}; + +Ftp.prototype.destroy = function() { + if (this._keepAliveInterval) + clearInterval(this._keepAliveInterval); + + this.socket.destroy(); + this.features = null; + this.authenticated = false; +}; diff --git a/node_modules/jsftp/lib/lib/jsftp.js b/node_modules/jsftp/lib/lib/jsftp.js new file mode 100644 index 0000000..6901348 --- /dev/null +++ b/node_modules/jsftp/lib/lib/jsftp.js @@ -0,0 +1,6 @@ +if (typeof __coverage__ === 'undefined') { __coverage__ = {}; } +if (!__coverage__['/Users/sergi/programming/jsftp/lib/jsftp.js']) { + __coverage__['/Users/sergi/programming/jsftp/lib/jsftp.js'] = {"path":"/Users/sergi/programming/jsftp/lib/jsftp.js","s":{"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"83":0,"84":0,"85":0,"86":0,"87":0,"88":0,"89":0,"90":0,"91":0,"92":0,"93":0,"94":0,"95":0,"96":0,"97":0,"98":0,"99":0,"100":0,"101":0,"102":0,"103":0,"104":0,"105":0,"106":0,"107":0,"108":0,"109":0,"110":0,"111":0,"112":0,"113":0,"114":0,"115":0,"116":0,"117":0,"118":0,"119":0,"120":0,"121":0,"122":0,"123":0,"124":0,"125":0,"126":0,"127":0,"128":0,"129":0,"130":0,"131":0,"132":0,"133":0,"134":0,"135":0,"136":0,"137":0,"138":0,"139":0,"140":0,"141":0,"142":0,"143":0,"144":0,"145":0,"146":0,"147":0,"148":0,"149":0,"150":0,"151":0,"152":0,"153":0,"154":0,"155":0,"156":0,"157":0,"158":0,"159":0,"160":0,"161":0,"162":0,"163":0,"164":0,"165":0,"166":0,"167":0,"168":0,"169":0,"170":0,"171":0,"172":0,"173":0,"174":0,"175":0,"176":0,"177":0,"178":0,"179":0,"180":0,"181":0,"182":0,"183":0,"184":0,"185":0,"186":0,"187":0,"188":0,"189":0,"190":0,"191":0,"192":0,"193":0,"194":0,"195":0,"196":0,"197":0,"198":0,"199":0,"200":0,"201":0,"202":0,"203":0,"204":0,"205":0,"206":0,"207":0,"208":0,"209":0,"210":0,"211":0,"212":0,"213":0,"214":0,"215":0,"216":0,"217":0,"218":0,"219":0,"220":0,"221":0,"222":0,"223":0,"224":0,"225":0,"226":0,"227":0,"228":0,"229":0,"230":0,"231":0,"232":0,"233":0,"234":0,"235":0,"236":0,"237":0,"238":0,"239":0,"240":0,"241":0,"242":0,"243":0,"244":0,"245":0,"246":0,"247":0,"248":0,"249":0,"250":0,"251":0,"252":0,"253":0,"254":0,"255":0,"256":0,"257":0,"258":0,"259":0,"260":0,"261":0,"262":0,"263":0,"264":0,"265":0,"266":0,"267":0,"268":0,"269":0,"270":0,"271":0,"272":0,"273":0,"274":0,"275":0,"276":0,"277":0,"278":0,"279":0,"280":0,"281":0,"282":0,"283":0,"284":0,"285":0,"286":0,"287":0,"288":0,"289":0,"290":0,"291":0,"292":0,"293":0,"294":0,"295":0,"296":0,"297":0,"298":0,"299":0,"300":0,"301":0,"302":0,"303":0,"304":0,"305":0,"306":0,"307":0,"308":0,"309":0,"310":0,"311":0,"312":0,"313":0,"314":0,"315":0,"316":0,"317":0,"318":0,"319":0,"320":0,"321":0,"322":0},"b":{"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0,0],"12":[0,0],"13":[0,0],"14":[0,0],"15":[0,0],"16":[0,0],"17":[0,0],"18":[0,0],"19":[0,0],"20":[0,0],"21":[0,0],"22":[0,0],"23":[0,0],"24":[0,0],"25":[0,0],"26":[0,0],"27":[0,0],"28":[0,0],"29":[0,0],"30":[0,0],"31":[0,0],"32":[0,0],"33":[0,0],"34":[0,0],"35":[0,0],"36":[0,0],"37":[0,0],"38":[0,0],"39":[0,0],"40":[0,0],"41":[0,0],"42":[0,0],"43":[0,0],"44":[0,0],"45":[0,0],"46":[0,0],"47":[0,0],"48":[0,0],"49":[0,0],"50":[0,0],"51":[0,0],"52":[0,0],"53":[0,0],"54":[0,0],"55":[0,0],"56":[0,0],"57":[0,0],"58":[0,0],"59":[0,0],"60":[0,0],"61":[0,0],"62":[0,0],"63":[0,0],"64":[0,0],"65":[0,0],"66":[0,0],"67":[0,0],"68":[0,0],"69":[0,0],"70":[0,0],"71":[0,0],"72":[0,0],"73":[0,0],"74":[0,0],"75":[0,0,0,0,0],"76":[0,0],"77":[0,0],"78":[0,0]},"f":{"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0},"fnMap":{"1":{"name":"(anonymous_1)","line":34,"loc":{"start":{"line":34,"column":17},"end":{"line":34,"column":31}}},"2":{"name":"(anonymous_2)","line":36,"loc":{"start":{"line":36,"column":14},"end":{"line":36,"column":25}}},"3":{"name":"(anonymous_3)","line":37,"loc":{"start":{"line":37,"column":19},"end":{"line":37,"column":30}}},"4":{"name":"once","line":50,"loc":{"start":{"line":50,"column":0},"end":{"line":50,"column":18}}},"5":{"name":"(anonymous_5)","line":52,"loc":{"start":{"line":52,"column":9},"end":{"line":52,"column":20}}},"6":{"name":"(anonymous_6)","line":61,"loc":{"start":{"line":61,"column":27},"end":{"line":61,"column":41}}},"7":{"name":"(anonymous_7)","line":64,"loc":{"start":{"line":64,"column":27},"end":{"line":64,"column":41}}},"8":{"name":"(anonymous_8)","line":83,"loc":{"start":{"line":83,"column":19},"end":{"line":83,"column":33}}},"9":{"name":"(anonymous_9)","line":85,"loc":{"start":{"line":85,"column":19},"end":{"line":85,"column":33}}},"10":{"name":"(anonymous_10)","line":95,"loc":{"start":{"line":95,"column":33},"end":{"line":95,"column":44}}},"11":{"name":"(anonymous_11)","line":101,"loc":{"start":{"line":101,"column":31},"end":{"line":101,"column":45}}},"12":{"name":"(anonymous_12)","line":107,"loc":{"start":{"line":107,"column":33},"end":{"line":107,"column":44}}},"13":{"name":"(anonymous_13)","line":113,"loc":{"start":{"line":113,"column":30},"end":{"line":113,"column":51}}},"14":{"name":"(anonymous_14)","line":129,"loc":{"start":{"line":129,"column":31},"end":{"line":129,"column":48}}},"15":{"name":"(anonymous_15)","line":136,"loc":{"start":{"line":136,"column":27},"end":{"line":136,"column":42}}},"16":{"name":"(anonymous_16)","line":144,"loc":{"start":{"line":144,"column":30},"end":{"line":144,"column":45}}},"17":{"name":"(anonymous_17)","line":178,"loc":{"start":{"line":178,"column":21},"end":{"line":178,"column":39}}},"18":{"name":"(anonymous_18)","line":188,"loc":{"start":{"line":188,"column":24},"end":{"line":188,"column":35}}},"19":{"name":"(anonymous_19)","line":207,"loc":{"start":{"line":207,"column":25},"end":{"line":207,"column":52}}},"20":{"name":"(anonymous_20)","line":208,"loc":{"start":{"line":208,"column":28},"end":{"line":208,"column":39}}},"21":{"name":"execute","line":220,"loc":{"start":{"line":220,"column":2},"end":{"line":220,"column":21}}},"22":{"name":"executeCmd","line":221,"loc":{"start":{"line":221,"column":4},"end":{"line":221,"column":26}}},"23":{"name":"(anonymous_23)","line":230,"loc":{"start":{"line":230,"column":23},"end":{"line":230,"column":34}}},"24":{"name":"(anonymous_24)","line":247,"loc":{"start":{"line":247,"column":22},"end":{"line":247,"column":39}}},"25":{"name":"(anonymous_25)","line":273,"loc":{"start":{"line":273,"column":24},"end":{"line":273,"column":42}}},"26":{"name":"(anonymous_26)","line":286,"loc":{"start":{"line":286,"column":28},"end":{"line":286,"column":47}}},"27":{"name":"(anonymous_27)","line":288,"loc":{"start":{"line":288,"column":52},"end":{"line":288,"column":67}}},"28":{"name":"(anonymous_28)","line":297,"loc":{"start":{"line":297,"column":28},"end":{"line":297,"column":47}}},"29":{"name":"(anonymous_29)","line":300,"loc":{"start":{"line":300,"column":18},"end":{"line":300,"column":42}}},"30":{"name":"(anonymous_30)","line":302,"loc":{"start":{"line":302,"column":20},"end":{"line":302,"column":39}}},"31":{"name":"(anonymous_31)","line":320,"loc":{"start":{"line":320,"column":21},"end":{"line":320,"column":52}}},"32":{"name":"notifyAll","line":325,"loc":{"start":{"line":325,"column":2},"end":{"line":325,"column":31}}},"33":{"name":"(anonymous_33)","line":337,"loc":{"start":{"line":337,"column":22},"end":{"line":337,"column":41}}},"34":{"name":"(anonymous_34)","line":339,"loc":{"start":{"line":339,"column":26},"end":{"line":339,"column":45}}},"35":{"name":"(anonymous_35)","line":349,"loc":{"start":{"line":349,"column":29},"end":{"line":349,"column":40}}},"36":{"name":"(anonymous_36)","line":363,"loc":{"start":{"line":363,"column":24},"end":{"line":363,"column":49}}},"37":{"name":"(anonymous_37)","line":368,"loc":{"start":{"line":368,"column":22},"end":{"line":368,"column":42}}},"38":{"name":"(anonymous_38)","line":381,"loc":{"start":{"line":381,"column":21},"end":{"line":381,"column":46}}},"39":{"name":"(anonymous_39)","line":388,"loc":{"start":{"line":388,"column":11},"end":{"line":388,"column":34}}},"40":{"name":"(anonymous_40)","line":389,"loc":{"start":{"line":389,"column":27},"end":{"line":389,"column":38}}},"41":{"name":"(anonymous_41)","line":396,"loc":{"start":{"line":396,"column":20},"end":{"line":396,"column":31}}},"42":{"name":"(anonymous_42)","line":397,"loc":{"start":{"line":397,"column":23},"end":{"line":397,"column":45}}},"43":{"name":"(anonymous_43)","line":398,"loc":{"start":{"line":398,"column":24},"end":{"line":398,"column":39}}},"44":{"name":"(anonymous_44)","line":399,"loc":{"start":{"line":399,"column":25},"end":{"line":399,"column":39}}},"45":{"name":"(anonymous_45)","line":418,"loc":{"start":{"line":418,"column":20},"end":{"line":418,"column":62}}},"46":{"name":"(anonymous_46)","line":420,"loc":{"start":{"line":420,"column":33},"end":{"line":420,"column":44}}},"47":{"name":"(anonymous_47)","line":422,"loc":{"start":{"line":422,"column":34},"end":{"line":422,"column":56}}},"48":{"name":"(anonymous_48)","line":428,"loc":{"start":{"line":428,"column":32},"end":{"line":428,"column":43}}},"49":{"name":"(anonymous_49)","line":429,"loc":{"start":{"line":429,"column":34},"end":{"line":429,"column":56}}},"50":{"name":"onError","line":431,"loc":{"start":{"line":431,"column":6},"end":{"line":431,"column":28}}},"51":{"name":"(anonymous_51)","line":439,"loc":{"start":{"line":439,"column":23},"end":{"line":439,"column":34}}},"52":{"name":"(anonymous_52)","line":454,"loc":{"start":{"line":454,"column":29},"end":{"line":454,"column":54}}},"53":{"name":"(anonymous_53)","line":457,"loc":{"start":{"line":457,"column":21},"end":{"line":457,"column":43}}},"54":{"name":"cmdCallback","line":462,"loc":{"start":{"line":462,"column":4},"end":{"line":462,"column":35}}},"55":{"name":"(anonymous_55)","line":484,"loc":{"start":{"line":484,"column":20},"end":{"line":484,"column":49}}},"56":{"name":"(anonymous_56)","line":486,"loc":{"start":{"line":486,"column":26},"end":{"line":486,"column":48}}},"57":{"name":"(anonymous_57)","line":492,"loc":{"start":{"line":492,"column":20},"end":{"line":492,"column":37}}},"58":{"name":"(anonymous_58)","line":496,"loc":{"start":{"line":496,"column":28},"end":{"line":496,"column":50}}},"59":{"name":"(anonymous_59)","line":506,"loc":{"start":{"line":506,"column":29},"end":{"line":506,"column":68}}},"60":{"name":"(anonymous_60)","line":509,"loc":{"start":{"line":509,"column":38},"end":{"line":509,"column":49}}},"61":{"name":"(anonymous_61)","line":510,"loc":{"start":{"line":510,"column":23},"end":{"line":510,"column":46}}},"62":{"name":"(anonymous_62)","line":519,"loc":{"start":{"line":519,"column":21},"end":{"line":519,"column":43}}},"63":{"name":"putCallback","line":522,"loc":{"start":{"line":522,"column":27},"end":{"line":522,"column":58}}},"64":{"name":"(anonymous_64)","line":541,"loc":{"start":{"line":541,"column":30},"end":{"line":541,"column":49}}},"65":{"name":"(anonymous_65)","line":543,"loc":{"start":{"line":543,"column":30},"end":{"line":543,"column":41}}},"66":{"name":"(anonymous_66)","line":544,"loc":{"start":{"line":544,"column":24},"end":{"line":544,"column":43}}},"67":{"name":"(anonymous_67)","line":586,"loc":{"start":{"line":586,"column":19},"end":{"line":586,"column":48}}},"68":{"name":"entriesToList","line":587,"loc":{"start":{"line":587,"column":2},"end":{"line":587,"column":39}}},"69":{"name":"(anonymous_69)","line":597,"loc":{"start":{"line":597,"column":28},"end":{"line":597,"column":48}}},"70":{"name":"(anonymous_70)","line":618,"loc":{"start":{"line":618,"column":23},"end":{"line":618,"column":52}}},"71":{"name":"(anonymous_71)","line":620,"loc":{"start":{"line":620,"column":22},"end":{"line":620,"column":41}}},"72":{"name":"(anonymous_72)","line":622,"loc":{"start":{"line":622,"column":22},"end":{"line":622,"column":41}}},"73":{"name":"(anonymous_73)","line":626,"loc":{"start":{"line":626,"column":26},"end":{"line":626,"column":37}}},"74":{"name":"(anonymous_74)","line":634,"loc":{"start":{"line":634,"column":24},"end":{"line":634,"column":35}}}},"statementMap":{"1":{"start":{"line":10,"column":0},"end":{"line":10,"column":25}},"2":{"start":{"line":11,"column":0},"end":{"line":11,"column":50}},"3":{"start":{"line":12,"column":0},"end":{"line":12,"column":31}},"4":{"start":{"line":13,"column":0},"end":{"line":13,"column":33}},"5":{"start":{"line":14,"column":0},"end":{"line":14,"column":44}},"6":{"start":{"line":15,"column":0},"end":{"line":15,"column":31}},"7":{"start":{"line":16,"column":0},"end":{"line":16,"column":27}},"8":{"start":{"line":17,"column":0},"end":{"line":17,"column":23}},"9":{"start":{"line":19,"column":0},"end":{"line":19,"column":18}},"10":{"start":{"line":20,"column":0},"end":{"line":20,"column":23}},"11":{"start":{"line":21,"column":0},"end":{"line":21,"column":29}},"12":{"start":{"line":22,"column":0},"end":{"line":22,"column":22}},"13":{"start":{"line":23,"column":0},"end":{"line":31,"column":2}},"14":{"start":{"line":33,"column":0},"end":{"line":33,"column":14}},"15":{"start":{"line":34,"column":0},"end":{"line":48,"column":3}},"16":{"start":{"line":35,"column":2},"end":{"line":35,"column":26}},"17":{"start":{"line":36,"column":2},"end":{"line":47,"column":4}},"18":{"start":{"line":37,"column":4},"end":{"line":37,"column":33}},"19":{"start":{"line":38,"column":4},"end":{"line":38,"column":26}},"20":{"start":{"line":39,"column":4},"end":{"line":45,"column":5}},"21":{"start":{"line":40,"column":6},"end":{"line":40,"column":55}},"22":{"start":{"line":41,"column":6},"end":{"line":42,"column":30}},"23":{"start":{"line":42,"column":8},"end":{"line":42,"column":30}},"24":{"start":{"line":44,"column":6},"end":{"line":44,"column":42}},"25":{"start":{"line":46,"column":4},"end":{"line":46,"column":48}},"26":{"start":{"line":50,"column":0},"end":{"line":59,"column":1}},"27":{"start":{"line":51,"column":2},"end":{"line":51,"column":34}},"28":{"start":{"line":52,"column":2},"end":{"line":58,"column":4}},"29":{"start":{"line":53,"column":4},"end":{"line":56,"column":5}},"30":{"start":{"line":54,"column":6},"end":{"line":54,"column":20}},"31":{"start":{"line":55,"column":6},"end":{"line":55,"column":46}},"32":{"start":{"line":57,"column":4},"end":{"line":57,"column":23}},"33":{"start":{"line":61,"column":0},"end":{"line":91,"column":2}},"34":{"start":{"line":64,"column":2},"end":{"line":66,"column":11}},"35":{"start":{"line":65,"column":4},"end":{"line":65,"column":41}},"36":{"start":{"line":65,"column":20},"end":{"line":65,"column":41}},"37":{"start":{"line":68,"column":2},"end":{"line":68,"column":26}},"38":{"start":{"line":73,"column":2},"end":{"line":73,"column":23}},"39":{"start":{"line":74,"column":2},"end":{"line":74,"column":36}},"40":{"start":{"line":75,"column":2},"end":{"line":75,"column":20}},"41":{"start":{"line":76,"column":2},"end":{"line":76,"column":23}},"42":{"start":{"line":77,"column":2},"end":{"line":77,"column":43}},"43":{"start":{"line":82,"column":2},"end":{"line":82,"column":26}},"44":{"start":{"line":83,"column":2},"end":{"line":83,"column":77}},"45":{"start":{"line":83,"column":35},"end":{"line":83,"column":67}},"46":{"start":{"line":85,"column":2},"end":{"line":88,"column":5}},"47":{"start":{"line":86,"column":4},"end":{"line":87,"column":23}},"48":{"start":{"line":87,"column":6},"end":{"line":87,"column":23}},"49":{"start":{"line":90,"column":2},"end":{"line":90,"column":43}},"50":{"start":{"line":93,"column":0},"end":{"line":93,"column":33}},"51":{"start":{"line":95,"column":0},"end":{"line":99,"column":2}},"52":{"start":{"line":96,"column":2},"end":{"line":96,"column":34}},"53":{"start":{"line":97,"column":2},"end":{"line":97,"column":24}},"54":{"start":{"line":98,"column":2},"end":{"line":98,"column":17}},"55":{"start":{"line":101,"column":0},"end":{"line":105,"column":2}},"56":{"start":{"line":102,"column":2},"end":{"line":102,"column":48}},"57":{"start":{"line":103,"column":2},"end":{"line":103,"column":32}},"58":{"start":{"line":104,"column":2},"end":{"line":104,"column":24}},"59":{"start":{"line":107,"column":0},"end":{"line":111,"column":2}},"60":{"start":{"line":108,"column":2},"end":{"line":108,"column":36}},"61":{"start":{"line":109,"column":2},"end":{"line":109,"column":26}},"62":{"start":{"line":110,"column":2},"end":{"line":110,"column":25}},"63":{"start":{"line":113,"column":0},"end":{"line":127,"column":2}},"64":{"start":{"line":114,"column":2},"end":{"line":114,"column":41}},"65":{"start":{"line":114,"column":19},"end":{"line":114,"column":41}},"66":{"start":{"line":116,"column":2},"end":{"line":116,"column":25}},"67":{"start":{"line":117,"column":2},"end":{"line":117,"column":29}},"68":{"start":{"line":119,"column":2},"end":{"line":119,"column":62}},"69":{"start":{"line":120,"column":2},"end":{"line":121,"column":38}},"70":{"start":{"line":122,"column":2},"end":{"line":122,"column":53}},"71":{"start":{"line":123,"column":2},"end":{"line":123,"column":57}},"72":{"start":{"line":125,"column":2},"end":{"line":125,"column":35}},"73":{"start":{"line":126,"column":2},"end":{"line":126,"column":24}},"74":{"start":{"line":129,"column":0},"end":{"line":142,"column":2}},"75":{"start":{"line":130,"column":2},"end":{"line":133,"column":38}},"76":{"start":{"line":135,"column":2},"end":{"line":135,"column":18}},"77":{"start":{"line":136,"column":2},"end":{"line":141,"column":5}},"78":{"start":{"line":137,"column":4},"end":{"line":138,"column":46}},"79":{"start":{"line":138,"column":6},"end":{"line":138,"column":46}},"80":{"start":{"line":140,"column":4},"end":{"line":141,"column":2}},"81":{"start":{"line":144,"column":0},"end":{"line":170,"column":2}},"82":{"start":{"line":145,"column":2},"end":{"line":146,"column":11}},"83":{"start":{"line":146,"column":4},"end":{"line":146,"column":11}},"84":{"start":{"line":148,"column":2},"end":{"line":149,"column":11}},"85":{"start":{"line":149,"column":4},"end":{"line":149,"column":11}},"86":{"start":{"line":151,"column":2},"end":{"line":151,"column":38}},"87":{"start":{"line":152,"column":2},"end":{"line":162,"column":3}},"88":{"start":{"line":155,"column":4},"end":{"line":156,"column":13}},"89":{"start":{"line":156,"column":6},"end":{"line":156,"column":13}},"90":{"start":{"line":160,"column":4},"end":{"line":161,"column":54}},"91":{"start":{"line":161,"column":6},"end":{"line":161,"column":54}},"92":{"start":{"line":164,"column":2},"end":{"line":167,"column":3}},"93":{"start":{"line":165,"column":4},"end":{"line":165,"column":30}},"94":{"start":{"line":166,"column":4},"end":{"line":166,"column":11}},"95":{"start":{"line":169,"column":2},"end":{"line":169,"column":46}},"96":{"start":{"line":178,"column":0},"end":{"line":186,"column":2}},"97":{"start":{"line":179,"column":2},"end":{"line":180,"column":11}},"98":{"start":{"line":180,"column":4},"end":{"line":180,"column":11}},"99":{"start":{"line":182,"column":2},"end":{"line":183,"column":58}},"100":{"start":{"line":183,"column":4},"end":{"line":183,"column":58}},"101":{"start":{"line":185,"column":2},"end":{"line":185,"column":40}},"102":{"start":{"line":188,"column":0},"end":{"line":191,"column":2}},"103":{"start":{"line":189,"column":2},"end":{"line":190,"column":37}},"104":{"start":{"line":190,"column":4},"end":{"line":190,"column":37}},"105":{"start":{"line":207,"column":0},"end":{"line":235,"column":2}},"106":{"start":{"line":208,"column":2},"end":{"line":208,"column":42}},"107":{"start":{"line":208,"column":17},"end":{"line":208,"column":42}},"108":{"start":{"line":210,"column":2},"end":{"line":210,"column":18}},"109":{"start":{"line":211,"column":2},"end":{"line":218,"column":3}},"110":{"start":{"line":212,"column":4},"end":{"line":212,"column":14}},"111":{"start":{"line":214,"column":7},"end":{"line":218,"column":3}},"112":{"start":{"line":215,"column":4},"end":{"line":215,"column":31}},"113":{"start":{"line":216,"column":4},"end":{"line":216,"column":34}},"114":{"start":{"line":217,"column":4},"end":{"line":217,"column":45}},"115":{"start":{"line":220,"column":2},"end":{"line":234,"column":3}},"116":{"start":{"line":221,"column":4},"end":{"line":224,"column":5}},"117":{"start":{"line":222,"column":6},"end":{"line":222,"column":47}},"118":{"start":{"line":223,"column":6},"end":{"line":223,"column":21}},"119":{"start":{"line":226,"column":4},"end":{"line":233,"column":5}},"120":{"start":{"line":227,"column":6},"end":{"line":227,"column":19}},"121":{"start":{"line":230,"column":6},"end":{"line":232,"column":9}},"122":{"start":{"line":231,"column":8},"end":{"line":231,"column":52}},"123":{"start":{"line":247,"column":0},"end":{"line":265,"column":2}},"124":{"start":{"line":248,"column":2},"end":{"line":248,"column":27}},"125":{"start":{"line":249,"column":2},"end":{"line":249,"column":26}},"126":{"start":{"line":250,"column":2},"end":{"line":250,"column":22}},"127":{"start":{"line":252,"column":2},"end":{"line":253,"column":46}},"128":{"start":{"line":253,"column":4},"end":{"line":253,"column":46}},"129":{"start":{"line":258,"column":2},"end":{"line":258,"column":17}},"130":{"start":{"line":259,"column":2},"end":{"line":262,"column":3}},"131":{"start":{"line":260,"column":4},"end":{"line":260,"column":59}},"132":{"start":{"line":261,"column":4},"end":{"line":261,"column":29}},"133":{"start":{"line":264,"column":2},"end":{"line":264,"column":20}},"134":{"start":{"line":273,"column":0},"end":{"line":278,"column":2}},"135":{"start":{"line":274,"column":2},"end":{"line":275,"column":61}},"136":{"start":{"line":275,"column":4},"end":{"line":275,"column":61}},"137":{"start":{"line":277,"column":2},"end":{"line":278,"column":0}},"138":{"start":{"line":286,"column":0},"end":{"line":291,"column":2}},"139":{"start":{"line":288,"column":2},"end":{"line":290,"column":5}},"140":{"start":{"line":289,"column":4},"end":{"line":289,"column":63}},"141":{"start":{"line":297,"column":0},"end":{"line":311,"column":2}},"142":{"start":{"line":298,"column":2},"end":{"line":298,"column":18}},"143":{"start":{"line":299,"column":2},"end":{"line":310,"column":34}},"144":{"start":{"line":300,"column":4},"end":{"line":308,"column":7}},"145":{"start":{"line":301,"column":6},"end":{"line":301,"column":65}},"146":{"start":{"line":302,"column":6},"end":{"line":307,"column":9}},"147":{"start":{"line":303,"column":8},"end":{"line":304,"column":47}},"148":{"start":{"line":304,"column":10},"end":{"line":304,"column":47}},"149":{"start":{"line":306,"column":8},"end":{"line":306,"column":38}},"150":{"start":{"line":310,"column":4},"end":{"line":310,"column":34}},"151":{"start":{"line":320,"column":0},"end":{"line":361,"column":2}},"152":{"start":{"line":321,"column":2},"end":{"line":321,"column":30}},"153":{"start":{"line":323,"column":2},"end":{"line":323,"column":18}},"154":{"start":{"line":325,"column":2},"end":{"line":329,"column":3}},"155":{"start":{"line":326,"column":4},"end":{"line":326,"column":11}},"156":{"start":{"line":327,"column":4},"end":{"line":328,"column":19}},"157":{"start":{"line":328,"column":6},"end":{"line":328,"column":19}},"158":{"start":{"line":331,"column":2},"end":{"line":331,"column":34}},"159":{"start":{"line":331,"column":27},"end":{"line":331,"column":34}},"160":{"start":{"line":333,"column":2},"end":{"line":333,"column":32}},"161":{"start":{"line":333,"column":13},"end":{"line":333,"column":32}},"162":{"start":{"line":334,"column":2},"end":{"line":334,"column":33}},"163":{"start":{"line":334,"column":13},"end":{"line":334,"column":33}},"164":{"start":{"line":336,"column":2},"end":{"line":336,"column":29}},"165":{"start":{"line":337,"column":2},"end":{"line":360,"column":5}},"166":{"start":{"line":338,"column":4},"end":{"line":359,"column":5}},"167":{"start":{"line":339,"column":6},"end":{"line":354,"column":9}},"168":{"start":{"line":340,"column":8},"end":{"line":340,"column":36}},"169":{"start":{"line":342,"column":8},"end":{"line":343,"column":53}},"170":{"start":{"line":343,"column":10},"end":{"line":343,"column":53}},"171":{"start":{"line":345,"column":8},"end":{"line":353,"column":9}},"172":{"start":{"line":346,"column":10},"end":{"line":346,"column":36}},"173":{"start":{"line":347,"column":10},"end":{"line":347,"column":27}},"174":{"start":{"line":348,"column":10},"end":{"line":348,"column":27}},"175":{"start":{"line":349,"column":10},"end":{"line":349,"column":67}},"176":{"start":{"line":349,"column":42},"end":{"line":349,"column":63}},"177":{"start":{"line":351,"column":13},"end":{"line":353,"column":9}},"178":{"start":{"line":352,"column":10},"end":{"line":352,"column":28}},"179":{"start":{"line":357,"column":6},"end":{"line":357,"column":34}},"180":{"start":{"line":358,"column":6},"end":{"line":358,"column":49}},"181":{"start":{"line":363,"column":0},"end":{"line":373,"column":2}},"182":{"start":{"line":364,"column":2},"end":{"line":365,"column":19}},"183":{"start":{"line":365,"column":4},"end":{"line":365,"column":19}},"184":{"start":{"line":367,"column":2},"end":{"line":367,"column":18}},"185":{"start":{"line":368,"column":2},"end":{"line":372,"column":5}},"186":{"start":{"line":369,"column":4},"end":{"line":369,"column":31}},"187":{"start":{"line":369,"column":14},"end":{"line":369,"column":31}},"188":{"start":{"line":371,"column":4},"end":{"line":371,"column":24}},"189":{"start":{"line":381,"column":0},"end":{"line":405,"column":2}},"190":{"start":{"line":382,"column":2},"end":{"line":385,"column":3}},"191":{"start":{"line":383,"column":4},"end":{"line":383,"column":28}},"192":{"start":{"line":384,"column":4},"end":{"line":384,"column":14}},"193":{"start":{"line":387,"column":2},"end":{"line":387,"column":18}},"194":{"start":{"line":388,"column":2},"end":{"line":392,"column":4}},"195":{"start":{"line":389,"column":4},"end":{"line":391,"column":8}},"196":{"start":{"line":390,"column":6},"end":{"line":390,"column":29}},"197":{"start":{"line":393,"column":2},"end":{"line":393,"column":54}},"198":{"start":{"line":395,"column":2},"end":{"line":395,"column":19}},"199":{"start":{"line":396,"column":2},"end":{"line":404,"column":5}},"200":{"start":{"line":397,"column":4},"end":{"line":403,"column":7}},"201":{"start":{"line":398,"column":6},"end":{"line":398,"column":61}},"202":{"start":{"line":398,"column":41},"end":{"line":398,"column":57}},"203":{"start":{"line":399,"column":6},"end":{"line":399,"column":70}},"204":{"start":{"line":399,"column":41},"end":{"line":399,"column":66}},"205":{"start":{"line":400,"column":6},"end":{"line":400,"column":29}},"206":{"start":{"line":402,"column":6},"end":{"line":402,"column":40}},"207":{"start":{"line":418,"column":0},"end":{"line":444,"column":2}},"208":{"start":{"line":419,"column":2},"end":{"line":443,"column":3}},"209":{"start":{"line":420,"column":4},"end":{"line":420,"column":48}},"210":{"start":{"line":422,"column":4},"end":{"line":425,"column":7}},"211":{"start":{"line":423,"column":6},"end":{"line":423,"column":36}},"212":{"start":{"line":423,"column":15},"end":{"line":423,"column":36}},"213":{"start":{"line":424,"column":6},"end":{"line":424,"column":28}},"214":{"start":{"line":428,"column":4},"end":{"line":428,"column":47}},"215":{"start":{"line":429,"column":4},"end":{"line":443,"column":2}},"216":{"start":{"line":430,"column":6},"end":{"line":430,"column":32}},"217":{"start":{"line":430,"column":17},"end":{"line":430,"column":30}},"218":{"start":{"line":431,"column":6},"end":{"line":434,"column":7}},"219":{"start":{"line":432,"column":8},"end":{"line":432,"column":67}},"220":{"start":{"line":433,"column":8},"end":{"line":433,"column":22}},"221":{"start":{"line":436,"column":6},"end":{"line":436,"column":56}},"222":{"start":{"line":437,"column":6},"end":{"line":437,"column":39}},"223":{"start":{"line":439,"column":6},"end":{"line":439,"column":55}},"224":{"start":{"line":439,"column":36},"end":{"line":439,"column":51}},"225":{"start":{"line":440,"column":6},"end":{"line":440,"column":31}},"226":{"start":{"line":441,"column":6},"end":{"line":441,"column":22}},"227":{"start":{"line":454,"column":0},"end":{"line":474,"column":2}},"228":{"start":{"line":455,"column":2},"end":{"line":455,"column":18}},"229":{"start":{"line":456,"column":2},"end":{"line":456,"column":28}},"230":{"start":{"line":457,"column":2},"end":{"line":473,"column":5}},"231":{"start":{"line":458,"column":4},"end":{"line":458,"column":37}},"232":{"start":{"line":458,"column":13},"end":{"line":458,"column":37}},"233":{"start":{"line":460,"column":4},"end":{"line":460,"column":19}},"234":{"start":{"line":462,"column":4},"end":{"line":469,"column":5}},"235":{"start":{"line":463,"column":6},"end":{"line":463,"column":36}},"236":{"start":{"line":463,"column":15},"end":{"line":463,"column":36}},"237":{"start":{"line":465,"column":6},"end":{"line":468,"column":62}},"238":{"start":{"line":466,"column":8},"end":{"line":466,"column":31}},"239":{"start":{"line":468,"column":8},"end":{"line":468,"column":62}},"240":{"start":{"line":471,"column":4},"end":{"line":471,"column":65}},"241":{"start":{"line":472,"column":4},"end":{"line":472,"column":47}},"242":{"start":{"line":484,"column":0},"end":{"line":504,"column":2}},"243":{"start":{"line":485,"column":2},"end":{"line":503,"column":3}},"244":{"start":{"line":486,"column":4},"end":{"line":488,"column":17}},"245":{"start":{"line":487,"column":6},"end":{"line":487,"column":33}},"246":{"start":{"line":487,"column":16},"end":{"line":487,"column":33}},"247":{"start":{"line":491,"column":4},"end":{"line":491,"column":20}},"248":{"start":{"line":492,"column":4},"end":{"line":502,"column":7}},"249":{"start":{"line":493,"column":6},"end":{"line":494,"column":64}},"250":{"start":{"line":494,"column":8},"end":{"line":494,"column":64}},"251":{"start":{"line":496,"column":6},"end":{"line":501,"column":19}},"252":{"start":{"line":497,"column":8},"end":{"line":500,"column":9}},"253":{"start":{"line":498,"column":10},"end":{"line":498,"column":74}},"254":{"start":{"line":499,"column":10},"end":{"line":499,"column":30}},"255":{"start":{"line":506,"column":0},"end":{"line":539,"column":2}},"256":{"start":{"line":507,"column":2},"end":{"line":507,"column":69}},"257":{"start":{"line":507,"column":17},"end":{"line":507,"column":69}},"258":{"start":{"line":509,"column":2},"end":{"line":509,"column":53}},"259":{"start":{"line":510,"column":2},"end":{"line":516,"column":5}},"260":{"start":{"line":511,"column":4},"end":{"line":514,"column":5}},"261":{"start":{"line":512,"column":6},"end":{"line":512,"column":20}},"262":{"start":{"line":513,"column":6},"end":{"line":513,"column":31}},"263":{"start":{"line":515,"column":4},"end":{"line":515,"column":34}},"264":{"start":{"line":518,"column":2},"end":{"line":518,"column":18}},"265":{"start":{"line":519,"column":2},"end":{"line":538,"column":5}},"266":{"start":{"line":520,"column":4},"end":{"line":520,"column":35}},"267":{"start":{"line":520,"column":13},"end":{"line":520,"column":35}},"268":{"start":{"line":522,"column":4},"end":{"line":535,"column":7}},"269":{"start":{"line":523,"column":6},"end":{"line":523,"column":37}},"270":{"start":{"line":523,"column":15},"end":{"line":523,"column":37}},"271":{"start":{"line":527,"column":6},"end":{"line":534,"column":7}},"272":{"start":{"line":528,"column":8},"end":{"line":528,"column":41}},"273":{"start":{"line":529,"column":8},"end":{"line":529,"column":41}},"274":{"start":{"line":530,"column":8},"end":{"line":530,"column":32}},"275":{"start":{"line":533,"column":8},"end":{"line":533,"column":70}},"276":{"start":{"line":536,"column":4},"end":{"line":536,"column":65}},"277":{"start":{"line":537,"column":4},"end":{"line":537,"column":47}},"278":{"start":{"line":541,"column":0},"end":{"line":557,"column":2}},"279":{"start":{"line":542,"column":2},"end":{"line":542,"column":29}},"280":{"start":{"line":543,"column":2},"end":{"line":543,"column":45}},"281":{"start":{"line":544,"column":2},"end":{"line":556,"column":5}},"282":{"start":{"line":545,"column":4},"end":{"line":545,"column":34}},"283":{"start":{"line":545,"column":13},"end":{"line":545,"column":34}},"284":{"start":{"line":547,"column":4},"end":{"line":547,"column":46}},"285":{"start":{"line":548,"column":4},"end":{"line":549,"column":68}},"286":{"start":{"line":549,"column":6},"end":{"line":549,"column":68}},"287":{"start":{"line":551,"column":4},"end":{"line":551,"column":26}},"288":{"start":{"line":552,"column":4},"end":{"line":552,"column":26}},"289":{"start":{"line":553,"column":4},"end":{"line":553,"column":50}},"290":{"start":{"line":554,"column":4},"end":{"line":554,"column":42}},"291":{"start":{"line":555,"column":4},"end":{"line":555,"column":27}},"292":{"start":{"line":586,"column":0},"end":{"line":616,"column":2}},"293":{"start":{"line":587,"column":2},"end":{"line":590,"column":3}},"294":{"start":{"line":588,"column":4},"end":{"line":588,"column":38}},"295":{"start":{"line":588,"column":15},"end":{"line":588,"column":36}},"296":{"start":{"line":589,"column":4},"end":{"line":589,"column":62}},"297":{"start":{"line":592,"column":2},"end":{"line":615,"column":3}},"298":{"start":{"line":593,"column":4},"end":{"line":593,"column":39}},"299":{"start":{"line":596,"column":4},"end":{"line":596,"column":20}},"300":{"start":{"line":597,"column":4},"end":{"line":614,"column":7}},"301":{"start":{"line":602,"column":6},"end":{"line":613,"column":7}},"302":{"start":{"line":608,"column":8},"end":{"line":608,"column":28}},"303":{"start":{"line":609,"column":8},"end":{"line":609,"column":43}},"304":{"start":{"line":612,"column":8},"end":{"line":612,"column":33}},"305":{"start":{"line":618,"column":0},"end":{"line":624,"column":2}},"306":{"start":{"line":619,"column":2},"end":{"line":619,"column":18}},"307":{"start":{"line":620,"column":2},"end":{"line":623,"column":5}},"308":{"start":{"line":621,"column":4},"end":{"line":621,"column":34}},"309":{"start":{"line":621,"column":13},"end":{"line":621,"column":34}},"310":{"start":{"line":622,"column":4},"end":{"line":622,"column":66}},"311":{"start":{"line":622,"column":43},"end":{"line":622,"column":62}},"312":{"start":{"line":626,"column":0},"end":{"line":632,"column":2}},"313":{"start":{"line":627,"column":2},"end":{"line":627,"column":18}},"314":{"start":{"line":628,"column":2},"end":{"line":629,"column":43}},"315":{"start":{"line":629,"column":4},"end":{"line":629,"column":43}},"316":{"start":{"line":631,"column":2},"end":{"line":631,"column":66}},"317":{"start":{"line":634,"column":0},"end":{"line":641,"column":2}},"318":{"start":{"line":635,"column":2},"end":{"line":636,"column":43}},"319":{"start":{"line":636,"column":4},"end":{"line":636,"column":43}},"320":{"start":{"line":638,"column":2},"end":{"line":638,"column":24}},"321":{"start":{"line":639,"column":2},"end":{"line":639,"column":23}},"322":{"start":{"line":640,"column":2},"end":{"line":640,"column":29}}},"branchMap":{"1":{"line":39,"type":"if","locations":[{"start":{"line":39,"column":4},"end":{"line":39,"column":4}},{"start":{"line":39,"column":4},"end":{"line":39,"column":4}}]},"2":{"line":41,"type":"if","locations":[{"start":{"line":41,"column":6},"end":{"line":41,"column":6}},{"start":{"line":41,"column":6},"end":{"line":41,"column":6}}]},"3":{"line":53,"type":"if","locations":[{"start":{"line":53,"column":4},"end":{"line":53,"column":4}},{"start":{"line":53,"column":4},"end":{"line":53,"column":4}}]},"4":{"line":65,"type":"if","locations":[{"start":{"line":65,"column":4},"end":{"line":65,"column":4}},{"start":{"line":65,"column":4},"end":{"line":65,"column":4}}]},"5":{"line":74,"type":"binary-expr","locations":[{"start":{"line":74,"column":14},"end":{"line":74,"column":23}},{"start":{"line":74,"column":27},"end":{"line":74,"column":35}}]},"6":{"line":86,"type":"if","locations":[{"start":{"line":86,"column":4},"end":{"line":86,"column":4}},{"start":{"line":86,"column":4},"end":{"line":86,"column":4}}]},"7":{"line":114,"type":"if","locations":[{"start":{"line":114,"column":2},"end":{"line":114,"column":2}},{"start":{"line":114,"column":2},"end":{"line":114,"column":2}}]},"8":{"line":120,"type":"binary-expr","locations":[{"start":{"line":120,"column":20},"end":{"line":120,"column":32}},{"start":{"line":120,"column":36},"end":{"line":120,"column":43}}]},"9":{"line":137,"type":"if","locations":[{"start":{"line":137,"column":4},"end":{"line":137,"column":4}},{"start":{"line":137,"column":4},"end":{"line":137,"column":4}}]},"10":{"line":145,"type":"if","locations":[{"start":{"line":145,"column":2},"end":{"line":145,"column":2}},{"start":{"line":145,"column":2},"end":{"line":145,"column":2}}]},"11":{"line":148,"type":"if","locations":[{"start":{"line":148,"column":2},"end":{"line":148,"column":2}},{"start":{"line":148,"column":2},"end":{"line":148,"column":2}}]},"12":{"line":152,"type":"if","locations":[{"start":{"line":152,"column":2},"end":{"line":152,"column":2}},{"start":{"line":152,"column":2},"end":{"line":152,"column":2}}]},"13":{"line":155,"type":"if","locations":[{"start":{"line":155,"column":4},"end":{"line":155,"column":4}},{"start":{"line":155,"column":4},"end":{"line":155,"column":4}}]},"14":{"line":155,"type":"binary-expr","locations":[{"start":{"line":155,"column":8},"end":{"line":155,"column":28}},{"start":{"line":155,"column":32},"end":{"line":155,"column":83}}]},"15":{"line":160,"type":"if","locations":[{"start":{"line":160,"column":4},"end":{"line":160,"column":4}},{"start":{"line":160,"column":4},"end":{"line":160,"column":4}}]},"16":{"line":164,"type":"if","locations":[{"start":{"line":164,"column":2},"end":{"line":164,"column":2}},{"start":{"line":164,"column":2},"end":{"line":164,"column":2}}]},"17":{"line":164,"type":"binary-expr","locations":[{"start":{"line":164,"column":6},"end":{"line":164,"column":24}},{"start":{"line":164,"column":28},"end":{"line":164,"column":60}}]},"18":{"line":179,"type":"if","locations":[{"start":{"line":179,"column":2},"end":{"line":179,"column":2}},{"start":{"line":179,"column":2},"end":{"line":179,"column":2}}]},"19":{"line":179,"type":"binary-expr","locations":[{"start":{"line":179,"column":6},"end":{"line":179,"column":14}},{"start":{"line":179,"column":18},"end":{"line":179,"column":45}}]},"20":{"line":182,"type":"if","locations":[{"start":{"line":182,"column":2},"end":{"line":182,"column":2}},{"start":{"line":182,"column":2},"end":{"line":182,"column":2}}]},"21":{"line":189,"type":"if","locations":[{"start":{"line":189,"column":2},"end":{"line":189,"column":2}},{"start":{"line":189,"column":2},"end":{"line":189,"column":2}}]},"22":{"line":208,"type":"if","locations":[{"start":{"line":208,"column":2},"end":{"line":208,"column":2}},{"start":{"line":208,"column":2},"end":{"line":208,"column":2}}]},"23":{"line":211,"type":"if","locations":[{"start":{"line":211,"column":2},"end":{"line":211,"column":2}},{"start":{"line":211,"column":2},"end":{"line":211,"column":2}}]},"24":{"line":211,"type":"binary-expr","locations":[{"start":{"line":211,"column":6},"end":{"line":211,"column":17}},{"start":{"line":211,"column":21},"end":{"line":211,"column":41}}]},"25":{"line":214,"type":"if","locations":[{"start":{"line":214,"column":7},"end":{"line":214,"column":7}},{"start":{"line":214,"column":7},"end":{"line":214,"column":7}}]},"26":{"line":226,"type":"if","locations":[{"start":{"line":226,"column":4},"end":{"line":226,"column":4}},{"start":{"line":226,"column":4},"end":{"line":226,"column":4}}]},"27":{"line":226,"type":"binary-expr","locations":[{"start":{"line":226,"column":8},"end":{"line":226,"column":26}},{"start":{"line":226,"column":30},"end":{"line":226,"column":64}}]},"28":{"line":252,"type":"if","locations":[{"start":{"line":252,"column":2},"end":{"line":252,"column":2}},{"start":{"line":252,"column":2},"end":{"line":252,"column":2}}]},"29":{"line":259,"type":"if","locations":[{"start":{"line":259,"column":2},"end":{"line":259,"column":2}},{"start":{"line":259,"column":2},"end":{"line":259,"column":2}}]},"30":{"line":260,"type":"binary-expr","locations":[{"start":{"line":260,"column":20},"end":{"line":260,"column":33}},{"start":{"line":260,"column":37},"end":{"line":260,"column":57}}]},"31":{"line":274,"type":"if","locations":[{"start":{"line":274,"column":2},"end":{"line":274,"column":2}},{"start":{"line":274,"column":2},"end":{"line":274,"column":2}}]},"32":{"line":299,"type":"if","locations":[{"start":{"line":299,"column":2},"end":{"line":299,"column":2}},{"start":{"line":299,"column":2},"end":{"line":299,"column":2}}]},"33":{"line":301,"type":"cond-expr","locations":[{"start":{"line":301,"column":28},"end":{"line":301,"column":30}},{"start":{"line":301,"column":33},"end":{"line":301,"column":64}}]},"34":{"line":303,"type":"if","locations":[{"start":{"line":303,"column":8},"end":{"line":303,"column":8}},{"start":{"line":303,"column":8},"end":{"line":303,"column":8}}]},"35":{"line":303,"type":"binary-expr","locations":[{"start":{"line":303,"column":12},"end":{"line":303,"column":16}},{"start":{"line":303,"column":20},"end":{"line":303,"column":36}}]},"36":{"line":331,"type":"if","locations":[{"start":{"line":331,"column":2},"end":{"line":331,"column":2}},{"start":{"line":331,"column":2},"end":{"line":331,"column":2}}]},"37":{"line":333,"type":"if","locations":[{"start":{"line":333,"column":2},"end":{"line":333,"column":2}},{"start":{"line":333,"column":2},"end":{"line":333,"column":2}}]},"38":{"line":334,"type":"if","locations":[{"start":{"line":334,"column":2},"end":{"line":334,"column":2}},{"start":{"line":334,"column":2},"end":{"line":334,"column":2}}]},"39":{"line":338,"type":"if","locations":[{"start":{"line":338,"column":4},"end":{"line":338,"column":4}},{"start":{"line":338,"column":4},"end":{"line":338,"column":4}}]},"40":{"line":338,"type":"binary-expr","locations":[{"start":{"line":338,"column":8},"end":{"line":338,"column":12}},{"start":{"line":338,"column":16},"end":{"line":338,"column":54}}]},"41":{"line":342,"type":"if","locations":[{"start":{"line":342,"column":8},"end":{"line":342,"column":8}},{"start":{"line":342,"column":8},"end":{"line":342,"column":8}}]},"42":{"line":345,"type":"if","locations":[{"start":{"line":345,"column":8},"end":{"line":345,"column":8}},{"start":{"line":345,"column":8},"end":{"line":345,"column":8}}]},"43":{"line":351,"type":"if","locations":[{"start":{"line":351,"column":13},"end":{"line":351,"column":13}},{"start":{"line":351,"column":13},"end":{"line":351,"column":13}}]},"44":{"line":364,"type":"if","locations":[{"start":{"line":364,"column":2},"end":{"line":364,"column":2}},{"start":{"line":364,"column":2},"end":{"line":364,"column":2}}]},"45":{"line":369,"type":"if","locations":[{"start":{"line":369,"column":4},"end":{"line":369,"column":4}},{"start":{"line":369,"column":4},"end":{"line":369,"column":4}}]},"46":{"line":382,"type":"if","locations":[{"start":{"line":382,"column":2},"end":{"line":382,"column":2}},{"start":{"line":382,"column":2},"end":{"line":382,"column":2}}]},"47":{"line":399,"type":"binary-expr","locations":[{"start":{"line":399,"column":44},"end":{"line":399,"column":47}},{"start":{"line":399,"column":51},"end":{"line":399,"column":55}}]},"48":{"line":402,"type":"binary-expr","locations":[{"start":{"line":402,"column":27},"end":{"line":402,"column":31}},{"start":{"line":402,"column":35},"end":{"line":402,"column":37}}]},"49":{"line":419,"type":"if","locations":[{"start":{"line":419,"column":2},"end":{"line":419,"column":2}},{"start":{"line":419,"column":2},"end":{"line":419,"column":2}}]},"50":{"line":420,"type":"binary-expr","locations":[{"start":{"line":420,"column":20},"end":{"line":420,"column":29}},{"start":{"line":420,"column":33},"end":{"line":420,"column":46}}]},"51":{"line":423,"type":"if","locations":[{"start":{"line":423,"column":6},"end":{"line":423,"column":6}},{"start":{"line":423,"column":6},"end":{"line":423,"column":6}}]},"52":{"line":428,"type":"binary-expr","locations":[{"start":{"line":428,"column":20},"end":{"line":428,"column":28}},{"start":{"line":428,"column":32},"end":{"line":428,"column":45}}]},"53":{"line":430,"type":"if","locations":[{"start":{"line":430,"column":6},"end":{"line":430,"column":6}},{"start":{"line":430,"column":6},"end":{"line":430,"column":6}}]},"54":{"line":458,"type":"if","locations":[{"start":{"line":458,"column":4},"end":{"line":458,"column":4}},{"start":{"line":458,"column":4},"end":{"line":458,"column":4}}]},"55":{"line":463,"type":"if","locations":[{"start":{"line":463,"column":6},"end":{"line":463,"column":6}},{"start":{"line":463,"column":6},"end":{"line":463,"column":6}}]},"56":{"line":465,"type":"if","locations":[{"start":{"line":465,"column":6},"end":{"line":465,"column":6}},{"start":{"line":465,"column":6},"end":{"line":465,"column":6}}]},"57":{"line":485,"type":"if","locations":[{"start":{"line":485,"column":2},"end":{"line":485,"column":2}},{"start":{"line":485,"column":2},"end":{"line":485,"column":2}}]},"58":{"line":487,"type":"if","locations":[{"start":{"line":487,"column":6},"end":{"line":487,"column":6}},{"start":{"line":487,"column":6},"end":{"line":487,"column":6}}]},"59":{"line":493,"type":"if","locations":[{"start":{"line":493,"column":6},"end":{"line":493,"column":6}},{"start":{"line":493,"column":6},"end":{"line":493,"column":6}}]},"60":{"line":497,"type":"if","locations":[{"start":{"line":497,"column":8},"end":{"line":497,"column":8}},{"start":{"line":497,"column":8},"end":{"line":497,"column":8}}]},"61":{"line":507,"type":"if","locations":[{"start":{"line":507,"column":2},"end":{"line":507,"column":2}},{"start":{"line":507,"column":2},"end":{"line":507,"column":2}}]},"62":{"line":509,"type":"binary-expr","locations":[{"start":{"line":509,"column":22},"end":{"line":509,"column":34}},{"start":{"line":509,"column":38},"end":{"line":509,"column":51}}]},"63":{"line":511,"type":"if","locations":[{"start":{"line":511,"column":4},"end":{"line":511,"column":4}},{"start":{"line":511,"column":4},"end":{"line":511,"column":4}}]},"64":{"line":520,"type":"if","locations":[{"start":{"line":520,"column":4},"end":{"line":520,"column":4}},{"start":{"line":520,"column":4},"end":{"line":520,"column":4}}]},"65":{"line":523,"type":"if","locations":[{"start":{"line":523,"column":6},"end":{"line":523,"column":6}},{"start":{"line":523,"column":6},"end":{"line":523,"column":6}}]},"66":{"line":527,"type":"if","locations":[{"start":{"line":527,"column":6},"end":{"line":527,"column":6}},{"start":{"line":527,"column":6},"end":{"line":527,"column":6}}]},"67":{"line":543,"type":"binary-expr","locations":[{"start":{"line":543,"column":18},"end":{"line":543,"column":26}},{"start":{"line":543,"column":30},"end":{"line":543,"column":43}}]},"68":{"line":545,"type":"if","locations":[{"start":{"line":545,"column":4},"end":{"line":545,"column":4}},{"start":{"line":545,"column":4},"end":{"line":545,"column":4}}]},"69":{"line":548,"type":"if","locations":[{"start":{"line":548,"column":4},"end":{"line":548,"column":4}},{"start":{"line":548,"column":4},"end":{"line":548,"column":4}}]},"70":{"line":554,"type":"binary-expr","locations":[{"start":{"line":554,"column":22},"end":{"line":554,"column":29}},{"start":{"line":554,"column":33},"end":{"line":554,"column":40}}]},"71":{"line":588,"type":"if","locations":[{"start":{"line":588,"column":4},"end":{"line":588,"column":4}},{"start":{"line":588,"column":4},"end":{"line":588,"column":4}}]},"72":{"line":589,"type":"binary-expr","locations":[{"start":{"line":589,"column":36},"end":{"line":589,"column":48}},{"start":{"line":589,"column":52},"end":{"line":589,"column":59}}]},"73":{"line":592,"type":"if","locations":[{"start":{"line":592,"column":2},"end":{"line":592,"column":2}},{"start":{"line":592,"column":2},"end":{"line":592,"column":2}}]},"74":{"line":602,"type":"if","locations":[{"start":{"line":602,"column":6},"end":{"line":602,"column":6}},{"start":{"line":602,"column":6},"end":{"line":602,"column":6}}]},"75":{"line":602,"type":"binary-expr","locations":[{"start":{"line":602,"column":11},"end":{"line":602,"column":14}},{"start":{"line":602,"column":19},"end":{"line":602,"column":35}},{"start":{"line":602,"column":39},"end":{"line":602,"column":55}},{"start":{"line":603,"column":9},"end":{"line":603,"column":20}},{"start":{"line":603,"column":24},"end":{"line":603,"column":63}}]},"76":{"line":621,"type":"if","locations":[{"start":{"line":621,"column":4},"end":{"line":621,"column":4}},{"start":{"line":621,"column":4},"end":{"line":621,"column":4}}]},"77":{"line":628,"type":"if","locations":[{"start":{"line":628,"column":2},"end":{"line":628,"column":2}},{"start":{"line":628,"column":2},"end":{"line":628,"column":2}}]},"78":{"line":635,"type":"if","locations":[{"start":{"line":635,"column":2},"end":{"line":635,"column":2}},{"start":{"line":635,"column":2},"end":{"line":635,"column":2}}]}}}; +} +var __cov_0Zs5Q6XMeQ4aT8ak62bnkw = __coverage__['/Users/sergi/programming/jsftp/lib/jsftp.js']; +__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['1']++;var Net=require('net');__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['2']++;var EventEmitter=require('events').EventEmitter;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['3']++;var Stream=require('stream');__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['4']++;var es=require('event-stream');__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['5']++;var responseHandler=require('./response');__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['6']++;var Utils=require('./utils');__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['7']++;var util=require('util');__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['8']++;var fs=require('fs');__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['9']++;var FTP_PORT=21;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['10']++;var DEBUG_MODE=false;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['11']++;var TIMEOUT=10*60*1000;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['12']++;var IDLE_TIME=30000;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['13']++;var COMMANDS=['abor','pwd','cdup','feat','noop','quit','pasv','syst','cwd','dele','list','mdtm','mkd','mode','nlst','pass','retr','rmd','rnfr','rnto','site','stat','stor','type','user','pass','xrmd','opts','chmod','size'];__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['14']++;var Cmds={};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['15']++;COMMANDS.forEach(function(cmd){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['1']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['16']++;cmd=cmd.toLowerCase();__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['17']++;Cmds[cmd]=function(){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['2']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['18']++;var callback=function(){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['3']++;};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['19']++;var completeCmd=cmd;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['20']++;if(arguments.length){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['1'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['21']++;var args=Array.prototype.slice.call(arguments);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['22']++;if(typeof args[args.length-1]==='function'){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['2'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['23']++;callback=args.pop();}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['2'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['24']++;completeCmd+=' '+args.join(' ');}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['1'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['25']++;this.execute(completeCmd.trim(),callback);};});__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['26']++;function once(fn){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['4']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['27']++;var returnValue,called=false;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['28']++;return function(){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['5']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['29']++;if(!called){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['3'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['30']++;called=true;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['31']++;returnValue=fn.apply(this,arguments);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['3'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['32']++;return returnValue;};}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['33']++;var Ftp=module.exports=function(cfg){'use strict';__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['6']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['34']++;Object.keys(cfg).forEach(function(opt){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['7']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['35']++;if(!this[opt]){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['4'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['36']++;this[opt]=cfg[opt];}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['4'][1]++;}},this);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['37']++;EventEmitter.call(this);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['38']++;this.useList=false;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['39']++;this.port=(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['5'][0]++,this.port)||(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['5'][1]++,FTP_PORT);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['40']++;this.pending=[];__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['41']++;this.cmdBuffer_=[];__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['42']++;this.responseHandler=responseHandler();__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['43']++;var raw=this.raw={};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['44']++;COMMANDS.forEach(function(cmd){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['8']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['45']++;raw[cmd]=Cmds[cmd].bind(this);},this);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['46']++;this.on('error',function(err){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['9']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['47']++;if(DEBUG_MODE===true){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['6'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['48']++;console.log(err);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['6'][1]++;}});__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['49']++;this._createSocket(this.port,this.host);};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['50']++;util.inherits(Ftp,EventEmitter);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['51']++;Ftp.prototype._socketOnTimeout=function(){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['10']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['52']++;this.emit('timeout',errString);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['53']++;this.pipeline.pause();__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['54']++;this.destroy();};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['55']++;Ftp.prototype._socketOnError=function(err){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['11']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['56']++;var errString='Error on ftp socket: '+err;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['57']++;this.emit('error',errString);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['58']++;this.pipeline.pause();};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['59']++;Ftp.prototype._socketOnConnect=function(){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['12']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['60']++;this.emit('connect',this.socket);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['61']++;this.connecting=false;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['62']++;this.pipeline.resume();};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['63']++;Ftp.prototype._createSocket=function(port,host){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['13']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['64']++;if(this.socket){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['7'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['65']++;this.socket.destroy();}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['7'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['66']++;this.connecting=true;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['67']++;this.authenticated=false;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['68']++;var socket=this.socket=Net.createConnection(port,host);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['69']++;socket.setTimeout((__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['8'][0]++,this.timeout)||(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['8'][1]++,TIMEOUT),this._socketOnTimeout.bind(this));__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['70']++;socket.on('error',this._socketOnError.bind(this));__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['71']++;socket.on('connect',this._socketOnConnect.bind(this));__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['72']++;this._createStreams(this.socket);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['73']++;this.pipeline.pause();};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['74']++;Ftp.prototype._createStreams=function(socket){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['14']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['75']++;this.pipeline=es.pipeline(socket,es.split(),es.mapSync(this.responseHandler));__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['76']++;var self=this;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['77']++;this.pipeline.on('data',function(data){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['15']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['78']++;if(DEBUG_MODE===true){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['9'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['79']++;console.log(' ',data.code,data.text);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['9'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['80']++;self.parseResponse.call(self,data);});};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['81']++;Ftp.prototype.parseResponse=function(data){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['16']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['82']++;if(!this.cmdBuffer_.length){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['10'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['83']++;return;}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['10'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['84']++;if([220].indexOf(data.code)>-1){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['11'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['85']++;return;}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['11'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['86']++;var nextCmd=this.cmdBuffer_[0][1];__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['87']++;if(Utils.isMark(data.code)){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['12'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['88']++;if((__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['14'][0]++,!nextCmd.expectsMark)||(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['14'][1]++,nextCmd.expectsMark.marks.indexOf(data.code)===-1)){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['13'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['89']++;return;}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['13'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['90']++;if(nextCmd.expectsMark.ignore){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['15'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['91']++;this.ignoreCmdCode=nextCmd.expectsMark.ignore;}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['15'][1]++;}}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['12'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['92']++;if((__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['17'][0]++,this.ignoreCmdCode)&&(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['17'][1]++,this.ignoreCmdCode===data.code)){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['16'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['93']++;this.ignoreCmdCode=null;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['94']++;return;}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['16'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['95']++;this.parse([data,this.cmdBuffer_.shift()]);};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['96']++;Ftp.prototype.send=function(command){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['17']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['97']++;if((__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['19'][0]++,!command)||(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['19'][1]++,typeof command!=='string')){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['18'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['98']++;return;}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['18'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['99']++;if(DEBUG_MODE===true){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['20'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['100']++;console.log('\nSEND',command,this.cmdBuffer_,'\n');}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['20'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['101']++;this.pipeline.write(command+'\r\n');};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['102']++;Ftp.prototype.nextCmd=function(){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['18']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['103']++;if(this.cmdBuffer_[0]){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['21'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['104']++;this.send(this.cmdBuffer_[0][0]);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['21'][1]++;}};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['105']++;Ftp.prototype.execute=function(action,callback){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['19']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['106']++;if(!callback){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['22'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['107']++;callback=function(){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['20']++;};}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['22'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['108']++;var self=this;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['109']++;if((__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['24'][0]++,this.socket)&&(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['24'][1]++,this.socket.writable)){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['23'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['110']++;execute();}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['23'][1]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['111']++;if(!this.connecting){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['25'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['112']++;this.authenticated=false;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['113']++;this.once('connect',execute);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['114']++;this._createSocket(this.port,this.host);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['25'][1]++;}}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['115']++;function execute(){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['21']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['116']++;function executeCmd(){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['22']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['117']++;self.cmdBuffer_.push([action,callback]);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['118']++;self.nextCmd();}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['119']++;if((__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['27'][0]++,self.authenticated)||(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['27'][1]++,/feat|syst|user|pass/.test(action))){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['26'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['120']++;executeCmd();}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['26'][1]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['121']++;self.getFeatures(function(){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['23']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['122']++;self.auth(self.user,self.pass,executeCmd);});}}};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['123']++;Ftp.prototype.parse=function(action){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['24']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['124']++;var response=action[0];__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['125']++;var command=action[1];__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['126']++;var cb=command[1];__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['127']++;if(!response){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['28'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['128']++;cb(new Error('FTP response not defined'));}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['28'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['129']++;var err=null;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['130']++;if(response.code>399){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['29'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['131']++;err=new Error((__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['30'][0]++,response.text)||(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['30'][1]++,'Unknown FTP error.'));__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['132']++;err.code=response.code;}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['29'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['133']++;cb(err,response);};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['134']++;Ftp.prototype.hasFeat=function(feature){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['25']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['135']++;if(feature){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['31'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['136']++;return this.features.indexOf(feature.toLowerCase())>-1;}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['31'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['137']++;return false;};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['138']++;Ftp.prototype._parseFeats=function(features){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['26']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['139']++;return features.split(/\r\n|\n/).slice(1,-1).map(function(feat){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['27']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['140']++;return/^\s*(\w*)\s*/.exec(feat)[1].trim().toLowerCase();});};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['141']++;Ftp.prototype.getFeatures=function(callback){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['28']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['142']++;var self=this;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['143']++;if(!this.features){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['32'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['144']++;this.raw.feat(function(err,response){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['29']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['145']++;self.features=err?(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['33'][0]++,[]):(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['33'][1]++,self._parseFeats(response.text));__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['146']++;self.raw.syst(function(err,res){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['30']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['147']++;if((__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['35'][0]++,!err)&&(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['35'][1]++,res.code===215)){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['34'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['148']++;self.system=res.text.toLowerCase();}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['34'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['149']++;callback(null,self.features);});});}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['32'][1]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['150']++;callback(null,self.features);}};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['151']++;Ftp.prototype.auth=function(user,pass,callback){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['31']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['152']++;this.pending.push(callback);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['153']++;var self=this;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['154']++;function notifyAll(err,res){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['32']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['155']++;var cb;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['156']++;while(cb=self.pending.shift()){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['157']++;cb(err,res);}}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['158']++;if(this.authenticating){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['36'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['159']++;return;}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['36'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['160']++;if(!user){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['37'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['161']++;user='anonymous';}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['37'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['162']++;if(!pass){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['38'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['163']++;pass='@anonymous';}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['38'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['164']++;this.authenticating=true;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['165']++;self.raw.user(user,function(err,res){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['33']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['166']++;if((__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['40'][0]++,!err)&&(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['40'][1]++,[230,331,332].indexOf(res.code)>-1)){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['39'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['167']++;self.raw.pass(pass,function(err,res){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['34']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['168']++;self.authenticating=false;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['169']++;if(err){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['41'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['170']++;notifyAll(new Error('Login not accepted'));}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['41'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['171']++;if([230,202].indexOf(res.code)>-1){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['42'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['172']++;self.authenticated=true;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['173']++;self.user=user;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['174']++;self.pass=pass;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['175']++;self.raw.type('I',function(){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['35']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['176']++;notifyAll(null,res);});}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['42'][1]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['177']++;if(res.code===332){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['43'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['178']++;self.raw.acct('');}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['43'][1]++;}}});}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['39'][1]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['179']++;self.authenticating=false;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['180']++;notifyAll(new Error('Login not accepted'));}});};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['181']++;Ftp.prototype.setType=function(type,callback){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['36']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['182']++;if(this.type===type){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['44'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['183']++;callback(null);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['44'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['184']++;var self=this;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['185']++;this.raw.type(type,function(err,data){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['37']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['186']++;if(!err){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['45'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['187']++;self.type=type;}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['45'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['188']++;callback(err,data);});};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['189']++;Ftp.prototype.list=function(path,callback){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['38']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['190']++;if(arguments.length===1){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['46'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['191']++;callback=arguments[0];__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['192']++;path='';}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['46'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['193']++;var self=this;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['194']++;var cb=function(err,listing){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['39']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['195']++;self.setType('I',once(function(){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['40']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['196']++;callback(err,listing);}));};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['197']++;cb.expectsMark={marks:[125,150],ignore:226};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['198']++;var listing='';__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['199']++;this.setType('A',function(){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['41']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['200']++;self.getPasvSocket(function(err,socket){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['42']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['201']++;socket.on('data',function(data){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['43']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['202']++;listing+=data;});__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['203']++;socket.on('close',function(err){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['44']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['204']++;cb((__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['47'][0]++,err)||(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['47'][1]++,null),listing);});__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['205']++;socket.on('error',cb);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['206']++;self.send('list '+((__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['48'][0]++,path)||(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['48'][1]++,'')));});});};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['207']++;Ftp.prototype.get=function(remotePath,localPath,callback){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['45']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['208']++;if(arguments.length===2){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['49'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['209']++;callback=once((__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['50'][0]++,localPath)||(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['50'][1]++,function(){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['46']++;}));__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['210']++;this.getGetSocket(remotePath,function(err,socket){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['47']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['211']++;if(err){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['51'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['212']++;return callback(err);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['51'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['213']++;callback(err,socket);});}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['49'][1]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['214']++;callback=once((__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['52'][0]++,callback)||(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['52'][1]++,function(){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['48']++;}));__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['215']++;this.getGetSocket(remotePath,function(err,socket){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['49']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['216']++;if(err){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['53'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['217']++;onError(err);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['53'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['218']++;function onError(err){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['50']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['219']++;console.error('There was an error transferring the file.');__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['220']++;callback(err);}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['221']++;var writeStream=fs.createWriteStream(localPath);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['222']++;writeStream.on('error',onError);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['223']++;socket.on('end',function(){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['51']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['224']++;callback(null);});__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['225']++;socket.pipe(writeStream);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['226']++;socket.resume();});}};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['227']++;Ftp.prototype.getGetSocket=function(path,callback){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['52']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['228']++;var self=this;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['229']++;callback=once(callback);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['230']++;this.getPasvSocket(function(err,socket){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['53']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['231']++;if(err){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['54'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['232']++;return cmdCallback(err);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['54'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['233']++;socket.pause();__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['234']++;function cmdCallback(err,res){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['54']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['235']++;if(err){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['55'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['236']++;return callback(err);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['55'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['237']++;if(res.code===150){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['56'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['238']++;callback(null,socket);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['56'][1]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['239']++;callback(new Error('Unexpected command '+res.text));}}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['240']++;cmdCallback.expectsMark={marks:[125,150],ignore:226};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['241']++;self.execute('retr '+path,cmdCallback);});};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['242']++;Ftp.prototype.put=function(from,to,callback){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['55']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['243']++;if(from instanceof Buffer){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['57'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['244']++;this.getPutSocket(to,function(err,socket){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['56']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['245']++;if(!err){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['58'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['246']++;socket.end(from);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['58'][1]++;}},callback);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['57'][1]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['247']++;var self=this;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['248']++;fs.exists(from,function(exists){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['57']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['249']++;if(!exists){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['59'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['250']++;return callback(new Error('Local file doesn\'t exist.'));}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['59'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['251']++;self.getPutSocket(to,function(err,socket){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['58']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['252']++;if(!err){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['60'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['253']++;var origin=fs.createReadStream(from,{bufferSize:4*1024});__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['254']++;origin.pipe(socket);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['60'][1]++;}},callback);});}};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['255']++;Ftp.prototype.getPutSocket=function(path,callback,doneCallback){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['59']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['256']++;if(!callback){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['61'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['257']++;throw new Error('A callback argument is required.');}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['61'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['258']++;doneCallback=once((__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['62'][0]++,doneCallback)||(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['62'][1]++,function(){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['60']++;}));__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['259']++;var _callback=once(function(err,_socket){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['61']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['260']++;if(err){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['63'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['261']++;callback(err);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['262']++;return doneCallback(err);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['63'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['263']++;return callback(err,_socket);});__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['264']++;var self=this;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['265']++;this.getPasvSocket(function(err,socket){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['62']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['266']++;if(err){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['64'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['267']++;return _callback(err);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['64'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['268']++;var putCallback=once(function putCallback(err,res){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['63']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['269']++;if(err){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['65'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['270']++;return _callback(err);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['65'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['271']++;if(res.code===150){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['66'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['272']++;socket.on('close',doneCallback);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['273']++;socket.on('error',doneCallback);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['274']++;_callback(null,socket);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['66'][1]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['275']++;return _callback(new Error('Unexpected command '+res.text));}});__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['276']++;putCallback.expectsMark={marks:[125,150],ignore:226};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['277']++;self.execute('stor '+path,putCallback);});};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['278']++;Ftp.prototype.getPasvSocket=function(callback){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['64']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['279']++;var timeout=this.timeout;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['280']++;callback=once((__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['67'][0]++,callback)||(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['67'][1]++,function(){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['65']++;}));__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['281']++;this.execute('pasv',function(err,res){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['66']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['282']++;if(err){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['68'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['283']++;return callback(err);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['68'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['284']++;var pasvRes=Utils.getPasvPort(res.text);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['285']++;if(pasvRes===false){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['69'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['286']++;return callback(new Error('PASV: Bad host/port combination'));}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['69'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['287']++;var host=pasvRes[0];__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['288']++;var port=pasvRes[1];__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['289']++;var socket=Net.createConnection(port,host);__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['290']++;socket.setTimeout((__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['70'][0]++,timeout)||(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['70'][1]++,TIMEOUT));__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['291']++;callback(null,socket);});};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['292']++;Ftp.prototype.ls=function(filePath,callback){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['67']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['293']++;function entriesToList(err,entries){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['68']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['294']++;if(err){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['71'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['295']++;return callback(err);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['71'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['296']++;callback(null,Utils.parseEntry((__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['72'][0]++,entries.text)||(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['72'][1]++,entries)));}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['297']++;if(this.useList){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['73'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['298']++;this.list(filePath,entriesToList);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['73'][1]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['299']++;var self=this;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['300']++;this.raw.stat(filePath,function(err,data){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['69']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['301']++;if((__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['75'][0]++,err)&&((__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['75'][1]++,err.code===502)||(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['75'][2]++,err.code===500))||(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['75'][3]++,self.system)&&(__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['75'][4]++,self.system.indexOf('hummingbird')>-1)){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['74'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['302']++;self.useList=true;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['303']++;self.list(filePath,entriesToList);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['74'][1]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['304']++;entriesToList(err,data);}});}};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['305']++;Ftp.prototype.rename=function(from,to,callback){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['70']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['306']++;var self=this;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['307']++;this.raw.rnfr(from,function(err,res){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['71']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['308']++;if(err){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['76'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['309']++;return callback(err);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['76'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['310']++;self.raw.rnto(to,function(err,res){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['72']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['311']++;callback(err,res);});});};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['312']++;Ftp.prototype.keepAlive=function(){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['73']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['313']++;var self=this;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['314']++;if(this._keepAliveInterval){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['77'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['315']++;clearInterval(this._keepAliveInterval);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['77'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['316']++;this._keepAliveInterval=setInterval(self.raw.noop,IDLE_TIME);};__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['317']++;Ftp.prototype.destroy=function(){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.f['74']++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['318']++;if(this._keepAliveInterval){__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['78'][0]++;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['319']++;clearInterval(this._keepAliveInterval);}else{__cov_0Zs5Q6XMeQ4aT8ak62bnkw.b['78'][1]++;}__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['320']++;this.socket.destroy();__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['321']++;this.features=null;__cov_0Zs5Q6XMeQ4aT8ak62bnkw.s['322']++;this.authenticated=false;}; diff --git a/node_modules/jsftp/lib/lib/response.js b/node_modules/jsftp/lib/lib/response.js new file mode 100644 index 0000000..e660f48 --- /dev/null +++ b/node_modules/jsftp/lib/lib/response.js @@ -0,0 +1,6 @@ +if (typeof __coverage__ === 'undefined') { __coverage__ = {}; } +if (!__coverage__['/Users/sergi/programming/jsftp/lib/response.js']) { + __coverage__['/Users/sergi/programming/jsftp/lib/response.js'] = {"path":"/Users/sergi/programming/jsftp/lib/response.js","s":{"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0},"b":{"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0]},"f":{"1":0,"2":0},"fnMap":{"1":{"name":"responseHandler","line":12,"loc":{"start":{"line":12,"column":0},"end":{"line":12,"column":27}}},"2":{"name":"(anonymous_2)","line":16,"loc":{"start":{"line":16,"column":9},"end":{"line":16,"column":24}}}},"statementMap":{"1":{"start":{"line":1,"column":0},"end":{"line":1,"column":13}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":31}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":28}},"4":{"start":{"line":12,"column":0},"end":{"line":60,"column":1}},"5":{"start":{"line":13,"column":2},"end":{"line":13,"column":18}},"6":{"start":{"line":14,"column":2},"end":{"line":14,"column":22}},"7":{"start":{"line":16,"column":2},"end":{"line":60,"column":0}},"8":{"start":{"line":17,"column":4},"end":{"line":17,"column":38}},"9":{"start":{"line":18,"column":4},"end":{"line":18,"column":17}},"10":{"start":{"line":20,"column":4},"end":{"line":20,"column":13}},"11":{"start":{"line":21,"column":4},"end":{"line":58,"column":5}},"12":{"start":{"line":22,"column":6},"end":{"line":22,"column":40}},"13":{"start":{"line":24,"column":6},"end":{"line":32,"column":7}},"14":{"start":{"line":25,"column":8},"end":{"line":25,"column":26}},"15":{"start":{"line":27,"column":8},"end":{"line":31,"column":9}},"16":{"start":{"line":28,"column":10},"end":{"line":28,"column":35}},"17":{"start":{"line":29,"column":10},"end":{"line":29,"column":22}},"18":{"start":{"line":30,"column":10},"end":{"line":30,"column":26}},"19":{"start":{"line":34,"column":6},"end":{"line":34,"column":40}},"20":{"start":{"line":37,"column":6},"end":{"line":56,"column":7}},"21":{"start":{"line":38,"column":8},"end":{"line":38,"column":48}},"22":{"start":{"line":40,"column":11},"end":{"line":56,"column":7}},"23":{"start":{"line":57,"column":6},"end":{"line":57,"column":35}},"24":{"start":{"line":62,"column":0},"end":{"line":62,"column":33}}},"branchMap":{"1":{"line":21,"type":"if","locations":[{"start":{"line":21,"column":4},"end":{"line":21,"column":4}},{"start":{"line":21,"column":4},"end":{"line":21,"column":4}}]},"2":{"line":24,"type":"if","locations":[{"start":{"line":24,"column":6},"end":{"line":24,"column":6}},{"start":{"line":24,"column":6},"end":{"line":24,"column":6}}]},"3":{"line":27,"type":"if","locations":[{"start":{"line":27,"column":8},"end":{"line":27,"column":8}},{"start":{"line":27,"column":8},"end":{"line":27,"column":8}}]},"4":{"line":37,"type":"if","locations":[{"start":{"line":37,"column":6},"end":{"line":37,"column":6}},{"start":{"line":37,"column":6},"end":{"line":37,"column":6}}]},"5":{"line":37,"type":"binary-expr","locations":[{"start":{"line":37,"column":10},"end":{"line":37,"column":24}},{"start":{"line":37,"column":29},"end":{"line":37,"column":59}}]},"6":{"line":40,"type":"if","locations":[{"start":{"line":40,"column":11},"end":{"line":40,"column":11}},{"start":{"line":40,"column":11},"end":{"line":40,"column":11}}]}}}; +} +var __cov_Cbo7IaZZ3Ln4aLUw25DLOQ = __coverage__['/Users/sergi/programming/jsftp/lib/response.js']; +__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['1']++;'use strict';__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['2']++;var RE_RES=/^(\d\d\d)\s(.*)/;__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['3']++;var RE_MULTI=/^(\d\d\d)-/;__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['4']++;function responseHandler(){__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.f['1']++;__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['5']++;var buffer=[];__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['6']++;var currentCode=0;__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['7']++;return function(line){__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.f['2']++;__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['8']++;var simpleRes=RE_RES.exec(line);__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['9']++;var multiRes;__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['10']++;var code;__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['11']++;if(simpleRes){__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.b['1'][0]++;__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['12']++;code=parseInt(simpleRes[1],10);__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['13']++;if(buffer.length){__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.b['2'][0]++;__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['14']++;buffer.push(line);__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['15']++;if(currentCode===code){__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.b['3'][0]++;__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['16']++;line=buffer.join('\n');__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['17']++;buffer=[];__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['18']++;currentCode=0;}else{__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.b['3'][1]++;}}else{__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.b['2'][1]++;}__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['19']++;return{code:code,text:line};}else{__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.b['1'][1]++;__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['20']++;if((__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.b['5'][0]++,!buffer.length)&&(__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.b['5'][1]++,multiRes=RE_MULTI.exec(line))){__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.b['4'][0]++;__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['21']++;currentCode=parseInt(multiRes[1],10);}else{__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.b['4'][1]++;__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['22']++;if(buffer.length){__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.b['6'][0]++;}else{__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.b['6'][1]++;}}__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['23']++;buffer.push(line.toString());}};}__cov_Cbo7IaZZ3Ln4aLUw25DLOQ.s['24']++;module.exports=responseHandler; diff --git a/node_modules/jsftp/lib/lib/utils.js b/node_modules/jsftp/lib/lib/utils.js new file mode 100644 index 0000000..35ed10b --- /dev/null +++ b/node_modules/jsftp/lib/lib/utils.js @@ -0,0 +1,6 @@ +if (typeof __coverage__ === 'undefined') { __coverage__ = {}; } +if (!__coverage__['/Users/sergi/programming/jsftp/lib/utils.js']) { + __coverage__['/Users/sergi/programming/jsftp/lib/utils.js'] = {"path":"/Users/sergi/programming/jsftp/lib/utils.js","s":{"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0},"b":{"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0,0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0,0],"12":[0,0],"13":[0,0],"14":[0,0]},"f":{"1":0,"2":0,"3":0,"4":0,"5":0,"6":0},"fnMap":{"1":{"name":"(anonymous_1)","line":10,"loc":{"start":{"line":10,"column":10},"end":{"line":10,"column":25}}},"2":{"name":"(anonymous_2)","line":21,"loc":{"start":{"line":21,"column":14},"end":{"line":21,"column":32}}},"3":{"name":"(anonymous_3)","line":26,"loc":{"start":{"line":26,"column":35},"end":{"line":26,"column":57}}},"4":{"name":"_next","line":27,"loc":{"start":{"line":27,"column":6},"end":{"line":27,"column":23}}},"5":{"name":"(anonymous_5)","line":74,"loc":{"start":{"line":74,"column":15},"end":{"line":74,"column":30}}},"6":{"name":"(anonymous_6)","line":91,"loc":{"start":{"line":91,"column":12},"end":{"line":91,"column":26}}}},"statementMap":{"1":{"start":{"line":1,"column":0},"end":{"line":1,"column":38}},"2":{"start":{"line":2,"column":0},"end":{"line":2,"column":29}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":31}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":28}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":41}},"6":{"start":{"line":8,"column":0},"end":{"line":103,"column":0}},"7":{"start":{"line":11,"column":4},"end":{"line":11,"column":30}},"8":{"start":{"line":12,"column":4},"end":{"line":12,"column":36}},"9":{"start":{"line":22,"column":4},"end":{"line":22,"column":23}},"10":{"start":{"line":23,"column":4},"end":{"line":23,"column":14}},"11":{"start":{"line":24,"column":4},"end":{"line":24,"column":20}},"12":{"start":{"line":25,"column":4},"end":{"line":25,"column":48}},"13":{"start":{"line":26,"column":4},"end":{"line":69,"column":7}},"14":{"start":{"line":27,"column":6},"end":{"line":30,"column":7}},"15":{"start":{"line":28,"column":8},"end":{"line":28,"column":15}},"16":{"start":{"line":29,"column":8},"end":{"line":29,"column":15}},"17":{"start":{"line":34,"column":6},"end":{"line":35,"column":32}},"18":{"start":{"line":35,"column":8},"end":{"line":35,"column":32}},"19":{"start":{"line":37,"column":6},"end":{"line":37,"column":27}},"20":{"start":{"line":42,"column":6},"end":{"line":45,"column":7}},"21":{"start":{"line":44,"column":8},"end":{"line":44,"column":23}},"22":{"start":{"line":47,"column":6},"end":{"line":47,"column":45}},"23":{"start":{"line":48,"column":6},"end":{"line":67,"column":7}},"24":{"start":{"line":49,"column":8},"end":{"line":55,"column":9}},"25":{"start":{"line":50,"column":10},"end":{"line":50,"column":61}},"26":{"start":{"line":51,"column":10},"end":{"line":54,"column":11}},"27":{"start":{"line":52,"column":12},"end":{"line":52,"column":62}},"28":{"start":{"line":53,"column":12},"end":{"line":53,"column":27}},"29":{"start":{"line":57,"column":8},"end":{"line":62,"column":9}},"30":{"start":{"line":58,"column":10},"end":{"line":58,"column":61}},"31":{"start":{"line":59,"column":10},"end":{"line":61,"column":11}},"32":{"start":{"line":60,"column":12},"end":{"line":60,"column":42}},"33":{"start":{"line":65,"column":8},"end":{"line":67,"column":6}},"34":{"start":{"line":66,"column":10},"end":{"line":67,"column":6}},"35":{"start":{"line":68,"column":6},"end":{"line":68,"column":14}},"36":{"start":{"line":71,"column":4},"end":{"line":71,"column":18}},"37":{"start":{"line":75,"column":4},"end":{"line":75,"column":68}},"38":{"start":{"line":76,"column":4},"end":{"line":76,"column":35}},"39":{"start":{"line":77,"column":4},"end":{"line":77,"column":29}},"40":{"start":{"line":77,"column":16},"end":{"line":77,"column":29}},"41":{"start":{"line":80,"column":4},"end":{"line":81,"column":77}},"42":{"start":{"line":92,"column":4},"end":{"line":92,"column":24}},"43":{"start":{"line":92,"column":14},"end":{"line":92,"column":24}},"44":{"start":{"line":94,"column":4},"end":{"line":94,"column":31}},"45":{"start":{"line":95,"column":4},"end":{"line":96,"column":51}},"46":{"start":{"line":96,"column":6},"end":{"line":96,"column":51}},"47":{"start":{"line":98,"column":4},"end":{"line":98,"column":15}}},"branchMap":{"1":{"line":12,"type":"binary-expr","locations":[{"start":{"line":12,"column":11},"end":{"line":12,"column":21}},{"start":{"line":12,"column":25},"end":{"line":12,"column":35}}]},"2":{"line":34,"type":"if","locations":[{"start":{"line":34,"column":6},"end":{"line":34,"column":6}},{"start":{"line":34,"column":6},"end":{"line":34,"column":6}}]},"3":{"line":42,"type":"if","locations":[{"start":{"line":42,"column":6},"end":{"line":42,"column":6}},{"start":{"line":42,"column":6},"end":{"line":42,"column":6}}]},"4":{"line":42,"type":"binary-expr","locations":[{"start":{"line":42,"column":10},"end":{"line":42,"column":40}},{"start":{"line":43,"column":8},"end":{"line":43,"column":26}},{"start":{"line":43,"column":30},"end":{"line":43,"column":50}}]},"5":{"line":48,"type":"if","locations":[{"start":{"line":48,"column":6},"end":{"line":48,"column":6}},{"start":{"line":48,"column":6},"end":{"line":48,"column":6}}]},"6":{"line":49,"type":"if","locations":[{"start":{"line":49,"column":8},"end":{"line":49,"column":8}},{"start":{"line":49,"column":8},"end":{"line":49,"column":8}}]},"7":{"line":51,"type":"if","locations":[{"start":{"line":51,"column":10},"end":{"line":51,"column":10}},{"start":{"line":51,"column":10},"end":{"line":51,"column":10}}]},"8":{"line":57,"type":"if","locations":[{"start":{"line":57,"column":8},"end":{"line":57,"column":8}},{"start":{"line":57,"column":8},"end":{"line":57,"column":8}}]},"9":{"line":57,"type":"binary-expr","locations":[{"start":{"line":57,"column":12},"end":{"line":57,"column":31}},{"start":{"line":57,"column":35},"end":{"line":57,"column":52}}]},"10":{"line":59,"type":"if","locations":[{"start":{"line":59,"column":10},"end":{"line":59,"column":10}},{"start":{"line":59,"column":10},"end":{"line":59,"column":10}}]},"11":{"line":65,"type":"if","locations":[{"start":{"line":65,"column":8},"end":{"line":65,"column":8}},{"start":{"line":65,"column":8},"end":{"line":65,"column":8}}]},"12":{"line":77,"type":"if","locations":[{"start":{"line":77,"column":4},"end":{"line":77,"column":4}},{"start":{"line":77,"column":4},"end":{"line":77,"column":4}}]},"13":{"line":92,"type":"if","locations":[{"start":{"line":92,"column":4},"end":{"line":92,"column":4}},{"start":{"line":92,"column":4},"end":{"line":92,"column":4}}]},"14":{"line":95,"type":"if","locations":[{"start":{"line":95,"column":4},"end":{"line":95,"column":4}},{"start":{"line":95,"column":4},"end":{"line":95,"column":4}}]}}}; +} +var __cov_YktIlyn0AIPXiguqHg3XXw = __coverage__['/Users/sergi/programming/jsftp/lib/utils.js']; +__cov_YktIlyn0AIPXiguqHg3XXw.s['1']++;var Parser=require('parse-listing');__cov_YktIlyn0AIPXiguqHg3XXw.s['2']++;var async=require('async');__cov_YktIlyn0AIPXiguqHg3XXw.s['3']++;var RE_RES=/^(\d\d\d)\s(.*)/;__cov_YktIlyn0AIPXiguqHg3XXw.s['4']++;var RE_MULTI=/^(\d\d\d)-/;__cov_YktIlyn0AIPXiguqHg3XXw.s['5']++;var RE_SERVER_RESPONSE=/^(\d\d\d)(.*)/;__cov_YktIlyn0AIPXiguqHg3XXw.s['6']++;var Utils=module.exports={isMark:function(code){__cov_YktIlyn0AIPXiguqHg3XXw.f['1']++;__cov_YktIlyn0AIPXiguqHg3XXw.s['7']++;code=parseInt(code,10);__cov_YktIlyn0AIPXiguqHg3XXw.s['8']++;return(__cov_YktIlyn0AIPXiguqHg3XXw.b['1'][0]++,code>100)&&(__cov_YktIlyn0AIPXiguqHg3XXw.b['1'][1]++,code<200);},parseEntry:function(listing){__cov_YktIlyn0AIPXiguqHg3XXw.f['2']++;__cov_YktIlyn0AIPXiguqHg3XXw.s['9']++;var t,parsedEntry;__cov_YktIlyn0AIPXiguqHg3XXw.s['10']++;var i=0;__cov_YktIlyn0AIPXiguqHg3XXw.s['11']++;var parsed=[];__cov_YktIlyn0AIPXiguqHg3XXw.s['12']++;var splitEntries=listing.split(/\r\n|\n/);__cov_YktIlyn0AIPXiguqHg3XXw.s['13']++;async.eachSeries(splitEntries,function(entry,next){__cov_YktIlyn0AIPXiguqHg3XXw.f['3']++;__cov_YktIlyn0AIPXiguqHg3XXw.s['14']++;function _next(){__cov_YktIlyn0AIPXiguqHg3XXw.f['4']++;__cov_YktIlyn0AIPXiguqHg3XXw.s['15']++;i+=1;__cov_YktIlyn0AIPXiguqHg3XXw.s['16']++;next();}__cov_YktIlyn0AIPXiguqHg3XXw.s['17']++;if(RE_MULTI.test(entry)){__cov_YktIlyn0AIPXiguqHg3XXw.b['2'][0]++;__cov_YktIlyn0AIPXiguqHg3XXw.s['18']++;entry=entry.substr(3);}else{__cov_YktIlyn0AIPXiguqHg3XXw.b['2'][1]++;}__cov_YktIlyn0AIPXiguqHg3XXw.s['19']++;entry=entry.trim();__cov_YktIlyn0AIPXiguqHg3XXw.s['20']++;if((__cov_YktIlyn0AIPXiguqHg3XXw.b['4'][0]++,RE_SERVER_RESPONSE.test(entry))||(__cov_YktIlyn0AIPXiguqHg3XXw.b['4'][1]++,RE_RES.test(entry))||(__cov_YktIlyn0AIPXiguqHg3XXw.b['4'][2]++,RE_MULTI.test(entry))){__cov_YktIlyn0AIPXiguqHg3XXw.b['3'][0]++;__cov_YktIlyn0AIPXiguqHg3XXw.s['21']++;return _next();}else{__cov_YktIlyn0AIPXiguqHg3XXw.b['3'][1]++;}__cov_YktIlyn0AIPXiguqHg3XXw.s['22']++;parsedEntry=Parser.parseEntry(entry);__cov_YktIlyn0AIPXiguqHg3XXw.s['23']++;if(parsedEntry===null){__cov_YktIlyn0AIPXiguqHg3XXw.b['5'][0]++;__cov_YktIlyn0AIPXiguqHg3XXw.s['24']++;if(splitEntries[i+1]){__cov_YktIlyn0AIPXiguqHg3XXw.b['6'][0]++;__cov_YktIlyn0AIPXiguqHg3XXw.s['25']++;t=Parser.parseEntry(entry+splitEntries[i+1]);__cov_YktIlyn0AIPXiguqHg3XXw.s['26']++;if(t!==null){__cov_YktIlyn0AIPXiguqHg3XXw.b['7'][0]++;__cov_YktIlyn0AIPXiguqHg3XXw.s['27']++;splitEntries[i+1]=entry+splitEntries[i+1];__cov_YktIlyn0AIPXiguqHg3XXw.s['28']++;return _next();}else{__cov_YktIlyn0AIPXiguqHg3XXw.b['7'][1]++;}}else{__cov_YktIlyn0AIPXiguqHg3XXw.b['6'][1]++;}__cov_YktIlyn0AIPXiguqHg3XXw.s['29']++;if((__cov_YktIlyn0AIPXiguqHg3XXw.b['9'][0]++,splitEntries[i-1])&&(__cov_YktIlyn0AIPXiguqHg3XXw.b['9'][1]++,parsed.length>0)){__cov_YktIlyn0AIPXiguqHg3XXw.b['8'][0]++;__cov_YktIlyn0AIPXiguqHg3XXw.s['30']++;t=Parser.parseEntry(splitEntries[i-1]+entry);__cov_YktIlyn0AIPXiguqHg3XXw.s['31']++;if(t!==null){__cov_YktIlyn0AIPXiguqHg3XXw.b['10'][0]++;__cov_YktIlyn0AIPXiguqHg3XXw.s['32']++;parsed[parsed.length-1]=t;}else{__cov_YktIlyn0AIPXiguqHg3XXw.b['10'][1]++;}}else{__cov_YktIlyn0AIPXiguqHg3XXw.b['8'][1]++;}}else{__cov_YktIlyn0AIPXiguqHg3XXw.b['5'][1]++;__cov_YktIlyn0AIPXiguqHg3XXw.s['33']++;if(parsedEntry){__cov_YktIlyn0AIPXiguqHg3XXw.b['11'][0]++;__cov_YktIlyn0AIPXiguqHg3XXw.s['34']++;parsed.push(parsedEntry);}else{__cov_YktIlyn0AIPXiguqHg3XXw.b['11'][1]++;}}__cov_YktIlyn0AIPXiguqHg3XXw.s['35']++;_next();});__cov_YktIlyn0AIPXiguqHg3XXw.s['36']++;return parsed;},getPasvPort:function(text){__cov_YktIlyn0AIPXiguqHg3XXw.f['5']++;__cov_YktIlyn0AIPXiguqHg3XXw.s['37']++;var RE_PASV=/([-\d]+,[-\d]+,[-\d]+,[-\d]+),([-\d]+),([-\d]+)/;__cov_YktIlyn0AIPXiguqHg3XXw.s['38']++;var match=RE_PASV.exec(text);__cov_YktIlyn0AIPXiguqHg3XXw.s['39']++;if(!match){__cov_YktIlyn0AIPXiguqHg3XXw.b['12'][0]++;__cov_YktIlyn0AIPXiguqHg3XXw.s['40']++;return false;}else{__cov_YktIlyn0AIPXiguqHg3XXw.b['12'][1]++;}__cov_YktIlyn0AIPXiguqHg3XXw.s['41']++;return[match[1].replace(/,/g,'.'),(parseInt(match[2],10)&255)*256+(parseInt(match[3],10)&255)];},sanitize:function(cmd){__cov_YktIlyn0AIPXiguqHg3XXw.f['6']++;__cov_YktIlyn0AIPXiguqHg3XXw.s['42']++;if(!cmd){__cov_YktIlyn0AIPXiguqHg3XXw.b['13'][0]++;__cov_YktIlyn0AIPXiguqHg3XXw.s['43']++;return'';}else{__cov_YktIlyn0AIPXiguqHg3XXw.b['13'][1]++;}__cov_YktIlyn0AIPXiguqHg3XXw.s['44']++;var _cmd=cmd.slice(0,5);__cov_YktIlyn0AIPXiguqHg3XXw.s['45']++;if(_cmd==='pass '){__cov_YktIlyn0AIPXiguqHg3XXw.b['14'][0]++;__cov_YktIlyn0AIPXiguqHg3XXw.s['46']++;cmd=_cmd+Array(cmd.length-5).join('*');}else{__cov_YktIlyn0AIPXiguqHg3XXw.b['14'][1]++;}__cov_YktIlyn0AIPXiguqHg3XXw.s['47']++;return cmd;}}; diff --git a/node_modules/jsftp/lib/response.js b/node_modules/jsftp/lib/response.js new file mode 100644 index 0000000..7a5664d --- /dev/null +++ b/node_modules/jsftp/lib/response.js @@ -0,0 +1,45 @@ +"use strict"; + +var RE_RES = /^(\d\d\d)\s(.*)/; +var RE_MULTI = /^(\d\d\d)-/; + +/** + * Receives a stream of responses from the server and filters + * them before pushing them back into the stream. The filtering is + * necessary to detect multiline responses, in which several responses from + * the server belong to a single command. + */ +function responseHandler() { + var buffer = []; + var currentCode = 0; + + return function(line) { + var simpleRes = RE_RES.exec(line); + var multiRes; + + var code; + if (simpleRes) { + code = parseInt(simpleRes[1], 10); + + if (buffer.length) { + buffer.push(line); + + if (currentCode === code) { + line = buffer.join("\n"); + buffer = []; + currentCode = 0; + } + } + + return { code: code, text: line }; + } + else { + if (!buffer.length && (multiRes = RE_MULTI.exec(line))) { + currentCode = parseInt(multiRes[1], 10); + } + buffer.push(line.toString()); + } + } +} + +module.exports = responseHandler; diff --git a/node_modules/jsftp/lib/utils.js b/node_modules/jsftp/lib/utils.js new file mode 100644 index 0000000..d569e99 --- /dev/null +++ b/node_modules/jsftp/lib/utils.js @@ -0,0 +1,101 @@ +var Parser = require("parse-listing"); +var async = require("async"); + +var RE_RES = /^(\d\d\d)\s(.*)/; +var RE_MULTI = /^(\d\d\d)-/; +var RE_SERVER_RESPONSE = /^(\d\d\d)(.*)/; + +var Utils = module.exports = { + // Codes from 100 to 200 are FTP marks + isMark: function(code) { + code = parseInt(code, 10); + return code > 100 && code < 200; + }, + + /** + * Parse raw output of a file listing, trying in to clean up broken listings in + * the process + * @param {String} listing Raw file listing coming from a 'list' or 'stat' + * @returns {Object[]} + */ + parseEntry: function(listing) { + var t, parsedEntry; + var i = 0; + var parsed = []; + var splitEntries = listing.split(/\r\n|\n/); + async.eachSeries(splitEntries, function(entry, next) { + function _next() { + i += 1; + next(); + } + + // Some servers include an official code-multiline sign at the beginning of + // every string. We must strip it if that's the case. + if (RE_MULTI.test(entry)) + entry = entry.substr(3); + + entry = entry.trim(); + + // Filter file-listing results from 'STAT' command, since they include + // server responses before and after the file listing. + // Issue: https://github.com/sergi/jsftp/issues/3 + if (RE_SERVER_RESPONSE.test(entry) || + RE_RES.test(entry) || RE_MULTI.test(entry)) { + return _next(); + } + + parsedEntry = Parser.parseEntry(entry); + if (parsedEntry === null) { + if (splitEntries[i + 1]) { + t = Parser.parseEntry(entry + splitEntries[i + 1]); + if (t !== null) { + splitEntries[i + 1] = entry + splitEntries[i + 1]; + return _next(); + } + } + + if (splitEntries[i - 1] && parsed.length > 0) { + t = Parser.parseEntry(splitEntries[i - 1] + entry); + if (t !== null) { + parsed[parsed.length - 1] = t; + } + } + } + else { + if (parsedEntry) + parsed.push(parsedEntry) + } + _next(); + }); + + return parsed; + }, + + getPasvPort: function(text) { + var RE_PASV = /([-\d]+,[-\d]+,[-\d]+,[-\d]+),([-\d]+),([-\d]+)/; + var match = RE_PASV.exec(text); + if (!match) return false; + + // Array containing the passive host and the port number + return [match[1].replace(/,/g, "."), + (parseInt(match[2], 10) & 255) * 256 + (parseInt(match[3], 10) & 255)]; + }, + + /** + * Cleans up commands with potentially insecure data in them, such as + * passwords, personal info, etc. + * + * @param cmd {String} Command to be sanitized + * @returns {String} Sanitized command + */ + sanitize: function(cmd) { + if (!cmd) return ""; + + var _cmd = cmd.slice(0, 5); + if (_cmd === "pass ") + cmd = _cmd + Array(cmd.length - 5).join("*"); + + return cmd; + } +} + diff --git a/node_modules/jsftp/node_modules/async/LICENSE b/node_modules/jsftp/node_modules/async/LICENSE new file mode 100644 index 0000000..b7f9d50 --- /dev/null +++ b/node_modules/jsftp/node_modules/async/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010 Caolan McMahon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/jsftp/node_modules/async/README.md b/node_modules/jsftp/node_modules/async/README.md new file mode 100644 index 0000000..9ff1acf --- /dev/null +++ b/node_modules/jsftp/node_modules/async/README.md @@ -0,0 +1,1414 @@ +# Async.js + +Async is a utility module which provides straight-forward, powerful functions +for working with asynchronous JavaScript. Although originally designed for +use with [node.js](http://nodejs.org), it can also be used directly in the +browser. Also supports [component](https://github.com/component/component). + +Async provides around 20 functions that include the usual 'functional' +suspects (map, reduce, filter, each…) as well as some common patterns +for asynchronous control flow (parallel, series, waterfall…). All these +functions assume you follow the node.js convention of providing a single +callback as the last argument of your async function. + + +## Quick Examples + +```javascript +async.map(['file1','file2','file3'], fs.stat, function(err, results){ + // results is now an array of stats for each file +}); + +async.filter(['file1','file2','file3'], fs.exists, function(results){ + // results now equals an array of the existing files +}); + +async.parallel([ + function(){ ... }, + function(){ ... } +], callback); + +async.series([ + function(){ ... }, + function(){ ... } +]); +``` + +There are many more functions available so take a look at the docs below for a +full list. This module aims to be comprehensive, so if you feel anything is +missing please create a GitHub issue for it. + +## Common Pitfalls + +### Binding a context to an iterator + +This section is really about bind, not about async. If you are wondering how to +make async execute your iterators in a given context, or are confused as to why +a method of another library isn't working as an iterator, study this example: + +```js +// Here is a simple object with an (unnecessarily roundabout) squaring method +var AsyncSquaringLibrary = { + squareExponent: 2, + square: function(number, callback){ + var result = Math.pow(number, this.squareExponent); + setTimeout(function(){ + callback(null, result); + }, 200); + } +}; + +async.map([1, 2, 3], AsyncSquaringLibrary.square, function(err, result){ + // result is [NaN, NaN, NaN] + // This fails because the `this.squareExponent` expression in the square + // function is not evaluated in the context of AsyncSquaringLibrary, and is + // therefore undefined. +}); + +async.map([1, 2, 3], AsyncSquaringLibrary.square.bind(AsyncSquaringLibrary), function(err, result){ + // result is [1, 4, 9] + // With the help of bind we can attach a context to the iterator before + // passing it to async. Now the square function will be executed in its + // 'home' AsyncSquaringLibrary context and the value of `this.squareExponent` + // will be as expected. +}); +``` + +## Download + +The source is available for download from +[GitHub](http://github.com/caolan/async). +Alternatively, you can install using Node Package Manager (npm): + + npm install async + +__Development:__ [async.js](https://github.com/caolan/async/raw/master/lib/async.js) - 29.6kb Uncompressed + +## In the Browser + +So far it's been tested in IE6, IE7, IE8, FF3.6 and Chrome 5. Usage: + +```html + + +``` + +## Documentation + +### Collections + +* [each](#each) +* [map](#map) +* [filter](#filter) +* [reject](#reject) +* [reduce](#reduce) +* [detect](#detect) +* [sortBy](#sortBy) +* [some](#some) +* [every](#every) +* [concat](#concat) + +### Control Flow + +* [series](#series) +* [parallel](#parallel) +* [whilst](#whilst) +* [doWhilst](#doWhilst) +* [until](#until) +* [doUntil](#doUntil) +* [forever](#forever) +* [waterfall](#waterfall) +* [compose](#compose) +* [applyEach](#applyEach) +* [queue](#queue) +* [cargo](#cargo) +* [auto](#auto) +* [iterator](#iterator) +* [apply](#apply) +* [nextTick](#nextTick) +* [times](#times) +* [timesSeries](#timesSeries) + +### Utils + +* [memoize](#memoize) +* [unmemoize](#unmemoize) +* [log](#log) +* [dir](#dir) +* [noConflict](#noConflict) + + +## Collections + + + +### each(arr, iterator, callback) + +Applies an iterator function to each item in an array, in parallel. +The iterator is called with an item from the list and a callback for when it +has finished. If the iterator passes an error to this callback, the main +callback for the each function is immediately called with the error. + +Note, that since this function applies the iterator to each item in parallel +there is no guarantee that the iterator functions will complete in order. + +__Arguments__ + +* arr - An array to iterate over. +* iterator(item, callback) - A function to apply to each item in the array. + The iterator is passed a callback(err) which must be called once it has + completed. If no error has occured, the callback should be run without + arguments or with an explicit null argument. +* callback(err) - A callback which is called after all the iterator functions + have finished, or an error has occurred. + +__Example__ + +```js +// assuming openFiles is an array of file names and saveFile is a function +// to save the modified contents of that file: + +async.each(openFiles, saveFile, function(err){ + // if any of the saves produced an error, err would equal that error +}); +``` + +--------------------------------------- + + + +### eachSeries(arr, iterator, callback) + +The same as each only the iterator is applied to each item in the array in +series. The next iterator is only called once the current one has completed +processing. This means the iterator functions will complete in order. + + +--------------------------------------- + + + +### eachLimit(arr, limit, iterator, callback) + +The same as each only no more than "limit" iterators will be simultaneously +running at any time. + +Note that the items are not processed in batches, so there is no guarantee that + the first "limit" iterator functions will complete before any others are +started. + +__Arguments__ + +* arr - An array to iterate over. +* limit - The maximum number of iterators to run at any time. +* iterator(item, callback) - A function to apply to each item in the array. + The iterator is passed a callback(err) which must be called once it has + completed. If no error has occured, the callback should be run without + arguments or with an explicit null argument. +* callback(err) - A callback which is called after all the iterator functions + have finished, or an error has occurred. + +__Example__ + +```js +// Assume documents is an array of JSON objects and requestApi is a +// function that interacts with a rate-limited REST api. + +async.eachLimit(documents, 20, requestApi, function(err){ + // if any of the saves produced an error, err would equal that error +}); +``` + +--------------------------------------- + + +### map(arr, iterator, callback) + +Produces a new array of values by mapping each value in the given array through +the iterator function. The iterator is called with an item from the array and a +callback for when it has finished processing. The callback takes 2 arguments, +an error and the transformed item from the array. If the iterator passes an +error to this callback, the main callback for the map function is immediately +called with the error. + +Note, that since this function applies the iterator to each item in parallel +there is no guarantee that the iterator functions will complete in order, however +the results array will be in the same order as the original array. + +__Arguments__ + +* arr - An array to iterate over. +* iterator(item, callback) - A function to apply to each item in the array. + The iterator is passed a callback(err, transformed) which must be called once + it has completed with an error (which can be null) and a transformed item. +* callback(err, results) - A callback which is called after all the iterator + functions have finished, or an error has occurred. Results is an array of the + transformed items from the original array. + +__Example__ + +```js +async.map(['file1','file2','file3'], fs.stat, function(err, results){ + // results is now an array of stats for each file +}); +``` + +--------------------------------------- + + +### mapSeries(arr, iterator, callback) + +The same as map only the iterator is applied to each item in the array in +series. The next iterator is only called once the current one has completed +processing. The results array will be in the same order as the original. + + +--------------------------------------- + + +### mapLimit(arr, limit, iterator, callback) + +The same as map only no more than "limit" iterators will be simultaneously +running at any time. + +Note that the items are not processed in batches, so there is no guarantee that + the first "limit" iterator functions will complete before any others are +started. + +__Arguments__ + +* arr - An array to iterate over. +* limit - The maximum number of iterators to run at any time. +* iterator(item, callback) - A function to apply to each item in the array. + The iterator is passed a callback(err, transformed) which must be called once + it has completed with an error (which can be null) and a transformed item. +* callback(err, results) - A callback which is called after all the iterator + functions have finished, or an error has occurred. Results is an array of the + transformed items from the original array. + +__Example__ + +```js +async.map(['file1','file2','file3'], 1, fs.stat, function(err, results){ + // results is now an array of stats for each file +}); +``` + +--------------------------------------- + + +### filter(arr, iterator, callback) + +__Alias:__ select + +Returns a new array of all the values which pass an async truth test. +_The callback for each iterator call only accepts a single argument of true or +false, it does not accept an error argument first!_ This is in-line with the +way node libraries work with truth tests like fs.exists. This operation is +performed in parallel, but the results array will be in the same order as the +original. + +__Arguments__ + +* arr - An array to iterate over. +* iterator(item, callback) - A truth test to apply to each item in the array. + The iterator is passed a callback(truthValue) which must be called with a + boolean argument once it has completed. +* callback(results) - A callback which is called after all the iterator + functions have finished. + +__Example__ + +```js +async.filter(['file1','file2','file3'], fs.exists, function(results){ + // results now equals an array of the existing files +}); +``` + +--------------------------------------- + + +### filterSeries(arr, iterator, callback) + +__alias:__ selectSeries + +The same as filter only the iterator is applied to each item in the array in +series. The next iterator is only called once the current one has completed +processing. The results array will be in the same order as the original. + +--------------------------------------- + + +### reject(arr, iterator, callback) + +The opposite of filter. Removes values that pass an async truth test. + +--------------------------------------- + + +### rejectSeries(arr, iterator, callback) + +The same as reject, only the iterator is applied to each item in the array +in series. + + +--------------------------------------- + + +### reduce(arr, memo, iterator, callback) + +__aliases:__ inject, foldl + +Reduces a list of values into a single value using an async iterator to return +each successive step. Memo is the initial state of the reduction. This +function only operates in series. For performance reasons, it may make sense to +split a call to this function into a parallel map, then use the normal +Array.prototype.reduce on the results. This function is for situations where +each step in the reduction needs to be async, if you can get the data before +reducing it then it's probably a good idea to do so. + +__Arguments__ + +* arr - An array to iterate over. +* memo - The initial state of the reduction. +* iterator(memo, item, callback) - A function applied to each item in the + array to produce the next step in the reduction. The iterator is passed a + callback(err, reduction) which accepts an optional error as its first + argument, and the state of the reduction as the second. If an error is + passed to the callback, the reduction is stopped and the main callback is + immediately called with the error. +* callback(err, result) - A callback which is called after all the iterator + functions have finished. Result is the reduced value. + +__Example__ + +```js +async.reduce([1,2,3], 0, function(memo, item, callback){ + // pointless async: + process.nextTick(function(){ + callback(null, memo + item) + }); +}, function(err, result){ + // result is now equal to the last value of memo, which is 6 +}); +``` + +--------------------------------------- + + +### reduceRight(arr, memo, iterator, callback) + +__Alias:__ foldr + +Same as reduce, only operates on the items in the array in reverse order. + + +--------------------------------------- + + +### detect(arr, iterator, callback) + +Returns the first value in a list that passes an async truth test. The +iterator is applied in parallel, meaning the first iterator to return true will +fire the detect callback with that result. That means the result might not be +the first item in the original array (in terms of order) that passes the test. + +If order within the original array is important then look at detectSeries. + +__Arguments__ + +* arr - An array to iterate over. +* iterator(item, callback) - A truth test to apply to each item in the array. + The iterator is passed a callback(truthValue) which must be called with a + boolean argument once it has completed. +* callback(result) - A callback which is called as soon as any iterator returns + true, or after all the iterator functions have finished. Result will be + the first item in the array that passes the truth test (iterator) or the + value undefined if none passed. + +__Example__ + +```js +async.detect(['file1','file2','file3'], fs.exists, function(result){ + // result now equals the first file in the list that exists +}); +``` + +--------------------------------------- + + +### detectSeries(arr, iterator, callback) + +The same as detect, only the iterator is applied to each item in the array +in series. This means the result is always the first in the original array (in +terms of array order) that passes the truth test. + + +--------------------------------------- + + +### sortBy(arr, iterator, callback) + +Sorts a list by the results of running each value through an async iterator. + +__Arguments__ + +* arr - An array to iterate over. +* iterator(item, callback) - A function to apply to each item in the array. + The iterator is passed a callback(err, sortValue) which must be called once it + has completed with an error (which can be null) and a value to use as the sort + criteria. +* callback(err, results) - A callback which is called after all the iterator + functions have finished, or an error has occurred. Results is the items from + the original array sorted by the values returned by the iterator calls. + +__Example__ + +```js +async.sortBy(['file1','file2','file3'], function(file, callback){ + fs.stat(file, function(err, stats){ + callback(err, stats.mtime); + }); +}, function(err, results){ + // results is now the original array of files sorted by + // modified date +}); +``` + +--------------------------------------- + + +### some(arr, iterator, callback) + +__Alias:__ any + +Returns true if at least one element in the array satisfies an async test. +_The callback for each iterator call only accepts a single argument of true or +false, it does not accept an error argument first!_ This is in-line with the +way node libraries work with truth tests like fs.exists. Once any iterator +call returns true, the main callback is immediately called. + +__Arguments__ + +* arr - An array to iterate over. +* iterator(item, callback) - A truth test to apply to each item in the array. + The iterator is passed a callback(truthValue) which must be called with a + boolean argument once it has completed. +* callback(result) - A callback which is called as soon as any iterator returns + true, or after all the iterator functions have finished. Result will be + either true or false depending on the values of the async tests. + +__Example__ + +```js +async.some(['file1','file2','file3'], fs.exists, function(result){ + // if result is true then at least one of the files exists +}); +``` + +--------------------------------------- + + +### every(arr, iterator, callback) + +__Alias:__ all + +Returns true if every element in the array satisfies an async test. +_The callback for each iterator call only accepts a single argument of true or +false, it does not accept an error argument first!_ This is in-line with the +way node libraries work with truth tests like fs.exists. + +__Arguments__ + +* arr - An array to iterate over. +* iterator(item, callback) - A truth test to apply to each item in the array. + The iterator is passed a callback(truthValue) which must be called with a + boolean argument once it has completed. +* callback(result) - A callback which is called after all the iterator + functions have finished. Result will be either true or false depending on + the values of the async tests. + +__Example__ + +```js +async.every(['file1','file2','file3'], fs.exists, function(result){ + // if result is true then every file exists +}); +``` + +--------------------------------------- + + +### concat(arr, iterator, callback) + +Applies an iterator to each item in a list, concatenating the results. Returns the +concatenated list. The iterators are called in parallel, and the results are +concatenated as they return. There is no guarantee that the results array will +be returned in the original order of the arguments passed to the iterator function. + +__Arguments__ + +* arr - An array to iterate over +* iterator(item, callback) - A function to apply to each item in the array. + The iterator is passed a callback(err, results) which must be called once it + has completed with an error (which can be null) and an array of results. +* callback(err, results) - A callback which is called after all the iterator + functions have finished, or an error has occurred. Results is an array containing + the concatenated results of the iterator function. + +__Example__ + +```js +async.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files){ + // files is now a list of filenames that exist in the 3 directories +}); +``` + +--------------------------------------- + + +### concatSeries(arr, iterator, callback) + +Same as async.concat, but executes in series instead of parallel. + + +## Control Flow + + +### series(tasks, [callback]) + +Run an array of functions in series, each one running once the previous +function has completed. If any functions in the series pass an error to its +callback, no more functions are run and the callback for the series is +immediately called with the value of the error. Once the tasks have completed, +the results are passed to the final callback as an array. + +It is also possible to use an object instead of an array. Each property will be +run as a function and the results will be passed to the final callback as an object +instead of an array. This can be a more readable way of handling results from +async.series. + + +__Arguments__ + +* tasks - An array or object containing functions to run, each function is passed + a callback(err, result) it must call on completion with an error (which can + be null) and an optional result value. +* callback(err, results) - An optional callback to run once all the functions + have completed. This function gets a results array (or object) containing all + the result arguments passed to the task callbacks. + +__Example__ + +```js +async.series([ + function(callback){ + // do some stuff ... + callback(null, 'one'); + }, + function(callback){ + // do some more stuff ... + callback(null, 'two'); + } +], +// optional callback +function(err, results){ + // results is now equal to ['one', 'two'] +}); + + +// an example using an object instead of an array +async.series({ + one: function(callback){ + setTimeout(function(){ + callback(null, 1); + }, 200); + }, + two: function(callback){ + setTimeout(function(){ + callback(null, 2); + }, 100); + } +}, +function(err, results) { + // results is now equal to: {one: 1, two: 2} +}); +``` + +--------------------------------------- + + +### parallel(tasks, [callback]) + +Run an array of functions in parallel, without waiting until the previous +function has completed. If any of the functions pass an error to its +callback, the main callback is immediately called with the value of the error. +Once the tasks have completed, the results are passed to the final callback as an +array. + +It is also possible to use an object instead of an array. Each property will be +run as a function and the results will be passed to the final callback as an object +instead of an array. This can be a more readable way of handling results from +async.parallel. + + +__Arguments__ + +* tasks - An array or object containing functions to run, each function is passed + a callback(err, result) it must call on completion with an error (which can + be null) and an optional result value. +* callback(err, results) - An optional callback to run once all the functions + have completed. This function gets a results array (or object) containing all + the result arguments passed to the task callbacks. + +__Example__ + +```js +async.parallel([ + function(callback){ + setTimeout(function(){ + callback(null, 'one'); + }, 200); + }, + function(callback){ + setTimeout(function(){ + callback(null, 'two'); + }, 100); + } +], +// optional callback +function(err, results){ + // the results array will equal ['one','two'] even though + // the second function had a shorter timeout. +}); + + +// an example using an object instead of an array +async.parallel({ + one: function(callback){ + setTimeout(function(){ + callback(null, 1); + }, 200); + }, + two: function(callback){ + setTimeout(function(){ + callback(null, 2); + }, 100); + } +}, +function(err, results) { + // results is now equals to: {one: 1, two: 2} +}); +``` + +--------------------------------------- + + +### parallelLimit(tasks, limit, [callback]) + +The same as parallel only the tasks are executed in parallel with a maximum of "limit" +tasks executing at any time. + +Note that the tasks are not executed in batches, so there is no guarantee that +the first "limit" tasks will complete before any others are started. + +__Arguments__ + +* tasks - An array or object containing functions to run, each function is passed + a callback(err, result) it must call on completion with an error (which can + be null) and an optional result value. +* limit - The maximum number of tasks to run at any time. +* callback(err, results) - An optional callback to run once all the functions + have completed. This function gets a results array (or object) containing all + the result arguments passed to the task callbacks. + +--------------------------------------- + + +### whilst(test, fn, callback) + +Repeatedly call fn, while test returns true. Calls the callback when stopped, +or an error occurs. + +__Arguments__ + +* test() - synchronous truth test to perform before each execution of fn. +* fn(callback) - A function to call each time the test passes. The function is + passed a callback(err) which must be called once it has completed with an + optional error argument. +* callback(err) - A callback which is called after the test fails and repeated + execution of fn has stopped. + +__Example__ + +```js +var count = 0; + +async.whilst( + function () { return count < 5; }, + function (callback) { + count++; + setTimeout(callback, 1000); + }, + function (err) { + // 5 seconds have passed + } +); +``` + +--------------------------------------- + + +### doWhilst(fn, test, callback) + +The post check version of whilst. To reflect the difference in the order of operations `test` and `fn` arguments are switched. `doWhilst` is to `whilst` as `do while` is to `while` in plain JavaScript. + +--------------------------------------- + + +### until(test, fn, callback) + +Repeatedly call fn, until test returns true. Calls the callback when stopped, +or an error occurs. + +The inverse of async.whilst. + +--------------------------------------- + + +### doUntil(fn, test, callback) + +Like doWhilst except the test is inverted. Note the argument ordering differs from `until`. + +--------------------------------------- + + +### forever(fn, callback) + +Calls the asynchronous function 'fn' repeatedly, in series, indefinitely. +If an error is passed to fn's callback then 'callback' is called with the +error, otherwise it will never be called. + +--------------------------------------- + + +### waterfall(tasks, [callback]) + +Runs an array of functions in series, each passing their results to the next in +the array. However, if any of the functions pass an error to the callback, the +next function is not executed and the main callback is immediately called with +the error. + +__Arguments__ + +* tasks - An array of functions to run, each function is passed a + callback(err, result1, result2, ...) it must call on completion. The first + argument is an error (which can be null) and any further arguments will be + passed as arguments in order to the next task. +* callback(err, [results]) - An optional callback to run once all the functions + have completed. This will be passed the results of the last task's callback. + + + +__Example__ + +```js +async.waterfall([ + function(callback){ + callback(null, 'one', 'two'); + }, + function(arg1, arg2, callback){ + callback(null, 'three'); + }, + function(arg1, callback){ + // arg1 now equals 'three' + callback(null, 'done'); + } +], function (err, result) { + // result now equals 'done' +}); +``` + +--------------------------------------- + +### compose(fn1, fn2...) + +Creates a function which is a composition of the passed asynchronous +functions. Each function consumes the return value of the function that +follows. Composing functions f(), g() and h() would produce the result of +f(g(h())), only this version uses callbacks to obtain the return values. + +Each function is executed with the `this` binding of the composed function. + +__Arguments__ + +* functions... - the asynchronous functions to compose + + +__Example__ + +```js +function add1(n, callback) { + setTimeout(function () { + callback(null, n + 1); + }, 10); +} + +function mul3(n, callback) { + setTimeout(function () { + callback(null, n * 3); + }, 10); +} + +var add1mul3 = async.compose(mul3, add1); + +add1mul3(4, function (err, result) { + // result now equals 15 +}); +``` + +--------------------------------------- + +### applyEach(fns, args..., callback) + +Applies the provided arguments to each function in the array, calling the +callback after all functions have completed. If you only provide the first +argument then it will return a function which lets you pass in the +arguments as if it were a single function call. + +__Arguments__ + +* fns - the asynchronous functions to all call with the same arguments +* args... - any number of separate arguments to pass to the function +* callback - the final argument should be the callback, called when all + functions have completed processing + + +__Example__ + +```js +async.applyEach([enableSearch, updateSchema], 'bucket', callback); + +// partial application example: +async.each( + buckets, + async.applyEach([enableSearch, updateSchema]), + callback +); +``` + +--------------------------------------- + + +### applyEachSeries(arr, iterator, callback) + +The same as applyEach only the functions are applied in series. + +--------------------------------------- + + +### queue(worker, concurrency) + +Creates a queue object with the specified concurrency. Tasks added to the +queue will be processed in parallel (up to the concurrency limit). If all +workers are in progress, the task is queued until one is available. Once +a worker has completed a task, the task's callback is called. + +__Arguments__ + +* worker(task, callback) - An asynchronous function for processing a queued + task, which must call its callback(err) argument when finished, with an + optional error as an argument. +* concurrency - An integer for determining how many worker functions should be + run in parallel. + +__Queue objects__ + +The queue object returned by this function has the following properties and +methods: + +* length() - a function returning the number of items waiting to be processed. +* concurrency - an integer for determining how many worker functions should be + run in parallel. This property can be changed after a queue is created to + alter the concurrency on-the-fly. +* push(task, [callback]) - add a new task to the queue, the callback is called + once the worker has finished processing the task. + instead of a single task, an array of tasks can be submitted. the respective callback is used for every task in the list. +* unshift(task, [callback]) - add a new task to the front of the queue. +* saturated - a callback that is called when the queue length hits the concurrency and further tasks will be queued +* empty - a callback that is called when the last item from the queue is given to a worker +* drain - a callback that is called when the last item from the queue has returned from the worker + +__Example__ + +```js +// create a queue object with concurrency 2 + +var q = async.queue(function (task, callback) { + console.log('hello ' + task.name); + callback(); +}, 2); + + +// assign a callback +q.drain = function() { + console.log('all items have been processed'); +} + +// add some items to the queue + +q.push({name: 'foo'}, function (err) { + console.log('finished processing foo'); +}); +q.push({name: 'bar'}, function (err) { + console.log('finished processing bar'); +}); + +// add some items to the queue (batch-wise) + +q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function (err) { + console.log('finished processing bar'); +}); + +// add some items to the front of the queue + +q.unshift({name: 'bar'}, function (err) { + console.log('finished processing bar'); +}); +``` + +--------------------------------------- + + +### cargo(worker, [payload]) + +Creates a cargo object with the specified payload. Tasks added to the +cargo will be processed altogether (up to the payload limit). If the +worker is in progress, the task is queued until it is available. Once +the worker has completed some tasks, each callback of those tasks is called. + +__Arguments__ + +* worker(tasks, callback) - An asynchronous function for processing an array of + queued tasks, which must call its callback(err) argument when finished, with + an optional error as an argument. +* payload - An optional integer for determining how many tasks should be + processed per round; if omitted, the default is unlimited. + +__Cargo objects__ + +The cargo object returned by this function has the following properties and +methods: + +* length() - a function returning the number of items waiting to be processed. +* payload - an integer for determining how many tasks should be + process per round. This property can be changed after a cargo is created to + alter the payload on-the-fly. +* push(task, [callback]) - add a new task to the queue, the callback is called + once the worker has finished processing the task. + instead of a single task, an array of tasks can be submitted. the respective callback is used for every task in the list. +* saturated - a callback that is called when the queue length hits the concurrency and further tasks will be queued +* empty - a callback that is called when the last item from the queue is given to a worker +* drain - a callback that is called when the last item from the queue has returned from the worker + +__Example__ + +```js +// create a cargo object with payload 2 + +var cargo = async.cargo(function (tasks, callback) { + for(var i=0; i +### auto(tasks, [callback]) + +Determines the best order for running functions based on their requirements. +Each function can optionally depend on other functions being completed first, +and each function is run as soon as its requirements are satisfied. If any of +the functions pass an error to their callback, that function will not complete +(so any other functions depending on it will not run) and the main callback +will be called immediately with the error. Functions also receive an object +containing the results of functions which have completed so far. + +Note, all functions are called with a results object as a second argument, +so it is unsafe to pass functions in the tasks object which cannot handle the +extra argument. For example, this snippet of code: + +```js +async.auto({ + readData: async.apply(fs.readFile, 'data.txt', 'utf-8'); +}, callback); +``` + +will have the effect of calling readFile with the results object as the last +argument, which will fail: + +```js +fs.readFile('data.txt', 'utf-8', cb, {}); +``` + +Instead, wrap the call to readFile in a function which does not forward the +results object: + +```js +async.auto({ + readData: function(cb, results){ + fs.readFile('data.txt', 'utf-8', cb); + } +}, callback); +``` + +__Arguments__ + +* tasks - An object literal containing named functions or an array of + requirements, with the function itself the last item in the array. The key + used for each function or array is used when specifying requirements. The + function receives two arguments: (1) a callback(err, result) which must be + called when finished, passing an error (which can be null) and the result of + the function's execution, and (2) a results object, containing the results of + the previously executed functions. +* callback(err, results) - An optional callback which is called when all the + tasks have been completed. The callback will receive an error as an argument + if any tasks pass an error to their callback. Results will always be passed + but if an error occurred, no other tasks will be performed, and the results + object will only contain partial results. + + +__Example__ + +```js +async.auto({ + get_data: function(callback){ + // async code to get some data + }, + make_folder: function(callback){ + // async code to create a directory to store a file in + // this is run at the same time as getting the data + }, + write_file: ['get_data', 'make_folder', function(callback){ + // once there is some data and the directory exists, + // write the data to a file in the directory + callback(null, filename); + }], + email_link: ['write_file', function(callback, results){ + // once the file is written let's email a link to it... + // results.write_file contains the filename returned by write_file. + }] +}); +``` + +This is a fairly trivial example, but to do this using the basic parallel and +series functions would look like this: + +```js +async.parallel([ + function(callback){ + // async code to get some data + }, + function(callback){ + // async code to create a directory to store a file in + // this is run at the same time as getting the data + } +], +function(err, results){ + async.series([ + function(callback){ + // once there is some data and the directory exists, + // write the data to a file in the directory + }, + function(callback){ + // once the file is written let's email a link to it... + } + ]); +}); +``` + +For a complicated series of async tasks using the auto function makes adding +new tasks much easier and makes the code more readable. + + +--------------------------------------- + + +### iterator(tasks) + +Creates an iterator function which calls the next function in the array, +returning a continuation to call the next one after that. It's also possible to +'peek' the next iterator by doing iterator.next(). + +This function is used internally by the async module but can be useful when +you want to manually control the flow of functions in series. + +__Arguments__ + +* tasks - An array of functions to run. + +__Example__ + +```js +var iterator = async.iterator([ + function(){ sys.p('one'); }, + function(){ sys.p('two'); }, + function(){ sys.p('three'); } +]); + +node> var iterator2 = iterator(); +'one' +node> var iterator3 = iterator2(); +'two' +node> iterator3(); +'three' +node> var nextfn = iterator2.next(); +node> nextfn(); +'three' +``` + +--------------------------------------- + + +### apply(function, arguments..) + +Creates a continuation function with some arguments already applied, a useful +shorthand when combined with other control flow functions. Any arguments +passed to the returned function are added to the arguments originally passed +to apply. + +__Arguments__ + +* function - The function you want to eventually apply all arguments to. +* arguments... - Any number of arguments to automatically apply when the + continuation is called. + +__Example__ + +```js +// using apply + +async.parallel([ + async.apply(fs.writeFile, 'testfile1', 'test1'), + async.apply(fs.writeFile, 'testfile2', 'test2'), +]); + + +// the same process without using apply + +async.parallel([ + function(callback){ + fs.writeFile('testfile1', 'test1', callback); + }, + function(callback){ + fs.writeFile('testfile2', 'test2', callback); + } +]); +``` + +It's possible to pass any number of additional arguments when calling the +continuation: + +```js +node> var fn = async.apply(sys.puts, 'one'); +node> fn('two', 'three'); +one +two +three +``` + +--------------------------------------- + + +### nextTick(callback) + +Calls the callback on a later loop around the event loop. In node.js this just +calls process.nextTick, in the browser it falls back to setImmediate(callback) +if available, otherwise setTimeout(callback, 0), which means other higher priority +events may precede the execution of the callback. + +This is used internally for browser-compatibility purposes. + +__Arguments__ + +* callback - The function to call on a later loop around the event loop. + +__Example__ + +```js +var call_order = []; +async.nextTick(function(){ + call_order.push('two'); + // call_order now equals ['one','two'] +}); +call_order.push('one') +``` + + +### times(n, callback) + +Calls the callback n times and accumulates results in the same manner +you would use with async.map. + +__Arguments__ + +* n - The number of times to run the function. +* callback - The function to call n times. + +__Example__ + +```js +// Pretend this is some complicated async factory +var createUser = function(id, callback) { + callback(null, { + id: 'user' + id + }) +} +// generate 5 users +async.times(5, function(n, next){ + createUser(n, function(err, user) { + next(err, user) + }) +}, function(err, users) { + // we should now have 5 users +}); +``` + + +### timesSeries(n, callback) + +The same as times only the iterator is applied to each item in the array in +series. The next iterator is only called once the current one has completed +processing. The results array will be in the same order as the original. + + +## Utils + + +### memoize(fn, [hasher]) + +Caches the results of an async function. When creating a hash to store function +results against, the callback is omitted from the hash and an optional hash +function can be used. + +The cache of results is exposed as the `memo` property of the function returned +by `memoize`. + +__Arguments__ + +* fn - the function you to proxy and cache results from. +* hasher - an optional function for generating a custom hash for storing + results, it has all the arguments applied to it apart from the callback, and + must be synchronous. + +__Example__ + +```js +var slow_fn = function (name, callback) { + // do something + callback(null, result); +}; +var fn = async.memoize(slow_fn); + +// fn can now be used as if it were slow_fn +fn('some name', function () { + // callback +}); +``` + + +### unmemoize(fn) + +Undoes a memoized function, reverting it to the original, unmemoized +form. Comes handy in tests. + +__Arguments__ + +* fn - the memoized function + + +### log(function, arguments) + +Logs the result of an async function to the console. Only works in node.js or +in browsers that support console.log and console.error (such as FF and Chrome). +If multiple arguments are returned from the async function, console.log is +called on each argument in order. + +__Arguments__ + +* function - The function you want to eventually apply all arguments to. +* arguments... - Any number of arguments to apply to the function. + +__Example__ + +```js +var hello = function(name, callback){ + setTimeout(function(){ + callback(null, 'hello ' + name); + }, 1000); +}; +``` +```js +node> async.log(hello, 'world'); +'hello world' +``` + +--------------------------------------- + + +### dir(function, arguments) + +Logs the result of an async function to the console using console.dir to +display the properties of the resulting object. Only works in node.js or +in browsers that support console.dir and console.error (such as FF and Chrome). +If multiple arguments are returned from the async function, console.dir is +called on each argument in order. + +__Arguments__ + +* function - The function you want to eventually apply all arguments to. +* arguments... - Any number of arguments to apply to the function. + +__Example__ + +```js +var hello = function(name, callback){ + setTimeout(function(){ + callback(null, {hello: name}); + }, 1000); +}; +``` +```js +node> async.dir(hello, 'world'); +{hello: 'world'} +``` + +--------------------------------------- + + +### noConflict() + +Changes the value of async back to its original value, returning a reference to the +async object. diff --git a/node_modules/jsftp/node_modules/async/component.json b/node_modules/jsftp/node_modules/async/component.json new file mode 100644 index 0000000..bbb0115 --- /dev/null +++ b/node_modules/jsftp/node_modules/async/component.json @@ -0,0 +1,11 @@ +{ + "name": "async", + "repo": "caolan/async", + "description": "Higher-order functions and common patterns for asynchronous code", + "version": "0.1.23", + "keywords": [], + "dependencies": {}, + "development": {}, + "main": "lib/async.js", + "scripts": [ "lib/async.js" ] +} diff --git a/node_modules/jsftp/node_modules/async/lib/async.js b/node_modules/jsftp/node_modules/async/lib/async.js new file mode 100755 index 0000000..cb6320d --- /dev/null +++ b/node_modules/jsftp/node_modules/async/lib/async.js @@ -0,0 +1,955 @@ +/*global setImmediate: false, setTimeout: false, console: false */ +(function () { + + var async = {}; + + // global on the server, window in the browser + var root, previous_async; + + root = this; + if (root != null) { + previous_async = root.async; + } + + async.noConflict = function () { + root.async = previous_async; + return async; + }; + + function only_once(fn) { + var called = false; + return function() { + if (called) throw new Error("Callback was already called."); + called = true; + fn.apply(root, arguments); + } + } + + //// cross-browser compatiblity functions //// + + var _each = function (arr, iterator) { + if (arr.forEach) { + return arr.forEach(iterator); + } + for (var i = 0; i < arr.length; i += 1) { + iterator(arr[i], i, arr); + } + }; + + var _map = function (arr, iterator) { + if (arr.map) { + return arr.map(iterator); + } + var results = []; + _each(arr, function (x, i, a) { + results.push(iterator(x, i, a)); + }); + return results; + }; + + var _reduce = function (arr, iterator, memo) { + if (arr.reduce) { + return arr.reduce(iterator, memo); + } + _each(arr, function (x, i, a) { + memo = iterator(memo, x, i, a); + }); + return memo; + }; + + var _keys = function (obj) { + if (Object.keys) { + return Object.keys(obj); + } + var keys = []; + for (var k in obj) { + if (obj.hasOwnProperty(k)) { + keys.push(k); + } + } + return keys; + }; + + //// exported async module functions //// + + //// nextTick implementation with browser-compatible fallback //// + if (typeof process === 'undefined' || !(process.nextTick)) { + if (typeof setImmediate === 'function') { + async.nextTick = function (fn) { + // not a direct alias for IE10 compatibility + setImmediate(fn); + }; + async.setImmediate = async.nextTick; + } + else { + async.nextTick = function (fn) { + setTimeout(fn, 0); + }; + async.setImmediate = async.nextTick; + } + } + else { + async.nextTick = process.nextTick; + if (typeof setImmediate !== 'undefined') { + async.setImmediate = setImmediate; + } + else { + async.setImmediate = async.nextTick; + } + } + + async.each = function (arr, iterator, callback) { + callback = callback || function () {}; + if (!arr.length) { + return callback(); + } + var completed = 0; + _each(arr, function (x) { + iterator(x, only_once(function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + if (completed >= arr.length) { + callback(null); + } + } + })); + }); + }; + async.forEach = async.each; + + async.eachSeries = function (arr, iterator, callback) { + callback = callback || function () {}; + if (!arr.length) { + return callback(); + } + var completed = 0; + var iterate = function () { + iterator(arr[completed], function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + if (completed >= arr.length) { + callback(null); + } + else { + iterate(); + } + } + }); + }; + iterate(); + }; + async.forEachSeries = async.eachSeries; + + async.eachLimit = function (arr, limit, iterator, callback) { + var fn = _eachLimit(limit); + fn.apply(null, [arr, iterator, callback]); + }; + async.forEachLimit = async.eachLimit; + + var _eachLimit = function (limit) { + + return function (arr, iterator, callback) { + callback = callback || function () {}; + if (!arr.length || limit <= 0) { + return callback(); + } + var completed = 0; + var started = 0; + var running = 0; + + (function replenish () { + if (completed >= arr.length) { + return callback(); + } + + while (running < limit && started < arr.length) { + started += 1; + running += 1; + iterator(arr[started - 1], function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + running -= 1; + if (completed >= arr.length) { + callback(); + } + else { + replenish(); + } + } + }); + } + })(); + }; + }; + + + var doParallel = function (fn) { + return function () { + var args = Array.prototype.slice.call(arguments); + return fn.apply(null, [async.each].concat(args)); + }; + }; + var doParallelLimit = function(limit, fn) { + return function () { + var args = Array.prototype.slice.call(arguments); + return fn.apply(null, [_eachLimit(limit)].concat(args)); + }; + }; + var doSeries = function (fn) { + return function () { + var args = Array.prototype.slice.call(arguments); + return fn.apply(null, [async.eachSeries].concat(args)); + }; + }; + + + var _asyncMap = function (eachfn, arr, iterator, callback) { + var results = []; + arr = _map(arr, function (x, i) { + return {index: i, value: x}; + }); + eachfn(arr, function (x, callback) { + iterator(x.value, function (err, v) { + results[x.index] = v; + callback(err); + }); + }, function (err) { + callback(err, results); + }); + }; + async.map = doParallel(_asyncMap); + async.mapSeries = doSeries(_asyncMap); + async.mapLimit = function (arr, limit, iterator, callback) { + return _mapLimit(limit)(arr, iterator, callback); + }; + + var _mapLimit = function(limit) { + return doParallelLimit(limit, _asyncMap); + }; + + // reduce only has a series version, as doing reduce in parallel won't + // work in many situations. + async.reduce = function (arr, memo, iterator, callback) { + async.eachSeries(arr, function (x, callback) { + iterator(memo, x, function (err, v) { + memo = v; + callback(err); + }); + }, function (err) { + callback(err, memo); + }); + }; + // inject alias + async.inject = async.reduce; + // foldl alias + async.foldl = async.reduce; + + async.reduceRight = function (arr, memo, iterator, callback) { + var reversed = _map(arr, function (x) { + return x; + }).reverse(); + async.reduce(reversed, memo, iterator, callback); + }; + // foldr alias + async.foldr = async.reduceRight; + + var _filter = function (eachfn, arr, iterator, callback) { + var results = []; + arr = _map(arr, function (x, i) { + return {index: i, value: x}; + }); + eachfn(arr, function (x, callback) { + iterator(x.value, function (v) { + if (v) { + results.push(x); + } + callback(); + }); + }, function (err) { + callback(_map(results.sort(function (a, b) { + return a.index - b.index; + }), function (x) { + return x.value; + })); + }); + }; + async.filter = doParallel(_filter); + async.filterSeries = doSeries(_filter); + // select alias + async.select = async.filter; + async.selectSeries = async.filterSeries; + + var _reject = function (eachfn, arr, iterator, callback) { + var results = []; + arr = _map(arr, function (x, i) { + return {index: i, value: x}; + }); + eachfn(arr, function (x, callback) { + iterator(x.value, function (v) { + if (!v) { + results.push(x); + } + callback(); + }); + }, function (err) { + callback(_map(results.sort(function (a, b) { + return a.index - b.index; + }), function (x) { + return x.value; + })); + }); + }; + async.reject = doParallel(_reject); + async.rejectSeries = doSeries(_reject); + + var _detect = function (eachfn, arr, iterator, main_callback) { + eachfn(arr, function (x, callback) { + iterator(x, function (result) { + if (result) { + main_callback(x); + main_callback = function () {}; + } + else { + callback(); + } + }); + }, function (err) { + main_callback(); + }); + }; + async.detect = doParallel(_detect); + async.detectSeries = doSeries(_detect); + + async.some = function (arr, iterator, main_callback) { + async.each(arr, function (x, callback) { + iterator(x, function (v) { + if (v) { + main_callback(true); + main_callback = function () {}; + } + callback(); + }); + }, function (err) { + main_callback(false); + }); + }; + // any alias + async.any = async.some; + + async.every = function (arr, iterator, main_callback) { + async.each(arr, function (x, callback) { + iterator(x, function (v) { + if (!v) { + main_callback(false); + main_callback = function () {}; + } + callback(); + }); + }, function (err) { + main_callback(true); + }); + }; + // all alias + async.all = async.every; + + async.sortBy = function (arr, iterator, callback) { + async.map(arr, function (x, callback) { + iterator(x, function (err, criteria) { + if (err) { + callback(err); + } + else { + callback(null, {value: x, criteria: criteria}); + } + }); + }, function (err, results) { + if (err) { + return callback(err); + } + else { + var fn = function (left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }; + callback(null, _map(results.sort(fn), function (x) { + return x.value; + })); + } + }); + }; + + async.auto = function (tasks, callback) { + callback = callback || function () {}; + var keys = _keys(tasks); + if (!keys.length) { + return callback(null); + } + + var results = {}; + + var listeners = []; + var addListener = function (fn) { + listeners.unshift(fn); + }; + var removeListener = function (fn) { + for (var i = 0; i < listeners.length; i += 1) { + if (listeners[i] === fn) { + listeners.splice(i, 1); + return; + } + } + }; + var taskComplete = function () { + _each(listeners.slice(0), function (fn) { + fn(); + }); + }; + + addListener(function () { + if (_keys(results).length === keys.length) { + callback(null, results); + callback = function () {}; + } + }); + + _each(keys, function (k) { + var task = (tasks[k] instanceof Function) ? [tasks[k]]: tasks[k]; + var taskCallback = function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + if (err) { + var safeResults = {}; + _each(_keys(results), function(rkey) { + safeResults[rkey] = results[rkey]; + }); + safeResults[k] = args; + callback(err, safeResults); + // stop subsequent errors hitting callback multiple times + callback = function () {}; + } + else { + results[k] = args; + async.setImmediate(taskComplete); + } + }; + var requires = task.slice(0, Math.abs(task.length - 1)) || []; + var ready = function () { + return _reduce(requires, function (a, x) { + return (a && results.hasOwnProperty(x)); + }, true) && !results.hasOwnProperty(k); + }; + if (ready()) { + task[task.length - 1](taskCallback, results); + } + else { + var listener = function () { + if (ready()) { + removeListener(listener); + task[task.length - 1](taskCallback, results); + } + }; + addListener(listener); + } + }); + }; + + async.waterfall = function (tasks, callback) { + callback = callback || function () {}; + if (tasks.constructor !== Array) { + var err = new Error('First argument to waterfall must be an array of functions'); + return callback(err); + } + if (!tasks.length) { + return callback(); + } + var wrapIterator = function (iterator) { + return function (err) { + if (err) { + callback.apply(null, arguments); + callback = function () {}; + } + else { + var args = Array.prototype.slice.call(arguments, 1); + var next = iterator.next(); + if (next) { + args.push(wrapIterator(next)); + } + else { + args.push(callback); + } + async.setImmediate(function () { + iterator.apply(null, args); + }); + } + }; + }; + wrapIterator(async.iterator(tasks))(); + }; + + var _parallel = function(eachfn, tasks, callback) { + callback = callback || function () {}; + if (tasks.constructor === Array) { + eachfn.map(tasks, function (fn, callback) { + if (fn) { + fn(function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + callback.call(null, err, args); + }); + } + }, callback); + } + else { + var results = {}; + eachfn.each(_keys(tasks), function (k, callback) { + tasks[k](function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + results[k] = args; + callback(err); + }); + }, function (err) { + callback(err, results); + }); + } + }; + + async.parallel = function (tasks, callback) { + _parallel({ map: async.map, each: async.each }, tasks, callback); + }; + + async.parallelLimit = function(tasks, limit, callback) { + _parallel({ map: _mapLimit(limit), each: _eachLimit(limit) }, tasks, callback); + }; + + async.series = function (tasks, callback) { + callback = callback || function () {}; + if (tasks.constructor === Array) { + async.mapSeries(tasks, function (fn, callback) { + if (fn) { + fn(function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + callback.call(null, err, args); + }); + } + }, callback); + } + else { + var results = {}; + async.eachSeries(_keys(tasks), function (k, callback) { + tasks[k](function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + results[k] = args; + callback(err); + }); + }, function (err) { + callback(err, results); + }); + } + }; + + async.iterator = function (tasks) { + var makeCallback = function (index) { + var fn = function () { + if (tasks.length) { + tasks[index].apply(null, arguments); + } + return fn.next(); + }; + fn.next = function () { + return (index < tasks.length - 1) ? makeCallback(index + 1): null; + }; + return fn; + }; + return makeCallback(0); + }; + + async.apply = function (fn) { + var args = Array.prototype.slice.call(arguments, 1); + return function () { + return fn.apply( + null, args.concat(Array.prototype.slice.call(arguments)) + ); + }; + }; + + var _concat = function (eachfn, arr, fn, callback) { + var r = []; + eachfn(arr, function (x, cb) { + fn(x, function (err, y) { + r = r.concat(y || []); + cb(err); + }); + }, function (err) { + callback(err, r); + }); + }; + async.concat = doParallel(_concat); + async.concatSeries = doSeries(_concat); + + async.whilst = function (test, iterator, callback) { + if (test()) { + iterator(function (err) { + if (err) { + return callback(err); + } + async.whilst(test, iterator, callback); + }); + } + else { + callback(); + } + }; + + async.doWhilst = function (iterator, test, callback) { + iterator(function (err) { + if (err) { + return callback(err); + } + if (test()) { + async.doWhilst(iterator, test, callback); + } + else { + callback(); + } + }); + }; + + async.until = function (test, iterator, callback) { + if (!test()) { + iterator(function (err) { + if (err) { + return callback(err); + } + async.until(test, iterator, callback); + }); + } + else { + callback(); + } + }; + + async.doUntil = function (iterator, test, callback) { + iterator(function (err) { + if (err) { + return callback(err); + } + if (!test()) { + async.doUntil(iterator, test, callback); + } + else { + callback(); + } + }); + }; + + async.queue = function (worker, concurrency) { + if (concurrency === undefined) { + concurrency = 1; + } + function _insert(q, data, pos, callback) { + if(data.constructor !== Array) { + data = [data]; + } + _each(data, function(task) { + var item = { + data: task, + callback: typeof callback === 'function' ? callback : null + }; + + if (pos) { + q.tasks.unshift(item); + } else { + q.tasks.push(item); + } + + if (q.saturated && q.tasks.length === concurrency) { + q.saturated(); + } + async.setImmediate(q.process); + }); + } + + var workers = 0; + var q = { + tasks: [], + concurrency: concurrency, + saturated: null, + empty: null, + drain: null, + push: function (data, callback) { + _insert(q, data, false, callback); + }, + unshift: function (data, callback) { + _insert(q, data, true, callback); + }, + process: function () { + if (workers < q.concurrency && q.tasks.length) { + var task = q.tasks.shift(); + if (q.empty && q.tasks.length === 0) { + q.empty(); + } + workers += 1; + var next = function () { + workers -= 1; + if (task.callback) { + task.callback.apply(task, arguments); + } + if (q.drain && q.tasks.length + workers === 0) { + q.drain(); + } + q.process(); + }; + var cb = only_once(next); + worker(task.data, cb); + } + }, + length: function () { + return q.tasks.length; + }, + running: function () { + return workers; + } + }; + return q; + }; + + async.cargo = function (worker, payload) { + var working = false, + tasks = []; + + var cargo = { + tasks: tasks, + payload: payload, + saturated: null, + empty: null, + drain: null, + push: function (data, callback) { + if(data.constructor !== Array) { + data = [data]; + } + _each(data, function(task) { + tasks.push({ + data: task, + callback: typeof callback === 'function' ? callback : null + }); + if (cargo.saturated && tasks.length === payload) { + cargo.saturated(); + } + }); + async.setImmediate(cargo.process); + }, + process: function process() { + if (working) return; + if (tasks.length === 0) { + if(cargo.drain) cargo.drain(); + return; + } + + var ts = typeof payload === 'number' + ? tasks.splice(0, payload) + : tasks.splice(0); + + var ds = _map(ts, function (task) { + return task.data; + }); + + if(cargo.empty) cargo.empty(); + working = true; + worker(ds, function () { + working = false; + + var args = arguments; + _each(ts, function (data) { + if (data.callback) { + data.callback.apply(null, args); + } + }); + + process(); + }); + }, + length: function () { + return tasks.length; + }, + running: function () { + return working; + } + }; + return cargo; + }; + + var _console_fn = function (name) { + return function (fn) { + var args = Array.prototype.slice.call(arguments, 1); + fn.apply(null, args.concat([function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (typeof console !== 'undefined') { + if (err) { + if (console.error) { + console.error(err); + } + } + else if (console[name]) { + _each(args, function (x) { + console[name](x); + }); + } + } + }])); + }; + }; + async.log = _console_fn('log'); + async.dir = _console_fn('dir'); + /*async.info = _console_fn('info'); + async.warn = _console_fn('warn'); + async.error = _console_fn('error');*/ + + async.memoize = function (fn, hasher) { + var memo = {}; + var queues = {}; + hasher = hasher || function (x) { + return x; + }; + var memoized = function () { + var args = Array.prototype.slice.call(arguments); + var callback = args.pop(); + var key = hasher.apply(null, args); + if (key in memo) { + callback.apply(null, memo[key]); + } + else if (key in queues) { + queues[key].push(callback); + } + else { + queues[key] = [callback]; + fn.apply(null, args.concat([function () { + memo[key] = arguments; + var q = queues[key]; + delete queues[key]; + for (var i = 0, l = q.length; i < l; i++) { + q[i].apply(null, arguments); + } + }])); + } + }; + memoized.memo = memo; + memoized.unmemoized = fn; + return memoized; + }; + + async.unmemoize = function (fn) { + return function () { + return (fn.unmemoized || fn).apply(null, arguments); + }; + }; + + async.times = function (count, iterator, callback) { + var counter = []; + for (var i = 0; i < count; i++) { + counter.push(i); + } + return async.map(counter, iterator, callback); + }; + + async.timesSeries = function (count, iterator, callback) { + var counter = []; + for (var i = 0; i < count; i++) { + counter.push(i); + } + return async.mapSeries(counter, iterator, callback); + }; + + async.compose = function (/* functions... */) { + var fns = Array.prototype.reverse.call(arguments); + return function () { + var that = this; + var args = Array.prototype.slice.call(arguments); + var callback = args.pop(); + async.reduce(fns, args, function (newargs, fn, cb) { + fn.apply(that, newargs.concat([function () { + var err = arguments[0]; + var nextargs = Array.prototype.slice.call(arguments, 1); + cb(err, nextargs); + }])) + }, + function (err, results) { + callback.apply(that, [err].concat(results)); + }); + }; + }; + + var _applyEach = function (eachfn, fns /*args...*/) { + var go = function () { + var that = this; + var args = Array.prototype.slice.call(arguments); + var callback = args.pop(); + return eachfn(fns, function (fn, cb) { + fn.apply(that, args.concat([cb])); + }, + callback); + }; + if (arguments.length > 2) { + var args = Array.prototype.slice.call(arguments, 2); + return go.apply(this, args); + } + else { + return go; + } + }; + async.applyEach = doParallel(_applyEach); + async.applyEachSeries = doSeries(_applyEach); + + async.forever = function (fn, callback) { + function next(err) { + if (err) { + if (callback) { + return callback(err); + } + throw err; + } + fn(next); + } + next(); + }; + + // AMD / RequireJS + if (typeof define !== 'undefined' && define.amd) { + define([], function () { + return async; + }); + } + // Node.js + else if (typeof module !== 'undefined' && module.exports) { + module.exports = async; + } + // included directly via \n\n```\n\n## Documentation\n\n### Collections\n\n* [each](#each)\n* [map](#map)\n* [filter](#filter)\n* [reject](#reject)\n* [reduce](#reduce)\n* [detect](#detect)\n* [sortBy](#sortBy)\n* [some](#some)\n* [every](#every)\n* [concat](#concat)\n\n### Control Flow\n\n* [series](#series)\n* [parallel](#parallel)\n* [whilst](#whilst)\n* [doWhilst](#doWhilst)\n* [until](#until)\n* [doUntil](#doUntil)\n* [forever](#forever)\n* [waterfall](#waterfall)\n* [compose](#compose)\n* [applyEach](#applyEach)\n* [queue](#queue)\n* [cargo](#cargo)\n* [auto](#auto)\n* [iterator](#iterator)\n* [apply](#apply)\n* [nextTick](#nextTick)\n* [times](#times)\n* [timesSeries](#timesSeries)\n\n### Utils\n\n* [memoize](#memoize)\n* [unmemoize](#unmemoize)\n* [log](#log)\n* [dir](#dir)\n* [noConflict](#noConflict)\n\n\n## Collections\n\n\n\n### each(arr, iterator, callback)\n\nApplies an iterator function to each item in an array, in parallel.\nThe iterator is called with an item from the list and a callback for when it\nhas finished. If the iterator passes an error to this callback, the main\ncallback for the each function is immediately called with the error.\n\nNote, that since this function applies the iterator to each item in parallel\nthere is no guarantee that the iterator functions will complete in order.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback(err) which must be called once it has \n completed. If no error has occured, the callback should be run without \n arguments or with an explicit null argument.\n* callback(err) - A callback which is called after all the iterator functions\n have finished, or an error has occurred.\n\n__Example__\n\n```js\n// assuming openFiles is an array of file names and saveFile is a function\n// to save the modified contents of that file:\n\nasync.each(openFiles, saveFile, function(err){\n // if any of the saves produced an error, err would equal that error\n});\n```\n\n---------------------------------------\n\n\n\n### eachSeries(arr, iterator, callback)\n\nThe same as each only the iterator is applied to each item in the array in\nseries. The next iterator is only called once the current one has completed\nprocessing. This means the iterator functions will complete in order.\n\n\n---------------------------------------\n\n\n\n### eachLimit(arr, limit, iterator, callback)\n\nThe same as each only no more than \"limit\" iterators will be simultaneously \nrunning at any time.\n\nNote that the items are not processed in batches, so there is no guarantee that\n the first \"limit\" iterator functions will complete before any others are \nstarted.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* limit - The maximum number of iterators to run at any time.\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback(err) which must be called once it has \n completed. If no error has occured, the callback should be run without \n arguments or with an explicit null argument.\n* callback(err) - A callback which is called after all the iterator functions\n have finished, or an error has occurred.\n\n__Example__\n\n```js\n// Assume documents is an array of JSON objects and requestApi is a\n// function that interacts with a rate-limited REST api.\n\nasync.eachLimit(documents, 20, requestApi, function(err){\n // if any of the saves produced an error, err would equal that error\n});\n```\n\n---------------------------------------\n\n\n### map(arr, iterator, callback)\n\nProduces a new array of values by mapping each value in the given array through\nthe iterator function. The iterator is called with an item from the array and a\ncallback for when it has finished processing. The callback takes 2 arguments, \nan error and the transformed item from the array. If the iterator passes an\nerror to this callback, the main callback for the map function is immediately\ncalled with the error.\n\nNote, that since this function applies the iterator to each item in parallel\nthere is no guarantee that the iterator functions will complete in order, however\nthe results array will be in the same order as the original array.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback(err, transformed) which must be called once \n it has completed with an error (which can be null) and a transformed item.\n* callback(err, results) - A callback which is called after all the iterator\n functions have finished, or an error has occurred. Results is an array of the\n transformed items from the original array.\n\n__Example__\n\n```js\nasync.map(['file1','file2','file3'], fs.stat, function(err, results){\n // results is now an array of stats for each file\n});\n```\n\n---------------------------------------\n\n\n### mapSeries(arr, iterator, callback)\n\nThe same as map only the iterator is applied to each item in the array in\nseries. The next iterator is only called once the current one has completed\nprocessing. The results array will be in the same order as the original.\n\n\n---------------------------------------\n\n\n### mapLimit(arr, limit, iterator, callback)\n\nThe same as map only no more than \"limit\" iterators will be simultaneously \nrunning at any time.\n\nNote that the items are not processed in batches, so there is no guarantee that\n the first \"limit\" iterator functions will complete before any others are \nstarted.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* limit - The maximum number of iterators to run at any time.\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback(err, transformed) which must be called once \n it has completed with an error (which can be null) and a transformed item.\n* callback(err, results) - A callback which is called after all the iterator\n functions have finished, or an error has occurred. Results is an array of the\n transformed items from the original array.\n\n__Example__\n\n```js\nasync.map(['file1','file2','file3'], 1, fs.stat, function(err, results){\n // results is now an array of stats for each file\n});\n```\n\n---------------------------------------\n\n\n### filter(arr, iterator, callback)\n\n__Alias:__ select\n\nReturns a new array of all the values which pass an async truth test.\n_The callback for each iterator call only accepts a single argument of true or\nfalse, it does not accept an error argument first!_ This is in-line with the\nway node libraries work with truth tests like fs.exists. This operation is\nperformed in parallel, but the results array will be in the same order as the\noriginal.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A truth test to apply to each item in the array.\n The iterator is passed a callback(truthValue) which must be called with a \n boolean argument once it has completed.\n* callback(results) - A callback which is called after all the iterator\n functions have finished.\n\n__Example__\n\n```js\nasync.filter(['file1','file2','file3'], fs.exists, function(results){\n // results now equals an array of the existing files\n});\n```\n\n---------------------------------------\n\n\n### filterSeries(arr, iterator, callback)\n\n__alias:__ selectSeries\n\nThe same as filter only the iterator is applied to each item in the array in\nseries. The next iterator is only called once the current one has completed\nprocessing. The results array will be in the same order as the original.\n\n---------------------------------------\n\n\n### reject(arr, iterator, callback)\n\nThe opposite of filter. Removes values that pass an async truth test.\n\n---------------------------------------\n\n\n### rejectSeries(arr, iterator, callback)\n\nThe same as reject, only the iterator is applied to each item in the array\nin series.\n\n\n---------------------------------------\n\n\n### reduce(arr, memo, iterator, callback)\n\n__aliases:__ inject, foldl\n\nReduces a list of values into a single value using an async iterator to return\neach successive step. Memo is the initial state of the reduction. This\nfunction only operates in series. For performance reasons, it may make sense to\nsplit a call to this function into a parallel map, then use the normal\nArray.prototype.reduce on the results. This function is for situations where\neach step in the reduction needs to be async, if you can get the data before\nreducing it then it's probably a good idea to do so.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* memo - The initial state of the reduction.\n* iterator(memo, item, callback) - A function applied to each item in the\n array to produce the next step in the reduction. The iterator is passed a\n callback(err, reduction) which accepts an optional error as its first \n argument, and the state of the reduction as the second. If an error is \n passed to the callback, the reduction is stopped and the main callback is \n immediately called with the error.\n* callback(err, result) - A callback which is called after all the iterator\n functions have finished. Result is the reduced value.\n\n__Example__\n\n```js\nasync.reduce([1,2,3], 0, function(memo, item, callback){\n // pointless async:\n process.nextTick(function(){\n callback(null, memo + item)\n });\n}, function(err, result){\n // result is now equal to the last value of memo, which is 6\n});\n```\n\n---------------------------------------\n\n\n### reduceRight(arr, memo, iterator, callback)\n\n__Alias:__ foldr\n\nSame as reduce, only operates on the items in the array in reverse order.\n\n\n---------------------------------------\n\n\n### detect(arr, iterator, callback)\n\nReturns the first value in a list that passes an async truth test. The\niterator is applied in parallel, meaning the first iterator to return true will\nfire the detect callback with that result. That means the result might not be\nthe first item in the original array (in terms of order) that passes the test.\n\nIf order within the original array is important then look at detectSeries.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A truth test to apply to each item in the array.\n The iterator is passed a callback(truthValue) which must be called with a \n boolean argument once it has completed.\n* callback(result) - A callback which is called as soon as any iterator returns\n true, or after all the iterator functions have finished. Result will be\n the first item in the array that passes the truth test (iterator) or the\n value undefined if none passed.\n\n__Example__\n\n```js\nasync.detect(['file1','file2','file3'], fs.exists, function(result){\n // result now equals the first file in the list that exists\n});\n```\n\n---------------------------------------\n\n\n### detectSeries(arr, iterator, callback)\n\nThe same as detect, only the iterator is applied to each item in the array\nin series. This means the result is always the first in the original array (in\nterms of array order) that passes the truth test.\n\n\n---------------------------------------\n\n\n### sortBy(arr, iterator, callback)\n\nSorts a list by the results of running each value through an async iterator.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback(err, sortValue) which must be called once it\n has completed with an error (which can be null) and a value to use as the sort\n criteria.\n* callback(err, results) - A callback which is called after all the iterator\n functions have finished, or an error has occurred. Results is the items from\n the original array sorted by the values returned by the iterator calls.\n\n__Example__\n\n```js\nasync.sortBy(['file1','file2','file3'], function(file, callback){\n fs.stat(file, function(err, stats){\n callback(err, stats.mtime);\n });\n}, function(err, results){\n // results is now the original array of files sorted by\n // modified date\n});\n```\n\n---------------------------------------\n\n\n### some(arr, iterator, callback)\n\n__Alias:__ any\n\nReturns true if at least one element in the array satisfies an async test.\n_The callback for each iterator call only accepts a single argument of true or\nfalse, it does not accept an error argument first!_ This is in-line with the\nway node libraries work with truth tests like fs.exists. Once any iterator\ncall returns true, the main callback is immediately called.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A truth test to apply to each item in the array.\n The iterator is passed a callback(truthValue) which must be called with a \n boolean argument once it has completed.\n* callback(result) - A callback which is called as soon as any iterator returns\n true, or after all the iterator functions have finished. Result will be\n either true or false depending on the values of the async tests.\n\n__Example__\n\n```js\nasync.some(['file1','file2','file3'], fs.exists, function(result){\n // if result is true then at least one of the files exists\n});\n```\n\n---------------------------------------\n\n\n### every(arr, iterator, callback)\n\n__Alias:__ all\n\nReturns true if every element in the array satisfies an async test.\n_The callback for each iterator call only accepts a single argument of true or\nfalse, it does not accept an error argument first!_ This is in-line with the\nway node libraries work with truth tests like fs.exists.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A truth test to apply to each item in the array.\n The iterator is passed a callback(truthValue) which must be called with a \n boolean argument once it has completed.\n* callback(result) - A callback which is called after all the iterator\n functions have finished. Result will be either true or false depending on\n the values of the async tests.\n\n__Example__\n\n```js\nasync.every(['file1','file2','file3'], fs.exists, function(result){\n // if result is true then every file exists\n});\n```\n\n---------------------------------------\n\n\n### concat(arr, iterator, callback)\n\nApplies an iterator to each item in a list, concatenating the results. Returns the\nconcatenated list. The iterators are called in parallel, and the results are\nconcatenated as they return. There is no guarantee that the results array will\nbe returned in the original order of the arguments passed to the iterator function.\n\n__Arguments__\n\n* arr - An array to iterate over\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback(err, results) which must be called once it \n has completed with an error (which can be null) and an array of results.\n* callback(err, results) - A callback which is called after all the iterator\n functions have finished, or an error has occurred. Results is an array containing\n the concatenated results of the iterator function.\n\n__Example__\n\n```js\nasync.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files){\n // files is now a list of filenames that exist in the 3 directories\n});\n```\n\n---------------------------------------\n\n\n### concatSeries(arr, iterator, callback)\n\nSame as async.concat, but executes in series instead of parallel.\n\n\n## Control Flow\n\n\n### series(tasks, [callback])\n\nRun an array of functions in series, each one running once the previous\nfunction has completed. If any functions in the series pass an error to its\ncallback, no more functions are run and the callback for the series is\nimmediately called with the value of the error. Once the tasks have completed,\nthe results are passed to the final callback as an array.\n\nIt is also possible to use an object instead of an array. Each property will be\nrun as a function and the results will be passed to the final callback as an object\ninstead of an array. This can be a more readable way of handling results from\nasync.series.\n\n\n__Arguments__\n\n* tasks - An array or object containing functions to run, each function is passed\n a callback(err, result) it must call on completion with an error (which can\n be null) and an optional result value.\n* callback(err, results) - An optional callback to run once all the functions\n have completed. This function gets a results array (or object) containing all \n the result arguments passed to the task callbacks.\n\n__Example__\n\n```js\nasync.series([\n function(callback){\n // do some stuff ...\n callback(null, 'one');\n },\n function(callback){\n // do some more stuff ...\n callback(null, 'two');\n }\n],\n// optional callback\nfunction(err, results){\n // results is now equal to ['one', 'two']\n});\n\n\n// an example using an object instead of an array\nasync.series({\n one: function(callback){\n setTimeout(function(){\n callback(null, 1);\n }, 200);\n },\n two: function(callback){\n setTimeout(function(){\n callback(null, 2);\n }, 100);\n }\n},\nfunction(err, results) {\n // results is now equal to: {one: 1, two: 2}\n});\n```\n\n---------------------------------------\n\n\n### parallel(tasks, [callback])\n\nRun an array of functions in parallel, without waiting until the previous\nfunction has completed. If any of the functions pass an error to its\ncallback, the main callback is immediately called with the value of the error.\nOnce the tasks have completed, the results are passed to the final callback as an\narray.\n\nIt is also possible to use an object instead of an array. Each property will be\nrun as a function and the results will be passed to the final callback as an object\ninstead of an array. This can be a more readable way of handling results from\nasync.parallel.\n\n\n__Arguments__\n\n* tasks - An array or object containing functions to run, each function is passed \n a callback(err, result) it must call on completion with an error (which can\n be null) and an optional result value.\n* callback(err, results) - An optional callback to run once all the functions\n have completed. This function gets a results array (or object) containing all \n the result arguments passed to the task callbacks.\n\n__Example__\n\n```js\nasync.parallel([\n function(callback){\n setTimeout(function(){\n callback(null, 'one');\n }, 200);\n },\n function(callback){\n setTimeout(function(){\n callback(null, 'two');\n }, 100);\n }\n],\n// optional callback\nfunction(err, results){\n // the results array will equal ['one','two'] even though\n // the second function had a shorter timeout.\n});\n\n\n// an example using an object instead of an array\nasync.parallel({\n one: function(callback){\n setTimeout(function(){\n callback(null, 1);\n }, 200);\n },\n two: function(callback){\n setTimeout(function(){\n callback(null, 2);\n }, 100);\n }\n},\nfunction(err, results) {\n // results is now equals to: {one: 1, two: 2}\n});\n```\n\n---------------------------------------\n\n\n### parallelLimit(tasks, limit, [callback])\n\nThe same as parallel only the tasks are executed in parallel with a maximum of \"limit\" \ntasks executing at any time.\n\nNote that the tasks are not executed in batches, so there is no guarantee that \nthe first \"limit\" tasks will complete before any others are started.\n\n__Arguments__\n\n* tasks - An array or object containing functions to run, each function is passed \n a callback(err, result) it must call on completion with an error (which can\n be null) and an optional result value.\n* limit - The maximum number of tasks to run at any time.\n* callback(err, results) - An optional callback to run once all the functions\n have completed. This function gets a results array (or object) containing all \n the result arguments passed to the task callbacks.\n\n---------------------------------------\n\n\n### whilst(test, fn, callback)\n\nRepeatedly call fn, while test returns true. Calls the callback when stopped,\nor an error occurs.\n\n__Arguments__\n\n* test() - synchronous truth test to perform before each execution of fn.\n* fn(callback) - A function to call each time the test passes. The function is\n passed a callback(err) which must be called once it has completed with an \n optional error argument.\n* callback(err) - A callback which is called after the test fails and repeated\n execution of fn has stopped.\n\n__Example__\n\n```js\nvar count = 0;\n\nasync.whilst(\n function () { return count < 5; },\n function (callback) {\n count++;\n setTimeout(callback, 1000);\n },\n function (err) {\n // 5 seconds have passed\n }\n);\n```\n\n---------------------------------------\n\n\n### doWhilst(fn, test, callback)\n\nThe post check version of whilst. To reflect the difference in the order of operations `test` and `fn` arguments are switched. `doWhilst` is to `whilst` as `do while` is to `while` in plain JavaScript.\n\n---------------------------------------\n\n\n### until(test, fn, callback)\n\nRepeatedly call fn, until test returns true. Calls the callback when stopped,\nor an error occurs.\n\nThe inverse of async.whilst.\n\n---------------------------------------\n\n\n### doUntil(fn, test, callback)\n\nLike doWhilst except the test is inverted. Note the argument ordering differs from `until`.\n\n---------------------------------------\n\n\n### forever(fn, callback)\n\nCalls the asynchronous function 'fn' repeatedly, in series, indefinitely.\nIf an error is passed to fn's callback then 'callback' is called with the\nerror, otherwise it will never be called.\n\n---------------------------------------\n\n\n### waterfall(tasks, [callback])\n\nRuns an array of functions in series, each passing their results to the next in\nthe array. However, if any of the functions pass an error to the callback, the\nnext function is not executed and the main callback is immediately called with\nthe error.\n\n__Arguments__\n\n* tasks - An array of functions to run, each function is passed a \n callback(err, result1, result2, ...) it must call on completion. The first\n argument is an error (which can be null) and any further arguments will be \n passed as arguments in order to the next task.\n* callback(err, [results]) - An optional callback to run once all the functions\n have completed. This will be passed the results of the last task's callback.\n\n\n\n__Example__\n\n```js\nasync.waterfall([\n function(callback){\n callback(null, 'one', 'two');\n },\n function(arg1, arg2, callback){\n callback(null, 'three');\n },\n function(arg1, callback){\n // arg1 now equals 'three'\n callback(null, 'done');\n }\n], function (err, result) {\n // result now equals 'done' \n});\n```\n\n---------------------------------------\n\n### compose(fn1, fn2...)\n\nCreates a function which is a composition of the passed asynchronous\nfunctions. Each function consumes the return value of the function that\nfollows. Composing functions f(), g() and h() would produce the result of\nf(g(h())), only this version uses callbacks to obtain the return values.\n\nEach function is executed with the `this` binding of the composed function.\n\n__Arguments__\n\n* functions... - the asynchronous functions to compose\n\n\n__Example__\n\n```js\nfunction add1(n, callback) {\n setTimeout(function () {\n callback(null, n + 1);\n }, 10);\n}\n\nfunction mul3(n, callback) {\n setTimeout(function () {\n callback(null, n * 3);\n }, 10);\n}\n\nvar add1mul3 = async.compose(mul3, add1);\n\nadd1mul3(4, function (err, result) {\n // result now equals 15\n});\n```\n\n---------------------------------------\n\n### applyEach(fns, args..., callback)\n\nApplies the provided arguments to each function in the array, calling the\ncallback after all functions have completed. If you only provide the first\nargument then it will return a function which lets you pass in the\narguments as if it were a single function call.\n\n__Arguments__\n\n* fns - the asynchronous functions to all call with the same arguments\n* args... - any number of separate arguments to pass to the function\n* callback - the final argument should be the callback, called when all\n functions have completed processing\n\n\n__Example__\n\n```js\nasync.applyEach([enableSearch, updateSchema], 'bucket', callback);\n\n// partial application example:\nasync.each(\n buckets,\n async.applyEach([enableSearch, updateSchema]),\n callback\n);\n```\n\n---------------------------------------\n\n\n### applyEachSeries(arr, iterator, callback)\n\nThe same as applyEach only the functions are applied in series.\n\n---------------------------------------\n\n\n### queue(worker, concurrency)\n\nCreates a queue object with the specified concurrency. Tasks added to the\nqueue will be processed in parallel (up to the concurrency limit). If all\nworkers are in progress, the task is queued until one is available. Once\na worker has completed a task, the task's callback is called.\n\n__Arguments__\n\n* worker(task, callback) - An asynchronous function for processing a queued\n task, which must call its callback(err) argument when finished, with an \n optional error as an argument.\n* concurrency - An integer for determining how many worker functions should be\n run in parallel.\n\n__Queue objects__\n\nThe queue object returned by this function has the following properties and\nmethods:\n\n* length() - a function returning the number of items waiting to be processed.\n* concurrency - an integer for determining how many worker functions should be\n run in parallel. This property can be changed after a queue is created to\n alter the concurrency on-the-fly.\n* push(task, [callback]) - add a new task to the queue, the callback is called\n once the worker has finished processing the task.\n instead of a single task, an array of tasks can be submitted. the respective callback is used for every task in the list.\n* unshift(task, [callback]) - add a new task to the front of the queue.\n* saturated - a callback that is called when the queue length hits the concurrency and further tasks will be queued\n* empty - a callback that is called when the last item from the queue is given to a worker\n* drain - a callback that is called when the last item from the queue has returned from the worker\n\n__Example__\n\n```js\n// create a queue object with concurrency 2\n\nvar q = async.queue(function (task, callback) {\n console.log('hello ' + task.name);\n callback();\n}, 2);\n\n\n// assign a callback\nq.drain = function() {\n console.log('all items have been processed');\n}\n\n// add some items to the queue\n\nq.push({name: 'foo'}, function (err) {\n console.log('finished processing foo');\n});\nq.push({name: 'bar'}, function (err) {\n console.log('finished processing bar');\n});\n\n// add some items to the queue (batch-wise)\n\nq.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function (err) {\n console.log('finished processing bar');\n});\n\n// add some items to the front of the queue\n\nq.unshift({name: 'bar'}, function (err) {\n console.log('finished processing bar');\n});\n```\n\n---------------------------------------\n\n\n### cargo(worker, [payload])\n\nCreates a cargo object with the specified payload. Tasks added to the\ncargo will be processed altogether (up to the payload limit). If the\nworker is in progress, the task is queued until it is available. Once\nthe worker has completed some tasks, each callback of those tasks is called.\n\n__Arguments__\n\n* worker(tasks, callback) - An asynchronous function for processing an array of\n queued tasks, which must call its callback(err) argument when finished, with \n an optional error as an argument.\n* payload - An optional integer for determining how many tasks should be\n processed per round; if omitted, the default is unlimited.\n\n__Cargo objects__\n\nThe cargo object returned by this function has the following properties and\nmethods:\n\n* length() - a function returning the number of items waiting to be processed.\n* payload - an integer for determining how many tasks should be\n process per round. This property can be changed after a cargo is created to\n alter the payload on-the-fly.\n* push(task, [callback]) - add a new task to the queue, the callback is called\n once the worker has finished processing the task.\n instead of a single task, an array of tasks can be submitted. the respective callback is used for every task in the list.\n* saturated - a callback that is called when the queue length hits the concurrency and further tasks will be queued\n* empty - a callback that is called when the last item from the queue is given to a worker\n* drain - a callback that is called when the last item from the queue has returned from the worker\n\n__Example__\n\n```js\n// create a cargo object with payload 2\n\nvar cargo = async.cargo(function (tasks, callback) {\n for(var i=0; i\n### auto(tasks, [callback])\n\nDetermines the best order for running functions based on their requirements.\nEach function can optionally depend on other functions being completed first,\nand each function is run as soon as its requirements are satisfied. If any of\nthe functions pass an error to their callback, that function will not complete\n(so any other functions depending on it will not run) and the main callback\nwill be called immediately with the error. Functions also receive an object\ncontaining the results of functions which have completed so far.\n\nNote, all functions are called with a results object as a second argument, \nso it is unsafe to pass functions in the tasks object which cannot handle the\nextra argument. For example, this snippet of code:\n\n```js\nasync.auto({\n readData: async.apply(fs.readFile, 'data.txt', 'utf-8');\n}, callback);\n```\n\nwill have the effect of calling readFile with the results object as the last\nargument, which will fail:\n\n```js\nfs.readFile('data.txt', 'utf-8', cb, {});\n```\n\nInstead, wrap the call to readFile in a function which does not forward the \nresults object:\n\n```js\nasync.auto({\n readData: function(cb, results){\n fs.readFile('data.txt', 'utf-8', cb);\n }\n}, callback);\n```\n\n__Arguments__\n\n* tasks - An object literal containing named functions or an array of\n requirements, with the function itself the last item in the array. The key\n used for each function or array is used when specifying requirements. The \n function receives two arguments: (1) a callback(err, result) which must be \n called when finished, passing an error (which can be null) and the result of \n the function's execution, and (2) a results object, containing the results of\n the previously executed functions.\n* callback(err, results) - An optional callback which is called when all the\n tasks have been completed. The callback will receive an error as an argument\n if any tasks pass an error to their callback. Results will always be passed\n\tbut if an error occurred, no other tasks will be performed, and the results\n\tobject will only contain partial results.\n \n\n__Example__\n\n```js\nasync.auto({\n get_data: function(callback){\n // async code to get some data\n },\n make_folder: function(callback){\n // async code to create a directory to store a file in\n // this is run at the same time as getting the data\n },\n write_file: ['get_data', 'make_folder', function(callback){\n // once there is some data and the directory exists,\n // write the data to a file in the directory\n callback(null, filename);\n }],\n email_link: ['write_file', function(callback, results){\n // once the file is written let's email a link to it...\n // results.write_file contains the filename returned by write_file.\n }]\n});\n```\n\nThis is a fairly trivial example, but to do this using the basic parallel and\nseries functions would look like this:\n\n```js\nasync.parallel([\n function(callback){\n // async code to get some data\n },\n function(callback){\n // async code to create a directory to store a file in\n // this is run at the same time as getting the data\n }\n],\nfunction(err, results){\n async.series([\n function(callback){\n // once there is some data and the directory exists,\n // write the data to a file in the directory\n },\n function(callback){\n // once the file is written let's email a link to it...\n }\n ]);\n});\n```\n\nFor a complicated series of async tasks using the auto function makes adding\nnew tasks much easier and makes the code more readable.\n\n\n---------------------------------------\n\n\n### iterator(tasks)\n\nCreates an iterator function which calls the next function in the array,\nreturning a continuation to call the next one after that. It's also possible to\n'peek' the next iterator by doing iterator.next().\n\nThis function is used internally by the async module but can be useful when\nyou want to manually control the flow of functions in series.\n\n__Arguments__\n\n* tasks - An array of functions to run.\n\n__Example__\n\n```js\nvar iterator = async.iterator([\n function(){ sys.p('one'); },\n function(){ sys.p('two'); },\n function(){ sys.p('three'); }\n]);\n\nnode> var iterator2 = iterator();\n'one'\nnode> var iterator3 = iterator2();\n'two'\nnode> iterator3();\n'three'\nnode> var nextfn = iterator2.next();\nnode> nextfn();\n'three'\n```\n\n---------------------------------------\n\n\n### apply(function, arguments..)\n\nCreates a continuation function with some arguments already applied, a useful\nshorthand when combined with other control flow functions. Any arguments\npassed to the returned function are added to the arguments originally passed\nto apply.\n\n__Arguments__\n\n* function - The function you want to eventually apply all arguments to.\n* arguments... - Any number of arguments to automatically apply when the\n continuation is called.\n\n__Example__\n\n```js\n// using apply\n\nasync.parallel([\n async.apply(fs.writeFile, 'testfile1', 'test1'),\n async.apply(fs.writeFile, 'testfile2', 'test2'),\n]);\n\n\n// the same process without using apply\n\nasync.parallel([\n function(callback){\n fs.writeFile('testfile1', 'test1', callback);\n },\n function(callback){\n fs.writeFile('testfile2', 'test2', callback);\n }\n]);\n```\n\nIt's possible to pass any number of additional arguments when calling the\ncontinuation:\n\n```js\nnode> var fn = async.apply(sys.puts, 'one');\nnode> fn('two', 'three');\none\ntwo\nthree\n```\n\n---------------------------------------\n\n\n### nextTick(callback)\n\nCalls the callback on a later loop around the event loop. In node.js this just\ncalls process.nextTick, in the browser it falls back to setImmediate(callback)\nif available, otherwise setTimeout(callback, 0), which means other higher priority\nevents may precede the execution of the callback.\n\nThis is used internally for browser-compatibility purposes.\n\n__Arguments__\n\n* callback - The function to call on a later loop around the event loop.\n\n__Example__\n\n```js\nvar call_order = [];\nasync.nextTick(function(){\n call_order.push('two');\n // call_order now equals ['one','two']\n});\ncall_order.push('one')\n```\n\n\n### times(n, callback)\n\nCalls the callback n times and accumulates results in the same manner\nyou would use with async.map.\n\n__Arguments__\n\n* n - The number of times to run the function.\n* callback - The function to call n times.\n\n__Example__\n\n```js\n// Pretend this is some complicated async factory\nvar createUser = function(id, callback) {\n callback(null, {\n id: 'user' + id\n })\n}\n// generate 5 users\nasync.times(5, function(n, next){\n createUser(n, function(err, user) {\n next(err, user)\n })\n}, function(err, users) {\n // we should now have 5 users\n});\n```\n\n\n### timesSeries(n, callback)\n\nThe same as times only the iterator is applied to each item in the array in\nseries. The next iterator is only called once the current one has completed\nprocessing. The results array will be in the same order as the original.\n\n\n## Utils\n\n\n### memoize(fn, [hasher])\n\nCaches the results of an async function. When creating a hash to store function\nresults against, the callback is omitted from the hash and an optional hash\nfunction can be used.\n\nThe cache of results is exposed as the `memo` property of the function returned\nby `memoize`.\n\n__Arguments__\n\n* fn - the function you to proxy and cache results from.\n* hasher - an optional function for generating a custom hash for storing\n results, it has all the arguments applied to it apart from the callback, and\n must be synchronous.\n\n__Example__\n\n```js\nvar slow_fn = function (name, callback) {\n // do something\n callback(null, result);\n};\nvar fn = async.memoize(slow_fn);\n\n// fn can now be used as if it were slow_fn\nfn('some name', function () {\n // callback\n});\n```\n\n\n### unmemoize(fn)\n\nUndoes a memoized function, reverting it to the original, unmemoized\nform. Comes handy in tests.\n\n__Arguments__\n\n* fn - the memoized function\n\n\n### log(function, arguments)\n\nLogs the result of an async function to the console. Only works in node.js or\nin browsers that support console.log and console.error (such as FF and Chrome).\nIf multiple arguments are returned from the async function, console.log is\ncalled on each argument in order.\n\n__Arguments__\n\n* function - The function you want to eventually apply all arguments to.\n* arguments... - Any number of arguments to apply to the function.\n\n__Example__\n\n```js\nvar hello = function(name, callback){\n setTimeout(function(){\n callback(null, 'hello ' + name);\n }, 1000);\n};\n```\n```js\nnode> async.log(hello, 'world');\n'hello world'\n```\n\n---------------------------------------\n\n\n### dir(function, arguments)\n\nLogs the result of an async function to the console using console.dir to\ndisplay the properties of the resulting object. Only works in node.js or\nin browsers that support console.dir and console.error (such as FF and Chrome).\nIf multiple arguments are returned from the async function, console.dir is\ncalled on each argument in order.\n\n__Arguments__\n\n* function - The function you want to eventually apply all arguments to.\n* arguments... - Any number of arguments to apply to the function.\n\n__Example__\n\n```js\nvar hello = function(name, callback){\n setTimeout(function(){\n callback(null, {hello: name});\n }, 1000);\n};\n```\n```js\nnode> async.dir(hello, 'world');\n{hello: 'world'}\n```\n\n---------------------------------------\n\n\n### noConflict()\n\nChanges the value of async back to its original value, returning a reference to the\nasync object.\n", + "_id": "async@0.2.9", + "dist": { + "shasum": "0c284b6d60cdb1069d75c3db6108c8a7f0d64798" + }, + "_from": "async@~0.2.9" +} diff --git a/node_modules/jsftp/node_modules/event-stream/.npmignore b/node_modules/jsftp/node_modules/event-stream/.npmignore new file mode 100644 index 0000000..13abef4 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/.npmignore @@ -0,0 +1,3 @@ +node_modules +node_modules/* +npm_debug.log diff --git a/node_modules/jsftp/node_modules/event-stream/.travis.yml b/node_modules/jsftp/node_modules/event-stream/.travis.yml new file mode 100644 index 0000000..895dbd3 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.6 + - 0.8 diff --git a/node_modules/jsftp/node_modules/event-stream/LICENCE b/node_modules/jsftp/node_modules/event-stream/LICENCE new file mode 100644 index 0000000..171dd97 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/LICENCE @@ -0,0 +1,22 @@ +Copyright (c) 2011 Dominic Tarr + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/jsftp/node_modules/event-stream/examples/pretty.js b/node_modules/jsftp/node_modules/event-stream/examples/pretty.js new file mode 100644 index 0000000..af04340 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/examples/pretty.js @@ -0,0 +1,25 @@ + +var inspect = require('util').inspect + +if(!module.parent) { + var es = require('..') //load event-stream + es.pipe( //pipe joins streams together + process.openStdin(), //open stdin + es.split(), //split stream to break on newlines + es.map(function (data, callback) {//turn this async function into a stream + var j + try { + j = JSON.parse(data) //try to parse input into json + } catch (err) { + return callback(null, data) //if it fails just pass it anyway + } + callback(null, inspect(j)) //render it nicely + }), + process.stdout // pipe it to stdout ! + ) + } + +// run this +// +// curl -sS registry.npmjs.org/event-stream | node pretty.js +// diff --git a/node_modules/jsftp/node_modules/event-stream/index.js b/node_modules/jsftp/node_modules/event-stream/index.js new file mode 100644 index 0000000..a7c05bb --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/index.js @@ -0,0 +1,302 @@ +//filter will reemit the data if cb(err,pass) pass is truthy + +// reduce is more tricky +// maybe we want to group the reductions or emit progress updates occasionally +// the most basic reduce just emits one 'data' event after it has recieved 'end' + +var Stream = require('stream').Stream + , es = exports + , through = require('through') + , from = require('from') + , duplex = require('duplexer') + , map = require('map-stream') + , pause = require('pause-stream') + , split = require('split') + , pipeline = require('stream-combiner') + +es.Stream = Stream //re-export Stream from core +es.through = through +es.from = from +es.duplex = duplex +es.map = map +es.pause = pause +es.split = split +es.pipeline = es.connect = es.pipe = pipeline +// merge / concat +// +// combine multiple streams into a single stream. +// will emit end only once + +es.concat = //actually this should be called concat +es.merge = function (/*streams...*/) { + var toMerge = [].slice.call(arguments) + var stream = new Stream() + var endCount = 0 + stream.writable = stream.readable = true + + toMerge.forEach(function (e) { + e.pipe(stream, {end: false}) + var ended = false + e.on('end', function () { + if(ended) return + ended = true + endCount ++ + if(endCount == toMerge.length) + stream.emit('end') + }) + }) + stream.write = function (data) { + this.emit('data', data) + } + stream.destroy = function () { + merge.forEach(function (e) { + if(e.destroy) e.destroy() + }) + } + return stream +} + + +// writable stream, collects all events into an array +// and calls back when 'end' occurs +// mainly I'm using this to test the other functions + +es.writeArray = function (done) { + if ('function' !== typeof done) + throw new Error('function writeArray (done): done must be function') + + var a = new Stream () + , array = [], isDone = false + a.write = function (l) { + array.push(l) + } + a.end = function () { + isDone = true + done(null, array) + } + a.writable = true + a.readable = false + a.destroy = function () { + a.writable = a.readable = false + if(isDone) return + done(new Error('destroyed before end'), array) + } + return a +} + +//return a Stream that reads the properties of an object +//respecting pause() and resume() + +es.readArray = function (array) { + var stream = new Stream() + , i = 0 + , paused = false + , ended = false + + stream.readable = true + stream.writable = false + + if(!Array.isArray(array)) + throw new Error('event-stream.read expects an array') + + stream.resume = function () { + if(ended) return + paused = false + var l = array.length + while(i < l && !paused && !ended) { + stream.emit('data', array[i++]) + } + if(i == l && !ended) + ended = true, stream.readable = false, stream.emit('end') + } + process.nextTick(stream.resume) + stream.pause = function () { + paused = true + } + stream.destroy = function () { + ended = true + stream.emit('close') + } + return stream +} + +// +// readable (asyncFunction) +// return a stream that calls an async function while the stream is not paused. +// +// the function must take: (count, callback) {... +// + +es.readable = +function (func, continueOnError) { + var stream = new Stream() + , i = 0 + , paused = false + , ended = false + , reading = false + + stream.readable = true + stream.writable = false + + if('function' !== typeof func) + throw new Error('event-stream.readable expects async function') + + stream.on('end', function () { ended = true }) + + function get (err, data) { + + if(err) { + stream.emit('error', err) + if(!continueOnError) stream.emit('end') + } else if (arguments.length > 1) + stream.emit('data', data) + + process.nextTick(function () { + if(ended || paused || reading) return + try { + reading = true + func.call(stream, i++, function () { + reading = false + get.apply(null, arguments) + }) + } catch (err) { + stream.emit('error', err) + } + }) + } + stream.resume = function () { + paused = false + get() + } + process.nextTick(get) + stream.pause = function () { + paused = true + } + stream.destroy = function () { + stream.emit('end') + stream.emit('close') + ended = true + } + return stream +} + + +// +// map sync +// + +es.mapSync = function (sync) { + return es.through(function write(data) { + var mappedData = sync(data) + if (typeof mappedData !== 'undefined') + this.emit('data', mappedData) + }) +} + +// +// log just print out what is coming through the stream, for debugging +// + +es.log = function (name) { + return es.through(function (data) { + var args = [].slice.call(arguments) + if(name) console.error(name, data) + else console.error(data) + this.emit('data', data) + }) +} + + +// +// child -- pipe through a child process +// + +es.child = function (child) { + + return es.duplex(child.stdin, child.stdout) + +} + +// +// parse +// +// must be used after es.split() to ensure that each chunk represents a line +// source.pipe(es.split()).pipe(es.parse()) + +es.parse = function () { + return es.through(function (data) { + var obj + try { + if(data) //ignore empty lines + obj = JSON.parse(data.toString()) + } catch (err) { + return console.error(err, 'attemping to parse:', data) + } + //ignore lines that where only whitespace. + if(obj !== undefined) + this.emit('data', obj) + }) +} +// +// stringify +// + +es.stringify = function () { + var Buffer = require('buffer').Buffer + return es.mapSync(function (e){ + return JSON.stringify(Buffer.isBuffer(e) ? e.toString() : e) + '\n' + }) +} + +// +// replace a string within a stream. +// +// warn: just concatenates the string and then does str.split().join(). +// probably not optimal. +// for smallish responses, who cares? +// I need this for shadow-npm so it's only relatively small json files. + +es.replace = function (from, to) { + return es.pipeline(es.split(from), es.join(to)) +} + +// +// join chunks with a joiner. just like Array#join +// also accepts a callback that is passed the chunks appended together +// this is still supported for legacy reasons. +// + +es.join = function (str) { + + //legacy api + if('function' === typeof str) + return es.wait(str) + + var first = true + return es.through(function (data) { + if(!first) + this.emit('data', str) + first = false + this.emit('data', data) + return true + }) +} + + +// +// wait. callback when 'end' is emitted, with all chunks appended as string. +// + +es.wait = function (callback) { + var body = '' + return es.through(function (data) { body += data }, + function () { + this.emit('data', body) + this.emit('end') + if(callback) callback(null, body) + }) +} + +es.pipeable = function () { + throw new Error('[EVENT-STREAM] es.pipeable is deprecated') +} diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/.npmignore b/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/.npmignore new file mode 100644 index 0000000..062c11e --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/.npmignore @@ -0,0 +1,3 @@ +node_modules +*.log +*.err \ No newline at end of file diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/.travis.yml b/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/.travis.yml new file mode 100644 index 0000000..c2ba3f9 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/.travis.yml @@ -0,0 +1,3 @@ +language: node_js +node_js: + - 0.8 \ No newline at end of file diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/LICENCE b/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/LICENCE new file mode 100644 index 0000000..a23e08a --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/LICENCE @@ -0,0 +1,19 @@ +Copyright (c) 2012 Raynos. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/Makefile b/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/Makefile new file mode 100644 index 0000000..1f8985d --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/Makefile @@ -0,0 +1,4 @@ +test: + node test.js + +.PHONY: test \ No newline at end of file diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/README.md b/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/README.md new file mode 100644 index 0000000..a24cecb --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/README.md @@ -0,0 +1,36 @@ +# duplexer [![build status][1]][2] + +Creates a duplex stream + +Taken from [event-stream][3] + +## duplex (writeStream, readStream) + +Takes a writable stream and a readable stream and makes them appear as a readable writable stream. + +It is assumed that the two streams are connected to each other in some way. + +## Example + + var grep = cp.exec('grep Stream') + + duplex(grep.stdin, grep.stdout) + +## Installation + +`npm install duplexer` + +## Tests + +`make test` + +## Contributors + + - Dominictarr + - Raynos + +## MIT Licenced + + [1]: https://secure.travis-ci.org/Raynos/duplexer.png + [2]: http://travis-ci.org/Raynos/duplexer + [3]: https://github.com/dominictarr/event-stream#duplex-writestream-readstream \ No newline at end of file diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/index.js b/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/index.js new file mode 100644 index 0000000..22e0d51 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/index.js @@ -0,0 +1,77 @@ +var Stream = require("stream") + , writeMethods = ["write", "end", "destroy"] + , readMethods = ["resume", "pause"] + , readEvents = ["data", "close"] + , slice = Array.prototype.slice + +module.exports = duplex + +function duplex(writer, reader) { + var stream = new Stream() + , ended = false + + writeMethods.forEach(proxyWriter) + + readMethods.forEach(proxyReader) + + readEvents.forEach(proxyStream) + + reader.on("end", handleEnd) + + writer.on("drain", function() { + stream.emit("drain") + }) + + writer.on("error", reemit) + reader.on("error", reemit) + + stream.writable = writer.writable + stream.readable = reader.readable + + return stream + + function proxyWriter(methodName) { + stream[methodName] = method + + function method() { + return writer[methodName].apply(writer, arguments) + } + } + + function proxyReader(methodName) { + stream[methodName] = method + + function method() { + stream.emit(methodName) + var func = reader[methodName] + if (func) { + return func.apply(reader, arguments) + } + reader.emit(methodName) + } + } + + function proxyStream(methodName) { + reader.on(methodName, reemit) + + function reemit() { + var args = slice.call(arguments) + args.unshift(methodName) + stream.emit.apply(stream, args) + } + } + + function handleEnd() { + if (ended) { + return + } + ended = true + var args = slice.call(arguments) + args.unshift("end") + stream.emit.apply(stream, args) + } + + function reemit(err) { + stream.emit("error", err) + } +} diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/package.json b/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/package.json new file mode 100644 index 0000000..b0cb5e3 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/package.json @@ -0,0 +1,44 @@ +{ + "name": "duplexer", + "version": "0.0.4", + "description": "Creates a duplex stream", + "keywords": [], + "author": { + "name": "Raynos", + "email": "raynos2@gmail.com" + }, + "repository": { + "type": "git", + "url": "git://github.com/Raynos/duplexer.git" + }, + "main": "index", + "homepage": "https://github.com/Raynos/duplexer", + "contributors": [ + { + "name": "Jake Verbaten" + } + ], + "bugs": { + "url": "https://github.com/Raynos/duplexer/issues", + "email": "raynos2@gmail.com" + }, + "dependencies": {}, + "devDependencies": { + "through": "~0.1.4" + }, + "licenses": [ + { + "type": "MIT", + "url": "http://github.com/Raynos/duplexer/raw/master/LICENSE" + } + ], + "scripts": { + "test": "make test" + }, + "readme": "# duplexer [![build status][1]][2]\n\nCreates a duplex stream\n\nTaken from [event-stream][3]\n\n## duplex (writeStream, readStream)\n\nTakes a writable stream and a readable stream and makes them appear as a readable writable stream.\n\nIt is assumed that the two streams are connected to each other in some way.\n\n## Example\n\n var grep = cp.exec('grep Stream')\n\n duplex(grep.stdin, grep.stdout)\n\n## Installation\n\n`npm install duplexer`\n\n## Tests\n\n`make test`\n\n## Contributors\n\n - Dominictarr\n - Raynos\n\n## MIT Licenced\n\n [1]: https://secure.travis-ci.org/Raynos/duplexer.png\n [2]: http://travis-ci.org/Raynos/duplexer\n [3]: https://github.com/dominictarr/event-stream#duplex-writestream-readstream", + "_id": "duplexer@0.0.4", + "dist": { + "shasum": "49e7655791cdb887f4ce1616756bbb5f65d96157" + }, + "_from": "duplexer@~0.0.2" +} diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/test.js b/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/test.js new file mode 100644 index 0000000..e06a864 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/duplexer/test.js @@ -0,0 +1,27 @@ +var duplex = require("./index") + , assert = require("assert") + , through = require("through") + +var readable = through() + , writable = through(write) + , written = 0 + , data = 0 + +var stream = duplex(writable, readable) + +function write() { + written++ +} + +stream.on("data", ondata) + +function ondata() { + data++ +} + +stream.write() +readable.emit("data") + +assert.equal(written, 1) +assert.equal(data, 1) +console.log("DONE") \ No newline at end of file diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/from/.npmignore b/node_modules/jsftp/node_modules/event-stream/node_modules/from/.npmignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/from/.npmignore @@ -0,0 +1 @@ +node_modules diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/from/LICENSE.APACHE2 b/node_modules/jsftp/node_modules/event-stream/node_modules/from/LICENSE.APACHE2 new file mode 100644 index 0000000..6366c04 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/from/LICENSE.APACHE2 @@ -0,0 +1,15 @@ +Apache License, Version 2.0 + +Copyright (c) 2011 Dominic Tarr + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/from/LICENSE.MIT b/node_modules/jsftp/node_modules/event-stream/node_modules/from/LICENSE.MIT new file mode 100644 index 0000000..6eafbd7 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/from/LICENSE.MIT @@ -0,0 +1,24 @@ +The MIT License + +Copyright (c) 2011 Dominic Tarr + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/from/index.js b/node_modules/jsftp/node_modules/event-stream/node_modules/from/index.js new file mode 100644 index 0000000..bba40da --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/from/index.js @@ -0,0 +1,68 @@ + +'use strict'; + +var Stream = require('stream') + +// from +// +// a stream that reads from an source. +// source may be an array, or a function. +// from handles pause behaviour for you. + +module.exports = +function from (source) { + if(Array.isArray(source)) { + source = source.slice() + return from (function (i) { + if(source.length) + this.emit('data', source.shift()) + else + this.emit('end') + return true + }) + } + var s = new Stream(), i = 0 + s.ended = false + s.started = false + s.readable = true + s.writable = false + s.paused = false + s.ended = false + s.pause = function () { + s.started = true + s.paused = true + } + function next () { + s.started = true + if(s.ended) return + while(!s.ended && !s.paused && source.call(s, i++, function () { + if(!s.ended && !s.paused) + next() + })) + ; + } + s.resume = function () { + s.started = true + s.paused = false + next() + } + s.on('end', function () { + s.ended = true + s.readable = false + process.nextTick(s.destroy) + }) + s.destroy = function () { + s.ended = true + s.emit('close') + } + /* + by default, the stream will start emitting at nextTick + if you want, you can pause it, after pipeing. + you can also resume before next tick, and that will also + work. + */ + process.nextTick(function () { + if(!s.started) s.resume() + }) + return s +} diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/from/package.json b/node_modules/jsftp/node_modules/event-stream/node_modules/from/package.json new file mode 100644 index 0000000..114327f --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/from/package.json @@ -0,0 +1,36 @@ +{ + "name": "from", + "version": "0.1.3", + "description": "Easy way to make a Readable Stream", + "main": "index.js", + "scripts": { + "test": "asynct test/*.js" + }, + "repository": { + "type": "git", + "url": "git://github.com/dominictarr/from.git" + }, + "keywords": [ + "stream", + "streams", + "readable", + "easy" + ], + "devDependencies": { + "asynct": "1", + "stream-spec": "0", + "assertions": "~2.3.0" + }, + "author": { + "name": "Dominic Tarr", + "email": "dominic.tarr@gmail.com", + "url": "dominictarr.com" + }, + "license": "MIT", + "readme": "# from\n\nAn easy way to create a `readable Stream`.\n\n## from(function getChunk(count, next))\n\nfrom takes a `getChunk` function and returns a stream. \n\n`getChunk` is called again and again, after each time the user calls `next()`, \nuntil the user emits `'end'`\n\nif `pause()` is called, the `getChunk` won't be called again untill `resume()` is called.\n\n\n```js\nvar from = require('from')\n\nvar stream = \n from(function getChunk(count, next) {\n //do some sort of data\n this.emit('data', whatever)\n \n if(itsOver)\n this.emit('end')\n\n //ready to handle the next chunk\n next()\n //or, if it's sync:\n return true \n })\n```\n\n## from(array)\n\nfrom also takes an `Array` whose elements it emits one after another.\n\n## License\nMIT / Apache2\n", + "_id": "from@0.1.3", + "dist": { + "shasum": "0ac5c9c9018c3f249f72e92396f3e5f25aa6fdbd" + }, + "_from": "from@~0" +} diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/from/readme.markdown b/node_modules/jsftp/node_modules/event-stream/node_modules/from/readme.markdown new file mode 100644 index 0000000..a619163 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/from/readme.markdown @@ -0,0 +1,38 @@ +# from + +An easy way to create a `readable Stream`. + +## from(function getChunk(count, next)) + +from takes a `getChunk` function and returns a stream. + +`getChunk` is called again and again, after each time the user calls `next()`, +until the user emits `'end'` + +if `pause()` is called, the `getChunk` won't be called again untill `resume()` is called. + + +```js +var from = require('from') + +var stream = + from(function getChunk(count, next) { + //do some sort of data + this.emit('data', whatever) + + if(itsOver) + this.emit('end') + + //ready to handle the next chunk + next() + //or, if it's sync: + return true + }) +``` + +## from(array) + +from also takes an `Array` whose elements it emits one after another. + +## License +MIT / Apache2 diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/from/test/index.js b/node_modules/jsftp/node_modules/event-stream/node_modules/from/test/index.js new file mode 100644 index 0000000..fafea57 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/from/test/index.js @@ -0,0 +1,141 @@ +var from = require('..') +var spec = require('stream-spec') +var a = require('assertions') + +function read(stream, callback) { + var actual = [] + stream.on('data', function (data) { + actual.push(data) + }) + stream.once('end', function () { + callback(null, actual) + }) + stream.once('error', function (err) { + callback(err) + }) +} + +function pause(stream) { + stream.on('data', function () { + if(Math.random() > 0.1) return + stream.pause() + process.nextTick(function () { + stream.resume() + }) + }) +} + +exports['inc'] = function (test) { + + var fs = from(function (i) { + this.emit('data', i) + if(i >= 99) + return this.emit('end') + return true + }) + + spec(fs).readable().validateOnExit() + + read(fs, function (err, arr) { + test.equal(arr.length, 100) + test.done() + }) +} + +exports['simple'] = function (test) { + + var l = 1000 + , expected = [] + + while(l--) expected.push(l * Math.random()) + + var t = from(expected.slice()) + + spec(t) + .readable() + .pausable({strict: true}) + .validateOnExit() + + read(t, function (err, actual) { + if(err) test.error(err) //fail + a.deepEqual(actual, expected) + test.done() + }) + +} + +exports['simple pausable'] = function (test) { + + var l = 1000 + , expected = [] + + while(l--) expected.push(l * Math.random()) + + var t = from(expected.slice()) + + spec(t) + .readable() + .pausable({strict: true}) + .validateOnExit() + + pause(t) + + read(t, function (err, actual) { + if(err) test.error(err) //fail + a.deepEqual(actual, expected) + test.done() + }) + +} + +exports['simple (not strictly pausable) setTimeout'] = function (test) { + + var l = 10 + , expected = [] + while(l--) expected.push(l * Math.random()) + + + var _expected = expected.slice() + var t = from(function (i, n) { + var self = this + setTimeout(function () { + if(_expected.length) + self.emit('data', _expected.shift()) + else + self.emit('end') + n() + }, 3) + }) + + /* + using from in this way will not be strictly pausable. + it could be extended to buffer outputs, but I think a better + way would be to use a PauseStream that implements strict pause. + */ + + spec(t) + .readable() + .pausable({strict: false }) + .validateOnExit() + + //pause(t) + var paused = false + var i = setInterval(function () { + if(!paused) t.pause() + else t.resume() + paused = !paused + }, 2) + + t.on('end', function () { + clearInterval(i) + }) + + read(t, function (err, actual) { + if(err) test.error(err) //fail + a.deepEqual(actual, expected) + test.done() + }) + +} + + diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/.npmignore b/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/.npmignore new file mode 100644 index 0000000..13abef4 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/.npmignore @@ -0,0 +1,3 @@ +node_modules +node_modules/* +npm_debug.log diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/.travis.yml b/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/.travis.yml new file mode 100644 index 0000000..895dbd3 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.6 + - 0.8 diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/LICENCE b/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/LICENCE new file mode 100644 index 0000000..171dd97 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/LICENCE @@ -0,0 +1,22 @@ +Copyright (c) 2011 Dominic Tarr + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/examples/pretty.js b/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/examples/pretty.js new file mode 100644 index 0000000..ab07398 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/examples/pretty.js @@ -0,0 +1,26 @@ + +var inspect = require('util').inspect + +if(!module.parent) { + var map = require('..') //load map-stream + var es = require('event-stream') //load event-stream + es.pipe( //pipe joins streams together + process.openStdin(), //open stdin + es.split(), //split stream to break on newlines + map(function (data, callback) { //turn this async function into a stream + var j + try { + j = JSON.parse(data) //try to parse input into json + } catch (err) { + return callback(null, data) //if it fails just pass it anyway + } + callback(null, inspect(j)) //render it nicely + }), + process.stdout // pipe it to stdout ! + ) + } + +// run this +// +// curl -sS registry.npmjs.org/event-stream | node pretty.js +// diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/index.js b/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/index.js new file mode 100644 index 0000000..d3a7fd9 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/index.js @@ -0,0 +1,105 @@ +//filter will reemit the data if cb(err,pass) pass is truthy + +// reduce is more tricky +// maybe we want to group the reductions or emit progress updates occasionally +// the most basic reduce just emits one 'data' event after it has recieved 'end' + + +var Stream = require('stream').Stream + + +//create an event stream and apply function to each .write +//emitting each response as data +//unless it's an empty callback + +module.exports = function (mapper) { + var stream = new Stream() + , inputs = 0 + , outputs = 0 + , ended = false + , paused = false + , destroyed = false + + stream.writable = true + stream.readable = true + + stream.write = function () { + if(ended) throw new Error('map stream is not writable') + inputs ++ + var args = [].slice.call(arguments) + , r + , inNext = false + //pipe only allows one argument. so, do not + function next (err) { + if(destroyed) return + inNext = true + outputs ++ + var args = [].slice.call(arguments) + if(err) { + args.unshift('error') + return inNext = false, stream.emit.apply(stream, args) + } + args.shift() //drop err + if (args.length) { + args.unshift('data') + r = stream.emit.apply(stream, args) + } + if(inputs == outputs) { + if(paused) paused = false, stream.emit('drain') //written all the incoming events + if(ended) end() + } + inNext = false + } + args.push(next) + + try { + //catch sync errors and handle them like async errors + var written = mapper.apply(null, args) + paused = (written === false) + return !paused + } catch (err) { + //if the callback has been called syncronously, and the error + //has occured in an listener, throw it again. + if(inNext) + throw err + next(err) + return !paused + } + } + + function end (data) { + //if end was called with args, write it, + ended = true //write will emit 'end' if ended is true + stream.writable = false + if(data !== undefined) + return stream.write(data) + else if (inputs == outputs) //wait for processing + stream.readable = false, stream.emit('end'), stream.destroy() + } + + stream.end = function (data) { + if(ended) return + end() + } + + stream.destroy = function () { + ended = destroyed = true + stream.writable = stream.readable = paused = false + process.nextTick(function () { + stream.emit('close') + }) + } + stream.pause = function () { + paused = true + } + + stream.resume = function () { + paused = false + } + + return stream +} + + + + diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/package.json b/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/package.json new file mode 100644 index 0000000..90bdd63 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/package.json @@ -0,0 +1,33 @@ +{ + "name": "map-stream", + "version": "0.0.2", + "description": "construct pipes of streams of events", + "homepage": "http://github.com/dominictarr/map-stream", + "repository": { + "type": "git", + "url": "git://github.com/dominictarr/map-stream.git" + }, + "dependencies": {}, + "devDependencies": { + "asynct": "*", + "it-is": "1", + "ubelt": "~2.9", + "stream-spec": "~0.2", + "event-stream": "~2.1", + "from": "0.0.2" + }, + "scripts": { + "test": "asynct test/" + }, + "author": { + "name": "Dominic Tarr", + "email": "dominic.tarr@gmail.com", + "url": "http://dominictarr.com" + }, + "readme": "# MapStream\n\nRefactored out of [event-stream](https://github.com/dominictarr/event-stream)\n\n##map (asyncFunction)\n\nCreate a through stream from an asyncronous function. \n\n``` js\nvar map = require('map-stream')\n\nmap(function (data, callback) {\n //transform data\n // ...\n callback(null, data)\n})\n\n```\n\nEach map MUST call the callback. It may callback with data, with an error or with no arguments, \n\n * `callback()` drop this data. \n this makes the map work like `filter`, \n note:`callback(null,null)` is not the same, and will emit `null`\n\n * `callback(null, newData)` turn data into newData\n \n * `callback(error)` emit an error for this item.\n\n>Note: if a callback is not called, `map` will think that it is still being processed, \n>every call must be answered or the stream will not know when to end. \n>\n>Also, if the callback is called more than once, every call but the first will be ignored.\n\n\n", + "_id": "map-stream@0.0.2", + "dist": { + "shasum": "8c7fc337e11ea95e1e6edc5b18b7e4a2944e2f0f" + }, + "_from": "map-stream@0.0.2" +} diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/readme.markdown b/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/readme.markdown new file mode 100644 index 0000000..7e54504 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/readme.markdown @@ -0,0 +1,35 @@ +# MapStream + +Refactored out of [event-stream](https://github.com/dominictarr/event-stream) + +##map (asyncFunction) + +Create a through stream from an asyncronous function. + +``` js +var map = require('map-stream') + +map(function (data, callback) { + //transform data + // ... + callback(null, data) +}) + +``` + +Each map MUST call the callback. It may callback with data, with an error or with no arguments, + + * `callback()` drop this data. + this makes the map work like `filter`, + note:`callback(null,null)` is not the same, and will emit `null` + + * `callback(null, newData)` turn data into newData + + * `callback(error)` emit an error for this item. + +>Note: if a callback is not called, `map` will think that it is still being processed, +>every call must be answered or the stream will not know when to end. +> +>Also, if the callback is called more than once, every call but the first will be ignored. + + diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/test/simple-map.asynct.js b/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/test/simple-map.asynct.js new file mode 100644 index 0000000..b04d953 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/map-stream/test/simple-map.asynct.js @@ -0,0 +1,270 @@ +'use strict'; + +var map = require('../') + , it = require('it-is') + , u = require('ubelt') + , spec = require('stream-spec') + , from = require('from') + , Stream = require('stream') + , es = require('event-stream') + +//REFACTOR THIS TEST TO USE es.readArray and es.writeArray + +function writeArray(array, stream) { + + array.forEach( function (j) { + stream.write(j) + }) + stream.end() + +} + +function readStream(stream, done) { + + var array = [] + stream.on('data', function (data) { + array.push(data) + }) + stream.on('error', done) + stream.on('end', function (data) { + done(null, array) + }) + +} + +//call sink on each write, +//and complete when finished. + +function pauseStream (prob, delay) { + var pauseIf = ( + 'number' == typeof prob + ? function () { + return Math.random() < prob + } + : 'function' == typeof prob + ? prob + : 0.1 + ) + var delayer = ( + !delay + ? process.nextTick + : 'number' == typeof delay + ? function (next) { setTimeout(next, delay) } + : delay + ) + + return es.through(function (data) { + if(!this.paused && pauseIf()) { + console.log('PAUSE STREAM PAUSING') + this.pause() + var self = this + delayer(function () { + console.log('PAUSE STREAM RESUMING') + self.resume() + }) + } + console.log("emit ('data', " + data + ')') + this.emit('data', data) + }) +} + +exports ['simple map applied to a stream'] = function (test) { + + var input = [1,2,3,7,5,3,1,9,0,2,4,6] + //create event stream from + + var doubler = map(function (data, cb) { + cb(null, data * 2) + }) + + spec(doubler).through().validateOnExit() + + //a map is only a middle man, so it is both readable and writable + + it(doubler).has({ + readable: true, + writable: true, + }) + + readStream(doubler, function (err, output) { + it(output).deepEqual(input.map(function (j) { + return j * 2 + })) +// process.nextTick(x.validate) + test.done() + }) + + writeArray(input, doubler) + +} + +exports['pipe two maps together'] = function (test) { + + var input = [1,2,3,7,5,3,1,9,0,2,4,6] + //create event stream from + function dd (data, cb) { + cb(null, data * 2) + } + var doubler1 = map(dd), doubler2 = map(dd) + + doubler1.pipe(doubler2) + + spec(doubler1).through().validateOnExit() + spec(doubler2).through().validateOnExit() + + readStream(doubler2, function (err, output) { + it(output).deepEqual(input.map(function (j) { + return j * 4 + })) + test.done() + }) + + writeArray(input, doubler1) + +} + +//next: +// +// test pause, resume and drian. +// + +// then make a pipe joiner: +// +// plumber (evStr1, evStr2, evStr3, evStr4, evStr5) +// +// will return a single stream that write goes to the first + +exports ['map will not call end until the callback'] = function (test) { + + var ticker = map(function (data, cb) { + process.nextTick(function () { + cb(null, data * 2) + }) + }) + + spec(ticker).through().validateOnExit() + + ticker.write('x') + ticker.end() + + ticker.on('end', function () { + test.done() + }) +} + + +exports ['emit error thrown'] = function (test) { + + var err = new Error('INTENSIONAL ERROR') + , mapper = + map(function () { + throw err + }) + + mapper.on('error', function (_err) { + it(_err).equal(err) + test.done() + }) + + mapper.write('hello') + +} + +exports ['emit error calledback'] = function (test) { + + var err = new Error('INTENSIONAL ERROR') + , mapper = + map(function (data, callback) { + callback(err) + }) + + mapper.on('error', function (_err) { + it(_err).equal(err) + test.done() + }) + + mapper.write('hello') + +} + +exports ['do not emit drain if not paused'] = function (test) { + + var maps = map(function (data, callback) { + u.delay(callback)(null, 1) + return true + }) + + spec(maps).through().pausable().validateOnExit() + + maps.on('drain', function () { + it(false).ok('should not emit drain unless the stream is paused') + }) + + it(maps.write('hello')).equal(true) + it(maps.write('hello')).equal(true) + it(maps.write('hello')).equal(true) + setTimeout(function () {maps.end()},10) + maps.on('end', test.done) +} + +exports ['emits drain if paused, when all '] = function (test) { + var active = 0 + var drained = false + var maps = map(function (data, callback) { + active ++ + u.delay(function () { + active -- + callback(null, 1) + })() + console.log('WRITE', false) + return false + }) + + spec(maps).through().validateOnExit() + + maps.on('drain', function () { + drained = true + it(active).equal(0, 'should emit drain when all maps are done') + }) + + it(maps.write('hello')).equal(false) + it(maps.write('hello')).equal(false) + it(maps.write('hello')).equal(false) + + process.nextTick(function () {maps.end()},10) + + maps.on('end', function () { + console.log('end') + it(drained).ok('shoud have emitted drain before end') + test.done() + }) + +} + +exports ['map applied to a stream with filtering'] = function (test) { + + var input = [1,2,3,7,5,3,1,9,0,2,4,6] + + var doubler = map(function (data, callback) { + if (data % 2) + callback(null, data * 2) + else + callback() + }) + + readStream(doubler, function (err, output) { + it(output).deepEqual(input.filter(function (j) { + return j % 2 + }).map(function (j) { + return j * 2 + })) + test.done() + }) + + spec(doubler).through().validateOnExit() + + writeArray(input, doubler) + +} + + diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/pause-stream/.npmignore b/node_modules/jsftp/node_modules/event-stream/node_modules/pause-stream/.npmignore new file mode 100644 index 0000000..13abef4 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/pause-stream/.npmignore @@ -0,0 +1,3 @@ +node_modules +node_modules/* +npm_debug.log diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/pause-stream/index.js b/node_modules/jsftp/node_modules/event-stream/node_modules/pause-stream/index.js new file mode 100644 index 0000000..0e0bf96 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/pause-stream/index.js @@ -0,0 +1,3 @@ +//through@2 handles this by default! +module.exports = require('through') + diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/pause-stream/package.json b/node_modules/jsftp/node_modules/event-stream/node_modules/pause-stream/package.json new file mode 100644 index 0000000..cc11408 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/pause-stream/package.json @@ -0,0 +1,45 @@ +{ + "name": "pause-stream", + "version": "0.0.10", + "description": "a ThroughStream that strictly buffers all readable events when paused.", + "main": "index.js", + "directories": { + "test": "test" + }, + "devDependencies": { + "stream-tester": "0.0.2", + "stream-spec": "~0.2.0" + }, + "scripts": { + "test": "node test/index.js && node test/pause-end.js" + }, + "repository": { + "type": "git", + "url": "git://github.com/dominictarr/pause-stream.git" + }, + "keywords": [ + "stream", + "pipe", + "pause", + "drain", + "buffer" + ], + "author": { + "name": "Dominic Tarr", + "email": "dominic.tarr@gmail.com", + "url": "dominictarr.com" + }, + "license": [ + "MIT", + "Apache2" + ], + "dependencies": { + "through": "~2.3" + }, + "readme": "# PauseStream\n\nThis is a `Stream` that will strictly buffer when paused.\nConnect it to anything you need buffered.\n\n``` js\n var ps = require('pause-stream')();\n\n badlyBehavedStream.pipe(ps.pause())\n\n aLittleLater(function (err, data) {\n ps.pipe(createAnotherStream(data))\n ps.resume()\n })\n```\n\n`PauseStream` will buffer whenever paused.\nit will buffer when yau have called `pause` manually.\nbut also when it's downstream `dest.write()===false`.\nit will attempt to drain the buffer when you call resume\nor the downstream emits `'drain'`\n\n`PauseStream` is tested using [stream-spec](https://github.com/dominictarr/stream-spec)\nand [stream-tester](https://github.com/dominictarr/stream-tester)\n\nThis is now the default case of \n[through](https://github.com/dominictarr/through)\n\nhttps://github.com/dominictarr/pause-stream/commit/4a6fe3dc2c11091b1efbfde912e0473719ed9cc0\n", + "_id": "pause-stream@0.0.10", + "dist": { + "shasum": "0b28365a705bb18fecd89d4722e11275c109eb66" + }, + "_from": "pause-stream@0.0.10" +} diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/pause-stream/readme.markdown b/node_modules/jsftp/node_modules/event-stream/node_modules/pause-stream/readme.markdown new file mode 100644 index 0000000..2366939 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/pause-stream/readme.markdown @@ -0,0 +1,29 @@ +# PauseStream + +This is a `Stream` that will strictly buffer when paused. +Connect it to anything you need buffered. + +``` js + var ps = require('pause-stream')(); + + badlyBehavedStream.pipe(ps.pause()) + + aLittleLater(function (err, data) { + ps.pipe(createAnotherStream(data)) + ps.resume() + }) +``` + +`PauseStream` will buffer whenever paused. +it will buffer when yau have called `pause` manually. +but also when it's downstream `dest.write()===false`. +it will attempt to drain the buffer when you call resume +or the downstream emits `'drain'` + +`PauseStream` is tested using [stream-spec](https://github.com/dominictarr/stream-spec) +and [stream-tester](https://github.com/dominictarr/stream-tester) + +This is now the default case of +[through](https://github.com/dominictarr/through) + +https://github.com/dominictarr/pause-stream/commit/4a6fe3dc2c11091b1efbfde912e0473719ed9cc0 diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/pause-stream/test/index.js b/node_modules/jsftp/node_modules/event-stream/node_modules/pause-stream/test/index.js new file mode 100644 index 0000000..db8778d --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/pause-stream/test/index.js @@ -0,0 +1,17 @@ +var spec = require('stream-spec') +var tester = require('stream-tester') +var ps = require('..')() + +spec(ps) + .through({strict: false}) + .validateOnExit() + +var master = tester.createConsistent + +tester.createRandomStream(1000) //1k random numbers + .pipe(master = tester.createConsistentStream()) + .pipe(tester.createUnpauseStream()) + .pipe(ps) + .pipe(tester.createPauseStream()) + .pipe(master.createSlave()) + diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/pause-stream/test/pause-end.js b/node_modules/jsftp/node_modules/event-stream/node_modules/pause-stream/test/pause-end.js new file mode 100644 index 0000000..a6c27ef --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/pause-stream/test/pause-end.js @@ -0,0 +1,33 @@ + +var pause = require('..') +var assert = require('assert') + +var ps = pause() +var read = [], ended = false + +ps.on('data', function (i) { + read.push(i) +}) + +ps.on('end', function () { + ended = true +}) + +assert.deepEqual(read, []) + +ps.write(0) +ps.write(1) +ps.write(2) + +assert.deepEqual(read, [0, 1, 2]) + +ps.pause() + +assert.deepEqual(read, [0, 1, 2]) + +ps.end() +assert.equal(ended, false) +ps.resume() +assert.equal(ended, true) + + diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/split/.npmignore b/node_modules/jsftp/node_modules/event-stream/node_modules/split/.npmignore new file mode 100644 index 0000000..13abef4 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/split/.npmignore @@ -0,0 +1,3 @@ +node_modules +node_modules/* +npm_debug.log diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/split/.travis.yml b/node_modules/jsftp/node_modules/event-stream/node_modules/split/.travis.yml new file mode 100644 index 0000000..dad2273 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/split/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.8 + - "0.10" diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/split/LICENCE b/node_modules/jsftp/node_modules/event-stream/node_modules/split/LICENCE new file mode 100644 index 0000000..171dd97 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/split/LICENCE @@ -0,0 +1,22 @@ +Copyright (c) 2011 Dominic Tarr + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/split/examples/pretty.js b/node_modules/jsftp/node_modules/event-stream/node_modules/split/examples/pretty.js new file mode 100644 index 0000000..2e89131 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/split/examples/pretty.js @@ -0,0 +1,26 @@ + +var inspect = require('util').inspect +var es = require('event-stream') //load event-stream +var split = require('../') + +if(!module.parent) { + es.pipe( //pipe joins streams together + process.openStdin(), //open stdin + split(), //split stream to break on newlines + es.map(function (data, callback) {//turn this async function into a stream + var j + try { + j = JSON.parse(data) //try to parse input into json + } catch (err) { + return callback(null, data) //if it fails just pass it anyway + } + callback(null, inspect(j)) //render it nicely + }), + process.stdout // pipe it to stdout ! + ) + } + +// run this +// +// curl -sS registry.npmjs.org/event-stream | node pretty.js +// diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/split/index.js b/node_modules/jsftp/node_modules/event-stream/node_modules/split/index.js new file mode 100644 index 0000000..ca57e0f --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/split/index.js @@ -0,0 +1,59 @@ +//filter will reemit the data if cb(err,pass) pass is truthy + +// reduce is more tricky +// maybe we want to group the reductions or emit progress updates occasionally +// the most basic reduce just emits one 'data' event after it has recieved 'end' + + +var through = require('through') +var Decoder = require('string_decoder').StringDecoder + +module.exports = split + +//TODO pass in a function to map across the lines. + +function split (matcher, mapper) { + var decoder = new Decoder() + var soFar = '' + if('function' === typeof matcher) + mapper = matcher, matcher = null + if (!matcher) + matcher = /\r?\n/ + + function emit(stream, piece) { + if(mapper) { + try { + piece = mapper(piece) + } + catch (err) { + return stream.emit('error', err) + } + if('undefined' !== typeof piece) + stream.queue(piece) + } + else + stream.queue(piece) + } + + function next (stream, buffer) { + var pieces = (soFar + buffer).split(matcher) + soFar = pieces.pop() + + for (var i = 0; i < pieces.length; i++) { + var piece = pieces[i] + emit(stream, piece) + } + } + + return through(function (b) { + next(this, decoder.write(b)) + }, + function () { + if(decoder.end) + next(this, decoder.end()) + if(soFar != null) + emit(this, soFar) + this.queue(null) + }) +} + diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/split/package.json b/node_modules/jsftp/node_modules/event-stream/node_modules/split/package.json new file mode 100644 index 0000000..55a77ee --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/split/package.json @@ -0,0 +1,38 @@ +{ + "name": "split", + "version": "0.2.10", + "description": "split a Text Stream into a Line Stream", + "homepage": "http://github.com/dominictarr/split", + "repository": { + "type": "git", + "url": "git://github.com/dominictarr/split.git" + }, + "dependencies": { + "through": "2" + }, + "devDependencies": { + "asynct": "*", + "it-is": "1", + "ubelt": "~2.9", + "stream-spec": "~0.2", + "event-stream": "~3.0.2" + }, + "scripts": { + "test": "asynct test/" + }, + "author": { + "name": "Dominic Tarr", + "email": "dominic.tarr@gmail.com", + "url": "http://bit.ly/dominictarr" + }, + "optionalDependencies": {}, + "engines": { + "node": "*" + }, + "readme": "# Split (matcher)\n\n[![build status](https://secure.travis-ci.org/dominictarr/split.png)](http://travis-ci.org/dominictarr/split)\n\nBreak up a stream and reassemble it so that each line is a chunk. matcher may be a `String`, or a `RegExp` \n\nExample, read every line in a file ...\n\n``` js\n fs.createReadStream(file)\n .pipe(split())\n .on('data', function (line) {\n //each chunk now is a seperate line!\n })\n\n```\n\n`split` takes the same arguments as `string.split` except it defaults to '/\\r?\\n/' instead of ',', and the optional `limit` paremeter is ignored.\n[String#split](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/split)\n\n# NDJ - Newline Delimited Json\n\n`split` accepts a function which transforms each line.\n\n``` js\nfs.createReadStream(file)\n .pipe(split(JSON.parse))\n .on('data', function (obj) {\n //each chunk now is a a js object\n })\n .on('error', function (err) {\n //syntax errors will land here\n //note, this ends the stream.\n })\n```\n\n# License\n\nMIT\n", + "_id": "split@0.2.10", + "dist": { + "shasum": "f3df4e839e7f9dc513d5463b1c8ad95d4bf75e85" + }, + "_from": "split@0.2" +} diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/split/readme.markdown b/node_modules/jsftp/node_modules/event-stream/node_modules/split/readme.markdown new file mode 100644 index 0000000..55b8ba8 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/split/readme.markdown @@ -0,0 +1,39 @@ +# Split (matcher) + +[![build status](https://secure.travis-ci.org/dominictarr/split.png)](http://travis-ci.org/dominictarr/split) + +Break up a stream and reassemble it so that each line is a chunk. matcher may be a `String`, or a `RegExp` + +Example, read every line in a file ... + +``` js + fs.createReadStream(file) + .pipe(split()) + .on('data', function (line) { + //each chunk now is a seperate line! + }) + +``` + +`split` takes the same arguments as `string.split` except it defaults to '/\r?\n/' instead of ',', and the optional `limit` paremeter is ignored. +[String#split](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/split) + +# NDJ - Newline Delimited Json + +`split` accepts a function which transforms each line. + +``` js +fs.createReadStream(file) + .pipe(split(JSON.parse)) + .on('data', function (obj) { + //each chunk now is a a js object + }) + .on('error', function (err) { + //syntax errors will land here + //note, this ends the stream. + }) +``` + +# License + +MIT diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/split/test/partitioned_unicode.js b/node_modules/jsftp/node_modules/event-stream/node_modules/split/test/partitioned_unicode.js new file mode 100644 index 0000000..aff3d5d --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/split/test/partitioned_unicode.js @@ -0,0 +1,34 @@ +var it = require('it-is').style('colour') + , split = require('..') + +exports ['split data with partitioned unicode character'] = function (test) { + var s = split(/,/g) + , caughtError = false + , rows = [] + + s.on('error', function (err) { + caughtError = true + }) + + s.on('data', function (row) { rows.push(row) }) + + var x = 'テスト試験今日とても,よい天気で' + unicodeData = new Buffer(x); + + // partition of 日 + piece1 = unicodeData.slice(0, 20); + piece2 = unicodeData.slice(20, unicodeData.length); + + s.write(piece1); + s.write(piece2); + + s.end() + + it(caughtError).equal(false) + + it(rows).deepEqual(['テスト試験今日とても', 'よい天気で']); + + it(rows).deepEqual(x.split(',')) + + test.done() +} diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/split/test/split.asynct.js b/node_modules/jsftp/node_modules/event-stream/node_modules/split/test/split.asynct.js new file mode 100644 index 0000000..fb15b28 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/split/test/split.asynct.js @@ -0,0 +1,85 @@ +var es = require('event-stream') + , it = require('it-is').style('colour') + , d = require('ubelt') + , split = require('..') + , join = require('path').join + , fs = require('fs') + , Stream = require('stream').Stream + , spec = require('stream-spec') + +exports ['split() works like String#split'] = function (test) { + var readme = join(__filename) + , expected = fs.readFileSync(readme, 'utf-8').split('\n') + , cs = split() + , actual = [] + , ended = false + , x = spec(cs).through() + + var a = new Stream () + + a.write = function (l) { + actual.push(l.trim()) + } + a.end = function () { + + ended = true + expected.forEach(function (v,k) { + //String.split will append an empty string '' + //if the string ends in a split pattern. + //es.split doesn't which was breaking this test. + //clearly, appending the empty string is correct. + //tests are passing though. which is the current job. + if(v) + it(actual[k]).like(v) + }) + //give the stream time to close + process.nextTick(function () { + test.done() + x.validate() + }) + } + a.writable = true + + fs.createReadStream(readme, {flags: 'r'}).pipe(cs) + cs.pipe(a) + +} + +exports ['split() takes mapper function'] = function (test) { + var readme = join(__filename) + , expected = fs.readFileSync(readme, 'utf-8').split('\n') + , cs = split(function (line) { return line.toUpperCase() }) + , actual = [] + , ended = false + , x = spec(cs).through() + + var a = new Stream () + + a.write = function (l) { + actual.push(l.trim()) + } + a.end = function () { + + ended = true + expected.forEach(function (v,k) { + //String.split will append an empty string '' + //if the string ends in a split pattern. + //es.split doesn't which was breaking this test. + //clearly, appending the empty string is correct. + //tests are passing though. which is the current job. + if(v) + it(actual[k]).equal(v.trim().toUpperCase()) + }) + //give the stream time to close + process.nextTick(function () { + test.done() + x.validate() + }) + } + a.writable = true + + fs.createReadStream(readme, {flags: 'r'}).pipe(cs) + cs.pipe(a) + +} + diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/split/test/try_catch.asynct.js b/node_modules/jsftp/node_modules/event-stream/node_modules/split/test/try_catch.asynct.js new file mode 100644 index 0000000..39e49f7 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/split/test/try_catch.asynct.js @@ -0,0 +1,51 @@ +var it = require('it-is').style('colour') + , split = require('..') + +exports ['emit mapper exceptions as error events'] = function (test) { + var s = split(JSON.parse) + , caughtError = false + , rows = [] + + s.on('error', function (err) { + caughtError = true + }) + + s.on('data', function (row) { rows.push(row) }) + + s.write('{"a":1}\n{"') + it(caughtError).equal(false) + it(rows).deepEqual([ { a: 1 } ]) + + s.write('b":2}\n{"c":}\n') + it(caughtError).equal(true) + it(rows).deepEqual([ { a: 1 }, { b: 2 } ]) + + s.end() + test.done() +} + +exports ['mapper error events on trailing chunks'] = function (test) { + var s = split(JSON.parse) + , caughtError = false + , rows = [] + + s.on('error', function (err) { + caughtError = true + }) + + s.on('data', function (row) { rows.push(row) }) + + s.write('{"a":1}\n{"') + it(caughtError).equal(false) + it(rows).deepEqual([ { a: 1 } ]) + + s.write('b":2}\n{"c":}') + it(caughtError).equal(false) + it(rows).deepEqual([ { a: 1 }, { b: 2 } ]) + + s.end() + it(caughtError).equal(true) + it(rows).deepEqual([ { a: 1 }, { b: 2 } ]) + + test.done() +} diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/.npmignore b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/.npmignore new file mode 100644 index 0000000..13abef4 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/.npmignore @@ -0,0 +1,3 @@ +node_modules +node_modules/* +npm_debug.log diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/.travis.yml b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/.travis.yml new file mode 100644 index 0000000..895dbd3 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.6 + - 0.8 diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/LICENSE b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/LICENSE new file mode 100644 index 0000000..6d03581 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2012 'Dominic Tarr' + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/README.md b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/README.md new file mode 100644 index 0000000..d0c129c --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/README.md @@ -0,0 +1,29 @@ +# stream-combiner + + + +## pipeline (stream1,...,streamN) + +Turn a pipeline into a single stream. `pipeline` returns a stream that writes to the first stream +and reads from the last stream. + +Listening for 'error' will recieve errors from all streams inside the pipe. + +> `connect` is an alias for `pipeline`. + +``` js + + es.pipeline( //connect streams together with `pipe` + process.openStdin(), //open stdin + es.split(), //split stream to break on newlines + es.map(function (data, callback) {//turn this async function into a stream + callback(null + , inspect(JSON.parse(data))) //render it nicely + }), + process.stdout // pipe it to stdout ! + ) +``` + +## License + +MIT diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/index.js b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/index.js new file mode 100644 index 0000000..99cf5b6 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/index.js @@ -0,0 +1,39 @@ +var duplexer = require('duplexer') + +module.exports = function () { + + var streams = [].slice.call(arguments) + , first = streams[0] + , last = streams[streams.length - 1] + , thepipe = duplexer(first, last) + + if(streams.length == 1) + return streams[0] + else if (!streams.length) + throw new Error('connect called with empty args') + + //pipe all the streams together + + function recurse (streams) { + if(streams.length < 2) + return + streams[0].pipe(streams[1]) + recurse(streams.slice(1)) + } + + recurse(streams) + + function onerror () { + var args = [].slice.call(arguments) + args.unshift('error') + thepipe.emit.apply(thepipe, args) + } + + //es.duplex already reemits the error from the first and last stream. + //add a listener for the inner streams in the pipeline. + for(var i = 1; i < streams.length - 1; i ++) + streams[i].on('error', onerror) + + return thepipe +} + diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/.npmignore b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/.npmignore new file mode 100644 index 0000000..062c11e --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/.npmignore @@ -0,0 +1,3 @@ +node_modules +*.log +*.err \ No newline at end of file diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/.travis.yml b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/.travis.yml new file mode 100644 index 0000000..c2ba3f9 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/.travis.yml @@ -0,0 +1,3 @@ +language: node_js +node_js: + - 0.8 \ No newline at end of file diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/LICENCE b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/LICENCE new file mode 100644 index 0000000..a23e08a --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/LICENCE @@ -0,0 +1,19 @@ +Copyright (c) 2012 Raynos. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/Makefile b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/Makefile new file mode 100644 index 0000000..1f8985d --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/Makefile @@ -0,0 +1,4 @@ +test: + node test.js + +.PHONY: test \ No newline at end of file diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/README.md b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/README.md new file mode 100644 index 0000000..a24cecb --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/README.md @@ -0,0 +1,36 @@ +# duplexer [![build status][1]][2] + +Creates a duplex stream + +Taken from [event-stream][3] + +## duplex (writeStream, readStream) + +Takes a writable stream and a readable stream and makes them appear as a readable writable stream. + +It is assumed that the two streams are connected to each other in some way. + +## Example + + var grep = cp.exec('grep Stream') + + duplex(grep.stdin, grep.stdout) + +## Installation + +`npm install duplexer` + +## Tests + +`make test` + +## Contributors + + - Dominictarr + - Raynos + +## MIT Licenced + + [1]: https://secure.travis-ci.org/Raynos/duplexer.png + [2]: http://travis-ci.org/Raynos/duplexer + [3]: https://github.com/dominictarr/event-stream#duplex-writestream-readstream \ No newline at end of file diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/index.js b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/index.js new file mode 100644 index 0000000..fee581d --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/index.js @@ -0,0 +1,87 @@ +var Stream = require("stream") + , writeMethods = ["write", "end", "destroy"] + , readMethods = ["resume", "pause"] + , readEvents = ["data", "close"] + , slice = Array.prototype.slice + +module.exports = duplex + +function duplex(writer, reader) { + var stream = new Stream() + , ended = false + + Object.defineProperties(stream, { + writable: { + get: getWritable + } + , readable: { + get: getReadable + } + }) + + writeMethods.forEach(proxyWriter) + + readMethods.forEach(proxyReader) + + readEvents.forEach(proxyStream) + + reader.on("end", handleEnd) + + writer.on("error", reemit) + reader.on("error", reemit) + + return stream + + function getWritable() { + return writer.writable + } + + function getReadable() { + return reader.readable + } + + function proxyWriter(methodName) { + stream[methodName] = method + + function method() { + return writer[methodName].apply(writer, arguments) + } + } + + function proxyReader(methodName) { + stream[methodName] = method + + function method() { + stream.emit(methodName) + var func = reader[methodName] + if (func) { + return func.apply(reader, arguments) + } + reader.emit(methodName) + } + } + + function proxyStream(methodName) { + reader.on(methodName, reemit) + + function reemit() { + var args = slice.call(arguments) + args.unshift(methodName) + stream.emit.apply(stream, args) + } + } + + function handleEnd() { + if (ended) { + return + } + ended = true + var args = slice.call(arguments) + args.unshift("end") + stream.emit.apply(stream, args) + } + + function reemit(err) { + stream.emit("error", err) + } +} \ No newline at end of file diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/package.json b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/package.json new file mode 100644 index 0000000..c04e5f6 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/package.json @@ -0,0 +1,44 @@ +{ + "name": "duplexer", + "version": "0.0.2", + "description": "Creates a duplex stream", + "keywords": [], + "author": { + "name": "Raynos", + "email": "raynos2@gmail.com" + }, + "repository": { + "type": "git", + "url": "git://github.com/Raynos/duplexer.git" + }, + "main": "index", + "homepage": "https://github.com/Raynos/duplexer", + "contributors": [ + { + "name": "Jake Verbaten" + } + ], + "bugs": { + "url": "https://github.com/Raynos/duplexer/issues", + "email": "raynos2@gmail.com" + }, + "dependencies": {}, + "devDependencies": { + "through": "~0.1.4" + }, + "licenses": [ + { + "type": "MIT", + "url": "http://github.com/Raynos/duplexer/raw/master/LICENSE" + } + ], + "scripts": { + "test": "make test" + }, + "readme": "# duplexer [![build status][1]][2]\n\nCreates a duplex stream\n\nTaken from [event-stream][3]\n\n## duplex (writeStream, readStream)\n\nTakes a writable stream and a readable stream and makes them appear as a readable writable stream.\n\nIt is assumed that the two streams are connected to each other in some way.\n\n## Example\n\n var grep = cp.exec('grep Stream')\n\n duplex(grep.stdin, grep.stdout)\n\n## Installation\n\n`npm install duplexer`\n\n## Tests\n\n`make test`\n\n## Contributors\n\n - Dominictarr\n - Raynos\n\n## MIT Licenced\n\n [1]: https://secure.travis-ci.org/Raynos/duplexer.png\n [2]: http://travis-ci.org/Raynos/duplexer\n [3]: https://github.com/dominictarr/event-stream#duplex-writestream-readstream", + "_id": "duplexer@0.0.2", + "dist": { + "shasum": "21b2a2e78861eea18bb080cb9491cbcd193ead08" + }, + "_from": "duplexer@0.0.2" +} diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/test.js b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/test.js new file mode 100644 index 0000000..e06a864 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/node_modules/duplexer/test.js @@ -0,0 +1,27 @@ +var duplex = require("./index") + , assert = require("assert") + , through = require("through") + +var readable = through() + , writable = through(write) + , written = 0 + , data = 0 + +var stream = duplex(writable, readable) + +function write() { + written++ +} + +stream.on("data", ondata) + +function ondata() { + data++ +} + +stream.write() +readable.emit("data") + +assert.equal(written, 1) +assert.equal(data, 1) +console.log("DONE") \ No newline at end of file diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/package.json b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/package.json new file mode 100644 index 0000000..4b60b98 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/package.json @@ -0,0 +1,32 @@ +{ + "name": "stream-combiner", + "version": "0.0.0", + "homepage": "https://github.com/dominictarr/stream-combiner", + "repository": { + "type": "git", + "url": "git://github.com/dominictarr/stream-combiner.git" + }, + "dependencies": { + "duplexer": "0.0.2" + }, + "devDependencies": { + "tape": "0.0.4", + "event-stream": "~3.0.7" + }, + "scripts": { + "test": "set -e; for t in test/*.js; do node $t; done" + }, + "author": { + "name": "'Dominic Tarr'", + "email": "dominic.tarr@gmail.com", + "url": "http://dominictarr.com" + }, + "license": "MIT", + "readme": "# stream-combiner\n\n\n\n## pipeline (stream1,...,streamN)\n\nTurn a pipeline into a single stream. `pipeline` returns a stream that writes to the first stream\nand reads from the last stream. \n\nListening for 'error' will recieve errors from all streams inside the pipe.\n\n> `connect` is an alias for `pipeline`.\n\n``` js\n\n es.pipeline( //connect streams together with `pipe`\n process.openStdin(), //open stdin\n es.split(), //split stream to break on newlines\n es.map(function (data, callback) {//turn this async function into a stream\n callback(null\n , inspect(JSON.parse(data))) //render it nicely\n }),\n process.stdout // pipe it to stdout !\n )\n```\n\n## License\n\nMIT\n", + "_id": "stream-combiner@0.0.0", + "description": "", + "dist": { + "shasum": "e0b7200a986c630eef402100cef434714b4db89c" + }, + "_from": "stream-combiner@0.0.0" +} diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/test/index.js b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/test/index.js new file mode 100644 index 0000000..d17ddad --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/stream-combiner/test/index.js @@ -0,0 +1,52 @@ +var es = require('event-stream') +var combine = require('..') +var test = require('tape') + +test('do not duplicate errors', function (test) { + + var errors = 0; + var pipe = combine( + es.through(function(data) { + return this.emit('data', data); + }), + es.through(function(data) { + return this.emit('error', new Error(data)); + }) + ) + + pipe.on('error', function(err) { + errors++ + test.ok(errors, 'expected error count') + process.nextTick(function () { + return test.end(); + }) + }) + + return pipe.write('meh'); +}) + +test('3 pipe do not duplicate errors', function (test) { + + var errors = 0; + var pipe = combine( + es.through(function(data) { + return this.emit('data', data); + }), + es.through(function(data) { + return this.emit('error', new Error(data)); + }), + es.through() + ) + + pipe.on('error', function(err) { + errors++ + test.ok(errors, 'expected error count') + process.nextTick(function () { + return test.end(); + }) + }) + + return pipe.write('meh'); + +}) + diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/through/.travis.yml b/node_modules/jsftp/node_modules/event-stream/node_modules/through/.travis.yml new file mode 100644 index 0000000..c693a93 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/through/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - 0.6 + - 0.8 + - "0.10" diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/through/LICENSE.APACHE2 b/node_modules/jsftp/node_modules/event-stream/node_modules/through/LICENSE.APACHE2 new file mode 100644 index 0000000..6366c04 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/through/LICENSE.APACHE2 @@ -0,0 +1,15 @@ +Apache License, Version 2.0 + +Copyright (c) 2011 Dominic Tarr + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/through/LICENSE.MIT b/node_modules/jsftp/node_modules/event-stream/node_modules/through/LICENSE.MIT new file mode 100644 index 0000000..6eafbd7 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/through/LICENSE.MIT @@ -0,0 +1,24 @@ +The MIT License + +Copyright (c) 2011 Dominic Tarr + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/through/index.js b/node_modules/jsftp/node_modules/event-stream/node_modules/through/index.js new file mode 100644 index 0000000..7b935bf --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/through/index.js @@ -0,0 +1,108 @@ +var Stream = require('stream') + +// through +// +// a stream that does nothing but re-emit the input. +// useful for aggregating a series of changing but not ending streams into one stream) + +exports = module.exports = through +through.through = through + +//create a readable writable stream. + +function through (write, end, opts) { + write = write || function (data) { this.queue(data) } + end = end || function () { this.queue(null) } + + var ended = false, destroyed = false, buffer = [], _ended = false + var stream = new Stream() + stream.readable = stream.writable = true + stream.paused = false + +// stream.autoPause = !(opts && opts.autoPause === false) + stream.autoDestroy = !(opts && opts.autoDestroy === false) + + stream.write = function (data) { + write.call(this, data) + return !stream.paused + } + + function drain() { + while(buffer.length && !stream.paused) { + var data = buffer.shift() + if(null === data) + return stream.emit('end') + else + stream.emit('data', data) + } + } + + stream.queue = stream.push = function (data) { +// console.error(ended) + if(_ended) return stream + if(data == null) _ended = true + buffer.push(data) + drain() + return stream + } + + //this will be registered as the first 'end' listener + //must call destroy next tick, to make sure we're after any + //stream piped from here. + //this is only a problem if end is not emitted synchronously. + //a nicer way to do this is to make sure this is the last listener for 'end' + + stream.on('end', function () { + stream.readable = false + if(!stream.writable && stream.autoDestroy) + process.nextTick(function () { + stream.destroy() + }) + }) + + function _end () { + stream.writable = false + end.call(stream) + if(!stream.readable && stream.autoDestroy) + stream.destroy() + } + + stream.end = function (data) { + if(ended) return + ended = true + if(arguments.length) stream.write(data) + _end() // will emit or queue + return stream + } + + stream.destroy = function () { + if(destroyed) return + destroyed = true + ended = true + buffer.length = 0 + stream.writable = stream.readable = false + stream.emit('close') + return stream + } + + stream.pause = function () { + if(stream.paused) return + stream.paused = true + return stream + } + + stream.resume = function () { + if(stream.paused) { + stream.paused = false + stream.emit('resume') + } + drain() + //may have become paused again, + //as drain emits 'data'. + if(!stream.paused) + stream.emit('drain') + return stream + } + return stream +} + diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/through/package.json b/node_modules/jsftp/node_modules/event-stream/node_modules/through/package.json new file mode 100644 index 0000000..833c49e --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/through/package.json @@ -0,0 +1,45 @@ +{ + "name": "through", + "version": "2.3.4", + "description": "simplified stream contsruction", + "main": "index.js", + "scripts": { + "test": "set -e; for t in test/*.js; do node $t; done" + }, + "devDependencies": { + "stream-spec": "~0.3.5", + "tape": "~0.2.2" + }, + "keywords": [ + "stream", + "streams", + "user-streams", + "pipe" + ], + "author": { + "name": "Dominic Tarr", + "email": "dominic.tarr@gmail.com", + "url": "dominictarr.com" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/dominictarr/through.git" + }, + "homepage": "http://github.com/dominictarr/through", + "testling": { + "browsers": [ + "ie/8..latest", + "ff/15..latest", + "chrome/20..latest", + "safari/5.1..latest" + ], + "files": "test/*.js" + }, + "readme": "#through\n\n[![build status](https://secure.travis-ci.org/dominictarr/through.png)](http://travis-ci.org/dominictarr/through)\n\nEasy way to create a `Stream` that is both `readable` and `writable`. \n\n* Pass in optional `write` and `end` methods.\n* `through` takes care of pause/resume logic if you use `this.queue(data)` instead of `this.emit('data', data)`.\n* Use `this.pause()` and `this.resume()` to manage flow.\n* Check `this.paused` to see current flow state. (`write` always returns `!this.paused`).\n\nThis function is the basis for most of the synchronous streams in \n[event-stream](http://github.com/dominictarr/event-stream).\n\n``` js\nvar through = require('through')\n\nthrough(function write(data) {\n this.queue(data) //data *must* not be null\n },\n function end () { //optional\n this.queue(null)\n })\n```\n\nOr, can also be used _without_ buffering on pause, use `this.emit('data', data)`,\nand this.emit('end')\n\n``` js\nvar through = require('through')\n\nthrough(function write(data) {\n this.emit('data', data)\n //this.pause() \n },\n function end () { //optional\n this.emit('end')\n })\n```\n\n## Extended Options\n\nYou will probably not need these 99% of the time.\n\n### autoDestroy=false\n\nBy default, `through` emits close when the writable\nand readable side of the stream has ended.\nIf that is not desired, set `autoDestroy=false`.\n\n``` js\nvar through = require('through')\n\n//like this\nvar ts = through(write, end, {autoDestroy: false})\n//or like this\nvar ts = through(write, end)\nts.autoDestroy = false\n```\n\n## License\n\nMIT / Apache2\n", + "_id": "through@2.3.4", + "dist": { + "shasum": "b0b7e25d5785ed1c45add69ad718a3ab3bc1f81a" + }, + "_from": "through@~2.3.1" +} diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/through/readme.markdown b/node_modules/jsftp/node_modules/event-stream/node_modules/through/readme.markdown new file mode 100644 index 0000000..b20bc9a --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/through/readme.markdown @@ -0,0 +1,63 @@ +#through + +[![build status](https://secure.travis-ci.org/dominictarr/through.png)](http://travis-ci.org/dominictarr/through) + +Easy way to create a `Stream` that is both `readable` and `writable`. + +* Pass in optional `write` and `end` methods. +* `through` takes care of pause/resume logic if you use `this.queue(data)` instead of `this.emit('data', data)`. +* Use `this.pause()` and `this.resume()` to manage flow. +* Check `this.paused` to see current flow state. (`write` always returns `!this.paused`). + +This function is the basis for most of the synchronous streams in +[event-stream](http://github.com/dominictarr/event-stream). + +``` js +var through = require('through') + +through(function write(data) { + this.queue(data) //data *must* not be null + }, + function end () { //optional + this.queue(null) + }) +``` + +Or, can also be used _without_ buffering on pause, use `this.emit('data', data)`, +and this.emit('end') + +``` js +var through = require('through') + +through(function write(data) { + this.emit('data', data) + //this.pause() + }, + function end () { //optional + this.emit('end') + }) +``` + +## Extended Options + +You will probably not need these 99% of the time. + +### autoDestroy=false + +By default, `through` emits close when the writable +and readable side of the stream has ended. +If that is not desired, set `autoDestroy=false`. + +``` js +var through = require('through') + +//like this +var ts = through(write, end, {autoDestroy: false}) +//or like this +var ts = through(write, end) +ts.autoDestroy = false +``` + +## License + +MIT / Apache2 diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/through/test/auto-destroy.js b/node_modules/jsftp/node_modules/event-stream/node_modules/through/test/auto-destroy.js new file mode 100644 index 0000000..9a8fd00 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/through/test/auto-destroy.js @@ -0,0 +1,30 @@ +var test = require('tape') +var through = require('../') + +// must emit end before close. + +test('end before close', function (assert) { + var ts = through() + ts.autoDestroy = false + var ended = false, closed = false + + ts.on('end', function () { + assert.ok(!closed) + ended = true + }) + ts.on('close', function () { + assert.ok(ended) + closed = true + }) + + ts.write(1) + ts.write(2) + ts.write(3) + ts.end() + assert.ok(ended) + assert.notOk(closed) + ts.destroy() + assert.ok(closed) + assert.end() +}) + diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/through/test/buffering.js b/node_modules/jsftp/node_modules/event-stream/node_modules/through/test/buffering.js new file mode 100644 index 0000000..b0084bf --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/through/test/buffering.js @@ -0,0 +1,71 @@ +var test = require('tape') +var through = require('../') + +// must emit end before close. + +test('buffering', function(assert) { + var ts = through(function (data) { + this.queue(data) + }, function () { + this.queue(null) + }) + + var ended = false, actual = [] + + ts.on('data', actual.push.bind(actual)) + ts.on('end', function () { + ended = true + }) + + ts.write(1) + ts.write(2) + ts.write(3) + assert.deepEqual(actual, [1, 2, 3]) + ts.pause() + ts.write(4) + ts.write(5) + ts.write(6) + assert.deepEqual(actual, [1, 2, 3]) + ts.resume() + assert.deepEqual(actual, [1, 2, 3, 4, 5, 6]) + ts.pause() + ts.end() + assert.ok(!ended) + ts.resume() + assert.ok(ended) + assert.end() +}) + +test('buffering has data in queue, when ends', function (assert) { + + /* + * If stream ends while paused with data in the queue, + * stream should still emit end after all data is written + * on resume. + */ + + var ts = through(function (data) { + this.queue(data) + }, function () { + this.queue(null) + }) + + var ended = false, actual = [] + + ts.on('data', actual.push.bind(actual)) + ts.on('end', function () { + ended = true + }) + + ts.pause() + ts.write(1) + ts.write(2) + ts.write(3) + ts.end() + assert.deepEqual(actual, [], 'no data written yet, still paused') + assert.ok(!ended, 'end not emitted yet, still paused') + ts.resume() + assert.deepEqual(actual, [1, 2, 3], 'resumed, all data should be delivered') + assert.ok(ended, 'end should be emitted once all data was delivered') + assert.end(); +}) diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/through/test/end.js b/node_modules/jsftp/node_modules/event-stream/node_modules/through/test/end.js new file mode 100644 index 0000000..fa113f5 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/through/test/end.js @@ -0,0 +1,45 @@ +var test = require('tape') +var through = require('../') + +// must emit end before close. + +test('end before close', function (assert) { + var ts = through() + var ended = false, closed = false + + ts.on('end', function () { + assert.ok(!closed) + ended = true + }) + ts.on('close', function () { + assert.ok(ended) + closed = true + }) + + ts.write(1) + ts.write(2) + ts.write(3) + ts.end() + assert.ok(ended) + assert.ok(closed) + assert.end() +}) + +test('end only once', function (t) { + + var ts = through() + var ended = false, closed = false + + ts.on('end', function () { + t.equal(ended, false) + ended = true + }) + + ts.queue(null) + ts.queue(null) + ts.queue(null) + + ts.resume() + + t.end() +}) diff --git a/node_modules/jsftp/node_modules/event-stream/node_modules/through/test/index.js b/node_modules/jsftp/node_modules/event-stream/node_modules/through/test/index.js new file mode 100644 index 0000000..33e33f9 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/node_modules/through/test/index.js @@ -0,0 +1,114 @@ + +var test = require('tape') +var spec = require('stream-spec') +var through = require('../') + +/* + I'm using these two functions, and not streams and pipe + so there is less to break. if this test fails it must be + the implementation of _through_ +*/ + +function write(array, stream) { + array = array.slice() + function next() { + while(array.length) + if(stream.write(array.shift()) === false) + return stream.once('drain', next) + + stream.end() + } + + next() +} + +function read(stream, callback) { + var actual = [] + stream.on('data', function (data) { + actual.push(data) + }) + stream.once('end', function () { + callback(null, actual) + }) + stream.once('error', function (err) { + callback(err) + }) +} + +test('simple defaults', function(assert) { + + var l = 1000 + , expected = [] + + while(l--) expected.push(l * Math.random()) + + var t = through() + spec(t) + .through() + .pausable() + .validateOnExit() + + read(t, function (err, actual) { + assert.ifError(err) + assert.deepEqual(actual, expected) + assert.end() + }) + + write(expected, t) +}); + +test('simple functions', function(assert) { + + var l = 1000 + , expected = [] + + while(l--) expected.push(l * Math.random()) + + var t = through(function (data) { + this.emit('data', data*2) + }) + spec(t) + .through() + .pausable() + .validateOnExit() + + read(t, function (err, actual) { + assert.ifError(err) + assert.deepEqual(actual, expected.map(function (data) { + return data*2 + })) + assert.end() + }) + + write(expected, t) +}) + +test('pauses', function(assert) { + + var l = 1000 + , expected = [] + + while(l--) expected.push(l) //Math.random()) + + var t = through() + spec(t) + .through() + .pausable() + .validateOnExit() + + t.on('data', function () { + if(Math.random() > 0.1) return + t.pause() + process.nextTick(function () { + t.resume() + }) + }) + + read(t, function (err, actual) { + assert.ifError(err) + assert.deepEqual(actual, expected) + assert.end() + }) + + write(expected, t) +}) diff --git a/node_modules/jsftp/node_modules/event-stream/package.json b/node_modules/jsftp/node_modules/event-stream/package.json new file mode 100644 index 0000000..40c8504 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/package.json @@ -0,0 +1,62 @@ +{ + "name": "event-stream", + "version": "3.0.16", + "description": "construct pipes of streams of events", + "homepage": "http://github.com/dominictarr/event-stream", + "repository": { + "type": "git", + "url": "git://github.com/dominictarr/event-stream.git" + }, + "dependencies": { + "through": "~2.3.1", + "duplexer": "~0.0.2", + "from": "~0", + "map-stream": "0.0.2", + "pause-stream": "0.0.10", + "split": "0.2", + "stream-combiner": "0.0.0" + }, + "devDependencies": { + "asynct": "*", + "it-is": "1", + "ubelt": "~2.9", + "stream-spec": "~0.2", + "tape": "~0.1.5" + }, + "scripts": { + "test": "asynct test/", + "test_tap": "set -e; for t in test/*.js; do node $t; done" + }, + "testling": { + "files": "test/*.js", + "browsers": { + "ie": [ + 8, + 9 + ], + "firefox": [ + 13 + ], + "chrome": [ + 20 + ], + "safari": [ + 5.1 + ], + "opera": [ + 12 + ] + } + }, + "author": { + "name": "Dominic Tarr", + "email": "dominic.tarr@gmail.com", + "url": "http://bit.ly/dominictarr" + }, + "readme": "# EventStream\n\n\n\n[![browser status](http://ci.testling.com/dominictarr/event-stream.png)]\n(http://ci.testling.com/dominictarr/event-stream)\n\n[Streams](http://nodejs.org/api/strems.html \"Stream\") are node's best and most misunderstood idea, and \n_EventStream_ is a toolkit to make creating and working with streams easy. \n\nNormally, streams are only used of IO, \nbut in event stream we send all kinds of objects down the pipe. \nIf your application's input and output are streams, \nshouldn't the throughput be a stream too? \n\nThe *EventStream* functions resemble the array functions, \nbecause Streams are like Arrays, but laid out in time, rather than in memory. \n\nAll the `event-stream` functions return instances of `Stream`.\n\n`event-stream` creates \n[0.8 streams](https://github.com/joyent/node/blob/v0.8/doc/api/stream.markdown)\n, which are compatible with [0.10 streams](http://nodejs.org/api/stream.html \"Stream\")\n\n>NOTE: I shall use the term \"through stream\" to refer to a stream that is writable and readable. \n\n###[simple example](https://github.com/dominictarr/event-stream/blob/master/examples/pretty.js):\n\n``` js\n\n//pretty.js\n\nif(!module.parent) {\n var es = require('event-stream')\n es.pipeline( //connect streams together with `pipe`\n process.openStdin(), //open stdin\n es.split(), //split stream to break on newlines\n es.map(function (data, callback) {//turn this async function into a stream\n callback(null\n , inspect(JSON.parse(data))) //render it nicely\n }),\n process.stdout // pipe it to stdout !\n )\n }\n```\nrun it ...\n\n``` bash \ncurl -sS registry.npmjs.org/event-stream | node pretty.js\n```\n \n[node Stream documentation](http://nodejs.org/api/stream.html)\n\n## through (write?, end?)\n\nReemits data synchronously. Easy way to create syncronous through streams.\nPass in an optional `write` and `end` methods. They will be called in the \ncontext of the stream. Use `this.pause()` and `this.resume()` to manage flow.\nCheck `this.paused` to see current flow state. (write always returns `!this.paused`)\n\nthis function is the basis for most of the syncronous streams in `event-stream`.\n\n``` js\n\nes.through(function write(data) {\n this.emit('data', data)\n //this.pause() \n },\n function end () { //optional\n this.emit('end')\n })\n\n```\n\n##map (asyncFunction)\n\nCreate a through stream from an asyncronous function. \n\n``` js\nvar es = require('event-stream')\n\nes.map(function (data, callback) {\n //transform data\n // ...\n callback(null, data)\n})\n\n```\n\nEach map MUST call the callback. It may callback with data, with an error or with no arguments, \n\n * `callback()` drop this data. \n this makes the map work like `filter`, \n note:`callback(null,null)` is not the same, and will emit `null`\n\n * `callback(null, newData)` turn data into newData\n \n * `callback(error)` emit an error for this item.\n\n>Note: if a callback is not called, `map` will think that it is still being processed, \n>every call must be answered or the stream will not know when to end. \n>\n>Also, if the callback is called more than once, every call but the first will be ignored.\n\n## mapSync (syncFunction)\n\nSame as `map`, but the callback is called synchronously. Based on `es.through`\n\n## split (matcher)\n\nBreak up a stream and reassemble it so that each line is a chunk. matcher may be a `String`, or a `RegExp` \n\nExample, read every line in a file ...\n\n``` js\n es.pipeline(\n fs.createReadStream(file, {flags: 'r'}),\n es.split(),\n es.map(function (line, cb) {\n //do something with the line \n cb(null, line)\n })\n )\n\n```\n\n`split` takes the same arguments as `string.split` except it defaults to '\\n' instead of ',', and the optional `limit` paremeter is ignored.\n[String#split](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/split)\n\n## join (separator)\n\ncreate a through stream that emits `separator` between each chunk, just like Array#join.\n\n(for legacy reasons, if you pass a callback instead of a string, join is a synonym for `es.wait`)\n\n## replace (from, to)\n\nReplace all occurences of `from` with `to`. `from` may be a `String` or a `RegExp`. \nWorks just like `string.split(from).join(to)`, but streaming.\n\n\n## parse\n\nConvienience function for parsing JSON chunks. For newline separated JSON,\nuse with `es.split`\n\n``` js\nfs.createReadStream(filename)\n .pipe(es.split()) //defaults to lines.\n .pipe(es.parse())\n```\n\n## stringify\n\nconvert javascript objects into lines of text. The text will have whitespace escaped and have a `\\n` appended, so it will be compatible with `es.parse`\n\n``` js\nobjectStream\n .pipe(es.stringify())\n .pipe(fs.createWriteStream(filename))\n```\n\n##readable (asyncFunction) \n\ncreate a readable stream (that respects pause) from an async function. \nwhile the stream is not paused, \nthe function will be polled with `(count, callback)`, \nand `this` will be the readable stream.\n\n``` js\n\nes.readable(function (count, callback) {\n if(streamHasEnded)\n return this.emit('end')\n \n //...\n \n this.emit('data', data) //use this way to emit multiple chunks per call.\n \n callback() // you MUST always call the callback eventually.\n // the function will not be called again until you do this.\n})\n```\nyou can also pass the data and the error to the callback. \nyou may only call the callback once. \ncalling the same callback more than once will have no effect. \n\n##readArray (array)\n\nCreate a readable stream from an Array.\n\nJust emit each item as a data event, respecting `pause` and `resume`.\n\n``` js\n var es = require('event-stream')\n , reader = es.readArray([1,2,3])\n\n reader.pipe(...)\n```\n\n## writeArray (callback)\n\ncreate a writeable stream from a callback, \nall `data` events are stored in an array, which is passed to the callback when the stream ends.\n\n``` js\n var es = require('event-stream')\n , reader = es.readArray([1, 2, 3])\n , writer = es.writeArray(function (err, array){\n //array deepEqual [1, 2, 3]\n })\n\n reader.pipe(writer)\n```\n\n## pipeline (stream1,...,streamN)\n\nTurn a pipeline into a single stream. `pipeline` returns a stream that writes to the first stream\nand reads from the last stream. \n\nListening for 'error' will recieve errors from all streams inside the pipe.\n\n> `connect` is an alias for `pipeline`.\n\n``` js\n\n es.pipeline( //connect streams together with `pipe`\n process.openStdin(), //open stdin\n es.split(), //split stream to break on newlines\n es.map(function (data, callback) {//turn this async function into a stream\n callback(null\n , inspect(JSON.parse(data))) //render it nicely\n }),\n process.stdout // pipe it to stdout !\n )\n```\n\n## pause () \n\nA stream that buffers all chunks when paused.\n\n\n``` js\n var ps = es.pause()\n ps.pause() //buffer the stream, also do not allow 'end' \n ps.resume() //allow chunks through\n```\n\n## duplex (writeStream, readStream)\n\nTakes a writable stream and a readable stream and makes them appear as a readable writable stream.\n\nIt is assumed that the two streams are connected to each other in some way. \n\n(This is used by `pipeline` and `child`.)\n\n``` js\n var grep = cp.exec('grep Stream')\n\n es.duplex(grep.stdin, grep.stdout)\n```\n\n## child (child_process)\n\nCreate a through stream from a child process ...\n\n``` js\n var cp = require('child_process')\n\n es.child(cp.exec('grep Stream')) // a through stream\n\n```\n\n## wait (callback)\n\nwaits for stream to emit 'end'.\njoins chunks of a stream into a single string. \ntakes an optional callback, which will be passed the \ncomplete string when it receives the 'end' event.\n\nalso, emits a single 'data' event.\n\n``` js\n\nreadStream.pipe(es.join(function (err, text) {\n // have complete text here.\n}))\n\n```\n\n\n", + "_id": "event-stream@3.0.16", + "dist": { + "shasum": "0b040584bf4f31ca2d250d6a2ee285fea3e8e358" + }, + "_from": "event-stream@~3.0.14" +} diff --git a/node_modules/jsftp/node_modules/event-stream/readme.markdown b/node_modules/jsftp/node_modules/event-stream/readme.markdown new file mode 100644 index 0000000..4674c4e --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/readme.markdown @@ -0,0 +1,291 @@ +# EventStream + + + +[![browser status](http://ci.testling.com/dominictarr/event-stream.png)] +(http://ci.testling.com/dominictarr/event-stream) + +[Streams](http://nodejs.org/api/strems.html "Stream") are node's best and most misunderstood idea, and +_EventStream_ is a toolkit to make creating and working with streams easy. + +Normally, streams are only used of IO, +but in event stream we send all kinds of objects down the pipe. +If your application's input and output are streams, +shouldn't the throughput be a stream too? + +The *EventStream* functions resemble the array functions, +because Streams are like Arrays, but laid out in time, rather than in memory. + +All the `event-stream` functions return instances of `Stream`. + +`event-stream` creates +[0.8 streams](https://github.com/joyent/node/blob/v0.8/doc/api/stream.markdown) +, which are compatible with [0.10 streams](http://nodejs.org/api/stream.html "Stream") + +>NOTE: I shall use the term "through stream" to refer to a stream that is writable and readable. + +###[simple example](https://github.com/dominictarr/event-stream/blob/master/examples/pretty.js): + +``` js + +//pretty.js + +if(!module.parent) { + var es = require('event-stream') + es.pipeline( //connect streams together with `pipe` + process.openStdin(), //open stdin + es.split(), //split stream to break on newlines + es.map(function (data, callback) {//turn this async function into a stream + callback(null + , inspect(JSON.parse(data))) //render it nicely + }), + process.stdout // pipe it to stdout ! + ) + } +``` +run it ... + +``` bash +curl -sS registry.npmjs.org/event-stream | node pretty.js +``` + +[node Stream documentation](http://nodejs.org/api/stream.html) + +## through (write?, end?) + +Reemits data synchronously. Easy way to create syncronous through streams. +Pass in an optional `write` and `end` methods. They will be called in the +context of the stream. Use `this.pause()` and `this.resume()` to manage flow. +Check `this.paused` to see current flow state. (write always returns `!this.paused`) + +this function is the basis for most of the syncronous streams in `event-stream`. + +``` js + +es.through(function write(data) { + this.emit('data', data) + //this.pause() + }, + function end () { //optional + this.emit('end') + }) + +``` + +##map (asyncFunction) + +Create a through stream from an asyncronous function. + +``` js +var es = require('event-stream') + +es.map(function (data, callback) { + //transform data + // ... + callback(null, data) +}) + +``` + +Each map MUST call the callback. It may callback with data, with an error or with no arguments, + + * `callback()` drop this data. + this makes the map work like `filter`, + note:`callback(null,null)` is not the same, and will emit `null` + + * `callback(null, newData)` turn data into newData + + * `callback(error)` emit an error for this item. + +>Note: if a callback is not called, `map` will think that it is still being processed, +>every call must be answered or the stream will not know when to end. +> +>Also, if the callback is called more than once, every call but the first will be ignored. + +## mapSync (syncFunction) + +Same as `map`, but the callback is called synchronously. Based on `es.through` + +## split (matcher) + +Break up a stream and reassemble it so that each line is a chunk. matcher may be a `String`, or a `RegExp` + +Example, read every line in a file ... + +``` js + es.pipeline( + fs.createReadStream(file, {flags: 'r'}), + es.split(), + es.map(function (line, cb) { + //do something with the line + cb(null, line) + }) + ) + +``` + +`split` takes the same arguments as `string.split` except it defaults to '\n' instead of ',', and the optional `limit` paremeter is ignored. +[String#split](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/split) + +## join (separator) + +create a through stream that emits `separator` between each chunk, just like Array#join. + +(for legacy reasons, if you pass a callback instead of a string, join is a synonym for `es.wait`) + +## replace (from, to) + +Replace all occurences of `from` with `to`. `from` may be a `String` or a `RegExp`. +Works just like `string.split(from).join(to)`, but streaming. + + +## parse + +Convienience function for parsing JSON chunks. For newline separated JSON, +use with `es.split` + +``` js +fs.createReadStream(filename) + .pipe(es.split()) //defaults to lines. + .pipe(es.parse()) +``` + +## stringify + +convert javascript objects into lines of text. The text will have whitespace escaped and have a `\n` appended, so it will be compatible with `es.parse` + +``` js +objectStream + .pipe(es.stringify()) + .pipe(fs.createWriteStream(filename)) +``` + +##readable (asyncFunction) + +create a readable stream (that respects pause) from an async function. +while the stream is not paused, +the function will be polled with `(count, callback)`, +and `this` will be the readable stream. + +``` js + +es.readable(function (count, callback) { + if(streamHasEnded) + return this.emit('end') + + //... + + this.emit('data', data) //use this way to emit multiple chunks per call. + + callback() // you MUST always call the callback eventually. + // the function will not be called again until you do this. +}) +``` +you can also pass the data and the error to the callback. +you may only call the callback once. +calling the same callback more than once will have no effect. + +##readArray (array) + +Create a readable stream from an Array. + +Just emit each item as a data event, respecting `pause` and `resume`. + +``` js + var es = require('event-stream') + , reader = es.readArray([1,2,3]) + + reader.pipe(...) +``` + +## writeArray (callback) + +create a writeable stream from a callback, +all `data` events are stored in an array, which is passed to the callback when the stream ends. + +``` js + var es = require('event-stream') + , reader = es.readArray([1, 2, 3]) + , writer = es.writeArray(function (err, array){ + //array deepEqual [1, 2, 3] + }) + + reader.pipe(writer) +``` + +## pipeline (stream1,...,streamN) + +Turn a pipeline into a single stream. `pipeline` returns a stream that writes to the first stream +and reads from the last stream. + +Listening for 'error' will recieve errors from all streams inside the pipe. + +> `connect` is an alias for `pipeline`. + +``` js + + es.pipeline( //connect streams together with `pipe` + process.openStdin(), //open stdin + es.split(), //split stream to break on newlines + es.map(function (data, callback) {//turn this async function into a stream + callback(null + , inspect(JSON.parse(data))) //render it nicely + }), + process.stdout // pipe it to stdout ! + ) +``` + +## pause () + +A stream that buffers all chunks when paused. + + +``` js + var ps = es.pause() + ps.pause() //buffer the stream, also do not allow 'end' + ps.resume() //allow chunks through +``` + +## duplex (writeStream, readStream) + +Takes a writable stream and a readable stream and makes them appear as a readable writable stream. + +It is assumed that the two streams are connected to each other in some way. + +(This is used by `pipeline` and `child`.) + +``` js + var grep = cp.exec('grep Stream') + + es.duplex(grep.stdin, grep.stdout) +``` + +## child (child_process) + +Create a through stream from a child process ... + +``` js + var cp = require('child_process') + + es.child(cp.exec('grep Stream')) // a through stream + +``` + +## wait (callback) + +waits for stream to emit 'end'. +joins chunks of a stream into a single string. +takes an optional callback, which will be passed the +complete string when it receives the 'end' event. + +also, emits a single 'data' event. + +``` js + +readStream.pipe(es.join(function (err, text) { + // have complete text here. +})) + +``` + + diff --git a/node_modules/jsftp/node_modules/event-stream/test/connect.asynct.js b/node_modules/jsftp/node_modules/event-stream/test/connect.asynct.js new file mode 100644 index 0000000..e6a442b --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/test/connect.asynct.js @@ -0,0 +1,86 @@ +var es = require('../') + , it = require('it-is').style('colour') + , d = require('ubelt') + +function makeExamplePipe() { + + return es.connect( + es.map(function (data, callback) { + callback(null, data * 2) + }), + es.map(function (data, callback) { + d.delay(callback)(null, data) + }), + es.map(function (data, callback) { + callback(null, data + 2) + })) +} + +exports['simple pipe'] = function (test) { + + var pipe = makeExamplePipe() + + pipe.on('data', function (data) { + it(data).equal(18) + test.done() + }) + + pipe.write(8) + +} + +exports['read array then map'] = function (test) { + + var readThis = d.map(3, 6, 100, d.id) //array of multiples of 3 < 100 + , first = es.readArray(readThis) + , read = [] + , pipe = + es.connect( + first, + es.map(function (data, callback) { + callback(null, {data: data}) + }), + es.map(function (data, callback) { + callback(null, {data: data}) + }), + es.writeArray(function (err, array) { + it(array).deepEqual(d.map(readThis, function (data) { + return {data: {data: data}} + })) + test.done() + }) + ) +} + +exports ['connect returns a stream'] = function (test) { + + var rw = + es.connect( + es.map(function (data, callback) { + callback(null, data * 2) + }), + es.map(function (data, callback) { + callback(null, data * 5) + }) + ) + + it(rw).has({readable: true, writable: true}) + + var array = [190, 24, 6, 7, 40, 57, 4, 6] + , _array = [] + , c = + es.connect( + es.readArray(array), + rw, + es.log('after rw:'), + es.writeArray(function (err, _array) { + it(_array).deepEqual(array.map(function (e) { return e * 10 })) + test.done() + }) + ) + +} + + +require('./helper')(module) + diff --git a/node_modules/jsftp/node_modules/event-stream/test/helper/index.js b/node_modules/jsftp/node_modules/event-stream/test/helper/index.js new file mode 100644 index 0000000..96abaeb --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/test/helper/index.js @@ -0,0 +1,12 @@ +var tape = require('tape') + +module.exports = function (m) { + if(m.parent) return + for(var name in m.exports) { + tape(name, function (t) { + console.log('start', name) + t.done = t.end + m.exports[name](t) + }) + } +} diff --git a/node_modules/jsftp/node_modules/event-stream/test/merge.asynct.js b/node_modules/jsftp/node_modules/event-stream/test/merge.asynct.js new file mode 100644 index 0000000..6ac65f3 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/test/merge.asynct.js @@ -0,0 +1,21 @@ +var es = require('../') + , it = require('it-is').style('colour') + , d = require('ubelt') + +exports.merge = function (t) { + var odd = d.map(1, 3, 100, d.id) //array of multiples of 3 < 100 + var even = d.map(2, 4, 100, d.id) //array of multiples of 3 < 100 + + var r1 = es.readArray(even) + var r2 = es.readArray(odd) + + var writer = es.writeArray(function (err, array){ + if(err) throw err //unpossible + it(array.sort()).deepEqual(even.concat(odd).sort()) + t.done() + }) + + es.merge(r1, r2).pipe(writer) + +} +require('./helper')(module) diff --git a/node_modules/jsftp/node_modules/event-stream/test/pause.asynct.js b/node_modules/jsftp/node_modules/event-stream/test/pause.asynct.js new file mode 100644 index 0000000..b073604 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/test/pause.asynct.js @@ -0,0 +1,39 @@ + +var es = require('../') + , it = require('it-is') + , d = require('ubelt') + +exports ['gate buffers when shut'] = function (test) { + + var hundy = d.map(1,100, d.id) + , gate = es.pause() + , ten = 10 + es.connect( + es.readArray(hundy), + es.log('after readArray'), + gate, + //es.log('after gate'), + es.map(function (num, next) { + //stick a map in here to check that gate never emits when open + it(gate.paused).equal(false) + console.log('data', num) + if(!--ten) { + console.log('PAUSE') + gate.pause()//.resume() + d.delay(gate.resume.bind(gate), 10)() + ten = 10 + } + + next(null, num) + }), + es.writeArray(function (err, array) { //just realized that I should remove the error param. errors will be emitted + console.log('eonuhoenuoecbulc') + it(array).deepEqual(hundy) + test.done() + }) + ) + + gate.resume() + +} +require('./helper')(module) diff --git a/node_modules/jsftp/node_modules/event-stream/test/pipeline.asynct.js b/node_modules/jsftp/node_modules/event-stream/test/pipeline.asynct.js new file mode 100644 index 0000000..f07c6fa --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/test/pipeline.asynct.js @@ -0,0 +1,52 @@ +var es = require('..') + +exports['do not duplicate errors'] = function (test) { + + var errors = 0; + var pipe = es.pipeline( + es.through(function(data) { + return this.emit('data', data); + }), + es.through(function(data) { + return this.emit('error', new Error(data)); + }) + ) + + pipe.on('error', function(err) { + errors++ + console.log('error count', errors) + process.nextTick(function () { + return test.done(); + }) + }) + + return pipe.write('meh'); + +} + +exports['3 pipe do not duplicate errors'] = function (test) { + + var errors = 0; + var pipe = es.pipeline( + es.through(function(data) { + return this.emit('data', data); + }), + es.through(function(data) { + return this.emit('error', new Error(data)); + }), + es.through() + ) + + pipe.on('error', function(err) { + errors++ + console.log('error count', errors) + process.nextTick(function () { + return test.done(); + }) + }) + + return pipe.write('meh'); + +} + +require('./helper')(module) diff --git a/node_modules/jsftp/node_modules/event-stream/test/readArray.asynct.js b/node_modules/jsftp/node_modules/event-stream/test/readArray.asynct.js new file mode 100644 index 0000000..3eeb90a --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/test/readArray.asynct.js @@ -0,0 +1,89 @@ + +var es = require('../') + , it = require('it-is').style('colour') + , d = require('ubelt') + +function readStream(stream, pauseAt, done) { + if(!done) done = pauseAt, pauseAt = -1 + var array = [] + stream.on('data', function (data) { + array.push(data) + if(!--pauseAt ) + stream.pause(), done(null, array) + }) + stream.on('error', done) + stream.on('end', function (data) { + done(null, array) + }) + +} + +exports ['read an array'] = function (test) { + + var readThis = d.map(3, 6, 100, d.id) //array of multiples of 3 < 100 + + var reader = es.readArray(readThis) + + var writer = es.writeArray(function (err, array){ + if(err) throw err //unpossible + it(array).deepEqual(readThis) + test.done() + }) + + reader.pipe(writer) +} + +exports ['read an array and pause it.'] = function (test) { + + var readThis = d.map(3, 6, 100, d.id) //array of multiples of 3 < 100 + + var reader = es.readArray(readThis) + + readStream(reader, 10, function (err, data) { + if(err) throw err + it(data).deepEqual([3, 6, 9, 12, 15, 18, 21, 24, 27, 30]) + readStream(reader, 10, function (err, data) { + it(data).deepEqual([33, 36, 39, 42, 45, 48, 51, 54, 57, 60]) + test.done() + }) + reader.resume() + }) + +} + +exports ['reader is readable, but not writeable'] = function (test) { + var reader = es.readArray([1]) + it(reader).has({ + readable: true, + writable: false + }) + + test.done() +} + + +exports ['read one item per tick'] = function (test) { + var readThis = d.map(3, 6, 100, d.id) //array of multiples of 3 < 100 + var drains = 0 + var reader = es.readArray(readThis) + var tickMapper = es.map(function (data,callback) { + process.nextTick(function () { + callback(null, data) + }) + //since tickMapper is returning false + //pipe should pause the writer until a drain occurs + return false + }) + reader.pipe(tickMapper) + readStream(tickMapper, function (err, array) { + it(array).deepEqual(readThis) + it(array.length).deepEqual(readThis.length) + it(drains).equal(readThis.length) + test.done() + }) + tickMapper.on('drain', function () { + drains ++ + }) + +} +require('./helper')(module) diff --git a/node_modules/jsftp/node_modules/event-stream/test/readable.asynct.js b/node_modules/jsftp/node_modules/event-stream/test/readable.asynct.js new file mode 100644 index 0000000..a33988c --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/test/readable.asynct.js @@ -0,0 +1,175 @@ + +var es = require('../') + , it = require('it-is').style('colour') + , u = require('ubelt') + +exports ['read an array'] = function (test) { + + + console.log('readable') + return test.end() + var readThis = u.map(3, 6, 100, u.id) //array of multiples of 3 < 100 + + console.log('readable') + + var reader = + es.readable(function (i, callback) { + if(i >= readThis.length) + return this.emit('end') + console.log('readable') + callback(null, readThis[i]) + }) + + var writer = es.writeArray(function (err, array){ + if(err) throw err + it(array).deepEqual(readThis) + test.done() + }) + + reader.pipe(writer) +} + +exports ['read an array - async'] = function (test) { + + var readThis = u.map(3, 6, 100, u.id) //array of multiples of 3 < 100 + + var reader = + es.readable(function (i, callback) { + if(i >= readThis.length) + return this.emit('end') + u.delay(callback)(null, readThis[i]) + }) + + var writer = es.writeArray(function (err, array){ + if(err) throw err + it(array).deepEqual(readThis) + test.done() + }) + + reader.pipe(writer) +} + + +exports ['emit data then call next() also works'] = function (test) { + + var readThis = u.map(3, 6, 100, u.id) //array of multiples of 3 < 100 + + var reader = + es.readable(function (i, next) { + if(i >= readThis.length) + return this.emit('end') + this.emit('data', readThis[i]) + next() + }) + + var writer = es.writeArray(function (err, array){ + if(err) throw err + it(array).deepEqual(readThis) + test.done() + }) + + reader.pipe(writer) +} + + +exports ['callback emits error, then stops'] = function (test) { + + var err = new Error('INTENSIONAL ERROR') + , called = 0 + + var reader = + es.readable(function (i, callback) { + if(called++) + return + callback(err) + }) + + reader.on('error', function (_err){ + it(_err).deepEqual(err) + u.delay(function() { + it(called).equal(1) + test.done() + }, 50)() + }) +} + +exports['readable does not call read concurrently'] = function (test) { + + var current = 0 + var source = es.readable(function(count, cb){ + current ++ + if(count > 100) + return this.emit('end') + u.delay(function(){ + current -- + it(current).equal(0) + cb(null, {ok: true, n: count}); + })(); + }); + + var destination = es.map(function(data, cb){ + //console.info(data); + cb(); + }); + + var all = es.connect(source, destination); + + destination.on('end', test.done) +} + + +// +// emitting multiple errors is not supported by stream. +// +// I do not think that this is a good idea, at least, there should be an option to pipe to +// continue on error. it makes alot ef sense, if you are using Stream like I am, to be able to emit multiple errors. +// an error might not necessarily mean the end of the stream. it depends on the error, at least. +// +// I will start a thread on the mailing list. I'd rather that than use a custom `pipe` implementation. +// +// basically, I want to be able use pipe to transform objects, and if one object is invalid, +// the next might still be good, so I should get to choose if it's gonna stop. +// re-enstate this test when this issue progresses. +// +// hmm. I could add this to es.connect by deregistering the error listener, +// but I would rather it be an option in core. + +/* +exports ['emit multiple errors, with 2nd parameter (continueOnError)'] = function (test) { + + var readThis = d.map(1, 100, d.id) + , errors = 0 + var reader = + es.readable(function (i, callback) { + console.log(i, readThis.length) + if(i >= readThis.length) + return this.emit('end') + if(!(readThis[i] % 7)) + return callback(readThis[i]) + callback(null, readThis[i]) + }, true) + + var writer = es.writeArray(function (err, array) { + if(err) throw err + it(array).every(function (u){ + it(u % 7).notEqual(0) + }).property('length', 80) + it(errors).equal(14) + test.done() + }) + + reader.on('error', function (u) { + errors ++ + console.log(u) + if('number' !== typeof u) + throw u + + it(u % 7).equal(0) + + }) + + reader.pipe(writer) +} +*/ + +require('./helper')(module) diff --git a/node_modules/jsftp/node_modules/event-stream/test/replace.asynct.js b/node_modules/jsftp/node_modules/event-stream/test/replace.asynct.js new file mode 100644 index 0000000..382082f --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/test/replace.asynct.js @@ -0,0 +1,51 @@ +var es = require('../') + , it = require('it-is').style('colour') + , d = require('ubelt') + , spec = require('stream-spec') + +var next = process.nextTick + +var fizzbuzz = '12F4BF78FB11F1314FB1617F19BF2223FB26F2829FB3132F34BF3738FB41F4344FB4647F49BF5253FB56F5859FB6162F64BF6768FB71F7374FB7677F79BF8283FB86F8889FB9192F94BF9798FB' + , fizz7buzz = '12F4BFseven8FB11F1314FB161sevenF19BF2223FB26F2829FB3132F34BF3seven38FB41F4344FB464sevenF49BF5253FB56F5859FB6162F64BF6seven68FBseven1Fseven3seven4FBseven6sevensevenFseven9BF8283FB86F8889FB9192F94BF9seven98FB' + +exports ['fizz buzz'] = function (test) { + + var readThis = d.map(1, 100, function (i) { + return ( + ! (i % 3 || i % 5) ? "FB" : + !(i % 3) ? "F" : + !(i % 5) ? "B" : + ''+i + ) + }) //array of multiples of 3 < 100 + + var reader = es.readArray(readThis) + var join = es.wait(function (err, string){ + it(string).equal(fizzbuzz) + test.done() + }) + reader.pipe(join) + +} + + +exports ['fizz buzz replace'] = function (test) { + var split = es.split(/(1)/) + var replace = es.replace('7', 'seven') + var x = spec(replace).through() + split + .pipe(replace) + .pipe(es.join(function (err, string) { + it(string).equal(fizz7buzz) + })) + + replace.on('close', function () { + x.validate() + test.done() + }) + + split.write(fizzbuzz) + split.end() + +} +require('./helper')(module) diff --git a/node_modules/jsftp/node_modules/event-stream/test/simple-map.asynct.js b/node_modules/jsftp/node_modules/event-stream/test/simple-map.asynct.js new file mode 100644 index 0000000..25121f9 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/test/simple-map.asynct.js @@ -0,0 +1,343 @@ +'use strict'; + +var es = require('../') + , it = require('it-is') + , u = require('ubelt') + , spec = require('stream-spec') + , Stream = require('stream') + , from = require('from') + , through = require('through') + +//REFACTOR THIS TEST TO USE es.readArray and es.writeArray + +function writeArray(array, stream) { + + array.forEach( function (j) { + stream.write(j) + }) + stream.end() + +} + +function readStream(stream, done) { + + var array = [] + stream.on('data', function (data) { + array.push(data) + }) + stream.on('error', done) + stream.on('end', function (data) { + done(null, array) + }) + +} + +//call sink on each write, +//and complete when finished. + +function pauseStream (prob, delay) { + var pauseIf = ( + 'number' == typeof prob + ? function () { + return Math.random() < prob + } + : 'function' == typeof prob + ? prob + : 0.1 + ) + var delayer = ( + !delay + ? process.nextTick + : 'number' == typeof delay + ? function (next) { setTimeout(next, delay) } + : delay + ) + + return es.through(function (data) { + if(!this.paused && pauseIf()) { + console.log('PAUSE STREAM PAUSING') + this.pause() + var self = this + delayer(function () { + console.log('PAUSE STREAM RESUMING') + self.resume() + }) + } + console.log("emit ('data', " + data + ')') + this.emit('data', data) + }) +} + +exports ['simple map'] = function (test) { + + var input = u.map(1, 1000, function () { + return Math.random() + }) + var expected = input.map(function (v) { + return v * 2 + }) + + var pause = pauseStream(0.1) + var fs = from(input) + var ts = es.writeArray(function (err, ar) { + it(ar).deepEqual(expected) + test.done() + }) + var map = es.through(function (data) { + this.emit('data', data * 2) + }) + + spec(map).through().validateOnExit() + spec(pause).through().validateOnExit() + + fs.pipe(map).pipe(pause).pipe(ts) +} + +exports ['simple map applied to a stream'] = function (test) { + + var input = [1,2,3,7,5,3,1,9,0,2,4,6] + //create event stream from + + var doubler = es.map(function (data, cb) { + cb(null, data * 2) + }) + + spec(doubler).through().validateOnExit() + + //a map is only a middle man, so it is both readable and writable + + it(doubler).has({ + readable: true, + writable: true, + }) + + readStream(doubler, function (err, output) { + it(output).deepEqual(input.map(function (j) { + return j * 2 + })) +// process.nextTick(x.validate) + test.done() + }) + + writeArray(input, doubler) + +} + +exports['pipe two maps together'] = function (test) { + + var input = [1,2,3,7,5,3,1,9,0,2,4,6] + //create event stream from + function dd (data, cb) { + cb(null, data * 2) + } + var doubler1 = es.map(dd), doubler2 = es.map(dd) + + doubler1.pipe(doubler2) + + spec(doubler1).through().validateOnExit() + spec(doubler2).through().validateOnExit() + + readStream(doubler2, function (err, output) { + it(output).deepEqual(input.map(function (j) { + return j * 4 + })) + test.done() + }) + + writeArray(input, doubler1) + +} + +//next: +// +// test pause, resume and drian. +// + +// then make a pipe joiner: +// +// plumber (evStr1, evStr2, evStr3, evStr4, evStr5) +// +// will return a single stream that write goes to the first + +exports ['map will not call end until the callback'] = function (test) { + + var ticker = es.map(function (data, cb) { + process.nextTick(function () { + cb(null, data * 2) + }) + }) + + spec(ticker).through().validateOnExit() + + ticker.write('x') + ticker.end() + + ticker.on('end', function () { + test.done() + }) +} + + +exports ['emit error thrown'] = function (test) { + + var err = new Error('INTENSIONAL ERROR') + , mapper = + es.map(function () { + throw err + }) + + mapper.on('error', function (_err) { + it(_err).equal(err) + test.done() + }) + +// onExit(spec(mapper).basic().validate) +//need spec that says stream may error. + + mapper.write('hello') + +} + +exports ['emit error calledback'] = function (test) { + + var err = new Error('INTENSIONAL ERROR') + , mapper = + es.map(function (data, callback) { + callback(err) + }) + + mapper.on('error', function (_err) { + it(_err).equal(err) + test.done() + }) + + mapper.write('hello') + +} + +exports ['do not emit drain if not paused'] = function (test) { + + var map = es.map(function (data, callback) { + u.delay(callback)(null, 1) + return true + }) + + spec(map).through().pausable().validateOnExit() + + map.on('drain', function () { + it(false).ok('should not emit drain unless the stream is paused') + }) + + it(map.write('hello')).equal(true) + it(map.write('hello')).equal(true) + it(map.write('hello')).equal(true) + setTimeout(function () {map.end()},10) + map.on('end', test.done) +} + +exports ['emits drain if paused, when all '] = function (test) { + var active = 0 + var drained = false + var map = es.map(function (data, callback) { + active ++ + u.delay(function () { + active -- + callback(null, 1) + })() + console.log('WRITE', false) + return false + }) + + spec(map).through().validateOnExit() + + map.on('drain', function () { + drained = true + it(active).equal(0, 'should emit drain when all maps are done') + }) + + it(map.write('hello')).equal(false) + it(map.write('hello')).equal(false) + it(map.write('hello')).equal(false) + + process.nextTick(function () {map.end()},10) + + map.on('end', function () { + console.log('end') + it(drained).ok('shoud have emitted drain before end') + test.done() + }) + +} + +exports ['map applied to a stream with filtering'] = function (test) { + + var input = [1,2,3,7,5,3,1,9,0,2,4,6] + + var doubler = es.map(function (data, callback) { + if (data % 2) + callback(null, data * 2) + else + callback() + }) + + readStream(doubler, function (err, output) { + it(output).deepEqual(input.filter(function (j) { + return j % 2 + }).map(function (j) { + return j * 2 + })) + test.done() + }) + + spec(doubler).through().validateOnExit() + + writeArray(input, doubler) + +} + +exports ['simple mapSync applied to a stream'] = function (test) { + + var input = [1,2,3,7,5,3,1,9,0,2,4,6] + + var doubler = es.mapSync(function (data) { + return data * 2 + }) + + readStream(doubler, function (err, output) { + it(output).deepEqual(input.map(function (j) { + return j * 2 + })) + test.done() + }) + + spec(doubler).through().validateOnExit() + + writeArray(input, doubler) + +} + +exports ['mapSync applied to a stream with filtering'] = function (test) { + + var input = [1,2,3,7,5,3,1,9,0,2,4,6] + + var doubler = es.mapSync(function (data) { + if (data % 2) + return data * 2 + }) + + readStream(doubler, function (err, output) { + it(output).deepEqual(input.filter(function (j) { + return j % 2 + }).map(function (j) { + return j * 2 + })) + test.done() + }) + + spec(doubler).through().validateOnExit() + + writeArray(input, doubler) + +} + +require('./helper')(module) diff --git a/node_modules/jsftp/node_modules/event-stream/test/spec.asynct.js b/node_modules/jsftp/node_modules/event-stream/test/spec.asynct.js new file mode 100644 index 0000000..6d5fb6b --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/test/spec.asynct.js @@ -0,0 +1,86 @@ +/* + assert that data is called many times + assert that end is called eventually + + assert that when stream enters pause state, + on drain is emitted eventually. +*/ + +var es = require('..') +var it = require('it-is').style('colour') +var spec = require('stream-spec') + +exports['simple stream'] = function (test) { + + var stream = es.through() + var x = spec(stream).basic().pausable() + + stream.write(1) + stream.write(1) + stream.pause() + stream.write(1) + stream.resume() + stream.write(1) + stream.end(2) //this will call write() + + process.nextTick(function (){ + x.validate() + test.done() + }) +} + +exports['throw on write when !writable'] = function (test) { + + var stream = es.through() + var x = spec(stream).basic().pausable() + + stream.write(1) + stream.write(1) + stream.end(2) //this will call write() + stream.write(1) //this will be throwing..., but the spec will catch it. + + process.nextTick(function () { + x.validate() + test.done() + }) + +} + +exports['end fast'] = function (test) { + + var stream = es.through() + var x = spec(stream).basic().pausable() + + stream.end() //this will call write() + + process.nextTick(function () { + x.validate() + test.done() + }) + +} + + +/* + okay, that was easy enough, whats next? + + say, after you call paused(), write should return false + until resume is called. + + simple way to implement this: + write must return !paused + after pause() paused = true + after resume() paused = false + + on resume, if !paused drain is emitted again. + after drain, !paused + + there are lots of subtle ordering bugs in streams. + + example, set !paused before emitting drain. + + the stream api is stateful. +*/ + + +require('./helper')(module) diff --git a/node_modules/jsftp/node_modules/event-stream/test/split.asynct.js b/node_modules/jsftp/node_modules/event-stream/test/split.asynct.js new file mode 100644 index 0000000..7d44bac --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/test/split.asynct.js @@ -0,0 +1,47 @@ +var es = require('../') + , it = require('it-is').style('colour') + , d = require('ubelt') + , join = require('path').join + , fs = require('fs') + , Stream = require('stream').Stream + , spec = require('stream-spec') + +exports ['es.split() works like String#split'] = function (test) { + var readme = join(__filename) + , expected = fs.readFileSync(readme, 'utf-8').split('\n') + , cs = es.split() + , actual = [] + , ended = false + , x = spec(cs).through() + + var a = new Stream () + + a.write = function (l) { + actual.push(l.trim()) + } + a.end = function () { + + ended = true + expected.forEach(function (v,k) { + //String.split will append an empty string '' + //if the string ends in a split pattern. + //es.split doesn't which was breaking this test. + //clearly, appending the empty string is correct. + //tests are passing though. which is the current job. + if(v) + it(actual[k]).like(v) + }) + //give the stream time to close + process.nextTick(function () { + test.done() + x.validate() + }) + } + a.writable = true + + fs.createReadStream(readme, {flags: 'r'}).pipe(cs) + cs.pipe(a) + +} + +require('./helper')(module) diff --git a/node_modules/jsftp/node_modules/event-stream/test/stringify.js b/node_modules/jsftp/node_modules/event-stream/test/stringify.js new file mode 100644 index 0000000..d158185 --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/test/stringify.js @@ -0,0 +1,15 @@ + + + +var es = require('../') + +exports['handle buffer'] = function (t) { + + + es.stringify().on('data', function (d) { + t.equal(d.trim(), JSON.stringify('HELLO')) + t.end() + }).write(new Buffer('HELLO')) + +} +require('./helper')(module) diff --git a/node_modules/jsftp/node_modules/event-stream/test/writeArray.asynct.js b/node_modules/jsftp/node_modules/event-stream/test/writeArray.asynct.js new file mode 100644 index 0000000..5d705de --- /dev/null +++ b/node_modules/jsftp/node_modules/event-stream/test/writeArray.asynct.js @@ -0,0 +1,31 @@ + +var es = require('../') + , it = require('it-is').style('colour') + , d = require('ubelt') + +exports ['write an array'] = function (test) { + + var readThis = d.map(3, 6, 100, d.id) //array of multiples of 3 < 100 + + var writer = es.writeArray(function (err, array){ + if(err) throw err //unpossible + it(array).deepEqual(readThis) + test.done() + }) + + d.each(readThis, writer.write.bind(writer)) + writer.end() + +} + + +exports ['writer is writable, but not readable'] = function (test) { + var reader = es.writeArray(function () {}) + it(reader).has({ + readable: false, + writable: true + }) + + test.done() +} +require('./helper')(module) diff --git a/node_modules/jsftp/node_modules/parse-listing/..gitignore.un~ b/node_modules/jsftp/node_modules/parse-listing/..gitignore.un~ new file mode 100644 index 0000000..ca0ee26 Binary files /dev/null and b/node_modules/jsftp/node_modules/parse-listing/..gitignore.un~ differ diff --git a/node_modules/jsftp/node_modules/parse-listing/..travis.yml.un~ b/node_modules/jsftp/node_modules/parse-listing/..travis.yml.un~ new file mode 100644 index 0000000..4612490 Binary files /dev/null and b/node_modules/jsftp/node_modules/parse-listing/..travis.yml.un~ differ diff --git a/node_modules/jsftp/node_modules/parse-listing/.Makefile.un~ b/node_modules/jsftp/node_modules/parse-listing/.Makefile.un~ new file mode 100644 index 0000000..80d06e7 Binary files /dev/null and b/node_modules/jsftp/node_modules/parse-listing/.Makefile.un~ differ diff --git a/node_modules/jsftp/node_modules/parse-listing/.README.md.un~ b/node_modules/jsftp/node_modules/parse-listing/.README.md.un~ new file mode 100644 index 0000000..1fc33a3 Binary files /dev/null and b/node_modules/jsftp/node_modules/parse-listing/.README.md.un~ differ diff --git a/node_modules/jsftp/node_modules/parse-listing/.idea/.name b/node_modules/jsftp/node_modules/parse-listing/.idea/.name new file mode 100644 index 0000000..89accd2 --- /dev/null +++ b/node_modules/jsftp/node_modules/parse-listing/.idea/.name @@ -0,0 +1 @@ +parse-listing \ No newline at end of file diff --git a/node_modules/jsftp/node_modules/parse-listing/.idea/encodings.xml b/node_modules/jsftp/node_modules/parse-listing/.idea/encodings.xml new file mode 100644 index 0000000..e206d70 --- /dev/null +++ b/node_modules/jsftp/node_modules/parse-listing/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/node_modules/jsftp/node_modules/parse-listing/.idea/inspectionProfiles/Project_Default.xml b/node_modules/jsftp/node_modules/parse-listing/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..c66df00 --- /dev/null +++ b/node_modules/jsftp/node_modules/parse-listing/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/node_modules/jsftp/node_modules/parse-listing/.idea/inspectionProfiles/profiles_settings.xml b/node_modules/jsftp/node_modules/parse-listing/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..3b31283 --- /dev/null +++ b/node_modules/jsftp/node_modules/parse-listing/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/node_modules/jsftp/node_modules/parse-listing/.idea/misc.xml b/node_modules/jsftp/node_modules/parse-listing/.idea/misc.xml new file mode 100644 index 0000000..d16ceba --- /dev/null +++ b/node_modules/jsftp/node_modules/parse-listing/.idea/misc.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/node_modules/jsftp/node_modules/parse-listing/.idea/modules.xml b/node_modules/jsftp/node_modules/parse-listing/.idea/modules.xml new file mode 100644 index 0000000..44e569a --- /dev/null +++ b/node_modules/jsftp/node_modules/parse-listing/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/node_modules/jsftp/node_modules/parse-listing/.idea/parse-listing.iml b/node_modules/jsftp/node_modules/parse-listing/.idea/parse-listing.iml new file mode 100644 index 0000000..6b8184f --- /dev/null +++ b/node_modules/jsftp/node_modules/parse-listing/.idea/parse-listing.iml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/node_modules/jsftp/node_modules/parse-listing/.idea/scopes/scope_settings.xml b/node_modules/jsftp/node_modules/parse-listing/.idea/scopes/scope_settings.xml new file mode 100644 index 0000000..922003b --- /dev/null +++ b/node_modules/jsftp/node_modules/parse-listing/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/node_modules/jsftp/node_modules/parse-listing/.idea/vcs.xml b/node_modules/jsftp/node_modules/parse-listing/.idea/vcs.xml new file mode 100644 index 0000000..c80f219 --- /dev/null +++ b/node_modules/jsftp/node_modules/parse-listing/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/node_modules/jsftp/node_modules/parse-listing/.idea/workspace.xml b/node_modules/jsftp/node_modules/parse-listing/.idea/workspace.xml new file mode 100644 index 0000000..269558c --- /dev/null +++ b/node_modules/jsftp/node_modules/parse-listing/.idea/workspace.xml @@ -0,0 +1,358 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GeneralJavaScript + + + JavaScript + + + Potentially confusing code constructsJavaScript + + + Probable bugsJavaScript + + + Spelling + + + + + Spelling + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1370715995211 + 1370715995211 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/node_modules/jsftp/node_modules/parse-listing/.npmignore b/node_modules/jsftp/node_modules/parse-listing/.npmignore new file mode 100644 index 0000000..8214859 --- /dev/null +++ b/node_modules/jsftp/node_modules/parse-listing/.npmignore @@ -0,0 +1,3 @@ +node_modules +reports +lcov-report diff --git a/node_modules/jsftp/node_modules/parse-listing/.package.json.un~ b/node_modules/jsftp/node_modules/parse-listing/.package.json.un~ new file mode 100644 index 0000000..db0166b Binary files /dev/null and b/node_modules/jsftp/node_modules/parse-listing/.package.json.un~ differ diff --git a/node_modules/jsftp/node_modules/parse-listing/.parser.js.un~ b/node_modules/jsftp/node_modules/parse-listing/.parser.js.un~ new file mode 100644 index 0000000..d83cf00 Binary files /dev/null and b/node_modules/jsftp/node_modules/parse-listing/.parser.js.un~ differ diff --git a/node_modules/jsftp/node_modules/parse-listing/.parset_test.js.un~ b/node_modules/jsftp/node_modules/parse-listing/.parset_test.js.un~ new file mode 100644 index 0000000..de15bb6 Binary files /dev/null and b/node_modules/jsftp/node_modules/parse-listing/.parset_test.js.un~ differ diff --git a/node_modules/jsftp/node_modules/parse-listing/.travis.yml b/node_modules/jsftp/node_modules/parse-listing/.travis.yml new file mode 100644 index 0000000..9ce59d7 --- /dev/null +++ b/node_modules/jsftp/node_modules/parse-listing/.travis.yml @@ -0,0 +1,7 @@ +language: node_js +node_js: + - "0.10" + - "0.8" + - "0.6" +before_script: + - "npm install --dev" diff --git a/node_modules/jsftp/node_modules/parse-listing/Makefile b/node_modules/jsftp/node_modules/parse-listing/Makefile new file mode 100644 index 0000000..84b8ccf --- /dev/null +++ b/node_modules/jsftp/node_modules/parse-listing/Makefile @@ -0,0 +1,39 @@ +# use the tools as dev dependencies rather than installing them globaly +# it lets you handle specific versions of the tooling for each of your projects +MOCHA=node_modules/.bin/mocha +ISTANBUL=node_modules/.bin/istanbul +JSHINT=node_modules/.bin/jshint + +# test files must end with ".test.js" +TESTS=$(shell find test/ -name "*.test.js") + +clean: + rm -rf reports + +test: + $(MOCHA) -R spec $(TESTS) + +coverage: + @# check if reports folder exists, if not create it + @test -d reports || mkdir reports + $(ISTANBUL) instrument --output src-cov src + @# move original src code and replace it by the instrumented one + mv src src-orig && mv src-cov src + @# tell istanbul to only generate the lcov file + ISTANBUL_REPORTERS=lcovonly $(MOCHA) -R mocha-istanbul $(TESTS) + @# place the lcov report in the report folder, remove instrumented code + @# and reput src at its place + mv lcov.info reports/ + rm -rf src + mv src-orig src + genhtml reports/lcov.info --output-directory reports/ + +jshint: + $(JSHINT) src test --show-non-errors + +checkstyle: + @# check if reports folder exists, if not create it + @test -d reports || mkdir reports + $(JSHINT) src test --reporter=checkstyle > reports/checkstyle.xml + +.PHONY: clean test coverage jshint checkstyle diff --git a/node_modules/jsftp/node_modules/parse-listing/README.md b/node_modules/jsftp/node_modules/parse-listing/README.md new file mode 100644 index 0000000..1cddefd --- /dev/null +++ b/node_modules/jsftp/node_modules/parse-listing/README.md @@ -0,0 +1,115 @@ +## parse-listing [![Build Status](https://secure.travis-ci.org/sergi/parse-listing.png)](http://travis-ci.org/sergi/parse-listing) + +Simple library that parses file listing input for different platforms. An +example file listing is the output of the `ls` command in unix, or the `dir` +command in DOS/Windows platforms. + +## Example + +```javascript +var Parser = require("parse-listing"); + +// Yeah yeah, multiline strings would make Crockford mad, I know. +var str = "drwxr-xr-x 5 1001 1001 4096 Jan 09 11:52 .\r\n\ +drwxr-xr-x 4 0 0 4096 Sep 19 13:50 ..\r\n\ +-rw------- 1 1001 1001 1118 Jan 09 12:09 .bash_history\r\n\ +-rw------- 1 1001 1001 943 Jan 09 11:52 .viminfo\r\n\ +drwxrwxr-x 5 1001 1001 4096 Jan 09 11:52 inaccessible\r\n\ +drwxrwxrwx 2 1001 1001 4096 Sep 21 11:20 project1\r\n\ +drwx------ 2 1001 1001 4096 Oct 19 16:17 project2\r\n"; + +Parser.parseEntries(str, function(err, entryArray) { + entryArray.forEach(function(entry, i) { + console.log("Name:",entry.name); + console.log("Type", entry.type); + console.log("Size:", entry.size); + }); +}); +``` + +The example above will print the name, type and size for every entry in the +listing. A parsed line becomes a JavaScript object like this: + +```javascript + { + type: 1, + size: 4096, + name: "project2", + time: 1382192220000, + owner: "1001", + group: "1001", + + userPermissions: { + read: true, + write: true, + exec: true + }, + + groupPermissions: { + read: false, + write: false, + exec: false + }, + + otherPermissions: { + read: false, + write: false, + exec: false + } + } +``` + +## Methods + +### parseEntries(string | array, callback) + +Asynchronously parses a list of entries either in a single string (entries will +be split by newlines) or in an array of strings. Invokes the callback when all +the entries have been processed, and passes an array of JavaScript objects. + +### parseEntry(string) + +Parses a single string such as: + +`drwxrwxrwx 2 1001 1001 4096 Sep 21 11:20 project` + +and returns the JavaScript object for it. There is no need to specify whether the +entry is in UNIX or MS-DOS/Windows format. The parser will find out by itself. + +## Installation + +With NPM: + + npm install parse-listing + +From GitHub: + + git clone https://github.com/sergi/parse-listing.git + +## License + +The MIT License + +Copyright(c) 2013 Sergi Mansilla + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + diff --git a/node_modules/jsftp/node_modules/parse-listing/package.json b/node_modules/jsftp/node_modules/parse-listing/package.json new file mode 100644 index 0000000..706fee8 --- /dev/null +++ b/node_modules/jsftp/node_modules/parse-listing/package.json @@ -0,0 +1,55 @@ +{ + "name": "parse-listing", + "id": "parse-listing", + "version": "1.0.1", + "description": "Small library to parse file listings into JavaScript objects", + "keywords": [ + "parse", + "ls", + "list", + "listing", + "ftp", + "unix", + "dos" + ], + "author": { + "name": "Sergi Mansilla", + "email": "sergi.mansilla@gmail.com", + "url": "http://sergimansilla.com" + }, + "homepage": "https://github.com/sergi/parse-listing", + "repository": { + "type": "git", + "url": "https://github.com/sergi/parse-listing.git" + }, + "bugs": { + "url": "https://github.com/sergi/parse-listing/issues" + }, + "dependencies": { + "async": "~0.2.9" + }, + "devDependencies": { + "mocha": "~1.10.0", + "istanbul": "~0.1.36", + "mocha-istanbul": "~0.2.0" + }, + "main": "src/parser.js", + "engines": { + "node": ">=0.6.21" + }, + "scripts": { + "test": "mocha -R spec -t 5000" + }, + "licenses": [ + { + "type": "MIT", + "url": "https://github.com/sergi/parse-listing/blob/master/LICENSE" + } + ], + "readme": "## parse-listing [![Build Status](https://secure.travis-ci.org/sergi/parse-listing.png)](http://travis-ci.org/sergi/parse-listing)\n\nSimple library that parses file listing input for different platforms. An\nexample file listing is the output of the `ls` command in unix, or the `dir`\ncommand in DOS/Windows platforms.\n\n## Example\n\n```javascript\nvar Parser = require(\"parse-listing\");\n\n// Yeah yeah, multiline strings would make Crockford mad, I know.\nvar str = \"drwxr-xr-x 5 1001 1001 4096 Jan 09 11:52 .\\r\\n\\\ndrwxr-xr-x 4 0 0 4096 Sep 19 13:50 ..\\r\\n\\\n-rw------- 1 1001 1001 1118 Jan 09 12:09 .bash_history\\r\\n\\\n-rw------- 1 1001 1001 943 Jan 09 11:52 .viminfo\\r\\n\\\ndrwxrwxr-x 5 1001 1001 4096 Jan 09 11:52 inaccessible\\r\\n\\\ndrwxrwxrwx 2 1001 1001 4096 Sep 21 11:20 project1\\r\\n\\\ndrwx------ 2 1001 1001 4096 Oct 19 16:17 project2\\r\\n\";\n\nParser.parseEntries(str, function(err, entryArray) {\n entryArray.forEach(function(entry, i) {\n console.log(\"Name:\",entry.name);\n console.log(\"Type\", entry.type);\n console.log(\"Size:\", entry.size);\n });\n});\n```\n\nThe example above will print the name, type and size for every entry in the\nlisting. A parsed line becomes a JavaScript object like this:\n\n```javascript\n {\n type: 1,\n size: 4096,\n name: \"project2\",\n time: 1382192220000,\n owner: \"1001\",\n group: \"1001\",\n\n userPermissions: {\n read: true,\n write: true,\n exec: true\n },\n\n groupPermissions: {\n read: false,\n write: false,\n exec: false\n },\n\n otherPermissions: {\n read: false,\n write: false,\n exec: false\n }\n }\n```\n\n## Methods\n\n### parseEntries(string | array, callback)\n\nAsynchronously parses a list of entries either in a single string (entries will\nbe split by newlines) or in an array of strings. Invokes the callback when all\nthe entries have been processed, and passes an array of JavaScript objects.\n\n### parseEntry(string)\n\nParses a single string such as:\n\n`drwxrwxrwx 2 1001 1001 4096 Sep 21 11:20 project`\n\nand returns the JavaScript object for it. There is no need to specify whether the\nentry is in UNIX or MS-DOS/Windows format. The parser will find out by itself.\n\n## Installation\n\nWith NPM:\n\n npm install parse-listing\n\nFrom GitHub:\n\n git clone https://github.com/sergi/parse-listing.git\n\n## License\n\nThe MIT License\n\nCopyright(c) 2013 Sergi Mansilla \n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\n\n\n\n", + "_id": "parse-listing@1.0.1", + "dist": { + "shasum": "3c24c2694ad07dd88959cc1d4d8c4b77699113c8" + }, + "_from": "parse-listing@~1.0.0" +} diff --git a/node_modules/jsftp/node_modules/parse-listing/src/.parser.js.un~ b/node_modules/jsftp/node_modules/parse-listing/src/.parser.js.un~ new file mode 100644 index 0000000..23e16e3 Binary files /dev/null and b/node_modules/jsftp/node_modules/parse-listing/src/.parser.js.un~ differ diff --git a/node_modules/jsftp/node_modules/parse-listing/src/parser.js b/node_modules/jsftp/node_modules/parse-listing/src/parser.js new file mode 100644 index 0000000..1d9ef4b --- /dev/null +++ b/node_modules/jsftp/node_modules/parse-listing/src/parser.js @@ -0,0 +1,281 @@ +/** + * @package parse-listing + * @author Sergi Mansilla + * @license https://github.com/sergi/parse-listing/blob/master/LICENSE MIT License + */ + +"use strict"; + +var async = require("async"); + +/** + * this is the regular expression used by Unix Parsers. + * + * Permissions: + * r the file is readable + * w the file is writable + * x the file is executable + * - the indicated permission is not granted + * L mandatory locking occurs during access (the set-group-ID bit is + * on and the group execution bit is off) + * s the set-user-ID or set-group-ID bit is on, and the corresponding + * user or group execution bit is also on + * S undefined bit-state (the set-user-ID bit is on and the user + * execution bit is off) + * t the 1000 (octal) bit, or sticky bit, is on [see chmod(1)], and + * execution is on + * T the 1000 bit is turned on, and execution is off (undefined bit- + * state) + */ + +var RE_UnixEntry = new RegExp( + "([bcdlfmpSs-])" + + "(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?\\s+" + + "(\\d+)\\s+" + + "(\\S+)\\s+" + + "(?:(\\S+)\\s+)?" + + "(\\d+)\\s+" + + //numeric or standard format date + + "((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S+\\s+\\S+))\\s+" + + // year (for non-recent standard format) + // or time (for numeric or recent standard format) + + "(\\d+(?::\\d+)?)\\s*" + + //+ "(\\S*)(\\s*.*)" + + "(.*)" +); + +// MSDOS format +// 04-27-00 09:09PM licensed +// 07-18-00 10:16AM pub +// 04-14-00 03:47PM 589 readme.htm +var RE_DOSEntry = new RegExp( + "(\\S+)\\s+(\\S+)\\s+" + + "()?\\s*" + + "([0-9]+)?\\s*" + + "(\\S.*)" +); + +// Not used for now +// var RE_VMSEntry = new RegExp( +// "(.*;[0-9]+)\\s*" +// + "(\\d+)/\\d+\\s*" +// + "(\\S+)\\s+(\\S+)\\s+" +// + "\\[(([0-9$A-Za-z_]+)|([0-9$A-Za-z_]+),([0-9$a-zA-Z_]+))\\]?\\s*" +// + "\\([a-zA-Z]*,[a-zA-Z]*,[a-zA-Z]*,[a-zA-Z]*\\)" +// ); + +exports.nodeTypes = { + FILE_TYPE: 0, + DIRECTORY_TYPE: 1, + SYMBOLIC_LINK_TYPE: 2, + UNKNOWN_TYPE: 3 +}; + +exports.permissions = { + READ_PERMISSION: 0, + WRITE_PERMISSION: 1, + EXECUTE_PERMISSION: 2 +}; + +exports.access = { + USER_ACCESS: 0, + GROUP_ACCESS: 1, + WORLD_ACCESS: 2 +}; + +/** + * Selects which parser to use depending on the first character of the line to + * parse. + * + * @param entries {Array.|string} FTP file entry line. + * @param callback {Function} Callback function with error or result. + */ +exports.parseEntries = function(entries, callback) { + if (typeof entries === "string") { + entries = entries.split(/(\r\n|\n)/); + } + async.map(entries, parseEntry, callback); +}; + +/** + * Selects which parser to use depending on the first character of the line to + * parse. + * + * @param entry {string} FTP file entry line + * @returns {Object|null} Parsed object with the file entry properties + */ +var parseEntry = exports.parseEntry = function(entry) { + var c = entry.charAt(0); + + if ('bcdlps-'.indexOf(c) > -1) { + return parsers.unix(entry); + } + else if ('0123456789'.indexOf(c) > -1) { + return parsers.msdos(entry); + } + else { + return null; + } +}; + +var parsers = { + unix: function(entry) { + var target, writePerm, readPerm, execPerm; + var group = entry.match(RE_UnixEntry); + + if (group) { + var type = group[1]; + //var hardLinks = group[15]; + var usr = group[16]; + var grp = group[17]; + var size = group[18]; + var name = group[21]; + + var date; + // Check whether we are given the time (recent file) or the year + // (older file) in the file listing. + if (group[20].indexOf(":") === -1) { + date = +new Date(group[19] + " " + group[20]).getTime(); + } + else { + var currentMonth = new Date().getMonth(); + var month = new Date(group[19]).getMonth(); + var year = new Date().getFullYear() - (currentMonth < month ? 1 : 0); + + date = +new Date(group[19] + " " + group[20] + " " + year); + } + + // Ignoring '.' and '..' entries for now + if (name === "." || name === "..") + return; + + //var endtoken = group[22]; + + switch (type[0]) { + case 'd': + type = exports.nodeTypes.DIRECTORY_TYPE; + break; + case 'l': + type = exports.nodeTypes.SYMBOLIC_LINK_TYPE; + var isLink = /(.*)\s->\s(.*)/.exec(name); + if (isLink) { + name = isLink[1]; + target = isLink[2]; + } + break; + case 'b': + case 'c': + // break; - fall through + case 'f': + case '-': + type = exports.nodeTypes.FILE_TYPE; + break; + default: + type = exports.nodeTypes.UNKNOWN_TYPE; + } + + var file = { + name: name, + type: type, + time: date, + size: size, + owner: usr, + group: grp + }; + + if (target) file.target = target; + + var g = 4; + ["user", "group", "other"].forEach(function(access) { + // Use != '-' to avoid having to check for suid and sticky bits + readPerm = group[g] !== "-"; + writePerm = group[g + 1] !== "-"; + + var execPermStr = group[g + 2]; + + file[access + "Permissions"] = { + read: readPerm, + write: writePerm, + exec: (execPermStr !== "-") && !(/[A-Z]/.test(execPermStr[0])) + }; + + g += 4; + }); + + return file; + } + }, + + msdos: function(entry) { + var group = entry.match(RE_DOSEntry); + var type; + + if (!group) + return null; + + var replacer = function replacer(str, hour, min, ampm, offset, s) { + return hour + ":" + min + " " + ampm; + }; + + var time = group[2].replace(/(\d{2}):(\d{2})([AP]M)/, replacer); + var date = new Date(group[1] + " " + time).getTime(); + var dirString = group[3]; + var size = group[4]; + var name = group[5]; + + if (null == name || name === "." || name === "..") + return null; + + if (dirString === "") { + type = exports.nodeTypes.DIRECTORY_TYPE; + size = 0; + } + else { + type = exports.nodeTypes.FILE_TYPE; + } + + return { + name: name, + type: type, + time: date, + size: size + }; + } +}; + +/* + * MLSx commands are not being used for now. + * + * http://rfc-ref.org/RFC-TEXTS/3659/chapter7.html + * http://www.rhinosoft.com/newsletter/NewsL2005-07-06.asp?prod=rs + * + var reKV = /(.+?)=(.+?);/; + exports.parseMList = function(line) { + var ret; + var result = line.trim().split(reKV); + + if (result && result.length > 0) { + ret = {}; + if (result.length === 1) { + ret.name = result[0].trim(); + } + else { + var i, k, v, len = result.length; + for (i = 1; i < len; i += 3) { + k = result[i]; + v = result[i+1]; + ret[k] = v; + } + ret.name = result[result.length-1].trim(); + } + } else + ret = line; + + return ret; + } + */ + + diff --git a/node_modules/jsftp/node_modules/parse-listing/test/.parser.test.js.un~ b/node_modules/jsftp/node_modules/parse-listing/test/.parser.test.js.un~ new file mode 100644 index 0000000..98e2321 Binary files /dev/null and b/node_modules/jsftp/node_modules/parse-listing/test/.parser.test.js.un~ differ diff --git a/node_modules/jsftp/node_modules/parse-listing/test/.parser_test.js.un~ b/node_modules/jsftp/node_modules/parse-listing/test/.parser_test.js.un~ new file mode 100644 index 0000000..a798f2d Binary files /dev/null and b/node_modules/jsftp/node_modules/parse-listing/test/.parser_test.js.un~ differ diff --git a/node_modules/jsftp/node_modules/parse-listing/test/parser.test.js b/node_modules/jsftp/node_modules/parse-listing/test/parser.test.js new file mode 100644 index 0000000..017bfb5 --- /dev/null +++ b/node_modules/jsftp/node_modules/parse-listing/test/parser.test.js @@ -0,0 +1,804 @@ +/* + * @package jsftp + * @copyright Copyright(c) 2011 Ajax.org B.V. + * @author Sergi Mansilla + * @license https://github.com/sergi/jsFTP/blob/master/LICENSE MIT License + */ + +"use strict"; + +var assert = require("assert"); +var Parser = require("../src/parser"); + +describe("jsftp file listing parser", function() { + it("test ftp unix STAT responses", function() { + var str = "drwxr-xr-x 5 1001 1001 4096 Jan 09 11:52 .\r\n\ +drwxr-xr-x 4 0 0 4096 Sep 19 13:50 ..\r\n\ +-rw------- 1 1001 1001 1118 Jan 09 12:09 .bash_history\r\n\ +-rw------- 1 1001 1001 943 Jan 09 11:52 .viminfo\r\n\ +drwxrwxr-x 5 1001 1001 4096 Jan 09 11:52 inaccessible\r\n\ +drwxrwxrwx 2 1001 1001 4096 Sep 21 11:20 project1\r\n\ +drwx------ 2 1001 1001 4096 Oct 19 16:17 project2\r\n"; + + var unixEntries = [ + { + type: 0, + size: 1118, + name: ".bash_history", + time: +new Date("Jan 9 12:09 " + new Date().getFullYear()), + owner: "1001", + group: "1001", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: false, + + groupReadPerm: false, + groupWritePerm: false, + groupExecPerm: false, + + otherReadPerm: false, + otherWritePerm: false, + otherExecPerm: false + }, + { + type: 0, + size: 943, + name: ".viminfo", + time: +new Date("Jan 9 11:52 " + new Date().getFullYear()), + owner: "1001", + group: "1001", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: false, + + groupReadPerm: false, + groupWritePerm: false, + groupExecPerm: false, + + otherReadPerm: false, + otherWritePerm: false, + otherExecPerm: false + }, + { + type: 1, + size: 4096, + name: "inaccessible", + time: +new Date("Jan 9 11:52 " + new Date().getFullYear()), + owner: "1001", + group: "1001", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: true, + + groupReadPerm: true, + groupWritePerm: true, + groupExecPerm: true, + + otherReadPerm: true, + otherWritePerm: false, + otherExecPerm: true + }, + { + type: 1, + size: 4096, + name: "project1", + time: +new Date("Sep 21 11:20 " + new Date().getFullYear()), + owner: "1001", + group: "1001", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: true, + + groupReadPerm: true, + groupWritePerm: true, + groupExecPerm: true, + + otherReadPerm: true, + otherWritePerm: true, + otherExecPerm: true + }, + { + type: 1, + size: 4096, + name: "project2", + time: +new Date("Oct 19 16:17 " + new Date().getFullYear()), + owner: "1001", + group: "1001", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: true, + + groupReadPerm: false, + groupWritePerm: false, + groupExecPerm: false, + + otherReadPerm: false, + otherWritePerm: false, + otherExecPerm: false + } + ]; + + Parser.parseEntries(str, function(err, entryArray) { + entryArray.forEach(function(entry, i) { + assert.equal(unixEntries[i].type, entry.type); + assert.equal(unixEntries[i].size, entry.size); + assert.equal(unixEntries[i].name, entry.name); + //assert.equal(unixEntries[i].time, entry.time); + assert.equal(unixEntries[i].owner, entry.owner); + assert.equal(unixEntries[i].group, entry.group); + + assert.equal(unixEntries[i].userReadPerm, entry.userPermissions.read); + assert.equal(unixEntries[i].userWritePerm, entry.userPermissions.write); + assert.equal(unixEntries[i].userExecPerm, entry.userPermissions.exec); + + assert.equal(unixEntries[i].groupReadPerm, entry.groupPermissions.read); + assert.equal(unixEntries[i].groupWritePerm, entry.groupPermissions.write); + assert.equal(unixEntries[i].groupExecPerm, entry.groupPermissions.exec); + + assert.equal(unixEntries[i].otherReadPerm, entry.otherPermissions.read); + assert.equal(unixEntries[i].otherWritePerm, entry.otherPermissions.write); + assert.equal(unixEntries[i].otherExecPerm, entry.otherPermissions.exec); + }); + }) + }); + + it("test ftp unix LIST responses", function() { + var str = "drwx--x--- 10 mrclash adm 4096 Aug 9 14:48 .\r\n\ + drwx--x--- 10 mrclash adm 4096 Aug 9 14:48 ..\r\n\ + -rw-r--r-- 1 mrclash pg223090 260 Mar 25 2008 .alias\r\n\ + -rw------- 1 mrclash pg223090 2219 Sep 5 2010 .bash_history\r\n\ + -rw-r--r-- 1 mrclash pg223090 55 Mar 25 2008 .bashrc\r\n\ + drwx------ 2 mrclash pg223090 4096 Aug 9 14:39 .ssh\r\n\ + -rw-r--r-- 1 mrclash pg223090 18 Aug 8 13:06 Cloud9 FTP connection test.\r\n\ + -rwxr-xr-x 1 mrclash pg223090 68491314 Jan 22 2009 Documents.zip\r\n\ + -rwxr-xr-x 1 mrclash pg223090 141 Nov 1 2008 EcPxMptYISIdOSjS.XFV.Q--.html\r\n\ + dr-xr-x--- 7 mrclash dhapache 4096 May 29 07:47 logs\r\n\ + drwxr-xr-x 7 mrclash pg223090 4096 Aug 9 14:48 re-alpine.git\r\n\ + -rwxr-xr-x 1 mrclash pg223090 312115 Jan 22 2009 restaurants.csv\r\n\ + drwxr-xr-x 12 mrclash pg223090 4096 Jul 24 02:42 sergimansilla.com\r\n\ + drwxr-xr-x 10 mrclash pg223090 4096 Aug 3 2009 svn\r\n\ + -rwxr-xr-x 1 mrclash pg223090 76 Aug 9 14:47 sync-alpine.sh\r\n\ + drwxr-xr-x 2 mrclash pg223090 4096 Aug 4 10:00 test_c9\r\n\ + -rw-r--r-- 1 mrclash pg223090 4 Aug 4 09:11 testfile.txt\r\n\ + lrwxr-xr-x 1 sergi staff 11 Jul 7 2011 .vimrc -> .vim/.vimrc\r\n"; + + var unixEntries = [ + { + //line: "-rw-r--r-- 1 mrclash pg223090 260 Mar 25 2008 .alias", + type: 0, + size: 260, + name: ".alias", + time: +new Date("Mar 25 2008"), + owner: "mrclash", + group: "pg223090", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: false, + + groupReadPerm: true, + groupWritePerm: false, + groupExecPerm: false, + + otherReadPerm: true, + otherWritePerm: false, + otherExecPerm: false + }, + { + //line: "-rw------- 1 mrclash pg223090 2219 Sep 5 2010 .bash_history", + type: 0, + size: 2219, + name: ".bash_history", + time: +new Date("Sep 5 2010"), + owner: "mrclash", + group: "pg223090", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: false, + + groupReadPerm: false, + groupWritePerm: false, + groupExecPerm: false, + + otherReadPerm: false, + otherWritePerm: false, + otherExecPerm: false + }, + { + type: 0, + size: 55, + name: ".bashrc", + time: +new Date("Mar 25 2008"), + owner: "mrclash", + group: "pg223090", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: false, + + groupReadPerm: true, + groupWritePerm: false, + groupExecPerm: false, + + otherReadPerm: true, + otherWritePerm: false, + otherExecPerm: false + }, + { + type: 1, + size: 4096, + name: ".ssh", + time: +new Date("Aug 9 14:39 " + new Date().getFullYear()), + owner: "mrclash", + group: "pg223090", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: true, + + groupReadPerm: false, + groupWritePerm: false, + groupExecPerm: false, + + otherReadPerm: false, + otherWritePerm: false, + otherExecPerm: false + }, + { + type: 0, + size: 18, + name: "Cloud9 FTP connection test.", + time: +new Date("Aug 8 13:06 " + new Date().getFullYear()), + owner: "mrclash", + group: "pg223090", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: false, + + groupReadPerm: true, + groupWritePerm: false, + groupExecPerm: false, + + otherReadPerm: true, + otherWritePerm: false, + otherExecPerm: false + }, + { + type: 0, + size: 68491314, + name: "Documents.zip", + time: +new Date("Jan 22 2009"), + owner: "mrclash", + group: "pg223090", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: true, + + groupReadPerm: true, + groupWritePerm: false, + groupExecPerm: true, + + otherReadPerm: true, + otherWritePerm: false, + otherExecPerm: true + }, + { + type: 0, + size: 141, + name: "EcPxMptYISIdOSjS.XFV.Q--.html", + time: +new Date("Nov 1 2008"), + owner: "mrclash", + group: "pg223090", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: true, + + groupReadPerm: true, + groupWritePerm: false, + groupExecPerm: true, + + otherReadPerm: true, + otherWritePerm: false, + otherExecPerm: true + }, + { + type: 1, + size: 4096, + name: "logs", + time: +new Date("May 29 07:47 " + new Date().getFullYear()), + owner: "mrclash", + group: "dhapache", + + userReadPerm: true, + userWritePerm: false, + userExecPerm: true, + + groupReadPerm: true, + groupWritePerm: false, + groupExecPerm: true, + + otherReadPerm: false, + otherWritePerm: false, + otherExecPerm: false + }, + { + type: 1, + size: 4096, + name: "re-alpine.git", + time: +new Date("Aug 9 14:48 " + new Date().getFullYear()), + owner: "mrclash", + group: "pg223090", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: true, + + groupReadPerm: true, + groupWritePerm: false, + groupExecPerm: true, + + otherReadPerm: true, + otherWritePerm: false, + otherExecPerm: true + }, + { + type: 0, + size: 312115, + time: +new Date("Jan 22 2009"), + name: "restaurants.csv", + owner: "mrclash", + group: "pg223090", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: true, + + groupReadPerm: true, + groupWritePerm: false, + groupExecPerm: true, + + otherReadPerm: true, + otherWritePerm: false, + otherExecPerm: true + }, + { + type: 1, + size: 4096, + time: +new Date("Jul 24 02:42 " + new Date().getFullYear()), + name: "sergimansilla.com", + owner: "mrclash", + group: "pg223090", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: true, + + groupReadPerm: true, + groupWritePerm: false, + groupExecPerm: true, + + otherReadPerm: true, + otherWritePerm: false, + otherExecPerm: true + }, + { + type: 1, + size: 4096, + time: +new Date("Aug 3 2009"), + name: "svn", + owner: "mrclash", + group: "pg223090", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: true, + + groupReadPerm: true, + groupWritePerm: false, + groupExecPerm: true, + + otherReadPerm: true, + otherWritePerm: false, + otherExecPerm: true + }, + { + type: 0, + size: 76, + time: +new Date("Aug 9 14:47 " + new Date().getFullYear()), + name: "sync-alpine.sh", + owner: "mrclash", + group: "pg223090", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: true, + + groupReadPerm: true, + groupWritePerm: false, + groupExecPerm: true, + + otherReadPerm: true, + otherWritePerm: false, + otherExecPerm: true + }, + { + type: 1, + size: 4096, + time: +new Date("Aug 4 10:00 " + new Date().getFullYear()), + name: "test_c9", + owner: "mrclash", + group: "pg223090", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: true, + + groupReadPerm: true, + groupWritePerm: false, + groupExecPerm: true, + + otherReadPerm: true, + otherWritePerm: false, + otherExecPerm: true + }, + { + type: 0, + size: 4, + time: +new Date("Aug 4 09:11 " + new Date().getFullYear()), + name: "testfile.txt", + owner: "mrclash", + group: "pg223090", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: false, + + groupReadPerm: true, + groupWritePerm: false, + groupExecPerm: false, + + otherReadPerm: true, + otherWritePerm: false, + otherExecPerm: false + }, + { + type: 2, + size: 11, + time: +new Date("Jul 7 2011"), + name: ".vimrc", + target: ".vim/.vimrc", + owner: "sergi", + group: "staff", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: true, + + groupReadPerm: true, + groupWritePerm: false, + groupExecPerm: true, + + otherReadPerm: true, + otherWritePerm: false, + otherExecPerm: true + } + ]; + + var str2 = "\ +-drwxr-x--- 2 userName alternc 4096 Aug 22 03:45 .\r\n\ +-drwxr-x--- 5 userName alternc 4096 Aug 22 03:45 ..\r\n\ +--rw-r----- 1 userName alternc 460 Aug 22 03:45 test1\r\n\ +--rw-r----- 1 userName alternc 560 Aug 22 03:47 test2\r\n"; + + var unixEntries2 = [ + { + //line: "-rw-r--r-- 1 mrclash pg223090 260 Mar 25 2008 .alias", + type: 0, + size: 460, + name: "test1", + time: +new Date("Aug 22 03:45 " + new Date().getFullYear()), + owner: "userName", + group: "alternc", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: false, + + groupReadPerm: true, + groupWritePerm: false, + groupExecPerm: false, + + otherReadPerm: false, + otherWritePerm: false, + otherExecPerm: false + }, + { + //line: "-rw-r--r-- 1 mrclash pg223090 260 Mar 25 2008 .alias", + type: 0, + size: 560, + name: "test2", + time: +new Date("Aug 22 03:47 " + new Date().getFullYear()), + owner: "userName", + group: "alternc", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: false, + + groupReadPerm: true, + groupWritePerm: false, + groupExecPerm: false, + + otherReadPerm: false, + otherWritePerm: false, + otherExecPerm: false + } + ]; + + Parser.parseEntries(str, function(err, entryArray) { + entryArray.forEach(function(entry, i) { + assert.equal(unixEntries[i].type, entry.type); + assert.equal(unixEntries[i].size, entry.size); + assert.equal(unixEntries[i].name, entry.name); + //assert.equal(unixEntries[i].time, entry.time); + assert.equal(unixEntries[i].owner, entry.owner); + assert.equal(unixEntries[i].group, entry.group); + + assert.equal(unixEntries[i].userReadPerm, entry.userPermissions.read); + assert.equal(unixEntries[i].userWritePerm, entry.userPermissions.write); + assert.equal(unixEntries[i].userExecPerm, entry.userPermissions.exec); + + assert.equal(unixEntries[i].groupReadPerm, entry.groupPermissions.read); + assert.equal(unixEntries[i].groupWritePerm, entry.groupPermissions.write); + assert.equal(unixEntries[i].groupExecPerm, entry.groupPermissions.exec); + + assert.equal(unixEntries[i].otherReadPerm, entry.otherPermissions.read); + assert.equal(unixEntries[i].otherWritePerm, entry.otherPermissions.write); + assert.equal(unixEntries[i].otherExecPerm, entry.otherPermissions.exec); + }); + }); + + Parser.parseEntries(str2, function(err, entryArray) { + entryArray.forEach(function(entry, i) { + assert.equal(unixEntries2[i].type, entry.type); + assert.equal(unixEntries2[i].size, entry.size); + assert.equal(unixEntries2[i].name, entry.name); + //assert.equal(unixEntries2[i].time, entry.time); + assert.equal(unixEntries2[i].owner, entry.owner); + assert.equal(unixEntries2[i].group, entry.group); + + assert.equal(unixEntries2[i].userReadPerm, entry.userPermissions.read); + assert.equal(unixEntries2[i].userWritePerm, entry.userPermissions.write); + assert.equal(unixEntries2[i].userExecPerm, entry.userPermissions.exec); + + assert.equal(unixEntries2[i].groupReadPerm, entry.groupPermissions.read); + assert.equal(unixEntries2[i].groupWritePerm, entry.groupPermissions.write); + assert.equal(unixEntries2[i].groupExecPerm, entry.groupPermissions.exec); + + assert.equal(unixEntries2[i].otherReadPerm, entry.otherPermissions.read); + assert.equal(unixEntries2[i].otherWritePerm, entry.otherPermissions.write); + assert.equal(unixEntries2[i].otherExecPerm, entry.otherPermissions.exec); + }); + }) + }); + + + it("test ftp windows/DOS LIST responses", function() { + var dosEntries = [ + { + line: '04-27-00 09:09PM licensed', + type: 1, + size: 0, + time: +(new Date("04-27-00 09:09 PM")), + name: 'licensed', + }, + { + line: '11-18-03 10:16AM pub', + type: 1, + size: 0, + time: +(new Date("11-18-03 10:16 AM")), + name: 'pub', + }, + { + line: '04-14-99 03:47PM 589 readme.htm', + type: 0, + size: 589, + time: +(new Date("04-14-99 03:47 PM")), + name: 'readme.htm' + } + ]; + + dosEntries.forEach(function(entry) { + var result = Parser.parseEntry(entry.line); + + assert.equal(result.type, entry.type); + assert.equal(result.size, entry.size); + assert.equal(result.name, entry.name); + assert.equal(result.time, entry.time); + }); + }); + + it("test truncated listing", function() { + var truncated = "\ +drwxr-xr-x 33 0 0 4096 Nov 28 01:19 .\r\n\ +drwxr-xr-x 33 0 0 4096 Nov 28 01:19 ..\r\n\ +-rwxr-xr-x 1 mrclash pg223090 68491314 Jan 22 2009 Documents.zip\r\n\ +drwxr-xr-x 3 0 0 4096 Apr 16 2011 bourd\n\ +arie\r\n\ +drwxr-xr-x 2 0 0 4096 Apr 16 2011 denton\r\n\ +drwx------ 2 0 0 4096 Apr 16 2011 lost+found\r\n" + + var unixEntries = [ + { + type: 0, + size: 68491314, + name: "Documents.zip", + time: +new Date("Jan 22 2009"), + owner: "mrclash", + group: "pg223090", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: true, + + groupReadPerm: true, + groupWritePerm: false, + groupExecPerm: true, + + otherReadPerm: true, + otherWritePerm: false, + otherExecPerm: true + }, + { + type: 1, + size: 4096, + name: "bourdarie", + time: +new Date("Apr 16 2011"), + owner: "0", + group: "0", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: true, + + groupReadPerm: true, + groupWritePerm: false, + groupExecPerm: true, + + otherReadPerm: true, + otherWritePerm: false, + otherExecPerm: true + }, + { + type: 1, + size: 4096, + name: "denton", + time: +new Date("Apr 16 2011"), + owner: "0", + group: "0", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: true, + + groupReadPerm: true, + groupWritePerm: false, + groupExecPerm: true, + + otherReadPerm: true, + otherWritePerm: false, + otherExecPerm: true + }, + { + type: 1, + size: 4096, + name: "lost+found", + time: +new Date("Apr 16 2011"), + owner: "0", + group: "0", + + userReadPerm: true, + userWritePerm: true, + userExecPerm: true, + + groupReadPerm: false, + groupWritePerm: false, + groupExecPerm: false, + + otherReadPerm: false, + otherWritePerm: false, + otherExecPerm: false + } + ]; + + Parser.parseEntries(truncated, function(err, entryArray) { + entryArray.forEach(function(entry, i) { + assert.equal(unixEntries[i].type, entry.type); + assert.equal(unixEntries[i].size, entry.size); + assert.equal(unixEntries[i].name, entry.name); + //assert.equal(unixEntries[i].time, entry.time); + assert.equal(unixEntries[i].owner, entry.owner); + assert.equal(unixEntries[i].group, entry.group); + + assert.equal(unixEntries[i].userReadPerm, entry.userPermissions.read); + assert.equal(unixEntries[i].userWritePerm, entry.userPermissions.write); + assert.equal(unixEntries[i].userExecPerm, entry.userPermissions.exec); + + assert.equal(unixEntries[i].groupReadPerm, entry.groupPermissions.read); + assert.equal(unixEntries[i].groupWritePerm, entry.groupPermissions.write); + assert.equal(unixEntries[i].groupExecPerm, entry.groupPermissions.exec); + + assert.equal(unixEntries[i].otherReadPerm, entry.otherPermissions.read); + assert.equal(unixEntries[i].otherWritePerm, entry.otherPermissions.write); + assert.equal(unixEntries[i].otherExecPerm, entry.otherPermissions.exec); + }); + }); + }); + + + /* + * We are not supporting MLSx commands yet + * + * http://rfc-ref.org/RFC-TEXTS/3659/chapter7.html + * http://www.rhinosoft.com/newsletter/NewsL2005-07-06.asp?prod=rs + * + "test parse MLSD command lines" : function(next) { + var lines = [ + { + line: "Type=file;Size=17709913;Modify=20050502182143; Choices.mp3", + Type: "file", + Size: "17709913", + Modify: "20050502182143", + name: "Choices.mp3" + }, + { + line: "Type=cdir;Perm=el;Unique=keVO1+ZF4; test", + type: "file", + perm: "el", + + }, + { + line: "Type=pdir;Perm=e;Unique=keVO1+d?3; .." + } + ]; + + + + + //"Type=cdir;Perm=el;Unique=keVO1+ZF4; test", + //"Type=pdir;Perm=e;Unique=keVO1+d?3; ..", + //"Type=OS.unix=slink:/foobar;Perm=;Unique=keVO1+4G4; foobar", + //"Type=OS.unix=chr-13/29;Perm=;Unique=keVO1+5G4; device", + //"Type=OS.unix=blk-11/108;Perm=;Unique=keVO1+6G4; block", + //"Type=file;Perm=awr;Unique=keVO1+8G4; writable", + //"Type=dir;Perm=cpmel;Unique=keVO1+7G4; promiscuous", + //"Type=dir;Perm=;Unique=keVO1+1t2; no-exec", + //"Type=file;Perm=r;Unique=keVO1+EG4; two words", + //"Type=file;Perm=r;Unique=keVO1+IH4; leading space", + //"Type=file;Perm=r;Unique=keVO1+1G4; file1", + //"Type=dir;Perm=cpmel;Unique=keVO1+7G4; incoming", + //"Type=file;Perm=r;Unique=keVO1+1G4; file2", + //"Type=file;Perm=r;Unique=keVO1+1G4; file3", + //"Type=file;Perm=r;Unique=keVO1+1G4; file4", + + var parsed = Parser.parseMList(line); + + assert.equal("file", parsed.Type); + assert.equal("17709913", parsed.Size); + assert.equal("20050502182143", parsed.Modify); + assert.equal("Choices.mp3", parsed.name); + next(); + } + */ +}); + + diff --git a/node_modules/jsftp/package.json b/node_modules/jsftp/package.json new file mode 100644 index 0000000..55fe5eb --- /dev/null +++ b/node_modules/jsftp/package.json @@ -0,0 +1,59 @@ +{ + "name": "jsftp", + "id": "jsftp", + "version": "1.1.1", + "description": "A sane FTP client implementation for NodeJS", + "keywords": [ + "ftp", + "protocol", + "files", + "server", + "client", + "async" + ], + "author": { + "name": "Sergi Mansilla", + "email": "sergi.mansilla@gmail.com", + "url": "http://sergimansilla.com" + }, + "homepage": "https://github.com/sergi/jsftp", + "repository": { + "type": "git", + "url": "https://github.com/sergi/jsftp.git" + }, + "bugs": { + "url": "https://github.com/sergi/jsftp/issues" + }, + "dependencies": { + "event-stream": "~3.0.14", + "parse-listing": "~1.0.0", + "async": "~0.2.9" + }, + "devDependencies": { + "mocha": "~1.10.0", + "istanbul": "~0.1.36", + "mocha-istanbul": "~0.2.0", + "sinon": "~1.7.2", + "ftp-test-server": "~0.0.1", + "rimraf": "~2.2.0" + }, + "main": "./jsftp.js", + "engines": { + "node": ">=0.6.21" + }, + "scripts": { + "test": "mocha -R spec -t 5000" + }, + "licenses": [ + { + "type": "MIT", + "url": "https://github.com/sergi/jsftp/blob/master/LICENSE" + } + ], + "readme": "jsftp \"Flattr\n=====\n\nA client FTP library for NodeJS that focuses on correctness, clarity\nand conciseness. It doesn't get in the way and plays nice with streaming APIs.\n\n[![NPM](https://nodei.co/npm/jsftp.png)](https://nodei.co/npm/jsftp/)\n\n**Warning: The latest version (1.0.0) of jsftp breaks API compatibility with previous\nversions, it is NOT a drop-in replacement. Please be careful when upgrading. The\nAPI changes are not drastic at all and it is all documented below. If you do not\nwant to upgrade yet you should stay with version 0.6.0, the last one before the\nupgrade. The API docs below are updated for 1.0.**\n\nStarting it up\n--------------\n\n```javascript\nvar JSFtp = require(\"jsftp\");\n\nvar Ftp = new JSFtp({\n host: \"myserver.com\",\n port: 3331, // defaults to 21\n user: \"user\", // defaults to \"anonymous\"\n pass: \"1234\" // defaults to \"@anonymous\"\n};\n```\n\njsftp gives you access to all the raw commands of the FTP protocol in form of\nmethods in the `Ftp` object. It also provides several convenience methods for\nactions that require complex chains of commands (e.g. uploading and retrieving\nfiles, passive operations), as shown below.\n\nWhen raw commands succeed they always pass the response of the server to the\ncallback, in the form of an object that contains two properties: `code`, which\nis the response code of the FTP operation, and `text`, which is the complete\ntext of the response.\n\nRaw (or native) commands are accessible in the form `Ftp.raw[\"command\"](params, callback)`\n\nThus, a command like `QUIT` will be called like this:\n\n```javascript\nFtp.raw.quit(function(err, data) {\n if (err) return console.error(err);\n\n console.log(\"Bye!\");\n});\n```\n\nand a command like `MKD` (make directory), which accepts parameters, looks like this:\n\n```javascript\nFtp.raw.mkd(\"/new_dir\", function(err, data) {\n if (err) return console.error(err);\n\n console.log(data.text); // Show the FTP response text to the user\n console.log(data.code); // Show the FTP response code to the user\n});\n```\n\nAPI and examples\n----------------\n\n#### new Ftp(options)\n - `options` is an object with the following properties:\n\n ```javascript\n {\n host: 'localhost', // Host name for the current FTP server.\n port: 3333, // Port number for the current FTP server (defaults to 21).\n user: 'user', // Username\n pass: 'pass', // Password\n }\n ```\n\nCreates a new Ftp instance.\n\n\n#### Ftp.host\n\nHost name for the current FTP server.\n\n#### Ftp.port\n\nPort number for the current FTP server (defaults to 21).\n\n#### Ftp.socket\n\nNodeJS socket for the current FTP server.\n\n#### Ftp.features\n\nArray of feature names for the current FTP server. It is\ngenerated when the user authenticates with the `auth` method.\n\n#### Ftp.system\n\nContains the system identification string for the remote FTP server.\n\n\n### Methods\n\n#### Ftp.raw.FTP_COMMAND([params], callback)\nAll the standard FTP commands are available under the `raw` namespace. These\ncommands might accept parameters or not, but they always accept a callback\nwith the signature `err, data`, in which `err` is the error response coming\nfrom the server (usually a 4xx or 5xx error code) and the data is an object\nthat contains two properties: `code` and `text`. `code` is an integer indicating\nthe response code of the response and `text` is the response string itself.\n\n#### Ftp.auth(username, password, callback)\nAuthenticates the user with the given username and password. If null or empty\nvalues are passed for those, `auth` will use anonymous credentials. `callback`\nwill be called with the response text in case of successful login or with an\nerror as a first parameter, in normal Node fashion.\n\n#### Ftp.ls(filePath, callback)\nLists information about files or directories and yields an array of file objects\nwith parsed file properties to the `callback`. You should use this function\ninstead of `stat` or `list` in case you need to do something with the individual\nfile properties.\n\n```javascript\nftp.ls(\".\", function(err, res) {\n res.forEach(function(file) {\n console.log(file.name);\n });\n});\n```\n\n#### Ftp.list(filePath, callback)\nLists `filePath` contents using a passive connection. Calls callback with an\narray of strings with complete file information.\n\n```javascript\nftp.list(remoteCWD, function(err, res) {\n res.forEach(function(file) {\n console.log(file.name);\n });\n // Prints something like\n // -rw-r--r-- 1 sergi staff 4 Jun 03 09:32 testfile1.txt\n // -rw-r--r-- 1 sergi staff 4 Jun 03 09:31 testfile2.txt\n // -rw-r--r-- 1 sergi staff 0 May 29 13:05 testfile3.txt\n // ...\n});\n```\n\n#### Ftp.get(remotePath, callback)\nGives back a paused socket with the file contents ready to be streamed,\nor calls the callback with an error if not successful.\n\n```javascript\n var str = \"\"; // Will store the contents of the file\n ftp.get('remote/path/file.txt', function(err, socket) {\n if (err) return;\n\n socket.on(\"data\", function(d) { str += d.toString(); })\n socket.on(\"close\", function(hadErr) {\n if (hadErr)\n console.error('There was an error retrieving the file.');\n });\n socket.resume();\n });\n```\n\n#### Ftp.get(remotePath, localPath, callback)\nStores the remote file directly in the given local path.\n\n```javascript\n ftp.get('remote/file.txt, 'local/file.txt, function(hadErr) {\n if (hadErr)\n console.error('There was an error retrieving the file.');\n else\n console.log('File copied successfully!');\n });\n```\n\n#### Ftp.put(source, remotePath, callback)\nUploads a file to `filePath`. It accepts a string with the local path for the\nfile or a `Buffer` as a `source` parameter.\n\n```javascript\nftp.put(buffer, 'path/to/remote/file.txt', function(hadError) {\n if (!hadError)\n console.log(\"File transferred successfully!\");\n});\n```\n\n#### Ftp.rename(from, to, callback)\nRenames a file in the server. `from` and `to` are both filepaths.\n\n```javascript\nftp.rename(from, to, function(err, res) {\n if (!err)\n console.log(\"Renaming successful!\");\n});\n```\n\n#### Ftp.keepAlive()\nRefreshes the interval thats keep the server connection active.\n\nYou can find more usage examples in the [unit tests](https://github.com/sergi/jsftp/blob/master/test/jsftp_test.js). This documentation\nwill grow as jsftp evolves.\n\nInstallation\n------------\n\n npm install jsftp\n\nTest coverage\n-------------\n\nIn order to run coverage reports:\n\n npm install --dev\n make coverage\n\n Current overall coverage rate:\n lines......: 92.1% (316 of 343 lines)\n functions..: 91.0% (71 of 78 functions)\n\n\nTests\n-----\n\nTo run tests:\n\n npm install --dev\n make test\n\nLicense\n-------\n\nSee LICENSE.\n", + "_id": "jsftp@1.1.1", + "dist": { + "shasum": "7947db8de04c8ea1a035451c1a8b6bcaa5b0d2d5" + }, + "_from": "jsftp" +} diff --git a/node_modules/jsftp/test.js b/node_modules/jsftp/test.js new file mode 100644 index 0000000..f0c023f --- /dev/null +++ b/node_modules/jsftp/test.js @@ -0,0 +1,78 @@ +// Retrieve a file in the remote server. When the file has been retrieved, +// the callback will be called with `data` being the Buffer with the +// contents of the file. + +// Store the file in `remotePath` locally in `"/Users/sergi/file.txt"`. +var remotePath = "/folder/file.txt"; +var localPath = "/Users/sergi/file.txt"; +ftp.get(remotePath, localPath, function(err, data) { + if (err) + return console.error(err); + + console.log(remotePath + " stored successfully at " + localPath); +}); + + +// Store the file in `remotePath` locally in `"/Users/sergi/file.txt"`. +ftp.get(remotePath, function(err, socket) { + if (err) + console.error("Something went wrong."); + + var writeStream = fs.createWriteStream("/Users/sergi/file.txt"); + socket.pipe(writeStream); + socket.resume(); +}); + +// Create a directory +ftp.raw.mkd("/example_dir", function(err, data) { + if (err) return console.error(err); + + console.log(data.text); +}); + +// Delete a directory +ftp.raw.rmd("/example_dir", function(err, data) { + if (err) return console.error(err); + + console.log(data.text); +}); + +// Listing a directory +ftp.ls("/example_dir", function(err, files) { + if (err) return console.error(err); + console.log(files); // Contains an array of file objects +}); + +// Retrieving a file using streams +ftp.getGetSocket("/test_dir/testfile.txt", function(err, socket) { + if (err) return console.error(err); + + var pieces = []; + // `readable` is a stream, so we can attach events to it now + socket.on("data", function(p) { pieces.push(p); }); + socket.on("close", function(err) { + if (err) return console.error(new Error("readable connection error")); + + // `Ftp._concat` is an internal method used to concatenate buffers, it + // is used here only for illustration purposes. + console.log(Ftp._concat(pieces)); // print the contents of the file + }); + + // The readable stream is already paused, we have to resume it so it can + // start streaming. + readable.resume(); +}); + +// Storing a file in the FTP server, using streams +var originalData = Fs.createReadStream("sourceFile.txt") +) +; +originalData.pause(); + +ftp.getPutSocket("/remote_folder/sourceFileCopy.txt"), function(err, socket) { + if (err) return console.error(err); + originalData.pipe(socket); // Transfer from source to the remote file + originalData.resume(); +} +) +; diff --git a/node_modules/jsftp/test/jsftp_test.js b/node_modules/jsftp/test/jsftp_test.js new file mode 100755 index 0000000..cfb4555 --- /dev/null +++ b/node_modules/jsftp/test/jsftp_test.js @@ -0,0 +1,698 @@ +/* + * @package jsftp + * @copyright Copyright(c) 2011 Ajax.org B.V. + * @author Sergi Mansilla + * @license https://github.com/sergi/jsFTP/blob/master/LICENSE MIT License + */ +/*global it describe beforeEach afterEach */ + +"use strict"; + +var assert = require("assert"); +var Fs = require("fs"); +var exec = require('child_process').spawn; +var Ftp = require("../"); +var Path = require("path"); +var Utils = require("../lib/utils"); +var sinon = require("sinon"); +var EventEmitter = require("events").EventEmitter; +var ftpServer = require("ftp-test-server"); +var rimraf = require("rimraf"); + +var concat = function(bufs) { + var buffer, length = 0, + index = 0; + + if (!Array.isArray(bufs)) + bufs = Array.prototype.slice.call(arguments); + + for (var i = 0, l = bufs.length; i < l; i++) { + buffer = bufs[i]; + length += buffer.length; + } + + buffer = new Buffer(length); + + bufs.forEach(function(buf) { + buf.copy(buffer, index, 0, buf.length); + index += buf.length; + }); + + return buffer; +}; + +var concatStream = function(err, socket, callback) { + if (err) return callback(err); + + var pieces = []; + socket.on("data", function(p) { + pieces.push(p); + }); + socket.on("close", function(hadError) { + if (hadError) + return callback(new Error("Socket connection error")); + + callback(null, concat(pieces)); + }); + socket.resume(); +}; + +// Write down your system credentials. This test suite will use OSX internal +// FTP server. If you want to test against a remote server, simply change the +// `host` and `port` properties as well. +var FTPCredentials = { + host: "localhost", + user: "user", + port: 3334, + pass: "12345" +}; + +function getRemotePath(path) { + return Path.join('test', 'test_c9', path); +} + +function getLocalPath(path) { + return Path.join(process.cwd(), 'test', 'test_c9', path); +} +var CWD = process.cwd() + "/test"; +var remoteCWD = "test/test_c9"; +var daemon; +exec('mkdir', [__dirname + "/" + remoteCWD]); + +describe("jsftp test suite", function() { + var ftp, server; + beforeEach(function(next) { + rimraf(getLocalPath(''), function() { + Fs.mkdirSync(getLocalPath('')); + Fs.writeFileSync(getLocalPath('testfile.txt'), "test"); + + if (FTPCredentials.host === "localhost") { + server = new ftpServer(); + server.init(FTPCredentials); + } + + setTimeout(function() { + ftp = new Ftp(FTPCredentials); + next(); + }, 100); + }); + }); + + afterEach(function(next) { + if (daemon) + daemon.kill(); + + setTimeout(function() { + server.stop(); + if (ftp) { + ftp.destroy(); + ftp = null; + } + }, 50); + next(); + }); + + it("test initialize bad host", function(next) { + var ftp2 = new Ftp({ + host: "badhost", + user: "user", + port: 21, + pass: "12345" + }); + + ftp2.on("error", function(err) { + assert.equal(err.code, 'ENOTFOUND'); + next(); + }); + }); + + it("test initialize", function(next) { + assert.equal(ftp.host, FTPCredentials.host); + assert.equal(ftp.port, FTPCredentials.port); + assert.equal(ftp.user, FTPCredentials.user); + + assert.ok(ftp instanceof EventEmitter); + assert.equal(ftp.pending.length, 0); + assert.equal(ftp.cmdBuffer_.length, 0); + + next(); + }) + + it("test parseResponse with mark", function(next) { + var cb = sinon.spy(); + cb.expectsMark = { + marks: [150] + }; + var data = { + code: 150, + text: "150 File status okay; about to open data connection." + }; + + ftp.cmdBuffer_ = [ + ["retr fakefile.txt", cb] + ]; + ftp.parse = sinon.spy(); + + var firstCmd = ftp.cmdBuffer_[0]; + ftp.parseResponse(data); + assert(ftp.parse.calledWith(data, firstCmd)); + next(); + }); + + it("test parseResponse with no mark", function(next) { + var cb = sinon.spy(); + var data = { + code: 150, + text: "150 File status okay; about to open data connection." + }; + + ftp.cmdBuffer_ = [ + ["retr fakefile.txt", cb] + ]; + ftp.parse = sinon.spy(); + + ftp.parseResponse(data); + assert.equal(ftp.parse.callCount, 0); + next(); + }); + + it("test send function", function(next) { + ftp.pipeline = { + write: sinon.spy() + }; + ftp.send(); + ftp.send("list /"); + assert.equal(ftp.pipeline.write.callCount, 1); + assert(ftp.pipeline.write.calledWithExactly("list /\r\n")); + next(); + }); + + it("test parseResponse with ignore code", function(next) { + var cb = sinon.spy(); + cb.expectsMark = { + marks: [150], + ignore: 226 + }; + var data1 = { + code: 150, + text: "150 File status okay; about to open data connection." + }; + var data2 = { + code: 226, + text: "226 Transfer complete." + }; + + ftp.cmdBuffer_ = [ + ["retr fakefile.txt", cb], + ["list /", function() {}] + ]; + ftp.parse = sinon.spy(); + ftp.ignoreCmdCode = 150; + + ftp.parseResponse(data1); + assert.equal(ftp.ignoreCmdCode, 226); + ftp.parseResponse(data2); + assert.equal(ftp.ignoreCmdCode, null); + assert(ftp.parse.calledOnce); + next(); + }); + + it("test getFeatures", function(next) { + ftp.getFeatures(function(err, feats) { + assert.ok(Array.isArray(feats)); + assert.ok(Array.isArray(ftp.features)); + assert.ok(ftp.system.length > 0); + + var feat = ftp.features[0]; + assert.ok(ftp.hasFeat(feat)); + assert.equal(false, ftp.hasFeat("madeup-feat")); + next(); + }); + }); + + it("test getFeatures", function(next) { + ftp.getFeatures(function(err, feats) { + assert.ok(Array.isArray(feats)); + assert.ok(Array.isArray(ftp.features)); + assert.ok(ftp.system.length > 0); + + var feat = ftp.features[0]; + assert.ok(ftp.hasFeat(feat)); + assert.equal(false, ftp.hasFeat("madeup-feat")); + next(); + }); + }); + + it("test print working directory", function(next) { + ftp.raw.pwd(function(err, res) { + assert(!err, err); + + var code = parseInt(res.code, 10); + assert.ok(code === 257, "PWD command was not successful: " + res.text); + + next(); + }); + }); + + it("test switch CWD", function(next) { + ftp.raw.cwd(remoteCWD, function(err, res) { + assert.ok(!err, err); + + var code = parseInt(res.code, 10); + assert.ok(code === 200 || code === 250, "CWD command was not successful"); + + ftp.raw.pwd(function(err, res) { + assert.ok(!err, err); + + var code = parseInt(res.code, 10); + assert.ok(code === 257, "PWD command was not successful"); + assert.ok(res.text.indexOf(remoteCWD), "Unexpected CWD"); + next(); + }); + }); + }); + + it("test switch to unexistent CWD", function(next) { + ftp.raw.cwd("/unexistentDir/", function(err, res) { + var code = parseInt(res.code, 10); + assert.ok( !! err); + assert.equal(code, 550, "A (wrong) CWD command was successful. It should have failed"); + next(); + }); + }); + + it("test listing with bad line breaks", function(next) { + var badStr = "\ +213-Status follows:\r\n\ +-rw-r--r-- 1 0 0 105981956 Dec 20 18:07 GAT\r\n\ +SBY.MPG\r\n\ +-rw-r--r-- 1 0 0 74450948 Jan 17 18:16 GIJO.MPG\r\n\ +drwxr-xr-x 3 0 0 4096 Apr 16 2011 bourd\n\ +arie\r\n\ +drwxr-xr-x 2 0 0 4096 Apr 16 2011 denton\r\n\ +213 End of status"; + + var entries = Utils.parseEntry(badStr); + assert.equal("GATSBY.MPG", entries[0].name); + assert.equal("GIJO.MPG", entries[1].name); + assert.equal("bourdarie", entries[2].name); + assert.equal("denton", entries[3].name); + + next(); + }); + + it("test passive listing of current directory", function(next) { + ftp.list(remoteCWD, function(err, res) { + assert.ok(!err, err); + assert.ok(res.length > 0); + next(); + }); + }); + + it("test ftp node stat", function(next) { + ftp.raw.pwd(function(err, res) { + var parent = /.*"(.*)".*/.exec(res.text)[1]; + var path = Path.resolve(parent + "/" + remoteCWD); + ftp.raw.stat(path, function(err, res) { + assert.ok(!err, res); + assert.ok(res); + + assert.ok(res.code === 211 || res.code === 212 || res.code === 213); + next(); + }); + }); + }); + + it("test create and delete a directory", function(next) { + var newDir = remoteCWD + "/ftp_test_dir"; + ftp.raw.mkd(newDir, function(err, res) { + assert.ok(!err); + assert.equal(res.code, 257); + + ftp.raw.rmd(newDir, function(err, res) { + assert.ok(!err); + next(); + }); + }); + }); + + it("test create and delete a directory containing a space", function(next) { + var newDir = remoteCWD + "/ftp test dür"; + ftp.raw.mkd(newDir, function(err, res) { + assert.ok(!err); + assert.equal(res.code, 257); + + ftp.raw.rmd(newDir, function(err, res) { + assert.ok(!err); + next(); + }); + }); + }); + + it("test create and delete a file", function(next) { + var filePath = getRemotePath("file_ftp_test.txt"); + Fs.readFile(__filename, "binary", function(err, data) { + var buffer = new Buffer(data, "binary"); + ftp.put(buffer, filePath, function(hadError) { + assert.ok(!hadError); + + ftp.ls(filePath, function(err, res) { + assert.ok(!err); + assert.equal(buffer.length, Fs.statSync(CWD + "/jsftp_test.js").size); + + ftp.raw.dele(filePath, function(err, data) { + assert.ok(!err); + + next(); + }); + }); + }); + }); + }); + + it("test save a remote copy of a local file", function(next) { + var filePath = getRemotePath("file_ftp_test.txt"); + var onProgress = sinon.spy(); + ftp.on('progress', onProgress); + ftp.put(__filename, filePath, function(err, res) { + assert.ok(!err, err); + + ftp.ls(filePath, function(err, res) { + assert.ok(!err); + assert(onProgress.called); + var data = onProgress.args[0][0]; + assert.equal(data.filename, filePath); + assert.equal(data.action, 'put'); + assert.ok(typeof data.transferred, 'number'); + + ftp.raw.dele(filePath, function(err, data) { + assert.ok(!err); + next(); + }); + }); + }); + }); + + it("test rename a file", function(next) { + var from = getRemotePath("file_ftp_test.txt"); + var to = getRemotePath("file_ftp_test_renamed.txt"); + Fs.readFile(__filename, "binary", function(err, data) { + assert.ok(!err, err); + var buffer = new Buffer(data, "binary"); + ftp.put(buffer, from, function(err, res) { + assert.ok(!err, err); + + ftp.rename(from, to, function(err, res) { + ftp.ls(to, function(err, res) { + assert.ok(!err); + + assert.equal(buffer.length, Fs.statSync(__filename).size); + + ftp.raw.dele(to, function(err, data) { + assert.ok(!err); + next(); + }); + }); + }); + }); + }); + }); + + it("test get a file", function(next) { + var localPath = CWD + '/test_c9/testfile.txt'; + var remotePath = remoteCWD + "/testfile.txt"; + var realContents = Fs.readFileSync(localPath, "utf8"); + var str = ""; + ftp.get(remotePath, function(err, socket) { + assert.ok(!err, err); + assert.ok(arguments.length === 2); + socket.on("data", function(d) { + str += d; + }) + socket.on("close", function(hadErr) { + assert.equal(realContents, str); + next(); + }); + socket.resume(); + }); + }); + + it("test get a file and save it locally", function(next) { + var localPath = getLocalPath("testfile.txt"); + var remotePath = getRemotePath("testfile.txt"); + var destination = localPath + ".copy"; + var onProgress = sinon.spy(); + ftp.on('progress', onProgress); + + Fs.unlink(destination, function() { + Fs.readFile(localPath, "utf8", function(err, realContents) { + ftp.get(remotePath, destination, function(err) { + assert.ok(!err, err); + assert.ok(arguments.length < 2, arguments.length); + var data = onProgress.args[0][0]; + assert.equal(data.filename, remotePath); + assert.equal(data.action, 'get'); + assert.ok(typeof data.transferred, 'number'); + Fs.readFile(destination, "utf8", function(err, data) { + assert.strictEqual(data, realContents); + next(); + }); + }); + }); + }); + }); + + it("test get a big file stream", function(next) { + var remotePath = getRemotePath("bigfile.test"); + var localPath = getLocalPath("bigfile.test"); + var data = (new Array(1 * 1024 * 1024)).join("x"); + var buffer = new Buffer(data, "binary"); + + Fs.writeFileSync(localPath, buffer); + + ftp.getGetSocket(remotePath, function(err, socket) { + assert.ok(!err, err); + + socket.resume(); + + var counter = 0; + + socket.on('data', function(data) { + counter += data.length; + }); + + socket.on('close', function() { + assert.equal(buffer.length, counter); + + ftp.raw.dele(remotePath, function(err, data) { + assert.ok(!err); + next(); + }); + }); + }); + }); + + it("test put a big file stream", function(next) { + var remotePath = getRemotePath("bigfile.test"); + var data = (new Array(1 * 1024 * 1024)).join("x"); + + ftp.getPutSocket(remotePath, function(err, socket) { + assert.ok(!err, err); + + socket.write(data, function(err) { + assert.ok(!err, err); + socket.end(); + }); + }, function(err, res) { + assert.ok(!err, err); + + ftp.raw.dele(remotePath, function(err, data) { + assert.ok(!err); + next(); + }); + }); + }); + + it("test put a big file stream fail", function(next) { + var remotePath = getRemotePath("/nonexisting/path/to/file.txt"); + + ftp.getPutSocket(remotePath, function(err, socket, res) { + assert.ok( !! err, err); + assert.equal(err.code, 550, err); + }, function(err, res) { + assert.ok( !! err); + next(); + }); + }); + + it("test get fileList array", function(next) { + var file1 = "testfile.txt"; + + ftp.raw.cwd(getRemotePath(''), function() { + ftp.ls(".", function(err, res) { + assert.ok(!err, err); + assert.ok(Array.isArray(res)); + + res.forEach(assert.ok); + res = res.map(function(file) { + return file.name; + }); + + assert.ok(res.indexOf(file1) > -1); + + next(); + }); + }); + }); + + it("test reconnect", function(next) { + ftp.raw.pwd(function(err, res) { + if (err) throw err; + + var code = parseInt(res.code, 10); + assert.ok(code === 257, "PWD command was not successful"); + + ftp.socket.end(); + ftp.raw.quit(function(err, res) { + if (err) throw err; + next(); + }); + }); + }); + + it("test attach event handlers: connect", function(_next) { + var clientOnConnect = function() { + client.auth(FTPCredentials.user, FTPCredentials.pass, next); + }; + + var next = function(err) { + assert.ok(!err); + client.destroy(); + _next(); + }; + + var client = new Ftp({ + host: "localhost", + user: "user", + port: 3334, + pass: "12345" + }); + client.on("connect", clientOnConnect); + }); + + it("test PASV streaming: Copy file using piping", function(next) { + var filePath = getRemotePath("testfile.txt"); + var originalData = Fs.readFileSync(getLocalPath("testfile.txt")); + ftp.getGetSocket(filePath, function(err, readable) { + assert(!err, err); + assert.ok(readable); + + readable.on("error", error); + + function error(err) { + assert.ok(!err, err); + if (readable.destroy) readable.destroy(); + next(); + } + + var remoteCopy = filePath + ".bak"; + ftp.getPutSocket(remoteCopy, function(err, socket) { + assert.ok(!err, err); + readable.pipe(socket); + readable.resume(); + }, + + function(hadError) { + assert.ok(!hadError); + + var str = ""; + ftp.getGetSocket(remoteCopy, function(err, socket) { + assert.ok(!err, err); + socket.on("data", function(d) { + str += d; + }); + socket.on("close", function(hadErr) { + assert.equal(originalData.toString("utf8"), str); + next(); + }); + socket.resume(); + }); + }); + }); + }); + + it("Test that streaming GET (RETR) retrieves a file properly", function(next) { + var path = getLocalPath("testfile.txt"); + var originalData = Fs.readFileSync(path); + ftp.getGetSocket(getRemotePath("testfile.txt"), function(err, readable) { + assert.ok(!err); + concatStream(err, readable, function(err, buffer) { + assert.ok(!err); + assert.equal(buffer.toString(), originalData.toString()); + next(); + }); + }); + }); + + it("Test that streaming GET (RETR) fails when a file is not present", function(next) { + ftp.getGetSocket("unexisting/file/path", function(err, readable) { + assert.ok(err); + assert.equal(550, err.code); + next(); + }); + }); + + it("Test that streaming PUT (STOR) stores a file properly", function(next) { + var path = getLocalPath("testfile.txt"); + var originalData = Fs.createReadStream(getLocalPath("testfile.txt")); + originalData.pause(); + + ftp.getPutSocket(getRemotePath("testfile.txt.bak"), function(err, socket) { + assert.ok(!err); + originalData.pipe(socket); + originalData.resume(); + concatStream(err, originalData, function(err, buffer) { + assert.ok(!err); + Fs.readFile(path, "utf8", function(err, original) { + assert.equal(buffer.toString("utf8"), original); + next(); + }); + }); + }); + }); + + it("Test that streaming PUT (STOR) fails when a file is not present", function(next) { + ftp.getPutSocket("unexisting/file/path", function(err, socket) { + assert.ok(err); + next(); + }); + }); + + it("Test that onConnect is called", function(next) { + var ftp2 = new Ftp(FTPCredentials); + ftp2.on("connect", function() { + next(); + }); + }); + + it("Test for correct data on ls 1", function(next) { + var paths = ['test/test_c9/testfile.txt', 'work/test/main.css']; + ftp.auth(FTPCredentials.user, FTPCredentials.pass, function(err, data) { + if (err) return console.log(err); + + var processed = 0; + ftp.ls(paths[0], function showFile(err, res) { + assert.ok(!err); + assert.strictEqual(res[0].name, 'testfile.txt'); + processed += 1; + if (processed === 2) next(); + }); + + ftp.ls(paths[1], function showFile(err, res1) { + assert.ok(!!err); + processed += 1; + if (processed === 2) next(); + }); + }); + }); +}); diff --git a/node_modules/jsftp/test/keycert.pem b/node_modules/jsftp/test/keycert.pem new file mode 100644 index 0000000..bfa28de --- /dev/null +++ b/node_modules/jsftp/test/keycert.pem @@ -0,0 +1,32 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXwIBAAKBgQC8ddrhm+LutBvjYcQlnH21PPIseJ1JVG2HMmN2CmZk2YukO+9L +opdJhTvbGfEj0DQs1IE8M+kTUyOmuKfVrFMKwtVeCJphrAnhoz7TYOuLBSqt7lVH +fhi/VwovESJlaBOp+WMnfhcduPEYHYx/6cnVapIkZnLt30zu2um+DzA9jQIDAQAB +AoGBAK0FZpaKj6WnJZN0RqhhK+ggtBWwBnc0U/ozgKz2j1s3fsShYeiGtW6CK5nU +D1dZ5wzhbGThI7LiOXDvRucc9n7vUgi0alqPQ/PFodPxAN/eEYkmXQ7W2k7zwsDA +IUK0KUhktQbLu8qF/m8qM86ba9y9/9YkXuQbZ3COl5ahTZrhAkEA301P08RKv3KM +oXnGU2UHTuJ1MAD2hOrPxjD4/wxA/39EWG9bZczbJyggB4RHu0I3NOSFjAm3HQm0 +ANOu5QK9owJBANgOeLfNNcF4pp+UikRFqxk5hULqRAWzVxVrWe85FlPm0VVmHbb/ +loif7mqjU8o1jTd/LM7RD9f2usZyE2psaw8CQQCNLhkpX3KO5kKJmS9N7JMZSc4j +oog58yeYO8BBqKKzpug0LXuQultYv2K4veaIO04iL9VLe5z9S/Q1jaCHBBuXAkEA +z8gjGoi1AOp6PBBLZNsncCvcV/0aC+1se4HxTNo2+duKSDnbq+ljqOM+E7odU+Nq +ewvIWOG//e8fssd0mq3HywJBAJ8l/c8GVmrpFTx8r/nZ2Pyyjt3dH1widooDXYSV +q6Gbf41Llo5sYAtmxdndTLASuHKecacTgZVhy0FryZpLKrU= +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIICpzCCAhCgAwIBAgIJAP+qStv1cIGNMA0GCSqGSIb3DQEBBQUAMIGJMQswCQYD +VQQGEwJVUzERMA8GA1UECBMIRGVsYXdhcmUxEzARBgNVBAcTCldpbG1pbmd0b24x +IzAhBgNVBAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMQwwCgYDVQQLEwNT +U0wxHzAdBgNVBAMTFnNvbWVtYWNoaW5lLnB5dGhvbi5vcmcwHhcNMDcwODI3MTY1 +NDUwWhcNMTMwMjE2MTY1NDUwWjCBiTELMAkGA1UEBhMCVVMxETAPBgNVBAgTCERl +bGF3YXJlMRMwEQYDVQQHEwpXaWxtaW5ndG9uMSMwIQYDVQQKExpQeXRob24gU29m +dHdhcmUgRm91bmRhdGlvbjEMMAoGA1UECxMDU1NMMR8wHQYDVQQDExZzb21lbWFj +aGluZS5weXRob24ub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8ddrh +m+LutBvjYcQlnH21PPIseJ1JVG2HMmN2CmZk2YukO+9LopdJhTvbGfEj0DQs1IE8 +M+kTUyOmuKfVrFMKwtVeCJphrAnhoz7TYOuLBSqt7lVHfhi/VwovESJlaBOp+WMn +fhcduPEYHYx/6cnVapIkZnLt30zu2um+DzA9jQIDAQABoxUwEzARBglghkgBhvhC +AQEEBAMCBkAwDQYJKoZIhvcNAQEFBQADgYEAF4Q5BVqmCOLv1n8je/Jw9K669VXb +08hyGzQhkemEBYQd6fzQ9A/1ZzHkJKb1P6yreOLSEh4KcxYPyrLRC1ll8nr5OlCx +CMhKkTnR6qBsdNV0XtdU2+N25hqW+Ma4ZeqsN/iiJVCGNOZGnvQuvCAGWF8+J/f/ +iHkC6gGdBJhogs4= +-----END CERTIFICATE----- diff --git a/node_modules/jsftp/test/mockup_test.js b/node_modules/jsftp/test/mockup_test.js new file mode 100644 index 0000000..e69de29 diff --git a/node_modules/jsftp/test/test_c9/testfile.txt b/node_modules/jsftp/test/test_c9/testfile.txt new file mode 100644 index 0000000..30d74d2 --- /dev/null +++ b/node_modules/jsftp/test/test_c9/testfile.txt @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/node_modules/jsftp/test/tls_ftpd.py b/node_modules/jsftp/test/tls_ftpd.py new file mode 100644 index 0000000..d2c204e --- /dev/null +++ b/node_modules/jsftp/test/tls_ftpd.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python + +""" +An RFC-4217 asynchronous FTPS server supporting both SSL and TLS. +Requires PyOpenSSL module (http://pypi.python.org/pypi/pyOpenSSL). +""" + +from pyftpdlib.servers import FTPServer +from pyftpdlib.authorizers import DummyAuthorizer +from pyftpdlib.handlers import TLS_FTPHandler,FTPHandler + + +def main(): + authorizer = DummyAuthorizer() + authorizer.add_user('user', '12345', '.', perm='elradfmw') + authorizer.add_anonymous('.') + handler = TLS_FTPHandler + handler.certfile = 'keycert.pem' + handler.authorizer = authorizer + # requires SSL for both control and data channel + #handler.tls_control_required = True + #handler.tls_data_required = True + server = FTPServer(('', 21), handler) + server.serve_forever() + +if __name__ == '__main__': + main() diff --git a/node_modules/ws/.npmignore b/node_modules/ws/.npmignore new file mode 100644 index 0000000..8233793 --- /dev/null +++ b/node_modules/ws/.npmignore @@ -0,0 +1,6 @@ +npm-debug.log +node_modules +.*.swp +.lock-* +build + diff --git a/node_modules/ws/.travis.yml b/node_modules/ws/.travis.yml new file mode 100644 index 0000000..08e4dad --- /dev/null +++ b/node_modules/ws/.travis.yml @@ -0,0 +1,6 @@ +language: node_js +npm_args: --ws:native +node_js: + - "0.6" + - "0.8" + - "0.10" diff --git a/node_modules/ws/History.md b/node_modules/ws/History.md new file mode 100644 index 0000000..63cf0ea --- /dev/null +++ b/node_modules/ws/History.md @@ -0,0 +1,312 @@ +v0.4.31 - September 23th, 2013 +===================== + +* Component support + +v0.4.30 - August 30th, 2013 +===================== + +* BufferedAmount could be undefined, default to 0 [TooTallNate] +* Support protocols as second argument and options as third [TooTallNate] +* Proper browserify shim [mcollina] +* Broadcasting example in README [stefanocudini] + +v0.4.29 - August 23th, 2013 +===================== +* Small clean up of the Node 0.11 support by using NAN from the NPM registry [kkoopa] +* Support for custom `Agent`'s through the options. [gramakri] & [TooTallNate] +* Support for custom headers through the options [3rd-Eden] +* Added a `gypfile` flag to the package.json for compiled module discovery [wolfeidau] + +v0.4.28 - August 16th, 2013 +===================== +* Node 0.11 support. [kkoopa] +* Authorization headers are sent when basic auth is used in the url [jcrugzz] +* Origin header will now include the port number [Jason Plum] +* Race condition fixed where data was received before the readyState was updated. [saschagehlich] + +v0.4.27 - June 27th, 2013 +===================== +* Frames are no longer masked in `wscat`. [slaskis] +* Don't retrain reference to large slab buffers. [jmatthewsr-msi] +* Don't use Buffer.byteLength for ArrayBuffer's. [Anthony Pesch] +* Fix browser field in package.json. [shtylman] +* Client-side certificate support & documentation improvements. [Lukas Berns] +* WebSocket readyState's is added to the prototype for spec compatiblity. [BallBearing] +* Use Object.defineProperty. [arlolra] +* Autodetect ArrayBuffers as binary when sending. [BallBearing] +* Check instanceof Buffer for binary data. [arlolra] +* Emit the close event before destroying the internal socket. [3rd-Eden] +* Don't setup multiply timeouts for one connection. [AndreasMadsen] +* Allow support for binding to ethereal port. [wpreul] +* Fix broken terminate reference. [3rd-Eden] +* Misc node 0.10 test fixes and documentation improvements. [3rd-Eden] +* Ensure ssl options are propagated to request. [einaros] +* Add 'Host' and 'Origin' to request header. [Lars-Magnus Skog] +* Subprotocol support. [kanaka] +* Honor ArrayBufferView's byteOffset when sending. [Anthony Pesch] +* Added target attribute for events. [arlolra] + +v0.4.26 - Skipped +===================== + +v0.4.25 - December 17th, 2012 +===================== +* Removed install.js. [shtylman] +* Added browser field to package.json. [shtylman] +* Support overwriting host header. [Raynos] +* Emit 'listening' also with custom http server. [sebiq] + +v0.4.24 - December 6th, 2012 +===================== +* Yet another intermediate release, to not delay minor features any longer. +* Native support installation issues further circumvented. [einaros] + +v0.4.23 - November 19th, 2012 +===================== +* Service release - last before major upgrade. +* Changes default host from 127.0.0.1 to 0.0.0.0. [einaros] + +v0.4.22 - October 3rd, 2012 +===================== +* clear failsafe cleanup timeout once cleanup is called [AndreasMadsen] +* added w3c compatible CloseEvent for onclose / addEventListener("close", ...). [einaros] +* fix the sub protocol header handler [sonnyp] +* fix unhandled exception if socket closes and 'error' is emitted [jmatthewsr-ms] + +v0.4.21 - July 14th, 2012 +===================== +* Emit error if server reponds with anything other than status code 101. [einaros] +* Added 'headers' event to server. [rauchg] +* path.exists moved to fs.exists. [blakmatrix] + +v0.4.20 - June 26th, 2012 +===================== +* node v0.8.0 compatibility release. + +v0.4.19 - June 19th, 2012 +===================== +* Change sender to merge buffers for relatively small payloads, may improve perf in some cases [einaros] +* Avoid EventEmitter for Receiver classes. As above this may improve perf. [einaros] +* Renamed fallback files from the somewhat misleading '*Windows'. [einaros] + +v0.4.18 - June 14th 2012 +===================== +* Fixed incorrect md5 digest encoding in Hixie handshake [nicokaiser] +* Added example of use with Express 3 [einaros] +* Change installation procedure to not require --ws:native to build native extensions. They will now build if a compiler is available. [einaros] + +v0.4.17 - June 13th 2012 +===================== +* Improve error handling during connection handshaking [einaros] +* Ensure that errors are caught also after connection teardown [nicokaiser] +* Update 'mocha' version to 1.1.0. [einaros] +* Stop showing 'undefined' for some error logs. [tricknotes] +* Update 'should' version to 0.6.3 [tricknotes] + +v0.4.16 - June 1st 2012 +===================== +* Build fix for Windows. [einaros] + +v0.4.15 - May 20th 2012 +===================== +* Enable fauxe streaming for hixie tansport. [einaros] +* Allow hixie sender to deal with buffers. [einaros/pigne] +* Allow error code 1011. [einaros] +* Fix framing for empty packets (empty pings and pongs might break). [einaros] +* Improve error and close handling, to avoid connections lingering in CLOSING state. [einaros] + +v0.4.14 - Apr 30th 2012 +===================== +* use node-gyp instead of node-waf [TooTallNate] +* remove old windows compatibility makefile, and silently fall back to native modules [einaros] +* ensure connection status [nicokaiser] +* websocket client updated to use port 443 by default for wss:// connections [einaros] +* support unix sockets [kschzt] + +v0.4.13 - Apr 12th 2012 +===================== + +* circumvent node 0.6+ related memory leak caused by Object.defineProperty [nicokaiser] +* improved error handling, improving stability in massive load use cases [nicokaiser] + +v0.4.12 - Mar 30th 2012 +===================== + +* various memory leak / possible memory leak cleanups [einaros] +* api documentation [nicokaiser] +* add option to disable client tracking [nicokaiser] + +v0.4.11 - Mar 24th 2012 +===================== + +* node v0.7 compatibillity release +* gyp support [TooTallNate] +* commander dependency update [jwueller] +* loadbalancer support [nicokaiser] + +v0.4.10 - Mar 22th 2012 +===================== + +* Final hixie close frame fixes. [nicokaiser] + +v0.4.9 - Mar 21st 2012 +===================== + +* Various hixie bugfixes (such as proper close frame handling). [einaros] + +v0.4.8 - Feb 29th 2012 +===================== + +* Allow verifyClient to run asynchronously [karlsequin] +* Various bugfixes and cleanups. [einaros] + +v0.4.7 - Feb 21st 2012 +===================== + +* Exposed bytesReceived from websocket client object, which makes it possible to implement bandwidth sampling. [einaros] +* Updated browser based file upload example to include and output per websocket channel bandwidth sampling. [einaros] +* Changed build scripts to check which architecture is currently in use. Required after the node.js changes to have prebuilt packages target ia32 by default. [einaros] + +v0.4.6 - Feb 9th 2012 +===================== + +* Added browser based file upload example. [einaros] +* Added server-to-browser status push example. [einaros] +* Exposed pause() and resume() on WebSocket object, to enable client stream shaping. [einaros] + +v0.4.5 - Feb 7th 2012 +===================== + +* Corrected regression bug in handling of connections with the initial frame delivered across both http upgrade head and a standalone packet. This would lead to a race condition, which in some cases could cause message corruption. [einaros] + +v0.4.4 - Feb 6th 2012 +===================== + +* Pass original request object to verifyClient, for cookie or authentication verifications. [einaros] +* Implemented addEventListener and slightly improved the emulation API by adding a MessageEvent with a readonly data attribute. [aslakhellesoy] +* Rewrite parts of hybi receiver to avoid stack overflows for large amounts of packets bundled in the same buffer / packet. [einaros] + +v0.4.3 - Feb 4th 2012 +===================== + +* Prioritized update: Corrected issue which would cause sockets to stay open longer than necessary, and resource leakage because of this. [einaros] + +v0.4.2 - Feb 4th 2012 +===================== + +* Breaking change: WebSocketServer's verifyOrigin option has been renamed to verifyClient. [einaros] +* verifyClient now receives { origin: 'origin header', secure: true/false }, where 'secure' will be true for ssl connections. [einaros] +* Split benchmark, in preparation for more thorough case. [einaros] +* Introduced hixie-76 draft support for server, since Safari (iPhone / iPad / OS X) and Opera still aren't updated to use Hybi. [einaros] +* Expose 'supports' object from WebSocket, to indicate e.g. the underlying transport's support for binary data. [einaros] +* Test and code cleanups. [einaros] + +v0.4.1 - Jan 25th 2012 +===================== + +* Use readline in wscat [tricknotes] +* Refactor _state away, in favor of the new _readyState [tricknotes] +* travis-ci integration [einaros] +* Fixed race condition in testsuite, causing a few tests to fail (without actually indicating errors) on travis [einaros] +* Expose pong event [paddybyers] +* Enabled running of WebSocketServer in noServer-mode, meaning that upgrades are passed in manually. [einaros] +* Reworked connection procedure for WebSocketServer, and cleaned up tests. [einaros] + +v0.4.0 - Jan 2nd 2012 +===================== + +* Windows compatibility [einaros] +* Windows compatible test script [einaros] + +v0.3.9 - Jan 1st 2012 +====================== + +* Improved protocol framing performance [einaros] +* WSS support [kazuyukitanimura] +* WSS tests [einaros] +* readyState exposed [justinlatimer, tricknotes] +* url property exposed [justinlatimer] +* Removed old 'state' property [einaros] +* Test cleanups [einaros] + +v0.3.8 - Dec 27th 2011 +====================== + +* Made it possible to listen on specific paths, which is especially good to have for precreated http servers [einaros] +* Extensive WebSocket / WebSocketServer cleanup, including changing all internal properties to unconfigurable, unenumerable properties [einaros] +* Receiver modifications to ensure even better performance with fragmented sends [einaros] +* Fixed issue in sender.js, which would cause SlowBuffer instances (such as returned from the crypto library's randomBytes) to be copied (and thus be dead slow) [einaros] +* Removed redundant buffer copy in sender.js, which should improve server performance [einaros] + +v0.3.7 - Dec 25nd 2011 +====================== + +* Added a browser based API which uses EventEmitters internally [3rd-Eden] +* Expose request information from upgrade event for websocket server clients [mmalecki] + +v0.3.6 - Dec 19th 2011 +====================== + +* Added option to let WebSocket.Server use an already existing http server [mmalecki] +* Migrating various option structures to use options.js module [einaros] +* Added a few more tests, options and handshake verifications to ensure that faulty connections are dealt with [einaros] +* Code cleanups in Sender and Receiver, to ensure even faster parsing [einaros] + +v0.3.5 - Dec 13th 2011 +====================== + +* Optimized Sender.js, Receiver.js and bufferutil.cc: + * Apply loop-unrolling-like small block copies rather than use node.js Buffer#copy() (which is slow). + * Mask blocks of data using combination of 32bit xor and loop-unrolling, instead of single bytes. + * Keep pre-made send buffer for small transfers. +* Leak fixes and code cleanups. + +v0.3.3 - Dec 12th 2011 +====================== + +* Compile fix for Linux. +* Rewrote parts of WebSocket.js, to avoid try/catch and thus avoid optimizer bailouts. + +v0.3.2 - Dec 11th 2011 +====================== + +* Further performance updates, including the additions of a native BufferUtil module, which deals with several of the cpu intensive WebSocket operations. + +v0.3.1 - Dec 8th 2011 +====================== + +* Service release, fixing broken tests. + +v0.3.0 - Dec 8th 2011 +====================== + +* Node.js v0.4.x compatibility. +* Code cleanups and efficiency improvements. +* WebSocket server added, although this will still mainly be a client library. +* WebSocket server certified to pass the Autobahn test suite. +* Protocol improvements and corrections - such as handling (redundant) masks for empty fragments. +* 'wscat' command line utility added, which can act as either client or server. + +v0.2.6 - Dec 3rd 2011 +====================== + +* Renamed to 'ws'. Big woop, right -- but easy-websocket really just doesn't cut it anymore! + +v0.2.5 - Dec 3rd 2011 +====================== + + * Rewrote much of the WebSocket parser, to ensure high speed for highly fragmented messages. + * Added a BufferPool, as a start to more efficiently deal with allocations for WebSocket connections. More work to come, in that area. + * Updated the Autobahn report, at http://einaros.github.com/easy-websocket, with comparisons against WebSocket-Node 1.0.2 and Chrome 16. + +v0.2.0 - Nov 25th 2011 +====================== + + * Major rework to make sure all the Autobahn test cases pass. Also updated the internal tests to cover more corner cases. + +v0.1.2 - Nov 14th 2011 +====================== + + * Back and forth, back and forth: now settled on keeping the api (event names, methods) closer to the websocket browser api. This will stick now. + * Started keeping this history record. Better late than never, right? diff --git a/node_modules/ws/Makefile b/node_modules/ws/Makefile new file mode 100644 index 0000000..151aa2b --- /dev/null +++ b/node_modules/ws/Makefile @@ -0,0 +1,40 @@ +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 2000 \ + -s 2400 \ + $(TESTFLAGS) \ + $(TESTS) + +run-integrationtests: + @./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 + +benchmark: + @node bench/sender.benchmark.js + @node bench/parser.benchmark.js + +autobahn: + @NODE_PATH=lib node test/autobahn.js + +autobahn-server: + @NODE_PATH=lib node test/autobahn-server.js + +.PHONY: test diff --git a/node_modules/ws/README.md b/node_modules/ws/README.md new file mode 100644 index 0000000..e6646e7 --- /dev/null +++ b/node_modules/ws/README.md @@ -0,0 +1,171 @@ +[![Build Status](https://secure.travis-ci.org/einaros/ws.png)](http://travis-ci.org/einaros/ws) + +# ws: a node.js websocket library # + +`ws` is a simple to use websocket implementation, up-to-date against RFC-6455, and [probably the fastest WebSocket library for node.js](http://web.archive.org/web/20130314230536/http://hobbycoding.posterous.com/the-fastest-websocket-module-for-nodejs). + +Passes the quite extensive Autobahn test suite. See http://einaros.github.com/ws for the full reports. + +Comes with a command line utility, `wscat`, which can either act as a server (--listen), or client (--connect); Use it to debug simple websocket services. + +## Protocol support ## + +* **Hixie draft 76** (Old and deprecated, but still in use by Safari and Opera. Added to ws version 0.4.2, but server only. Can be disabled by setting the `disableHixie` option to true.) +* **HyBi drafts 07-12** (Use the option `protocolVersion: 8`, or argument `-p 8` for wscat) +* **HyBi drafts 13-17** (Current default, alternatively option `protocolVersion: 13`, or argument `-p 13` for wscat) + +_See the echo.websocket.org example below for how to use the `protocolVersion` option._ + +## Usage ## + +### Installing ### + +`npm install ws` + +### Sending and receiving text data ### + +```js +var WebSocket = require('ws'); +var ws = new WebSocket('ws://www.host.com/path'); +ws.on('open', function() { + ws.send('something'); +}); +ws.on('message', function(data, flags) { + // flags.binary will be set if a binary data is received + // flags.masked will be set if the data was masked +}); +``` + +### Sending binary data ### + +```js +var WebSocket = require('ws'); +var ws = new WebSocket('ws://www.host.com/path'); +ws.on('open', function() { + var array = new Float32Array(5); + for (var i = 0; i < array.length; ++i) array[i] = i / 2; + ws.send(array, {binary: true, mask: true}); +}); +``` + +Setting `mask`, as done for the send options above, will cause the data to be masked according to the websocket protocol. The same option applies for text data. + +### Server example ### + +```js +var WebSocketServer = require('ws').Server + , wss = new WebSocketServer({port: 8080}); +wss.on('connection', function(ws) { + ws.on('message', function(message) { + console.log('received: %s', message); + }); + ws.send('something'); +}); +``` + +### Server sending broadcast data ### + +```js +var WebSocketServer = require('ws').Server + , wss = new WebSocketServer({port: 8080}); + +wss.broadcast = function(data) { + for(var i in this.clients) + this.clients[i].send(data); +}; +``` + +### Error handling best practices ### + +```js +// If the WebSocket is closed before the following send is attempted +ws.send('something'); + +// Errors (both immediate and async write errors) can be detected in an optional callback. +// The callback is also the only way of being notified that data has actually been sent. +ws.send('something', function(error) { + // if error is null, the send has been completed, + // otherwise the error object will indicate what failed. +}); + +// Immediate errors can also be handled with try/catch-blocks, but **note** +// that since sends are inherently asynchronous, socket write failures will *not* +// be captured when this technique is used. +try { + ws.send('something'); +} +catch (e) { + // handle error +} +``` + +### echo.websocket.org demo ### + +```js +var WebSocket = require('ws'); +var ws = new WebSocket('ws://echo.websocket.org/', {protocolVersion: 8, origin: 'http://websocket.org'}); +ws.on('open', function() { + console.log('connected'); + ws.send(Date.now().toString(), {mask: true}); +}); +ws.on('close', function() { + console.log('disconnected'); +}); +ws.on('message', function(data, flags) { + console.log('Roundtrip time: ' + (Date.now() - parseInt(data)) + 'ms', flags); + setTimeout(function() { + ws.send(Date.now().toString(), {mask: true}); + }, 500); +}); +``` + +### wscat against echo.websocket.org ### + + $ npm install -g ws + $ wscat -c ws://echo.websocket.org -p 8 + connected (press CTRL+C to quit) + > hi there + < hi there + > are you a happy parrot? + < are you a happy parrot? + +### Other examples ### + +For a full example with a browser client communicating with a ws server, see the examples folder. + +Note that the usage together with Express 3.0 is quite different from Express 2.x. The difference is expressed in the two different serverstats-examples. + +Otherwise, see the test cases. + +### Running the tests ### + +`make test` + +## API Docs ## + +See the doc/ directory for Node.js-like docs for the ws classes. + +## License ## + +(The MIT License) + +Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/ws/bench/parser.benchmark.js b/node_modules/ws/bench/parser.benchmark.js new file mode 100644 index 0000000..ff5f737 --- /dev/null +++ b/node_modules/ws/bench/parser.benchmark.js @@ -0,0 +1,115 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +/** + * Benchmark dependencies. + */ + +var benchmark = require('benchmark') + , Receiver = require('../').Receiver + , suite = new benchmark.Suite('Receiver'); +require('tinycolor'); +require('./util'); + +/** + * Setup receiver. + */ + +suite.on('start', function () { + receiver = new Receiver(); +}); + +suite.on('cycle', function () { + receiver = new Receiver(); +}); + +/** + * Benchmarks. + */ + +var pingMessage = 'Hello' + , pingPacket1 = getBufferFromHexString('89 ' + (pack(2, 0x80 | pingMessage.length)) + + ' 34 83 a8 68 '+ getHexStringFromBuffer(mask(pingMessage, '34 83 a8 68'))); +suite.add('ping message', function () { + receiver.add(pingPacket1); +}); + +var pingPacket2 = getBufferFromHexString('89 00') +suite.add('ping with no data', function () { + receiver.add(pingPacket2); +}); + +var closePacket = getBufferFromHexString('88 00'); +suite.add('close message', function () { + receiver.add(closePacket); + receiver.endPacket(); +}); + +var maskedTextPacket = getBufferFromHexString('81 93 34 83 a8 68 01 b9 92 52 4f a1 c6 09 59 e6 8a 52 16 e6 cb 00 5b a1 d5'); +suite.add('masked text message', function () { + receiver.add(maskedTextPacket); +}); + +binaryDataPacket = (function() { + var length = 125 + , message = new Buffer(length) + for (var i = 0; i < length; ++i) message[i] = i % 10; + return getBufferFromHexString('82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + + getHexStringFromBuffer(mask(message), '34 83 a8 68')); +})(); +suite.add('binary data (125 bytes)', function () { + try { + receiver.add(binaryDataPacket); + + } + catch(e) {console.log(e)} +}); + +binaryDataPacket2 = (function() { + var length = 65535 + , message = new Buffer(length) + for (var i = 0; i < length; ++i) message[i] = i % 10; + return getBufferFromHexString('82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + + getHexStringFromBuffer(mask(message), '34 83 a8 68')); +})(); +suite.add('binary data (65535 bytes)', function () { + receiver.add(binaryDataPacket2); +}); + +binaryDataPacket3 = (function() { + var length = 200*1024 + , message = new Buffer(length) + for (var i = 0; i < length; ++i) message[i] = i % 10; + return getBufferFromHexString('82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + + getHexStringFromBuffer(mask(message), '34 83 a8 68')); +})(); +suite.add('binary data (200 kB)', function () { + receiver.add(binaryDataPacket3); +}); + +/** + * Output progress. + */ + +suite.on('cycle', function (bench, details) { + console.log('\n ' + suite.name.grey, details.name.white.bold); + console.log(' ' + [ + details.hz.toFixed(2).cyan + ' ops/sec'.grey + , details.count.toString().white + ' times executed'.grey + , 'benchmark took '.grey + details.times.elapsed.toString().white + ' sec.'.grey + , + ].join(', '.grey)); +}); + +/** + * Run/export benchmarks. + */ + +if (!module.parent) { + suite.run(); +} else { + module.exports = suite; +} diff --git a/node_modules/ws/bench/sender.benchmark.js b/node_modules/ws/bench/sender.benchmark.js new file mode 100644 index 0000000..20c171a --- /dev/null +++ b/node_modules/ws/bench/sender.benchmark.js @@ -0,0 +1,66 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +/** + * Benchmark dependencies. + */ + +var benchmark = require('benchmark') + , Sender = require('../').Sender + , suite = new benchmark.Suite('Sender'); +require('tinycolor'); +require('./util'); + +/** + * Setup sender. + */ + +suite.on('start', function () { + sender = new Sender(); + sender._socket = { write: function() {} }; +}); + +suite.on('cycle', function () { + sender = new Sender(); + sender._socket = { write: function() {} }; +}); + +/** + * Benchmarks + */ + +framePacket = new Buffer(200*1024); +framePacket.fill(99); +suite.add('frameAndSend, unmasked (200 kB)', function () { + sender.frameAndSend(0x2, framePacket, true, false); +}); +suite.add('frameAndSend, masked (200 kB)', function () { + sender.frameAndSend(0x2, framePacket, true, true); +}); + +/** + * Output progress. + */ + +suite.on('cycle', function (bench, details) { + console.log('\n ' + suite.name.grey, details.name.white.bold); + console.log(' ' + [ + details.hz.toFixed(2).cyan + ' ops/sec'.grey + , details.count.toString().white + ' times executed'.grey + , 'benchmark took '.grey + details.times.elapsed.toString().white + ' sec.'.grey + , + ].join(', '.grey)); +}); + +/** + * Run/export benchmarks. + */ + +if (!module.parent) { + suite.run(); +} else { + module.exports = suite; +} diff --git a/node_modules/ws/bench/speed.js b/node_modules/ws/bench/speed.js new file mode 100644 index 0000000..3ce6414 --- /dev/null +++ b/node_modules/ws/bench/speed.js @@ -0,0 +1,105 @@ +var cluster = require('cluster') + , WebSocket = require('../') + , WebSocketServer = WebSocket.Server + , crypto = require('crypto') + , util = require('util') + , ansi = require('ansi'); +require('tinycolor'); + +function roundPrec(num, prec) { + var mul = Math.pow(10, prec); + return Math.round(num * mul) / mul; +} + +function humanSize(bytes) { + if (bytes >= 1048576) return roundPrec(bytes / 1048576, 2) + ' MB'; + if (bytes >= 1024) return roundPrec(bytes / 1024, 2) + ' kB'; + return roundPrec(bytes, 2) + ' B'; +} + +function generateRandomData(size) { + var buffer = new Buffer(size); + for (var i = 0; i < size; ++i) { + buffer[i] = ~~(Math.random() * 127); + } + return buffer; +} + +if (cluster.isMaster) { + var wss = new WebSocketServer({port: 8181}, function() { + cluster.fork(); + }); + wss.on('connection', function(ws) { + ws.on('message', function(data, flags) { + ws.send(data, {binary: flags&&flags.binary}); + }); + ws.on('close', function() {}); + }); + cluster.on('death', function(worker) { + wss.close(); + }); +} +else { + var cursor = ansi(process.stdout); + + var configs = [ + [true, 10000, 64], + [true, 5000, 16*1024], + [true, 1000, 128*1024], + [true, 100, 1024*1024], + [true, 1, 500*1024*1024], + [false, 10000, 64], + [false, 5000, 16*1024], + [false, 1000, 128*1024], + [false, 100, 1024*1024], + ]; + + var largest = configs[0][1]; + for (var i = 0, l = configs.length; i < l; ++i) { + if (configs[i][2] > largest) largest = configs[i][2]; + } + + console.log('Generating %s of test data ...', humanSize(largest)); + var randomBytes = generateRandomData(largest); + + function roundtrip(useBinary, roundtrips, size, cb) { + var data = randomBytes.slice(0, size); + var prefix = util.format('Running %d roundtrips of %s %s data', roundtrips, humanSize(size), useBinary ? 'binary' : 'text'); + console.log(prefix); + var client = new WebSocket('ws://localhost:' + '8181'); + var dt; + var roundtrip = 0; + function send() { + client.send(data, {binary: useBinary}); + } + client.on('error', function(e) { + console.error(e); + process.exit(); + }); + client.on('open', function() { + dt = Date.now(); + send(); + }); + client.on('message', function(data, flags) { + if (++roundtrip == roundtrips) { + var elapsed = Date.now() - dt; + cursor.up(); + console.log('%s:\t%ss\t%s' + , useBinary ? prefix.green : prefix.cyan + , roundPrec(elapsed / 1000, 1).toString().green.bold + , (humanSize((size * roundtrips) / elapsed * 1000) + '/s').blue.bold); + client.close(); + cb(); + return; + } + process.nextTick(send); + }); + } + + (function run() { + if (configs.length == 0) process.exit(); + var config = configs.shift(); + config.push(run); + roundtrip.apply(null, config); + })(); +} \ No newline at end of file diff --git a/node_modules/ws/bench/util.js b/node_modules/ws/bench/util.js new file mode 100644 index 0000000..5f01281 --- /dev/null +++ b/node_modules/ws/bench/util.js @@ -0,0 +1,105 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +/** + * Returns a Buffer from a "ff 00 ff"-type hex string. + */ + +getBufferFromHexString = function(byteStr) { + var bytes = byteStr.split(' '); + var buf = new Buffer(bytes.length); + for (var i = 0; i < bytes.length; ++i) { + buf[i] = parseInt(bytes[i], 16); + } + return buf; +} + +/** + * Returns a hex string from a Buffer. + */ + +getHexStringFromBuffer = function(data) { + var s = ''; + for (var i = 0; i < data.length; ++i) { + s += padl(data[i].toString(16), 2, '0') + ' '; + } + return s.trim(); +} + +/** + * Splits a buffer in two parts. + */ + +splitBuffer = function(buffer) { + var b1 = new Buffer(Math.ceil(buffer.length / 2)); + buffer.copy(b1, 0, 0, b1.length); + var b2 = new Buffer(Math.floor(buffer.length / 2)); + buffer.copy(b2, 0, b1.length, b1.length + b2.length); + return [b1, b2]; +} + +/** + * Performs hybi07+ type masking on a hex string or buffer. + */ + +mask = function(buf, maskString) { + if (typeof buf == 'string') buf = new Buffer(buf); + var mask = getBufferFromHexString(maskString || '34 83 a8 68'); + for (var i = 0; i < buf.length; ++i) { + buf[i] ^= mask[i % 4]; + } + return buf; +} + +/** + * Returns a hex string representing the length of a message + */ + +getHybiLengthAsHexString = function(len, masked) { + if (len < 126) { + var buf = new Buffer(1); + buf[0] = (masked ? 0x80 : 0) | len; + } + else if (len < 65536) { + var buf = new Buffer(3); + buf[0] = (masked ? 0x80 : 0) | 126; + getBufferFromHexString(pack(4, len)).copy(buf, 1); + } + else { + var buf = new Buffer(9); + buf[0] = (masked ? 0x80 : 0) | 127; + getBufferFromHexString(pack(16, len)).copy(buf, 1); + } + return getHexStringFromBuffer(buf); +} + +/** + * Unpacks a Buffer into a number. + */ + +unpack = function(buffer) { + var n = 0; + for (var i = 0; i < buffer.length; ++i) { + n = (i == 0) ? buffer[i] : (n * 256) + buffer[i]; + } + return n; +} + +/** + * Returns a hex string, representing a specific byte count 'length', from a number. + */ + +pack = function(length, number) { + return padl(number.toString(16), length, '0').replace(/([0-9a-f][0-9a-f])/gi, '$1 ').trim(); +} + +/** + * Left pads the string 's' to a total length of 'n' with char 'c'. + */ + +padl = function(s, n, c) { + return new Array(1 + n - s.length).join(c) + s; +} diff --git a/node_modules/ws/bin/wscat b/node_modules/ws/bin/wscat new file mode 100755 index 0000000..0ec894d --- /dev/null +++ b/node_modules/ws/bin/wscat @@ -0,0 +1,190 @@ +#!/usr/bin/env node + +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var WebSocket = require('../') + , fs = require('fs') + , program = require('commander') + , util = require('util') + , events = require('events') + , readline = require('readline'); + +/** + * InputReader - processes console input + */ + +function Console() { + this.stdin = process.stdin; + this.stdout = process.stdout; + + this.readlineInterface = readline.createInterface(this.stdin, this.stdout); + + var self = this; + this.readlineInterface.on('line', function(data) { + self.emit('line', data); + }); + this.readlineInterface.on('close', function() { + self.emit('close'); + }); + + this._resetInput = function() { + self.clear(); + } +} +util.inherits(Console, events.EventEmitter); + +Console.Colors = { + Red: '\033[31m', + Green: '\033[32m', + Yellow: '\033[33m', + Blue: '\033[34m', + Default: '\033[39m' +}; + +Console.prototype.prompt = function() { + this.readlineInterface.prompt(); +} + +Console.prototype.print = function(msg, color) { + this.clear(); + color = color || Console.Colors.Default; + this.stdout.write(color + msg + Console.Colors.Default + '\n'); + this.prompt(); +} + +Console.prototype.clear = function() { + this.stdout.write('\033[2K\033[E'); +} + +Console.prototype.pause = function() { + this.stdin.on('keypress', this._resetInput); +} + +Console.prototype.resume = function() { + this.stdin.removeListener('keypress', this._resetInput); +} + +/** + * The actual application + */ + +var version = JSON.parse(fs.readFileSync(__dirname + '/../package.json', 'utf8')).version; +program + .version(version) + .usage('[options] ') + .option('-l, --listen ', 'listen on port') + .option('-c, --connect ', 'connect to a websocket server') + .option('-p, --protocol ', 'optional protocol version') + .option('-o, --origin ', 'optional origin') + .option('--host ', 'optional host') + .option('-s, --subprotocol ', 'optional subprotocol') + .parse(process.argv); + +if (program.listen && program.connect) { + console.error('\033[33merror: use either --listen or --connect\033[39m'); + process.exit(-1); +} +else if (program.listen) { + var wsConsole = new Console(); + wsConsole.pause(); + var options = {}; + if (program.protocol) options.protocolVersion = program.protocol; + if (program.origin) options.origin = program.origin; + if (program.subprotocol) options.protocol = program.subprotocol; + var ws = null; + var wss = new WebSocket.Server({port: program.listen}, function() { + wsConsole.print('listening on port ' + program.listen + ' (press CTRL+C to quit)', Console.Colors.Green); + wsConsole.clear(); + }); + wsConsole.on('close', function() { + if (ws) { + try { + ws.close(); + } + catch (e) {} + } + process.exit(0); + }); + wsConsole.on('line', function(data) { + if (ws) { + ws.send(data, {mask: false}); + wsConsole.prompt(); + } + }); + wss.on('connection', function(newClient) { + if (ws) { + // limit to one client + newClient.terminate(); + return; + }; + ws = newClient; + wsConsole.resume(); + wsConsole.prompt(); + wsConsole.print('client connected', Console.Colors.Green); + ws.on('close', function() { + wsConsole.print('disconnected', Console.Colors.Green); + wsConsole.clear(); + wsConsole.pause(); + ws = null; + }); + ws.on('error', function(code, description) { + wsConsole.print('error: ' + code + (description ? ' ' + description : ''), Console.Colors.Yellow); + }); + ws.on('message', function(data, flags) { + wsConsole.print('< ' + data, Console.Colors.Blue); + }); + }); + wss.on('error', function(error) { + wsConsole.print('error: ' + error.toString(), Console.Colors.Yellow); + process.exit(-1); + }); +} +else if (program.connect) { + var wsConsole = new Console(); + var options = {}; + if (program.protocol) options.protocolVersion = program.protocol; + if (program.origin) options.origin = program.origin; + if (program.subprotocol) options.protocol = program.subprotocol; + if (program.host) options.host = program.host; + var ws = new WebSocket(program.connect, options); + ws.on('open', function() { + wsConsole.print('connected (press CTRL+C to quit)', Console.Colors.Green); + wsConsole.on('line', function(data) { + ws.send(data, {mask: true}); + wsConsole.prompt(); + }); + }); + ws.on('close', function() { + wsConsole.print('disconnected', Console.Colors.Green); + wsConsole.clear(); + process.exit(); + }); + ws.on('error', function(code, description) { + wsConsole.print('error: ' + code + (description ? ' ' + description : ''), Console.Colors.Yellow); + process.exit(-1); + }); + ws.on('message', function(data, flags) { + wsConsole.print('< ' + data, Console.Colors.Blue); + }); + wsConsole.on('close', function() { + if (ws) { + try { + ws.close(); + } + catch(e) {} + process.exit(); + } + }); +} +else { + console.error('\033[33merror: use either --listen or --connect\033[39m'); + process.exit(-1); +} diff --git a/node_modules/ws/binding.gyp b/node_modules/ws/binding.gyp new file mode 100644 index 0000000..c9b4d1a --- /dev/null +++ b/node_modules/ws/binding.gyp @@ -0,0 +1,16 @@ +{ + 'targets': [ + { + 'target_name': 'validation', + 'include_dirs': ["> $(depfile) +# Add extra rules as in (2). +# We remove slashes and replace spaces with new lines; +# remove blank lines; +# delete the first line and append a colon to the remaining lines. +sed -e 's|\\||' -e 'y| |\n|' $(depfile).raw |\ + grep -v '^$$' |\ + sed -e 1d -e 's|$$|:|' \ + >> $(depfile) +rm $(depfile).raw +endef + +# Command definitions: +# - cmd_foo is the actual command to run; +# - quiet_cmd_foo is the brief-output summary of the command. + +quiet_cmd_cc = CC($(TOOLSET)) $@ +cmd_cc = $(CC.$(TOOLSET)) $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_cxx = CXX($(TOOLSET)) $@ +cmd_cxx = $(CXX.$(TOOLSET)) $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_objc = CXX($(TOOLSET)) $@ +cmd_objc = $(CC.$(TOOLSET)) $(GYP_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $< + +quiet_cmd_objcxx = CXX($(TOOLSET)) $@ +cmd_objcxx = $(CXX.$(TOOLSET)) $(GYP_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $< + +# Commands for precompiled header files. +quiet_cmd_pch_c = CXX($(TOOLSET)) $@ +cmd_pch_c = $(CC.$(TOOLSET)) $(GYP_PCH_CFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< +quiet_cmd_pch_cc = CXX($(TOOLSET)) $@ +cmd_pch_cc = $(CC.$(TOOLSET)) $(GYP_PCH_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< +quiet_cmd_pch_m = CXX($(TOOLSET)) $@ +cmd_pch_m = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $< +quiet_cmd_pch_mm = CXX($(TOOLSET)) $@ +cmd_pch_mm = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $< + +# gyp-mac-tool is written next to the root Makefile by gyp. +# Use $(4) for the command, since $(2) and $(3) are used as flag by do_cmd +# already. +quiet_cmd_mac_tool = MACTOOL $(4) $< +cmd_mac_tool = ./gyp-mac-tool $(4) $< "$@" + +quiet_cmd_mac_package_framework = PACKAGE FRAMEWORK $@ +cmd_mac_package_framework = ./gyp-mac-tool package-framework "$@" $(4) + +quiet_cmd_infoplist = INFOPLIST $@ +cmd_infoplist = $(CC.$(TOOLSET)) -E -P -Wno-trigraphs -x c $(INFOPLIST_DEFINES) "$<" -o "$@" + +quiet_cmd_touch = TOUCH $@ +cmd_touch = touch $@ + +quiet_cmd_copy = COPY $@ +# send stderr to /dev/null to ignore messages when linking directories. +cmd_copy = ln -f "$<" "$@" 2>/dev/null || (rm -rf "$@" && cp -af "$<" "$@") + +quiet_cmd_alink = LIBTOOL-STATIC $@ +cmd_alink = rm -f $@ && ./gyp-mac-tool filter-libtool libtool -static -o $@ $(filter %.o,$^) + +quiet_cmd_link = LINK($(TOOLSET)) $@ +cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS) + +# TODO(thakis): Find out and document the difference between shared_library and +# loadable_module on mac. +quiet_cmd_solink = SOLINK($(TOOLSET)) $@ +cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS) + +# TODO(thakis): The solink_module rule is likely wrong. Xcode seems to pass +# -bundle -single_module here (for osmesa.so). +quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ +cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS) + + +# Define an escape_quotes function to escape single quotes. +# This allows us to handle quotes properly as long as we always use +# use single quotes and escape_quotes. +escape_quotes = $(subst ','\'',$(1)) +# This comment is here just to include a ' to unconfuse syntax highlighting. +# Define an escape_vars function to escape '$' variable syntax. +# This allows us to read/write command lines with shell variables (e.g. +# $LD_LIBRARY_PATH), without triggering make substitution. +escape_vars = $(subst $$,$$$$,$(1)) +# Helper that expands to a shell command to echo a string exactly as it is in +# make. This uses printf instead of echo because printf's behaviour with respect +# to escape sequences is more portable than echo's across different shells +# (e.g., dash, bash). +exact_echo = printf '%s\n' '$(call escape_quotes,$(1))' + +# Helper to compare the command we're about to run against the command +# we logged the last time we ran the command. Produces an empty +# string (false) when the commands match. +# Tricky point: Make has no string-equality test function. +# The kernel uses the following, but it seems like it would have false +# positives, where one string reordered its arguments. +# arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \ +# $(filter-out $(cmd_$@), $(cmd_$(1)))) +# We instead substitute each for the empty string into the other, and +# say they're equal if both substitutions produce the empty string. +# .d files contain ? instead of spaces, take that into account. +command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\ + $(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1)))) + +# Helper that is non-empty when a prerequisite changes. +# Normally make does this implicitly, but we force rules to always run +# so we can check their command lines. +# $? -- new prerequisites +# $| -- order-only dependencies +prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?)) + +# Helper that executes all postbuilds, and deletes the output file when done +# if any of the postbuilds failed. +define do_postbuilds + @E=0;\ + for p in $(POSTBUILDS); do\ + eval $$p;\ + F=$$?;\ + if [ $$F -ne 0 ]; then\ + E=$$F;\ + fi;\ + done;\ + if [ $$E -ne 0 ]; then\ + rm -rf "$@";\ + exit $$E;\ + fi +endef + +# do_cmd: run a command via the above cmd_foo names, if necessary. +# Should always run for a given target to handle command-line changes. +# Second argument, if non-zero, makes it do asm/C/C++ dependency munging. +# Third argument, if non-zero, makes it do POSTBUILDS processing. +# Note: We intentionally do NOT call dirx for depfile, since it contains ? for +# spaces already and dirx strips the ? characters. +define do_cmd +$(if $(or $(command_changed),$(prereq_changed)), + @$(call exact_echo, $($(quiet)cmd_$(1))) + @mkdir -p "$(call dirx,$@)" "$(dir $(depfile))" + $(if $(findstring flock,$(word 2,$(cmd_$1))), + @$(cmd_$(1)) + @echo " $(quiet_cmd_$(1)): Finished", + @$(cmd_$(1)) + ) + @$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile) + @$(if $(2),$(fixup_dep)) + $(if $(and $(3), $(POSTBUILDS)), + $(call do_postbuilds) + ) +) +endef + +# Declare the "all" target first so it is the default, +# even though we don't have the deps yet. +.PHONY: all +all: + +# make looks for ways to re-generate included makefiles, but in our case, we +# don't have a direct way. Explicitly telling make that it has nothing to do +# for them makes it go faster. +%.d: ; + +# Use FORCE_DO_CMD to force a target to run. Should be coupled with +# do_cmd. +.PHONY: FORCE_DO_CMD +FORCE_DO_CMD: + +TOOLSET := target +# Suffix rules, putting all outputs into $(obj). +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + + +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,bufferutil.target.mk)))),) + include bufferutil.target.mk +endif +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,validation.target.mk)))),) + include validation.target.mk +endif + +quiet_cmd_regen_makefile = ACTION Regenerating $@ +cmd_regen_makefile = /Users/jefklak/.node-gyp/0.8.12/tools/gyp/gyp -fmake --ignore-environment "--toplevel-dir=." -I/Users/jefklak/development/privatews/webcam/node_modules/ws/build/config.gypi -I/usr/local/Cellar/node/0.8.12/lib/node_modules/npm/node_modules/node-gyp/addon.gypi -I/Users/jefklak/.node-gyp/0.8.12/common.gypi "--depth=." "-Goutput_dir=." "--generator-output=build" "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/Users/jefklak/.node-gyp/0.8.12" "-Dmodule_root_dir=/Users/jefklak/development/privatews/webcam/node_modules/ws" binding.gyp +Makefile: $(srcdir)/../../../../../.node-gyp/0.8.12/common.gypi $(srcdir)/../../../../../../../usr/local/Cellar/node/0.8.12/lib/node_modules/npm/node_modules/node-gyp/addon.gypi $(srcdir)/build/config.gypi $(srcdir)/binding.gyp + $(call do_cmd,regen_makefile) + +# "all" is a concatenation of the "all" targets from all the included +# sub-makefiles. This is just here to clarify. +all: + +# Add in dependency-tracking rules. $(all_deps) is the list of every single +# target in our tree. Only consider the ones with .d (dependency) info: +d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d)) +ifneq ($(d_files),) + include $(d_files) +endif diff --git a/node_modules/ws/build/Release/.deps/Release/bufferutil.node.d b/node_modules/ws/build/Release/.deps/Release/bufferutil.node.d new file mode 100644 index 0000000..45d22a2 --- /dev/null +++ b/node_modules/ws/build/Release/.deps/Release/bufferutil.node.d @@ -0,0 +1 @@ +cmd_Release/bufferutil.node := ./gyp-mac-tool flock ./Release/linker.lock c++ -shared -Wl,-search_paths_first -mmacosx-version-min=10.5 -arch x86_64 -L./Release -install_name /usr/local/lib/bufferutil.node -o Release/bufferutil.node Release/obj.target/bufferutil/src/bufferutil.o -undefined dynamic_lookup diff --git a/node_modules/ws/build/Release/.deps/Release/obj.target/bufferutil/src/bufferutil.o.d b/node_modules/ws/build/Release/.deps/Release/obj.target/bufferutil/src/bufferutil.o.d new file mode 100644 index 0000000..45d116f --- /dev/null +++ b/node_modules/ws/build/Release/.deps/Release/obj.target/bufferutil/src/bufferutil.o.d @@ -0,0 +1,33 @@ +cmd_Release/obj.target/bufferutil/src/bufferutil.o := c++ '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-D_DARWIN_USE_64_BIT_INODE=1' -I/Users/jefklak/.node-gyp/0.8.12/src -I/Users/jefklak/.node-gyp/0.8.12/deps/uv/include -I/Users/jefklak/.node-gyp/0.8.12/deps/v8/include -I/Users/jefklak/development/privatews/webcam/node_modules/ws/node_modules/nan -Os -gdwarf-2 -mmacosx-version-min=10.5 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -fno-rtti -fno-exceptions -fno-threadsafe-statics -fno-strict-aliasing -MMD -MF ./Release/.deps/Release/obj.target/bufferutil/src/bufferutil.o.d.raw -c -o Release/obj.target/bufferutil/src/bufferutil.o ../src/bufferutil.cc +Release/obj.target/bufferutil/src/bufferutil.o: ../src/bufferutil.cc \ + /Users/jefklak/.node-gyp/0.8.12/deps/v8/include/v8.h \ + /Users/jefklak/.node-gyp/0.8.12/deps/v8/include/v8stdint.h \ + /Users/jefklak/.node-gyp/0.8.12/src/node.h \ + /Users/jefklak/.node-gyp/0.8.12/deps/uv/include/uv.h \ + /Users/jefklak/.node-gyp/0.8.12/deps/uv/include/ares.h \ + /Users/jefklak/.node-gyp/0.8.12/deps/uv/include/ares_version.h \ + /Users/jefklak/.node-gyp/0.8.12/deps/uv/include/uv-private/uv-unix.h \ + /Users/jefklak/.node-gyp/0.8.12/deps/uv/include/uv-private/ngx-queue.h \ + /Users/jefklak/.node-gyp/0.8.12/deps/uv/include/uv-private/ev.h \ + /Users/jefklak/.node-gyp/0.8.12/deps/uv/include/uv-private/eio.h \ + /Users/jefklak/.node-gyp/0.8.12/src/node_object_wrap.h \ + /Users/jefklak/.node-gyp/0.8.12/src/ev-emul.h \ + /Users/jefklak/.node-gyp/0.8.12/src/eio-emul.h \ + /Users/jefklak/.node-gyp/0.8.12/src/node_buffer.h \ + /Users/jefklak/development/privatews/webcam/node_modules/ws/node_modules/nan/nan.h +../src/bufferutil.cc: +/Users/jefklak/.node-gyp/0.8.12/deps/v8/include/v8.h: +/Users/jefklak/.node-gyp/0.8.12/deps/v8/include/v8stdint.h: +/Users/jefklak/.node-gyp/0.8.12/src/node.h: +/Users/jefklak/.node-gyp/0.8.12/deps/uv/include/uv.h: +/Users/jefklak/.node-gyp/0.8.12/deps/uv/include/ares.h: +/Users/jefklak/.node-gyp/0.8.12/deps/uv/include/ares_version.h: +/Users/jefklak/.node-gyp/0.8.12/deps/uv/include/uv-private/uv-unix.h: +/Users/jefklak/.node-gyp/0.8.12/deps/uv/include/uv-private/ngx-queue.h: +/Users/jefklak/.node-gyp/0.8.12/deps/uv/include/uv-private/ev.h: +/Users/jefklak/.node-gyp/0.8.12/deps/uv/include/uv-private/eio.h: +/Users/jefklak/.node-gyp/0.8.12/src/node_object_wrap.h: +/Users/jefklak/.node-gyp/0.8.12/src/ev-emul.h: +/Users/jefklak/.node-gyp/0.8.12/src/eio-emul.h: +/Users/jefklak/.node-gyp/0.8.12/src/node_buffer.h: +/Users/jefklak/development/privatews/webcam/node_modules/ws/node_modules/nan/nan.h: diff --git a/node_modules/ws/build/Release/.deps/Release/obj.target/validation/src/validation.o.d b/node_modules/ws/build/Release/.deps/Release/obj.target/validation/src/validation.o.d new file mode 100644 index 0000000..a827fc8 --- /dev/null +++ b/node_modules/ws/build/Release/.deps/Release/obj.target/validation/src/validation.o.d @@ -0,0 +1,33 @@ +cmd_Release/obj.target/validation/src/validation.o := c++ '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-D_DARWIN_USE_64_BIT_INODE=1' -I/Users/jefklak/.node-gyp/0.8.12/src -I/Users/jefklak/.node-gyp/0.8.12/deps/uv/include -I/Users/jefklak/.node-gyp/0.8.12/deps/v8/include -I/Users/jefklak/development/privatews/webcam/node_modules/ws/node_modules/nan -Os -gdwarf-2 -mmacosx-version-min=10.5 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -fno-rtti -fno-exceptions -fno-threadsafe-statics -fno-strict-aliasing -MMD -MF ./Release/.deps/Release/obj.target/validation/src/validation.o.d.raw -c -o Release/obj.target/validation/src/validation.o ../src/validation.cc +Release/obj.target/validation/src/validation.o: ../src/validation.cc \ + /Users/jefklak/.node-gyp/0.8.12/deps/v8/include/v8.h \ + /Users/jefklak/.node-gyp/0.8.12/deps/v8/include/v8stdint.h \ + /Users/jefklak/.node-gyp/0.8.12/src/node.h \ + /Users/jefklak/.node-gyp/0.8.12/deps/uv/include/uv.h \ + /Users/jefklak/.node-gyp/0.8.12/deps/uv/include/ares.h \ + /Users/jefklak/.node-gyp/0.8.12/deps/uv/include/ares_version.h \ + /Users/jefklak/.node-gyp/0.8.12/deps/uv/include/uv-private/uv-unix.h \ + /Users/jefklak/.node-gyp/0.8.12/deps/uv/include/uv-private/ngx-queue.h \ + /Users/jefklak/.node-gyp/0.8.12/deps/uv/include/uv-private/ev.h \ + /Users/jefklak/.node-gyp/0.8.12/deps/uv/include/uv-private/eio.h \ + /Users/jefklak/.node-gyp/0.8.12/src/node_object_wrap.h \ + /Users/jefklak/.node-gyp/0.8.12/src/ev-emul.h \ + /Users/jefklak/.node-gyp/0.8.12/src/eio-emul.h \ + /Users/jefklak/.node-gyp/0.8.12/src/node_buffer.h \ + /Users/jefklak/development/privatews/webcam/node_modules/ws/node_modules/nan/nan.h +../src/validation.cc: +/Users/jefklak/.node-gyp/0.8.12/deps/v8/include/v8.h: +/Users/jefklak/.node-gyp/0.8.12/deps/v8/include/v8stdint.h: +/Users/jefklak/.node-gyp/0.8.12/src/node.h: +/Users/jefklak/.node-gyp/0.8.12/deps/uv/include/uv.h: +/Users/jefklak/.node-gyp/0.8.12/deps/uv/include/ares.h: +/Users/jefklak/.node-gyp/0.8.12/deps/uv/include/ares_version.h: +/Users/jefklak/.node-gyp/0.8.12/deps/uv/include/uv-private/uv-unix.h: +/Users/jefklak/.node-gyp/0.8.12/deps/uv/include/uv-private/ngx-queue.h: +/Users/jefklak/.node-gyp/0.8.12/deps/uv/include/uv-private/ev.h: +/Users/jefklak/.node-gyp/0.8.12/deps/uv/include/uv-private/eio.h: +/Users/jefklak/.node-gyp/0.8.12/src/node_object_wrap.h: +/Users/jefklak/.node-gyp/0.8.12/src/ev-emul.h: +/Users/jefklak/.node-gyp/0.8.12/src/eio-emul.h: +/Users/jefklak/.node-gyp/0.8.12/src/node_buffer.h: +/Users/jefklak/development/privatews/webcam/node_modules/ws/node_modules/nan/nan.h: diff --git a/node_modules/ws/build/Release/.deps/Release/validation.node.d b/node_modules/ws/build/Release/.deps/Release/validation.node.d new file mode 100644 index 0000000..55bc3e7 --- /dev/null +++ b/node_modules/ws/build/Release/.deps/Release/validation.node.d @@ -0,0 +1 @@ +cmd_Release/validation.node := ./gyp-mac-tool flock ./Release/linker.lock c++ -shared -Wl,-search_paths_first -mmacosx-version-min=10.5 -arch x86_64 -L./Release -install_name /usr/local/lib/validation.node -o Release/validation.node Release/obj.target/validation/src/validation.o -undefined dynamic_lookup diff --git a/node_modules/ws/build/Release/bufferutil.node b/node_modules/ws/build/Release/bufferutil.node new file mode 100755 index 0000000..9cb2b98 Binary files /dev/null and b/node_modules/ws/build/Release/bufferutil.node differ diff --git a/node_modules/ws/build/Release/linker.lock b/node_modules/ws/build/Release/linker.lock new file mode 100644 index 0000000..e69de29 diff --git a/node_modules/ws/build/Release/obj.target/bufferutil/src/bufferutil.o b/node_modules/ws/build/Release/obj.target/bufferutil/src/bufferutil.o new file mode 100644 index 0000000..aa26c86 Binary files /dev/null and b/node_modules/ws/build/Release/obj.target/bufferutil/src/bufferutil.o differ diff --git a/node_modules/ws/build/Release/obj.target/validation/src/validation.o b/node_modules/ws/build/Release/obj.target/validation/src/validation.o new file mode 100644 index 0000000..f077de7 Binary files /dev/null and b/node_modules/ws/build/Release/obj.target/validation/src/validation.o differ diff --git a/node_modules/ws/build/Release/validation.node b/node_modules/ws/build/Release/validation.node new file mode 100755 index 0000000..b3d2c22 Binary files /dev/null and b/node_modules/ws/build/Release/validation.node differ diff --git a/node_modules/ws/build/binding.Makefile b/node_modules/ws/build/binding.Makefile new file mode 100644 index 0000000..a69b3d2 --- /dev/null +++ b/node_modules/ws/build/binding.Makefile @@ -0,0 +1,6 @@ +# This file is generated by gyp; do not edit. + +export builddir_name ?= build/./. +.PHONY: all +all: + $(MAKE) bufferutil validation diff --git a/node_modules/ws/build/bufferutil.target.mk b/node_modules/ws/build/bufferutil.target.mk new file mode 100644 index 0000000..04820fb --- /dev/null +++ b/node_modules/ws/build/bufferutil.target.mk @@ -0,0 +1,149 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := bufferutil +DEFS_Debug := \ + '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-D_DARWIN_USE_64_BIT_INODE=1' \ + '-DDEBUG' \ + '-D_DEBUG' + +# Flags passed to all source files. +CFLAGS_Debug := \ + -Os \ + -gdwarf-2 \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -Wall \ + -Wendif-labels \ + -W \ + -Wno-unused-parameter + +# Flags passed to only C files. +CFLAGS_C_Debug := \ + -fno-strict-aliasing + +# Flags passed to only C++ files. +CFLAGS_CC_Debug := \ + -fno-rtti \ + -fno-exceptions \ + -fno-threadsafe-statics \ + -fno-strict-aliasing + +# Flags passed to only ObjC files. +CFLAGS_OBJC_Debug := + +# Flags passed to only ObjC++ files. +CFLAGS_OBJCC_Debug := + +INCS_Debug := \ + -I/Users/jefklak/.node-gyp/0.8.12/src \ + -I/Users/jefklak/.node-gyp/0.8.12/deps/uv/include \ + -I/Users/jefklak/.node-gyp/0.8.12/deps/v8/include \ + -I/Users/jefklak/development/privatews/webcam/node_modules/ws/node_modules/nan + +DEFS_Release := \ + '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-D_DARWIN_USE_64_BIT_INODE=1' + +# Flags passed to all source files. +CFLAGS_Release := \ + -Os \ + -gdwarf-2 \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -Wall \ + -Wendif-labels \ + -W \ + -Wno-unused-parameter + +# Flags passed to only C files. +CFLAGS_C_Release := \ + -fno-strict-aliasing + +# Flags passed to only C++ files. +CFLAGS_CC_Release := \ + -fno-rtti \ + -fno-exceptions \ + -fno-threadsafe-statics \ + -fno-strict-aliasing + +# Flags passed to only ObjC files. +CFLAGS_OBJC_Release := + +# Flags passed to only ObjC++ files. +CFLAGS_OBJCC_Release := + +INCS_Release := \ + -I/Users/jefklak/.node-gyp/0.8.12/src \ + -I/Users/jefklak/.node-gyp/0.8.12/deps/uv/include \ + -I/Users/jefklak/.node-gyp/0.8.12/deps/v8/include \ + -I/Users/jefklak/development/privatews/webcam/node_modules/ws/node_modules/nan + +OBJS := \ + $(obj).target/$(TARGET)/src/bufferutil.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(CFLAGS_OBJC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(CFLAGS_OBJCC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Debug := \ + -Wl,-search_paths_first \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -L$(builddir) \ + -install_name /usr/local/lib/bufferutil.node + +LDFLAGS_Release := \ + -Wl,-search_paths_first \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -L$(builddir) \ + -install_name /usr/local/lib/bufferutil.node + +LIBS := \ + -undefined dynamic_lookup + +$(builddir)/bufferutil.node: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/bufferutil.node: LIBS := $(LIBS) +$(builddir)/bufferutil.node: TOOLSET := $(TOOLSET) +$(builddir)/bufferutil.node: $(OBJS) FORCE_DO_CMD + $(call do_cmd,solink_module) + +all_deps += $(builddir)/bufferutil.node +# Add target alias +.PHONY: bufferutil +bufferutil: $(builddir)/bufferutil.node + +# Short alias for building this executable. +.PHONY: bufferutil.node +bufferutil.node: $(builddir)/bufferutil.node + +# Add executable to "all" target. +.PHONY: all +all: $(builddir)/bufferutil.node + diff --git a/node_modules/ws/build/config.gypi b/node_modules/ws/build/config.gypi new file mode 100644 index 0000000..2d35968 --- /dev/null +++ b/node_modules/ws/build/config.gypi @@ -0,0 +1,102 @@ +# Do not edit. File was generated by node-gyp's "configure" step +{ + "target_defaults": { + "cflags": [], + "default_configuration": "Release", + "defines": [], + "include_dirs": [], + "libraries": [] + }, + "variables": { + "clang": 1, + "host_arch": "x64", + "node_install_npm": "true", + "node_install_waf": "true", + "node_prefix": "/usr/local/Cellar/node/0.8.12", + "node_shared_openssl": "false", + "node_shared_v8": "false", + "node_shared_zlib": "false", + "node_use_dtrace": "false", + "node_use_etw": "false", + "node_use_openssl": "true", + "target_arch": "x64", + "v8_no_strict_aliasing": 1, + "v8_use_snapshot": "true", + "nodedir": "/Users/jefklak/.node-gyp/0.8.12", + "copy_dev_lib": "true", + "save_dev": "", + "browser": "", + "viewer": "man", + "rollback": "true", + "usage": "", + "globalignorefile": "/usr/local/share/npm/etc/npmignore", + "init_author_url": "", + "shell": "/bin/bash", + "parseable": "", + "userignorefile": "/Users/jefklak/.npmignore", + "cache_max": "null", + "init_author_email": "", + "sign_git_tag": "", + "ignore": "", + "long": "", + "registry": "https://registry.npmjs.org/", + "fetch_retries": "2", + "npat": "", + "message": "%s", + "versions": "", + "globalconfig": "/usr/local/share/npm/etc/npmrc", + "always_auth": "", + "cache_lock_retries": "10", + "fetch_retry_mintimeout": "10000", + "proprietary_attribs": "true", + "coverage": "", + "json": "", + "pre": "", + "description": "true", + "engine_strict": "", + "https_proxy": "", + "init_module": "/Users/jefklak/.npm-init.js", + "userconfig": "/Users/jefklak/.npmrc", + "npaturl": "http://npat.npmjs.org/", + "node_version": "v0.8.12", + "user": "501", + "editor": "vi", + "save": "", + "tag": "latest", + "global": "", + "optional": "true", + "username": "", + "force": "", + "searchopts": "", + "depth": "null", + "rebuild_bundle": "true", + "searchsort": "name", + "unicode": "true", + "yes": "", + "fetch_retry_maxtimeout": "60000", + "strict_ssl": "true", + "dev": "", + "fetch_retry_factor": "10", + "group": "20", + "cache_lock_stale": "60000", + "version": "", + "cache_min": "", + "cache": "/Users/jefklak/.npm", + "searchexclude": "", + "color": "true", + "save_optional": "", + "user_agent": "node/v0.8.12", + "cache_lock_wait": "10000", + "production": "", + "save_bundle": "", + "init_version": "0.0.0", + "umask": "18", + "git": "git", + "init_author_name": "", + "onload_script": "", + "tmp": "/Users/jefklak/tmp", + "unsafe_perm": "", + "prefix": "/usr/local/share/npm", + "link": "" + } +} diff --git a/node_modules/ws/build/gyp-mac-tool b/node_modules/ws/build/gyp-mac-tool new file mode 100755 index 0000000..22f8331 --- /dev/null +++ b/node_modules/ws/build/gyp-mac-tool @@ -0,0 +1,210 @@ +#!/usr/bin/env python +# Generated by gyp. Do not edit. +# Copyright (c) 2012 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Utility functions to perform Xcode-style build steps. + +These functions are executed via gyp-mac-tool when using the Makefile generator. +""" + +import fcntl +import os +import plistlib +import re +import shutil +import string +import subprocess +import sys + + +def main(args): + executor = MacTool() + exit_code = executor.Dispatch(args) + if exit_code is not None: + sys.exit(exit_code) + + +class MacTool(object): + """This class performs all the Mac tooling steps. The methods can either be + executed directly, or dispatched from an argument list.""" + + def Dispatch(self, args): + """Dispatches a string command to a method.""" + if len(args) < 1: + raise Exception("Not enough arguments") + + method = "Exec%s" % self._CommandifyName(args[0]) + return getattr(self, method)(*args[1:]) + + def _CommandifyName(self, name_string): + """Transforms a tool name like copy-info-plist to CopyInfoPlist""" + return name_string.title().replace('-', '') + + def ExecCopyBundleResource(self, source, dest): + """Copies a resource file to the bundle/Resources directory, performing any + necessary compilation on each resource.""" + extension = os.path.splitext(source)[1].lower() + if os.path.isdir(source): + # Copy tree. + if os.path.exists(dest): + shutil.rmtree(dest) + shutil.copytree(source, dest) + elif extension == '.xib': + return self._CopyXIBFile(source, dest) + elif extension == '.strings': + self._CopyStringsFile(source, dest) + else: + shutil.copyfile(source, dest) + + def _CopyXIBFile(self, source, dest): + """Compiles a XIB file with ibtool into a binary plist in the bundle.""" + tools_dir = os.environ.get('DEVELOPER_BIN_DIR', '/usr/bin') + args = [os.path.join(tools_dir, 'ibtool'), '--errors', '--warnings', + '--notices', '--output-format', 'human-readable-text', '--compile', + dest, source] + ibtool_section_re = re.compile(r'/\*.*\*/') + ibtool_re = re.compile(r'.*note:.*is clipping its content') + ibtoolout = subprocess.Popen(args, stdout=subprocess.PIPE) + current_section_header = None + for line in ibtoolout.stdout: + if ibtool_section_re.match(line): + current_section_header = line + elif not ibtool_re.match(line): + if current_section_header: + sys.stdout.write(current_section_header) + current_section_header = None + sys.stdout.write(line) + return ibtoolout.returncode + + def _CopyStringsFile(self, source, dest): + """Copies a .strings file using iconv to reconvert the input into UTF-16.""" + input_code = self._DetectInputEncoding(source) or "UTF-8" + fp = open(dest, 'w') + args = ['/usr/bin/iconv', '--from-code', input_code, '--to-code', + 'UTF-16', source] + subprocess.call(args, stdout=fp) + fp.close() + + def _DetectInputEncoding(self, file_name): + """Reads the first few bytes from file_name and tries to guess the text + encoding. Returns None as a guess if it can't detect it.""" + fp = open(file_name, 'rb') + try: + header = fp.read(3) + except e: + fp.close() + return None + fp.close() + if header.startswith("\xFE\xFF"): + return "UTF-16BE" + elif header.startswith("\xFF\xFE"): + return "UTF-16LE" + elif header.startswith("\xEF\xBB\xBF"): + return "UTF-8" + else: + return None + + def ExecCopyInfoPlist(self, source, dest): + """Copies the |source| Info.plist to the destination directory |dest|.""" + # Read the source Info.plist into memory. + fd = open(source, 'r') + lines = fd.read() + fd.close() + + # Go through all the environment variables and replace them as variables in + # the file. + for key in os.environ: + if key.startswith('_'): + continue + evar = '${%s}' % key + lines = string.replace(lines, evar, os.environ[key]) + + # Write out the file with variables replaced. + fd = open(dest, 'w') + fd.write(lines) + fd.close() + + # Now write out PkgInfo file now that the Info.plist file has been + # "compiled". + self._WritePkgInfo(dest) + + def _WritePkgInfo(self, info_plist): + """This writes the PkgInfo file from the data stored in Info.plist.""" + plist = plistlib.readPlist(info_plist) + if not plist: + return + + # Only create PkgInfo for executable types. + package_type = plist['CFBundlePackageType'] + if package_type != 'APPL': + return + + # The format of PkgInfo is eight characters, representing the bundle type + # and bundle signature, each four characters. If that is missing, four + # '?' characters are used instead. + signature_code = plist.get('CFBundleSignature', '????') + if len(signature_code) != 4: # Wrong length resets everything, too. + signature_code = '?' * 4 + + dest = os.path.join(os.path.dirname(info_plist), 'PkgInfo') + fp = open(dest, 'w') + fp.write('%s%s' % (package_type, signature_code)) + fp.close() + + def ExecFlock(self, lockfile, *cmd_list): + """Emulates the most basic behavior of Linux's flock(1).""" + # Rely on exception handling to report errors. + fd = os.open(lockfile, os.O_RDONLY|os.O_NOCTTY|os.O_CREAT, 0o666) + fcntl.flock(fd, fcntl.LOCK_EX) + return subprocess.call(cmd_list) + + def ExecFilterLibtool(self, *cmd_list): + """Calls libtool and filters out 'libtool: file: foo.o has no symbols'.""" + libtool_re = re.compile(r'^libtool: file: .* has no symbols$') + libtoolout = subprocess.Popen(cmd_list, stderr=subprocess.PIPE) + for line in libtoolout.stderr: + if not libtool_re.match(line): + sys.stderr.write(line) + return libtoolout.returncode + + def ExecPackageFramework(self, framework, version): + """Takes a path to Something.framework and the Current version of that and + sets up all the symlinks.""" + # Find the name of the binary based on the part before the ".framework". + binary = os.path.basename(framework).split('.')[0] + + CURRENT = 'Current' + RESOURCES = 'Resources' + VERSIONS = 'Versions' + + if not os.path.exists(os.path.join(framework, VERSIONS, version, binary)): + # Binary-less frameworks don't seem to contain symlinks (see e.g. + # chromium's out/Debug/org.chromium.Chromium.manifest/ bundle). + return + + # Move into the framework directory to set the symlinks correctly. + pwd = os.getcwd() + os.chdir(framework) + + # Set up the Current version. + self._Relink(version, os.path.join(VERSIONS, CURRENT)) + + # Set up the root symlinks. + self._Relink(os.path.join(VERSIONS, CURRENT, binary), binary) + self._Relink(os.path.join(VERSIONS, CURRENT, RESOURCES), RESOURCES) + + # Back to where we were before! + os.chdir(pwd) + + def _Relink(self, dest, link): + """Creates a symlink to |dest| named |link|. If |link| already exists, + it is overwritten.""" + if os.path.lexists(link): + os.remove(link) + os.symlink(dest, link) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/node_modules/ws/build/validation.target.mk b/node_modules/ws/build/validation.target.mk new file mode 100644 index 0000000..8ba524d --- /dev/null +++ b/node_modules/ws/build/validation.target.mk @@ -0,0 +1,149 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := validation +DEFS_Debug := \ + '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-D_DARWIN_USE_64_BIT_INODE=1' \ + '-DDEBUG' \ + '-D_DEBUG' + +# Flags passed to all source files. +CFLAGS_Debug := \ + -Os \ + -gdwarf-2 \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -Wall \ + -Wendif-labels \ + -W \ + -Wno-unused-parameter + +# Flags passed to only C files. +CFLAGS_C_Debug := \ + -fno-strict-aliasing + +# Flags passed to only C++ files. +CFLAGS_CC_Debug := \ + -fno-rtti \ + -fno-exceptions \ + -fno-threadsafe-statics \ + -fno-strict-aliasing + +# Flags passed to only ObjC files. +CFLAGS_OBJC_Debug := + +# Flags passed to only ObjC++ files. +CFLAGS_OBJCC_Debug := + +INCS_Debug := \ + -I/Users/jefklak/.node-gyp/0.8.12/src \ + -I/Users/jefklak/.node-gyp/0.8.12/deps/uv/include \ + -I/Users/jefklak/.node-gyp/0.8.12/deps/v8/include \ + -I/Users/jefklak/development/privatews/webcam/node_modules/ws/node_modules/nan + +DEFS_Release := \ + '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-D_DARWIN_USE_64_BIT_INODE=1' + +# Flags passed to all source files. +CFLAGS_Release := \ + -Os \ + -gdwarf-2 \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -Wall \ + -Wendif-labels \ + -W \ + -Wno-unused-parameter + +# Flags passed to only C files. +CFLAGS_C_Release := \ + -fno-strict-aliasing + +# Flags passed to only C++ files. +CFLAGS_CC_Release := \ + -fno-rtti \ + -fno-exceptions \ + -fno-threadsafe-statics \ + -fno-strict-aliasing + +# Flags passed to only ObjC files. +CFLAGS_OBJC_Release := + +# Flags passed to only ObjC++ files. +CFLAGS_OBJCC_Release := + +INCS_Release := \ + -I/Users/jefklak/.node-gyp/0.8.12/src \ + -I/Users/jefklak/.node-gyp/0.8.12/deps/uv/include \ + -I/Users/jefklak/.node-gyp/0.8.12/deps/v8/include \ + -I/Users/jefklak/development/privatews/webcam/node_modules/ws/node_modules/nan + +OBJS := \ + $(obj).target/$(TARGET)/src/validation.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(CFLAGS_OBJC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(CFLAGS_OBJCC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Debug := \ + -Wl,-search_paths_first \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -L$(builddir) \ + -install_name /usr/local/lib/validation.node + +LDFLAGS_Release := \ + -Wl,-search_paths_first \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -L$(builddir) \ + -install_name /usr/local/lib/validation.node + +LIBS := \ + -undefined dynamic_lookup + +$(builddir)/validation.node: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/validation.node: LIBS := $(LIBS) +$(builddir)/validation.node: TOOLSET := $(TOOLSET) +$(builddir)/validation.node: $(OBJS) FORCE_DO_CMD + $(call do_cmd,solink_module) + +all_deps += $(builddir)/validation.node +# Add target alias +.PHONY: validation +validation: $(builddir)/validation.node + +# Short alias for building this executable. +.PHONY: validation.node +validation.node: $(builddir)/validation.node + +# Add executable to "all" target. +.PHONY: all +all: $(builddir)/validation.node + diff --git a/node_modules/ws/builderror.log b/node_modules/ws/builderror.log new file mode 100644 index 0000000..fc2464d --- /dev/null +++ b/node_modules/ws/builderror.log @@ -0,0 +1,2 @@ +gyp http GET http://nodejs.org/dist/v0.8.12/node-v0.8.12.tar.gz +gyp http 200 http://nodejs.org/dist/v0.8.12/node-v0.8.12.tar.gz diff --git a/node_modules/ws/doc/ws.md b/node_modules/ws/doc/ws.md new file mode 100644 index 0000000..d84fd62 --- /dev/null +++ b/node_modules/ws/doc/ws.md @@ -0,0 +1,181 @@ +# ws + +## Class: ws.Server + +This class is a WebSocket server. It is an `EventEmitter`. + +### new ws.Server([options], [callback]) + +* `options` Object + * `host` String + * `port` Number + * `server` http.Server + * `verifyClient` Function + * `path` String + * `noServer` Boolean + * `disableHixie` Boolean + * `clientTracking` Boolean +* `callback` Function + +Construct a new server object. + +Either `port` or `server` must be provided, otherwise you might enable +`noServer` if you want to pass the requests directly. Please note that the +`callback` is only used when you supply the a `port` number in the options. + +### server.close([code], [data]) + +Close the server and terminate all clients + +### server.handleUpgrade(request, socket, upgradeHead, callback) + +Handles a HTTP Upgrade request. `request` is an instance of `http.ServerRequest`, `socket` is an instance of `net.Socket`. + +When the Upgrade was successfully, the `callback` will be called with a `ws.WebSocket` object as parameter. + +### Event: 'error' + +`function (error) { }` + +If the underlying server emits an error, it will be forwarded here. + +### Event: 'headers' + +`function (headers) { }` + +Emitted with the object of HTTP headers that are going to be written to the `Stream` as part of the handshake. + +### Event: 'connection' + +`function (socket) { }` + +When a new WebSocket connection is established. `socket` is an object of type `ws.WebSocket`. + + +## Class: ws.WebSocket + +This class represents a WebSocket connection. It is an `EventEmitter`. + +### new ws.WebSocket(address, [options]) + +* `address` String|Array +* `options` Object + * `protocol` String + * `agent` Agent + * `headers` Object + * `protocolVersion` Number|String + -- the following only apply if `address` is a String + * `host` String + * `origin` String + * `pfx` String|Buffer + * `key` String|Buffer + * `passphrase` String + * `cert` String|Buffer + * `ca` Array + * `ciphers` String + * `rejectUnauthorized` Boolean + +Instantiating with an `address` creates a new WebSocket client object. If `address` is an Array (request, socket, rest), it is instantiated as a Server client (e.g. called from the `ws.Server`). + +### websocket.bytesReceived + +Received bytes count. + +### websocket.readyState + +Possible states are `WebSocket.CONNECTING`, `WebSocket.OPEN`, `WebSocket.CLOSING`, `WebSocket.CLOSED`. + +### websocket.protocolVersion + +The WebSocket protocol version used for this connection, `8`, `13` or `hixie-76` (the latter only for server clients). + +### websocket.url + +The URL of the WebSocket server (only for clients) + +### websocket.supports + +Describes the feature of the used protocol version. E.g. `supports.binary` is a boolean that describes if the connection supports binary messages. + +### websocket.close([code], [data]) + +Gracefully closes the connection, after sending a description message + +### websocket.pause() + +Pause the client stream + +### websocket.ping([data], [options], [dontFailWhenClosed]) + +Sends a ping. `data` is sent, `options` is an object with members `mask` and `binary`. `dontFailWhenClosed` indicates whether or not to throw if the connection isnt open. + +### websocket.pong([data], [options], [dontFailWhenClosed]) + +Sends a pong. `data` is sent, `options` is an object with members `mask` and `binary`. `dontFailWhenClosed` indicates whether or not to throw if the connection isnt open. + + +### websocket.resume() + +Resume the client stream + +### websocket.send(data, [options], [callback]) + +Sends `data` through the connection. `options` can be an object with members `mask` and `binary`. The optional `callback` is executed after the send completes. + +### websocket.stream([options], callback) + +Streams data through calls to a user supplied function. `options` can be an object with members `mask` and `binary`. `callback` is executed on successive ticks of which send is `function (data, final)`. + +### websocket.terminate() + +Immediately shuts down the connection + +### websocket.onopen +### websocket.onerror +### websocket.onclose +### websocket.onmessage + +Emulates the W3C Browser based WebSocket interface using function members. + +### websocket.addEventListener(method, listener) + +Emulates the W3C Browser based WebSocket interface using addEventListener. + +### Event: 'error' + +`function (error) { }` + +If the client emits an error, this event is emitted (errors from the underlying `net.Socket` are forwarded here). + +### Event: 'close' + +`function (code, message) { }` + +Is emitted when the connection is closed. `code` is defined in the WebSocket specification. + +The `close` event is also emitted when then underlying `net.Socket` closes the connection (`end` or `close`). + +### Event: 'message' + +`function (data, flags) { }` + +Is emitted when data is received. `flags` is an object with member `binary`. + +### Event: 'ping' + +`function (data, flags) { }` + +Is emitted when a ping is received. `flags` is an object with member `binary`. + +### Event: 'pong' + +`function (data, flags) { }` + +Is emitted when a pong is received. `flags` is an object with member `binary`. + +### Event: 'open' + +`function () { }` + +Emitted when the connection is established. + diff --git a/node_modules/ws/examples/fileapi/.npmignore b/node_modules/ws/examples/fileapi/.npmignore new file mode 100644 index 0000000..dcd5756 --- /dev/null +++ b/node_modules/ws/examples/fileapi/.npmignore @@ -0,0 +1 @@ +uploaded diff --git a/node_modules/ws/examples/fileapi/package.json b/node_modules/ws/examples/fileapi/package.json new file mode 100644 index 0000000..7816f27 --- /dev/null +++ b/node_modules/ws/examples/fileapi/package.json @@ -0,0 +1,18 @@ +{ + "author": "", + "name": "fileapi", + "version": "0.0.0", + "repository": { + "type": "git", + "url": "git://github.com/einaros/ws.git" + }, + "engines": { + "node": "~0.6.8" + }, + "dependencies": { + "express": "latest", + "ansi": "https://github.com/einaros/ansi.js/tarball/master" + }, + "devDependencies": {}, + "optionalDependencies": {} +} diff --git a/node_modules/ws/examples/fileapi/public/app.js b/node_modules/ws/examples/fileapi/public/app.js new file mode 100644 index 0000000..e812cc3 --- /dev/null +++ b/node_modules/ws/examples/fileapi/public/app.js @@ -0,0 +1,39 @@ +function onFilesSelected(e) { + var button = e.srcElement; + button.disabled = true; + var progress = document.querySelector('div#progress'); + progress.innerHTML = '0%'; + var files = e.target.files; + var totalFiles = files.length; + var filesSent = 0; + if (totalFiles) { + var uploader = new Uploader('ws://localhost:8080', function () { + Array.prototype.slice.call(files, 0).forEach(function(file) { + if (file.name == '.') { + --totalFiles; + return; + } + uploader.sendFile(file, function(error) { + if (error) { + console.log(error); + return; + } + ++filesSent; + progress.innerHTML = ~~(filesSent / totalFiles * 100) + '%'; + console.log('Sent: ' + file.name); + }); + }); + }); + } + uploader.ondone = function() { + uploader.close(); + progress.innerHTML = '100% done, ' + totalFiles + ' files sent.'; + } +} + +window.onload = function() { + var importButtons = document.querySelectorAll('[type="file"]'); + Array.prototype.slice.call(importButtons, 0).forEach(function(importButton) { + importButton.addEventListener('change', onFilesSelected, false); + }); +} diff --git a/node_modules/ws/examples/fileapi/public/index.html b/node_modules/ws/examples/fileapi/public/index.html new file mode 100644 index 0000000..0d463dd --- /dev/null +++ b/node_modules/ws/examples/fileapi/public/index.html @@ -0,0 +1,22 @@ + + + + + + + + +

This example will upload an entire directory tree to the node.js server via a fast and persistent WebSocket connection.

+

Note that the example is Chrome only for now.

+

+ Upload status: +
Please select a directory to upload.
+ + diff --git a/node_modules/ws/examples/fileapi/public/uploader.js b/node_modules/ws/examples/fileapi/public/uploader.js new file mode 100644 index 0000000..0c34a7f --- /dev/null +++ b/node_modules/ws/examples/fileapi/public/uploader.js @@ -0,0 +1,55 @@ +function Uploader(url, cb) { + this.ws = new WebSocket(url); + if (cb) this.ws.onopen = cb; + this.sendQueue = []; + this.sending = null; + this.sendCallback = null; + this.ondone = null; + var self = this; + this.ws.onmessage = function(event) { + var data = JSON.parse(event.data); + if (data.event == 'complete') { + if (data.path != self.sending.path) { + self.sendQueue = []; + self.sending = null; + self.sendCallback = null; + throw new Error('Got message for wrong file!'); + } + self.sending = null; + var callback = self.sendCallback; + self.sendCallback = null; + if (callback) callback(); + if (self.sendQueue.length === 0 && self.ondone) self.ondone(null); + if (self.sendQueue.length > 0) { + var args = self.sendQueue.pop(); + setTimeout(function() { self.sendFile.apply(self, args); }, 0); + } + } + else if (data.event == 'error') { + self.sendQueue = []; + self.sending = null; + var callback = self.sendCallback; + self.sendCallback = null; + var error = new Error('Server reported send error for file ' + data.path); + if (callback) callback(error); + if (self.ondone) self.ondone(error); + } + } +} + +Uploader.prototype.sendFile = function(file, cb) { + if (this.ws.readyState != WebSocket.OPEN) throw new Error('Not connected'); + if (this.sending) { + this.sendQueue.push(arguments); + return; + } + var fileData = { name: file.name, path: file.webkitRelativePath }; + this.sending = fileData; + this.sendCallback = cb; + this.ws.send(JSON.stringify(fileData)); + this.ws.send(file); +} + +Uploader.prototype.close = function() { + this.ws.close(); +} diff --git a/node_modules/ws/examples/fileapi/server.js b/node_modules/ws/examples/fileapi/server.js new file mode 100644 index 0000000..badfeba --- /dev/null +++ b/node_modules/ws/examples/fileapi/server.js @@ -0,0 +1,103 @@ +var WebSocketServer = require('../../').Server + , express = require('express') + , fs = require('fs') + , http = require('http') + , util = require('util') + , path = require('path') + , app = express.createServer() + , events = require('events') + , ansi = require('ansi') + , cursor = ansi(process.stdout); + +function BandwidthSampler(ws, interval) { + interval = interval || 2000; + var previousByteCount = 0; + var self = this; + var intervalId = setInterval(function() { + var byteCount = ws.bytesReceived; + var bytesPerSec = (byteCount - previousByteCount) / (interval / 1000); + previousByteCount = byteCount; + self.emit('sample', bytesPerSec); + }, interval); + ws.on('close', function() { + clearInterval(intervalId); + }); +} +util.inherits(BandwidthSampler, events.EventEmitter); + +function makePathForFile(filePath, prefix, cb) { + if (typeof cb !== 'function') throw new Error('callback is required'); + filePath = path.dirname(path.normalize(filePath)).replace(/^(\/|\\)+/, ''); + var pieces = filePath.split(/(\\|\/)/); + var incrementalPath = prefix; + function step(error) { + if (error) return cb(error); + if (pieces.length == 0) return cb(null, incrementalPath); + incrementalPath += '/' + pieces.shift(); + fs.exists(incrementalPath, function(exists) { + if (!exists) fs.mkdir(incrementalPath, step); + else process.nextTick(step); + }); + } + step(); +} + +cursor.eraseData(2).goto(1, 1); +app.use(express.static(__dirname + '/public')); + +var clientId = 0; +var wss = new WebSocketServer({server: app}); +wss.on('connection', function(ws) { + var thisId = ++clientId; + cursor.goto(1, 4 + thisId).eraseLine(); + console.log('Client #%d connected', thisId); + + var sampler = new BandwidthSampler(ws); + sampler.on('sample', function(bps) { + cursor.goto(1, 4 + thisId).eraseLine(); + console.log('WebSocket #%d incoming bandwidth: %d MB/s', thisId, Math.round(bps / (1024*1024))); + }); + + var filesReceived = 0; + var currentFile = null; + ws.on('message', function(data, flags) { + if (!flags.binary) { + currentFile = JSON.parse(data); + // note: a real-world app would want to sanity check the data + } + else { + if (currentFile == null) return; + makePathForFile(currentFile.path, __dirname + '/uploaded', function(error, path) { + if (error) { + console.log(error); + ws.send(JSON.stringify({event: 'error', path: currentFile.path, message: error.message})); + return; + } + fs.writeFile(path + '/' + currentFile.name, data, function(error) { + ++filesReceived; + // console.log('received %d bytes long file, %s', data.length, currentFile.path); + ws.send(JSON.stringify({event: 'complete', path: currentFile.path})); + currentFile = null; + }); + }); + } + }); + + ws.on('close', function() { + cursor.goto(1, 4 + thisId).eraseLine(); + console.log('Client #%d disconnected. %d files received.', thisId, filesReceived); + }); + + ws.on('error', function(e) { + cursor.goto(1, 4 + thisId).eraseLine(); + console.log('Client #%d error: %s', thisId, e.message); + }); +}); + +fs.mkdir(__dirname + '/uploaded', function(error) { + // ignore errors, most likely means directory exists + console.log('Uploaded files will be saved to %s/uploaded.', __dirname); + console.log('Remember to wipe this directory if you upload lots and lots.'); + app.listen(8080); + console.log('Listening on http://localhost:8080'); +}); diff --git a/node_modules/ws/examples/serverstats-express_3/package.json b/node_modules/ws/examples/serverstats-express_3/package.json new file mode 100644 index 0000000..99722c4 --- /dev/null +++ b/node_modules/ws/examples/serverstats-express_3/package.json @@ -0,0 +1,17 @@ +{ + "author": "", + "name": "serverstats", + "version": "0.0.0", + "repository": { + "type": "git", + "url": "git://github.com/einaros/ws.git" + }, + "engines": { + "node": ">0.4.0" + }, + "dependencies": { + "express": "~3.0.0" + }, + "devDependencies": {}, + "optionalDependencies": {} +} diff --git a/node_modules/ws/examples/serverstats-express_3/public/index.html b/node_modules/ws/examples/serverstats-express_3/public/index.html new file mode 100644 index 0000000..24d84e1 --- /dev/null +++ b/node_modules/ws/examples/serverstats-express_3/public/index.html @@ -0,0 +1,33 @@ + + + + + + + + Server Stats
+ RSS:

+ Heap total:

+ Heap used:

+ + diff --git a/node_modules/ws/examples/serverstats-express_3/server.js b/node_modules/ws/examples/serverstats-express_3/server.js new file mode 100644 index 0000000..88bbc9e --- /dev/null +++ b/node_modules/ws/examples/serverstats-express_3/server.js @@ -0,0 +1,21 @@ +var WebSocketServer = require('../../').Server + , http = require('http') + , express = require('express') + , app = express(); + +app.use(express.static(__dirname + '/public')); + +var server = http.createServer(app); +server.listen(8080); + +var wss = new WebSocketServer({server: server}); +wss.on('connection', function(ws) { + var id = setInterval(function() { + ws.send(JSON.stringify(process.memoryUsage()), function() { /* ignore errors */ }); + }, 100); + console.log('started client interval'); + ws.on('close', function() { + console.log('stopping client interval'); + clearInterval(id); + }); +}); diff --git a/node_modules/ws/examples/serverstats/package.json b/node_modules/ws/examples/serverstats/package.json new file mode 100644 index 0000000..65c900a --- /dev/null +++ b/node_modules/ws/examples/serverstats/package.json @@ -0,0 +1,17 @@ +{ + "author": "", + "name": "serverstats", + "version": "0.0.0", + "repository": { + "type": "git", + "url": "git://github.com/einaros/ws.git" + }, + "engines": { + "node": ">0.4.0" + }, + "dependencies": { + "express": "2.x" + }, + "devDependencies": {}, + "optionalDependencies": {} +} diff --git a/node_modules/ws/examples/serverstats/public/index.html b/node_modules/ws/examples/serverstats/public/index.html new file mode 100644 index 0000000..24d84e1 --- /dev/null +++ b/node_modules/ws/examples/serverstats/public/index.html @@ -0,0 +1,33 @@ + + + + + + + + Server Stats
+ RSS:

+ Heap total:

+ Heap used:

+ + diff --git a/node_modules/ws/examples/serverstats/server.js b/node_modules/ws/examples/serverstats/server.js new file mode 100644 index 0000000..0bbce36 --- /dev/null +++ b/node_modules/ws/examples/serverstats/server.js @@ -0,0 +1,19 @@ +var WebSocketServer = require('../../').Server + , http = require('http') + , express = require('express') + , app = express.createServer(); + +app.use(express.static(__dirname + '/public')); +app.listen(8080); + +var wss = new WebSocketServer({server: app}); +wss.on('connection', function(ws) { + var id = setInterval(function() { + ws.send(JSON.stringify(process.memoryUsage()), function() { /* ignore errors */ }); + }, 100); + console.log('started client interval'); + ws.on('close', function() { + console.log('stopping client interval'); + clearInterval(id); + }) +}); diff --git a/node_modules/ws/examples/ssl.js b/node_modules/ws/examples/ssl.js new file mode 100644 index 0000000..bf1bf53 --- /dev/null +++ b/node_modules/ws/examples/ssl.js @@ -0,0 +1,59 @@ + +(function(){ + + "use strict"; + + var fs = require('fs'); + + // you'll probably load configuration from config + var cfg = { + ssl: true, + port: 8080, + ssl_key: '/path/to/you/ssl.key', + ssl_cert: '/path/to/you/ssl.crt' + }; + + var httpServ = ( cfg.ssl ) ? require('https') : require('http'); + + var WebSocketServer = require('../').Server; + + var app = null; + + // dummy request processing + var processRequest = function( req, res ) { + + res.writeHead(200); + res.end("All glory to WebSockets!\n"); + }; + + if ( cfg.ssl ) { + + app = httpServ.createServer({ + + // providing server with SSL key/cert + key: fs.readFileSync( cfg.ssl_key ), + cert: fs.readFileSync( cfg.ssl_cert ) + + }, processRequest ).listen( cfg.port ); + + } else { + + app = httpServ.createServer( processRequest ).listen( cfg.port ); + } + + // passing or reference to web server so WS would knew port and SSL capabilities + var wss = new WebSocketServer( { server: app } ); + + + wss.on( 'connection', function ( wsConnect ) { + + wsConnect.on( 'message', function ( message ) { + + console.log( message ); + + }); + + }); + + +}()); \ No newline at end of file diff --git a/node_modules/ws/index.js b/node_modules/ws/index.js new file mode 100644 index 0000000..3423ff2 --- /dev/null +++ b/node_modules/ws/index.js @@ -0,0 +1,26 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +module.exports = require('./lib/WebSocket'); +module.exports.Server = require('./lib/WebSocketServer'); +module.exports.Sender = require('./lib/Sender'); +module.exports.Receiver = require('./lib/Receiver'); + +module.exports.createServer = function (options, connectionListener) { + var server = new module.exports.Server(options); + if (typeof connectionListener === 'function') { + server.on('connection', connectionListener); + } + return server; +}; + +module.exports.connect = module.exports.createConnection = function (address, openListener) { + var client = new module.exports(address); + if (typeof openListener === 'function') { + client.on('open', openListener); + } + return client; +}; diff --git a/node_modules/ws/lib/BufferPool.js b/node_modules/ws/lib/BufferPool.js new file mode 100644 index 0000000..faf8637 --- /dev/null +++ b/node_modules/ws/lib/BufferPool.js @@ -0,0 +1,59 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var util = require('util'); + +function BufferPool(initialSize, growStrategy, shrinkStrategy) { + if (typeof initialSize === 'function') { + shrinkStrategy = growStrategy; + growStrategy = initialSize; + initialSize = 0; + } + else if (typeof initialSize === 'undefined') { + initialSize = 0; + } + this._growStrategy = (growStrategy || function(db, size) { + return db.used + size; + }).bind(null, this); + this._shrinkStrategy = (shrinkStrategy || function(db) { + return initialSize; + }).bind(null, this); + this._buffer = initialSize ? new Buffer(initialSize) : null; + this._offset = 0; + this._used = 0; + this._changeFactor = 0; + this.__defineGetter__('size', function(){ + return this._buffer == null ? 0 : this._buffer.length; + }); + this.__defineGetter__('used', function(){ + return this._used; + }); +} + +BufferPool.prototype.get = function(length) { + if (this._buffer == null || this._offset + length > this._buffer.length) { + var newBuf = new Buffer(this._growStrategy(length)); + this._buffer = newBuf; + this._offset = 0; + } + this._used += length; + var buf = this._buffer.slice(this._offset, this._offset + length); + this._offset += length; + return buf; +} + +BufferPool.prototype.reset = function(forceNewBuffer) { + var len = this._shrinkStrategy(); + if (len < this.size) this._changeFactor -= 1; + if (forceNewBuffer || this._changeFactor < -2) { + this._changeFactor = 0; + this._buffer = len ? new Buffer(len) : null; + } + this._offset = 0; + this._used = 0; +} + +module.exports = BufferPool; diff --git a/node_modules/ws/lib/BufferUtil.fallback.js b/node_modules/ws/lib/BufferUtil.fallback.js new file mode 100644 index 0000000..508542c --- /dev/null +++ b/node_modules/ws/lib/BufferUtil.fallback.js @@ -0,0 +1,47 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +module.exports.BufferUtil = { + merge: function(mergedBuffer, buffers) { + var offset = 0; + for (var i = 0, l = buffers.length; i < l; ++i) { + var buf = buffers[i]; + buf.copy(mergedBuffer, offset); + offset += buf.length; + } + }, + mask: function(source, mask, output, offset, length) { + var maskNum = mask.readUInt32LE(0, true); + var i = 0; + for (; i < length - 3; i += 4) { + var num = maskNum ^ source.readUInt32LE(i, true); + if (num < 0) num = 4294967296 + num; + output.writeUInt32LE(num, offset + i, true); + } + switch (length % 4) { + case 3: output[offset + i + 2] = source[i + 2] ^ mask[2]; + case 2: output[offset + i + 1] = source[i + 1] ^ mask[1]; + case 1: output[offset + i] = source[i] ^ mask[0]; + case 0:; + } + }, + unmask: function(data, mask) { + var maskNum = mask.readUInt32LE(0, true); + var length = data.length; + var i = 0; + for (; i < length - 3; i += 4) { + var num = maskNum ^ data.readUInt32LE(i, true); + if (num < 0) num = 4294967296 + num; + data.writeUInt32LE(num, i, true); + } + switch (length % 4) { + case 3: data[i + 2] = data[i + 2] ^ mask[2]; + case 2: data[i + 1] = data[i + 1] ^ mask[1]; + case 1: data[i] = data[i] ^ mask[0]; + case 0:; + } + } +} diff --git a/node_modules/ws/lib/BufferUtil.js b/node_modules/ws/lib/BufferUtil.js new file mode 100644 index 0000000..15d35b9 --- /dev/null +++ b/node_modules/ws/lib/BufferUtil.js @@ -0,0 +1,16 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +try { + module.exports = require('../build/Release/bufferutil'); +} catch (e) { try { + module.exports = require('../build/default/bufferutil'); +} catch (e) { try { + module.exports = require('./BufferUtil.fallback'); +} catch (e) { + console.error('bufferutil.node seems to not have been built. Run npm install.'); + throw e; +}}} diff --git a/node_modules/ws/lib/ErrorCodes.js b/node_modules/ws/lib/ErrorCodes.js new file mode 100644 index 0000000..55ebd52 --- /dev/null +++ b/node_modules/ws/lib/ErrorCodes.js @@ -0,0 +1,24 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +module.exports = { + isValidErrorCode: function(code) { + return (code >= 1000 && code <= 1011 && code != 1004 && code != 1005 && code != 1006) || + (code >= 3000 && code <= 4999); + }, + 1000: 'normal', + 1001: 'going away', + 1002: 'protocol error', + 1003: 'unsupported data', + 1004: 'reserved', + 1005: 'reserved for extensions', + 1006: 'reserved for extensions', + 1007: 'inconsistent or invalid data', + 1008: 'policy violation', + 1009: 'message too big', + 1010: 'extension handshake missing', + 1011: 'an unexpected condition prevented the request from being fulfilled', +}; \ No newline at end of file diff --git a/node_modules/ws/lib/Receiver.hixie.js b/node_modules/ws/lib/Receiver.hixie.js new file mode 100644 index 0000000..f54ad96 --- /dev/null +++ b/node_modules/ws/lib/Receiver.hixie.js @@ -0,0 +1,180 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var util = require('util'); + +/** + * State constants + */ + +var EMPTY = 0 + , BODY = 1; +var BINARYLENGTH = 2 + , BINARYBODY = 3; + +/** + * Hixie Receiver implementation + */ + +function Receiver () { + this.state = EMPTY; + this.buffers = []; + this.messageEnd = -1; + this.spanLength = 0; + this.dead = 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) { + var self = this; + function doAdd() { + if (self.state === EMPTY) { + if (data.length == 2 && data[0] == 0xFF && data[1] == 0x00) { + self.reset(); + self.onclose(); + return; + } + if (data[0] === 0x80) { + self.messageEnd = 0; + self.state = BINARYLENGTH; + data = data.slice(1); + } else { + + if (data[0] !== 0x00) { + self.error('payload must start with 0x00 byte', true); + return; + } + data = data.slice(1); + self.state = BODY; + + } + } + if (self.state === BINARYLENGTH) { + var i = 0; + while ((i < data.length) && (data[i] & 0x80)) { + self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f); + ++i; + } + if (i < data.length) { + self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f); + self.state = BINARYBODY; + ++i; + } + if (i > 0) + data = data.slice(i); + } + if (self.state === BINARYBODY) { + var dataleft = self.messageEnd - self.spanLength; + if (data.length >= dataleft) { + // consume the whole buffer to finish the frame + self.buffers.push(data); + self.spanLength += dataleft; + self.messageEnd = dataleft; + return self.parse(); + } + // frame's not done even if we consume it all + self.buffers.push(data); + self.spanLength += data.length; + return; + } + self.buffers.push(data); + if ((self.messageEnd = bufferIndex(data, 0xFF)) != -1) { + self.spanLength += self.messageEnd; + return self.parse(); + } + else self.spanLength += data.length; + } + while(data) data = doAdd(); +} + +/** + * Releases all resources used by the receiver. + * + * @api public + */ + +Receiver.prototype.cleanup = function() { + this.dead = true; + this.state = EMPTY; + this.buffers = []; +} + +/** + * Process buffered data. + * + * @api public + */ + +Receiver.prototype.parse = function() { + var output = new Buffer(this.spanLength); + var outputIndex = 0; + for (var bi = 0, bl = this.buffers.length; bi < bl - 1; ++bi) { + var buffer = this.buffers[bi]; + buffer.copy(output, outputIndex); + outputIndex += buffer.length; + } + var lastBuffer = this.buffers[this.buffers.length - 1]; + if (this.messageEnd > 0) lastBuffer.copy(output, outputIndex, 0, this.messageEnd); + if (this.state !== BODY) --this.messageEnd; + var tail = null; + if (this.messageEnd < lastBuffer.length - 1) { + tail = lastBuffer.slice(this.messageEnd + 1); + } + this.reset(); + this.ontext(output.toString('utf8')); + return tail; +} + +/** + * Handles an error + * + * @api private + */ + +Receiver.prototype.error = function (reason, terminate) { + this.reset(); + this.onerror(reason, terminate); + return this; +} + +/** + * Reset parser state + * + * @api private + */ + +Receiver.prototype.reset = function (reason) { + if (this.dead) return; + this.state = EMPTY; + this.buffers = []; + this.messageEnd = -1; + this.spanLength = 0; +} + +/** + * Internal api + */ + +function bufferIndex(buffer, byte) { + for (var i = 0, l = buffer.length; i < l; ++i) { + if (buffer[i] === byte) return i; + } + return -1; +} diff --git a/node_modules/ws/lib/Receiver.js b/node_modules/ws/lib/Receiver.js new file mode 100644 index 0000000..2752726 --- /dev/null +++ b/node_modules/ws/lib/Receiver.js @@ -0,0 +1,591 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var util = require('util') + , Validation = require('./Validation').Validation + , ErrorCodes = require('./ErrorCodes') + , BufferPool = require('./BufferPool') + , bufferUtil = require('./BufferUtil').BufferUtil; + +/** + * Node version 0.4 and 0.6 compatibility + */ + +var isNodeV4 = /^v0\.4/.test(process.version); + +/** + * HyBi Receiver implementation + */ + +function Receiver () { + // 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 ? + (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 ? + (unfragmentedPoolPrevUsed + db.used) / 2 : + db.used; + }); + + 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.expectHeader(2, this.processPacket); + this.dead = 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) { + 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 = !isNodeV4 + ? function(length, isFragmented) { return (isFragmented ? this.fragmentedBufferPool : this.unfragmentedBufferPool).get(length); } + : function(length) { return new Buffer(length); }; + +/** + * Start processing a new packet. + * + * @api private + */ + +Receiver.prototype.processPacket = function (data) { + 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 opcode = data[0] & 0xf; + if (opcode === 0) { + // 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; + } + 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.state.fragmentedOperation) this.unfragmentedBufferPool.reset(true); + else if (this.state.lastFragment) this.fragmentedBufferPool.reset(false); + 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.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 = []; +} + +/** + * 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') : ''; +} + +/** + * 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 + * + * @api private + */ + +Receiver.prototype.error = function (reason, protocolErrorCode) { + this.reset(); + this.onerror(reason, protocolErrorCode); + return this; +} + +/** + * 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]; + } +} + +/** + * Opcode handlers + */ + +var opcodes = { + // text + '1': { + start: function(data) { + var self = this; + // decode length + var firstLength = data[1] & 0x7f; + if (firstLength < 126) { + opcodes['1'].getData.call(self, firstLength); + } + else if (firstLength == 126) { + self.expectHeader(2, function(data) { + opcodes['1'].getData.call(self, readUInt16BE.call(data, 0)); + }); + } + 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; + } + 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 packet = this.unmask(mask, data, true); + if (packet != null) this.currentMessage.push(packet); + if (this.state.lastFragment) { + var messageBuffer = this.concatBuffers(this.currentMessage); + if (!Validation.isValidUTF8(messageBuffer)) { + this.error('invalid utf8 sequence', 1007); + return; + } + this.ontext(messageBuffer.toString('utf8'), {masked: this.state.masked, buffer: messageBuffer}); + this.currentMessage = []; + } + this.endPacket(); + } + }, + // binary + '2': { + start: function(data) { + var self = this; + // decode length + var firstLength = data[1] & 0x7f; + if (firstLength < 126) { + opcodes['2'].getData.call(self, firstLength); + } + else if (firstLength == 126) { + self.expectHeader(2, function(data) { + opcodes['2'].getData.call(self, readUInt16BE.call(data, 0)); + }); + } + 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; + } + opcodes['2'].getData.call(self, readUInt32BE.call(data, 4, true)); + }); + } + }, + 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 packet = this.unmask(mask, data, true); + if (packet != null) this.currentMessage.push(packet); + if (this.state.lastFragment) { + var messageBuffer = this.concatBuffers(this.currentMessage); + this.onbinary(messageBuffer, {masked: this.state.masked, buffer: messageBuffer}); + this.currentMessage = []; + } + 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); + 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'); + } + this.onclose(code, message, {masked: self.state.masked}); + this.reset(); + }, + }, + // 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) { + this.onping(this.unmask(mask, data, true), {masked: this.state.masked, binary: true}); + 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) { + this.onpong(this.unmask(mask, data, true), {masked: this.state.masked, binary: true}); + this.endPacket(); + } + } +} diff --git a/node_modules/ws/lib/Sender.hixie.js b/node_modules/ws/lib/Sender.hixie.js new file mode 100644 index 0000000..1754afb --- /dev/null +++ b/node_modules/ws/lib/Sender.hixie.js @@ -0,0 +1,123 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var events = require('events') + , util = require('util') + , EventEmitter = events.EventEmitter; + +/** + * Hixie Sender implementation + */ + +function Sender(socket) { + this.socket = socket; + this.continuationFrame = false; + this.isClosed = false; +} + +module.exports = Sender; + +/** + * Inherits from EventEmitter. + */ + +util.inherits(Sender, events.EventEmitter); + +/** + * Frames and writes data. + * + * @api public + */ + +Sender.prototype.send = function(data, options, cb) { + if (this.isClosed) return; +/* + if (options && options.binary) { + this.error('hixie websockets do not support binary'); + return; + } +*/ + var isString = typeof data == 'string' + , length = isString ? Buffer.byteLength(data) : data.length + , lengthbytes = (length > 127) ? 2 : 1 // assume less than 2**14 bytes + , writeStartMarker = this.continuationFrame == false + , writeEndMarker = !options || !(typeof options.fin != 'undefined' && !options.fin) + , buffer = new Buffer((writeStartMarker ? ((options && options.binary) ? (1 + lengthbytes) : 1) : 0) + length + ((writeEndMarker && !(options && options.binary)) ? 1 : 0)) + , offset = writeStartMarker ? 1 : 0; + + if (writeStartMarker) { + if (options && options.binary) { + buffer.write('\x80', 'binary'); + // assume length less than 2**14 bytes + if (lengthbytes > 1) + buffer.write(String.fromCharCode(128+length/128), offset++, 'binary'); + buffer.write(String.fromCharCode(length&0x7f), offset++, 'binary'); + } else + buffer.write('\x00', 'binary'); + } + + if (isString) buffer.write(data, offset, 'utf8'); + else data.copy(buffer, offset, 0); + + if (writeEndMarker) { + if (options && options.binary) { + // sending binary, not writing end marker + } else + buffer.write('\xff', offset + length, 'binary'); + this.continuationFrame = false; + } + else this.continuationFrame = true; + + try { + this.socket.write(buffer, 'binary', cb); + } catch (e) { + this.error(e.toString()); + } +} + +/** + * Sends a close instruction to the remote party. + * + * @api public + */ + +Sender.prototype.close = function(code, data, mask, cb) { + if (this.isClosed) return; + this.isClosed = true; + try { + if (this.continuationFrame) this.socket.write(new Buffer([0xff], 'binary')); + this.socket.write(new Buffer([0xff, 0x00]), 'binary', cb); + } catch (e) { + this.error(e.toString()); + } +} + +/** + * Sends a ping message to the remote party. Not available for hixie. + * + * @api public + */ + +Sender.prototype.ping = function(data, options) {} + +/** + * Sends a pong message to the remote party. Not available for hixie. + * + * @api public + */ + +Sender.prototype.pong = function(data, options) {} + +/** + * Handles an error + * + * @api private + */ + +Sender.prototype.error = function (reason) { + this.emit('error', reason); + return this; +} diff --git a/node_modules/ws/lib/Sender.js b/node_modules/ws/lib/Sender.js new file mode 100644 index 0000000..fc3b437 --- /dev/null +++ b/node_modules/ws/lib/Sender.js @@ -0,0 +1,227 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var events = require('events') + , util = require('util') + , EventEmitter = events.EventEmitter + , ErrorCodes = require('./ErrorCodes') + , bufferUtil = require('./BufferUtil').BufferUtil; + +/** + * HyBi Sender implementation + */ + +function Sender(socket) { + this._socket = socket; + this.firstFragment = true; +} + +/** + * Inherits from EventEmitter. + */ + +util.inherits(Sender, events.EventEmitter); + +/** + * Sends a close instruction to the remote party. + * + * @api public + */ + +Sender.prototype.close = function(code, data, mask) { + if (typeof code !== 'undefined') { + if (typeof code !== 'number' || + !ErrorCodes.isValidErrorCode(code)) throw new Error('first argument must be a valid error code number'); + } + code = code || 1000; + var dataBuffer = new Buffer(2 + (data ? Buffer.byteLength(data) : 0)); + writeUInt16BE.call(dataBuffer, code, 0); + if (dataBuffer.length > 2) dataBuffer.write(data, 2); + this.frameAndSend(0x8, dataBuffer, true, mask); +} + +/** + * Sends a ping message to the remote party. + * + * @api public + */ + +Sender.prototype.ping = function(data, options) { + var mask = options && options.mask; + this.frameAndSend(0x9, data || '', true, mask); +} + +/** + * Sends a pong message to the remote party. + * + * @api public + */ + +Sender.prototype.pong = function(data, options) { + var mask = options && options.mask; + this.frameAndSend(0xa, data || '', true, mask); +} + +/** + * Sends text or binary data to the remote party. + * + * @api public + */ + +Sender.prototype.send = function(data, options, cb) { + var finalFragment = options && options.fin === false ? false : true; + var mask = options && options.mask; + var opcode = options && options.binary ? 2 : 1; + if (this.firstFragment === false) opcode = 0; + else this.firstFragment = false; + if (finalFragment) this.firstFragment = true + this.frameAndSend(opcode, data, finalFragment, mask, cb); +} + +/** + * Frames and sends a piece of data according to the HyBi WebSocket protocol. + * + * @api private + */ + +Sender.prototype.frameAndSend = function(opcode, data, finalFragment, maskData, cb) { + var canModifyData = false; + + if (!data) { + try { + this._socket.write(new Buffer([opcode | (finalFragment ? 0x80 : 0), 0 | (maskData ? 0x80 : 0)].concat(maskData ? [0, 0, 0, 0] : [])), 'binary', cb); + } + catch (e) { + if (typeof cb == 'function') cb(e); + else this.emit('error', e); + } + return; + } + + if (!Buffer.isBuffer(data)) { + canModifyData = true; + if (data && (typeof data.byteLength !== 'undefined' || typeof data.buffer !== 'undefined')) { + data = getArrayBuffer(data); + } else { + data = new Buffer(data); + } + } + + var dataLength = data.length + , dataOffset = maskData ? 6 : 2 + , secondByte = dataLength; + + if (dataLength >= 65536) { + dataOffset += 8; + secondByte = 127; + } + else if (dataLength > 125) { + dataOffset += 2; + secondByte = 126; + } + + var mergeBuffers = dataLength < 32768 || (maskData && !canModifyData); + var totalLength = mergeBuffers ? dataLength + dataOffset : dataOffset; + var outputBuffer = new Buffer(totalLength); + outputBuffer[0] = finalFragment ? opcode | 0x80 : opcode; + + switch (secondByte) { + case 126: + writeUInt16BE.call(outputBuffer, dataLength, 2); + break; + case 127: + writeUInt32BE.call(outputBuffer, 0, 2); + writeUInt32BE.call(outputBuffer, dataLength, 6); + } + + if (maskData) { + outputBuffer[1] = secondByte | 0x80; + var mask = this._randomMask || (this._randomMask = getRandomMask()); + outputBuffer[dataOffset - 4] = mask[0]; + outputBuffer[dataOffset - 3] = mask[1]; + outputBuffer[dataOffset - 2] = mask[2]; + outputBuffer[dataOffset - 1] = mask[3]; + if (mergeBuffers) { + bufferUtil.mask(data, mask, outputBuffer, dataOffset, dataLength); + try { + this._socket.write(outputBuffer, 'binary', cb); + } + catch (e) { + if (typeof cb == 'function') cb(e); + else this.emit('error', e); + } + } + else { + bufferUtil.mask(data, mask, data, 0, dataLength); + try { + this._socket.write(outputBuffer, 'binary'); + this._socket.write(data, 'binary', cb); + } + catch (e) { + if (typeof cb == 'function') cb(e); + else this.emit('error', e); + } + } + } + else { + outputBuffer[1] = secondByte; + if (mergeBuffers) { + data.copy(outputBuffer, dataOffset); + try { + this._socket.write(outputBuffer, 'binary', cb); + } + catch (e) { + if (typeof cb == 'function') cb(e); + else this.emit('error', e); + } + } + else { + try { + this._socket.write(outputBuffer, 'binary'); + this._socket.write(data, 'binary', cb); + } + catch (e) { + if (typeof cb == 'function') cb(e); + else this.emit('error', e); + } + } + } +} + +module.exports = Sender; + +function writeUInt16BE(value, offset) { + this[offset] = (value & 0xff00)>>8; + this[offset+1] = value & 0xff; +} + +function writeUInt32BE(value, offset) { + this[offset] = (value & 0xff000000)>>24; + this[offset+1] = (value & 0xff0000)>>16; + this[offset+2] = (value & 0xff00)>>8; + this[offset+3] = value & 0xff; +} + +function getArrayBuffer(data) { + // data is either an ArrayBuffer or ArrayBufferView. + var array = new Uint8Array(data.buffer || data) + , l = data.byteLength || data.length + , o = data.byteOffset || 0 + , buffer = new Buffer(l); + for (var i = 0; i < l; ++i) { + buffer[i] = array[o+i]; + } + return buffer; +} + +function getRandomMask() { + return new Buffer([ + ~~(Math.random() * 255), + ~~(Math.random() * 255), + ~~(Math.random() * 255), + ~~(Math.random() * 255) + ]); +} diff --git a/node_modules/ws/lib/Validation.fallback.js b/node_modules/ws/lib/Validation.fallback.js new file mode 100644 index 0000000..2c7c4fd --- /dev/null +++ b/node_modules/ws/lib/Validation.fallback.js @@ -0,0 +1,12 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +module.exports.Validation = { + isValidUTF8: function(buffer) { + return true; + } +}; + diff --git a/node_modules/ws/lib/Validation.js b/node_modules/ws/lib/Validation.js new file mode 100644 index 0000000..0f3109a --- /dev/null +++ b/node_modules/ws/lib/Validation.js @@ -0,0 +1,16 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +try { + module.exports = require('../build/Release/validation'); +} catch (e) { try { + module.exports = require('../build/default/validation'); +} catch (e) { try { + module.exports = require('./Validation.fallback'); +} catch (e) { + console.error('validation.node seems to not have been built. Run npm install.'); + throw e; +}}} diff --git a/node_modules/ws/lib/WebSocket.js b/node_modules/ws/lib/WebSocket.js new file mode 100644 index 0000000..cce3cb4 --- /dev/null +++ b/node_modules/ws/lib/WebSocket.js @@ -0,0 +1,818 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var util = require('util') + , events = require('events') + , http = require('http') + , https = require('https') + , crypto = require('crypto') + , url = require('url') + , fs = require('fs') + , Options = require('options') + , Sender = require('./Sender') + , Receiver = require('./Receiver') + , SenderHixie = require('./Sender.hixie') + , ReceiverHixie = require('./Receiver.hixie'); + +/** + * Constants + */ + +// Default protocol version + +var protocolVersion = 13; + +// Close timeout + +var closeTimeout = 30000; // Allow 5 seconds to terminate the connection cleanly + +/** + * Node version 0.4 and 0.6 compatibility + */ + +var isNodeV4 = /^v0\.4/.test(process.version); + +/** + * WebSocket implementation + */ + +function WebSocket(address, protocols, options) { + + if (protocols && !Array.isArray(protocols) && 'object' == typeof protocols) { + // accept the "options" Object as the 2nd argument + options = protocols; + protocols = null; + } + if ('string' == typeof protocols) { + protocols = [ protocols ]; + } + if (!Array.isArray(protocols)) { + protocols = []; + } + // TODO: actually handle the `Sub-Protocols` part of the WebSocket client + + this._socket = null; + this.bytesReceived = 0; + this.readyState = null; + this.supports = {}; + + if (Array.isArray(address)) { + initAsServerClient.apply(this, address.concat(options)); + } else { + initAsClient.apply(this, [address, protocols, options]); + } +} + +/** + * Inherits from EventEmitter. + */ + +util.inherits(WebSocket, events.EventEmitter); + +/** + * Ready States + */ + +["CONNECTING", "OPEN", "CLOSING", "CLOSED"].forEach(function (state, index) { + WebSocket.prototype[state] = WebSocket[state] = index; +}); + +/** + * Gracefully closes the connection, after sending a description message to the server + * + * @param {Object} data to be sent to the server + * @api public + */ + +WebSocket.prototype.close = function(code, data) { + if (this.readyState == WebSocket.CLOSING || this.readyState == WebSocket.CLOSED) return; + if (this.readyState == WebSocket.CONNECTING) { + this.readyState = WebSocket.CLOSED; + return; + } + try { + this.readyState = WebSocket.CLOSING; + this._closeCode = code; + this._closeMessage = data; + var mask = !this._isServer; + this._sender.close(code, data, mask); + } + catch (e) { + this.emit('error', e); + } + finally { + this.terminate(); + } +} + +/** + * Pause the client stream + * + * @api public + */ + +WebSocket.prototype.pause = function() { + if (this.readyState != WebSocket.OPEN) throw new Error('not opened'); + return this._socket.pause(); +} + +/** + * Sends a ping + * + * @param {Object} data to be sent to the server + * @param {Object} Members - mask: boolean, binary: boolean + * @param {boolean} dontFailWhenClosed indicates whether or not to throw if the connection isnt open + * @api public + */ + +WebSocket.prototype.ping = function(data, options, dontFailWhenClosed) { + if (this.readyState != WebSocket.OPEN) { + if (dontFailWhenClosed === true) return; + throw new Error('not opened'); + } + options = options || {}; + if (typeof options.mask == 'undefined') options.mask = !this._isServer; + this._sender.ping(data, options); +} + +/** + * Sends a pong + * + * @param {Object} data to be sent to the server + * @param {Object} Members - mask: boolean, binary: boolean + * @param {boolean} dontFailWhenClosed indicates whether or not to throw if the connection isnt open + * @api public + */ + +WebSocket.prototype.pong = function(data, options, dontFailWhenClosed) { + if (this.readyState != WebSocket.OPEN) { + if (dontFailWhenClosed === true) return; + throw new Error('not opened'); + } + options = options || {}; + if (typeof options.mask == 'undefined') options.mask = !this._isServer; + this._sender.pong(data, options); +} + +/** + * Resume the client stream + * + * @api public + */ + +WebSocket.prototype.resume = function() { + if (this.readyState != WebSocket.OPEN) throw new Error('not opened'); + return this._socket.resume(); +} + +/** + * Sends a piece of data + * + * @param {Object} data to be sent to the server + * @param {Object} Members - mask: boolean, binary: boolean + * @param {function} Optional callback which is executed after the send completes + * @api public + */ + +WebSocket.prototype.send = function(data, options, cb) { + if (typeof options == 'function') { + cb = options; + options = {}; + } + if (this.readyState != WebSocket.OPEN) { + if (typeof cb == 'function') cb(new Error('not opened')); + else throw new Error('not opened'); + return; + } + if (!data) data = ''; + if (this._queue) { + var self = this; + this._queue.push(function() { self.send(data, options, cb); }); + return; + } + options = options || {}; + options.fin = true; + if (typeof options.binary == 'undefined') { + options.binary = (data instanceof ArrayBuffer || data instanceof Buffer || + data instanceof Uint8Array || + data instanceof Uint16Array || + data instanceof Uint32Array || + data instanceof Int8Array || + data instanceof Int16Array || + data instanceof Int32Array || + data instanceof Float32Array || + data instanceof Float64Array); + } + if (typeof options.mask == 'undefined') options.mask = !this._isServer; + if (data instanceof fs.ReadStream) { + startQueue(this); + var self = this; + sendStream(this, data, options, function(error) { + process.nextTick(function() { executeQueueSends(self); }); + if (typeof cb == 'function') cb(error); + }); + } + else this._sender.send(data, options, cb); +} + +/** + * Streams data through calls to a user supplied function + * + * @param {Object} Members - mask: boolean, binary: boolean + * @param {function} 'function (error, send)' which is executed on successive ticks of which send is 'function (data, final)'. + * @api public + */ + +WebSocket.prototype.stream = function(options, cb) { + if (typeof options == 'function') { + cb = options; + options = {}; + } + var self = this; + if (typeof cb != 'function') throw new Error('callback must be provided'); + if (this.readyState != WebSocket.OPEN) { + if (typeof cb == 'function') cb(new Error('not opened')); + else throw new Error('not opened'); + return; + } + if (this._queue) { + this._queue.push(function() { self.stream(options, cb); }); + return; + } + options = options || {}; + if (typeof options.mask == 'undefined') options.mask = !this._isServer; + startQueue(this); + var send = function(data, final) { + try { + if (self.readyState != WebSocket.OPEN) throw new Error('not opened'); + options.fin = final === true; + self._sender.send(data, options); + if (!final) process.nextTick(cb.bind(null, null, send)); + else executeQueueSends(self); + } + catch (e) { + if (typeof cb == 'function') cb(e); + else { + delete self._queue; + self.emit('error', e); + } + } + } + process.nextTick(cb.bind(null, null, send)); +} + +/** + * Immediately shuts down the connection + * + * @api public + */ + +WebSocket.prototype.terminate = function() { + if (this.readyState == WebSocket.CLOSED) return; + if (this._socket) { + try { + // End the connection + this._socket.end(); + } + catch (e) { + // Socket error during end() call, so just destroy it right now + cleanupWebsocketResources.call(this, true); + return; + } + + // Add a timeout to ensure that the connection is completely + // cleaned up within 30 seconds, even if the clean close procedure + // fails for whatever reason + this._closeTimer = setTimeout(cleanupWebsocketResources.bind(this, true), closeTimeout); + } + else if (this.readyState == WebSocket.CONNECTING) { + cleanupWebsocketResources.call(this, true); + } +}; + +/** + * Expose bufferedAmount + * + * @api public + */ + +Object.defineProperty(WebSocket.prototype, 'bufferedAmount', { + get: function get() { + var amount = 0; + if (this._socket) { + amount = this._socket.bufferSize || 0; + } + return amount; + } +}); + +/** + * Emulates the W3C Browser based WebSocket interface using function members. + * + * @see http://dev.w3.org/html5/websockets/#the-websocket-interface + * @api public + */ + +['open', 'error', 'close', 'message'].forEach(function(method) { + Object.defineProperty(WebSocket.prototype, 'on' + method, { + /** + * Returns the current listener + * + * @returns {Mixed} the set function or undefined + * @api public + */ + + get: function get() { + var listener = this.listeners(method)[0]; + return listener ? (listener._listener ? listener._listener : listener) : undefined; + }, + + /** + * Start listening for events + * + * @param {Function} listener the listener + * @returns {Mixed} the set function or undefined + * @api public + */ + + set: function set(listener) { + this.removeAllListeners(method); + this.addEventListener(method, listener); + } + }); +}); + +/** + * Emulates the W3C Browser based WebSocket interface using addEventListener. + * + * @see https://developer.mozilla.org/en/DOM/element.addEventListener + * @see http://dev.w3.org/html5/websockets/#the-websocket-interface + * @api public + */ +WebSocket.prototype.addEventListener = function(method, listener) { + var target = this; + if (typeof listener === 'function') { + if (method === 'message') { + function onMessage (data, flags) { + listener.call(this, new MessageEvent(data, flags.binary ? 'Binary' : 'Text', target)); + } + // store a reference so we can return the original function from the addEventListener hook + onMessage._listener = listener; + this.on(method, onMessage); + } else if (method === 'close') { + function onClose (code, message) { + listener.call(this, new CloseEvent(code, message, target)); + } + // store a reference so we can return the original function from the addEventListener hook + onClose._listener = listener; + this.on(method, onClose); + } else if (method === 'error') { + function onError (event) { + event.target = target; + listener.call(this, event); + } + // store a reference so we can return the original function from the addEventListener hook + onError._listener = listener; + this.on(method, onError); + } else if (method === 'open') { + function onOpen () { + listener.call(this, new OpenEvent(target)); + } + // store a reference so we can return the original function from the addEventListener hook + onOpen._listener = listener; + this.on(method, onOpen); + } else { + this.on(method, listener); + } + } +} + +module.exports = WebSocket; + +/** + * W3C MessageEvent + * + * @see http://www.w3.org/TR/html5/comms.html + * @api private + */ + +function MessageEvent(dataArg, typeArg, target) { + this.data = dataArg; + this.type = typeArg; + this.target = target; +} + +/** + * W3C CloseEvent + * + * @see http://www.w3.org/TR/html5/comms.html + * @api private + */ + +function CloseEvent(code, reason, target) { + this.wasClean = (typeof code == 'undefined' || code == 1000); + this.code = code; + this.reason = reason; + this.target = target; +} + +/** + * W3C OpenEvent + * + * @see http://www.w3.org/TR/html5/comms.html + * @api private + */ + +function OpenEvent(target) { + this.target = target; +} + +/** + * Entirely private apis, + * which may or may not be bound to a sepcific WebSocket instance. + */ + +function initAsServerClient(req, socket, upgradeHead, options) { + options = new Options({ + protocolVersion: protocolVersion, + protocol: null + }).merge(options); + + // expose state properties + this.protocol = options.value.protocol; + this.protocolVersion = options.value.protocolVersion; + this.supports.binary = (this.protocolVersion != 'hixie-76'); + this.upgradeReq = req; + this.readyState = WebSocket.CONNECTING; + this._isServer = true; + + // establish connection + if (options.value.protocolVersion == 'hixie-76') establishConnection.call(this, ReceiverHixie, SenderHixie, socket, upgradeHead); + else establishConnection.call(this, Receiver, Sender, socket, upgradeHead); +} + +function initAsClient(address, protocols, options) { + options = new Options({ + origin: null, + protocolVersion: protocolVersion, + host: null, + headers: null, + protocol: null, + agent: null, + + // ssl-related options + pfx: null, + key: null, + passphrase: null, + cert: null, + ca: null, + ciphers: null, + rejectUnauthorized: null + }).merge(options); + if (options.value.protocolVersion != 8 && options.value.protocolVersion != 13) { + throw new Error('unsupported protocol version'); + } + + // verify url and establish http class + var serverUrl = url.parse(address); + var isUnixSocket = serverUrl.protocol === 'ws+unix:'; + if (!serverUrl.host && !isUnixSocket) throw new Error('invalid url'); + var isSecure = serverUrl.protocol === 'wss:' || serverUrl.protocol === 'https:'; + var httpObj = isSecure ? https : http; + var port = serverUrl.port || (isSecure ? 443 : 80); + var auth = serverUrl.auth; + + // expose state properties + this._isServer = false; + this.url = address; + this.protocolVersion = options.value.protocolVersion; + this.supports.binary = (this.protocolVersion != 'hixie-76'); + + // begin handshake + var key = new Buffer(options.value.protocolVersion + '-' + Date.now()).toString('base64'); + var shasum = crypto.createHash('sha1'); + shasum.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'); + var expectedServerKey = shasum.digest('base64'); + + var agent = options.value.agent; + // node<=v0.4.x compatibility + if (!agent && isNodeV4) { + isNodeV4 = true; + agent = new httpObj.Agent({ + host: serverUrl.hostname, + port: port + }); + } + + var headerHost = serverUrl.hostname; + // Append port number to Host and Origin header, only if specified in the url and non-default + if(serverUrl.port) { + if((isSecure && (port != 443)) || (!isSecure && (port != 80))){ + headerHost = headerHost + ':' + port; + } + } + + var requestOptions = { + port: port, + host: serverUrl.hostname, + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Host': headerHost, + 'Origin': headerHost, + 'Sec-WebSocket-Version': options.value.protocolVersion, + 'Sec-WebSocket-Key': key + } + }; + + // If we have basic auth. + if (auth) { + requestOptions.headers['Authorization'] = 'Basic ' + new Buffer(auth).toString('base64'); + } + + if (options.value.protocol) { + requestOptions.headers['Sec-WebSocket-Protocol'] = options.value.protocol; + } + + if (options.value.host) { + requestOptions.headers['Host'] = options.value.host; + } + + if (options.value.headers) { + for (var header in options.value.headers) { + if (options.value.headers.hasOwnProperty(header)) { + requestOptions.headers[header] = options.value.headers[header]; + } + } + } + + if (options.isDefinedAndNonNull('pfx') + || options.isDefinedAndNonNull('key') + || options.isDefinedAndNonNull('passphrase') + || options.isDefinedAndNonNull('cert') + || options.isDefinedAndNonNull('ca') + || options.isDefinedAndNonNull('ciphers') + || options.isDefinedAndNonNull('rejectUnauthorized')) { + + if (isNodeV4) { + throw new Error('Client side certificates are not supported on Node 0.4.x'); + } + + if (options.isDefinedAndNonNull('pfx')) requestOptions.pfx = options.value.pfx; + if (options.isDefinedAndNonNull('key')) requestOptions.key = options.value.key; + if (options.isDefinedAndNonNull('passphrase')) requestOptions.passphrase = options.value.passphrase; + if (options.isDefinedAndNonNull('cert')) requestOptions.cert = options.value.cert; + if (options.isDefinedAndNonNull('ca')) requestOptions.ca = options.value.ca; + if (options.isDefinedAndNonNull('ciphers')) requestOptions.ciphers = options.value.ciphers; + if (options.isDefinedAndNonNull('rejectUnauthorized')) requestOptions.rejectUnauthorized = options.value.rejectUnauthorized; + + if (!agent) { + // global agent ignores client side certificates + agent = new httpObj.Agent(requestOptions); + } + } + + if (isNodeV4) { + requestOptions.path = (serverUrl.pathname || '/') + (serverUrl.search || ''); + } + else requestOptions.path = serverUrl.path || '/'; + + if (agent) { + requestOptions.agent = agent; + } + + if (isUnixSocket) { + requestOptions.socketPath = serverUrl.pathname; + } + if (options.value.origin) { + if (options.value.protocolVersion < 13) requestOptions.headers['Sec-WebSocket-Origin'] = options.value.origin; + else requestOptions.headers['Origin'] = options.value.origin; + } + + var self = this; + var req = httpObj.request(requestOptions); + + (isNodeV4 ? agent : req).on('error', function(error) { + self.emit('error', error); + cleanupWebsocketResources.call(this, error); + }); + (isNodeV4 ? agent : req).once('response', function(res) { + var error = new Error('unexpected server response (' + res.statusCode + ')'); + self.emit('error', error); + cleanupWebsocketResources.call(this, error); + }); + (isNodeV4 ? agent : req).once('upgrade', function(res, socket, upgradeHead) { + if (self.readyState == WebSocket.CLOSED) { + // client closed before server accepted connection + self.emit('close'); + removeAllListeners(self); + socket.end(); + return; + } + var serverKey = res.headers['sec-websocket-accept']; + if (typeof serverKey == 'undefined' || serverKey !== expectedServerKey) { + self.emit('error', 'invalid server key'); + removeAllListeners(self); + socket.end(); + return; + } + + var serverProt = res.headers['sec-websocket-protocol']; + var protList = (options.value.protocol || "").split(/, */); + var protError = null; + if (!options.value.protocol && serverProt) { + protError = 'server sent a subprotocol even though none requested'; + } else if (options.value.protocol && !serverProt) { + protError = 'server sent no subprotocol even though requested'; + } else if (serverProt && protList.indexOf(serverProt) === -1) { + protError = 'server responded with an invalid protocol'; + } + if (protError) { + self.emit('error', protError); + removeAllListeners(self); + socket.end(); + return; + } else if (serverProt) { + self.protocol = serverProt; + } + + establishConnection.call(self, Receiver, Sender, socket, upgradeHead); + + // perform cleanup on http resources + removeAllListeners(isNodeV4 ? agent : req); + req = null; + agent = null; + }); + + req.end(); + this.readyState = WebSocket.CONNECTING; +} + +function establishConnection(ReceiverClass, SenderClass, socket, upgradeHead) { + this._socket = socket; + socket.setTimeout(0); + socket.setNoDelay(true); + var self = this; + this._receiver = new ReceiverClass(); + + // socket cleanup handlers + socket.on('end', cleanupWebsocketResources.bind(this)); + socket.on('close', cleanupWebsocketResources.bind(this)); + socket.on('error', cleanupWebsocketResources.bind(this)); + + // ensure that the upgradeHead is added to the receiver + function firstHandler(data) { + if (self.readyState != WebSocket.OPEN) return; + if (upgradeHead && upgradeHead.length > 0) { + self.bytesReceived += upgradeHead.length; + var head = upgradeHead; + upgradeHead = null; + self._receiver.add(head); + } + dataHandler = realHandler; + if (data) { + self.bytesReceived += data.length; + self._receiver.add(data); + } + } + // subsequent packets are pushed straight to the receiver + function realHandler(data) { + if (data) self.bytesReceived += data.length; + self._receiver.add(data); + } + var dataHandler = firstHandler; + // if data was passed along with the http upgrade, + // this will schedule a push of that on to the receiver. + // this has to be done on next tick, since the caller + // hasn't had a chance to set event handlers on this client + // object yet. + process.nextTick(firstHandler); + + // receiver event handlers + self._receiver.ontext = function (data, flags) { + flags = flags || {}; + self.emit('message', data, flags); + }; + self._receiver.onbinary = function (data, flags) { + flags = flags || {}; + flags.binary = true; + self.emit('message', data, flags); + }; + self._receiver.onping = function(data, flags) { + flags = flags || {}; + self.pong(data, {mask: !self._isServer, binary: flags.binary === true}, true); + self.emit('ping', data, flags); + }; + self._receiver.onpong = function(data, flags) { + self.emit('pong', data, flags); + }; + self._receiver.onclose = function(code, data, flags) { + flags = flags || {}; + self.close(code, data); + }; + self._receiver.onerror = function(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); + }; + + // finalize the client + this._sender = new SenderClass(socket); + this._sender.on('error', function(error) { + self.close(1002, ''); + self.emit('error', error); + }); + this.readyState = WebSocket.OPEN; + this.emit('open'); + + socket.on('data', dataHandler); +} + +function startQueue(instance) { + instance._queue = instance._queue || []; +} + +function executeQueueSends(instance) { + var queue = instance._queue; + if (typeof queue == 'undefined') return; + delete instance._queue; + for (var i = 0, l = queue.length; i < l; ++i) { + queue[i](); + } +} + +function sendStream(instance, stream, options, cb) { + stream.on('data', function(data) { + if (instance.readyState != WebSocket.OPEN) { + if (typeof cb == 'function') cb(new Error('not opened')); + else { + delete instance._queue; + instance.emit('error', new Error('not opened')); + } + return; + } + options.fin = false; + instance._sender.send(data, options); + }); + stream.on('end', function() { + if (instance.readyState != WebSocket.OPEN) { + if (typeof cb == 'function') cb(new Error('not opened')); + else { + delete instance._queue; + instance.emit('error', new Error('not opened')); + } + return; + } + options.fin = true; + instance._sender.send(null, options); + if (typeof cb == 'function') cb(null); + }); +} + +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) this.emit('close', this._closeCode || 1000, this._closeMessage || ''); + + if (this._socket) { + removeAllListeners(this._socket); + // catch all socket error after removing all standard handlers + var socket = this._socket; + this._socket.on('error', function() { + try { socket.destroy(); } catch (e) {} + }); + try { + if (!error) this._socket.end(); + else this._socket.destroy(); + } + catch (e) { /* Ignore termination errors */ } + this._socket = null; + } + if (this._sender) { + removeAllListeners(this._sender); + this._sender = null; + } + if (this._receiver) { + this._receiver.cleanup(); + this._receiver = null; + } + removeAllListeners(this); + this.on('error', function() {}); // catch all errors after this + delete this._queue; +} + +function removeAllListeners(instance) { + if (isNodeV4) { + // node v4 doesn't *actually* remove all listeners globally, + // so we do that instead + instance._events = {}; + } + else instance.removeAllListeners(); +} diff --git a/node_modules/ws/lib/WebSocketServer.js b/node_modules/ws/lib/WebSocketServer.js new file mode 100644 index 0000000..da759f8 --- /dev/null +++ b/node_modules/ws/lib/WebSocketServer.js @@ -0,0 +1,460 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var util = require('util') + , events = require('events') + , http = require('http') + , crypto = require('crypto') + , url = require('url') + , Options = require('options') + , WebSocket = require('./WebSocket') + , tls = require('tls') + , url = require('url'); + +/** + * WebSocket Server implementation + */ + +function WebSocketServer(options, callback) { + options = new Options({ + host: '0.0.0.0', + port: null, + server: null, + verifyClient: null, + handleProtocols: null, + path: null, + noServer: false, + disableHixie: false, + clientTracking: true + }).merge(options); + + if (!options.isDefinedAndNonNull('port') && !options.isDefinedAndNonNull('server') && !options.value.noServer) { + throw new TypeError('`port` or a `server` must be provided'); + } + + var self = this; + + if (options.isDefinedAndNonNull('port')) { + this._server = http.createServer(function (req, res) { + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.end('Not implemented'); + }); + this._server.listen(options.value.port, options.value.host, callback); + this._closeServer = function() { self._server.close(); }; + } + else if (options.value.server) { + this._server = options.value.server; + if (options.value.path) { + // take note of the path, to avoid collisions when multiple websocket servers are + // listening on the same http server + if (this._server._webSocketPaths && options.value.server._webSocketPaths[options.value.path]) { + throw new Error('two instances of WebSocketServer cannot listen on the same http server path'); + } + if (typeof this._server._webSocketPaths !== 'object') { + this._server._webSocketPaths = {}; + } + this._server._webSocketPaths[options.value.path] = 1; + } + } + if (this._server) this._server.once('listening', function() { self.emit('listening'); }); + + if (typeof this._server != 'undefined') { + this._server.on('error', function(error) { + self.emit('error', error) + }); + this._server.on('upgrade', 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); + + self.handleUpgrade(req, socket, head, function(client) { + self.emit('connection'+req.url, client); + self.emit('connection', client); + }); + }); + } + + this.options = options.value; + this.path = options.value.path; + this.clients = []; +} + +/** + * Inherits from EventEmitter. + */ + +util.inherits(WebSocketServer, events.EventEmitter); + +/** + * Immediately shuts down the connection. + * + * @api public + */ + +WebSocketServer.prototype.close = function() { + // terminate all associated clients + var error = null; + try { + for (var i = 0, l = this.clients.length; i < l; ++i) { + this.clients[i].terminate(); + } + } + catch (e) { + error = e; + } + + // remove path descriptor, if any + if (this.path && this._server._webSocketPaths) { + delete this._server._webSocketPaths[this.path]; + if (Object.keys(this._server._webSocketPaths).length == 0) { + delete this._server._webSocketPaths; + } + } + + // close the http server if it was internally created + try { + if (typeof this._closeServer !== 'undefined') { + this._closeServer(); + } + } + finally { + delete this._server; + } + if (error) throw error; +} + +/** + * Handle a HTTP Upgrade request. + * + * @api public + */ + +WebSocketServer.prototype.handleUpgrade = function(req, socket, upgradeHead, cb) { + // check for wrong path + if (this.options.path) { + var u = url.parse(req.url); + if (u && u.pathname !== this.options.path) return; + } + + if (typeof req.headers.upgrade === 'undefined' || req.headers.upgrade.toLowerCase() !== 'websocket') { + abortConnection(socket, 400, 'Bad Request'); + return; + } + + if (req.headers['sec-websocket-key1']) handleHixieUpgrade.apply(this, arguments); + else handleHybiUpgrade.apply(this, arguments); +} + +module.exports = WebSocketServer; + +/** + * Entirely private apis, + * which may or may not be bound to a sepcific WebSocket instance. + */ + +function handleHybiUpgrade(req, socket, upgradeHead, cb) { + // handle premature socket errors + var errorHandler = function() { + try { socket.destroy(); } catch (e) {} + } + socket.on('error', errorHandler); + + // verify key presence + if (!req.headers['sec-websocket-key']) { + abortConnection(socket, 400, 'Bad Request'); + return; + } + + // verify version + var version = parseInt(req.headers['sec-websocket-version']); + if ([8, 13].indexOf(version) === -1) { + abortConnection(socket, 400, 'Bad Request'); + return; + } + + // verify protocol + var protocols = req.headers['sec-websocket-protocol']; + + // verify client + var origin = version < 13 ? + req.headers['sec-websocket-origin'] : + req.headers['origin']; + + // handler to call when the connection sequence completes + var self = this; + var completeHybiUpgrade2 = function(protocol) { + + // calc key + var key = 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 + ]; + + if (typeof protocol != 'undefined') { + headers.push('Sec-WebSocket-Protocol: ' + protocol); + } + + // allows external modification/inspection of handshake headers + self.emit('headers', headers); + + socket.setTimeout(0); + socket.setNoDelay(true); + try { + socket.write(headers.concat('', '').join('\r\n')); + } + catch (e) { + // if the upgrade write fails, shut the connection down hard + try { socket.destroy(); } catch (e) {} + return; + } + + var client = new WebSocket([req, socket, upgradeHead], { + protocolVersion: version, + protocol: protocol + }); + + if (self.options.clientTracking) { + self.clients.push(client); + client.on('close', function() { + var index = self.clients.indexOf(client); + if (index != -1) { + self.clients.splice(index, 1); + } + }); + } + + // signal upgrade complete + socket.removeListener('error', errorHandler); + cb(client); + } + + // optionally call external protocol selection handler before + // calling completeHybiUpgrade2 + var completeHybiUpgrade1 = function() { + // choose from the sub-protocols + if (typeof self.options.handleProtocols == 'function') { + var protList = (protocols || "").split(/, */); + var callbackCalled = false; + var res = self.options.handleProtocols(protList, function(result, protocol) { + callbackCalled = true; + if (!result) abortConnection(socket, 404, 'Unauthorized') + else completeHybiUpgrade2(protocol); + }); + if (!callbackCalled) { + // the handleProtocols handler never called our callback + abortConnection(socket, 501, 'Could not process protocols'); + } + return; + } else { + if (typeof protocols !== 'undefined') { + completeHybiUpgrade2(protocols.split(/, */)[0]); + } + else { + completeHybiUpgrade2(); + } + } + } + + // optionally call external client verification handler + if (typeof this.options.verifyClient == 'function') { + var info = { + origin: origin, + secure: typeof req.connection.authorized !== 'undefined' || typeof req.connection.encrypted !== 'undefined', + req: req + }; + if (this.options.verifyClient.length == 2) { + this.options.verifyClient(info, function(result) { + if (!result) abortConnection(socket, 401, 'Unauthorized') + else completeHybiUpgrade1(); + }); + return; + } + else if (!this.options.verifyClient(info)) { + abortConnection(socket, 401, 'Unauthorized'); + return; + } + } + + completeHybiUpgrade1(); +} + +function handleHixieUpgrade(req, socket, upgradeHead, cb) { + // handle premature socket errors + var errorHandler = function() { + try { socket.destroy(); } catch (e) {} + } + socket.on('error', errorHandler); + + // bail if options prevent hixie + if (this.options.disableHixie) { + abortConnection(socket, 401, 'Hixie support disabled'); + return; + } + + // verify key presence + if (!req.headers['sec-websocket-key2']) { + abortConnection(socket, 400, 'Bad Request'); + return; + } + + var origin = req.headers['origin'] + , self = this; + + // setup handshake completion to run after client has been verified + var onClientVerified = function() { + var wshost; + if (!req.headers['x-forwarded-host']) + wshost = req.headers.host; + else + wshost = req.headers['x-forwarded-host']; + var location = ((req.headers['x-forwarded-proto'] === 'https' || socket.encrypted) ? 'wss' : 'ws') + '://' + wshost + req.url + , protocol = req.headers['sec-websocket-protocol']; + + // handshake completion code to run once nonce has been successfully retrieved + var completeHandshake = function(nonce, rest) { + // calculate key + var k1 = req.headers['sec-websocket-key1'] + , k2 = req.headers['sec-websocket-key2'] + , 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){ + abortConnection(socket, 400, 'Bad Request'); + return; + } + n /= spaces; + md5.update(String.fromCharCode( + n >> 24 & 0xFF, + n >> 16 & 0xFF, + n >> 8 & 0xFF, + n & 0xFF)); + }); + 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); + hashBuffer.copy(handshakeBuffer, headerBuffer.length); + + // do a single write, which - upon success - causes a new client websocket to be setup + socket.write(handshakeBuffer, 'binary', function(err) { + if (err) return; // do not create client if an error happens + var client = new WebSocket([req, socket, rest], { + protocolVersion: 'hixie-76', + protocol: protocol + }); + if (self.options.clientTracking) { + self.clients.push(client); + client.on('close', function() { + var index = self.clients.indexOf(client); + if (index != -1) { + self.clients.splice(index, 1); + } + }); + } + + // signal upgrade complete + socket.removeListener('error', errorHandler); + cb(client); + }); + } + catch (e) { + try { socket.destroy(); } catch (e) {} + return; + } + } + + // retrieve nonce + var nonceLength = 8; + 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); + } + else { + // nonce not present in upgradeHead, so we must wait for enough data + // data to arrive before continuing + var nonce = new Buffer(nonceLength); + upgradeHead.copy(nonce, 0); + var received = upgradeHead.length; + var rest = null; + var handler = function (data) { + var toRead = Math.min(data.length, nonceLength - received); + if (toRead === 0) return; + data.copy(nonce, received, 0, toRead); + received += toRead; + if (received == nonceLength) { + socket.removeListener('data', handler); + if (toRead < data.length) rest = data.slice(toRead); + completeHandshake.call(self, nonce, rest); + } + } + socket.on('data', handler); + } + } + + // verify client + if (typeof this.options.verifyClient == 'function') { + var info = { + origin: origin, + secure: typeof req.connection.authorized !== 'undefined' || typeof req.connection.encrypted !== 'undefined', + req: req + }; + if (this.options.verifyClient.length == 2) { + var self = this; + this.options.verifyClient(info, function(result) { + if (!result) abortConnection(socket, 401, 'Unauthorized') + else onClientVerified.apply(self); + }); + return; + } + else if (!this.options.verifyClient(info)) { + abortConnection(socket, 401, 'Unauthorized'); + return; + } + } + + // no client verification required + onClientVerified(); +} + +function abortConnection(socket, code, name) { + try { + var response = [ + 'HTTP/1.1 ' + code + ' ' + name, + 'Content-type: text/html' + ]; + socket.write(response.concat('', '').join('\r\n')); + } + catch (e) { /* ignore errors - we've aborted this connection */ } + finally { + // ensure that an early aborted connection is shut down completely + try { socket.destroy(); } catch (e) {} + } +} diff --git a/node_modules/ws/lib/browser.js b/node_modules/ws/lib/browser.js new file mode 100644 index 0000000..8d3a755 --- /dev/null +++ b/node_modules/ws/lib/browser.js @@ -0,0 +1,43 @@ + +/** + * Module dependencies. + */ + +var global = (function() { return this; })(); + +/** + * WebSocket constructor. + */ + +var WebSocket = global.WebSocket || global.MozWebSocket; + +/** + * Module exports. + */ + +module.exports = WebSocket ? ws : null; + +/** + * WebSocket constructor. + * + * The third `opts` options object gets ignored in web browsers, since it's + * non-standard, and throws a TypeError if passed to the constructor. + * See: https://github.com/einaros/ws/issues/227 + * + * @param {String} uri + * @param {Array} protocols (optional) + * @param {Object) opts (optional) + * @api public + */ + +function ws(uri, protocols, opts) { + var instance; + if (protocols) { + instance = new WebSocket(uri, protocols); + } else { + instance = new WebSocket(uri); + } + return instance; +} + +if (WebSocket) ws.prototype = WebSocket.prototype; diff --git a/node_modules/ws/node_modules/commander/.npmignore b/node_modules/ws/node_modules/commander/.npmignore new file mode 100644 index 0000000..f1250e5 --- /dev/null +++ b/node_modules/ws/node_modules/commander/.npmignore @@ -0,0 +1,4 @@ +support +test +examples +*.sock diff --git a/node_modules/ws/node_modules/commander/.travis.yml b/node_modules/ws/node_modules/commander/.travis.yml new file mode 100644 index 0000000..f1d0f13 --- /dev/null +++ b/node_modules/ws/node_modules/commander/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.4 + - 0.6 diff --git a/node_modules/ws/node_modules/commander/History.md b/node_modules/ws/node_modules/commander/History.md new file mode 100644 index 0000000..4961d2e --- /dev/null +++ b/node_modules/ws/node_modules/commander/History.md @@ -0,0 +1,107 @@ + +0.6.1 / 2012-06-01 +================== + + * Added: append (yes or no) on confirmation + * Added: allow node.js v0.7.x + +0.6.0 / 2012-04-10 +================== + + * Added `.prompt(obj, callback)` support. Closes #49 + * Added default support to .choose(). Closes #41 + * Fixed the choice example + +0.5.1 / 2011-12-20 +================== + + * Fixed `password()` for recent nodes. Closes #36 + +0.5.0 / 2011-12-04 +================== + + * Added sub-command option support [itay] + +0.4.3 / 2011-12-04 +================== + + * Fixed custom help ordering. Closes #32 + +0.4.2 / 2011-11-24 +================== + + * Added travis support + * Fixed: line-buffered input automatically trimmed. Closes #31 + +0.4.1 / 2011-11-18 +================== + + * Removed listening for "close" on --help + +0.4.0 / 2011-11-15 +================== + + * Added support for `--`. Closes #24 + +0.3.3 / 2011-11-14 +================== + + * Fixed: wait for close event when writing help info [Jerry Hamlet] + +0.3.2 / 2011-11-01 +================== + + * Fixed long flag definitions with values [felixge] + +0.3.1 / 2011-10-31 +================== + + * Changed `--version` short flag to `-V` from `-v` + * Changed `.version()` so it's configurable [felixge] + +0.3.0 / 2011-10-31 +================== + + * Added support for long flags only. Closes #18 + +0.2.1 / 2011-10-24 +================== + + * "node": ">= 0.4.x < 0.7.0". Closes #20 + +0.2.0 / 2011-09-26 +================== + + * Allow for defaults that are not just boolean. Default peassignment only occurs for --no-*, optional, and required arguments. [Jim Isaacs] + +0.1.0 / 2011-08-24 +================== + + * Added support for custom `--help` output + +0.0.5 / 2011-08-18 +================== + + * Changed: when the user enters nothing prompt for password again + * Fixed issue with passwords beginning with numbers [NuckChorris] + +0.0.4 / 2011-08-15 +================== + + * Fixed `Commander#args` + +0.0.3 / 2011-08-15 +================== + + * Added default option value support + +0.0.2 / 2011-08-15 +================== + + * Added mask support to `Command#password(str[, mask], fn)` + * Added `Command#password(str, fn)` + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/node_modules/ws/node_modules/commander/Makefile b/node_modules/ws/node_modules/commander/Makefile new file mode 100644 index 0000000..0074625 --- /dev/null +++ b/node_modules/ws/node_modules/commander/Makefile @@ -0,0 +1,7 @@ + +TESTS = $(shell find test/test.*.js) + +test: + @./test/run $(TESTS) + +.PHONY: test \ No newline at end of file diff --git a/node_modules/ws/node_modules/commander/Readme.md b/node_modules/ws/node_modules/commander/Readme.md new file mode 100644 index 0000000..b8328c3 --- /dev/null +++ b/node_modules/ws/node_modules/commander/Readme.md @@ -0,0 +1,262 @@ +# Commander.js + + The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/visionmedia/commander). + + [![Build Status](https://secure.travis-ci.org/visionmedia/commander.js.png)](http://travis-ci.org/visionmedia/commander.js) + +## Installation + + $ npm install commander + +## Option parsing + + Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options. + +```js +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('commander'); + +program + .version('0.0.1') + .option('-p, --peppers', 'Add peppers') + .option('-P, --pineapple', 'Add pineapple') + .option('-b, --bbq', 'Add bbq sauce') + .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble') + .parse(process.argv); + +console.log('you ordered a pizza with:'); +if (program.peppers) console.log(' - peppers'); +if (program.pineapple) console.log(' - pineappe'); +if (program.bbq) console.log(' - bbq'); +console.log(' - %s cheese', program.cheese); +``` + + Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as "--template-engine" are camel-cased, becoming `program.templateEngine` etc. + +## Automated --help + + The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free: + +``` + $ ./examples/pizza --help + + Usage: pizza [options] + + Options: + + -V, --version output the version number + -p, --peppers Add peppers + -P, --pineapple Add pineappe + -b, --bbq Add bbq sauce + -c, --cheese Add the specified type of cheese [marble] + -h, --help output usage information + +``` + +## Coercion + +```js +function range(val) { + return val.split('..').map(Number); +} + +function list(val) { + return val.split(','); +} + +program + .version('0.0.1') + .usage('[options] ') + .option('-i, --integer ', 'An integer argument', parseInt) + .option('-f, --float ', 'A float argument', parseFloat) + .option('-r, --range ..', 'A range', range) + .option('-l, --list ', 'A list', list) + .option('-o, --optional [value]', 'An optional value') + .parse(process.argv); + +console.log(' int: %j', program.integer); +console.log(' float: %j', program.float); +console.log(' optional: %j', program.optional); +program.range = program.range || []; +console.log(' range: %j..%j', program.range[0], program.range[1]); +console.log(' list: %j', program.list); +console.log(' args: %j', program.args); +``` + +## Custom help + + You can display arbitrary `-h, --help` information + by listening for "--help". Commander will automatically + exit once you are done so that the remainder of your program + does not execute causing undesired behaviours, for example + in the following executable "stuff" will not output when + `--help` is used. + +```js +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('../'); + +function list(val) { + return val.split(',').map(Number); +} + +program + .version('0.0.1') + .option('-f, --foo', 'enable some foo') + .option('-b, --bar', 'enable some bar') + .option('-B, --baz', 'enable some baz'); + +// must be before .parse() since +// node's emit() is immediate + +program.on('--help', function(){ + console.log(' Examples:'); + console.log(''); + console.log(' $ custom-help --help'); + console.log(' $ custom-help -h'); + console.log(''); +}); + +program.parse(process.argv); + +console.log('stuff'); +``` + +yielding the following help output: + +``` + +Usage: custom-help [options] + +Options: + + -h, --help output usage information + -V, --version output the version number + -f, --foo enable some foo + -b, --bar enable some bar + -B, --baz enable some baz + +Examples: + + $ custom-help --help + $ custom-help -h + +``` + +## .prompt(msg, fn) + + Single-line prompt: + +```js +program.prompt('name: ', function(name){ + console.log('hi %s', name); +}); +``` + + Multi-line prompt: + +```js +program.prompt('description:', function(name){ + console.log('hi %s', name); +}); +``` + + Coercion: + +```js +program.prompt('Age: ', Number, function(age){ + console.log('age: %j', age); +}); +``` + +```js +program.prompt('Birthdate: ', Date, function(date){ + console.log('date: %s', date); +}); +``` + +## .password(msg[, mask], fn) + +Prompt for password without echoing: + +```js +program.password('Password: ', function(pass){ + console.log('got "%s"', pass); + process.stdin.destroy(); +}); +``` + +Prompt for password with mask char "*": + +```js +program.password('Password: ', '*', function(pass){ + console.log('got "%s"', pass); + process.stdin.destroy(); +}); +``` + +## .confirm(msg, fn) + + Confirm with the given `msg`: + +```js +program.confirm('continue? ', function(ok){ + console.log(' got %j', ok); +}); +``` + +## .choose(list, fn) + + Let the user choose from a `list`: + +```js +var list = ['tobi', 'loki', 'jane', 'manny', 'luna']; + +console.log('Choose the coolest pet:'); +program.choose(list, function(i){ + console.log('you chose %d "%s"', i, list[i]); +}); +``` + +## Links + + - [API documentation](http://visionmedia.github.com/commander.js/) + - [ascii tables](https://github.com/LearnBoost/cli-table) + - [progress bars](https://github.com/visionmedia/node-progress) + - [more progress bars](https://github.com/substack/node-multimeter) + - [examples](https://github.com/visionmedia/commander.js/tree/master/examples) + +## License + +(The MIT License) + +Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/ws/node_modules/commander/index.js b/node_modules/ws/node_modules/commander/index.js new file mode 100644 index 0000000..06ec1e4 --- /dev/null +++ b/node_modules/ws/node_modules/commander/index.js @@ -0,0 +1,2 @@ + +module.exports = require('./lib/commander'); \ No newline at end of file diff --git a/node_modules/ws/node_modules/commander/lib/commander.js b/node_modules/ws/node_modules/commander/lib/commander.js new file mode 100644 index 0000000..5ba87eb --- /dev/null +++ b/node_modules/ws/node_modules/commander/lib/commander.js @@ -0,0 +1,1026 @@ + +/*! + * commander + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter + , path = require('path') + , tty = require('tty') + , basename = path.basename; + +/** + * Expose the root command. + */ + +exports = module.exports = new Command; + +/** + * Expose `Command`. + */ + +exports.Command = Command; + +/** + * Expose `Option`. + */ + +exports.Option = Option; + +/** + * Initialize a new `Option` with the given `flags` and `description`. + * + * @param {String} flags + * @param {String} description + * @api public + */ + +function Option(flags, description) { + this.flags = flags; + this.required = ~flags.indexOf('<'); + this.optional = ~flags.indexOf('['); + this.bool = !~flags.indexOf('-no-'); + flags = flags.split(/[ ,|]+/); + if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift(); + this.long = flags.shift(); + this.description = description; +} + +/** + * Return option name. + * + * @return {String} + * @api private + */ + +Option.prototype.name = function(){ + return this.long + .replace('--', '') + .replace('no-', ''); +}; + +/** + * Check if `arg` matches the short or long flag. + * + * @param {String} arg + * @return {Boolean} + * @api private + */ + +Option.prototype.is = function(arg){ + return arg == this.short + || arg == this.long; +}; + +/** + * Initialize a new `Command`. + * + * @param {String} name + * @api public + */ + +function Command(name) { + this.commands = []; + this.options = []; + this.args = []; + this.name = name; +} + +/** + * Inherit from `EventEmitter.prototype`. + */ + +Command.prototype.__proto__ = EventEmitter.prototype; + +/** + * Add command `name`. + * + * The `.action()` callback is invoked when the + * command `name` is specified via __ARGV__, + * and the remaining arguments are applied to the + * function for access. + * + * When the `name` is "*" an un-matched command + * will be passed as the first arg, followed by + * the rest of __ARGV__ remaining. + * + * Examples: + * + * program + * .version('0.0.1') + * .option('-C, --chdir ', 'change the working directory') + * .option('-c, --config ', 'set config path. defaults to ./deploy.conf') + * .option('-T, --no-tests', 'ignore test hook') + * + * program + * .command('setup') + * .description('run remote setup commands') + * .action(function(){ + * console.log('setup'); + * }); + * + * program + * .command('exec ') + * .description('run the given remote command') + * .action(function(cmd){ + * console.log('exec "%s"', cmd); + * }); + * + * program + * .command('*') + * .description('deploy the given env') + * .action(function(env){ + * console.log('deploying "%s"', env); + * }); + * + * program.parse(process.argv); + * + * @param {String} name + * @return {Command} the new command + * @api public + */ + +Command.prototype.command = function(name){ + var args = name.split(/ +/); + var cmd = new Command(args.shift()); + this.commands.push(cmd); + cmd.parseExpectedArgs(args); + cmd.parent = this; + return cmd; +}; + +/** + * Parse expected `args`. + * + * For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`. + * + * @param {Array} args + * @return {Command} for chaining + * @api public + */ + +Command.prototype.parseExpectedArgs = function(args){ + if (!args.length) return; + var self = this; + args.forEach(function(arg){ + switch (arg[0]) { + case '<': + self.args.push({ required: true, name: arg.slice(1, -1) }); + break; + case '[': + self.args.push({ required: false, name: arg.slice(1, -1) }); + break; + } + }); + return this; +}; + +/** + * Register callback `fn` for the command. + * + * Examples: + * + * program + * .command('help') + * .description('display verbose help') + * .action(function(){ + * // output help here + * }); + * + * @param {Function} fn + * @return {Command} for chaining + * @api public + */ + +Command.prototype.action = function(fn){ + var self = this; + this.parent.on(this.name, function(args, unknown){ + // Parse any so-far unknown options + unknown = unknown || []; + var parsed = self.parseOptions(unknown); + + // Output help if necessary + outputHelpIfNecessary(self, parsed.unknown); + + // If there are still any unknown options, then we simply + // die, unless someone asked for help, in which case we give it + // to them, and then we die. + if (parsed.unknown.length > 0) { + self.unknownOption(parsed.unknown[0]); + } + + self.args.forEach(function(arg, i){ + if (arg.required && null == args[i]) { + self.missingArgument(arg.name); + } + }); + + // Always append ourselves to the end of the arguments, + // to make sure we match the number of arguments the user + // expects + if (self.args.length) { + args[self.args.length] = self; + } else { + args.push(self); + } + + fn.apply(this, args); + }); + return this; +}; + +/** + * Define option with `flags`, `description` and optional + * coercion `fn`. + * + * The `flags` string should contain both the short and long flags, + * separated by comma, a pipe or space. The following are all valid + * all will output this way when `--help` is used. + * + * "-p, --pepper" + * "-p|--pepper" + * "-p --pepper" + * + * Examples: + * + * // simple boolean defaulting to false + * program.option('-p, --pepper', 'add pepper'); + * + * --pepper + * program.pepper + * // => Boolean + * + * // simple boolean defaulting to false + * program.option('-C, --no-cheese', 'remove cheese'); + * + * program.cheese + * // => true + * + * --no-cheese + * program.cheese + * // => true + * + * // required argument + * program.option('-C, --chdir ', 'change the working directory'); + * + * --chdir /tmp + * program.chdir + * // => "/tmp" + * + * // optional argument + * program.option('-c, --cheese [type]', 'add cheese [marble]'); + * + * @param {String} flags + * @param {String} description + * @param {Function|Mixed} fn or default + * @param {Mixed} defaultValue + * @return {Command} for chaining + * @api public + */ + +Command.prototype.option = function(flags, description, fn, defaultValue){ + var self = this + , option = new Option(flags, description) + , oname = option.name() + , name = camelcase(oname); + + // default as 3rd arg + if ('function' != typeof fn) defaultValue = fn, fn = null; + + // preassign default value only for --no-*, [optional], or + if (false == option.bool || option.optional || option.required) { + // when --no-* we make sure default is true + if (false == option.bool) defaultValue = true; + // preassign only if we have a default + if (undefined !== defaultValue) self[name] = defaultValue; + } + + // register the option + this.options.push(option); + + // when it's passed assign the value + // and conditionally invoke the callback + this.on(oname, function(val){ + // coercion + if (null != val && fn) val = fn(val); + + // unassigned or bool + if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) { + // if no value, bool true, and we have a default, then use it! + if (null == val) { + self[name] = option.bool + ? defaultValue || true + : false; + } else { + self[name] = val; + } + } else if (null !== val) { + // reassign + self[name] = val; + } + }); + + return this; +}; + +/** + * Parse `argv`, settings options and invoking commands when defined. + * + * @param {Array} argv + * @return {Command} for chaining + * @api public + */ + +Command.prototype.parse = function(argv){ + // store raw args + this.rawArgs = argv; + + // guess name + if (!this.name) this.name = basename(argv[1]); + + // process argv + var parsed = this.parseOptions(this.normalize(argv.slice(2))); + this.args = parsed.args; + return this.parseArgs(this.args, parsed.unknown); +}; + +/** + * Normalize `args`, splitting joined short flags. For example + * the arg "-abc" is equivalent to "-a -b -c". + * + * @param {Array} args + * @return {Array} + * @api private + */ + +Command.prototype.normalize = function(args){ + var ret = [] + , arg; + + for (var i = 0, len = args.length; i < len; ++i) { + arg = args[i]; + if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) { + arg.slice(1).split('').forEach(function(c){ + ret.push('-' + c); + }); + } else { + ret.push(arg); + } + } + + return ret; +}; + +/** + * Parse command `args`. + * + * When listener(s) are available those + * callbacks are invoked, otherwise the "*" + * event is emitted and those actions are invoked. + * + * @param {Array} args + * @return {Command} for chaining + * @api private + */ + +Command.prototype.parseArgs = function(args, unknown){ + var cmds = this.commands + , len = cmds.length + , name; + + if (args.length) { + name = args[0]; + if (this.listeners(name).length) { + this.emit(args.shift(), args, unknown); + } else { + this.emit('*', args); + } + } else { + outputHelpIfNecessary(this, unknown); + + // If there were no args and we have unknown options, + // then they are extraneous and we need to error. + if (unknown.length > 0) { + this.unknownOption(unknown[0]); + } + } + + return this; +}; + +/** + * Return an option matching `arg` if any. + * + * @param {String} arg + * @return {Option} + * @api private + */ + +Command.prototype.optionFor = function(arg){ + for (var i = 0, len = this.options.length; i < len; ++i) { + if (this.options[i].is(arg)) { + return this.options[i]; + } + } +}; + +/** + * Parse options from `argv` returning `argv` + * void of these options. + * + * @param {Array} argv + * @return {Array} + * @api public + */ + +Command.prototype.parseOptions = function(argv){ + var args = [] + , len = argv.length + , literal + , option + , arg; + + var unknownOptions = []; + + // parse options + for (var i = 0; i < len; ++i) { + arg = argv[i]; + + // literal args after -- + if ('--' == arg) { + literal = true; + continue; + } + + if (literal) { + args.push(arg); + continue; + } + + // find matching Option + option = this.optionFor(arg); + + // option is defined + if (option) { + // requires arg + if (option.required) { + arg = argv[++i]; + if (null == arg) return this.optionMissingArgument(option); + if ('-' == arg[0]) return this.optionMissingArgument(option, arg); + this.emit(option.name(), arg); + // optional arg + } else if (option.optional) { + arg = argv[i+1]; + if (null == arg || '-' == arg[0]) { + arg = null; + } else { + ++i; + } + this.emit(option.name(), arg); + // bool + } else { + this.emit(option.name()); + } + continue; + } + + // looks like an option + if (arg.length > 1 && '-' == arg[0]) { + unknownOptions.push(arg); + + // If the next argument looks like it might be + // an argument for this option, we pass it on. + // If it isn't, then it'll simply be ignored + if (argv[i+1] && '-' != argv[i+1][0]) { + unknownOptions.push(argv[++i]); + } + continue; + } + + // arg + args.push(arg); + } + + return { args: args, unknown: unknownOptions }; +}; + +/** + * Argument `name` is missing. + * + * @param {String} name + * @api private + */ + +Command.prototype.missingArgument = function(name){ + console.error(); + console.error(" error: missing required argument `%s'", name); + console.error(); + process.exit(1); +}; + +/** + * `Option` is missing an argument, but received `flag` or nothing. + * + * @param {String} option + * @param {String} flag + * @api private + */ + +Command.prototype.optionMissingArgument = function(option, flag){ + console.error(); + if (flag) { + console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag); + } else { + console.error(" error: option `%s' argument missing", option.flags); + } + console.error(); + process.exit(1); +}; + +/** + * Unknown option `flag`. + * + * @param {String} flag + * @api private + */ + +Command.prototype.unknownOption = function(flag){ + console.error(); + console.error(" error: unknown option `%s'", flag); + console.error(); + process.exit(1); +}; + +/** + * Set the program version to `str`. + * + * This method auto-registers the "-V, --version" flag + * which will print the version number when passed. + * + * @param {String} str + * @param {String} flags + * @return {Command} for chaining + * @api public + */ + +Command.prototype.version = function(str, flags){ + if (0 == arguments.length) return this._version; + this._version = str; + flags = flags || '-V, --version'; + this.option(flags, 'output the version number'); + this.on('version', function(){ + console.log(str); + process.exit(0); + }); + return this; +}; + +/** + * Set the description `str`. + * + * @param {String} str + * @return {String|Command} + * @api public + */ + +Command.prototype.description = function(str){ + if (0 == arguments.length) return this._description; + this._description = str; + return this; +}; + +/** + * Set / get the command usage `str`. + * + * @param {String} str + * @return {String|Command} + * @api public + */ + +Command.prototype.usage = function(str){ + var args = this.args.map(function(arg){ + return arg.required + ? '<' + arg.name + '>' + : '[' + arg.name + ']'; + }); + + var usage = '[options' + + (this.commands.length ? '] [command' : '') + + ']' + + (this.args.length ? ' ' + args : ''); + if (0 == arguments.length) return this._usage || usage; + this._usage = str; + + return this; +}; + +/** + * Return the largest option length. + * + * @return {Number} + * @api private + */ + +Command.prototype.largestOptionLength = function(){ + return this.options.reduce(function(max, option){ + return Math.max(max, option.flags.length); + }, 0); +}; + +/** + * Return help for options. + * + * @return {String} + * @api private + */ + +Command.prototype.optionHelp = function(){ + var width = this.largestOptionLength(); + + // Prepend the help information + return [pad('-h, --help', width) + ' ' + 'output usage information'] + .concat(this.options.map(function(option){ + return pad(option.flags, width) + + ' ' + option.description; + })) + .join('\n'); +}; + +/** + * Return command help documentation. + * + * @return {String} + * @api private + */ + +Command.prototype.commandHelp = function(){ + if (!this.commands.length) return ''; + return [ + '' + , ' Commands:' + , '' + , this.commands.map(function(cmd){ + var args = cmd.args.map(function(arg){ + return arg.required + ? '<' + arg.name + '>' + : '[' + arg.name + ']'; + }).join(' '); + + return cmd.name + + (cmd.options.length + ? ' [options]' + : '') + ' ' + args + + (cmd.description() + ? '\n' + cmd.description() + : ''); + }).join('\n\n').replace(/^/gm, ' ') + , '' + ].join('\n'); +}; + +/** + * Return program help documentation. + * + * @return {String} + * @api private + */ + +Command.prototype.helpInformation = function(){ + return [ + '' + , ' Usage: ' + this.name + ' ' + this.usage() + , '' + this.commandHelp() + , ' Options:' + , '' + , '' + this.optionHelp().replace(/^/gm, ' ') + , '' + , '' + ].join('\n'); +}; + +/** + * Prompt for a `Number`. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptForNumber = function(str, fn){ + var self = this; + this.promptSingleLine(str, function parseNumber(val){ + val = Number(val); + if (isNaN(val)) return self.promptSingleLine(str + '(must be a number) ', parseNumber); + fn(val); + }); +}; + +/** + * Prompt for a `Date`. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptForDate = function(str, fn){ + var self = this; + this.promptSingleLine(str, function parseDate(val){ + val = new Date(val); + if (isNaN(val.getTime())) return self.promptSingleLine(str + '(must be a date) ', parseDate); + fn(val); + }); +}; + +/** + * Single-line prompt. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptSingleLine = function(str, fn){ + if ('function' == typeof arguments[2]) { + return this['promptFor' + (fn.name || fn)](str, arguments[2]); + } + + process.stdout.write(str); + process.stdin.setEncoding('utf8'); + process.stdin.once('data', function(val){ + fn(val.trim()); + }).resume(); +}; + +/** + * Multi-line prompt. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptMultiLine = function(str, fn){ + var buf = []; + console.log(str); + process.stdin.setEncoding('utf8'); + process.stdin.on('data', function(val){ + if ('\n' == val || '\r\n' == val) { + process.stdin.removeAllListeners('data'); + fn(buf.join('\n')); + } else { + buf.push(val.trimRight()); + } + }).resume(); +}; + +/** + * Prompt `str` and callback `fn(val)` + * + * Commander supports single-line and multi-line prompts. + * To issue a single-line prompt simply add white-space + * to the end of `str`, something like "name: ", whereas + * for a multi-line prompt omit this "description:". + * + * + * Examples: + * + * program.prompt('Username: ', function(name){ + * console.log('hi %s', name); + * }); + * + * program.prompt('Description:', function(desc){ + * console.log('description was "%s"', desc.trim()); + * }); + * + * @param {String|Object} str + * @param {Function} fn + * @api public + */ + +Command.prototype.prompt = function(str, fn){ + var self = this; + + if ('string' == typeof str) { + if (/ $/.test(str)) return this.promptSingleLine.apply(this, arguments); + this.promptMultiLine(str, fn); + } else { + var keys = Object.keys(str) + , obj = {}; + + function next() { + var key = keys.shift() + , label = str[key]; + + if (!key) return fn(obj); + self.prompt(label, function(val){ + obj[key] = val; + next(); + }); + } + + next(); + } +}; + +/** + * Prompt for password with `str`, `mask` char and callback `fn(val)`. + * + * The mask string defaults to '', aka no output is + * written while typing, you may want to use "*" etc. + * + * Examples: + * + * program.password('Password: ', function(pass){ + * console.log('got "%s"', pass); + * process.stdin.destroy(); + * }); + * + * program.password('Password: ', '*', function(pass){ + * console.log('got "%s"', pass); + * process.stdin.destroy(); + * }); + * + * @param {String} str + * @param {String} mask + * @param {Function} fn + * @api public + */ + +Command.prototype.password = function(str, mask, fn){ + var self = this + , buf = ''; + + // default mask + if ('function' == typeof mask) { + fn = mask; + mask = ''; + } + + process.stdin.resume(); + tty.setRawMode(true); + process.stdout.write(str); + + // keypress + process.stdin.on('keypress', function(c, key){ + if (key && 'enter' == key.name) { + console.log(); + process.stdin.removeAllListeners('keypress'); + tty.setRawMode(false); + if (!buf.trim().length) return self.password(str, mask, fn); + fn(buf); + return; + } + + if (key && key.ctrl && 'c' == key.name) { + console.log('%s', buf); + process.exit(); + } + + process.stdout.write(mask); + buf += c; + }).resume(); +}; + +/** + * Confirmation prompt with `str` and callback `fn(bool)` + * + * Examples: + * + * program.confirm('continue? ', function(ok){ + * console.log(' got %j', ok); + * process.stdin.destroy(); + * }); + * + * @param {String} str + * @param {Function} fn + * @api public + */ + + +Command.prototype.confirm = function(str, fn, verbose){ + var self = this; + this.prompt(str, function(ok){ + if (!ok.trim()) { + if (!verbose) str += '(yes or no) '; + return self.confirm(str, fn, true); + } + fn(parseBool(ok)); + }); +}; + +/** + * Choice prompt with `list` of items and callback `fn(index, item)` + * + * Examples: + * + * var list = ['tobi', 'loki', 'jane', 'manny', 'luna']; + * + * console.log('Choose the coolest pet:'); + * program.choose(list, function(i){ + * console.log('you chose %d "%s"', i, list[i]); + * process.stdin.destroy(); + * }); + * + * @param {Array} list + * @param {Number|Function} index or fn + * @param {Function} fn + * @api public + */ + +Command.prototype.choose = function(list, index, fn){ + var self = this + , hasDefault = 'number' == typeof index; + + if (!hasDefault) { + fn = index; + index = null; + } + + list.forEach(function(item, i){ + if (hasDefault && i == index) { + console.log('* %d) %s', i + 1, item); + } else { + console.log(' %d) %s', i + 1, item); + } + }); + + function again() { + self.prompt(' : ', function(val){ + val = parseInt(val, 10) - 1; + if (hasDefault && isNaN(val)) val = index; + + if (null == list[val]) { + again(); + } else { + fn(val, list[val]); + } + }); + } + + again(); +}; + +/** + * Camel-case the given `flag` + * + * @param {String} flag + * @return {String} + * @api private + */ + +function camelcase(flag) { + return flag.split('-').reduce(function(str, word){ + return str + word[0].toUpperCase() + word.slice(1); + }); +} + +/** + * Parse a boolean `str`. + * + * @param {String} str + * @return {Boolean} + * @api private + */ + +function parseBool(str) { + return /^y|yes|ok|true$/i.test(str); +} + +/** + * Pad `str` to `width`. + * + * @param {String} str + * @param {Number} width + * @return {String} + * @api private + */ + +function pad(str, width) { + var len = Math.max(0, width - str.length); + return str + Array(len + 1).join(' '); +} + +/** + * Output help information if necessary + * + * @param {Command} command to output help for + * @param {Array} array of options to search for -h or --help + * @api private + */ + +function outputHelpIfNecessary(cmd, options) { + options = options || []; + for (var i = 0; i < options.length; i++) { + if (options[i] == '--help' || options[i] == '-h') { + process.stdout.write(cmd.helpInformation()); + cmd.emit('--help'); + process.exit(0); + } + } +} diff --git a/node_modules/ws/node_modules/commander/package.json b/node_modules/ws/node_modules/commander/package.json new file mode 100644 index 0000000..3200d50 --- /dev/null +++ b/node_modules/ws/node_modules/commander/package.json @@ -0,0 +1,37 @@ +{ + "name": "commander", + "version": "0.6.1", + "description": "the complete solution for node.js command-line programs", + "keywords": [ + "command", + "option", + "parser", + "prompt", + "stdin" + ], + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "repository": { + "type": "git", + "url": "https://github.com/visionmedia/commander.js.git" + }, + "dependencies": {}, + "devDependencies": { + "should": ">= 0.0.1" + }, + "scripts": { + "test": "make test" + }, + "main": "index", + "engines": { + "node": ">= 0.4.x" + }, + "readme": "# Commander.js\n\n The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/visionmedia/commander).\n\n [![Build Status](https://secure.travis-ci.org/visionmedia/commander.js.png)](http://travis-ci.org/visionmedia/commander.js)\n\n## Installation\n\n $ npm install commander\n\n## Option parsing\n\n Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options.\n\n```js\n#!/usr/bin/env node\n\n/**\n * Module dependencies.\n */\n\nvar program = require('commander');\n\nprogram\n .version('0.0.1')\n .option('-p, --peppers', 'Add peppers')\n .option('-P, --pineapple', 'Add pineapple')\n .option('-b, --bbq', 'Add bbq sauce')\n .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble')\n .parse(process.argv);\n\nconsole.log('you ordered a pizza with:');\nif (program.peppers) console.log(' - peppers');\nif (program.pineapple) console.log(' - pineappe');\nif (program.bbq) console.log(' - bbq');\nconsole.log(' - %s cheese', program.cheese);\n```\n\n Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as \"--template-engine\" are camel-cased, becoming `program.templateEngine` etc.\n\n## Automated --help\n\n The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free:\n\n``` \n $ ./examples/pizza --help\n\n Usage: pizza [options]\n\n Options:\n\n -V, --version output the version number\n -p, --peppers Add peppers\n -P, --pineapple Add pineappe\n -b, --bbq Add bbq sauce\n -c, --cheese Add the specified type of cheese [marble]\n -h, --help output usage information\n\n```\n\n## Coercion\n\n```js\nfunction range(val) {\n return val.split('..').map(Number);\n}\n\nfunction list(val) {\n return val.split(',');\n}\n\nprogram\n .version('0.0.1')\n .usage('[options] ')\n .option('-i, --integer ', 'An integer argument', parseInt)\n .option('-f, --float ', 'A float argument', parseFloat)\n .option('-r, --range ..', 'A range', range)\n .option('-l, --list ', 'A list', list)\n .option('-o, --optional [value]', 'An optional value')\n .parse(process.argv);\n\nconsole.log(' int: %j', program.integer);\nconsole.log(' float: %j', program.float);\nconsole.log(' optional: %j', program.optional);\nprogram.range = program.range || [];\nconsole.log(' range: %j..%j', program.range[0], program.range[1]);\nconsole.log(' list: %j', program.list);\nconsole.log(' args: %j', program.args);\n```\n\n## Custom help\n\n You can display arbitrary `-h, --help` information\n by listening for \"--help\". Commander will automatically\n exit once you are done so that the remainder of your program\n does not execute causing undesired behaviours, for example\n in the following executable \"stuff\" will not output when\n `--help` is used.\n\n```js\n#!/usr/bin/env node\n\n/**\n * Module dependencies.\n */\n\nvar program = require('../');\n\nfunction list(val) {\n return val.split(',').map(Number);\n}\n\nprogram\n .version('0.0.1')\n .option('-f, --foo', 'enable some foo')\n .option('-b, --bar', 'enable some bar')\n .option('-B, --baz', 'enable some baz');\n\n// must be before .parse() since\n// node's emit() is immediate\n\nprogram.on('--help', function(){\n console.log(' Examples:');\n console.log('');\n console.log(' $ custom-help --help');\n console.log(' $ custom-help -h');\n console.log('');\n});\n\nprogram.parse(process.argv);\n\nconsole.log('stuff');\n```\n\nyielding the following help output:\n\n```\n\nUsage: custom-help [options]\n\nOptions:\n\n -h, --help output usage information\n -V, --version output the version number\n -f, --foo enable some foo\n -b, --bar enable some bar\n -B, --baz enable some baz\n\nExamples:\n\n $ custom-help --help\n $ custom-help -h\n\n```\n\n## .prompt(msg, fn)\n\n Single-line prompt:\n\n```js\nprogram.prompt('name: ', function(name){\n console.log('hi %s', name);\n});\n```\n\n Multi-line prompt:\n\n```js\nprogram.prompt('description:', function(name){\n console.log('hi %s', name);\n});\n```\n\n Coercion:\n\n```js\nprogram.prompt('Age: ', Number, function(age){\n console.log('age: %j', age);\n});\n```\n\n```js\nprogram.prompt('Birthdate: ', Date, function(date){\n console.log('date: %s', date);\n});\n```\n\n## .password(msg[, mask], fn)\n\nPrompt for password without echoing:\n\n```js\nprogram.password('Password: ', function(pass){\n console.log('got \"%s\"', pass);\n process.stdin.destroy();\n});\n```\n\nPrompt for password with mask char \"*\":\n\n```js\nprogram.password('Password: ', '*', function(pass){\n console.log('got \"%s\"', pass);\n process.stdin.destroy();\n});\n```\n\n## .confirm(msg, fn)\n\n Confirm with the given `msg`:\n\n```js\nprogram.confirm('continue? ', function(ok){\n console.log(' got %j', ok);\n});\n```\n\n## .choose(list, fn)\n\n Let the user choose from a `list`:\n\n```js\nvar list = ['tobi', 'loki', 'jane', 'manny', 'luna'];\n\nconsole.log('Choose the coolest pet:');\nprogram.choose(list, function(i){\n console.log('you chose %d \"%s\"', i, list[i]);\n});\n```\n\n## Links\n\n - [API documentation](http://visionmedia.github.com/commander.js/)\n - [ascii tables](https://github.com/LearnBoost/cli-table)\n - [progress bars](https://github.com/visionmedia/node-progress)\n - [more progress bars](https://github.com/substack/node-multimeter)\n - [examples](https://github.com/visionmedia/commander.js/tree/master/examples)\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", + "_id": "commander@0.6.1", + "dist": { + "shasum": "ba9086c935fe769898d0417f9a8a0a19f37e85a2" + }, + "_from": "commander@~0.6.1" +} diff --git a/node_modules/ws/node_modules/nan/.index.js b/node_modules/ws/node_modules/nan/.index.js new file mode 100644 index 0000000..68da1f3 --- /dev/null +++ b/node_modules/ws/node_modules/nan/.index.js @@ -0,0 +1 @@ +//noop \ No newline at end of file diff --git a/node_modules/ws/node_modules/nan/LICENSE b/node_modules/ws/node_modules/nan/LICENSE new file mode 100644 index 0000000..352c287 --- /dev/null +++ b/node_modules/ws/node_modules/nan/LICENSE @@ -0,0 +1,43 @@ +Copyright 2013, NAN contributors: + - Rod Vagg + - Benjamin Byholm + - Trevor Norris +(the "Original Author") +All rights reserved. + +MIT +no-false-attribs License + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +Distributions of all or part of the Software intended to be used +by the recipients as they would use the unmodified Software, +containing modifications that substantially alter, remove, or +disable functionality of the Software, outside of the documented +configuration mechanisms provided by the Software, shall be +modified such that the Original Author's bug reporting email +addresses and urls are either replaced with the contact information +of the parties responsible for the changes, or removed entirely. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + + +Except where noted, this license applies to any and all software +programs and associated documentation files created by the +Original Author, when distributed with the Software. diff --git a/node_modules/ws/node_modules/nan/README.md b/node_modules/ws/node_modules/nan/README.md new file mode 100644 index 0000000..6ba57f7 --- /dev/null +++ b/node_modules/ws/node_modules/nan/README.md @@ -0,0 +1,705 @@ +Native Abstractions for Node.js +=============================== + +**A header file filled with macro and utility goodness for making addon development for Node.js easier across versions 0.8, 0.10 and 0.11, and eventually 0.12.** + +***Current version: 0.3.2*** *(See [nan.h](https://github.com/rvagg/nan/blob/master/nan.h) for changelog)* + +[![NPM](https://nodei.co/npm/nan.png?downloads=true&stars=true)](https://nodei.co/npm/nan/) [![NPM](https://nodei.co/npm-dl/nan.png?months=6)](https://nodei.co/npm/nan/) + +Thanks to the crazy changes in V8 (and some in Node core), keeping native addons compiling happily across versions, particularly 0.10 to 0.11/0.12, is a minor nightmare. The goal of this project is to store all logic necessary to develop native Node.js addons without having to inspect `NODE_MODULE_VERSION` and get yourself into a macro-tangle. + +This project also contains some helper utilities that make addon development a bit more pleasant. + + * **[Usage](#usage)** + * **[Example](#example)** + * **[API](#api)** + + +## Usage + +Simply add **NAN** as a dependency in the *package.json* of your Node addon: + +```js +"dependencies": { + ... + "nan" : "~0.3.1" + ... +} +``` + +Pull in the path to **NAN** in your *binding.gyp* so that you can use `#include "nan.h"` in your *.cpp*: + +```js +"include_dirs" : [ + ... + "` when compiling your addon. + + +## Example + +See **[LevelDOWN](https://github.com/rvagg/node-leveldown/pull/48)** for a full example of **NAN** in use. + +For a simpler example, see the **[async pi estimation example](https://github.com/rvagg/nan/tree/master/examples/async_pi_estimate)** in the examples directory for full code and an explanation of what this Monte Carlo Pi estimation example does. Below are just some parts of the full example that illustrate the use of **NAN**. + +Compare to the current 0.10 version of this example, found in the [node-addon-examples](https://github.com/rvagg/node-addon-examples/tree/master/9_async_work) repository and also a 0.11 version of the same found [here](https://github.com/kkoopa/node-addon-examples/tree/5c01f58fc993377a567812597e54a83af69686d7/9_async_work). + +Note that there is no embedded version sniffing going on here and also the async work is made much simpler, see below for details on the `NanAsyncWorker` class. + +```c++ +// addon.cc +#include +#include "nan.h" +// ... + +using namespace v8; + +void InitAll(Handle exports) { + exports->Set(NanSymbol("calculateSync"), + FunctionTemplate::New(CalculateSync)->GetFunction()); + + exports->Set(NanSymbol("calculateAsync"), + FunctionTemplate::New(CalculateAsync)->GetFunction()); +} + +NODE_MODULE(addon, InitAll) +``` + +```c++ +// sync.h +#include +#include "nan.h" + +NAN_METHOD(CalculateSync); +``` + +```c++ +// sync.cc +#include +#include "nan.h" +#include "sync.h" +// ... + +using namespace v8; + +// Simple synchronous access to the `Estimate()` function +NAN_METHOD(CalculateSync) { + NanScope(); + + // expect a number as the first argument + int points = args[0]->Uint32Value(); + double est = Estimate(points); + + NanReturnValue(Number::New(est)); +} +``` + +```c++ +// async.cc +#include +#include "nan.h" +#include "async.h" + +// ... + +using namespace v8; + +class PiWorker : public NanAsyncWorker { + public: + PiWorker(NanCallback *callback, int points) + : NanAsyncWorker(callback), points(points) {} + ~PiWorker() {} + + // Executed inside the worker-thread. + // It is not safe to access V8, or V8 data structures + // here, so everything we need for input and output + // should go on `this`. + void Execute () { + estimate = Estimate(points); + } + + // Executed when the async work is complete + // this function will be run inside the main event loop + // so it is safe to use V8 again + void HandleOKCallback () { + NanScope(); + + Local argv[] = { + Local::New(Null()) + , Number::New(estimate) + }; + + callback->Call(2, argv); + }; + + private: + int points; + double estimate; +}; + +// Asynchronous access to the `Estimate()` function +NAN_METHOD(CalculateAsync) { + NanScope(); + + int points = args[0]->Uint32Value(); + NanCallback *callback = new NanCallback(args[1].As()); + + NanAsyncQueueWorker(new PiWorker(callback, points)); + NanReturnUndefined(); +} +``` + + +## API + + * NAN_METHOD + * NAN_GETTER + * NAN_SETTER + * NAN_PROPERTY_GETTER + * NAN_PROPERTY_SETTER + * NAN_PROPERTY_ENUMERATOR + * NAN_PROPERTY_DELETER + * NAN_PROPERTY_QUERY + * NAN_WEAK_CALLBACK + * NanReturnValue + * NanReturnUndefined + * NanReturnNull + * NanReturnEmptyString + * NanScope + * NanLocker + * NanUnlocker + * NanGetInternalFieldPointer + * NanSetInternalFieldPointer + * NanObjectWrapHandle + * NanMakeWeak + * NanSymbol + * NanGetPointerSafe + * NanSetPointerSafe + * NanFromV8String + * NanBooleanOptionValue + * NanUInt32OptionValue + * NanThrowError, NanThrowTypeError, NanThrowRangeError, NanThrowError(Handle), NanThrowError(Handle, int) + * NanNewBufferHandle(char *, size_t, FreeCallback, void *), NanNewBufferHandle(char *, uint32_t), NanNewBufferHandle(uint32_t) + * NanBufferUse(char *, uint32_t) + * NanNewContextHandle + * NanHasInstance + * NanPersistentToLocal + * NanDispose + * NanAssignPersistent + * NanInitPersistent + * NanCallback + * NanAsyncWorker + * NanAsyncQueueWorker + + +### NAN_METHOD(methodname) + +Use `NAN_METHOD` to define your V8 accessible methods: + +```c++ +// .h: +class Foo : public node::ObjectWrap { + ... + + static NAN_METHOD(Bar); + static NAN_METHOD(Baz); +} + + +// .cc: +NAN_METHOD(Foo::Bar) { + ... +} + +NAN_METHOD(Foo::Baz) { + ... +} +``` + +The reason for this macro is because of the method signature change in 0.11: + +```c++ +// 0.10 and below: +Handle name(const Arguments& args) + +// 0.11 and above +void name(const FunctionCallbackInfo& args) +``` + +The introduction of `FunctionCallbackInfo` brings additional complications: + + +### NAN_GETTER(methodname) + +Use `NAN_GETTER` to declare your V8 accessible getters. You get a `Local` `property` and an appropriately typed `args` object that can act like the `args` argument to a `NAN_METHOD` call. + +You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_GETTER`. + + +### NAN_SETTER(methodname) + +Use `NAN_SETTER` to declare your V8 accessible setters. Same as `NAN_GETTER` but you also get a `Local` `value` object to work with. + +You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_SETTER`. + + +### NAN_PROPERTY_GETTER(cbname) +Use `NAN_PROPERTY_GETTER` to declare your V8 accessible property getters. You get a `Local` `property` and an appropriately typed `args` object that can act similar to the `args` argument to a `NAN_METHOD` call. + +You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_GETTER`. + + +### NAN_PROPERTY_SETTER(cbname) +Use `NAN_PROPERTY_SETTER` to declare your V8 accessible property setters. Same as `NAN_PROPERTY_GETTER` but you also get a `Local` `value` object to work with. + +You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_SETTER`. + + +### NAN_PROPERTY_ENUMERATOR(cbname) +Use `NAN_PROPERTY_ENUMERATOR` to declare your V8 accessible property enumerators. You get an appropriately typed `args` object like the `args` argument to a `NAN_PROPERTY_GETTER` call. + +You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_ENUMERATOR`. + + +### NAN_PROPERTY_DELETER(cbname) +Use `NAN_PROPERTY_DELETER` to declare your V8 accessible property deleters. Same as `NAN_PROPERTY_GETTER`. + +You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_DELETER`. + + +### NAN_PROPERTY_QUERY(cbname) +Use `NAN_PROPERTY_QUERY` to declare your V8 accessible property queries. Same as `NAN_PROPERTY_GETTER`. + +You can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_QUERY`. + + +### NAN_WEAK_CALLBACK(type, cbname) + +Use `NAN_WEAK_CALLBACK` to declare your V8 WeakReference callbacks. There is an object argument accessible through `NAN_WEAK_CALLBACK_OBJECT`. The `type` argument gives the type of the `data` argument, accessible through `NAN_WEAK_CALLBACK_DATA(type)`. + +```c++ +static NAN_WEAK_CALLBACK(BufferReference*, WeakCheck) { + if (NAN_WEAK_CALLBACK_DATA(BufferReference*)->noLongerNeeded_) { + delete NAN_WEAK_CALLBACK_DATA(BufferReference*); + } else { + // Still in use, revive, prevent GC + NanMakeWeak(NAN_WEAK_CALLBACK_OBJECT, NAN_WEAK_CALLBACK_DATA(BufferReference*), &WeakCheck); + } +} + +``` + +### NanReturnValue(Handle<Value>) + +Use `NanReturnValue` when you want to return a value from your V8 accessible method: + +```c++ +NAN_METHOD(Foo::Bar) { + ... + + NanReturnValue(String::New("FooBar!")); +} +``` + +No `return` statement required. + + +### NanReturnUndefined() + +Use `NanReturnUndefined` when you don't want to return anything from your V8 accessible method: + +```c++ +NAN_METHOD(Foo::Baz) { + ... + + NanReturnUndefined(); +} +``` + + +### NanReturnNull() + +Use `NanReturnNull` when you want to return `Null` from your V8 accessible method: + +```c++ +NAN_METHOD(Foo::Baz) { + ... + + NanReturnNull(); +} +``` + + +### NanReturnEmptyString() + +Use `NanReturnEmptyString` when you want to return an empty `String` from your V8 accessible method: + +```c++ +NAN_METHOD(Foo::Baz) { + ... + + NanReturnEmptyString(); +} +``` + + +### NanScope() + +The introduction of `isolate` references for many V8 calls in Node 0.11 makes `NanScope()` necessary, use it in place of `HandleScope scope`: + +```c++ +NAN_METHOD(Foo::Bar) { + NanScope(); + + NanReturnValue(String::New("FooBar!")); +} +``` + + +### NanLocker() + +The introduction of `isolate` references for many V8 calls in Node 0.11 makes `NanLocker()` necessary, use it in place of `Locker locker`: + +```c++ +NAN_METHOD(Foo::Bar) { + NanLocker(); + ... + NanUnlocker(); +} +``` + + +### NanUnlocker() + +The introduction of `isolate` references for many V8 calls in Node 0.11 makes `NanUnlocker()` necessary, use it in place of `Unlocker unlocker`: + +```c++ +NAN_METHOD(Foo::Bar) { + NanLocker(); + ... + NanUnlocker(); +} +``` + + +### void * NanGetInternalFieldPointer(Handle<Object>, int) + +Gets a pointer to the internal field with at `index` from a V8 `Object` handle. + +```c++ +Local obj; +... +NanGetInternalFieldPointer(obj, 0); +``` + +### void NanSetInternalFieldPointer(Handle<Object>, int, void *) + +Sets the value of the internal field at `index` on a V8 `Object` handle. + +```c++ +static Persistent dataWrapperCtor; +... +Local wrapper = NanPersistentToLocal(dataWrapperCtor)->NewInstance(); +NanSetInternalFieldPointer(wrapper, 0, this); +``` + + +### Local<Object> NanObjectWrapHandle(Object) + +When you want to fetch the V8 object handle from a native object you've wrapped with Node's `ObjectWrap`, you should use `NanObjectWrapHandle`: + +```c++ +NanObjectWrapHandle(iterator)->Get(String::NewSymbol("end")) +``` + + +### NanMakeWeak(Persistent<T>, parameter, callback) + +Make a persistent reference weak. + + +### String NanSymbol(char *) + +This isn't strictly about compatibility, it's just an easier way to create string symbol objects (i.e. `String::NewSymbol(x)`), for getting and setting object properties, or names of objects. + +```c++ +bool foo = false; +if (obj->Has(NanSymbol("foo"))) + foo = optionsObj->Get(NanSymbol("foo"))->BooleanValue() +``` + + +### Type NanGetPointerSafe(Type *[, Type]) + +A helper for getting values from optional pointers. If the pointer is `NULL`, the function returns the optional default value, which defaults to `0`. Otherwise, the function returns the value the pointer points to. + +```c++ +char *plugh(uint32_t *optional) { + char res[] = "xyzzy"; + uint32_t param = NanGetPointerSafe(optional, 0x1337); + switch (param) { + ... + } + NanSetPointerSafe(optional, 0xDEADBEEF); +} +``` + + +### bool NanSetPointerSafe(Type *, Type) + +A helper for setting optional argument pointers. If the pointer is `NULL`, the function simply return `false`. Otherwise, the value is assigned to the variable the pointer points to. + +```c++ +const char *plugh(size_t *outputsize) { + char res[] = "xyzzy"; + if !(NanSetPointerSafe(outputsize, strlen(res) + 1)) { + ... + } + + ... +} +``` + + +### char* NanFromV8String(Handle<Value>[, enum Nan::Encoding, size_t *, char *, size_t, int]) + +When you want to convert a V8 `String` to a `char*` use `NanFromV8String`. It is possible to define an encoding that defaults to `Nan::UTF8` as well as a pointer to a variable that will be assigned the number of bytes in the returned string. It is also possible to supply a buffer and its length to the function in order not to have a new buffer allocated. The final argument allows optionally setting `String::WriteOptions`, which default to `String::HINT_MANY_WRITES_EXPECTED | String::NO_NULL_TERMINATION`. +Just remember that you'll end up with an object that you'll need to `delete[]` at some point unless you supply your own buffer: + +```c++ +size_t count; +char* name = NanFromV8String(args[0]); +char* decoded = NanFromV8String(args[1], Nan::BASE64, &count, NULL, 0, String::HINT_MANY_WRITES_EXPECTED); +char param_copy[count]; +memcpy(param_copy, decoded, count); +delete[] decoded; +``` + + +### bool NanBooleanOptionValue(Handle<Value>, Handle<String>[, bool]) + +When you have an "options" object that you need to fetch properties from, boolean options can be fetched with this pair. They check first if the object exists (`IsEmpty`), then if the object has the given property (`Has`) then they get and convert/coerce the property to a `bool`. + +The optional last parameter is the *default* value, which is `false` if left off: + +```c++ +// `foo` is false unless the user supplies a truthy value for it +bool foo = NanBooleanOptionValue(optionsObj, NanSymbol("foo")); +// `bar` is true unless the user supplies a falsy value for it +bool bar = NanBooleanOptionValueDefTrue(optionsObj, NanSymbol("bar"), true); +``` + + +### uint32_t NanUInt32OptionValue(Handle<Value>, Handle<String>, uint32_t) + +Similar to `NanBooleanOptionValue`, use `NanUInt32OptionValue` to fetch an integer option from your options object. Can be any kind of JavaScript `Number` and it will be coerced to an unsigned 32-bit integer. + +Requires all 3 arguments as a default is not optional: + +```c++ +uint32_t count = NanUInt32OptionValue(optionsObj, NanSymbol("count"), 1024); +``` + + +### NanThrowError(message), NanThrowTypeError(message), NanThrowRangeError(message), NanThrowError(Local<Value>), NanThrowError(Local<Value>, int) + +For throwing `Error`, `TypeError` and `RangeError` objects. You should `return` this call: + +```c++ +return NanThrowError("you must supply a callback argument"); +``` + +Can also handle any custom object you may want to throw. If used with the error code argument, it will add the supplied error code to the error object as a property called `code`. + + +### Local<Object> NanNewBufferHandle(char *, uint32_t), Local<Object> NanNewBufferHandle(uint32_t) + +The `Buffer` API has changed a little in Node 0.11, this helper provides consistent access to `Buffer` creation: + +```c++ +NanNewBufferHandle((char*)value.data(), value.size()); +``` + +Can also be used to initialize a `Buffer` with just a `size` argument. + +Can also be supplied with a `NAN_WEAK_CALLBACK` and a hint for the garbage collector, when dealing with weak references. + + +### Local<Object> NanBufferUse(char*, uint32_t) + +`Buffer::New(char*, uint32_t)` prior to 0.11 would make a copy of the data. +While it was possible to get around this, it required a shim by passing a +callback. So the new API `Buffer::Use(char*, uint32_t)` was introduced to remove +needing to use this shim. + +`NanBufferUse` uses the `char*` passed as the backing data, and will free the +memory automatically when the weak callback is called. Keep this in mind, as +careless use can lead to "double free or corruption" and other cryptic failures. + + +### bool NanHasInstance(Persistent<FunctionTemplate>&, Handle<Value>) + +Can be used to check the type of an object to determine it is of a particular class you have already defined and have a `Persistent` handle for. + + +### Local<Type> NanPersistentToLocal(Persistent<Type>&) + +Aside from `FunctionCallbackInfo`, the biggest and most painful change to V8 in Node 0.11 is the many restrictions now placed on `Persistent` handles. They are difficult to assign and difficult to fetch the original value out of. + +Use `NanPersistentToLocal` to convert a `Persistent` handle back to a `Local` handle. + +```c++ +Local handle = NanPersistentToLocal(persistentHandle); +``` + + +### Local<Context> NanNewContextHandle([ExtensionConfiguration*, Handle<ObjectTemplate>, Handle<Value>]) +Creates a new `Local` handle. + +```c++ +Local ftmpl = FunctionTemplate::New(); +Local otmpl = ftmpl->InstanceTemplate(); +Local ctx = NanNewContextHandle(NULL, otmpl); +``` + + +### void NanDispose(Persistent<T> &) + +Use `NanDispose` to dispose a `Persistent` handle. + +```c++ +NanDispose(persistentHandle); +``` + + +### NanAssignPersistent(type, handle, object) + +Use `NanAssignPersistent` to assign a non-`Persistent` handle to a `Persistent` one. You can no longer just declare a `Persistent` handle and assign directly to it later, you have to `Reset` it in Node 0.11, so this makes it easier. + +In general it is now better to place anything you want to protect from V8's garbage collector as properties of a generic `Object` and then assign that to a `Persistent`. This works in older versions of Node also if you use `NanAssignPersistent`: + +```c++ +Persistent persistentHandle; + +... + +Local obj = Object::New(); +obj->Set(NanSymbol("key"), keyHandle); // where keyHandle might be a Local +NanAssignPersistent(Object, persistentHandle, obj) +``` + + +### NanInitPersistent(type, name, object) + +User `NanInitPersistent` to declare and initialize a new `Persistent` with the supplied object. The assignment operator for `Persistent` is no longer public in Node 0.11, so this macro makes it easier to declare and initializing a new `Persistent`. See NanAssignPersistent for more information. + +```c++ +Local obj = Object::New(); +obj->Set(NanSymbol("key"), keyHandle); // where keyHandle might be a Local +NanInitPersistent(Object, persistentHandle, obj); +``` + + +### NanCallback + +Because of the difficulties imposed by the changes to `Persistent` handles in V8 in Node 0.11, creating `Persistent` versions of your `Local` handles is annoyingly tricky. `NanCallback` makes it easier by taking your `Local` handle, making it persistent until the `NanCallback` is deleted and even providing a handy `Call()` method to fetch and execute the callback `Function`. + +```c++ +Local callbackHandle = callback = args[0].As(); +NanCallback *callback = new NanCallback(callbackHandle); +// pass `callback` around and it's safe from GC until you: +delete callback; +``` + +You can execute the callback like so: + +```c++ +// no arguments: +callback->Call(0, NULL); + +// an error argument: +Local argv[] = { + Exception::Error(String::New("fail!")) +}; +callback->Call(1, argv); + +// a success argument: +Local argv[] = { + Local::New(Null()), + String::New("w00t!") +}; +callback->Call(2, argv); +``` + +`NanCallback` also has a `Local GetCallback()` method that you can use to fetch a local handle to the underlying callback function if you need it. + + +### NanAsyncWorker + +`NanAsyncWorker` is an abstract class that you can subclass to have much of the annoying async queuing and handling taken care of for you. It can even store arbitrary V8 objects for you and have them persist while the async work is in progress. + +See a rough outline of the implementation: + +```c++ +class NanAsyncWorker { +public: + NanAsyncWorker (NanCallback *callback); + + // Clean up persistent handles and delete the *callback + virtual ~NanAsyncWorker (); + + // Check the `char *errmsg` property and call HandleOKCallback() + // or HandleErrorCallback depending on whether it has been set or not + virtual void WorkComplete (); + + // You must implement this to do some async work. If there is an + // error then allocate `errmsg` to to a message and the callback will + // be passed that string in an Error object + virtual void Execute (); + +protected: + // Set this if there is an error, otherwise it's NULL + const char *errmsg; + + // Save a V8 object in a Persistent handle to protect it from GC + void SavePersistent(const char *key, Local &obj); + + // Fetch a stored V8 object (don't call from within `Execute()`) + Local GetFromPersistent(const char *key); + + // Default implementation calls the callback function with no arguments. + // Override this to return meaningful data + virtual void HandleOKCallback (); + + // Default implementation calls the callback function with an Error object + // wrapping the `errmsg` string + virtual void HandleErrorCallback (); +}; +``` + + +### NanAsyncQueueWorker(NanAsyncWorker *) + +`NanAsyncQueueWorker` will run a `NanAsyncWorker` asynchronously via libuv. Both the *execute* and *after_work* steps are taken care of for you—most of the logic for this is embedded in `NanAsyncWorker`. + +### Contributors + +NAN is only possible due to the excellent work of the following contributors: + + + + + +
Rod VaggGitHub/rvaggTwitter/@rvagg
Benjamin ByholmGitHub/kkoopa
Trevor NorrisGitHub/trevnorrisTwitter/@trevnorris
+ +Licence & copyright +----------------------- + +Copyright (c) 2013 Rod Vagg & NAN contributors (listed above). + +Native Abstractions for Node.js is licensed under an MIT +no-false-attribs license. All rights not explicitly granted in the MIT license are reserved. See the included LICENSE file for more details. diff --git a/node_modules/ws/node_modules/nan/nan.h b/node_modules/ws/node_modules/nan/nan.h new file mode 100644 index 0000000..b3eb02d --- /dev/null +++ b/node_modules/ws/node_modules/nan/nan.h @@ -0,0 +1,884 @@ +/********************************************************************************** + * NAN - Native Abstractions for Node.js + * + * Copyright (c) 2013 NAN contributors: + * - Rod Vagg + * - Benjamin Byholm + * - Trevor Norris + * + * MIT +no-false-attribs License + * + * Version 0.3.2 (current Node unstable: 0.11.6, Node stable: 0.10.17) + * + * ChangeLog: + * * 0.3.2 Aug 30 2013 + * - Fix missing scope declaration in GetFromPersistent() and SaveToPersistent + * in NanAsyncWorker + * + * * 0.3.1 Aug 20 2013 + * - fix "not all control paths return a value" compile warning on some platforms + * + * * 0.3.0 Aug 19 2013 + * - Made NAN work with NPM + * - Lots of fixes to NanFromV8String, pulling in features from new Node core + * - Changed node::encoding to Nan::Encoding in NanFromV8String to unify the API + * - Added optional error number argument for NanThrowError() + * - Added NanInitPersistent() + * - Added NanReturnNull() and NanReturnEmptyString() + * - Added NanLocker and NanUnlocker + * - Added missing scopes + * - Made sure to clear disposed Persistent handles + * - Changed NanAsyncWorker to allocate error messages on the heap + * - Changed NanThrowError(Local) to NanThrowError(Handle) + * - Fixed leak in NanAsyncWorker when errmsg is used + * + * * 0.2.2 Aug 5 2013 + * - Fixed usage of undefined variable with node::BASE64 in NanFromV8String() + * + * * 0.2.1 Aug 5 2013 + * - Fixed 0.8 breakage, node::BUFFER encoding type not available in 0.8 for + * NanFromV8String() + * + * * 0.2.0 Aug 5 2013 + * - Added NAN_PROPERTY_GETTER, NAN_PROPERTY_SETTER, NAN_PROPERTY_ENUMERATOR, + * NAN_PROPERTY_DELETER, NAN_PROPERTY_QUERY + * - Extracted _NAN_METHOD_ARGS, _NAN_GETTER_ARGS, _NAN_SETTER_ARGS, + * _NAN_PROPERTY_GETTER_ARGS, _NAN_PROPERTY_SETTER_ARGS, + * _NAN_PROPERTY_ENUMERATOR_ARGS, _NAN_PROPERTY_DELETER_ARGS, + * _NAN_PROPERTY_QUERY_ARGS + * - Added NanGetInternalFieldPointer, NanSetInternalFieldPointer + * - Added NAN_WEAK_CALLBACK, NAN_WEAK_CALLBACK_OBJECT, + * NAN_WEAK_CALLBACK_DATA, NanMakeWeak + * - Renamed THROW_ERROR to _NAN_THROW_ERROR + * - Added NanNewBufferHandle(char*, size_t, node::smalloc::FreeCallback, void*) + * - Added NanBufferUse(char*, uint32_t) + * - Added NanNewContextHandle(v8::ExtensionConfiguration*, + * v8::Handle, v8::Handle) + * - Fixed broken NanCallback#GetFunction() + * - Added optional encoding and size arguments to NanFromV8String() + * - Added NanGetPointerSafe() and NanSetPointerSafe() + * - Added initial test suite (to be expanded) + * - Allow NanUInt32OptionValue to convert any Number object + * + * * 0.1.0 Jul 21 2013 + * - Added `NAN_GETTER`, `NAN_SETTER` + * - Added `NanThrowError` with single Local argument + * - Added `NanNewBufferHandle` with single uint32_t argument + * - Added `NanHasInstance(Persistent&, Handle)` + * - Added `Local NanCallback#GetFunction()` + * - Added `NanCallback#Call(int, Local[])` + * - Deprecated `NanCallback#Run(int, Local[])` in favour of Call + * + * See https://github.com/rvagg/nan for the latest update to this file + **********************************************************************************/ + +#ifndef NAN_H +#define NAN_H + +#include +#include +#include + +// some generic helpers + +template static inline bool NanSetPointerSafe(T *var, T val) { + if (var) { + *var = val; + return true; + } else { + return false; + } +} + +template static inline T NanGetPointerSafe( + T *var, + T fallback = reinterpret_cast(0)) { + if (var) { + return *var; + } else { + return fallback; + } +} + +#define NanSymbol(value) v8::String::NewSymbol(value) + +static inline bool NanBooleanOptionValue( + v8::Local optionsObj + , v8::Handle opt, bool def) { + + if (def) { + return optionsObj.IsEmpty() + || !optionsObj->Has(opt) + || optionsObj->Get(opt)->BooleanValue(); + } else { + return !optionsObj.IsEmpty() + && optionsObj->Has(opt) + && optionsObj->Get(opt)->BooleanValue(); + } +} + +static inline bool NanBooleanOptionValue( + v8::Local optionsObj + , v8::Handle opt) { + return NanBooleanOptionValue(optionsObj, opt, false); +} + +static inline uint32_t NanUInt32OptionValue( + v8::Local optionsObj + , v8::Handle opt + , uint32_t def) { + + return !optionsObj.IsEmpty() + && optionsObj->Has(opt) + && optionsObj->Get(opt)->IsNumber() + ? optionsObj->Get(opt)->Uint32Value() + : def; +} + +#if (NODE_MODULE_VERSION > 0x000B) +// Node 0.11+ (0.11.3 and below won't compile with these) + +static v8::Isolate* nan_isolate = v8::Isolate::GetCurrent(); + +# define _NAN_METHOD_ARGS const v8::FunctionCallbackInfo& args +# define NAN_METHOD(name) void name(_NAN_METHOD_ARGS) +# define _NAN_GETTER_ARGS const v8::PropertyCallbackInfo& args +# define NAN_GETTER(name) \ + void name(v8::Local property, _NAN_GETTER_ARGS) +# define _NAN_SETTER_ARGS const v8::PropertyCallbackInfo& args +# define NAN_SETTER(name) \ + void name( \ + v8::Local property \ + , v8::Local value \ + , _NAN_SETTER_ARGS) +# define _NAN_PROPERTY_GETTER_ARGS \ + const v8::PropertyCallbackInfo& args +# define NAN_PROPERTY_GETTER(name) \ + void name(v8::Local property \ + , _NAN_PROPERTY_GETTER_ARGS) +# define _NAN_PROPERTY_SETTER_ARGS \ + const v8::PropertyCallbackInfo& args +# define NAN_PROPERTY_SETTER(name) \ + void name(v8::Local property \ + , v8::Local value \ + , _NAN_PROPERTY_SETTER_ARGS) +# define _NAN_PROPERTY_ENUMERATOR_ARGS \ + const v8::PropertyCallbackInfo& args +# define NAN_PROPERTY_ENUMERATOR(name) \ + void name(_NAN_PROPERTY_ENUMERATOR_ARGS) +# define _NAN_PROPERTY_DELETER_ARGS \ + const v8::PropertyCallbackInfo& args +# define NAN_PROPERTY_DELETER(name) \ + void name( \ + v8::Local property \ + , _NAN_PROPERTY_DELETER_ARGS) +# define _NAN_PROPERTY_QUERY_ARGS \ + const v8::PropertyCallbackInfo& args +# define NAN_PROPERTY_QUERY(name) \ + void name(v8::Local property, _NAN_PROPERTY_QUERY_ARGS) +# define NanGetInternalFieldPointer(object, index) \ + object->GetAlignedPointerFromInternalField(index) +# define NanSetInternalFieldPointer(object, index, value) \ + object->SetAlignedPointerInInternalField(index, value) + +# define NAN_WEAK_CALLBACK(type, name) \ + void name( \ + v8::Isolate* isolate, \ + v8::Persistent* object, \ + type data) +# define NAN_WEAK_CALLBACK_OBJECT (*object) +# define NAN_WEAK_CALLBACK_DATA(type) ((type) data) + +# define NanScope() v8::HandleScope scope(nan_isolate) +# define NanLocker() v8::Locker locker(nan_isolate) +# define NanUnlocker() v8::Unlocker unlocker(nan_isolate) +# define NanReturnValue(value) return args.GetReturnValue().Set(value) +# define NanReturnUndefined() return +# define NanReturnNull() return args.GetReturnValue().SetNull() +# define NanReturnEmptyString() return args.GetReturnValue().SetEmptyString() +# define NanAssignPersistent(type, handle, obj) handle.Reset(nan_isolate, obj) +# define NanInitPersistent(type, name, obj) \ + v8::Persistent name(nan_isolate, obj) +# define NanObjectWrapHandle(obj) obj->handle() +# define NanMakeWeak(handle, parameter, callback) \ + handle.MakeWeak(nan_isolate, parameter, callback) + +# define _NAN_THROW_ERROR(fun, errmsg) \ + do { \ + NanScope(); \ + v8::ThrowException(fun(v8::String::New(errmsg))); \ + } while (0); + + inline static void NanThrowError(const char* errmsg) { + _NAN_THROW_ERROR(v8::Exception::Error, errmsg); + } + + inline static void NanThrowError(v8::Handle error) { + NanScope(); + v8::ThrowException(error); + } + + inline static void NanThrowError(const char *msg, const int errorNumber) { + v8::Local err = v8::Exception::Error(v8::String::New(msg)); + v8::Local obj = err.As(); + obj->Set(v8::String::New("code"), v8::Int32::New(errorNumber)); + NanThrowError(err); + } + + inline static void NanThrowTypeError(const char* errmsg) { + _NAN_THROW_ERROR(v8::Exception::TypeError, errmsg); + } + + inline static void NanThrowRangeError(const char* errmsg) { + _NAN_THROW_ERROR(v8::Exception::RangeError, errmsg); + } + + template static inline void NanDispose(v8::Persistent &handle) { + handle.Dispose(nan_isolate); + handle.Clear(); + } + + static inline v8::Local NanNewBufferHandle ( + char *data, + size_t length, + node::smalloc::FreeCallback callback, + void *hint) { + return node::Buffer::New(data, length, callback, hint); + } + + static inline v8::Local NanNewBufferHandle ( + char *data, uint32_t size) { + return node::Buffer::New(data, size); + } + + static inline v8::Local NanNewBufferHandle (uint32_t size) { + return node::Buffer::New(size); + } + + static inline v8::Local NanBufferUse(char* data, uint32_t size) { + return node::Buffer::Use(data, size); + } + + template + inline v8::Local NanPersistentToLocal( + const v8::Persistent& persistent) { + if (persistent.IsWeak()) { + return v8::Local::New(nan_isolate, persistent); + } else { + return *reinterpret_cast*>( + const_cast*>(&persistent)); + } + } + + inline bool NanHasInstance( + v8::Persistent& function_template + , v8::Handle value) { + return NanPersistentToLocal(function_template)->HasInstance(value); + } + + static inline v8::Local NanNewContextHandle( + v8::ExtensionConfiguration* extensions = NULL, + v8::Handle tmpl = v8::Handle(), + v8::Handle obj = v8::Handle()) { + return v8::Local::New(nan_isolate, v8::Context::New( + nan_isolate, extensions, tmpl, obj)); + } + +#else +// Node 0.8 and 0.10 + +# define _NAN_METHOD_ARGS const v8::Arguments& args +# define NAN_METHOD(name) v8::Handle name(_NAN_METHOD_ARGS) +# define _NAN_GETTER_ARGS const v8::AccessorInfo &args +# define NAN_GETTER(name) \ + v8::Handle name(v8::Local property, _NAN_GETTER_ARGS) +# define _NAN_SETTER_ARGS const v8::AccessorInfo &args +# define NAN_SETTER(name) \ + void name( \ + v8::Local property \ + , v8::Local value \ + , _NAN_SETTER_ARGS) +# define _NAN_PROPERTY_GETTER_ARGS const v8::AccessorInfo& args +# define NAN_PROPERTY_GETTER(name) \ + v8::Handle name(v8::Local property \ + , _NAN_PROPERTY_GETTER_ARGS) +# define _NAN_PROPERTY_SETTER_ARGS const v8::AccessorInfo& args +# define NAN_PROPERTY_SETTER(name) \ + v8::Handle name(v8::Local property \ + , v8::Local value \ + , _NAN_PROPERTY_SETTER_ARGS) +# define _NAN_PROPERTY_ENUMERATOR_ARGS const v8::AccessorInfo& args +# define NAN_PROPERTY_ENUMERATOR(name) \ + v8::Handle name(_NAN_PROPERTY_ENUMERATOR_ARGS) +# define _NAN_PROPERTY_DELETER_ARGS const v8::AccessorInfo& args +# define NAN_PROPERTY_DELETER(name) \ + v8::Handle name( \ + v8::Local property \ + , _NAN_PROPERTY_DELETER_ARGS) +# define _NAN_PROPERTY_QUERY_ARGS const v8::AccessorInfo& args +# define NAN_PROPERTY_QUERY(name) \ + v8::Handle name( \ + v8::Local property \ + , _NAN_PROPERTY_QUERY_ARGS) + +# define NanGetInternalFieldPointer(object, index) \ + object->GetPointerFromInternalField(index) +# define NanSetInternalFieldPointer(object, index, value) \ + object->SetPointerInInternalField(index, value) +# define NAN_WEAK_CALLBACK(type, name) void name( \ + v8::Persistent object, \ + void *data) +# define NAN_WEAK_CALLBACK_OBJECT object +# define NAN_WEAK_CALLBACK_DATA(type) ((type) data) + +# define NanScope() v8::HandleScope scope +# define NanLocker() v8::Locker locker +# define NanUnlocker() v8::Unlocker unlocker +# define NanReturnValue(value) return scope.Close(value) +# define NanReturnUndefined() return v8::Undefined() +# define NanReturnNull() return v8::Null() +# define NanReturnEmptyString() return v8::String::Empty() +# define NanInitPersistent(type, name, obj) \ + v8::Persistent name = v8::Persistent::New(obj) +# define NanAssignPersistent(type, handle, obj) \ + handle = v8::Persistent::New(obj) +# define NanObjectWrapHandle(obj) obj->handle_ +# define NanMakeWeak(handle, parameters, callback) \ + handle.MakeWeak(parameters, callback) + +# define _NAN_THROW_ERROR(fun, errmsg) \ + do { \ + NanScope(); \ + return v8::ThrowException(fun(v8::String::New(errmsg))); \ + } while (0); + + inline static v8::Handle NanThrowError(const char* errmsg) { + _NAN_THROW_ERROR(v8::Exception::Error, errmsg); + } + + inline static v8::Handle NanThrowError( + v8::Handle error) { + NanScope(); + return v8::ThrowException(error); + } + + inline static v8::Handle NanThrowError( + const char *msg, + const int errorNumber) { + v8::Local err = v8::Exception::Error(v8::String::New(msg)); + v8::Local obj = err.As(); + obj->Set(v8::String::New("code"), v8::Int32::New(errorNumber)); + return NanThrowError(err); + } + + inline static v8::Handle NanThrowTypeError(const char* errmsg) { + _NAN_THROW_ERROR(v8::Exception::TypeError, errmsg); + } + + inline static v8::Handle NanThrowRangeError(const char* errmsg) { + _NAN_THROW_ERROR(v8::Exception::RangeError, errmsg); + } + + template static inline void NanDispose(v8::Persistent &handle) { + handle.Dispose(); + handle.Clear(); + } + + static inline v8::Local NanNewBufferHandle ( + char *data, + size_t length, + node::Buffer::free_callback callback, + void *hint) { + return v8::Local::New( + node::Buffer::New(data, length, callback, hint)->handle_); + } + + static inline v8::Local NanNewBufferHandle ( + char *data, uint32_t size) { + return v8::Local::New(node::Buffer::New(data, size)->handle_); + } + + static inline v8::Local NanNewBufferHandle (uint32_t size) { + return v8::Local::New(node::Buffer::New(size)->handle_); + } + + static inline void FreeData(char *data, void *hint) { + delete[] data; + } + + static inline v8::Local NanBufferUse(char* data, uint32_t size) { + return v8::Local::New( + node::Buffer::New(data, size, FreeData, NULL)->handle_); + } + + template + inline v8::Local NanPersistentToLocal( + const v8::Persistent& persistent) { + if (persistent.IsWeak()) { + return v8::Local::New(persistent); + } else { + return *reinterpret_cast*>( + const_cast*>(&persistent)); + } + } + + inline bool NanHasInstance( + v8::Persistent& function_template + , v8::Handle value) { + return function_template->HasInstance(value); + } + + static inline v8::Local NanNewContextHandle( + v8::ExtensionConfiguration* extensions = NULL + , v8::Handle tmpl = + v8::Handle() + , v8::Handle obj = v8::Handle() + ) { + v8::Persistent ctx = + v8::Context::New(extensions, tmpl, obj); + v8::Local lctx = v8::Local::New(ctx); + ctx.Dispose(); + return lctx; + } + +#endif // node version + +class NanCallback { + public: + NanCallback(const v8::Local &fn) { + NanScope(); + v8::Local obj = v8::Object::New(); + obj->Set(NanSymbol("callback"), fn); + NanAssignPersistent(v8::Object, handle, obj); + } + + ~NanCallback() { + if (handle.IsEmpty()) return; + handle.Dispose(); + handle.Clear(); + } + + inline v8::Local GetFunction () { + return NanPersistentToLocal(handle)->Get(NanSymbol("callback")) + .As(); + } + + // deprecated + void Run(int argc, v8::Local argv[]) { + Call(argc, argv); + } + + void Call(int argc, v8::Local argv[]) { + NanScope(); + + v8::Local callback = NanPersistentToLocal(handle)-> + Get(NanSymbol("callback")).As(); + v8::TryCatch try_catch; + callback->Call(v8::Context::GetCurrent()->Global(), argc, argv); + if (try_catch.HasCaught()) { + node::FatalException(try_catch); + } + } + + private: + v8::Persistent handle; +}; + +/* abstract */ class NanAsyncWorker { +public: + NanAsyncWorker (NanCallback *callback) : callback(callback) { + request.data = this; + errmsg = NULL; + } + + virtual ~NanAsyncWorker () { + NanScope(); + + if (!persistentHandle.IsEmpty()) + NanDispose(persistentHandle); + if (callback) + delete callback; + if (errmsg) + delete errmsg; + } + + virtual void WorkComplete () { + NanScope(); + + if (errmsg == NULL) + HandleOKCallback(); + else + HandleErrorCallback(); + delete callback; + callback = NULL; + } + + virtual void Execute () =0; + + uv_work_t request; + +protected: + v8::Persistent persistentHandle; + NanCallback *callback; + const char *errmsg; + + void SavePersistent(const char *key, v8::Local &obj) { + NanScope(); + + v8::Local handle = NanPersistentToLocal(persistentHandle); + handle->Set(NanSymbol(key), obj); + } + + v8::Local GetFromPersistent(const char *key) { + NanScope(); + + v8::Local handle = NanPersistentToLocal(persistentHandle); + return handle->Get(NanSymbol(key)).As(); + } + + virtual void HandleOKCallback () { + NanScope(); + + callback->Call(0, NULL); + }; + + virtual void HandleErrorCallback () { + NanScope(); + + v8::Local argv[] = { + v8::Exception::Error(v8::String::New(errmsg)) + }; + callback->Call(1, argv); + } +}; + +inline void NanAsyncExecute (uv_work_t* req) { + NanAsyncWorker *worker = static_cast(req->data); + worker->Execute(); +} + +inline void NanAsyncExecuteComplete (uv_work_t* req) { + NanAsyncWorker* worker = static_cast(req->data); + worker->WorkComplete(); + delete worker; +} + +inline void NanAsyncQueueWorker (NanAsyncWorker* worker) { + uv_queue_work( + uv_default_loop() + , &worker->request + , NanAsyncExecute + , (uv_after_work_cb)NanAsyncExecuteComplete + ); +} + +//// Base 64 //// + +#define _nan_base64_encoded_size(size) ((size + 2 - ((size + 2) % 3)) / 3 * 4) + + +// Doesn't check for padding at the end. Can be 1-2 bytes over. +static inline size_t _nan_base64_decoded_size_fast(size_t size) { + size_t remainder = size % 4; + + size = (size / 4) * 3; + if (remainder) { + if (size == 0 && remainder == 1) { + // special case: 1-byte input cannot be decoded + size = 0; + } else { + // non-padded input, add 1 or 2 extra bytes + size += 1 + (remainder == 3); + } + } + + return size; +} + +template +static size_t _nan_base64_decoded_size(const TypeName* src, size_t size) { + if (size == 0) + return 0; + + if (src[size - 1] == '=') + size--; + if (size > 0 && src[size - 1] == '=') + size--; + + return _nan_base64_decoded_size_fast(size); +} + + +// supports regular and URL-safe base64 +static const int _nan_unbase64_table[] = + { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -1, -1, -2, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + +#define _nan_unbase64(x) _nan_unbase64_table[(uint8_t)(x)] + + +template +static size_t _nan_base64_decode(char* buf, + size_t len, + const TypeName* src, + const size_t srcLen) { + char a, b, c, d; + char* dst = buf; + char* dstEnd = buf + len; + const TypeName* srcEnd = src + srcLen; + + while (src < srcEnd && dst < dstEnd) { + int remaining = srcEnd - src; + + while (_nan_unbase64(*src) < 0 && src < srcEnd) src++, remaining--; + if (remaining == 0 || *src == '=') break; + a = _nan_unbase64(*src++); + + while (_nan_unbase64(*src) < 0 && src < srcEnd) src++, remaining--; + if (remaining <= 1 || *src == '=') break; + b = _nan_unbase64(*src++); + + *dst++ = (a << 2) | ((b & 0x30) >> 4); + if (dst == dstEnd) break; + + while (_nan_unbase64(*src) < 0 && src < srcEnd) src++, remaining--; + if (remaining <= 2 || *src == '=') break; + c = _nan_unbase64(*src++); + + *dst++ = ((b & 0x0F) << 4) | ((c & 0x3C) >> 2); + if (dst == dstEnd) break; + + while (_nan_unbase64(*src) < 0 && src < srcEnd) src++, remaining--; + if (remaining <= 3 || *src == '=') break; + d = _nan_unbase64(*src++); + + *dst++ = ((c & 0x03) << 6) | (d & 0x3F); + } + + return dst - buf; +} + +//// HEX //// + +template +unsigned _nan_hex2bin(TypeName c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'A' && c <= 'F') return 10 + (c - 'A'); + if (c >= 'a' && c <= 'f') return 10 + (c - 'a'); + return static_cast(-1); +} + + +template +static size_t _nan_hex_decode(char* buf, + size_t len, + const TypeName* src, + const size_t srcLen) { + size_t i; + for (i = 0; i < len && i * 2 + 1 < srcLen; ++i) { + unsigned a = _nan_hex2bin(src[i * 2 + 0]); + unsigned b = _nan_hex2bin(src[i * 2 + 1]); + if (!~a || !~b) return i; + buf[i] = a * 16 + b; + } + + return i; +} + +static bool _NanGetExternalParts( + v8::Handle val + , const char** data + , size_t* len) { + + if (node::Buffer::HasInstance(val)) { + *data = node::Buffer::Data(val.As()); + *len = node::Buffer::Length(val.As()); + return true; + + } + + assert(val->IsString()); + v8::Local str = v8::Local::New(val.As()); + + if (str->IsExternalAscii()) { + const v8::String::ExternalAsciiStringResource* ext; + ext = str->GetExternalAsciiStringResource(); + *data = ext->data(); + *len = ext->length(); + return true; + + } else if (str->IsExternal()) { + const v8::String::ExternalStringResource* ext; + ext = str->GetExternalStringResource(); + *data = reinterpret_cast(ext->data()); + *len = ext->length(); + return true; + } + + return false; +} + +namespace Nan { + enum Encoding {ASCII, UTF8, BASE64, UCS2, BINARY, HEX, BUFFER}; +} + +static inline char* NanFromV8String( + v8::Handle from + , enum Nan::Encoding encoding = Nan::UTF8 + , size_t *datalen = NULL + , char *buf = NULL + , size_t buflen = 0 + , int flags = v8::String::NO_NULL_TERMINATION + | v8::String::HINT_MANY_WRITES_EXPECTED) { + + NanScope(); + + size_t sz_; + size_t term_len = !(flags & v8::String::NO_NULL_TERMINATION); + char *data = NULL; + size_t len; + bool is_extern = _NanGetExternalParts( + from + , const_cast(&data) + , &len); + + if (is_extern && !term_len) { + NanSetPointerSafe(datalen, len); + return data; + } + + v8::Local toStr = from->ToString(); + + char *to = buf; + + v8::String::AsciiValue value(toStr); + switch(encoding) { + case Nan::ASCII: +#if NODE_MODULE_VERSION < 0x0C + sz_ = toStr->Length(); + if (to == NULL) { + to = new char[sz_ + term_len]; + } else { + assert(buflen >= sz_ + term_len && "too small buffer"); + } + NanSetPointerSafe( + datalen + , toStr->WriteAscii(to, 0, sz_ + term_len, flags)); + return to; +#endif + case Nan::BINARY: + case Nan::BUFFER: + sz_ = toStr->Length(); + if (to == NULL) { + to = new char[sz_ + term_len]; + } else { + assert(buflen >= sz_ + term_len && "too small buffer"); + } +#if NODE_MODULE_VERSION < 0x0C + // TODO(isaacs): THIS IS AWFUL!!! + // AGREE(kkoopa) + { + uint16_t* twobytebuf = new uint16_t[sz_ + term_len]; + + size_t len = toStr->Write(twobytebuf, 0, sz_ + term_len, flags); + + for (size_t i = 0; i < sz_ + term_len && i < len + term_len; i++) { + unsigned char *b = reinterpret_cast(&twobytebuf[i]); + to[i] = *b; + } + + NanSetPointerSafe(datalen, len); + + delete[] twobytebuf; + return to; + } +#else + NanSetPointerSafe( + datalen, + toStr->WriteOneByte( + reinterpret_cast(to) + , 0 + , sz_ + term_len + , flags)); + return to; +#endif + case Nan::UTF8: + sz_ = toStr->Utf8Length(); + if (to == NULL) { + to = new char[sz_ + term_len]; + } else { + assert(buflen >= sz_ + term_len && "too small buffer"); + } + NanSetPointerSafe( + datalen + , toStr->WriteUtf8(to, sz_ + term_len, NULL, flags) - term_len); + return to; + case Nan::BASE64: + sz_ = _nan_base64_decoded_size(*value, toStr->Length()); + if (to == NULL) { + to = new char[sz_ + term_len]; + } else { + assert(buflen >= sz_ + term_len); + } + NanSetPointerSafe( + datalen + , _nan_base64_decode(to, sz_, *value, value.length())); + if (term_len) { + to[sz_] = '\0'; + } + return to; + case Nan::UCS2: + { + sz_ = toStr->Length(); + if (to == NULL) { + to = new char[(sz_ + term_len) * 2]; + } else { + assert(buflen >= (sz_ + term_len) * 2 && "too small buffer"); + } + + int bc = 2 * toStr->Write( + reinterpret_cast(to) + , 0 + , sz_ + term_len + , flags); + NanSetPointerSafe(datalen, bc); + return to; + } + case Nan::HEX: + sz_ = toStr->Length(); + assert(!(sz_ & 1) && "bad hex data"); + if (to == NULL) { + to = new char[sz_ / 2 + term_len]; + } else { + assert(buflen >= sz_ / 2 + term_len && "too small buffer"); + } + + NanSetPointerSafe( + datalen + , _nan_hex_decode(to, sz_ / 2, *value, value.length())); + if (term_len) { + to[sz_ / 2] = '\0'; + } + return to; + default: + assert(0 && "unknown encoding"); + } + return to; +} + +#endif diff --git a/node_modules/ws/node_modules/nan/package.json b/node_modules/ws/node_modules/nan/package.json new file mode 100644 index 0000000..af7bf04 --- /dev/null +++ b/node_modules/ws/node_modules/nan/package.json @@ -0,0 +1,34 @@ +{ + "name": "nan", + "version": "0.3.2", + "description": "Native Abstractions for Node.js: C++ header for Node 0.8->0.12 compatibility", + "main": ".index.js", + "repository": { + "type": "git", + "url": "git://github.com/rvagg/nan.git" + }, + "contributors": [ + { + "name": "Rod Vagg", + "email": "r@va.gg", + "url": "https://github.com/rvagg" + }, + { + "name": "Benjamin Byholm", + "email": "bbyholm@abo.fi", + "url": "https://github.com/kkoopa/" + }, + { + "name": "Trevor Norris", + "email": "trev.norris@gmail.com", + "url": "https://github.com/trevnorris" + } + ], + "license": "MIT", + "readme": "Native Abstractions for Node.js\n===============================\n\n**A header file filled with macro and utility goodness for making addon development for Node.js easier across versions 0.8, 0.10 and 0.11, and eventually 0.12.**\n\n***Current version: 0.3.2*** *(See [nan.h](https://github.com/rvagg/nan/blob/master/nan.h) for changelog)*\n\n[![NPM](https://nodei.co/npm/nan.png?downloads=true&stars=true)](https://nodei.co/npm/nan/) [![NPM](https://nodei.co/npm-dl/nan.png?months=6)](https://nodei.co/npm/nan/)\n\nThanks to the crazy changes in V8 (and some in Node core), keeping native addons compiling happily across versions, particularly 0.10 to 0.11/0.12, is a minor nightmare. The goal of this project is to store all logic necessary to develop native Node.js addons without having to inspect `NODE_MODULE_VERSION` and get yourself into a macro-tangle.\n\nThis project also contains some helper utilities that make addon development a bit more pleasant.\n\n * **[Usage](#usage)**\n * **[Example](#example)**\n * **[API](#api)**\n\n\n## Usage\n\nSimply add **NAN** as a dependency in the *package.json* of your Node addon:\n\n```js\n\"dependencies\": {\n ...\n \"nan\" : \"~0.3.1\"\n ...\n}\n```\n\nPull in the path to **NAN** in your *binding.gyp* so that you can use `#include \"nan.h\"` in your *.cpp*:\n\n```js\n\"include_dirs\" : [\n ...\n \"` when compiling your addon.\n\n\n## Example\n\nSee **[LevelDOWN](https://github.com/rvagg/node-leveldown/pull/48)** for a full example of **NAN** in use.\n\nFor a simpler example, see the **[async pi estimation example](https://github.com/rvagg/nan/tree/master/examples/async_pi_estimate)** in the examples directory for full code and an explanation of what this Monte Carlo Pi estimation example does. Below are just some parts of the full example that illustrate the use of **NAN**.\n\nCompare to the current 0.10 version of this example, found in the [node-addon-examples](https://github.com/rvagg/node-addon-examples/tree/master/9_async_work) repository and also a 0.11 version of the same found [here](https://github.com/kkoopa/node-addon-examples/tree/5c01f58fc993377a567812597e54a83af69686d7/9_async_work).\n\nNote that there is no embedded version sniffing going on here and also the async work is made much simpler, see below for details on the `NanAsyncWorker` class.\n\n```c++\n// addon.cc\n#include \n#include \"nan.h\"\n// ...\n\nusing namespace v8;\n\nvoid InitAll(Handle exports) {\n exports->Set(NanSymbol(\"calculateSync\"),\n FunctionTemplate::New(CalculateSync)->GetFunction());\n\n exports->Set(NanSymbol(\"calculateAsync\"),\n FunctionTemplate::New(CalculateAsync)->GetFunction());\n}\n\nNODE_MODULE(addon, InitAll)\n```\n\n```c++\n// sync.h\n#include \n#include \"nan.h\"\n\nNAN_METHOD(CalculateSync);\n```\n\n```c++\n// sync.cc\n#include \n#include \"nan.h\"\n#include \"sync.h\"\n// ...\n\nusing namespace v8;\n\n// Simple synchronous access to the `Estimate()` function\nNAN_METHOD(CalculateSync) {\n NanScope();\n\n // expect a number as the first argument\n int points = args[0]->Uint32Value();\n double est = Estimate(points);\n\n NanReturnValue(Number::New(est));\n}\n```\n\n```c++\n// async.cc\n#include \n#include \"nan.h\"\n#include \"async.h\"\n\n// ...\n\nusing namespace v8;\n\nclass PiWorker : public NanAsyncWorker {\n public:\n PiWorker(NanCallback *callback, int points)\n : NanAsyncWorker(callback), points(points) {}\n ~PiWorker() {}\n\n // Executed inside the worker-thread.\n // It is not safe to access V8, or V8 data structures\n // here, so everything we need for input and output\n // should go on `this`.\n void Execute () {\n estimate = Estimate(points);\n }\n\n // Executed when the async work is complete\n // this function will be run inside the main event loop\n // so it is safe to use V8 again\n void HandleOKCallback () {\n NanScope();\n\n Local argv[] = {\n Local::New(Null())\n , Number::New(estimate)\n };\n\n callback->Call(2, argv);\n };\n\n private:\n int points;\n double estimate;\n};\n\n// Asynchronous access to the `Estimate()` function\nNAN_METHOD(CalculateAsync) {\n NanScope();\n\n int points = args[0]->Uint32Value();\n NanCallback *callback = new NanCallback(args[1].As());\n\n NanAsyncQueueWorker(new PiWorker(callback, points));\n NanReturnUndefined();\n}\n```\n\n\n## API\n\n * NAN_METHOD\n * NAN_GETTER\n * NAN_SETTER\n * NAN_PROPERTY_GETTER\n * NAN_PROPERTY_SETTER\n * NAN_PROPERTY_ENUMERATOR\n * NAN_PROPERTY_DELETER\n * NAN_PROPERTY_QUERY\n * NAN_WEAK_CALLBACK\n * NanReturnValue\n * NanReturnUndefined\n * NanReturnNull\n * NanReturnEmptyString\n * NanScope\n * NanLocker\n * NanUnlocker\n * NanGetInternalFieldPointer\n * NanSetInternalFieldPointer\n * NanObjectWrapHandle\n * NanMakeWeak\n * NanSymbol\n * NanGetPointerSafe\n * NanSetPointerSafe\n * NanFromV8String\n * NanBooleanOptionValue\n * NanUInt32OptionValue\n * NanThrowError, NanThrowTypeError, NanThrowRangeError, NanThrowError(Handle), NanThrowError(Handle, int)\n * NanNewBufferHandle(char *, size_t, FreeCallback, void *), NanNewBufferHandle(char *, uint32_t), NanNewBufferHandle(uint32_t)\n * NanBufferUse(char *, uint32_t)\n * NanNewContextHandle\n * NanHasInstance\n * NanPersistentToLocal\n * NanDispose\n * NanAssignPersistent\n * NanInitPersistent\n * NanCallback\n * NanAsyncWorker\n * NanAsyncQueueWorker\n\n\n### NAN_METHOD(methodname)\n\nUse `NAN_METHOD` to define your V8 accessible methods:\n\n```c++\n// .h:\nclass Foo : public node::ObjectWrap {\n ...\n\n static NAN_METHOD(Bar);\n static NAN_METHOD(Baz);\n}\n\n\n// .cc:\nNAN_METHOD(Foo::Bar) {\n ...\n}\n\nNAN_METHOD(Foo::Baz) {\n ...\n}\n```\n\nThe reason for this macro is because of the method signature change in 0.11:\n\n```c++\n// 0.10 and below:\nHandle name(const Arguments& args)\n\n// 0.11 and above\nvoid name(const FunctionCallbackInfo& args)\n```\n\nThe introduction of `FunctionCallbackInfo` brings additional complications:\n\n\n### NAN_GETTER(methodname)\n\nUse `NAN_GETTER` to declare your V8 accessible getters. You get a `Local` `property` and an appropriately typed `args` object that can act like the `args` argument to a `NAN_METHOD` call.\n\nYou can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_GETTER`.\n\n\n### NAN_SETTER(methodname)\n\nUse `NAN_SETTER` to declare your V8 accessible setters. Same as `NAN_GETTER` but you also get a `Local` `value` object to work with.\n\nYou can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_SETTER`.\n\n\n### NAN_PROPERTY_GETTER(cbname)\nUse `NAN_PROPERTY_GETTER` to declare your V8 accessible property getters. You get a `Local` `property` and an appropriately typed `args` object that can act similar to the `args` argument to a `NAN_METHOD` call.\n\nYou can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_GETTER`.\n\n\n### NAN_PROPERTY_SETTER(cbname)\nUse `NAN_PROPERTY_SETTER` to declare your V8 accessible property setters. Same as `NAN_PROPERTY_GETTER` but you also get a `Local` `value` object to work with.\n\nYou can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_SETTER`.\n\n\n### NAN_PROPERTY_ENUMERATOR(cbname)\nUse `NAN_PROPERTY_ENUMERATOR` to declare your V8 accessible property enumerators. You get an appropriately typed `args` object like the `args` argument to a `NAN_PROPERTY_GETTER` call.\n\nYou can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_ENUMERATOR`.\n\n\n### NAN_PROPERTY_DELETER(cbname)\nUse `NAN_PROPERTY_DELETER` to declare your V8 accessible property deleters. Same as `NAN_PROPERTY_GETTER`.\n\nYou can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_DELETER`.\n\n\n### NAN_PROPERTY_QUERY(cbname)\nUse `NAN_PROPERTY_QUERY` to declare your V8 accessible property queries. Same as `NAN_PROPERTY_GETTER`.\n\nYou can use `NanReturnNull()`, `NanReturnEmptyString()`, `NanReturnUndefined()` and `NanReturnValue()` in a `NAN_PROPERTY_QUERY`.\n\n\n### NAN_WEAK_CALLBACK(type, cbname)\n\nUse `NAN_WEAK_CALLBACK` to declare your V8 WeakReference callbacks. There is an object argument accessible through `NAN_WEAK_CALLBACK_OBJECT`. The `type` argument gives the type of the `data` argument, accessible through `NAN_WEAK_CALLBACK_DATA(type)`.\n\n```c++\nstatic NAN_WEAK_CALLBACK(BufferReference*, WeakCheck) {\n if (NAN_WEAK_CALLBACK_DATA(BufferReference*)->noLongerNeeded_) {\n delete NAN_WEAK_CALLBACK_DATA(BufferReference*);\n } else {\n // Still in use, revive, prevent GC\n NanMakeWeak(NAN_WEAK_CALLBACK_OBJECT, NAN_WEAK_CALLBACK_DATA(BufferReference*), &WeakCheck);\n }\n}\n\n```\n\n### NanReturnValue(Handle<Value>)\n\nUse `NanReturnValue` when you want to return a value from your V8 accessible method:\n\n```c++\nNAN_METHOD(Foo::Bar) {\n ...\n\n NanReturnValue(String::New(\"FooBar!\"));\n}\n```\n\nNo `return` statement required.\n\n\n### NanReturnUndefined()\n\nUse `NanReturnUndefined` when you don't want to return anything from your V8 accessible method:\n\n```c++\nNAN_METHOD(Foo::Baz) {\n ...\n\n NanReturnUndefined();\n}\n```\n\n\n### NanReturnNull()\n\nUse `NanReturnNull` when you want to return `Null` from your V8 accessible method:\n\n```c++\nNAN_METHOD(Foo::Baz) {\n ...\n\n NanReturnNull();\n}\n```\n\n\n### NanReturnEmptyString()\n\nUse `NanReturnEmptyString` when you want to return an empty `String` from your V8 accessible method:\n\n```c++\nNAN_METHOD(Foo::Baz) {\n ...\n\n NanReturnEmptyString();\n}\n```\n\n\n### NanScope()\n\nThe introduction of `isolate` references for many V8 calls in Node 0.11 makes `NanScope()` necessary, use it in place of `HandleScope scope`:\n\n```c++\nNAN_METHOD(Foo::Bar) {\n NanScope();\n\n NanReturnValue(String::New(\"FooBar!\"));\n}\n```\n\n\n### NanLocker()\n\nThe introduction of `isolate` references for many V8 calls in Node 0.11 makes `NanLocker()` necessary, use it in place of `Locker locker`:\n\n```c++\nNAN_METHOD(Foo::Bar) {\n NanLocker();\n ...\n NanUnlocker();\n}\n```\n\n\n### NanUnlocker()\n\nThe introduction of `isolate` references for many V8 calls in Node 0.11 makes `NanUnlocker()` necessary, use it in place of `Unlocker unlocker`:\n\n```c++\nNAN_METHOD(Foo::Bar) {\n NanLocker();\n ...\n NanUnlocker();\n}\n```\n\n\n### void * NanGetInternalFieldPointer(Handle<Object>, int)\n\nGets a pointer to the internal field with at `index` from a V8 `Object` handle.\n\n```c++\nLocal obj;\n...\nNanGetInternalFieldPointer(obj, 0);\n```\n\n### void NanSetInternalFieldPointer(Handle<Object>, int, void *)\n\nSets the value of the internal field at `index` on a V8 `Object` handle.\n\n```c++\nstatic Persistent dataWrapperCtor;\n...\nLocal wrapper = NanPersistentToLocal(dataWrapperCtor)->NewInstance();\nNanSetInternalFieldPointer(wrapper, 0, this);\n```\n\n\n### Local<Object> NanObjectWrapHandle(Object)\n\nWhen you want to fetch the V8 object handle from a native object you've wrapped with Node's `ObjectWrap`, you should use `NanObjectWrapHandle`:\n\n```c++\nNanObjectWrapHandle(iterator)->Get(String::NewSymbol(\"end\"))\n```\n\n\n### NanMakeWeak(Persistent<T>, parameter, callback)\n\nMake a persistent reference weak.\n\n\n### String NanSymbol(char *)\n\nThis isn't strictly about compatibility, it's just an easier way to create string symbol objects (i.e. `String::NewSymbol(x)`), for getting and setting object properties, or names of objects.\n\n```c++\nbool foo = false;\nif (obj->Has(NanSymbol(\"foo\")))\n foo = optionsObj->Get(NanSymbol(\"foo\"))->BooleanValue()\n```\n\n\n### Type NanGetPointerSafe(Type *[, Type])\n\nA helper for getting values from optional pointers. If the pointer is `NULL`, the function returns the optional default value, which defaults to `0`. Otherwise, the function returns the value the pointer points to.\n\n```c++\nchar *plugh(uint32_t *optional) {\n char res[] = \"xyzzy\";\n uint32_t param = NanGetPointerSafe(optional, 0x1337);\n switch (param) {\n ...\n }\n NanSetPointerSafe(optional, 0xDEADBEEF);\n} \n```\n\n\n### bool NanSetPointerSafe(Type *, Type)\n\nA helper for setting optional argument pointers. If the pointer is `NULL`, the function simply return `false`. Otherwise, the value is assigned to the variable the pointer points to.\n\n```c++\nconst char *plugh(size_t *outputsize) {\n char res[] = \"xyzzy\";\n if !(NanSetPointerSafe(outputsize, strlen(res) + 1)) {\n ...\n }\n\n ...\n}\n```\n\n\n### char* NanFromV8String(Handle<Value>[, enum Nan::Encoding, size_t *, char *, size_t, int])\n\nWhen you want to convert a V8 `String` to a `char*` use `NanFromV8String`. It is possible to define an encoding that defaults to `Nan::UTF8` as well as a pointer to a variable that will be assigned the number of bytes in the returned string. It is also possible to supply a buffer and its length to the function in order not to have a new buffer allocated. The final argument allows optionally setting `String::WriteOptions`, which default to `String::HINT_MANY_WRITES_EXPECTED | String::NO_NULL_TERMINATION`.\nJust remember that you'll end up with an object that you'll need to `delete[]` at some point unless you supply your own buffer:\n\n```c++\nsize_t count;\nchar* name = NanFromV8String(args[0]);\nchar* decoded = NanFromV8String(args[1], Nan::BASE64, &count, NULL, 0, String::HINT_MANY_WRITES_EXPECTED);\nchar param_copy[count];\nmemcpy(param_copy, decoded, count);\ndelete[] decoded;\n```\n\n\n### bool NanBooleanOptionValue(Handle<Value>, Handle<String>[, bool])\n\nWhen you have an \"options\" object that you need to fetch properties from, boolean options can be fetched with this pair. They check first if the object exists (`IsEmpty`), then if the object has the given property (`Has`) then they get and convert/coerce the property to a `bool`.\n\nThe optional last parameter is the *default* value, which is `false` if left off:\n\n```c++\n// `foo` is false unless the user supplies a truthy value for it\nbool foo = NanBooleanOptionValue(optionsObj, NanSymbol(\"foo\"));\n// `bar` is true unless the user supplies a falsy value for it\nbool bar = NanBooleanOptionValueDefTrue(optionsObj, NanSymbol(\"bar\"), true);\n```\n\n\n### uint32_t NanUInt32OptionValue(Handle<Value>, Handle<String>, uint32_t)\n\nSimilar to `NanBooleanOptionValue`, use `NanUInt32OptionValue` to fetch an integer option from your options object. Can be any kind of JavaScript `Number` and it will be coerced to an unsigned 32-bit integer.\n\nRequires all 3 arguments as a default is not optional:\n\n```c++\nuint32_t count = NanUInt32OptionValue(optionsObj, NanSymbol(\"count\"), 1024);\n```\n\n\n### NanThrowError(message), NanThrowTypeError(message), NanThrowRangeError(message), NanThrowError(Local<Value>), NanThrowError(Local<Value>, int)\n\nFor throwing `Error`, `TypeError` and `RangeError` objects. You should `return` this call:\n\n```c++\nreturn NanThrowError(\"you must supply a callback argument\");\n```\n\nCan also handle any custom object you may want to throw. If used with the error code argument, it will add the supplied error code to the error object as a property called `code`.\n\n\n### Local<Object> NanNewBufferHandle(char *, uint32_t), Local<Object> NanNewBufferHandle(uint32_t)\n\nThe `Buffer` API has changed a little in Node 0.11, this helper provides consistent access to `Buffer` creation:\n\n```c++\nNanNewBufferHandle((char*)value.data(), value.size());\n```\n\nCan also be used to initialize a `Buffer` with just a `size` argument.\n\nCan also be supplied with a `NAN_WEAK_CALLBACK` and a hint for the garbage collector, when dealing with weak references.\n\n\n### Local<Object> NanBufferUse(char*, uint32_t)\n\n`Buffer::New(char*, uint32_t)` prior to 0.11 would make a copy of the data.\nWhile it was possible to get around this, it required a shim by passing a\ncallback. So the new API `Buffer::Use(char*, uint32_t)` was introduced to remove\nneeding to use this shim.\n\n`NanBufferUse` uses the `char*` passed as the backing data, and will free the\nmemory automatically when the weak callback is called. Keep this in mind, as\ncareless use can lead to \"double free or corruption\" and other cryptic failures.\n\n\n### bool NanHasInstance(Persistent<FunctionTemplate>&, Handle<Value>)\n\nCan be used to check the type of an object to determine it is of a particular class you have already defined and have a `Persistent` handle for.\n\n\n### Local<Type> NanPersistentToLocal(Persistent<Type>&)\n\nAside from `FunctionCallbackInfo`, the biggest and most painful change to V8 in Node 0.11 is the many restrictions now placed on `Persistent` handles. They are difficult to assign and difficult to fetch the original value out of.\n\nUse `NanPersistentToLocal` to convert a `Persistent` handle back to a `Local` handle.\n\n```c++\nLocal handle = NanPersistentToLocal(persistentHandle);\n```\n\n\n### Local<Context> NanNewContextHandle([ExtensionConfiguration*, Handle<ObjectTemplate>, Handle<Value>])\nCreates a new `Local` handle.\n\n```c++\nLocal ftmpl = FunctionTemplate::New();\nLocal otmpl = ftmpl->InstanceTemplate();\nLocal ctx = NanNewContextHandle(NULL, otmpl);\n```\n\n\n### void NanDispose(Persistent<T> &)\n\nUse `NanDispose` to dispose a `Persistent` handle.\n\n```c++\nNanDispose(persistentHandle);\n```\n\n\n### NanAssignPersistent(type, handle, object)\n\nUse `NanAssignPersistent` to assign a non-`Persistent` handle to a `Persistent` one. You can no longer just declare a `Persistent` handle and assign directly to it later, you have to `Reset` it in Node 0.11, so this makes it easier.\n\nIn general it is now better to place anything you want to protect from V8's garbage collector as properties of a generic `Object` and then assign that to a `Persistent`. This works in older versions of Node also if you use `NanAssignPersistent`:\n\n```c++\nPersistent persistentHandle;\n\n...\n\nLocal obj = Object::New();\nobj->Set(NanSymbol(\"key\"), keyHandle); // where keyHandle might be a Local\nNanAssignPersistent(Object, persistentHandle, obj)\n```\n\n\n### NanInitPersistent(type, name, object)\n\nUser `NanInitPersistent` to declare and initialize a new `Persistent` with the supplied object. The assignment operator for `Persistent` is no longer public in Node 0.11, so this macro makes it easier to declare and initializing a new `Persistent`. See NanAssignPersistent for more information.\n\n```c++\nLocal obj = Object::New();\nobj->Set(NanSymbol(\"key\"), keyHandle); // where keyHandle might be a Local\nNanInitPersistent(Object, persistentHandle, obj);\n```\n\n\n### NanCallback\n\nBecause of the difficulties imposed by the changes to `Persistent` handles in V8 in Node 0.11, creating `Persistent` versions of your `Local` handles is annoyingly tricky. `NanCallback` makes it easier by taking your `Local` handle, making it persistent until the `NanCallback` is deleted and even providing a handy `Call()` method to fetch and execute the callback `Function`.\n\n```c++\nLocal callbackHandle = callback = args[0].As();\nNanCallback *callback = new NanCallback(callbackHandle);\n// pass `callback` around and it's safe from GC until you:\ndelete callback;\n```\n\nYou can execute the callback like so:\n\n```c++\n// no arguments:\ncallback->Call(0, NULL);\n\n// an error argument:\nLocal argv[] = {\n Exception::Error(String::New(\"fail!\"))\n};\ncallback->Call(1, argv);\n\n// a success argument:\nLocal argv[] = {\n Local::New(Null()),\n String::New(\"w00t!\")\n};\ncallback->Call(2, argv);\n```\n\n`NanCallback` also has a `Local GetCallback()` method that you can use to fetch a local handle to the underlying callback function if you need it.\n\n\n### NanAsyncWorker\n\n`NanAsyncWorker` is an abstract class that you can subclass to have much of the annoying async queuing and handling taken care of for you. It can even store arbitrary V8 objects for you and have them persist while the async work is in progress.\n\nSee a rough outline of the implementation:\n\n```c++\nclass NanAsyncWorker {\npublic:\n NanAsyncWorker (NanCallback *callback);\n\n // Clean up persistent handles and delete the *callback\n virtual ~NanAsyncWorker ();\n\n // Check the `char *errmsg` property and call HandleOKCallback()\n // or HandleErrorCallback depending on whether it has been set or not\n virtual void WorkComplete ();\n\n // You must implement this to do some async work. If there is an\n // error then allocate `errmsg` to to a message and the callback will\n // be passed that string in an Error object\n virtual void Execute ();\n\nprotected:\n // Set this if there is an error, otherwise it's NULL\n const char *errmsg;\n\n // Save a V8 object in a Persistent handle to protect it from GC\n void SavePersistent(const char *key, Local &obj);\n\n // Fetch a stored V8 object (don't call from within `Execute()`)\n Local GetFromPersistent(const char *key);\n\n // Default implementation calls the callback function with no arguments.\n // Override this to return meaningful data\n virtual void HandleOKCallback ();\n\n // Default implementation calls the callback function with an Error object\n // wrapping the `errmsg` string\n virtual void HandleErrorCallback ();\n};\n```\n\n\n### NanAsyncQueueWorker(NanAsyncWorker *)\n\n`NanAsyncQueueWorker` will run a `NanAsyncWorker` asynchronously via libuv. Both the *execute* and *after_work* steps are taken care of for you—most of the logic for this is embedded in `NanAsyncWorker`.\n\n### Contributors\n\nNAN is only possible due to the excellent work of the following contributors:\n\n\n\n\n\n
Rod VaggGitHub/rvaggTwitter/@rvagg
Benjamin ByholmGitHub/kkoopa
Trevor NorrisGitHub/trevnorrisTwitter/@trevnorris
\n\nLicence & copyright\n-----------------------\n\nCopyright (c) 2013 Rod Vagg & NAN contributors (listed above).\n\nNative Abstractions for Node.js is licensed under an MIT +no-false-attribs license. All rights not explicitly granted in the MIT license are reserved. See the included LICENSE file for more details.\n", + "_id": "nan@0.3.2", + "dist": { + "shasum": "10b41e29145e722aad573c9c5b493d9b1c0887c0" + }, + "_from": "nan@~0.3.0" +} diff --git a/node_modules/ws/node_modules/options/.npmignore b/node_modules/ws/node_modules/options/.npmignore new file mode 100644 index 0000000..6bfffbb --- /dev/null +++ b/node_modules/ws/node_modules/options/.npmignore @@ -0,0 +1,5 @@ +npm-debug.log +node_modules +.*.swp +.lock-* +build/ diff --git a/node_modules/ws/node_modules/options/Makefile b/node_modules/ws/node_modules/options/Makefile new file mode 100644 index 0000000..7496b6f --- /dev/null +++ b/node_modules/ws/node_modules/options/Makefile @@ -0,0 +1,12 @@ +ALL_TESTS = $(shell find test/ -name '*.test.js') + +run-tests: + @./node_modules/.bin/mocha \ + -t 2000 \ + $(TESTFLAGS) \ + $(TESTS) + +test: + @$(MAKE) NODE_PATH=lib TESTS="$(ALL_TESTS)" run-tests + +.PHONY: test diff --git a/node_modules/ws/node_modules/options/README.md b/node_modules/ws/node_modules/options/README.md new file mode 100644 index 0000000..4b39a2a --- /dev/null +++ b/node_modules/ws/node_modules/options/README.md @@ -0,0 +1,28 @@ +# options.js # + +A very light-weight in-code option parsers for node.js. + +## License ## + +(The MIT License) + +Copyright (c) 2012 Einar Otto Stangvik <einaros@gmail.com> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/ws/node_modules/options/lib/options.js b/node_modules/ws/node_modules/options/lib/options.js new file mode 100644 index 0000000..4fc45e9 --- /dev/null +++ b/node_modules/ws/node_modules/options/lib/options.js @@ -0,0 +1,86 @@ +/*! + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +var fs = require('fs'); + +function Options(defaults) { + var internalValues = {}; + var values = this.value = {}; + Object.keys(defaults).forEach(function(key) { + internalValues[key] = defaults[key]; + Object.defineProperty(values, key, { + get: function() { return internalValues[key]; }, + configurable: false, + enumerable: true + }); + }); + this.reset = function() { + Object.keys(defaults).forEach(function(key) { + internalValues[key] = defaults[key]; + }); + return this; + }; + this.merge = function(options, required) { + options = options || {}; + if (Object.prototype.toString.call(required) === '[object Array]') { + var missing = []; + for (var i = 0, l = required.length; i < l; ++i) { + var key = required[i]; + if (!(key in options)) { + missing.push(key); + } + } + if (missing.length > 0) { + if (missing.length > 1) { + throw new Error('options ' + + missing.slice(0, missing.length - 1).join(', ') + ' and ' + + missing[missing.length - 1] + ' must be defined'); + } + else throw new Error('option ' + missing[0] + ' must be defined'); + } + } + Object.keys(options).forEach(function(key) { + if (key in internalValues) { + internalValues[key] = options[key]; + } + }); + return this; + }; + this.copy = function(keys) { + var obj = {}; + Object.keys(defaults).forEach(function(key) { + if (keys.indexOf(key) !== -1) { + obj[key] = values[key]; + } + }); + return obj; + }; + this.read = function(filename, cb) { + if (typeof cb == 'function') { + var self = this; + fs.readFile(filename, function(error, data) { + if (error) return cb(error); + var conf = JSON.parse(data); + self.merge(conf); + cb(); + }); + } + else { + var conf = JSON.parse(fs.readFileSync(filename)); + this.merge(conf); + } + return this; + }; + this.isDefined = function(key) { + return typeof values[key] != 'undefined'; + }; + this.isDefinedAndNonNull = function(key) { + return typeof values[key] != 'undefined' && values[key] !== null; + }; + Object.freeze(values); + Object.freeze(this); +} + +module.exports = Options; diff --git a/node_modules/ws/node_modules/options/package.json b/node_modules/ws/node_modules/options/package.json new file mode 100644 index 0000000..cf42c90 --- /dev/null +++ b/node_modules/ws/node_modules/options/package.json @@ -0,0 +1,31 @@ +{ + "author": { + "name": "Einar Otto Stangvik", + "email": "einaros@gmail.com", + "url": "http://2x.io" + }, + "name": "options", + "description": "A very light-weight in-code option parsers for node.js.", + "version": "0.0.5", + "repository": { + "type": "git", + "url": "git://github.com/einaros/options.js.git" + }, + "main": "lib/options", + "scripts": { + "test": "make test" + }, + "engines": { + "node": ">=0.4.0" + }, + "dependencies": {}, + "devDependencies": { + "mocha": "latest" + }, + "readme": "# options.js #\n\nA very light-weight in-code option parsers for node.js.\n\n## License ##\n\n(The MIT License)\n\nCopyright (c) 2012 Einar Otto Stangvik <einaros@gmail.com>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", + "_id": "options@0.0.5", + "dist": { + "shasum": "b0976718113cc4ca5fc50fe967034c339207f0ed" + }, + "_from": "options@>=0.0.5" +} diff --git a/node_modules/ws/node_modules/options/test/fixtures/test.conf b/node_modules/ws/node_modules/options/test/fixtures/test.conf new file mode 100644 index 0000000..6e62444 --- /dev/null +++ b/node_modules/ws/node_modules/options/test/fixtures/test.conf @@ -0,0 +1,4 @@ +{ + "a": "foobar", + "b": false +} \ No newline at end of file diff --git a/node_modules/ws/node_modules/options/test/options.test.js b/node_modules/ws/node_modules/options/test/options.test.js new file mode 100644 index 0000000..6a1d9f5 --- /dev/null +++ b/node_modules/ws/node_modules/options/test/options.test.js @@ -0,0 +1,140 @@ +var Options = require('options') + , assert = require('assert'); + +describe('Options', function() { + describe('#ctor', function() { + it('initializes options', function() { + var option = new Options({a: true, b: false}); + assert.strictEqual(true, option.value.a); + assert.strictEqual(false, option.value.b); + }); + }); + + describe('#merge', function() { + it('merges options from another object', function() { + var option = new Options({a: true, b: false}); + option.merge({b: true}); + assert.strictEqual(true, option.value.a); + assert.strictEqual(true, option.value.b); + }); + it('does nothing when arguments are undefined', function() { + var option = new Options({a: true, b: false}); + option.merge(undefined); + assert.strictEqual(true, option.value.a); + assert.strictEqual(false, option.value.b); + }); + it('cannot set values that werent already there', function() { + var option = new Options({a: true, b: false}); + option.merge({c: true}); + assert.strictEqual('undefined', typeof option.value.c); + }); + it('can require certain options to be defined', function() { + var option = new Options({a: true, b: false, c: 3}); + var caughtException = false; + try { + option.merge({}, ['a', 'b', 'c']); + } + catch (e) { + caughtException = e.toString() == 'Error: options a, b and c must be defined'; + } + assert.strictEqual(true, caughtException); + }); + it('can require certain options to be defined, when options are undefined', function() { + var option = new Options({a: true, b: false, c: 3}); + var caughtException = false; + try { + option.merge(undefined, ['a', 'b', 'c']); + } + catch (e) { + caughtException = e.toString() == 'Error: options a, b and c must be defined'; + } + assert.strictEqual(true, caughtException); + }); + it('returns "this"', function() { + var option = new Options({a: true, b: false, c: 3}); + assert.strictEqual(option, option.merge()); + }); + }); + + describe('#copy', function() { + it('returns a new object with the indicated options', function() { + var option = new Options({a: true, b: false, c: 3}); + option.merge({c: 4}); + var obj = option.copy(['a', 'c']); + assert.strictEqual(true, obj.a); + assert.strictEqual(4, obj.c); + assert.strictEqual('undefined', typeof obj.b); + }); + }); + + describe('#value', function() { + it('can be enumerated', function() { + var option = new Options({a: true, b: false}); + assert.strictEqual(2, Object.keys(option.value).length); + }); + it('can not be used to set values', function() { + var option = new Options({a: true, b: false}); + option.value.b = true; + assert.strictEqual(false, option.value.b); + }); + it('can not be used to add values', function() { + var option = new Options({a: true, b: false}); + option.value.c = 3; + assert.strictEqual('undefined', typeof option.value.c); + }); + }); + + describe('#isDefined', function() { + it('returns true if the named value is defined', function() { + var option = new Options({a: undefined}); + assert.strictEqual(false, option.isDefined('a')); + option.merge({a: false}); + assert.strictEqual(true, option.isDefined('a')); + }); + }); + + describe('#isDefinedAndNonNull', function() { + it('returns true if the named value is defined and non-null', function() { + var option = new Options({a: undefined}); + assert.strictEqual(false, option.isDefinedAndNonNull('a')); + option.merge({a: null}); + assert.strictEqual(false, option.isDefinedAndNonNull('a')); + option.merge({a: 2}); + assert.strictEqual(true, option.isDefinedAndNonNull('a')); + }); + }); + + describe('#read', function() { + it('reads and merges config from a file', function() { + var option = new Options({a: true, b: true}); + option.read(__dirname + '/fixtures/test.conf'); + assert.strictEqual('foobar', option.value.a); + assert.strictEqual(false, option.value.b); + }); + + it('asynchronously reads and merges config from a file when a callback is passed', function(done) { + var option = new Options({a: true, b: true}); + option.read(__dirname + '/fixtures/test.conf', function(error) { + assert.strictEqual('foobar', option.value.a); + assert.strictEqual(false, option.value.b); + done(); + }); + }); + }); + + describe('#reset', function() { + it('resets options to defaults', function() { + var option = new Options({a: true, b: false}); + option.merge({b: true}); + assert.strictEqual(true, option.value.b); + option.reset(); + assert.strictEqual(false, option.value.b); + }); + }); + + it('is immutable', function() { + var option = new Options({a: true, b: false}); + option.foo = 2; + assert.strictEqual('undefined', typeof option.foo); + }); +}); diff --git a/node_modules/ws/node_modules/tinycolor/.npmignore b/node_modules/ws/node_modules/tinycolor/.npmignore new file mode 100644 index 0000000..6bfffbb --- /dev/null +++ b/node_modules/ws/node_modules/tinycolor/.npmignore @@ -0,0 +1,5 @@ +npm-debug.log +node_modules +.*.swp +.lock-* +build/ diff --git a/node_modules/ws/node_modules/tinycolor/README.md b/node_modules/ws/node_modules/tinycolor/README.md new file mode 100644 index 0000000..55eb3c1 --- /dev/null +++ b/node_modules/ws/node_modules/tinycolor/README.md @@ -0,0 +1,3 @@ +# tinycolor # + +This is a no-fuzz, barebone, zero muppetry color module for node.js. \ No newline at end of file diff --git a/node_modules/ws/node_modules/tinycolor/example.js b/node_modules/ws/node_modules/tinycolor/example.js new file mode 100644 index 0000000..f754046 --- /dev/null +++ b/node_modules/ws/node_modules/tinycolor/example.js @@ -0,0 +1,3 @@ +require('./tinycolor'); +console.log('this should be red and have an underline!'.grey.underline); +console.log('this should have a blue background!'.bgBlue); \ No newline at end of file diff --git a/node_modules/ws/node_modules/tinycolor/package.json b/node_modules/ws/node_modules/tinycolor/package.json new file mode 100644 index 0000000..d0faef4 --- /dev/null +++ b/node_modules/ws/node_modules/tinycolor/package.json @@ -0,0 +1,26 @@ +{ + "author": { + "name": "Einar Otto Stangvik", + "email": "einaros@gmail.com", + "url": "http://2x.io" + }, + "name": "tinycolor", + "description": "a to-the-point color module for node", + "version": "0.0.1", + "repository": { + "type": "git", + "url": "git://github.com/einaros/tinycolor.git" + }, + "engines": { + "node": ">=0.4.0" + }, + "dependencies": {}, + "devDependencies": {}, + "main": "tinycolor", + "readme": "# tinycolor #\n\nThis is a no-fuzz, barebone, zero muppetry color module for node.js.", + "_id": "tinycolor@0.0.1", + "dist": { + "shasum": "e1ee86d35d022d6d5fccba53141d5c50727ff76c" + }, + "_from": "tinycolor@0.x" +} diff --git a/node_modules/ws/node_modules/tinycolor/tinycolor.js b/node_modules/ws/node_modules/tinycolor/tinycolor.js new file mode 100644 index 0000000..36e552c --- /dev/null +++ b/node_modules/ws/node_modules/tinycolor/tinycolor.js @@ -0,0 +1,31 @@ +var styles = { + 'bold': ['\033[1m', '\033[22m'], + 'italic': ['\033[3m', '\033[23m'], + 'underline': ['\033[4m', '\033[24m'], + 'inverse': ['\033[7m', '\033[27m'], + 'black': ['\033[30m', '\033[39m'], + 'red': ['\033[31m', '\033[39m'], + 'green': ['\033[32m', '\033[39m'], + 'yellow': ['\033[33m', '\033[39m'], + 'blue': ['\033[34m', '\033[39m'], + 'magenta': ['\033[35m', '\033[39m'], + 'cyan': ['\033[36m', '\033[39m'], + 'white': ['\033[37m', '\033[39m'], + 'default': ['\033[39m', '\033[39m'], + 'grey': ['\033[90m', '\033[39m'], + 'bgBlack': ['\033[40m', '\033[49m'], + 'bgRed': ['\033[41m', '\033[49m'], + 'bgGreen': ['\033[42m', '\033[49m'], + 'bgYellow': ['\033[43m', '\033[49m'], + 'bgBlue': ['\033[44m', '\033[49m'], + 'bgMagenta': ['\033[45m', '\033[49m'], + 'bgCyan': ['\033[46m', '\033[49m'], + 'bgWhite': ['\033[47m', '\033[49m'], + 'bgDefault': ['\033[49m', '\033[49m'] +} +Object.keys(styles).forEach(function(style) { + Object.defineProperty(String.prototype, style, { + get: function() { return styles[style][0] + this + styles[style][1]; }, + enumerable: false + }); +}); diff --git a/node_modules/ws/package.json b/node_modules/ws/package.json new file mode 100644 index 0000000..ea5e990 --- /dev/null +++ b/node_modules/ws/package.json @@ -0,0 +1,59 @@ +{ + "author": { + "name": "Einar Otto Stangvik", + "email": "einaros@gmail.com", + "url": "http://2x.io" + }, + "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": "0.4.31", + "keywords": [ + "Hixie", + "HyBi", + "Push", + "RFC-6455", + "WebSocket", + "WebSockets", + "real-time" + ], + "repository": { + "type": "git", + "url": "git://github.com/einaros/ws.git" + }, + "bin": { + "wscat": "./bin/wscat" + }, + "scripts": { + "test": "make test", + "install": "(node-gyp rebuild 2> builderror.log) || (exit 0)" + }, + "engines": { + "node": ">=0.4.0" + }, + "dependencies": { + "commander": "~0.6.1", + "nan": "~0.3.0", + "tinycolor": "0.x", + "options": ">=0.0.5" + }, + "devDependencies": { + "mocha": "1.12.0", + "should": "1.2.x", + "expect.js": "0.2.x", + "benchmark": "0.3.x", + "ansi": "latest" + }, + "browser": "./lib/browser.js", + "component": { + "scripts": { + "ws/index.js": "./lib/browser.js" + } + }, + "gypfile": true, + "readme": "[![Build Status](https://secure.travis-ci.org/einaros/ws.png)](http://travis-ci.org/einaros/ws)\n\n# ws: a node.js websocket library #\n\n`ws` is a simple to use websocket implementation, up-to-date against RFC-6455, and [probably the fastest WebSocket library for node.js](http://web.archive.org/web/20130314230536/http://hobbycoding.posterous.com/the-fastest-websocket-module-for-nodejs).\n\nPasses the quite extensive Autobahn test suite. See http://einaros.github.com/ws for the full reports.\n\nComes with a command line utility, `wscat`, which can either act as a server (--listen), or client (--connect); Use it to debug simple websocket services.\n\n## Protocol support ##\n\n* **Hixie draft 76** (Old and deprecated, but still in use by Safari and Opera. Added to ws version 0.4.2, but server only. Can be disabled by setting the `disableHixie` option to true.)\n* **HyBi drafts 07-12** (Use the option `protocolVersion: 8`, or argument `-p 8` for wscat)\n* **HyBi drafts 13-17** (Current default, alternatively option `protocolVersion: 13`, or argument `-p 13` for wscat)\n\n_See the echo.websocket.org example below for how to use the `protocolVersion` option._\n\n## Usage ##\n\n### Installing ###\n\n`npm install ws`\n\n### Sending and receiving text data ###\n\n```js\nvar WebSocket = require('ws');\nvar ws = new WebSocket('ws://www.host.com/path');\nws.on('open', function() {\n ws.send('something');\n});\nws.on('message', function(data, flags) {\n // flags.binary will be set if a binary data is received\n // flags.masked will be set if the data was masked\n});\n```\n\n### Sending binary data ###\n\n```js\nvar WebSocket = require('ws');\nvar ws = new WebSocket('ws://www.host.com/path');\nws.on('open', function() {\n var array = new Float32Array(5);\n for (var i = 0; i < array.length; ++i) array[i] = i / 2;\n ws.send(array, {binary: true, mask: true});\n});\n```\n\nSetting `mask`, as done for the send options above, will cause the data to be masked according to the websocket protocol. The same option applies for text data.\n\n### Server example ###\n\n```js\nvar WebSocketServer = require('ws').Server\n , wss = new WebSocketServer({port: 8080});\nwss.on('connection', function(ws) {\n ws.on('message', function(message) {\n console.log('received: %s', message);\n });\n ws.send('something');\n});\n```\n\n### Server sending broadcast data ###\n\n```js\nvar WebSocketServer = require('ws').Server\n , wss = new WebSocketServer({port: 8080});\n \nwss.broadcast = function(data) {\n\tfor(var i in this.clients)\n\t\tthis.clients[i].send(data);\n};\n```\n\n### Error handling best practices ###\n\n```js\n// If the WebSocket is closed before the following send is attempted\nws.send('something');\n\n// Errors (both immediate and async write errors) can be detected in an optional callback.\n// The callback is also the only way of being notified that data has actually been sent.\nws.send('something', function(error) {\n // if error is null, the send has been completed,\n // otherwise the error object will indicate what failed.\n});\n\n// Immediate errors can also be handled with try/catch-blocks, but **note**\n// that since sends are inherently asynchronous, socket write failures will *not*\n// be captured when this technique is used.\ntry {\n ws.send('something');\n}\ncatch (e) {\n // handle error\n}\n```\n\n### echo.websocket.org demo ###\n\n```js\nvar WebSocket = require('ws');\nvar ws = new WebSocket('ws://echo.websocket.org/', {protocolVersion: 8, origin: 'http://websocket.org'});\nws.on('open', function() {\n console.log('connected');\n ws.send(Date.now().toString(), {mask: true});\n});\nws.on('close', function() {\n console.log('disconnected');\n});\nws.on('message', function(data, flags) {\n console.log('Roundtrip time: ' + (Date.now() - parseInt(data)) + 'ms', flags);\n setTimeout(function() {\n ws.send(Date.now().toString(), {mask: true});\n }, 500);\n});\n```\n\n### wscat against echo.websocket.org ###\n\n $ npm install -g ws\n $ wscat -c ws://echo.websocket.org -p 8\n connected (press CTRL+C to quit)\n > hi there\n < hi there\n > are you a happy parrot?\n < are you a happy parrot?\n\n### Other examples ###\n\nFor a full example with a browser client communicating with a ws server, see the examples folder.\n\nNote that the usage together with Express 3.0 is quite different from Express 2.x. The difference is expressed in the two different serverstats-examples.\n\nOtherwise, see the test cases.\n\n### Running the tests ###\n\n`make test`\n\n## API Docs ##\n\nSee the doc/ directory for Node.js-like docs for the ws classes.\n\n## License ##\n\n(The MIT License)\n\nCopyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", + "_id": "ws@0.4.31", + "dist": { + "shasum": "ee52dad87e6af1da37ab99ccad4e68ad4f11b3ee" + }, + "_from": "ws" +} diff --git a/node_modules/ws/src/bufferutil.cc b/node_modules/ws/src/bufferutil.cc new file mode 100644 index 0000000..f06777f --- /dev/null +++ b/node_modules/ws/src/bufferutil.cc @@ -0,0 +1,117 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "nan.h" + +using namespace v8; +using namespace node; + +class BufferUtil : public ObjectWrap +{ +public: + + static void Initialize(v8::Handle target) + { + NanScope(); + Local t = FunctionTemplate::New(New); + t->InstanceTemplate()->SetInternalFieldCount(1); + NODE_SET_METHOD(t, "unmask", BufferUtil::Unmask); + NODE_SET_METHOD(t, "mask", BufferUtil::Mask); + NODE_SET_METHOD(t, "merge", BufferUtil::Merge); + target->Set(String::NewSymbol("BufferUtil"), t->GetFunction()); + } + +protected: + + static NAN_METHOD(New) + { + NanScope(); + BufferUtil* bufferUtil = new BufferUtil(); + bufferUtil->Wrap(args.This()); + NanReturnValue(args.This()); + } + + static NAN_METHOD(Merge) + { + NanScope(); + Local bufferObj = args[0]->ToObject(); + char* buffer = Buffer::Data(bufferObj); + Local array = Local::Cast(args[1]); + unsigned int arrayLength = array->Length(); + size_t offset = 0; + unsigned int i; + for (i = 0; i < arrayLength; ++i) { + Local src = array->Get(i)->ToObject(); + size_t length = Buffer::Length(src); + memcpy(buffer + offset, Buffer::Data(src), length); + offset += length; + } + NanReturnValue(True()); + } + + static NAN_METHOD(Unmask) + { + NanScope(); + Local buffer_obj = args[0]->ToObject(); + size_t length = Buffer::Length(buffer_obj); + Local mask_obj = args[1]->ToObject(); + unsigned int *mask = (unsigned int*)Buffer::Data(mask_obj); + unsigned int* from = (unsigned int*)Buffer::Data(buffer_obj); + size_t len32 = length / 4; + unsigned int i; + for (i = 0; i < len32; ++i) *(from + i) ^= *mask; + from += i; + switch (length % 4) { + case 3: *((unsigned char*)from+2) = *((unsigned char*)from+2) ^ ((unsigned char*)mask)[2]; + case 2: *((unsigned char*)from+1) = *((unsigned char*)from+1) ^ ((unsigned char*)mask)[1]; + case 1: *((unsigned char*)from ) = *((unsigned char*)from ) ^ ((unsigned char*)mask)[0]; + case 0:; + } + NanReturnValue(True()); + } + + static NAN_METHOD(Mask) + { + NanScope(); + Local buffer_obj = args[0]->ToObject(); + Local mask_obj = args[1]->ToObject(); + unsigned int *mask = (unsigned int*)Buffer::Data(mask_obj); + Local output_obj = args[2]->ToObject(); + unsigned int dataOffset = args[3]->Int32Value(); + unsigned int length = args[4]->Int32Value(); + unsigned int* to = (unsigned int*)(Buffer::Data(output_obj) + dataOffset); + unsigned int* from = (unsigned int*)Buffer::Data(buffer_obj); + unsigned int len32 = length / 4; + unsigned int i; + for (i = 0; i < len32; ++i) *(to + i) = *(from + i) ^ *mask; + to += i; + from += i; + switch (length % 4) { + case 3: *((unsigned char*)to+2) = *((unsigned char*)from+2) ^ *((unsigned char*)mask+2); + case 2: *((unsigned char*)to+1) = *((unsigned char*)from+1) ^ *((unsigned char*)mask+1); + case 1: *((unsigned char*)to ) = *((unsigned char*)from ) ^ *((unsigned char*)mask); + case 0:; + } + NanReturnValue(True()); + } +}; + +extern "C" void init (Handle target) +{ + NanScope(); + BufferUtil::Initialize(target); +} + +NODE_MODULE(bufferutil, init) + diff --git a/node_modules/ws/src/validation.cc b/node_modules/ws/src/validation.cc new file mode 100644 index 0000000..528eda1 --- /dev/null +++ b/node_modules/ws/src/validation.cc @@ -0,0 +1,145 @@ +/*! + * ws: a node.js websocket client + * Copyright(c) 2011 Einar Otto Stangvik + * MIT Licensed + */ + +#include +#include +#include +#include +#include +#include +#include +#include "nan.h" + +using namespace v8; +using namespace node; + +#define UNI_SUR_HIGH_START (uint32_t) 0xD800 +#define UNI_SUR_LOW_END (uint32_t) 0xDFFF +#define UNI_REPLACEMENT_CHAR (uint32_t) 0x0000FFFD +#define UNI_MAX_LEGAL_UTF32 (uint32_t) 0x0010FFFF + +static const uint8_t trailingBytesForUTF8[256] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 +}; + +static const uint32_t offsetsFromUTF8[6] = { + 0x00000000, 0x00003080, 0x000E2080, + 0x03C82080, 0xFA082080, 0x82082080 +}; + +static int isLegalUTF8(const uint8_t *source, const int length) +{ + uint8_t a; + const uint8_t *srcptr = source+length; + switch (length) { + default: return 0; + /* Everything else falls through when "true"... */ + /* RFC3629 makes 5 & 6 bytes UTF-8 illegal + case 6: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0; + case 5: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0; */ + case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0; + case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0; + case 2: if ((a = (*--srcptr)) > 0xBF) return 0; + switch (*source) { + /* no fall-through in this inner switch */ + case 0xE0: if (a < 0xA0) return 0; break; + case 0xED: if (a > 0x9F) return 0; break; + case 0xF0: if (a < 0x90) return 0; break; + case 0xF4: if (a > 0x8F) return 0; break; + default: if (a < 0x80) return 0; + } + + case 1: if (*source >= 0x80 && *source < 0xC2) return 0; + } + if (*source > 0xF4) return 0; + return 1; +} + +int is_valid_utf8 (size_t len, char *value) +{ + /* is the string valid UTF-8? */ + for (unsigned int i = 0; i < len; i++) { + uint32_t ch = 0; + uint8_t extrabytes = trailingBytesForUTF8[(uint8_t) value[i]]; + + if (extrabytes + i >= len) + return 0; + + if (isLegalUTF8 ((uint8_t *) (value + i), extrabytes + 1) == 0) return 0; + + switch (extrabytes) { + case 5 : ch += (uint8_t) value[i++]; ch <<= 6; + case 4 : ch += (uint8_t) value[i++]; ch <<= 6; + case 3 : ch += (uint8_t) value[i++]; ch <<= 6; + case 2 : ch += (uint8_t) value[i++]; ch <<= 6; + case 1 : ch += (uint8_t) value[i++]; ch <<= 6; + case 0 : ch += (uint8_t) value[i]; + } + + ch -= offsetsFromUTF8[extrabytes]; + + if (ch <= UNI_MAX_LEGAL_UTF32) { + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) + return 0; + } else { + return 0; + } + } + + return 1; +} + +class Validation : public ObjectWrap +{ +public: + + static void Initialize(v8::Handle target) + { + HandleScope scope; + Local t = FunctionTemplate::New(New); + t->InstanceTemplate()->SetInternalFieldCount(1); + NODE_SET_METHOD(t, "isValidUTF8", Validation::IsValidUTF8); + target->Set(String::NewSymbol("Validation"), t->GetFunction()); + } + +protected: + + static NAN_METHOD(New) + { + NanScope(); + Validation* validation = new Validation(); + validation->Wrap(args.This()); + NanReturnValue(args.This()); + } + + static NAN_METHOD(IsValidUTF8) + { + NanScope(); + if (!Buffer::HasInstance(args[0])) { + return NanThrowTypeError("First argument needs to be a buffer"); + } + Local buffer_obj = args[0]->ToObject(); + char *buffer_data = Buffer::Data(buffer_obj); + size_t buffer_length = Buffer::Length(buffer_obj); + NanReturnValue(is_valid_utf8(buffer_length, buffer_data) == 1 ? True() : False()); + } +}; + +extern "C" void init (Handle target) +{ + NanScope(); + Validation::Initialize(target); +} + +NODE_MODULE(validation, init) + diff --git a/node_modules/ws/test/BufferPool.test.js b/node_modules/ws/test/BufferPool.test.js new file mode 100644 index 0000000..1ee7ff0 --- /dev/null +++ b/node_modules/ws/test/BufferPool.test.js @@ -0,0 +1,63 @@ +var BufferPool = require('../lib/BufferPool'); +require('should'); + +describe('BufferPool', function() { + describe('#ctor', function() { + it('allocates pool', function() { + var db = new BufferPool(1000); + db.size.should.eql(1000); + }); + }); + describe('#get', function() { + it('grows the pool if necessary', function() { + var db = new BufferPool(1000); + var buf = db.get(2000); + db.size.should.be.above(1000); + db.used.should.eql(2000); + buf.length.should.eql(2000); + }); + it('grows the pool after the first call, if necessary', function() { + var db = new BufferPool(1000); + var buf = db.get(1000); + db.used.should.eql(1000); + db.size.should.eql(1000); + buf.length.should.eql(1000); + var buf2 = db.get(1000); + db.used.should.eql(2000); + db.size.should.be.above(1000); + buf2.length.should.eql(1000); + }); + it('grows the pool according to the growStrategy if necessary', function() { + var db = new BufferPool(1000, function(db, length) { + return db.size + 2345; + }); + var buf = db.get(2000); + db.size.should.eql(3345); + buf.length.should.eql(2000); + }); + it('doesnt grow the pool if theres enough room available', function() { + var db = new BufferPool(1000); + var buf = db.get(1000); + db.size.should.eql(1000); + buf.length.should.eql(1000); + }); + }); + describe('#reset', function() { + it('shinks the pool', function() { + var db = new BufferPool(1000); + var buf = db.get(2000); + db.reset(true); + db.size.should.eql(1000); + }); + it('shrinks the pool according to the shrinkStrategy', function() { + var db = new BufferPool(1000, function(db, length) { + return db.used + length; + }, function(db) { + return 0; + }); + var buf = db.get(2000); + db.reset(true); + db.size.should.eql(0); + }); + }); +}); diff --git a/node_modules/ws/test/Receiver.hixie.test.js b/node_modules/ws/test/Receiver.hixie.test.js new file mode 100644 index 0000000..043d3bc --- /dev/null +++ b/node_modules/ws/test/Receiver.hixie.test.js @@ -0,0 +1,158 @@ +var assert = require('assert') + , expect = require('expect.js') + , Receiver = require('../lib/Receiver.hixie'); +require('./hybi-common'); + +describe('Receiver', function() { + it('can parse text message', function() { + var p = new Receiver(); + var packet = '00 48 65 6c 6c 6f ff'; + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal('Hello', data); + }; + + p.add(getBufferFromHexString(packet)); + expect(gotData).to.equal(true); + }); + + it('can parse multiple text messages', function() { + var p = new Receiver(); + var packet = '00 48 65 6c 6c 6f ff 00 48 65 6c 6c 6f ff'; + + var gotData = false; + var messages = []; + p.ontext = function(data) { + gotData = true; + messages.push(data); + }; + + p.add(getBufferFromHexString(packet)); + expect(gotData).to.equal(true); + for (var i = 0; i < 2; ++i) { + expect(messages[i]).to.equal('Hello'); + } + }); + + it('can parse empty message', function() { + var p = new Receiver(); + var packet = '00 ff'; + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal('', data); + }; + + p.add(getBufferFromHexString(packet)); + expect(gotData).to.equal(true); + }); + + it('can parse text messages delivered over multiple frames', function() { + var p = new Receiver(); + var packets = [ + '00 48', + '65 6c 6c', + '6f ff 00 48', + '65', + '6c 6c 6f', + 'ff' + ]; + + var gotData = false; + var messages = []; + p.ontext = function(data) { + gotData = true; + messages.push(data); + }; + + for (var i = 0; i < packets.length; ++i) { + p.add(getBufferFromHexString(packets[i])); + } + expect(gotData).to.equal(true); + for (var i = 0; i < 2; ++i) { + expect(messages[i]).to.equal('Hello'); + } + }); + + it('emits an error if a payload doesnt start with 0x00', function() { + var p = new Receiver(); + var packets = [ + '00 6c ff', + '00 6c ff ff', + 'ff 00 6c ff 00 6c ff', + '00', + '6c 6c 6f', + 'ff' + ]; + + var gotData = false; + var gotError = false; + var messages = []; + p.ontext = function(data) { + gotData = true; + messages.push(data); + }; + p.onerror = function(reason, code) { + gotError = code == true; + }; + + for (var i = 0; i < packets.length && !gotError; ++i) { + p.add(getBufferFromHexString(packets[i])); + } + expect(gotError).to.equal(true); + expect(messages[0]).to.equal('l'); + expect(messages[1]).to.equal('l'); + expect(messages.length).to.equal(2); + }); + + it('can parse close messages', function() { + var p = new Receiver(); + var packets = [ + 'ff 00' + ]; + + var gotClose = false; + var gotError = false; + p.onclose = function() { + gotClose = true; + }; + p.onerror = function(reason, code) { + gotError = code == true; + }; + + for (var i = 0; i < packets.length && !gotError; ++i) { + p.add(getBufferFromHexString(packets[i])); + } + expect(gotClose).to.equal(true); + expect(gotError).to.equal(false); + }); + + it('can parse binary messages delivered over multiple frames', function() { + var p = new Receiver(); + var packets = [ + '80 05 48', + '65 6c 6c', + '6f 80 80 05 48', + '65', + '6c 6c 6f' + ]; + + var gotData = false; + var messages = []; + p.ontext = function(data) { + gotData = true; + messages.push(data); + }; + + for (var i = 0; i < packets.length; ++i) { + p.add(getBufferFromHexString(packets[i])); + } + expect(gotData).to.equal(true); + for (var i = 0; i < 2; ++i) { + expect(messages[i]).to.equal('Hello'); + } + }); +}); diff --git a/node_modules/ws/test/Receiver.test.js b/node_modules/ws/test/Receiver.test.js new file mode 100644 index 0000000..b0b5c0a --- /dev/null +++ b/node_modules/ws/test/Receiver.test.js @@ -0,0 +1,255 @@ +var assert = require('assert') + , Receiver = require('../lib/Receiver'); +require('should'); +require('./hybi-common'); + +describe('Receiver', function() { + it('can parse unmasked text message', function() { + var p = new Receiver(); + var packet = '81 05 48 65 6c 6c 6f'; + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal('Hello', data); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); + it('can parse close message', function() { + var p = new Receiver(); + var packet = '88 00'; + + var gotClose = false; + p.onclose = function(data) { + gotClose = true; + }; + + p.add(getBufferFromHexString(packet)); + gotClose.should.be.ok; + }); + it('can parse masked text message', function() { + var p = new Receiver(); + var packet = '81 93 34 83 a8 68 01 b9 92 52 4f a1 c6 09 59 e6 8a 52 16 e6 cb 00 5b a1 d5'; + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal('5:::{"name":"echo"}', data); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); + it('can parse a masked text message longer than 125 bytes', function() { + var p = new Receiver(); + var message = 'A'; + for (var i = 0; i < 300; ++i) message += (i % 5).toString(); + var packet = '81 FE ' + pack(4, message.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal(message, data); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); + it('can parse a really long masked text message', function() { + var p = new Receiver(); + var message = 'A'; + for (var i = 0; i < 64*1024; ++i) message += (i % 5).toString(); + var packet = '81 FF ' + pack(16, message.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal(message, data); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); + it('can parse a fragmented masked text message of 300 bytes', function() { + var p = new Receiver(); + var message = 'A'; + for (var i = 0; i < 300; ++i) message += (i % 5).toString(); + var msgpiece1 = message.substr(0, 150); + var msgpiece2 = message.substr(150); + var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68')); + var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68')); + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal(message, data); + }; + + p.add(getBufferFromHexString(packet1)); + p.add(getBufferFromHexString(packet2)); + gotData.should.be.ok; + }); + it('can parse a ping message', function() { + var p = new Receiver(); + var message = 'Hello'; + var packet = '89 ' + getHybiLengthAsHexString(message.length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); + + var gotPing = false; + p.onping = function(data) { + gotPing = true; + assert.equal(message, data); + }; + + p.add(getBufferFromHexString(packet)); + gotPing.should.be.ok; + }); + it('can parse a ping with no data', function() { + var p = new Receiver(); + var packet = '89 00'; + + var gotPing = false; + p.onping = function(data) { + gotPing = true; + }; + + p.add(getBufferFromHexString(packet)); + gotPing.should.be.ok; + }); + it('can parse a fragmented masked text message of 300 bytes with a ping in the middle', function() { + var p = new Receiver(); + var message = 'A'; + for (var i = 0; i < 300; ++i) message += (i % 5).toString(); + + var msgpiece1 = message.substr(0, 150); + var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68')); + + var pingMessage = 'Hello'; + var pingPacket = '89 ' + getHybiLengthAsHexString(pingMessage.length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(pingMessage, '34 83 a8 68')); + + var msgpiece2 = message.substr(150); + var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68')); + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal(message, data); + }; + var gotPing = false; + p.onping = function(data) { + gotPing = true; + assert.equal(pingMessage, data); + }; + + p.add(getBufferFromHexString(packet1)); + p.add(getBufferFromHexString(pingPacket)); + p.add(getBufferFromHexString(packet2)); + gotData.should.be.ok; + gotPing.should.be.ok; + }); + it('can parse a fragmented masked text message of 300 bytes with a ping in the middle, which is delievered over sevaral tcp packets', function() { + var p = new Receiver(); + var message = 'A'; + for (var i = 0; i < 300; ++i) message += (i % 5).toString(); + + var msgpiece1 = message.substr(0, 150); + var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68')); + + var pingMessage = 'Hello'; + var pingPacket = '89 ' + getHybiLengthAsHexString(pingMessage.length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(pingMessage, '34 83 a8 68')); + + var msgpiece2 = message.substr(150); + var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68')); + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal(message, data); + }; + var gotPing = false; + p.onping = function(data) { + gotPing = true; + assert.equal(pingMessage, data); + }; + + var buffers = []; + buffers = buffers.concat(splitBuffer(getBufferFromHexString(packet1))); + buffers = buffers.concat(splitBuffer(getBufferFromHexString(pingPacket))); + buffers = buffers.concat(splitBuffer(getBufferFromHexString(packet2))); + for (var i = 0; i < buffers.length; ++i) { + p.add(buffers[i]); + } + gotData.should.be.ok; + gotPing.should.be.ok; + }); + it('can parse a 100 byte long masked binary message', function() { + var p = new Receiver(); + var length = 100; + var message = new Buffer(length); + for (var i = 0; i < length; ++i) message[i] = i % 256; + var originalMessage = getHexStringFromBuffer(message); + var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); + + var gotData = false; + p.onbinary = function(data) { + gotData = true; + assert.equal(originalMessage, getHexStringFromBuffer(data)); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); + it('can parse a 256 byte long masked binary message', function() { + var p = new Receiver(); + var length = 256; + var message = new Buffer(length); + for (var i = 0; i < length; ++i) message[i] = i % 256; + var originalMessage = getHexStringFromBuffer(message); + var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); + + var gotData = false; + p.onbinary = function(data) { + gotData = true; + assert.equal(originalMessage, getHexStringFromBuffer(data)); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); + it('can parse a 200kb long masked binary message', function() { + var p = new Receiver(); + var length = 200 * 1024; + var message = new Buffer(length); + for (var i = 0; i < length; ++i) message[i] = i % 256; + var originalMessage = getHexStringFromBuffer(message); + var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); + + var gotData = false; + p.onbinary = function(data) { + gotData = true; + assert.equal(originalMessage, getHexStringFromBuffer(data)); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); + it('can parse a 200kb long unmasked binary message', function() { + var p = new Receiver(); + var length = 200 * 1024; + var message = new Buffer(length); + for (var i = 0; i < length; ++i) message[i] = i % 256; + var originalMessage = getHexStringFromBuffer(message); + var packet = '82 ' + getHybiLengthAsHexString(length, false) + ' ' + getHexStringFromBuffer(message); + + var gotData = false; + p.onbinary = function(data) { + gotData = true; + assert.equal(originalMessage, getHexStringFromBuffer(data)); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); +}); + diff --git a/node_modules/ws/test/Sender.hixie.test.js b/node_modules/ws/test/Sender.hixie.test.js new file mode 100644 index 0000000..783f892 --- /dev/null +++ b/node_modules/ws/test/Sender.hixie.test.js @@ -0,0 +1,134 @@ +var assert = require('assert') + , Sender = require('../lib/Sender.hixie'); +require('should'); +require('./hybi-common'); + +describe('Sender', function() { + describe('#send', function() { + it('frames and sends a text message', function(done) { + var message = 'Hello world'; + var received; + var socket = { + write: function(data, encoding, cb) { + received = data; + process.nextTick(cb); + } + }; + var sender = new Sender(socket, {}); + sender.send(message, {}, function() { + received.toString('utf8').should.eql('\u0000' + message + '\ufffd'); + done(); + }); + }); + + it('frames and sends an empty message', function(done) { + var socket = { + write: function(data, encoding, cb) { + done(); + } + }; + var sender = new Sender(socket, {}); + sender.send('', {}, function() {}); + }); + + it('frames and sends a buffer', function(done) { + var received; + var socket = { + write: function(data, encoding, cb) { + received = data; + process.nextTick(cb); + } + }; + var sender = new Sender(socket, {}); + sender.send(new Buffer('foobar'), {}, function() { + received.toString('utf8').should.eql('\u0000foobar\ufffd'); + done(); + }); + }); + + it('frames and sends a binary message', function(done) { + var message = 'Hello world'; + var received; + var socket = { + write: function(data, encoding, cb) { + received = data; + process.nextTick(cb); + } + }; + var sender = new Sender(socket, {}); + sender.send(message, {binary: true}, function() { + received.toString('hex').should.eql( + // 0x80 0x0b H e l l o w o r l d + '800b48656c6c6f20776f726c64'); + done(); + }); + }); +/* + it('throws an exception for binary data', function(done) { + var socket = { + write: function(data, encoding, cb) { + process.nextTick(cb); + } + }; + var sender = new Sender(socket, {}); + sender.on('error', function() { + done(); + }); + sender.send(new Buffer(100), {binary: true}, function() {}); + }); +*/ + it('can fauxe stream data', function(done) { + var received = []; + var socket = { + write: function(data, encoding, cb) { + received.push(data); + process.nextTick(cb); + } + }; + var sender = new Sender(socket, {}); + sender.send(new Buffer('foobar'), { fin: false }, function() {}); + sender.send('bazbar', { fin: false }, function() {}); + sender.send(new Buffer('end'), { fin: true }, function() { + received[0].toString('utf8').should.eql('\u0000foobar'); + received[1].toString('utf8').should.eql('bazbar'); + received[2].toString('utf8').should.eql('end\ufffd'); + done(); + }); + }); + }); + + describe('#close', function() { + it('sends a hixie close frame', function(done) { + var received; + var socket = { + write: function(data, encoding, cb) { + received = data; + process.nextTick(cb); + } + }; + var sender = new Sender(socket, {}); + sender.close(null, null, null, function() { + received.toString('utf8').should.eql('\ufffd\u0000'); + done(); + }); + }); + + it('sends a message end marker if fauxe streaming has started, before hixie close frame', function(done) { + var received = []; + var socket = { + write: function(data, encoding, cb) { + received.push(data); + if (cb) process.nextTick(cb); + } + }; + var sender = new Sender(socket, {}); + sender.send(new Buffer('foobar'), { fin: false }, function() {}); + sender.close(null, null, null, function() { + received[0].toString('utf8').should.eql('\u0000foobar'); + received[1].toString('utf8').should.eql('\ufffd'); + received[2].toString('utf8').should.eql('\ufffd\u0000'); + done(); + }); + }); + }); +}); diff --git a/node_modules/ws/test/Sender.test.js b/node_modules/ws/test/Sender.test.js new file mode 100644 index 0000000..43b4864 --- /dev/null +++ b/node_modules/ws/test/Sender.test.js @@ -0,0 +1,24 @@ +var Sender = require('../lib/Sender'); +require('should'); + +describe('Sender', function() { + describe('#frameAndSend', function() { + it('does not modify a masked binary buffer', function() { + var sender = new Sender({ write: function() {} }); + var buf = new Buffer([1, 2, 3, 4, 5]); + sender.frameAndSend(2, buf, true, true); + buf[0].should.eql(1); + buf[1].should.eql(2); + buf[2].should.eql(3); + buf[3].should.eql(4); + buf[4].should.eql(5); + }); + + it('does not modify a masked text buffer', function() { + var sender = new Sender({ write: function() {} }); + var text = 'hi there'; + sender.frameAndSend(1, text, true, true); + text.should.eql('hi there'); + }); + }); +}); diff --git a/node_modules/ws/test/Validation.test.js b/node_modules/ws/test/Validation.test.js new file mode 100644 index 0000000..37c3399 --- /dev/null +++ b/node_modules/ws/test/Validation.test.js @@ -0,0 +1,23 @@ +var Validation = require('../lib/Validation').Validation; +require('should'); + +describe('Validation', function() { + describe('isValidUTF8', function() { + it('should return true for a valid utf8 string', function() { + var validBuffer = new Buffer('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque gravida mattis rhoncus. Donec iaculis, metus quis varius accumsan, erat mauris condimentum diam, et egestas erat enim ut ligula. Praesent sollicitudin tellus eget dolor euismod euismod. Nullam ac augue nec neque varius luctus. Curabitur elit mi, consequat ultricies adipiscing mollis, scelerisque in erat. Phasellus facilisis fermentum ullamcorper. Nulla et sem eu arcu pharetra pellentesque. Praesent consectetur tempor justo, vel iaculis dui ullamcorper sit amet. Integer tristique viverra ullamcorper. Vivamus laoreet, nulla eget suscipit eleifend, lacus lectus feugiat libero, non fermentum erat nisi at risus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut pulvinar dignissim tellus, eu dignissim lorem vulputate quis. Morbi ut pulvinar augue.'); + Validation.isValidUTF8(validBuffer).should.be.ok; + }); + it('should return false for an erroneous string', function() { + var invalidBuffer = new Buffer([0xce, 0xba, 0xe1, 0xbd, 0xb9, 0xcf, 0x83, 0xce, 0xbc, 0xce, 0xb5, 0xed, 0xa0, 0x80, 0x65, 0x64, 0x69, 0x74, 0x65, 0x64]); + Validation.isValidUTF8(invalidBuffer).should.not.be.ok; + }); + it('should return true for valid cases from the autobahn test suite', function() { + Validation.isValidUTF8(new Buffer('\xf0\x90\x80\x80')).should.be.ok; + Validation.isValidUTF8(new Buffer([0xf0, 0x90, 0x80, 0x80])).should.be.ok; + }); + it('should return false for erroneous autobahn strings', function() { + Validation.isValidUTF8(new Buffer([0xce, 0xba, 0xe1, 0xbd])).should.not.be.ok; + }); + }); +}); + diff --git a/node_modules/ws/test/WebSocket.integration.js b/node_modules/ws/test/WebSocket.integration.js new file mode 100644 index 0000000..5d4f426 --- /dev/null +++ b/node_modules/ws/test/WebSocket.integration.js @@ -0,0 +1,44 @@ +var assert = require('assert') + , WebSocket = require('../') + , server = require('./testserver'); + +var port = 20000; + +function getArrayBuffer(buf) { + var l = buf.length; + var arrayBuf = new ArrayBuffer(l); + var uint8View = new Uint8Array(arrayBuf); + + for (var i = 0; i < l; i++) { + uint8View[i] = buf[i]; + } + return uint8View.buffer; +} + +function areArraysEqual(x, y) { + if (x.length != y.length) return false; + for (var i = 0, l = x.length; i < l; ++i) { + if (x[i] !== y[i]) return false; + } + return true; +} + +describe('WebSocket', function() { + it('communicates successfully with echo service', function(done) { + var ws = new WebSocket('ws://echo.websocket.org/', {protocolVersion: 13, origin: 'http://websocket.org'}); + var str = Date.now().toString(); + var dataReceived = false; + ws.on('open', function() { + ws.send(str, {mask: true}); + }); + ws.on('close', function() { + assert.equal(true, dataReceived); + done(); + }); + ws.on('message', function(data, flags) { + assert.equal(str, data); + ws.terminate(); + dataReceived = true; + }); + }); +}); diff --git a/node_modules/ws/test/WebSocket.test.js b/node_modules/ws/test/WebSocket.test.js new file mode 100644 index 0000000..91336b9 --- /dev/null +++ b/node_modules/ws/test/WebSocket.test.js @@ -0,0 +1,1724 @@ +var assert = require('assert') + , https = require('https') + , http = require('http') + , should = require('should') + , WebSocket = require('../') + , WebSocketServer = require('../').Server + , fs = require('fs') + , server = require('./testserver') + , crypto = require('crypto'); + +var port = 20000; + +function getArrayBuffer(buf) { + var l = buf.length; + var arrayBuf = new ArrayBuffer(l); + var uint8View = new Uint8Array(arrayBuf); + for (var i = 0; i < l; i++) { + uint8View[i] = buf[i]; + } + return uint8View.buffer; +} + + +function areArraysEqual(x, y) { + if (x.length != y.length) return false; + for (var i = 0, l = x.length; i < l; ++i) { + if (x[i] !== y[i]) return false; + } + return true; +} + +describe('WebSocket', function() { + describe('#ctor', function() { + it('throws exception for invalid url', function(done) { + try { + var ws = new WebSocket('echo.websocket.org'); + } + catch (e) { + done(); + } + }); + }); + + describe('options', function() { + it('should accept an `agent` option', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var agent = { + addRequest: function() { + wss.close(); + done(); + } + }; + var ws = new WebSocket('ws://localhost:' + port, { agent: agent }); + }); + }); + // GH-227 + it('should accept the `options` object as the 3rd argument', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var agent = { + addRequest: function() { + wss.close(); + done(); + } + }; + var ws = new WebSocket('ws://localhost:' + port, [], { agent: agent }); + }); + }); + }); + + describe('properties', function() { + it('#bytesReceived exposes number of bytes received', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('message', function() { + ws.bytesReceived.should.eql(8); + wss.close(); + done(); + }); + }); + wss.on('connection', function(ws) { + ws.send('foobar'); + }); + }); + + it('#url exposes the server url', function(done) { + server.createServer(++port, function(srv) { + var url = 'ws://localhost:' + port; + var ws = new WebSocket(url); + assert.equal(url, ws.url); + ws.terminate(); + ws.on('close', function() { + srv.close(); + done(); + }); + }); + }); + + it('#protocolVersion exposes the protocol version', function(done) { + server.createServer(++port, function(srv) { + var url = 'ws://localhost:' + port; + var ws = new WebSocket(url); + assert.equal(13, ws.protocolVersion); + ws.terminate(); + ws.on('close', function() { + srv.close(); + done(); + }); + }); + }); + + describe('#bufferedAmount', function() { + it('defaults to zero', function(done) { + server.createServer(++port, function(srv) { + var url = 'ws://localhost:' + port; + var ws = new WebSocket(url); + assert.equal(0, ws.bufferedAmount); + ws.terminate(); + ws.on('close', function() { + srv.close(); + done(); + }); + }); + }); + + it('defaults to zero upon "open"', function(done) { + server.createServer(++port, function(srv) { + var url = 'ws://localhost:' + port; + var ws = new WebSocket(url); + ws.onopen = function() { + assert.equal(0, ws.bufferedAmount); + ws.terminate(); + ws.on('close', function() { + srv.close(); + done(); + }); + }; + }); + }); + + it('stress kernel write buffer', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + }); + wss.on('connection', function(ws) { + while (true) { + if (ws.bufferedAmount > 0) break; + ws.send((new Array(10000)).join('hello')); + } + ws.terminate(); + ws.on('close', function() { + wss.close(); + done(); + }); + }); + }); + }); + + describe('Custom headers', function() { + it('request has an authorization header', function (done) { + var auth = 'test:testpass'; + var srv = http.createServer(function (req, res) {}); + var wss = new WebSocketServer({server: srv}); + srv.listen(++port); + var ws = new WebSocket('ws://' + auth + '@localhost:' + port); + srv.on('upgrade', function (req, socket, head) { + assert(req.headers.authorization, 'auth header exists'); + assert.equal(req.headers.authorization, 'Basic ' + new Buffer(auth).toString('base64')); + ws.terminate(); + ws.on('close', function () { + srv.close(); + wss.close(); + done(); + }); + }); + }); + + it('accepts custom headers', function (done) { + var srv = http.createServer(function (req, res) {}); + var wss = new WebSocketServer({server: srv}); + srv.listen(++port); + + var ws = new WebSocket('ws://localhost:' + port, { + headers: { + 'Cookie': 'foo=bar' + } + }); + + srv.on('upgrade', function (req, socket, head) { + assert(req.headers.cookie, 'auth header exists'); + assert.equal(req.headers.cookie, 'foo=bar'); + + ws.terminate(); + ws.on('close', function () { + srv.close(); + wss.close(); + done(); + }); + }); + }); + }); + + describe('#readyState', function() { + it('defaults to connecting', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + assert.equal(WebSocket.CONNECTING, ws.readyState); + ws.terminate(); + ws.on('close', function() { + srv.close(); + done(); + }); + }); + }); + + it('set to open once connection is established', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + assert.equal(WebSocket.OPEN, ws.readyState); + srv.close(); + done(); + }); + }); + }); + + it('set to closed once connection is closed', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.close(1001); + ws.on('close', function() { + assert.equal(WebSocket.CLOSED, ws.readyState); + srv.close(); + done(); + }); + }); + }); + + it('set to closed once connection is terminated', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.terminate(); + ws.on('close', function() { + assert.equal(WebSocket.CLOSED, ws.readyState); + srv.close(); + done(); + }); + }); + }); + }); + + /* + * Ready state constants + */ + + var readyStates = { + CONNECTING: 0, + OPEN: 1, + CLOSING: 2, + CLOSED: 3 + }; + + /* + * Ready state constant tests + */ + + Object.keys(readyStates).forEach(function(state) { + describe('.' + state, function() { + it('is enumerable property of class', function() { + var propertyDescripter = Object.getOwnPropertyDescriptor(WebSocket, state) + assert.equal(readyStates[state], propertyDescripter.value); + assert.equal(true, propertyDescripter.enumerable); + }); + }); + }); + + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + Object.keys(readyStates).forEach(function(state) { + describe('.' + state, function() { + it('is property of instance', function() { + assert.equal(readyStates[state], ws[state]); + }); + }); + }); + }); + }); + + describe('events', function() { + it('emits a ping event', function(done) { + var wss = new WebSocketServer({port: ++port}); + wss.on('connection', function(client) { + client.ping(); + }); + var ws = new WebSocket('ws://localhost:' + port); + ws.on('ping', function() { + ws.terminate(); + wss.close(); + done(); + }); + }); + + it('emits a pong event', function(done) { + var wss = new WebSocketServer({port: ++port}); + wss.on('connection', function(client) { + client.pong(); + }); + var ws = new WebSocket('ws://localhost:' + port); + ws.on('pong', function() { + ws.terminate(); + wss.close(); + done(); + }); + }); + }); + + describe('connection establishing', function() { + it('can disconnect before connection is established', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.terminate(); + ws.on('open', function() { + assert.fail('connect shouldnt be raised here'); + }); + ws.on('close', function() { + srv.close(); + done(); + }); + }); + }); + + it('can close before connection is established', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.close(1001); + ws.on('open', function() { + assert.fail('connect shouldnt be raised here'); + }); + ws.on('close', function() { + srv.close(); + done(); + }); + }); + }); + + it('invalid server key is denied', function(done) { + server.createServer(++port, server.handlers.invalidKey, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('error', function() { + srv.close(); + done(); + }); + }); + }); + + it('close event is raised when server closes connection', function(done) { + server.createServer(++port, server.handlers.closeAfterConnect, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('close', function() { + srv.close(); + done(); + }); + }); + }); + + it('error is emitted if server aborts connection', function(done) { + server.createServer(++port, server.handlers.return401, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + assert.fail('connect shouldnt be raised here'); + }); + ws.on('error', function() { + srv.close(); + done(); + }); + }); + }); + }); + + describe('#pause and #resume', function() { + it('pauses the underlying stream', function(done) { + // this test is sort-of racecondition'y, since an unlikely slow connection + // to localhost can cause the test to succeed even when the stream pausing + // isn't working as intended. that is an extremely unlikely scenario, though + // and an acceptable risk for the test. + var client; + var serverClient; + var openCount = 0; + function onOpen() { + if (++openCount == 2) { + var paused = true; + serverClient.on('message', function() { + paused.should.not.be.ok; + wss.close(); + done(); + }); + serverClient.pause(); + setTimeout(function() { + paused = false; + serverClient.resume(); + }, 200); + client.send('foo'); + } + } + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + serverClient = ws; + serverClient.on('open', onOpen); + }); + wss.on('connection', function(ws) { + client = ws; + onOpen(); + }); + }); + }); + + describe('#ping', function() { + it('before connect should fail', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('error', function() {}); + try { + ws.ping(); + } + catch (e) { + srv.close(); + ws.terminate(); + done(); + } + }); + }); + + it('before connect can silently fail', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('error', function() {}); + ws.ping('', {}, true); + srv.close(); + ws.terminate(); + done(); + }); + }); + + it('without message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.ping(); + }); + srv.on('ping', function(message) { + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('with message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.ping('hi'); + }); + srv.on('ping', function(message) { + assert.equal('hi', message); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('with encoded message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.ping('hi', {mask: true}); + }); + srv.on('ping', function(message, flags) { + assert.ok(flags.masked); + assert.equal('hi', message); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + }); + + describe('#pong', function() { + it('before connect should fail', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('error', function() {}); + try { + ws.pong(); + } + catch (e) { + srv.close(); + ws.terminate(); + done(); + } + }); + }); + + it('before connect can silently fail', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('error', function() {}); + ws.pong('', {}, true); + srv.close(); + ws.terminate(); + done(); + }); + }); + + it('without message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.pong(); + }); + srv.on('pong', function(message) { + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('with message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.pong('hi'); + }); + srv.on('pong', function(message) { + assert.equal('hi', message); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('with encoded message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.pong('hi', {mask: true}); + }); + srv.on('pong', function(message, flags) { + assert.ok(flags.masked); + assert.equal('hi', message); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + }); + + describe('#send', function() { + it('very long binary data can be sent and received (with echoing server)', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var array = new Float32Array(5 * 1024 * 1024); + for (var i = 0; i < array.length; ++i) array[i] = i / 5; + ws.on('open', function() { + ws.send(array, {binary: true}); + }); + ws.on('message', function(message, flags) { + assert.ok(flags.binary); + assert.ok(areArraysEqual(array, new Float32Array(getArrayBuffer(message)))); + ws.terminate(); + srv.close(); + done(); + }); + }); + }); + + it('can send and receive text data', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.send('hi'); + }); + ws.on('message', function(message, flags) { + assert.equal('hi', message); + ws.terminate(); + srv.close(); + done(); + }); + }); + }); + + it('send and receive binary data as an array', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var array = new Float32Array(6); + for (var i = 0; i < array.length; ++i) array[i] = i / 2; + var partial = array.subarray(2, 5); + ws.on('open', function() { + ws.send(partial, {binary: true}); + }); + ws.on('message', function(message, flags) { + assert.ok(flags.binary); + assert.ok(areArraysEqual(partial, new Float32Array(getArrayBuffer(message)))); + ws.terminate(); + srv.close(); + done(); + }); + }); + }); + + it('binary data can be sent and received as buffer', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var buf = new Buffer('foobar'); + ws.on('open', function() { + ws.send(buf, {binary: true}); + }); + ws.on('message', function(message, flags) { + assert.ok(flags.binary); + assert.ok(areArraysEqual(buf, message)); + ws.terminate(); + srv.close(); + done(); + }); + }); + }); + + it('ArrayBuffer is auto-detected without binary flag', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var array = new Float32Array(5); + for (var i = 0; i < array.length; ++i) array[i] = i / 2; + ws.on('open', function() { + ws.send(array.buffer); + }); + ws.onmessage = function (event) { + assert.ok(event.type = 'Binary'); + assert.ok(areArraysEqual(array, new Float32Array(getArrayBuffer(event.data)))); + ws.terminate(); + srv.close(); + done(); + }; + }); + }); + + it('Buffer is auto-detected without binary flag', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var buf = new Buffer('foobar'); + ws.on('open', function() { + ws.send(buf); + }); + ws.onmessage = function (event) { + assert.ok(event.type = 'Binary'); + assert.ok(areArraysEqual(event.data, buf)); + ws.terminate(); + srv.close(); + done(); + }; + }); + }); + + it('before connect should fail', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('error', function() {}); + try { + ws.send('hi'); + } + catch (e) { + ws.terminate(); + srv.close(); + done(); + } + }); + }); + + it('before connect should pass error through callback, if present', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('error', function() {}); + ws.send('hi', function(error) { + assert.ok(error instanceof Error); + ws.terminate(); + srv.close(); + done(); + }); + }); + }); + + it('without data should be successful', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.send(); + }); + srv.on('message', function(message, flags) { + assert.equal('', message); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('calls optional callback when flushed', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.send('hi', function() { + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + }); + + it('with unencoded message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.send('hi'); + }); + srv.on('message', function(message, flags) { + assert.equal('hi', message); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('with encoded message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.send('hi', {mask: true}); + }); + srv.on('message', function(message, flags) { + assert.ok(flags.masked); + assert.equal('hi', message); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('with unencoded binary message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var array = new Float32Array(5); + for (var i = 0; i < array.length; ++i) array[i] = i / 2; + ws.on('open', function() { + ws.send(array, {binary: true}); + }); + srv.on('message', function(message, flags) { + assert.ok(flags.binary); + assert.ok(areArraysEqual(array, new Float32Array(getArrayBuffer(message)))); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('with encoded binary message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var array = new Float32Array(5); + for (var i = 0; i < array.length; ++i) array[i] = i / 2; + ws.on('open', function() { + ws.send(array, {mask: true, binary: true}); + }); + srv.on('message', function(message, flags) { + assert.ok(flags.binary); + assert.ok(flags.masked); + assert.ok(areArraysEqual(array, new Float32Array(getArrayBuffer(message)))); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('with binary stream will send fragmented data', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var callbackFired = false; + ws.on('open', function() { + var fileStream = fs.createReadStream('test/fixtures/textfile'); + fileStream.bufferSize = 100; + ws.send(fileStream, {binary: true}, function(error) { + assert.equal(null, error); + callbackFired = true; + }); + }); + srv.on('message', function(data, flags) { + assert.ok(flags.binary); + assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile'), data)); + ws.terminate(); + }); + ws.on('close', function() { + assert.ok(callbackFired); + srv.close(); + done(); + }); + }); + }); + + it('with text stream will send fragmented data', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var callbackFired = false; + ws.on('open', function() { + var fileStream = fs.createReadStream('test/fixtures/textfile'); + fileStream.setEncoding('utf8'); + fileStream.bufferSize = 100; + ws.send(fileStream, {binary: false}, function(error) { + assert.equal(null, error); + callbackFired = true; + }); + }); + srv.on('message', function(data, flags) { + assert.ok(!flags.binary); + assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data)); + ws.terminate(); + }); + ws.on('close', function() { + assert.ok(callbackFired); + srv.close(); + done(); + }); + }); + }); + + it('will cause intermittent send to be delayed in order', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + var fileStream = fs.createReadStream('test/fixtures/textfile'); + fileStream.setEncoding('utf8'); + fileStream.bufferSize = 100; + ws.send(fileStream); + ws.send('foobar'); + ws.send('baz'); + }); + var receivedIndex = 0; + srv.on('message', function(data, flags) { + ++receivedIndex; + if (receivedIndex == 1) { + assert.ok(!flags.binary); + assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data)); + } + else if (receivedIndex == 2) { + assert.ok(!flags.binary); + assert.equal('foobar', data); + } + else { + assert.ok(!flags.binary); + assert.equal('baz', data); + srv.close(); + ws.terminate(); + done(); + } + }); + }); + }); + + it('will cause intermittent stream to be delayed in order', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + var fileStream = fs.createReadStream('test/fixtures/textfile'); + fileStream.setEncoding('utf8'); + fileStream.bufferSize = 100; + ws.send(fileStream); + var i = 0; + ws.stream(function(error, send) { + assert.ok(!error); + if (++i == 1) send('foo'); + else send('bar', true); + }); + }); + var receivedIndex = 0; + srv.on('message', function(data, flags) { + ++receivedIndex; + if (receivedIndex == 1) { + assert.ok(!flags.binary); + assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data)); + } + else if (receivedIndex == 2) { + assert.ok(!flags.binary); + assert.equal('foobar', data); + srv.close(); + ws.terminate(); + done(); + } + }); + }); + }); + + it('will cause intermittent ping to be delivered', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + var fileStream = fs.createReadStream('test/fixtures/textfile'); + fileStream.setEncoding('utf8'); + fileStream.bufferSize = 100; + ws.send(fileStream); + ws.ping('foobar'); + }); + var receivedIndex = 0; + srv.on('message', function(data, flags) { + assert.ok(!flags.binary); + assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data)); + if (++receivedIndex == 2) { + srv.close(); + ws.terminate(); + done(); + } + }); + srv.on('ping', function(data) { + assert.equal('foobar', data); + if (++receivedIndex == 2) { + srv.close(); + ws.terminate(); + done(); + } + }); + }); + }); + + it('will cause intermittent pong to be delivered', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + var fileStream = fs.createReadStream('test/fixtures/textfile'); + fileStream.setEncoding('utf8'); + fileStream.bufferSize = 100; + ws.send(fileStream); + ws.pong('foobar'); + }); + var receivedIndex = 0; + srv.on('message', function(data, flags) { + assert.ok(!flags.binary); + assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data)); + if (++receivedIndex == 2) { + srv.close(); + ws.terminate(); + done(); + } + }); + srv.on('pong', function(data) { + assert.equal('foobar', data); + if (++receivedIndex == 2) { + srv.close(); + ws.terminate(); + done(); + } + }); + }); + }); + + it('will cause intermittent close to be delivered', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + var fileStream = fs.createReadStream('test/fixtures/textfile'); + fileStream.setEncoding('utf8'); + fileStream.bufferSize = 100; + ws.send(fileStream); + ws.close(1000, 'foobar'); + }); + ws.on('close', function() { + srv.close(); + ws.terminate(); + done(); + }); + ws.on('error', function() { /* That's quite alright -- a send was attempted after close */ }); + srv.on('message', function(data, flags) { + assert.ok(!flags.binary); + assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data)); + }); + srv.on('close', function(code, data) { + assert.equal(1000, code); + assert.equal('foobar', data); + }); + }); + }); + }); + + describe('#stream', function() { + it('very long binary data can be streamed', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var buffer = new Buffer(10 * 1024); + for (var i = 0; i < buffer.length; ++i) buffer[i] = i % 0xff; + ws.on('open', function() { + var i = 0; + var blockSize = 800; + var bufLen = buffer.length; + ws.stream({binary: true}, function(error, send) { + assert.ok(!error); + var start = i * blockSize; + var toSend = Math.min(blockSize, bufLen - (i * blockSize)); + var end = start + toSend; + var isFinal = toSend < blockSize; + send(buffer.slice(start, end), isFinal); + i += 1; + }); + }); + srv.on('message', function(data, flags) { + assert.ok(flags.binary); + assert.ok(areArraysEqual(buffer, data)); + ws.terminate(); + srv.close(); + done(); + }); + }); + }); + + it('before connect should pass error through callback', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('error', function() {}); + ws.stream(function(error) { + assert.ok(error instanceof Error); + ws.terminate(); + srv.close(); + done(); + }); + }); + }); + + it('without callback should fail', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var payload = 'HelloWorld'; + ws.on('open', function() { + try { + ws.stream(); + } + catch (e) { + srv.close(); + ws.terminate(); + done(); + } + }); + }); + }); + + it('will cause intermittent send to be delayed in order', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var payload = 'HelloWorld'; + ws.on('open', function() { + var i = 0; + ws.stream(function(error, send) { + assert.ok(!error); + if (++i == 1) { + send(payload.substr(0, 5)); + ws.send('foobar'); + ws.send('baz'); + } + else { + send(payload.substr(5, 5), true); + } + }); + }); + var receivedIndex = 0; + srv.on('message', function(data, flags) { + ++receivedIndex; + if (receivedIndex == 1) { + assert.ok(!flags.binary); + assert.equal(payload, data); + } + else if (receivedIndex == 2) { + assert.ok(!flags.binary); + assert.equal('foobar', data); + } + else { + assert.ok(!flags.binary); + assert.equal('baz', data); + srv.close(); + ws.terminate(); + done(); + } + }); + }); + }); + + it('will cause intermittent stream to be delayed in order', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var payload = 'HelloWorld'; + ws.on('open', function() { + var i = 0; + ws.stream(function(error, send) { + assert.ok(!error); + if (++i == 1) { + send(payload.substr(0, 5)); + var i2 = 0; + ws.stream(function(error, send) { + assert.ok(!error); + if (++i2 == 1) send('foo'); + else send('bar', true); + }); + ws.send('baz'); + } + else send(payload.substr(5, 5), true); + }); + }); + var receivedIndex = 0; + srv.on('message', function(data, flags) { + ++receivedIndex; + if (receivedIndex == 1) { + assert.ok(!flags.binary); + assert.equal(payload, data); + } + else if (receivedIndex == 2) { + assert.ok(!flags.binary); + assert.equal('foobar', data); + } + else if (receivedIndex == 3){ + assert.ok(!flags.binary); + assert.equal('baz', data); + setTimeout(function() { + srv.close(); + ws.terminate(); + done(); + }, 1000); + } + else throw new Error('more messages than we actually sent just arrived'); + }); + }); + }); + + it('will cause intermittent ping to be delivered', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var payload = 'HelloWorld'; + ws.on('open', function() { + var i = 0; + ws.stream(function(error, send) { + assert.ok(!error); + if (++i == 1) { + send(payload.substr(0, 5)); + ws.ping('foobar'); + } + else { + send(payload.substr(5, 5), true); + } + }); + }); + var receivedIndex = 0; + srv.on('message', function(data, flags) { + assert.ok(!flags.binary); + assert.equal(payload, data); + if (++receivedIndex == 2) { + srv.close(); + ws.terminate(); + done(); + } + }); + srv.on('ping', function(data) { + assert.equal('foobar', data); + if (++receivedIndex == 2) { + srv.close(); + ws.terminate(); + done(); + } + }); + }); + }); + + it('will cause intermittent pong to be delivered', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var payload = 'HelloWorld'; + ws.on('open', function() { + var i = 0; + ws.stream(function(error, send) { + assert.ok(!error); + if (++i == 1) { + send(payload.substr(0, 5)); + ws.pong('foobar'); + } + else { + send(payload.substr(5, 5), true); + } + }); + }); + var receivedIndex = 0; + srv.on('message', function(data, flags) { + assert.ok(!flags.binary); + assert.equal(payload, data); + if (++receivedIndex == 2) { + srv.close(); + ws.terminate(); + done(); + } + }); + srv.on('pong', function(data) { + assert.equal('foobar', data); + if (++receivedIndex == 2) { + srv.close(); + ws.terminate(); + done(); + } + }); + }); + }); + + it('will cause intermittent close to be delivered', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var payload = 'HelloWorld'; + var errorGiven = false; + ws.on('open', function() { + var i = 0; + ws.stream(function(error, send) { + if (++i == 1) { + send(payload.substr(0, 5)); + ws.close(1000, 'foobar'); + } + else if(i == 2) { + send(payload.substr(5, 5), true); + } + else if (i == 3) { + assert.ok(error); + errorGiven = true; + } + }); + }); + ws.on('close', function() { + assert.ok(errorGiven); + srv.close(); + ws.terminate(); + done(); + }); + srv.on('message', function(data, flags) { + assert.ok(!flags.binary); + assert.equal(payload, data); + }); + srv.on('close', function(code, data) { + assert.equal(1000, code); + assert.equal('foobar', data); + }); + }); + }); + }); + + describe('#close', function() { + it('will raise error callback, if any, if called during send stream', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var errorGiven = false; + ws.on('open', function() { + var fileStream = fs.createReadStream('test/fixtures/textfile'); + fileStream.setEncoding('utf8'); + fileStream.bufferSize = 100; + ws.send(fileStream, function(error) { + errorGiven = error != null; + }); + ws.close(1000, 'foobar'); + }); + ws.on('close', function() { + setTimeout(function() { + assert.ok(errorGiven); + srv.close(); + ws.terminate(); + done(); + }, 1000); + }); + }); + }); + + it('without invalid first argument throws exception', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + try { + ws.close('error'); + } + catch (e) { + srv.close(); + ws.terminate(); + done(); + } + }); + }); + }); + + it('without reserved error code 1004 throws exception', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + try { + ws.close(1004); + } + catch (e) { + srv.close(); + ws.terminate(); + done(); + } + }); + }); + }); + + it('without message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.close(1000); + }); + srv.on('close', function(code, message, flags) { + assert.equal('', message); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('with message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.close(1000, 'some reason'); + }); + srv.on('close', function(code, message, flags) { + assert.ok(flags.masked); + assert.equal('some reason', message); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('with encoded message is successfully transmitted to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('open', function() { + ws.close(1000, 'some reason', {mask: true}); + }); + srv.on('close', function(code, message, flags) { + assert.ok(flags.masked); + assert.equal('some reason', message); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + + it('ends connection to the server', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var connectedOnce = false; + ws.on('open', function() { + connectedOnce = true; + ws.close(1000, 'some reason', {mask: true}); + }); + ws.on('close', function() { + assert.ok(connectedOnce); + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + }); + + describe('W3C API emulation', function() { + it('should not throw errors when getting and setting', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var listener = function () {}; + + ws.onmessage = listener; + ws.onerror = listener; + ws.onclose = listener; + ws.onopen = listener; + + assert.ok(ws.onopen === listener); + assert.ok(ws.onmessage === listener); + assert.ok(ws.onclose === listener); + assert.ok(ws.onerror === listener); + + srv.close(); + ws.terminate(); + done(); + }); + }); + + it('should work the same as the EventEmitter api', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + var listener = function() {}; + var message = 0; + var close = 0; + var open = 0; + + ws.onmessage = function(messageEvent) { + assert.ok(!!messageEvent.data); + ++message; + ws.close(); + }; + + ws.onopen = function() { + ++open; + } + + ws.onclose = function() { + ++close; + } + + ws.on('open', function() { + ws.send('foo'); + }); + + ws.on('close', function() { + process.nextTick(function() { + assert.ok(message === 1); + assert.ok(open === 1); + assert.ok(close === 1); + + srv.close(); + ws.terminate(); + done(); + }); + }); + }); + }); + + it('should receive text data wrapped in a MessageEvent when using addEventListener', function(done) { + server.createServer(++port, function(srv) { + var ws = new WebSocket('ws://localhost:' + port); + ws.addEventListener('open', function() { + ws.send('hi'); + }); + ws.addEventListener('message', function(messageEvent) { + assert.equal('hi', messageEvent.data); + ws.terminate(); + srv.close(); + done(); + }); + }); + }); + + it('should receive valid CloseEvent when server closes with code 1000', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + ws.addEventListener('close', function(closeEvent) { + assert.equal(true, closeEvent.wasClean); + assert.equal(1000, closeEvent.code); + ws.terminate(); + wss.close(); + done(); + }); + }); + wss.on('connection', function(client) { + client.close(1000); + }); + }); + + it('should receive valid CloseEvent when server closes with code 1001', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + ws.addEventListener('close', function(closeEvent) { + assert.equal(false, closeEvent.wasClean); + assert.equal(1001, closeEvent.code); + assert.equal('some daft reason', closeEvent.reason); + ws.terminate(); + wss.close(); + done(); + }); + }); + wss.on('connection', function(client) { + client.close(1001, 'some daft reason'); + }); + }); + + it('should have target set on Events', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + ws.addEventListener('open', function(openEvent) { + assert.equal(ws, openEvent.target); + }); + ws.addEventListener('message', function(messageEvent) { + assert.equal(ws, messageEvent.target); + wss.close(); + }); + ws.addEventListener('close', function(closeEvent) { + assert.equal(ws, closeEvent.target); + ws.emit('error', new Error('forced')); + }); + ws.addEventListener('error', function(errorEvent) { + assert.equal(errorEvent.message, 'forced'); + assert.equal(ws, errorEvent.target); + ws.terminate(); + done(); + }); + }); + wss.on('connection', function(client) { + client.send('hi') + }); + }); + }); + + describe('ssl', function() { + it('can connect to secure websocket server', function(done) { + var options = { + key: fs.readFileSync('test/fixtures/key.pem'), + cert: fs.readFileSync('test/fixtures/certificate.pem') + }; + var app = https.createServer(options, function (req, res) { + res.writeHead(200); + res.end(); + }); + var wss = new WebSocketServer({server: app}); + app.listen(++port, function() { + var ws = new WebSocket('wss://localhost:' + port); + }); + wss.on('connection', function(ws) { + app.close(); + ws.terminate(); + wss.close(); + done(); + }); + }); + + it('can connect to secure websocket server with client side certificate', function(done) { + var options = { + key: fs.readFileSync('test/fixtures/key.pem'), + cert: fs.readFileSync('test/fixtures/certificate.pem'), + ca: [fs.readFileSync('test/fixtures/ca1-cert.pem')], + requestCert: true + }; + var clientOptions = { + key: fs.readFileSync('test/fixtures/agent1-key.pem'), + cert: fs.readFileSync('test/fixtures/agent1-cert.pem') + }; + var app = https.createServer(options, function (req, res) { + res.writeHead(200); + res.end(); + }); + var success = false; + var wss = new WebSocketServer({ + server: app, + verifyClient: function(info) { + success = !!info.req.client.authorized; + return true; + } + }); + app.listen(++port, function() { + var ws = new WebSocket('wss://localhost:' + port, clientOptions); + }); + wss.on('connection', function(ws) { + app.close(); + ws.terminate(); + wss.close(); + success.should.be.ok; + done(); + }); + }); + + it('cannot connect to secure websocket server via ws://', function(done) { + var options = { + key: fs.readFileSync('test/fixtures/key.pem'), + cert: fs.readFileSync('test/fixtures/certificate.pem') + }; + var app = https.createServer(options, function (req, res) { + res.writeHead(200); + res.end(); + }); + var wss = new WebSocketServer({server: app}); + app.listen(++port, function() { + var ws = new WebSocket('ws://localhost:' + port, { rejectUnauthorized :false }); + ws.on('error', function() { + app.close(); + ws.terminate(); + wss.close(); + done(); + }); + }); + }); + + it('can send and receive text data', function(done) { + var options = { + key: fs.readFileSync('test/fixtures/key.pem'), + cert: fs.readFileSync('test/fixtures/certificate.pem') + }; + var app = https.createServer(options, function (req, res) { + res.writeHead(200); + res.end(); + }); + var wss = new WebSocketServer({server: app}); + app.listen(++port, function() { + var ws = new WebSocket('wss://localhost:' + port); + ws.on('open', function() { + ws.send('foobar'); + }); + }); + wss.on('connection', function(ws) { + ws.on('message', function(message, flags) { + message.should.eql('foobar'); + app.close(); + ws.terminate(); + wss.close(); + done(); + }); + }); + }); + + it('can send and receive very long binary data', function(done) { + var options = { + key: fs.readFileSync('test/fixtures/key.pem'), + cert: fs.readFileSync('test/fixtures/certificate.pem') + } + var app = https.createServer(options, function (req, res) { + res.writeHead(200); + res.end(); + }); + crypto.randomBytes(5 * 1024 * 1024, function(ex, buf) { + if (ex) throw ex; + var wss = new WebSocketServer({server: app}); + app.listen(++port, function() { + var ws = new WebSocket('wss://localhost:' + port); + ws.on('open', function() { + ws.send(buf, {binary: true}); + }); + ws.on('message', function(message, flags) { + flags.binary.should.be.ok; + areArraysEqual(buf, message).should.be.ok; + app.close(); + ws.terminate(); + wss.close(); + done(); + }); + }); + wss.on('connection', function(ws) { + ws.on('message', function(message, flags) { + ws.send(message, {binary: true}); + }); + }); + }); + }); + }); + + describe('protocol support discovery', function() { + describe('#supports', function() { + describe('#binary', function() { + it('returns true for hybi transport', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + }); + wss.on('connection', function(client) { + assert.equal(true, client.supports.binary); + wss.close(); + done(); + }); + }); + + it('returns false for hixie transport', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90' + } + }; + var req = http.request(options); + req.write('WjN}|M(6'); + req.end(); + }); + wss.on('connection', function(client) { + assert.equal(false, client.supports.binary); + wss.close(); + done(); + }); + }); + }); + }); + }); + + describe('host and origin headers', function() { + it('includes the host header with port number', function(done) { + var srv = http.createServer(); + srv.listen(++port, function(){ + srv.on('upgrade', function(req, socket, upgradeHeade) { + assert.equal('localhost:' + port, req.headers['host']); + srv.close(); + done(); + }); + var ws = new WebSocket('ws://localhost:' + port); + }); + }); + + it('includes the origin header with port number', function(done) { + var srv = http.createServer(); + srv.listen(++port, function() { + srv.on('upgrade', function(req, socket, upgradeHeade) { + assert.equal('localhost:' + port, req.headers['origin']); + srv.close(); + done(); + }); + var ws = new WebSocket('ws://localhost:' + port); + }); + }); + }); + +}); diff --git a/node_modules/ws/test/WebSocketServer.test.js b/node_modules/ws/test/WebSocketServer.test.js new file mode 100644 index 0000000..c21fd97 --- /dev/null +++ b/node_modules/ws/test/WebSocketServer.test.js @@ -0,0 +1,1103 @@ +var http = require('http') + , https = require('https') + , WebSocket = require('../') + , WebSocketServer = WebSocket.Server + , fs = require('fs') + , should = require('should'); + +var port = 8000; + +function getArrayBuffer(buf) { + var l = buf.length; + var arrayBuf = new ArrayBuffer(l); + for (var i = 0; i < l; ++i) { + arrayBuf[i] = buf[i]; + } + return arrayBuf; +} + +function areArraysEqual(x, y) { + if (x.length != y.length) return false; + for (var i = 0, l = x.length; i < l; ++i) { + if (x[i] !== y[i]) return false; + } + return true; +} + +describe('WebSocketServer', function() { + describe('#ctor', function() { + it('throws an error if no option object is passed', function() { + var gotException = false; + try { + var wss = new WebSocketServer(); + } + catch (e) { + gotException = true; + } + gotException.should.be.ok; + }); + + it('throws an error if no port or server is specified', function() { + var gotException = false; + try { + var wss = new WebSocketServer({}); + } + catch (e) { + gotException = true; + } + gotException.should.be.ok; + }); + + it('does not throw an error if no port or server is specified, when the noServer option is true', function() { + var gotException = false; + try { + var wss = new WebSocketServer({noServer: true}); + } + catch (e) { + gotException = true; + } + gotException.should.eql(false); + }); + + it('emits an error if http server bind fails', function(done) { + var wss = new WebSocketServer({port: 1}); + wss.on('error', function() { done(); }); + }); + + it('starts a server on a given port', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + }); + wss.on('connection', function(client) { + wss.close(); + done(); + }); + }); + + it('uses a precreated http server', function (done) { + var srv = http.createServer(); + srv.listen(++port, function () { + var wss = new WebSocketServer({server: srv}); + var ws = new WebSocket('ws://localhost:' + port); + + wss.on('connection', function(client) { + wss.close(); + srv.close(); + done(); + }); + }); + }); + + it('uses a precreated http server listening on unix socket', function (done) { + var srv = http.createServer(); + var sockPath = '/tmp/ws_socket_'+new Date().getTime()+'.'+Math.floor(Math.random() * 1000); + srv.listen(sockPath, function () { + var wss = new WebSocketServer({server: srv}); + var ws = new WebSocket('ws+unix://'+sockPath); + + wss.on('connection', function(client) { + wss.close(); + srv.close(); + done(); + }); + }); + }); + + it('emits path specific connection event', function (done) { + var srv = http.createServer(); + srv.listen(++port, function () { + var wss = new WebSocketServer({server: srv}); + var ws = new WebSocket('ws://localhost:' + port+'/endpointName'); + + wss.on('connection/endpointName', function(client) { + wss.close(); + srv.close(); + done(); + }); + }); + }); + + it('can have two different instances listening on the same http server with two different paths', function(done) { + var srv = http.createServer(); + srv.listen(++port, function () { + var wss1 = new WebSocketServer({server: srv, path: '/wss1'}) + , wss2 = new WebSocketServer({server: srv, path: '/wss2'}); + var doneCount = 0; + wss1.on('connection', function(client) { + wss1.close(); + if (++doneCount == 2) { + srv.close(); + done(); + } + }); + wss2.on('connection', function(client) { + wss2.close(); + if (++doneCount == 2) { + srv.close(); + done(); + } + }); + var ws1 = new WebSocket('ws://localhost:' + port + '/wss1'); + var ws2 = new WebSocket('ws://localhost:' + port + '/wss2?foo=1'); + }); + }); + + it('cannot have two different instances listening on the same http server with the same path', function(done) { + var srv = http.createServer(); + srv.listen(++port, function () { + var wss1 = new WebSocketServer({server: srv, path: '/wss1'}); + try { + var wss2 = new WebSocketServer({server: srv, path: '/wss1'}); + } + catch (e) { + wss1.close(); + srv.close(); + done(); + } + }); + }); + }); + + describe('#close', function() { + it('will close all clients', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('close', function() { + if (++closes == 2) done(); + }); + }); + var closes = 0; + wss.on('connection', function(client) { + client.on('close', function() { + if (++closes == 2) done(); + }); + wss.close(); + }); + }); + + it('does not close a precreated server', function(done) { + var srv = http.createServer(); + var realClose = srv.close; + srv.close = function() { + should.fail('must not close pre-created server'); + } + srv.listen(++port, function () { + var wss = new WebSocketServer({server: srv}); + var ws = new WebSocket('ws://localhost:' + port); + wss.on('connection', function(client) { + wss.close(); + srv.close = realClose; + srv.close(); + done(); + }); + }); + }); + + it('cleans up websocket data on a precreated server', function(done) { + var srv = http.createServer(); + srv.listen(++port, function () { + var wss1 = new WebSocketServer({server: srv, path: '/wss1'}) + , wss2 = new WebSocketServer({server: srv, path: '/wss2'}); + (typeof srv._webSocketPaths).should.eql('object'); + Object.keys(srv._webSocketPaths).length.should.eql(2); + wss1.close(); + Object.keys(srv._webSocketPaths).length.should.eql(1); + wss2.close(); + (typeof srv._webSocketPaths).should.eql('undefined'); + srv.close(); + done(); + }); + }); + }); + + describe('#clients', function() { + it('returns a list of connected clients', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + wss.clients.length.should.eql(0); + var ws = new WebSocket('ws://localhost:' + port); + }); + wss.on('connection', function(client) { + wss.clients.length.should.eql(1); + wss.close(); + done(); + }); + }); + + it('can be disabled', function(done) { + var wss = new WebSocketServer({port: ++port, clientTracking: false}, function() { + wss.clients.length.should.eql(0); + var ws = new WebSocket('ws://localhost:' + port); + }); + wss.on('connection', function(client) { + wss.clients.length.should.eql(0); + wss.close(); + done(); + }); + }); + + it('is updated when client terminates the connection', function(done) { + var ws; + var wss = new WebSocketServer({port: ++port}, function() { + ws = new WebSocket('ws://localhost:' + port); + }); + wss.on('connection', function(client) { + client.on('close', function() { + wss.clients.length.should.eql(0); + wss.close(); + done(); + }); + ws.terminate(); + }); + }); + + it('is updated when client closes the connection', function(done) { + var ws; + var wss = new WebSocketServer({port: ++port}, function() { + ws = new WebSocket('ws://localhost:' + port); + }); + wss.on('connection', function(client) { + client.on('close', function() { + wss.clients.length.should.eql(0); + wss.close(); + done(); + }); + ws.close(); + }); + }); + }); + + describe('#options', function() { + it('exposes options passed to constructor', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + wss.options.port.should.eql(port); + wss.close(); + done(); + }); + }); + }); + + describe('#handleUpgrade', function() { + it('can be used for a pre-existing server', function (done) { + var srv = http.createServer(); + srv.listen(++port, function () { + var wss = new WebSocketServer({noServer: true}); + srv.on('upgrade', function(req, socket, upgradeHead) { + wss.handleUpgrade(req, socket, upgradeHead, function(client) { + client.send('hello'); + }); + }); + var ws = new WebSocket('ws://localhost:' + port); + ws.on('message', function(message) { + message.should.eql('hello'); + wss.close(); + srv.close(); + done(); + }); + }); + }); + }); + + describe('hybi mode', function() { + describe('connection establishing', function() { + it('does not accept connections with no sec-websocket-key', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket' + } + }; + var req = http.request(options); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(400); + wss.close(); + done(); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + + it('does not accept connections with no sec-websocket-version', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==' + } + }; + var req = http.request(options); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(400); + wss.close(); + done(); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + + it('does not accept connections with invalid sec-websocket-version', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': 12 + } + }; + var req = http.request(options); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(400); + wss.close(); + done(); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + + it('client can be denied', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o) { + return false; + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': 8, + 'Sec-WebSocket-Origin': 'http://foobar.com' + } + }; + var req = http.request(options); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(401); + process.nextTick(function() { + wss.close(); + done(); + }); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + + it('client can be accepted', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o) { + return true; + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': 13, + 'Origin': 'http://foobar.com' + } + }; + var req = http.request(options); + req.end(); + }); + wss.on('connection', function(ws) { + ws.terminate(); + wss.close(); + done(); + }); + wss.on('error', function() {}); + }); + + it('verifyClient gets client origin', function(done) { + var verifyClientCalled = false; + var wss = new WebSocketServer({port: ++port, verifyClient: function(info) { + info.origin.should.eql('http://foobarbaz.com'); + verifyClientCalled = true; + return false; + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': 13, + 'Origin': 'http://foobarbaz.com' + } + }; + var req = http.request(options); + req.end(); + req.on('response', function(res) { + verifyClientCalled.should.be.ok; + wss.close(); + done(); + }); + }); + wss.on('error', function() {}); + }); + + it('verifyClient gets original request', function(done) { + var verifyClientCalled = false; + var wss = new WebSocketServer({port: ++port, verifyClient: function(info) { + info.req.headers['sec-websocket-key'].should.eql('dGhlIHNhbXBsZSBub25jZQ=='); + verifyClientCalled = true; + return false; + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': 13, + 'Origin': 'http://foobarbaz.com' + } + }; + var req = http.request(options); + req.end(); + req.on('response', function(res) { + verifyClientCalled.should.be.ok; + wss.close(); + done(); + }); + }); + wss.on('error', function() {}); + }); + + it('verifyClient has secure:true for ssl connections', function(done) { + var options = { + key: fs.readFileSync('test/fixtures/key.pem'), + cert: fs.readFileSync('test/fixtures/certificate.pem') + }; + var app = https.createServer(options, function (req, res) { + res.writeHead(200); + res.end(); + }); + var success = false; + var wss = new WebSocketServer({ + server: app, + verifyClient: function(info) { + success = info.secure === true; + return true; + } + }); + app.listen(++port, function() { + var ws = new WebSocket('wss://localhost:' + port); + }); + wss.on('connection', function(ws) { + app.close(); + ws.terminate(); + wss.close(); + success.should.be.ok; + done(); + }); + }); + + it('verifyClient has secure:false for non-ssl connections', function(done) { + var app = http.createServer(function (req, res) { + res.writeHead(200); + res.end(); + }); + var success = false; + var wss = new WebSocketServer({ + server: app, + verifyClient: function(info) { + success = info.secure === false; + return true; + } + }); + app.listen(++port, function() { + var ws = new WebSocket('ws://localhost:' + port); + }); + wss.on('connection', function(ws) { + app.close(); + ws.terminate(); + wss.close(); + success.should.be.ok; + done(); + }); + }); + + it('client can be denied asynchronously', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) { + process.nextTick(function() { + cb(false); + }); + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': 8, + 'Sec-WebSocket-Origin': 'http://foobar.com' + } + }; + var req = http.request(options); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(401); + process.nextTick(function() { + wss.close(); + done(); + }); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + + it('client can be accepted asynchronously', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) { + process.nextTick(function() { + cb(true); + }); + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': 13, + 'Origin': 'http://foobar.com' + } + }; + var req = http.request(options); + req.end(); + }); + wss.on('connection', function(ws) { + ws.terminate(); + wss.close(); + done(); + }); + wss.on('error', function() {}); + }); + + it('handles messages passed along with the upgrade request (upgrade head)', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o) { + return true; + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': 13, + 'Origin': 'http://foobar.com' + } + }; + var req = http.request(options); + req.write(new Buffer([0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f], 'binary')); + req.end(); + }); + wss.on('connection', function(ws) { + ws.on('message', function(data) { + data.should.eql('Hello'); + ws.terminate(); + wss.close(); + done(); + }); + }); + wss.on('error', function() {}); + }); + + it('selects the first protocol by default', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'}); + ws.on('open', function(client) { + ws.protocol.should.eql('prot1'); + wss.close(); + done(); + }); + }); + }); + + it('selects the last protocol via protocol handler', function(done) { + var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) { + cb(true, ps[ps.length-1]); }}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'}); + ws.on('open', function(client) { + ws.protocol.should.eql('prot2'); + wss.close(); + done(); + }); + }); + }); + + it('client detects invalid server protocol', function(done) { + var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) { + cb(true, 'prot3'); }}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'}); + ws.on('open', function(client) { + done(new Error('connection must not be established')); + }); + ws.on('error', function() { + done(); + }); + }); + }); + + it('client detects no server protocol', function(done) { + var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) { + cb(true); }}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'}); + ws.on('open', function(client) { + done(new Error('connection must not be established')); + }); + ws.on('error', function() { + done(); + }); + }); + }); + + it('client refuses server protocols', function(done) { + var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) { + cb(false); }}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'}); + ws.on('open', function(client) { + done(new Error('connection must not be established')); + }); + ws.on('error', function() { + done(); + }); + }); + }); + + it('server detects invalid protocol handler', function(done) { + var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) { + // not calling callback is an error and shouldn't timeout + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': 13, + 'Sec-WebSocket-Origin': 'http://foobar.com' + } + }; + options.port = port; + var req = http.request(options); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(501); + wss.close(); + done(); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + }); + + describe('messaging', function() { + it('can send and receive data', function(done) { + var data = new Array(65*1024); + for (var i = 0; i < data.length; ++i) { + data[i] = String.fromCharCode(65 + ~~(25 * Math.random())); + } + data = data.join(''); + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('message', function(message, flags) { + ws.send(message); + }); + }); + wss.on('connection', function(client) { + client.on('message', function(message) { + message.should.eql(data); + wss.close(); + done(); + }); + client.send(data); + }); + }); + }); + }); + + describe('hixie mode', function() { + it('can be disabled', function(done) { + var wss = new WebSocketServer({port: ++port, disableHixie: true}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90' + } + }; + var req = http.request(options); + req.write('WjN}|M(6'); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(401); + process.nextTick(function() { + wss.close(); + done(); + }); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + + describe('connection establishing', function() { + it('does not accept connections with no sec-websocket-key1', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80' + } + }; + var req = http.request(options); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(400); + wss.close(); + done(); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + + it('does not accept connections with no sec-websocket-key2', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90' + } + }; + var req = http.request(options); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(400); + wss.close(); + done(); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + + it('accepts connections with valid handshake', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90' + } + }; + var req = http.request(options); + req.write('WjN}|M(6'); + req.end(); + }); + wss.on('connection', function(ws) { + ws.terminate(); + wss.close(); + done(); + }); + wss.on('error', function() {}); + }); + + it('client can be denied', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o) { + return false; + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90' + } + }; + var req = http.request(options); + req.write('WjN}|M(6'); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(401); + process.nextTick(function() { + wss.close(); + done(); + }); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + + it('client can be accepted', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o) { + return true; + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90' + } + }; + var req = http.request(options); + req.write('WjN}|M(6'); + req.end(); + }); + wss.on('connection', function(ws) { + ws.terminate(); + wss.close(); + done(); + }); + wss.on('error', function() {}); + }); + + it('verifyClient gets client origin', function(done) { + var verifyClientCalled = false; + var wss = new WebSocketServer({port: ++port, verifyClient: function(info) { + info.origin.should.eql('http://foobarbaz.com'); + verifyClientCalled = true; + return false; + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Origin': 'http://foobarbaz.com', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90' + } + }; + var req = http.request(options); + req.write('WjN}|M(6'); + req.end(); + req.on('response', function(res) { + verifyClientCalled.should.be.ok; + wss.close(); + done(); + }); + }); + wss.on('error', function() {}); + }); + + it('verifyClient gets original request', function(done) { + var verifyClientCalled = false; + var wss = new WebSocketServer({port: ++port, verifyClient: function(info) { + info.req.headers['sec-websocket-key1'].should.eql('3e6b263 4 17 80'); + verifyClientCalled = true; + return false; + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Origin': 'http://foobarbaz.com', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90' + } + }; + var req = http.request(options); + req.write('WjN}|M(6'); + req.end(); + req.on('response', function(res) { + verifyClientCalled.should.be.ok; + wss.close(); + done(); + }); + }); + wss.on('error', function() {}); + }); + + it('client can be denied asynchronously', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) { + cb(false); + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Origin': 'http://foobarbaz.com', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90' + } + }; + var req = http.request(options); + req.write('WjN}|M(6'); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(401); + process.nextTick(function() { + wss.close(); + done(); + }); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + + it('client can be accepted asynchronously', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) { + cb(true); + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Origin': 'http://foobarbaz.com', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90' + } + }; + var req = http.request(options); + req.write('WjN}|M(6'); + req.end(); + }); + wss.on('connection', function(ws) { + wss.close(); + done(); + }); + wss.on('error', function() {}); + }); + + it('handles messages passed along with the upgrade request (upgrade head)', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o) { + return true; + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90', + 'Origin': 'http://foobar.com' + } + }; + var req = http.request(options); + req.write('WjN}|M(6'); + req.write(new Buffer([0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff], 'binary')); + req.end(); + }); + wss.on('connection', function(ws) { + ws.on('message', function(data) { + data.should.eql('Hello'); + ws.terminate(); + wss.close(); + done(); + }); + }); + wss.on('error', function() {}); + }); + }); + }); + + describe('client properties', function() { + it('protocol is exposed', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocol: 'hi'}); + }); + wss.on('connection', function(client) { + client.protocol.should.eql('hi'); + wss.close(); + done(); + }); + }); + + it('protocolVersion is exposed', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocolVersion: 8}); + }); + wss.on('connection', function(client) { + client.protocolVersion.should.eql(8); + wss.close(); + done(); + }); + }); + + it('upgradeReq is the original request object', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocolVersion: 8}); + }); + wss.on('connection', function(client) { + client.upgradeReq.httpVersion.should.eql('1.1'); + wss.close(); + done(); + }); + }); + }); + +}); diff --git a/node_modules/ws/test/autobahn-server.js b/node_modules/ws/test/autobahn-server.js new file mode 100644 index 0000000..36fe0c2 --- /dev/null +++ b/node_modules/ws/test/autobahn-server.js @@ -0,0 +1,29 @@ +var WebSocketServer = require('../').Server; + +process.on('uncaughtException', function(err) { + console.log('Caught exception: ', err, err.stack); +}); + +process.on('SIGINT', function () { + try { + console.log('Updating reports and shutting down'); + var ws = new WebSocket('ws://localhost:9001/updateReports?agent=ws'); + ws.on('close', function() { + process.exit(); + }); + } + catch(e) { + process.exit(); + } +}); + +var wss = new WebSocketServer({port: 8181}); +wss.on('connection', function(ws) { + console.log('new connection'); + ws.on('message', function(data, flags) { + ws.send(flags.buffer, {binary: flags.binary === true}); + }); + ws.on('error', function() { + console.log('error', arguments); + }); +}); diff --git a/node_modules/ws/test/autobahn.js b/node_modules/ws/test/autobahn.js new file mode 100644 index 0000000..048cc90 --- /dev/null +++ b/node_modules/ws/test/autobahn.js @@ -0,0 +1,52 @@ +var WebSocket = require('../'); +var currentTest = 1; +var lastTest = -1; +var testCount = null; + +process.on('uncaughtException', function(err) { + console.log('Caught exception: ', err, err.stack); +}); + +process.on('SIGINT', function () { + try { + console.log('Updating reports and shutting down'); + var ws = new WebSocket('ws://localhost:9001/updateReports?agent=ws'); + ws.on('close', function() { + process.exit(); + }); + } + catch(e) { + process.exit(); + } +}); + +function nextTest() { + if (currentTest > testCount || (lastTest != -1 && currentTest > lastTest)) { + console.log('Updating reports and shutting down'); + var ws = new WebSocket('ws://localhost:9001/updateReports?agent=ws'); + ws.on('close', function() { + process.exit(); + }); + return; + }; + console.log('Running test case ' + currentTest + '/' + testCount); + var ws = new WebSocket('ws://localhost:9001/runCase?case=' + currentTest + '&agent=ws'); + ws.on('message', function(data, flags) { + ws.send(flags.buffer, {binary: flags.binary === true, mask: true}); + }); + ws.on('close', function(data) { + currentTest += 1; + process.nextTick(nextTest); + }); + ws.on('error', function(e) {}); +} + +var ws = new WebSocket('ws://localhost:9001/getCaseCount'); +ws.on('message', function(data, flags) { + testCount = parseInt(data); +}); +ws.on('close', function() { + if (testCount > 0) { + nextTest(); + } +}); \ No newline at end of file diff --git a/node_modules/ws/test/fixtures/agent1-cert.pem b/node_modules/ws/test/fixtures/agent1-cert.pem new file mode 100644 index 0000000..cccb9fb --- /dev/null +++ b/node_modules/ws/test/fixtures/agent1-cert.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICbjCCAdcCCQCVvok5oeLpqzANBgkqhkiG9w0BAQUFADB6MQswCQYDVQQGEwJV +UzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO +BgNVBAsTB05vZGUuanMxDDAKBgNVBAMTA2NhMTEgMB4GCSqGSIb3DQEJARYRcnlA +dGlueWNsb3Vkcy5vcmcwHhcNMTMwMzA4MDAzMDIyWhcNNDAwNzIzMDAzMDIyWjB9 +MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQK +EwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MTEgMB4G +CSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwgZ8wDQYJKoZIhvcNAQEBBQAD +gY0AMIGJAoGBAL6GwKosYb0Yc3Qo0OtQVlCJ4208Idw11ij+t2W5sfYbCil5tyQo +jnhGM1CJhEXynQpXXwjKJuIeTQCkeUibTyFKa0bs8+li2FiGoKYbb4G81ovnqkmE +2iDVb8Gw3rrM4zeZ0ZdFnjMsAZac8h6+C4sB/pS9BiMOo6qTl15RQlcJAgMBAAEw +DQYJKoZIhvcNAQEFBQADgYEAOtmLo8DwTPnI4wfQbQ3hWlTS/9itww6IsxH2ODt9 +ggB7wi7N3uAdIWRZ54ke0NEAO5CW1xNTwsWcxQbiHrDOqX1vfVCjIenI76jVEEap +/Ay53ydHNBKdsKkib61Me14Mu0bA3lUul57VXwmH4NUEFB3w973Q60PschUhOEXj +7DY= +-----END CERTIFICATE----- diff --git a/node_modules/ws/test/fixtures/agent1-key.pem b/node_modules/ws/test/fixtures/agent1-key.pem new file mode 100644 index 0000000..cbd5f0c --- /dev/null +++ b/node_modules/ws/test/fixtures/agent1-key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQC+hsCqLGG9GHN0KNDrUFZQieNtPCHcNdYo/rdlubH2Gwopebck +KI54RjNQiYRF8p0KV18IyibiHk0ApHlIm08hSmtG7PPpYthYhqCmG2+BvNaL56pJ +hNog1W/BsN66zOM3mdGXRZ4zLAGWnPIevguLAf6UvQYjDqOqk5deUUJXCQIDAQAB +AoGANu/CBA+SCyVOvRK70u4yRTzNMAUjukxnuSBhH1rg/pajYnwvG6T6F6IeT72n +P0gKkh3JUE6B0bds+p9yPUZTFUXghxjcF33wlIY44H6gFE4K5WutsFJ9c450wtuu +8rXZTsIg7lAXWjTFVmdtOEPetcGlO2Hpi1O7ZzkzHgB2w9ECQQDksCCYx78or1zY +ZSokm8jmpIjG3VLKdvI9HAoJRN40ldnwFoigrFa1AHwsFtWNe8bKyVRPDoLDUjpB +dkPWgweVAkEA1UfgqguQ2KIkbtp9nDBionu3QaajksrRHwIa8vdfRfLxszfHk2fh +NGY3dkRZF8HUAbzYLrd9poVhCBAEjWekpQJASOM6AHfpnXYHCZF01SYx6hEW5wsz +kARJQODm8f1ZNTlttO/5q/xBxn7ZFNRSTD3fJlL05B2j380ddC/Vf1FT4QJAP1BC +GliqnBSuGhZUWYxni3KMeTm9rzL0F29pjpzutHYlWB2D6ndY/FQnvL0XcZ0Bka58 +womIDGnl3x3aLBwLXQJBAJv6h5CHbXHx7VyDJAcNfppAqZGcEaiVg8yf2F33iWy2 +FLthhJucx7df7SO2aw5h06bRDRAhb9br0R9/3mLr7RE= +-----END RSA PRIVATE KEY----- diff --git a/node_modules/ws/test/fixtures/ca1-cert.pem b/node_modules/ws/test/fixtures/ca1-cert.pem new file mode 100644 index 0000000..1d0c0d6 --- /dev/null +++ b/node_modules/ws/test/fixtures/ca1-cert.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICazCCAdQCCQC9/g69HtxXRzANBgkqhkiG9w0BAQUFADB6MQswCQYDVQQGEwJV +UzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO +BgNVBAsTB05vZGUuanMxDDAKBgNVBAMTA2NhMTEgMB4GCSqGSIb3DQEJARYRcnlA +dGlueWNsb3Vkcy5vcmcwHhcNMTMwMzA4MDAzMDIyWhcNNDAwNzIzMDAzMDIyWjB6 +MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQK +EwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDDAKBgNVBAMTA2NhMTEgMB4GCSqG +SIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0A +MIGJAoGBAKxr1mARUcv7zaqx5y4AxJPK6c1jdbSg7StcL4vg8klaPAlfNO6o+/Cl +w5CdQD3ukaVUwUOJ4T/+b3Xf7785XcWBC33GdjVQkfbHATJYcka7j7JDw3qev5Jk +1rAbRw48hF6rYlSGcx1mccAjoLoa3I8jgxCNAYHIjUQXgdmU893rAgMBAAEwDQYJ +KoZIhvcNAQEFBQADgYEAis05yxjCtJRuv8uX/DK6TX/j9C9Lzp1rKDNFTaTZ0iRw +KCw1EcNx4OXSj9gNblW4PWxpDvygrt1AmH9h2cb8K859NSHa9JOBFw6MA5C2A4Sj +NQfNATqUl4T6cdORlcDEZwHtT8b6D4A6Er31G/eJF4Sen0TUFpjdjd+l9RBjHlo= +-----END CERTIFICATE----- diff --git a/node_modules/ws/test/fixtures/ca1-key.pem b/node_modules/ws/test/fixtures/ca1-key.pem new file mode 100644 index 0000000..df14950 --- /dev/null +++ b/node_modules/ws/test/fixtures/ca1-key.pem @@ -0,0 +1,17 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIFeWxJE1BrRECAggA +MBQGCCqGSIb3DQMHBAgu9PlMSQ+BOASCAoDEZN2tX0xWo/N+Jg+PrvCrFDk3P+3x +5xG/PEDjtMCAWPBEwbnaYHDzYmhNcAmxzGqEHGMDiWYs46LbO560VS3uMvFbEWPo +KYYVb13vkxl2poXdonCb5cHZA5GUYzTIVVJFptl4LHwBczHoMHtA4FqAhKlYvlWw +EOrdLB8XcwMmGPFabbbGxno0+EWWM27uNjlogfoxj35mQqSW4rOlhZ460XjOB1Zx +LjXMuZeONojkGYQRG5EUMchBoctQpCOM6cAi9r1B9BvtFCBpDV1c1zEZBzTEUd8o +kLn6tjLmY+QpTdylFjEWc7U3ppLY/pkoTBv4r85a2sEMWqkhSJboLaTboWzDJcU3 +Ke61pMpovt/3yCUd3TKgwduVwwQtDVTlBe0p66aN9QVj3CrFy/bKAGO3vxlli24H +aIjZf+OVoBY21ESlW3jLvNlBf7Ezf///2E7j4SCDLyZSFMTpFoAG/jDRyvi+wTKX +Kh485Bptnip6DCSuoH4u2SkOqwz3gJS/6s02YKe4m311QT4Pzne5/FwOFaS/HhQg +Xvyh2/d00OgJ0Y0PYQsHILPRgTUCKUXvj1O58opn3fxSacsPxIXwj6Z4FYAjUTaV +2B85k1lpant/JJEilDqMjqzx4pHZ/Z3Uto1lSM1JZs9SNL/0UR+6F0TXZTULVU9V +w8jYzz4sPr7LEyrrTbzmjQgnQFVbhAN/eKgRZK/SpLjxpmBV5MfpbPKsPUZqT4UC +4nXa8a/NYUQ9e+QKK8enq9E599c2W442W7Z1uFRZTWReMx/lF8wwA6G8zOPG0bdj +d+T5Gegzd5mvRiXMBklCo8RLxOOvgxun1n3PY4a63aH6mqBhdfhiLp5j +-----END ENCRYPTED PRIVATE KEY----- diff --git a/node_modules/ws/test/fixtures/certificate.pem b/node_modules/ws/test/fixtures/certificate.pem new file mode 100644 index 0000000..0efc2ef --- /dev/null +++ b/node_modules/ws/test/fixtures/certificate.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIICATCCAWoCCQDPufXH86n2QzANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJu +bzETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMB4XDTEyMDEwMTE0NDQwMFoXDTIwMDMxOTE0NDQwMFowRTELMAkG +A1UEBhMCbm8xEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 +IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtrQ7 ++r//2iV/B6F+4boH0XqFn7alcV9lpjvAmwRXNKnxAoa0f97AjYPGNLKrjpkNXXhB +JROIdbRbZnCNeC5fzX1a+JCo7KStzBXuGSZr27TtFmcV4H+9gIRIcNHtZmJLnxbJ +sIhkGR8yVYdmJZe4eT5ldk1zoB1adgPF1hZhCBMCAwEAATANBgkqhkiG9w0BAQUF +AAOBgQCeWBEHYJ4mCB5McwSSUox0T+/mJ4W48L/ZUE4LtRhHasU9hiW92xZkTa7E +QLcoJKQiWfiLX2ysAro0NX4+V8iqLziMqvswnPzz5nezaOLE/9U/QvH3l8qqNkXu +rNbsW1h/IO6FV8avWFYVFoutUwOaZ809k7iMh2F2JMgXQ5EymQ== +-----END CERTIFICATE----- diff --git a/node_modules/ws/test/fixtures/key.pem b/node_modules/ws/test/fixtures/key.pem new file mode 100644 index 0000000..176fe32 --- /dev/null +++ b/node_modules/ws/test/fixtures/key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQC2tDv6v//aJX8HoX7hugfReoWftqVxX2WmO8CbBFc0qfEChrR/ +3sCNg8Y0squOmQ1deEElE4h1tFtmcI14Ll/NfVr4kKjspK3MFe4ZJmvbtO0WZxXg +f72AhEhw0e1mYkufFsmwiGQZHzJVh2Yll7h5PmV2TXOgHVp2A8XWFmEIEwIDAQAB +AoGAAlVY8sHi/aE+9xT77twWX3mGHV0SzdjfDnly40fx6S1Gc7bOtVdd9DC7pk6l +3ENeJVR02IlgU8iC5lMHq4JEHPE272jtPrLlrpWLTGmHEqoVFv9AITPqUDLhB9Kk +Hjl7h8NYBKbr2JHKICr3DIPKOT+RnXVb1PD4EORbJ3ooYmkCQQDfknUnVxPgxUGs +ouABw1WJIOVgcCY/IFt4Ihf6VWTsxBgzTJKxn3HtgvE0oqTH7V480XoH0QxHhjLq +DrgobWU9AkEA0TRJ8/ouXGnFEPAXjWr9GdPQRZ1Use2MrFjneH2+Sxc0CmYtwwqL +Kr5kS6mqJrxprJeluSjBd+3/ElxURrEXjwJAUvmlN1OPEhXDmRHd92mKnlkyKEeX +OkiFCiIFKih1S5Y/sRJTQ0781nyJjtJqO7UyC3pnQu1oFEePL+UEniRztQJAMfav +AtnpYKDSM+1jcp7uu9BemYGtzKDTTAYfoiNF42EzSJiGrWJDQn4eLgPjY0T0aAf/ +yGz3Z9ErbhMm/Ysl+QJBAL4kBxRT8gM4ByJw4sdOvSeCCANFq8fhbgm8pGWlCPb5 +JGmX3/GHFM8x2tbWMGpyZP1DLtiNEFz7eCGktWK5rqE= +-----END RSA PRIVATE KEY----- diff --git a/node_modules/ws/test/fixtures/request.pem b/node_modules/ws/test/fixtures/request.pem new file mode 100644 index 0000000..51bc7f6 --- /dev/null +++ b/node_modules/ws/test/fixtures/request.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIBhDCB7gIBADBFMQswCQYDVQQGEwJubzETMBEGA1UECAwKU29tZS1TdGF0ZTEh +MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQC2tDv6v//aJX8HoX7hugfReoWftqVxX2WmO8CbBFc0qfEC +hrR/3sCNg8Y0squOmQ1deEElE4h1tFtmcI14Ll/NfVr4kKjspK3MFe4ZJmvbtO0W +ZxXgf72AhEhw0e1mYkufFsmwiGQZHzJVh2Yll7h5PmV2TXOgHVp2A8XWFmEIEwID +AQABoAAwDQYJKoZIhvcNAQEFBQADgYEAjsUXEARgfxZNkMjuUcudgU2w4JXS0gGI +JQ0U1LmU0vMDSKwqndMlvCbKzEgPbJnGJDI8D4MeINCJHa5Ceyb8c+jaJYUcCabl +lQW5Psn3+eWp8ncKlIycDRj1Qk615XuXtV0fhkrgQM2ZCm9LaQ1O1Gd/CzLihLjF +W0MmgMKMMRk= +-----END CERTIFICATE REQUEST----- diff --git a/node_modules/ws/test/fixtures/textfile b/node_modules/ws/test/fixtures/textfile new file mode 100644 index 0000000..a10483b --- /dev/null +++ b/node_modules/ws/test/fixtures/textfile @@ -0,0 +1,9 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam egestas, massa at aliquam luctus, sapien erat viverra elit, nec pulvinar turpis eros sagittis urna. Pellentesque imperdiet tempor varius. Pellentesque blandit, ipsum in imperdiet venenatis, mi elit faucibus odio, id condimentum ante enim sed lectus. Aliquam et odio non odio pellentesque pulvinar. Vestibulum a erat dolor. Integer pretium risus sit amet nisl volutpat nec venenatis magna egestas. Ut bibendum felis eu tellus laoreet eleifend. Nam pulvinar auctor tortor, eu iaculis leo vestibulum quis. In euismod risus ac purus vehicula et fermentum ligula consectetur. Vivamus condimentum tempus lacinia. + +Curabitur sodales condimentum urna id dictum. Sed quis justo sit amet quam ultrices tincidunt vel laoreet nulla. Nullam quis ipsum sed nisi mollis bibendum at sit amet nisi. Donec laoreet consequat velit sit amet mollis. Nam sed sapien a massa iaculis dapibus. Sed dui nunc, ultricies et pellentesque ullamcorper, aliquet vitae ligula. Integer eu velit in neque iaculis venenatis. Ut rhoncus cursus est, ac dignissim leo vehicula a. Nulla ullamcorper vulputate mauris id blandit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque eleifend, nisi a tempor sollicitudin, odio massa pretium urna, quis congue sapien elit at tortor. Curabitur ipsum orci, vehicula non commodo molestie, laoreet id enim. Pellentesque convallis ultrices congue. Pellentesque nec iaculis lorem. In sagittis pharetra ipsum eget sodales. + +Fusce id nulla odio. Nunc nibh justo, placerat vel tincidunt sed, ornare et enim. Nulla vel urna vel ante commodo bibendum in vitae metus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis erat nunc, semper eget sagittis sit amet, ullamcorper eget lacus. Donec hendrerit ipsum vitae eros vestibulum eu gravida neque tincidunt. Ut molestie lacinia nulla. Donec mattis odio at magna egestas at pellentesque eros accumsan. Praesent interdum sem sit amet nibh commodo dignissim. Duis laoreet, enim ultricies fringilla suscipit, enim libero cursus nulla, sollicitudin adipiscing erat velit ut dui. Nulla eleifend mauris at velit fringilla a molestie lorem venenatis. + +Donec sit amet scelerisque metus. Cras ac felis a nulla venenatis vulputate. Duis porttitor eros ac neque rhoncus eget aliquet neque egestas. Quisque sed nunc est, vitae dapibus quam. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In vehicula, est vitae posuere ultricies, diam purus pretium sapien, nec rhoncus dolor nisl eget arcu. Aliquam et nisi vitae risus tincidunt auctor. In vehicula, erat a cursus adipiscing, lorem orci congue est, nec ultricies elit dui in nunc. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + +Duis congue tempus elit sit amet auctor. Duis dignissim, risus ut sollicitudin ultricies, dolor ligula gravida odio, nec congue orci purus ut ligula. Fusce pretium dictum lectus at volutpat. Sed non auctor mauris. Etiam placerat vestibulum massa id blandit. Quisque consequat lacus ut nulla euismod facilisis. Sed aliquet ipsum nec mi imperdiet viverra. Pellentesque ullamcorper, lectus nec varius gravida, odio justo cursus risus, eu sagittis metus arcu quis felis. Phasellus consectetur vehicula libero, at condimentum orci euismod vel. Nunc purus tortor, suscipit nec fringilla nec, vulputate et nibh. Nam porta vehicula neque. Praesent porttitor, sapien eu auctor euismod, arcu quam elementum urna, sed hendrerit magna augue sed quam. \ No newline at end of file diff --git a/node_modules/ws/test/hybi-common.js b/node_modules/ws/test/hybi-common.js new file mode 100644 index 0000000..006f9c6 --- /dev/null +++ b/node_modules/ws/test/hybi-common.js @@ -0,0 +1,99 @@ +/** + * Returns a Buffer from a "ff 00 ff"-type hex string. + */ + +getBufferFromHexString = function(byteStr) { + var bytes = byteStr.split(' '); + var buf = new Buffer(bytes.length); + for (var i = 0; i < bytes.length; ++i) { + buf[i] = parseInt(bytes[i], 16); + } + return buf; +} + +/** + * Returns a hex string from a Buffer. + */ + +getHexStringFromBuffer = function(data) { + var s = ''; + for (var i = 0; i < data.length; ++i) { + s += padl(data[i].toString(16), 2, '0') + ' '; + } + return s.trim(); +} + +/** + * Splits a buffer in two parts. + */ + +splitBuffer = function(buffer) { + var b1 = new Buffer(Math.ceil(buffer.length / 2)); + buffer.copy(b1, 0, 0, b1.length); + var b2 = new Buffer(Math.floor(buffer.length / 2)); + buffer.copy(b2, 0, b1.length, b1.length + b2.length); + return [b1, b2]; +} + +/** + * Performs hybi07+ type masking on a hex string or buffer. + */ + +mask = function(buf, maskString) { + if (typeof buf == 'string') buf = new Buffer(buf); + var mask = getBufferFromHexString(maskString || '34 83 a8 68'); + for (var i = 0; i < buf.length; ++i) { + buf[i] ^= mask[i % 4]; + } + return buf; +} + +/** + * Returns a hex string representing the length of a message + */ + +getHybiLengthAsHexString = function(len, masked) { + if (len < 126) { + var buf = new Buffer(1); + buf[0] = (masked ? 0x80 : 0) | len; + } + else if (len < 65536) { + var buf = new Buffer(3); + buf[0] = (masked ? 0x80 : 0) | 126; + getBufferFromHexString(pack(4, len)).copy(buf, 1); + } + else { + var buf = new Buffer(9); + buf[0] = (masked ? 0x80 : 0) | 127; + getBufferFromHexString(pack(16, len)).copy(buf, 1); + } + return getHexStringFromBuffer(buf); +} + +/** + * Unpacks a Buffer into a number. + */ + +unpack = function(buffer) { + var n = 0; + for (var i = 0; i < buffer.length; ++i) { + n = (i == 0) ? buffer[i] : (n * 256) + buffer[i]; + } + return n; +} + +/** + * Returns a hex string, representing a specific byte count 'length', from a number. + */ + +pack = function(length, number) { + return padl(number.toString(16), length, '0').replace(/([0-9a-f][0-9a-f])/gi, '$1 ').trim(); +} + +/** + * Left pads the string 's' to a total length of 'n' with char 'c'. + */ + +padl = function(s, n, c) { + return new Array(1 + n - s.length).join(c) + s; +} diff --git a/node_modules/ws/test/testserver.js b/node_modules/ws/test/testserver.js new file mode 100644 index 0000000..3e7a966 --- /dev/null +++ b/node_modules/ws/test/testserver.js @@ -0,0 +1,180 @@ +var http = require('http') + , util = require('util') + , crypto = require('crypto') + , events = require('events') + , Sender = require('../lib/Sender') + , Receiver = require('../lib/Receiver'); + +module.exports = { + handlers: { + valid: validServer, + invalidKey: invalidRequestHandler, + closeAfterConnect: closeAfterConnectHandler, + return401: return401 + }, + createServer: function(port, handler, cb) { + if (handler && !cb) { + cb = handler; + handler = null; + } + var webServer = http.createServer(function (req, res) { + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.end('okay'); + }); + var srv = new Server(webServer); + webServer.on('upgrade', function(req, socket) { + webServer._socket = socket; + (handler || validServer)(srv, req, socket); + }); + webServer.listen(port, '127.0.0.1', function() { cb(srv); }); + } +}; + +/** + * Test strategies + */ + +function validServer(server, req, socket) { + if (typeof req.headers.upgrade === 'undefined' || + req.headers.upgrade.toLowerCase() !== 'websocket') { + throw new Error('invalid headers'); + return; + } + + if (!req.headers['sec-websocket-key']) { + socket.end(); + throw new Error('websocket key is missing'); + } + + // calc key + var key = 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 + ]; + + socket.write(headers.concat('', '').join('\r\n')); + socket.setTimeout(0); + socket.setNoDelay(true); + + var sender = new Sender(socket); + var receiver = new Receiver(); + receiver.ontext = function (message, flags) { + server.emit('message', message, flags); + sender.send(message); + }; + receiver.onbinary = function (message, flags) { + flags = flags || {}; + flags.binary = true; + server.emit('message', message, flags); + sender.send(message, {binary: true}); + }; + receiver.onping = function (message, flags) { + flags = flags || {}; + server.emit('ping', message, flags); + }; + receiver.onpong = function (message, flags) { + flags = flags || {}; + server.emit('pong', message, flags); + }; + receiver.onclose = function (code, message, flags) { + flags = flags || {}; + server.emit('close', code, message, flags); + }; + socket.on('data', function (data) { + receiver.add(data); + }); + socket.on('end', function() { + socket.end(); + }); +} + +function invalidRequestHandler(server, req, socket) { + if (typeof req.headers.upgrade === 'undefined' || + req.headers.upgrade.toLowerCase() !== 'websocket') { + throw new Error('invalid headers'); + return; + } + + if (!req.headers['sec-websocket-key']) { + socket.end(); + throw new Error('websocket key is missing'); + } + + // calc key + var key = req.headers['sec-websocket-key']; + var shasum = crypto.createHash('sha1'); + shasum.update(key + "bogus"); + key = shasum.digest('base64'); + + var headers = [ + 'HTTP/1.1 101 Switching Protocols' + , 'Upgrade: websocket' + , 'Connection: Upgrade' + , 'Sec-WebSocket-Accept: ' + key + ]; + + socket.write(headers.concat('', '').join('\r\n')); + socket.end(); +} + +function closeAfterConnectHandler(server, req, socket) { + if (typeof req.headers.upgrade === 'undefined' || + req.headers.upgrade.toLowerCase() !== 'websocket') { + throw new Error('invalid headers'); + return; + } + + if (!req.headers['sec-websocket-key']) { + socket.end(); + throw new Error('websocket key is missing'); + } + + // calc key + var key = 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 + ]; + + socket.write(headers.concat('', '').join('\r\n')); + socket.end(); +} + + +function return401(server, req, socket) { + var headers = [ + 'HTTP/1.1 401 Unauthorized' + , 'Content-type: text/html' + ]; + + socket.write(headers.concat('', '').join('\r\n')); + socket.end(); +} + +/** + * Server object, which will do the actual emitting + */ + +function Server(webServer) { + this.webServer = webServer; +} + +util.inherits(Server, events.EventEmitter); + +Server.prototype.close = function() { + this.webServer.close(); + if (this._socket) this._socket.end(); +} diff --git a/server.js b/server.js new file mode 100644 index 0000000..badbf2b --- /dev/null +++ b/server.js @@ -0,0 +1,102 @@ +// npm install ws en jsftp +var sys = require("sys"), + fs = require("fs"), + Ftp = require("jsftp"), + WebSocketServer = require('ws').Server; + +function hour() { return new Date().getHours(); }; + +var wss = new WebSocketServer({port: 8080}); +var clients = []; +var snapshotHour = -1; // start auto-capping the history feed. +var imageName = "image.jpg"; + +function convertDataURIToBinary(dataURI) { + var BASE64_MARKER = ';base64,'; + var base64Index = dataURI.indexOf(BASE64_MARKER) + BASE64_MARKER.length; + var base64 = dataURI.substring(base64Index); + return new Buffer(base64, 'base64'); // atob(base64) in window scope +} + +function reuploadImage() { + var user = "FILL ME IN"; + var pass = "FILL ME IN"; + var rootPath = "/domains/brainbaking.com/public_html/cam/"; + + var ftp = new Ftp({ + host: "ftp.brainbaking.com", + port: 21 + }); + + ftp.auth(user, pass, function(err, res) { + function uploadImageTo(path, callback) { + ftp.put(imageName, path, function(err, data) { + if(err) { + sys.debug("ERROR while uploading"); + sys.debug(err); + } else { + sys.debug("file uploaded to " + path + " - callback firing..."); + callback(data); + } + }); + } + + function uploadSnapshotIndexFile() { + var images = { files: [] }; + ftp.ls(rootPath, function(err, list) { + + images.files = list.filter(function(file) { + return file.name.indexOf('.jpg') > 0 && file.name.indexOf('_') > 0; + }); + + fs.writeFile("index.json", JSON.stringify(images), function(e) { + if(e) throw e; + + ftp.put("index.json", rootPath + "index.json", function(e) { + if(e) throw e; + sys.debug("written index JSON file."); + }); + }); + + }); + } + + function createHistorySnapshotIfNeeded() { + if(hour() !== snapshotHour) { + sys.debug("Hourly snapshot needs refreshment, creating..."); + snapshotHour = hour(); + uploadImageTo(rootPath + new Date().getTime() + "_" + imageName, uploadSnapshotIndexFile); + } + + } + + uploadImageTo(rootPath + imageName, createHistorySnapshotIfNeeded); + }); +} + +wss.on('connection', function(ws) { + sys.debug("connected client"); + clients.push(ws); + ws.on('message', function(message) { + sys.debug('received data - broadcasting back to client...'); + fs.writeFile(imageName, convertDataURIToBinary(message), function(e) { + if(e) { + sys.debug(e); + } else { + sys.debug("file written, starting upload..."); + reuploadImage(); + } + }); + + clients.forEach(function(client) { + try { + client.send(message); + } catch(notOpenEx) { + // should I remove the client? Can I do a check instead of try/catch? + sys.debug("client not connected anymore, ignoring exception"); + } + }); + }); +}); + +sys.debug("Listening on port 8080"); diff --git a/start.sh b/start.sh new file mode 100755 index 0000000..d3683a7 --- /dev/null +++ b/start.sh @@ -0,0 +1,2 @@ +python -m SimpleHTTPServer 8000 +