From 7c20db75791bf431e3030f6a6d51ec85caf7e8ff Mon Sep 17 00:00:00 2001 From: Tim Oxley Date: Wed, 8 Feb 2012 16:17:34 +1000 Subject: [PATCH 01/48] Handle errory connection string --- index.js | 4 ++++ test/error.js | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/index.js b/index.js index f1768f5..28ea2c8 100644 --- a/index.js +++ b/index.js @@ -100,6 +100,10 @@ dnode.prototype.connect = function () { } function attachDnode() { + if (!stream) { + self.emit('error', new Error('Could not create a stream with this information ' + JSON.stringify(params))) + return null + } client = createClient(self.proto, stream); client.end = function () { diff --git a/test/error.js b/test/error.js index 7287c40..61499d2 100644 --- a/test/error.js +++ b/test/error.js @@ -85,3 +85,15 @@ test('refused', function (t) { t.end(); }); }); + + +test('bad connection string', function(t) { + try { + var client = dnode.connect('garbage', function (remote, conn) { + assert.fail('should have been refused, very unlikely'); + }); + } catch (err) { + t.ok(/Could not create a stream/.test(err.message)); + t.end(); + } +}); From 4745db3cc2dc7a3d9b95a4ec5f8bc3b32e909f82 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Sat, 14 Apr 2012 05:25:57 -0700 Subject: [PATCH 02/48] bump for disconnect/close event propagation --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 73863a4..3e8952b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name" : "dnode", - "version" : "0.9.9", + "version" : "0.9.10", "description" : "freestyle RPC", "main" : "./index.js", "keywords" : [ From 9e24b97cf2ce931fbf6d7beb3731086b46bca887 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Mon, 30 Apr 2012 23:32:56 -0700 Subject: [PATCH 03/48] bump for better error message --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3e8952b..62c63c9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name" : "dnode", - "version" : "0.9.10", + "version" : "0.9.11", "description" : "freestyle RPC", "main" : "./index.js", "keywords" : [ From aa27299004191f1a87d0032273ec99e38cdf2c99 Mon Sep 17 00:00:00 2001 From: Jan Matousek Date: Wed, 13 Jun 2012 20:27:25 +0300 Subject: [PATCH 04/48] Add link to dnode-php-sync-client --- README.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/README.markdown b/README.markdown index 51959fe..b853b00 100644 --- a/README.markdown +++ b/README.markdown @@ -300,6 +300,7 @@ between scripts written in different languages. * [dnode-perl](http://github.com/substack/dnode-perl) * [dnode-ruby](http://github.com/substack/dnode-ruby) * [dnode-php](https://github.com/bergie/dnode-php) +* [dnode-php-sync-client](https://github.com/erasys/dnode-php-sync-client) - minimalistic synchronous client for PHP * [dnode-java](https://github.com/aslakhellesoy/dnode-java) There's a From 988a6e92068f10d9aa38d927125483ec4a5b9b6f Mon Sep 17 00:00:00 2001 From: James Halliday Date: Wed, 13 Jun 2012 12:05:20 -0700 Subject: [PATCH 05/48] drop support for 0.4 --- .travis.yml | 1 - package.json | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index f1d0f13..2d26206 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,3 @@ language: node_js node_js: - - 0.4 - 0.6 diff --git a/package.json b/package.json index 62c63c9..33f8480 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name" : "dnode", - "version" : "0.9.11", + "version" : "0.9.12", "description" : "freestyle RPC", "main" : "./index.js", "keywords" : [ @@ -39,7 +39,7 @@ }, "browserify" : "browser/index.js", "engine" : { - "node" : ">=0.4.0" + "node" : ">=0.6.0" }, "author" : { "name" : "James Halliday", From a3dbbcdadcefbb93593a6408607c6c2c655aec07 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Mon, 18 Jun 2012 23:51:10 -0700 Subject: [PATCH 06/48] remove socket.io --- index.js | 13 ----- lib/stream_socketio.js | 112 ----------------------------------------- package.json | 14 ++---- 3 files changed, 3 insertions(+), 136 deletions(-) delete mode 100644 lib/stream_socketio.js diff --git a/index.js b/index.js index 2a60aae..75aa341 100644 --- a/index.js +++ b/index.js @@ -5,7 +5,6 @@ var EventEmitter = require('events').EventEmitter; var protocol = require('dnode-protocol'); var Lazy = require('lazy'); -var SocketIO = require('./lib/stream_socketio'); exports = module.exports = dnode; @@ -183,18 +182,6 @@ dnode.prototype.listen = function () { this.emit.bind(this, 'ready') ); } - else if (server && ( - server instanceof http.Server - || server.hasOwnProperty('httpAllowHalfOpen') - || params.webserver - )) { - // a webserver, use socket.io - server = SocketIO( - server || params.webserver, - params.mount === undefined ? '/dnode.js' : params.mount, - params.io || {} - ); - } if (!server) { this.emit('error', new Error('Not sure how to fire up this listener')); diff --git a/lib/stream_socketio.js b/lib/stream_socketio.js deleted file mode 100644 index 6c24442..0000000 --- a/lib/stream_socketio.js +++ /dev/null @@ -1,112 +0,0 @@ -var EventEmitter = require('events').EventEmitter; -var Stream = require('stream').Stream; -var io = require('socket.io'); -var fs = require('fs'); - -var bundle = (function () { - var cache = null; - var file = __dirname + '/../browser/bundle.js'; - - return function (req, res) { - if (cache) { - var headers = { - 'content-type' : 'text/javascript', - 'last-modified' : cache.modified.toGMTString(), - 'date' : new Date().toGMTString(), - }; - - var ims = req.headers['if-modified-since']; - if (ims) { - var m = new Date(ims); - if (m >= cache.modified) { - res.writeHead(304, headers); - res.end(); - return; - } - } - - res.writeHead(200, headers); - res.end(cache.source); - } - else fs.stat(file, function (err0, stat) { - fs.readFile(file, function (err1, src) { - if (err0 || err1) { - var e = err0 || err1; - console.error(e.message || e); - res.writeHead(500, { 'content-type' : 'text/plain' }); - res.end('an error occured loading the bundle'); - } - else { - cache = { - source : src, - modified : stat.mtime, - }; - bundle(req, res); - } - }); - }); - }; -})(); - -module.exports = function (webserver, mount, ioOptions) { - if (ioOptions['log level'] === undefined) { - ioOptions['log level'] = -1; - } - - var sock = io.listen(webserver, ioOptions); - sock.set('logger', { - error : function () {}, - warn : function () {}, - info : function () {}, - debug : function () {} - }); - - var server = new EventEmitter; - server.socket = sock; - - if (mount && webserver.use) { - webserver.use(function (req, res, next) { - if (req.url.split('?')[0] === mount) { - bundle(req, res); - } - else next() - }); - } - else if (mount) { - if (!webserver._events) webserver._events = {}; - var ev = webserver._events; - - if (!ev.request) ev.request = []; - if (!Array.isArray(ev.request)) ev.request = [ ev.request ]; - - ev.request.push(function (req, res) { - if (!res.finished && req.url.split('?')[0] === mount) { - bundle(req, res); - } - }); - } - - sock.sockets.on('connection', function (client) { - var stream = new Stream; - - stream.socketio = client; - stream.readable = true; - stream.writable = true; - - stream.write = client.send.bind(client); - stream.end = stream.destroy = client.disconnect.bind(client); - - client.on('message', stream.emit.bind(stream, 'data')); - client.on('error', stream.emit.bind(stream, 'error')); - - client.on('disconnect', function () { - stream.writable = false; - stream.readable = false; - stream.emit('end'); - }); - - server.emit('connection', stream); - }); - - return server; -}; diff --git a/package.json b/package.json index 33f8480..f5bc2e7 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,11 @@ { "name" : "dnode", - "version" : "0.9.12", - "description" : "freestyle RPC", + "version" : "1.0.0", + "description" : "freestyle rpc", "main" : "./index.js", "keywords" : [ - "message passing", "rpc", - "rmi", - "drb", - "remote", - "communication", - "websockets", - "socket.io" + "callbacks" ], "repository" : { "type" : "git", @@ -22,8 +16,6 @@ "examples" : "./examples" }, "dependencies" : { - "socket.io" : "0.8.6", - "socket.io-client" : "https://github.com/substack/socket.io-client/tarball/master", "lazy" : ">=1.0.5 <1.1", "dnode-protocol" : "~0.1.2", "jsonify" : "0.0.x" From 8f0f14de02ed0366a120515802a9ca5cadf3a452 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 00:27:44 -0700 Subject: [PATCH 07/48] parse args taken from dnode-protocol and moved to lib/ --- index.js | 5 +++-- lib/parse_args.js | 51 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 lib/parse_args.js diff --git a/index.js b/index.js index 75aa341..5bc128f 100644 --- a/index.js +++ b/index.js @@ -4,6 +4,7 @@ var http = require('http'); var EventEmitter = require('events').EventEmitter; var protocol = require('dnode-protocol'); +var parseArgs = require('./lib/parse_args'); var Lazy = require('lazy'); exports = module.exports = dnode; @@ -25,7 +26,7 @@ dnode.prototype.use = function (middleware) { }; dnode.prototype.connect = function () { - var params = protocol.parseArgs(arguments); + var params = parseArgs(arguments); var stream = params.stream; var client = null; var self = this; @@ -138,7 +139,7 @@ dnode.prototype.connect = function () { dnode.prototype.listen = function () { var self = this; - var params = protocol.parseArgs(arguments); + var params = parseArgs(arguments); var server = params.server; if (params.port) { diff --git a/lib/parse_args.js b/lib/parse_args.js new file mode 100644 index 0000000..12bacef --- /dev/null +++ b/lib/parse_args.js @@ -0,0 +1,51 @@ +module.exports = function (argv) { + var params = {}; + + forEach([].slice.call(argv), function (arg) { + if (typeof arg === 'string') { + if (arg.match(/^\d+$/)) { + params.port = parseInt(arg, 10); + } + else if (arg.match('^/')) { + params.path = arg; + } + else { + params.host = arg; + } + } + else if (typeof arg === 'number') { + params.port = arg; + } + else if (typeof arg === 'function') { + params.block = arg; + } + else if (typeof arg === 'object') { + if (arg && typeof arg.listen === 'function') { + // servers can .listen() + params.server = arg; + } + else if (arg && typeof arg.write === 'function') { + // streams can .write() + params.stream = arg; + } + else { + // merge vanilla objects into params + forEach(objectKeys(arg), function (key) { + params[key] = key === 'port' + ? parseInt(arg[key], 10) + : arg[key] + ; + }); + } + } + else if (typeof arg === 'undefined') { + // ignore + } + else { + throw new Error('Not sure what to do about ' + + typeof arg + ' objects'); + } + }); + + return params; +}; From 311ad1066248b24f9d43ff8170b55f69fe3f494a Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 00:32:09 -0700 Subject: [PATCH 08/48] simpler parse args --- lib/parse_args.js | 36 ++++++++++-------------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/lib/parse_args.js b/lib/parse_args.js index 12bacef..f9a79ff 100644 --- a/lib/parse_args.js +++ b/lib/parse_args.js @@ -1,7 +1,8 @@ module.exports = function (argv) { var params = {}; - - forEach([].slice.call(argv), function (arg) { + for (var i = 0; i < argv.length; i++) { + var arg = argv[i]; + if (typeof arg === 'string') { if (arg.match(/^\d+$/)) { params.port = parseInt(arg, 10); @@ -20,32 +21,15 @@ module.exports = function (argv) { params.block = arg; } else if (typeof arg === 'object') { - if (arg && typeof arg.listen === 'function') { - // servers can .listen() - params.server = arg; - } - else if (arg && typeof arg.write === 'function') { - // streams can .write() - params.stream = arg; - } - else { - // merge vanilla objects into params - forEach(objectKeys(arg), function (key) { - params[key] = key === 'port' - ? parseInt(arg[key], 10) - : arg[key] - ; - }); + for (var key in arg) { + if (key === 'port') { + params[key] = parseInt(arg[key], 10) + } + else params[key] = arg[key] } } - else if (typeof arg === 'undefined') { - // ignore - } - else { - throw new Error('Not sure what to do about ' - + typeof arg + ' objects'); - } - }); + // ignore everything else + }; return params; }; From 2143136366ed10df5a18161627667c2ce2b8a8e6 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 02:37:02 -0700 Subject: [PATCH 09/48] functional pipe-able api example --- examples/stream/connect.js | 13 ++ examples/stream/listen.js | 13 ++ index.js | 289 ------------------------------------- lib/dnode.js | 93 ++++++++++++ 4 files changed, 119 insertions(+), 289 deletions(-) create mode 100644 examples/stream/connect.js create mode 100644 examples/stream/listen.js delete mode 100644 index.js create mode 100644 lib/dnode.js diff --git a/examples/stream/connect.js b/examples/stream/connect.js new file mode 100644 index 0000000..dfc986f --- /dev/null +++ b/examples/stream/connect.js @@ -0,0 +1,13 @@ +var dnode = require('../../lib/dnode'); +var net = require('net'); + +var d = new dnode(); +d.on('remote', function (remote) { + remote.transform('beep', function (s) { + console.log('beep => ' + s); + d.end(); + }); +}); + +var c = net.connect(5004); +c.pipe(d).pipe(c); diff --git a/examples/stream/listen.js b/examples/stream/listen.js new file mode 100644 index 0000000..e9fdf46 --- /dev/null +++ b/examples/stream/listen.js @@ -0,0 +1,13 @@ +var dnode = require('../../lib/dnode'); +var net = require('net'); + +var server = net.createServer(function (c) { + var d = new dnode({ + transform : function (s, cb) { + cb(s.replace(/[aeiou]{2,}/, 'oo').toUpperCase()) + } + }); + c.pipe(d).pipe(c); +}); + +server.listen(5004); diff --git a/index.js b/index.js deleted file mode 100644 index 5bc128f..0000000 --- a/index.js +++ /dev/null @@ -1,289 +0,0 @@ -var net = require('net'); -var tls = require('tls'); -var http = require('http'); -var EventEmitter = require('events').EventEmitter; - -var protocol = require('dnode-protocol'); -var parseArgs = require('./lib/parse_args'); -var Lazy = require('lazy'); - -exports = module.exports = dnode; - -function dnode (wrapper) { - if (!(this instanceof dnode)) return new dnode(wrapper); - - this.proto = protocol(wrapper); - this.stack = []; - this.streams = []; - return this; -} - -dnode.prototype = new EventEmitter; - -dnode.prototype.use = function (middleware) { - this.stack.push(middleware); - return this; -}; - -dnode.prototype.connect = function () { - var params = parseArgs(arguments); - var stream = params.stream; - var client = null; - var self = this; - - if (params.port) { - params.host = params.host || '127.0.0.1'; - if (params.key) { - var options = { - key: params.key, - cert: params.cert, - ca: params.ca, - requestCert: params.requestCert, - rejectUnauthorized: params.rejectUnauthorized - }; - stream = tls.connect(params.port, params.host, options, function() { - attachDnode(); - }); - } - else { - stream = net.createConnection(params.port, params.host); - stream.on('connect', function() { - attachDnode(); - }); - } - } - else if (params.path) { - stream = net.createConnection(params.path); - stream.on('connect', function() { - attachDnode(); - }); - } - else { - attachDnode(); - } - - stream.remoteAddress = params.host; - stream.remotePort = params.port; - - var args = arguments; - - if (params.reconnect) { - stream.on('error', (function (err) { - if (err.code === 'ECONNREFUSED' || err.code === 'ENOENT') { - if (client) client.emit('refused'); - - setTimeout((function () { - if (client) client.emit('reconnect'); - dnode.prototype.connect.apply(this, args); - }).bind(this), params.reconnect); - } - else if (client) client.emit('error', err) - else this.emit('error', err) - }).bind(this)); - - stream.once('end', (function () { - if (!params.reconnect) return; - client.emit('drop'); - - setTimeout((function () { - if (!params.reconnect) return; - client.emit('reconnect'); - dnode.prototype.connect.apply(this, args); - }).bind(this), params.reconnect); - }).bind(this)); - } - else { - stream.on('error', (function (err) { - if (client) client.emit('error', err) - else this.emit('error', err) - }).bind(this)); - } - - function attachDnode() { - if (!stream) { - self.emit('error', new Error('Could not create a stream with this information ' + JSON.stringify(params))) - return null - } - client = createClient(self.proto, stream); - - client.end = function () { - if (params.reconnect) params.reconnect = 0; - stream.end(); - }; - - self.stack.forEach(function (middleware) { - middleware.call(client.instance, client.remote, client); - }); - - if (params.block) { - client.on('remote', function () { - params.block.call(client.instance, client.remote, client); - }); - } - - process.nextTick(function () { - if (client.listeners('error').length === 0) { - // default error handler to keep everything from crashing - client.on('error', function (err) { - console.error(err && err.stack || err); - }) - } - }); - - client.start(); - }; - - this.streams.push(stream); - return this; -}; - -dnode.prototype.listen = function () { - var self = this; - var params = parseArgs(arguments); - var server = params.server; - - if (params.port) { - if (params.key) { - var options = { - key: params.key, - cert: params.cert, - ca: params.ca, - requestCert: params.requestCert, - rejectUnauthorized: params.rejectUnauthorized - }; - server = tls.createServer(options); - server.on('error', this.emit.bind(this, 'error')); - if (params.host) { - server.listen( - params.port, params.host, - this.emit.bind(this, 'ready') - ); - } - else { - server.listen( - params.port, - this.emit.bind(this, 'ready') - ); - } - } - else { - server = net.createServer(); - server.on('error', this.emit.bind(this, 'error')); - server.listen( - params.port, params.host, - this.emit.bind(this, 'ready') - ); - } - } - else if (params.path) { - server = net.createServer(); - server.on('error', this.emit.bind(this, 'error')); - server.listen( - params.path, - this.emit.bind(this, 'ready') - ); - } - - if (!server) { - this.emit('error', new Error('Not sure how to fire up this listener')); - } - - var clients = {}; - var listenFor = server instanceof tls.Server - ? 'secureConnection' - : 'connection' - ; - - server.on(listenFor, function (stream) { - var client = createClient(self.proto, stream); - clients[client.id] = client; - - self.stack.forEach(function (middleware) { - middleware.call(client.instance, client.remote, client); - }); - - if (params.block) { - client.on('remote', function () { - params.block.call(client.instance, client.remote, client); - }); - } - - client.start(); - }); - - this.server = server; - server.on('close', this.emit.bind(this, 'close')); - - return this; -}; - -function createClient (proto, stream) { - var client = proto.create(); - - process.nextTick(function () { - if (client.listeners('error').length === 0) { - // default error handler to keep everything from crashing - client.on('error', function (err) { - console.error(err && err.stack || err); - }) - } - }); - - client.stream = stream; - client.end = stream.end.bind(stream); - client.destroy = stream.destroy.bind(stream); - - stream.on('end', client.emit.bind(client, 'end')); - stream.on('disconnect', client.emit.bind(client, 'disconnect')); - stream.on('close', client.emit.bind(client, 'close')); - - client.on('request', function (req) { - if (stream.writable) { - stream.write(JSON.stringify(req) + '\n'); - } - else { - client.emit('dropped', req); - } - }); - - Lazy(stream).lines - .map(String) - .forEach(client.parse) - ; - - return client; -} - -dnode.prototype.end = function () { - Object.keys(this.proto.sessions) - .forEach((function (id) { - this.proto.sessions[id].stream.end() - }).bind(this)) - ; - this.emit('end'); -}; - -dnode.prototype.close = function () { - var self = this - try { - self.server.close(); - } - catch (e) { - if (e.message === 'Not running') { - self.server.emit('close'); - } - else { - self.server.emit('error', e); - } - } -}; - -dnode.connect = function () { - var d = dnode(); - return d.connect.apply(d, arguments); -}; - -dnode.listen = function () { - var d = dnode(); - return d.listen.apply(d, arguments); -}; diff --git a/lib/dnode.js b/lib/dnode.js new file mode 100644 index 0000000..37e361c --- /dev/null +++ b/lib/dnode.js @@ -0,0 +1,93 @@ +var protocol = require('dnode-protocol'); +var parseArgs = require('./parse_args'); +var Stream = require('stream'); +var util = require('util'); +var json = typeof JSON === 'object' ? JSON : require('jsonify'); + +module.exports = dnode; +util.inherits(dnode, Stream); + +function dnode (cons, opts) { + if (!opts) opts = {}; + + var self = this; + var proto = self.proto = protocol(cons).create(); + proto.on('remote', function (remote) { + self.emit('remote', remote, self); + }); + + self.stack = []; + self.readable = true; + self.writable = true; + + self._line = ''; + + proto.on('request', function (req) { + if (!self.readable) return; + + if (opts.emit === 'object') { + self.emit('data', req); + } + else self.emit('data', json.stringify(req) + '\n'); + }); + + process.nextTick(function () { + proto.start(); + }); +} + +dnode.prototype.use = function (middleware) { + this.stack.push(middleware); + return this; +}; + +dnode.prototype.write = function (buf) { + var row; + + if (buf && typeof buf === 'object' + && buf.constructor && buf.constructor.name === 'Buffer') { + // treat like a buffer + for (var i = 0; i < buf.length; i++) { + if (buf[i] === 0x0a) { + try { row = json.parse(this._line) } + catch (err) { return this.end() } + + this.proto.handle(row); + this._line = ''; + } + else this._line += String.fromCharCode(buf[i]) + } + } + else if (buf && typeof buf === 'object') { + // .isBuffer() without the Buffer + // Use this to pipe JSONStream.parse() streams. + this.proto.handle(buf); + return; + } + else { + if (typeof buf !== 'string') buf = String(buf); + + for (var i = 0; i < buf.length; i++) { + if (buf.charCodeAt(i) === 0x0a) { + try { row = json.parse(this._line) } + catch (err) { return this.end() } + + this.proto.handle(row); + this._line = ''; + } + else this._line += buf.charAt(i) + } + } +}; + +dnode.prototype.end = function () { + if (this._ended) return; + this._ended = true; + this.writable = false; + this.readable = false; + this.emit('end'); +}; + +dnode.prototype.destroy = function () { + this.end(); +}; From a2e53e9db509731db8884abb8d1e2973237825ce Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 03:48:03 -0700 Subject: [PATCH 10/48] emit "local" instead of .use() --- lib/dnode.js | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/lib/dnode.js b/lib/dnode.js index 37e361c..da45260 100644 --- a/lib/dnode.js +++ b/lib/dnode.js @@ -1,27 +1,41 @@ var protocol = require('dnode-protocol'); -var parseArgs = require('./parse_args'); var Stream = require('stream'); -var util = require('util'); var json = typeof JSON === 'object' ? JSON : require('jsonify'); module.exports = dnode; -util.inherits(dnode, Stream); +dnode.prototype = {}; +(function () { // browsers etc + for (var key in Stream.prototype) { + dnode.prototype[key] = Stream.prototype[key]; + } +})(); function dnode (cons, opts) { + Stream.call(this); + var self = this; + if (!opts) opts = {}; + self.cons = typeof cons === 'function' + ? cons + : function () { return cons_ || {} } + ; - var self = this; - var proto = self.proto = protocol(cons).create(); - proto.on('remote', function (remote) { - self.emit('remote', remote, self); - }); + var proto = self.proto = protocol(function (remote) { + var ref = cons.call(this, remote, self); + if (typeof ref !== 'object') ref = this; + self.emit('local', ref); + return ref; + }).create(); - self.stack = []; self.readable = true; self.writable = true; self._line = ''; + proto.on('remote', function (remote) { + self.emit('remote', remote, self); + }); + proto.on('request', function (req) { if (!self.readable) return; @@ -36,11 +50,6 @@ function dnode (cons, opts) { }); } -dnode.prototype.use = function (middleware) { - this.stack.push(middleware); - return this; -}; - dnode.prototype.write = function (buf) { var row; From 663576ebd25bcfae019488dff10a71ca7291a433 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 04:16:52 -0700 Subject: [PATCH 11/48] implemented .connect and .listen in terms of lib/dnode.js --- examples/stream/connect.js | 4 +- examples/stream/listen.js | 2 +- index.js | 112 +++++++++++++++++++++++++++++++++++++ lib/dnode.js | 9 ++- 4 files changed, 121 insertions(+), 6 deletions(-) create mode 100644 index.js diff --git a/examples/stream/connect.js b/examples/stream/connect.js index dfc986f..79b6547 100644 --- a/examples/stream/connect.js +++ b/examples/stream/connect.js @@ -1,7 +1,7 @@ -var dnode = require('../../lib/dnode'); +var dnode = require('../../'); var net = require('net'); -var d = new dnode(); +var d = dnode(); d.on('remote', function (remote) { remote.transform('beep', function (s) { console.log('beep => ' + s); diff --git a/examples/stream/listen.js b/examples/stream/listen.js index e9fdf46..db0ffb9 100644 --- a/examples/stream/listen.js +++ b/examples/stream/listen.js @@ -1,4 +1,4 @@ -var dnode = require('../../lib/dnode'); +var dnode = require('../../'); var net = require('net'); var server = net.createServer(function (c) { diff --git a/index.js b/index.js new file mode 100644 index 0000000..363dd4e --- /dev/null +++ b/index.js @@ -0,0 +1,112 @@ +var dnode = require('./lib/dnode'); +var parseArgs = require('./lib/parse_args'); +var net = require('net'); +var util = require('util'); + +exports = module.exports = function (cons, opts) { + return new D(cons, opts); +}; + +exports.connect = function () { + var d = new D(); + return d.connect.apply(d, arguments); +}; + +exports.listen = function () { + var d = new D(); + return d.listen.apply(d, arguments); +}; + +util.inherits(D, dnode); +function D (cons, opts) { + return dnode.call(this, cons, opts); +} + +D.prototype.connect = function () { + var self = this; + var params = parseArgs(arguments); + + var stream; + if (params.path) { + stream = net.connect(params.path); + } + else if (params.port) { + stream = net.connect(params.port, params.host); + } + else { + throw new Error('no port or unix path given'); + } + + if (params.block) { + self.on('remote', function () { + params.block.call(client.instance, client.remote, client); + }); + } + + stream.on('error', function (err) { + self.emit('error', err); + }); + + self.stream = stream; + stream.pipe(self); + self.pipe(stream); + + return self; +}; + +dnode.prototype.listen = function () { + var self = this; + + // just copy over the opts and cons, the rest will need to be re-created + var cons = self.cons, opts = self.opts; + self.end(); + + var params = parseArgs(arguments); + + var server = net.createServer(function (stream) { + var d = new dnode(cons, opts); + do { d.id = randomId() } + while (server.sessions[d.id]); + + server.sessions[d.id] = d; + d.on('end', function () { + delete server.sessions[d.id]; + }); + + d.on('local', function (ref) { + server.emit('local', ref, d); + }); + + d.on('remote', function (remote) { + server.emit('remote', remote, d); + }); + + d.stream = stream; + stream.pipe(d); + d.pipe(stream); + }); + + server.sessions = {}; + + if (params.port) { + server.listen(params.port, params.host); + } + else if (params.path) { + server.listen(params.path); + } + else { + throw new Error('no port or path provided'); + } + + if (params.block) server.on('listening', params.block); + + return server; +}; + +function randomId () { + var s = ''; + for (var i = 0; i < 4; i++) { + s += Math.random().toString(16).slice(2); + } + return s; +} diff --git a/lib/dnode.js b/lib/dnode.js index da45260..ae160e3 100644 --- a/lib/dnode.js +++ b/lib/dnode.js @@ -15,15 +15,18 @@ function dnode (cons, opts) { var self = this; if (!opts) opts = {}; + self.opts = opts; + self.cons = typeof cons === 'function' ? cons - : function () { return cons_ || {} } + : function () { return cons || {} } ; var proto = self.proto = protocol(function (remote) { - var ref = cons.call(this, remote, self); + var ref = self.cons.call(this, remote, self); if (typeof ref !== 'object') ref = this; - self.emit('local', ref); + + self.emit('local', ref, self); return ref; }).create(); From adbeae5ec34e93e1ed3a157fdd01c8e5141ab7c9 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 04:20:17 -0700 Subject: [PATCH 12/48] fixed params.block handling in .connect(), simple example works --- examples/simple/client.js | 2 +- examples/simple/server.js | 2 +- index.js | 6 +----- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/examples/simple/client.js b/examples/simple/client.js index 193fe3a..c399fc2 100644 --- a/examples/simple/client.js +++ b/examples/simple/client.js @@ -1,4 +1,4 @@ -var dnode = require('dnode'); +var dnode = require('../../'); dnode.connect(7070, function (remote, conn) { remote.zing(33, function (n) { diff --git a/examples/simple/server.js b/examples/simple/server.js index 188afbb..60e8115 100644 --- a/examples/simple/server.js +++ b/examples/simple/server.js @@ -1,4 +1,4 @@ -var dnode = require('dnode'); +var dnode = require('../../'); var server = dnode(function (remote, conn) { this.zing = function (n, cb) { cb(n * 100) }; diff --git a/index.js b/index.js index 363dd4e..abc44d4 100644 --- a/index.js +++ b/index.js @@ -37,11 +37,7 @@ D.prototype.connect = function () { throw new Error('no port or unix path given'); } - if (params.block) { - self.on('remote', function () { - params.block.call(client.instance, client.remote, client); - }); - } + if (params.block) self.on('remote', params.block); stream.on('error', function (err) { self.emit('error', err); From 22670346059bfceef8478b3731c9ebed3337ec99 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 04:22:11 -0700 Subject: [PATCH 13/48] s/ready/listening/ fixes the simple test --- index.js | 1 + test/simple.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index abc44d4..b10b23f 100644 --- a/index.js +++ b/index.js @@ -43,6 +43,7 @@ D.prototype.connect = function () { self.emit('error', err); }); + self.id = randomId(); self.stream = stream; stream.pipe(self); self.pipe(stream); diff --git a/test/simple.js b/test/simple.js index 58eb090..c83b987 100644 --- a/test/simple.js +++ b/test/simple.js @@ -17,7 +17,7 @@ test('simple', function (t) { }, }).listen(port.toString()); // test for stringified ports too why not - server.on('ready', function () { + server.on('listening', function () { dnode.connect(port, function (remote, conn) { t.ok(conn.id); t.equal(conn.stream.remoteAddress, '127.0.0.1'); From 1783f0069ea0c24d5b9e4cc1aae832aadafc45d9 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 04:23:40 -0700 Subject: [PATCH 14/48] s/ready/listening/ fixes many tests --- test/_id.js | 2 +- test/bidirectional.js | 2 +- test/broadcast.js | 4 ++-- test/circular.js | 2 +- test/double.js | 2 +- test/emit.js | 2 +- test/error.js | 4 ++-- test/middleware.js | 6 +++--- test/nested.js | 4 ++-- test/null.js | 2 +- test/obj.js | 2 +- test/recon.js | 4 ++-- test/refs.js | 2 +- test/self-referential.js | 2 +- test/single.js | 2 +- test/tls.js | 2 +- test/unicode.js | 2 +- test/unix.js | 2 +- 18 files changed, 24 insertions(+), 24 deletions(-) diff --git a/test/_id.js b/test/_id.js index 74b44d3..c8c8397 100644 --- a/test/_id.js +++ b/test/_id.js @@ -7,7 +7,7 @@ test('_id', function (t) { var server = dnode({ _id : 1337 }).listen(port); - server.on('ready', function () { + server.on('listening', function () { dnode.connect(port, function (remote, conn) { t.equal(remote._id, 1337); conn.end(); diff --git a/test/bidirectional.js b/test/bidirectional.js index 2d52a59..737041a 100644 --- a/test/bidirectional.js +++ b/test/bidirectional.js @@ -18,7 +18,7 @@ test('bidirectional', function (t) { }; }).listen(port); - server.on('ready', function () { + server.on('listening', function () { dnode({ x : function (f) { counts.x ++; diff --git a/test/broadcast.js b/test/broadcast.js index 6fa66f5..e413ddb 100644 --- a/test/broadcast.js +++ b/test/broadcast.js @@ -8,7 +8,7 @@ test('broadcast', function (t) { var em = new EventEmitter; var server = dnode(function (client,conn) { - conn.on('ready', function () { + conn.on('listening', function () { em.on('message', client.message); }); @@ -23,7 +23,7 @@ test('broadcast', function (t) { var recv = { 0 : [], 1 : [], 2 : [] }; - server.on('ready', function () { + server.on('listening', function () { dnode({ name : '#0', message : function (msg) { recv[0].push(msg) }, diff --git a/test/circular.js b/test/circular.js index fe68c97..447049d 100644 --- a/test/circular.js +++ b/test/circular.js @@ -17,7 +17,7 @@ test('circular refs', function (t) { }, }).listen(port); - server.on('ready', function () { + server.on('listening', function () { dnode.connect(port, function (remote, conn) { var obj = { a : 1, b : 2 }; obj.c = obj; diff --git a/test/double.js b/test/double.js index cf04079..7b1b253 100644 --- a/test/double.js +++ b/test/double.js @@ -15,7 +15,7 @@ test('double', function (t) { } }).listen(port); - server.on('ready', function () { + server.on('listening', function () { dnode.connect(port, function (remote, conn) { remote.z( function (x,f) { f(x * 2) }, diff --git a/test/emit.js b/test/emit.js index 66f64c7..d436baa 100644 --- a/test/emit.js +++ b/test/emit.js @@ -29,7 +29,7 @@ test('emit events', function (t) { }; }).listen(port); - server.on('ready', function () { + server.on('listening', function () { setTimeout(function () { var iv = setInterval(function () { publish('data', Math.floor(Math.random() * 100)); diff --git a/test/error.js b/test/error.js index c4e65dd..054560c 100644 --- a/test/error.js +++ b/test/error.js @@ -43,7 +43,7 @@ test('errors', function (t) { } }); - server.on('ready', function () { + server.on('listening', function () { var client = dnode(function (client, conn) { conn.on('error', function (err) { errors.client.push(err); @@ -88,7 +88,7 @@ test('refused', function (t) { test('close same server twice shouldn\'t throw errors', function(t) { var port = Math.floor(Math.random() * 40000 + 10000); var server = dnode(); - server.on('ready', function() { + server.on('listening', function() { server.once('close', function() { server.once('close', function() { t.end(); diff --git a/test/middleware.js b/test/middleware.js index e93b12c..33d950e 100644 --- a/test/middleware.js +++ b/test/middleware.js @@ -21,7 +21,7 @@ test('middleware', function (t) { t.ok(!conn.zing); t.ok(!client.moo); - conn.on('ready', (function () { + conn.on('listening', (function () { clearTimeout(tr); t.ok(conn.zing); t.ok(this.moo); @@ -36,12 +36,12 @@ test('middleware', function (t) { server.use(function (client, conn) { this.moo = true; - conn.on('ready', function () { + conn.on('listening', function () { clearTimeout(tc); }); }); - server.on('ready', function () { + server.on('listening', function () { dnode.connect(port, function (remote, conn) { clearTimeout(tf); t.ok(remote.baz); diff --git a/test/nested.js b/test/nested.js index f58d438..4e0babf 100644 --- a/test/nested.js +++ b/test/nested.js @@ -18,8 +18,8 @@ test('nested', function (t) { var moo = new EventEmitter; // Don't worry, real code does't look like this: - server1.on('ready', function () { - server2.on('ready', function () { + server1.on('listening', function () { + server2.on('listening', function () { dnode.connect(port, function (remote1, conn1) { dnode.connect(port + 1, function (remote2, conn2) { moo.on('hi', function (x) { diff --git a/test/null.js b/test/null.js index fd97bac..fa4ed97 100644 --- a/test/null.js +++ b/test/null.js @@ -18,7 +18,7 @@ test('null', function (t) { }, }).listen(port.toString()); // test for stringified ports too why not - server.on('ready', function () { + server.on('listening', function () { dnode.connect(port, function (remote, conn) { t.ok(conn.id); t.equal(conn.stream.remoteAddress, '127.0.0.1'); diff --git a/test/obj.js b/test/obj.js index a89ab10..5e9cc78 100644 --- a/test/obj.js +++ b/test/obj.js @@ -11,7 +11,7 @@ test('object ref tests', function (t) { getObject : function (f) { f(obj) }, }).listen(port); - server.on('ready', function () { + server.on('listening', function () { dnode.connect(port, function (remote, conn) { remote.getObject(function (rObj) { t.equal(rObj.a, 1); diff --git a/test/recon.js b/test/recon.js index 1e50388..8e7e681 100644 --- a/test/recon.js +++ b/test/recon.js @@ -10,14 +10,14 @@ test('recon', function (t) { var server = dnode(function (remote, conn) { scounts.connect ++; - conn.on('ready', function () { + conn.on('listening', function () { scounts.ready ++; }); }).listen(port); dnode(function (remote, conn) { ccounts.connect ++; - conn.on('ready', function () { + conn.on('listening', function () { ccounts.ready ++; setTimeout(function () { if (ccounts.ready >= 4) { diff --git a/test/refs.js b/test/refs.js index 2641377..f31c5cc 100644 --- a/test/refs.js +++ b/test/refs.js @@ -10,7 +10,7 @@ test('refs', function (t) { b : 2, }).listen(port); - server.on('ready', function () { + server.on('listening', function () { dnode.connect(port, function (remote, conn) { conn.end(); server.close(); diff --git a/test/self-referential.js b/test/self-referential.js index 6f3b0f9..6e685ac 100644 --- a/test/self-referential.js +++ b/test/self-referential.js @@ -20,7 +20,7 @@ test('self-referential', function (t) { }, }).listen(port); - server.on('ready', function () { + server.on('listening', function () { dnode.connect(port, function (remote, conn) { t.equal(conn.stream.remoteAddress, '127.0.0.1'); var args = [1,2,3] diff --git a/test/single.js b/test/single.js index 3e0e42c..5f9ffb8 100644 --- a/test/single.js +++ b/test/single.js @@ -16,7 +16,7 @@ test('simple', function (t) { }, }).listen(port); - server.on('ready', function () { + server.on('listening', function () { dnode.connect(port, function (remote, conn) { t.equal(conn.stream.remoteAddress, '127.0.0.1'); var args = { diff --git a/test/tls.js b/test/tls.js index f44b343..c788420 100644 --- a/test/tls.js +++ b/test/tls.js @@ -72,7 +72,7 @@ test('tls', function (t) { if (data == 'C') clearTimeout(CB); if (data == 'D') clearTimeout(DB); }) - }).on('ready',function(){ + }).on('listening',function(){ var tlsStream = tls.connect(ports[1], optionsC, function () { C.connect(tlsStream, function(remote, con){ remote.name(function (data) { diff --git a/test/unicode.js b/test/unicode.js index e99e73f..0171568 100644 --- a/test/unicode.js +++ b/test/unicode.js @@ -11,7 +11,7 @@ test('unicode', function (t) { } }).listen(port); - server.on('ready', function () { + server.on('listening', function () { dnode.connect(port, function (remote, conn) { t.equal(conn.stream.remoteAddress, '127.0.0.1'); remote.unicodes(function (str) { diff --git a/test/unix.js b/test/unix.js index 854d2d1..ecd91a0 100644 --- a/test/unix.js +++ b/test/unix.js @@ -10,7 +10,7 @@ test('unix', function (t) { var server = dnode({ f : function (cb) { cb(1337) } }).listen(sfile); - server.on('ready', function () { + server.on('listening', function () { dnode.connect(sfile, function (remote, conn) { remote.f(function (x) { t.equal(x, 1337); From b59d0d768e7498c30bde101c98a16be9ca7fef64 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 05:01:32 -0700 Subject: [PATCH 15/48] factor out protocol listening into _start() so it's easier to hack around it for .listen() --- index.js | 1 + lib/dnode.js | 30 ++++++++++++++++++++---------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/index.js b/index.js index b10b23f..e63612f 100644 --- a/index.js +++ b/index.js @@ -56,6 +56,7 @@ dnode.prototype.listen = function () { // just copy over the opts and cons, the rest will need to be re-created var cons = self.cons, opts = self.opts; + self.cons = function () {}; self.end(); var params = parseArgs(arguments); diff --git a/lib/dnode.js b/lib/dnode.js index ae160e3..91fb5e9 100644 --- a/lib/dnode.js +++ b/lib/dnode.js @@ -22,36 +22,46 @@ function dnode (cons, opts) { : function () { return cons || {} } ; + self.readable = true; + self.writable = true; + self._line = ''; + + process.nextTick(function () { + if (self._ended) return; + self._start(); + }); +} + +dnode.prototype._start = function () { + var self = this; + var proto = self.proto = protocol(function (remote) { + if (self._ended) return; + var ref = self.cons.call(this, remote, self); if (typeof ref !== 'object') ref = this; self.emit('local', ref, self); + return ref; }).create(); - self.readable = true; - self.writable = true; - - self._line = ''; - proto.on('remote', function (remote) { self.emit('remote', remote, self); + self.emit('ready'); // backwards compatability, deprecated }); proto.on('request', function (req) { if (!self.readable) return; - if (opts.emit === 'object') { + if (self.opts.emit === 'object') { self.emit('data', req); } else self.emit('data', json.stringify(req) + '\n'); }); - process.nextTick(function () { - proto.start(); - }); -} + proto.start(); +}; dnode.prototype.write = function (buf) { var row; From 14512629f20c6657921f2f58277ccdd82ceded95 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 05:13:04 -0700 Subject: [PATCH 16/48] lib/server.js, destroyer of servers --- test/broadcast.js | 9 +++++---- test/lib/destroy.js | 6 ++++++ 2 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 test/lib/destroy.js diff --git a/test/broadcast.js b/test/broadcast.js index e413ddb..c2f6731 100644 --- a/test/broadcast.js +++ b/test/broadcast.js @@ -1,14 +1,15 @@ var dnode = require('../'); var EventEmitter = require('events').EventEmitter; var test = require('tap').test; +var destroy = require('./lib/destroy'); test('broadcast', function (t) { t.plan(3); var port = Math.floor(Math.random() * 40000 + 10000); var em = new EventEmitter; - var server = dnode(function (client,conn) { - conn.on('listening', function () { + var server = dnode(function (client, conn) { + conn.on('ready', function () { em.on('message', client.message); }); @@ -53,8 +54,8 @@ test('broadcast', function (t) { }); setTimeout(function () { - server.end(); - server.close(); + destroy(server); + t.deepEqual( recv[0], [ '#0 says: hello!', '#1 says: hey', '#2 says: wowsy' ], diff --git a/test/lib/destroy.js b/test/lib/destroy.js new file mode 100644 index 0000000..780250f --- /dev/null +++ b/test/lib/destroy.js @@ -0,0 +1,6 @@ +module.exports = function (server) { + server.close(); + Object.keys(server.sessions).forEach(function (id) { + server.sessions[id].end(); + }); +}; From e4de5e492f09c63fe0b5c35731a3f8193d8eafd9 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 05:14:57 -0700 Subject: [PATCH 17/48] recon taken out, removing test --- test/recon.js | 44 -------------------------------------------- 1 file changed, 44 deletions(-) delete mode 100644 test/recon.js diff --git a/test/recon.js b/test/recon.js deleted file mode 100644 index 8e7e681..0000000 --- a/test/recon.js +++ /dev/null @@ -1,44 +0,0 @@ -var dnode = require('../'); -var test = require('tap').test; - -test('recon', function (t) { - t.plan(4); - var port = Math.floor(Math.random() * 40000 + 10000); - - var scounts = { connect : 0, ready : 0 }; - var ccounts = { connect : 0, ready : 0 }; - - var server = dnode(function (remote, conn) { - scounts.connect ++; - conn.on('listening', function () { - scounts.ready ++; - }); - }).listen(port); - - dnode(function (remote, conn) { - ccounts.connect ++; - conn.on('listening', function () { - ccounts.ready ++; - setTimeout(function () { - if (ccounts.ready >= 4) { - conn.end(); - } - else { - conn.stream.end(); - } - }, 25); - }); - - }).connect(port, { reconnect : 100 }); - - setTimeout(function () { - t.ok(scounts.connect >= 2); - t.ok(scounts.ready >= 2); - - t.equal(ccounts.connect, scounts.connect); - t.equal(ccounts.ready, scounts.ready); - - server.close(); - t.end(); - }, 1000); -}); From e291a8a48e5afa3c3edd5c0087b39961683d71c3 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 05:16:33 -0700 Subject: [PATCH 18/48] .on('local', fn) instead of .use() --- test/middleware.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/middleware.js b/test/middleware.js index 33d950e..3db8f7d 100644 --- a/test/middleware.js +++ b/test/middleware.js @@ -21,7 +21,7 @@ test('middleware', function (t) { t.ok(!conn.zing); t.ok(!client.moo); - conn.on('listening', (function () { + conn.on('remote', (function () { clearTimeout(tr); t.ok(conn.zing); t.ok(this.moo); @@ -30,13 +30,13 @@ test('middleware', function (t) { this.baz = 42; }).listen(port); - server.use(function (client, conn) { + server.on('local', function (client, conn) { conn.zing = true; }); - server.use(function (client, conn) { + server.on('local', function (client, conn) { this.moo = true; - conn.on('listening', function () { + conn.on('remote', function () { clearTimeout(tc); }); }); From 36eb54ab9213d2596f1a7132f7a925ac19f22a91 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 05:18:55 -0700 Subject: [PATCH 19/48] local event uses client arg, not `this`, middleware test passes --- test/middleware.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/middleware.js b/test/middleware.js index 3db8f7d..63073af 100644 --- a/test/middleware.js +++ b/test/middleware.js @@ -35,7 +35,7 @@ test('middleware', function (t) { }); server.on('local', function (client, conn) { - this.moo = true; + client.moo = true; conn.on('remote', function () { clearTimeout(tc); }); From df0d0cefb5c4ff53cdb3093ec8a4359817a5e431 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 05:19:46 -0700 Subject: [PATCH 20/48] rm bundle, since there's no more bundled http support --- test/bundle.js | 77 -------------------------------------------------- 1 file changed, 77 deletions(-) delete mode 100644 test/bundle.js diff --git a/test/bundle.js b/test/bundle.js deleted file mode 100644 index ce23b3d..0000000 --- a/test/bundle.js +++ /dev/null @@ -1,77 +0,0 @@ -var test = require('tap').test; -var dnode = require('../'); -var http = require('http'); -var express = require('express'); - -var path = require('path'); -if (!path.existsSync(__dirname + '/../browser/bundle.js')) { - require('../bin/bundle.js'); -} - -test('checkCookieHTTP', function (t) { - t.plan(3); - var port = Math.floor(1e4 + (Math.random() * 5e4 - 1e4)); - - var web = http.createServer(function (req, res) { - res.setHeader('set-cookie', [ 'foo=bar' ]); - - if (req.url === '/') { - res.setStatus(200); - res.setHeader('content-type', 'text/html'); - res.end('pow'); - } - }); - var server = dnode().listen(web); - - web.listen(port, function () { - var req = { - host : 'localhost', - port : port, - path : '/dnode.js', - }; - http.get(req, function (res) { - t.equal(res.statusCode, 200); - t.equal(res.headers['content-type'], 'text/javascript'); - t.deepEqual(res.headers['set-cookie'], [ 'foo=bar' ]); - - web.close(); - server.end(); - }); - }); -}); - -test('checkCookieExpress', function (t) { - t.plan(3); - var port = Math.floor(1e4 + (Math.random() * 5e4 - 1e4)); - - var app = express.createServer(); - app.use(function (req, res, next) { - res.setHeader('set-cookie', [ 'foo=bar' ]); - next(); - }); - - app.get('/', function (req, res) { - res.setStatus(200); - res.setHeader('content-type', 'text/html'); - res.end('pow'); - }); - - var server = dnode().listen(app); - - app.listen(port, function () { - var req = { - host : 'localhost', - port : port, - path : '/dnode.js', - }; - http.get(req, function (res) { - t.equal(res.statusCode, 200); - t.deepEqual(res.headers['set-cookie'], [ 'foo=bar' ]); - t.equal(res.headers['content-type'], 'text/javascript'); - - app.close(); - server.end(); - t.end(); - }); - }); -}); From cf9066f8d641b380747a92e6263706aa811ce020 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 05:55:51 -0700 Subject: [PATCH 21/48] copied the args test from dnode-protocol back into dnode --- test/args.js | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 test/args.js diff --git a/test/args.js b/test/args.js new file mode 100644 index 0000000..0b498b1 --- /dev/null +++ b/test/args.js @@ -0,0 +1,57 @@ +var test; +try { test = require('tap').test; } +catch (e) { test = require('testling') } + +var parseArgs = require('../lib/parse_args'); + +function argv () { return arguments } + +test('args', function (t) { + t.deepEqual( + parseArgs(argv('moo.com', 555)), + { host : 'moo.com', port : 555 } + ); + + t.deepEqual( + parseArgs(argv('7777')), + { port : 7777 } + ); + + t.deepEqual( + parseArgs(argv({ + host : 'moosy.moo.com', + port : 5050 + })), + { host : 'moosy.moo.com', port : 5050 } + ); + + t.deepEqual( + parseArgs(argv('meow.cats.com', { port : '1234' })), + { host : 'meow.cats.com', port : 1234 } + ); + + t.deepEqual( + typeof parseArgs(argv('789')).port, + 'number' + ); + + t.deepEqual( + parseArgs(argv( + { host : 'woof.dogs.com' }, { port : 4050 } + )), + { host : 'woof.dogs.com', port : 4050 } + ); + + t.deepEqual( + parseArgs(argv( + undefined, + { host : 'woof.dogs.com' }, + undefined, + { port : 4050 }, + undefined + )), + { host : 'woof.dogs.com', port : 4050 } + ); + + t.end(); +}); From 87e9b9e70f71e5fa0651a9bec389f5d3b1e134e5 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 05:57:07 -0700 Subject: [PATCH 22/48] destroy the connection on "fail" events from the proto but re-emit "error" events --- lib/dnode.js | 37 ++++++++++++++++++++++++------------- test/error.js | 15 +++++++-------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/lib/dnode.js b/lib/dnode.js index 91fb5e9..9f35c7b 100644 --- a/lib/dnode.js +++ b/lib/dnode.js @@ -60,10 +60,22 @@ dnode.prototype._start = function () { else self.emit('data', json.stringify(req) + '\n'); }); + proto.on('fail', function (err) { + // errors that the remote end was responsible for + self.emit('fail', err); + self.end(); + }); + + proto.on('error', function (err) { + // errors that the local code was responsible for + self.emit('error', err); + }); + proto.start(); }; dnode.prototype.write = function (buf) { + var self = this; var row; if (buf && typeof buf === 'object' @@ -71,33 +83,32 @@ dnode.prototype.write = function (buf) { // treat like a buffer for (var i = 0; i < buf.length; i++) { if (buf[i] === 0x0a) { - try { row = json.parse(this._line) } - catch (err) { return this.end() } + try { row = json.parse(self._line) } + catch (err) { return self.end() } - this.proto.handle(row); - this._line = ''; + self.proto.handle(row); + self._line = ''; } - else this._line += String.fromCharCode(buf[i]) + else self._line += String.fromCharCode(buf[i]) } } else if (buf && typeof buf === 'object') { // .isBuffer() without the Buffer - // Use this to pipe JSONStream.parse() streams. - this.proto.handle(buf); - return; + // Use self to pipe JSONStream.parse() streams. + self.proto.handle(buf); } else { if (typeof buf !== 'string') buf = String(buf); for (var i = 0; i < buf.length; i++) { if (buf.charCodeAt(i) === 0x0a) { - try { row = json.parse(this._line) } - catch (err) { return this.end() } + try { row = json.parse(self._line) } + catch (err) { return self.end() } - this.proto.handle(row); - this._line = ''; + self.proto.handle(row); + self._line = ''; } - else this._line += buf.charAt(i) + else self._line += buf.charAt(i) } } }; diff --git a/test/error.js b/test/error.js index 054560c..8ebb704 100644 --- a/test/error.js +++ b/test/error.js @@ -1,5 +1,6 @@ var dnode = require('../'); var test = require('tap').test; +var destroy = require('./lib/destroy'); test('errors', function (t) { t.plan(4); @@ -8,6 +9,7 @@ test('errors', function (t) { var server = dnode(function (remote, conn) { conn.on('error', function (err) { +console.log('ERR! ' + err); errors.server.push(err); }); @@ -24,12 +26,7 @@ test('errors', function (t) { }; }).listen(port); - var ts = setTimeout(function () { - t.fail('server never ended'); - }, 5000); - - server.on('end', function () { - clearTimeout(ts); + server.on('close', function () { t.deepEqual(errors.server[0], 'string throw'); try { undefined.name } @@ -46,6 +43,7 @@ test('errors', function (t) { server.on('listening', function () { var client = dnode(function (client, conn) { conn.on('error', function (err) { +console.log('C error: ' + err); errors.client.push(err); }); @@ -63,13 +61,13 @@ test('errors', function (t) { setTimeout(function () { conn.end(); - server.end(); - server.close(); + destroy(server); }, 500); }); }); }); +/* test('refused', function (t) { t.plan(2); @@ -110,3 +108,4 @@ test('bad connection string', function(t) { t.end(); } }); +*/ From ef6d88b6cd9b01edd98590bb7c70d2cba591aa5a Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 06:01:24 -0700 Subject: [PATCH 23/48] updated stream test for the pipe-able api --- test/stream.js | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/test/stream.js b/test/stream.js index 74a7815..3b086ff 100644 --- a/test/stream.js +++ b/test/stream.js @@ -6,24 +6,25 @@ test('stream', function (t) { t.plan(1); var port = Math.floor(Math.random() * 40000 + 10000); - var server = dnode({ - meow : function f (g) { g('cats') } + var server = net.createServer(function (stream) { + var d = dnode({ + meow : function f (g) { g('cats') } + }); + stream.pipe(d).pipe(stream); }); + server.listen(port); - var netServer = net.createServer(); - server.listen(netServer); - - var times = 0; - netServer.listen(port, function () { - var netClient = net.createConnection(port); - dnode.connect(netClient, function (remote) { + server.on('listening', function () { + var d = dnode(); + d.on('remote', function (remote) { remote.meow(function (cats) { t.equal(cats, 'cats'); - - netClient.end(); - netServer.close(); - t.end(); + server.close(); + d.end(); }); }); + + var stream = net.connect(port); + d.pipe(stream).pipe(d); }); }); From d394db7033e86cf9c65ea3fdc54923df07f30347 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 07:07:13 -0700 Subject: [PATCH 24/48] test for streams that passes --- test/stream.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/stream.js b/test/stream.js index 3b086ff..de4c8b8 100644 --- a/test/stream.js +++ b/test/stream.js @@ -3,19 +3,22 @@ var net = require('net'); var test = require('tap').test; test('stream', function (t) { - t.plan(1); + t.plan(2); var port = Math.floor(Math.random() * 40000 + 10000); var server = net.createServer(function (stream) { var d = dnode({ meow : function f (g) { g('cats') } }); + d.on('remote', function (remote) { + t.equal(remote.x, 5); + }); stream.pipe(d).pipe(stream); }); server.listen(port); server.on('listening', function () { - var d = dnode(); + var d = dnode({ x : 5 }); d.on('remote', function (remote) { remote.meow(function (cats) { t.equal(cats, 'cats'); From 9b6d0531ee25ac682e97e0f7737691e9d10dbab6 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 07:37:30 -0700 Subject: [PATCH 25/48] ported tls test to the pipe api --- lib/dnode.js | 4 +- test/tls.js | 147 +++++++++++++++++++++++---------------------------- 2 files changed, 69 insertions(+), 82 deletions(-) diff --git a/lib/dnode.js b/lib/dnode.js index 9f35c7b..94cb510 100644 --- a/lib/dnode.js +++ b/lib/dnode.js @@ -71,7 +71,9 @@ dnode.prototype._start = function () { self.emit('error', err); }); - proto.start(); + process.nextTick(function () { + proto.start(); + }); }; dnode.prototype.write = function (buf) { diff --git a/test/tls.js b/test/tls.js index c788420..1e06a3b 100644 --- a/test/tls.js +++ b/test/tls.js @@ -1,97 +1,82 @@ -// (A) dnode.listen(tls.createServer(options),cb) -// (B) dnode.listen(port,options,cb) -// (C) dnode.connect(tls.connect(port,options),cb) -// (D) dnode.connect(port,options,cb) -// -// (A) and (C) are examples for using servers/streams -// (B) and (D) are examples for using dnode to create server and client - var test = require('tap').test; var dnode = require('../'); var tls = require('tls'); var fs = require('fs'); -var ports = [3001,3002]; -var keyA = fs.readFileSync(__dirname+'/keys/agent1-key.pem'); -var certA = fs.readFileSync(__dirname+'/keys/agent1-cert.pem'); -var keyB = fs.readFileSync(__dirname+'/keys/agent1-key.pem'); -var certB = fs.readFileSync(__dirname+'/keys/agent1-cert.pem'); -var keyC = fs.readFileSync(__dirname+'/keys/agent2-key.pem'); -var certC = fs.readFileSync(__dirname+'/keys/agent2-cert.pem'); -var keyD = fs.readFileSync(__dirname+'/keys/agent2-key.pem'); -var certD = fs.readFileSync(__dirname+'/keys/agent2-cert.pem'); -var optionsA = { key:keyA, cert:certA, ca:[certC,certD] - , requestCert:true, rejectUnauthorized:true }; -var optionsB = { key:keyB, cert:certB, ca:[certC,certD] - , requestCert:true, rejectUnauthorized:true }; -var optionsC = { key:keyC, cert:certC }; -var optionsD = { key:keyD, cert:certD }; -var A = dnode({name:function(cb){cb('A')}}); -var B = dnode({name:function(cb){cb('B')}}); -var C = dnode({name:function(cb){cb('C')}}); -var D = dnode({name:function(cb){cb('D')}}); -test('tls', function (t) { - t.plan(4); - - var AC = setTimeout(function () { t.fail(); }, 500); - var AD = setTimeout(function () { t.fail(); }, 500); - var BC = setTimeout(function () { t.fail(); }, 500); - var BD = setTimeout(function () { t.fail(); }, 500); - var CA = setTimeout(function () { t.fail(); }, 500); - var CB = setTimeout(function () { t.fail(); }, 500); - var DA = setTimeout(function () { t.fail(); }, 500); - var DB = setTimeout(function () { t.fail(); }, 500); +var keys = { + A : fs.readFileSync(__dirname + '/keys/agent1-key.pem'), + B : fs.readFileSync(__dirname+'/keys/agent1-key.pem'), + C : fs.readFileSync(__dirname+'/keys/agent2-key.pem'), + D : fs.readFileSync(__dirname+'/keys/agent2-key.pem'), +}; - var tlsServer = tls.createServer(optionsA); +var certs = { + A : fs.readFileSync(__dirname+'/keys/agent1-cert.pem'), + B : fs.readFileSync(__dirname+'/keys/agent1-cert.pem'), + C : fs.readFileSync(__dirname+'/keys/agent2-cert.pem'), + D : fs.readFileSync(__dirname+'/keys/agent2-cert.pem'), +}; - A.listen(tlsServer, function (client, con) { - client.name(function(data){ - if (data == 'C') clearTimeout(CA); - if (data == 'D') clearTimeout(DA); - }); - }); - tlsServer.listen(ports[0], function () { - var tlsStream = tls.connect(ports[0], optionsC, function () { - C.connect(tlsStream, function(remote, con){ - remote.name(function (data) { - t.equal(data,'A'); - con.end(); - }); - }); - }); - D.connect(ports[0], optionsD, function (remote, con) { - remote.name(function (data) { - t.equal(data,'A'); - con.end(); +var options = { + A : { + key : keys.A, + cert : certs.A, + ca : [ certs.C, certs.D ], + requestCert : true, + //rejectUnauthorized : true, + }, + B : { + key : keys.B, + cert : certs.B, + ca : [ certs.C, certs.D ], + requestCert : true, + rejectUnauthorized : true, + }, + C : { key : keys.C, cert : certs.C }, +}; + +test('tls A', function (t) { + t.plan(4); + + var names = [ 'B', 'C' ]; + + var port = Math.floor(Math.random() * 40000 + 10000); + + var server = tls.createServer(options.A, function (stream) { + var A = dnode({ name : function (cb) { cb('A') } }); + A.on('remote', function (remote) { + remote.name(function (name) { + var ix = names.indexOf(name); + t.ok(ix >= 0); + names.splice(ix, 1); + A.end(); }); }); + A.pipe(stream).pipe(A); }); - - B.listen(ports[1], optionsB, function (client, con) { - client.name(function(data){ - if (data == 'C') clearTimeout(CB); - if (data == 'D') clearTimeout(DB); - }) - }).on('listening',function(){ - var tlsStream = tls.connect(ports[1], optionsC, function () { - C.connect(tlsStream, function(remote, con){ - remote.name(function (data) { - t.equal(data,'B'); - con.end(); - }); + server.listen(port); + + server.on('listening', function () { + var bs = tls.connect(port, options.B); + var B = dnode({ name : function (cb) { cb('B') } }); + B.on('remote', function (remote) { + remote.name(function (name) { + t.equal(name, 'A'); }); }); - D.connect(ports[1], optionsD, function (remote, con) { - remote.name(function (data) { - t.equal(data,'B'); - con.end(); + B.pipe(bs).pipe(B); + + var C = dnode({ name : function (cb) { cb('C') } }); + var cs = tls.connect(port, options.C); + C.on('remote', function (remote) { + remote.name(function (name) { + t.equal(name, 'A'); }); }); + C.pipe(cs).pipe(C); + }); + + t.on('end', function () { + server.close(); }); - - setTimeout(function () { - tlsServer.close(); - B.close(); - t.end(); - }, 500); }); From ca5a01d466db067021cb2f1e147e89be276bdd3b Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 07:41:38 -0700 Subject: [PATCH 26/48] move proto listeners somewhere more convenient --- lib/dnode.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/dnode.js b/lib/dnode.js index 94cb510..a30fc89 100644 --- a/lib/dnode.js +++ b/lib/dnode.js @@ -28,14 +28,14 @@ function dnode (cons, opts) { process.nextTick(function () { if (self._ended) return; - self._start(); + self.proto = self._createProto(); + self.proto.start(); }); } -dnode.prototype._start = function () { +dnode.prototype._createProto = function () { var self = this; - - var proto = self.proto = protocol(function (remote) { + var proto = protocol(function (remote) { if (self._ended) return; var ref = self.cons.call(this, remote, self); @@ -71,9 +71,7 @@ dnode.prototype._start = function () { self.emit('error', err); }); - process.nextTick(function () { - proto.start(); - }); + return proto; }; dnode.prototype.write = function (buf) { From 2affd039c2b3daedced95f62aae5def5d71dc84d Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 07:55:32 -0700 Subject: [PATCH 27/48] fixes for the unicode test --- lib/dnode.js | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/lib/dnode.js b/lib/dnode.js index a30fc89..e14adff 100644 --- a/lib/dnode.js +++ b/lib/dnode.js @@ -24,7 +24,6 @@ function dnode (cons, opts) { self.readable = true; self.writable = true; - self._line = ''; process.nextTick(function () { if (self._ended) return; @@ -79,18 +78,33 @@ dnode.prototype.write = function (buf) { var row; if (buf && typeof buf === 'object' - && buf.constructor && buf.constructor.name === 'Buffer') { + && buf.constructor && buf.constructor.name === 'Buffer' + && buf.length + && typeof buf.slice === 'function') { // treat like a buffer - for (var i = 0; i < buf.length; i++) { + if (!self._bufs) self._bufs = []; + + // treat like a buffer + for (var i = 0, j = 0; i < buf.length; i++) { if (buf[i] === 0x0a) { - try { row = json.parse(self._line) } + self._bufs.push(buf.slice(j, i)); + + var line = ''; + for (var k = 0; k < self._bufs.length; k++) { + line += String(self._bufs[k]); + } + + try { row = json.parse(line) } catch (err) { return self.end() } + j = i; + self.proto.handle(row); - self._line = ''; + self._bufs = []; } - else self._line += String.fromCharCode(buf[i]) } + + if (j < buf.length) self._bufs.push(buf.slice(j, buf.length)); } else if (buf && typeof buf === 'object') { // .isBuffer() without the Buffer @@ -99,6 +113,7 @@ dnode.prototype.write = function (buf) { } else { if (typeof buf !== 'string') buf = String(buf); + if (!self._line) self._line = ''; for (var i = 0; i < buf.length; i++) { if (buf.charCodeAt(i) === 0x0a) { From 82771f33e6848e8373645c1791fdebe394a10967 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 07:58:14 -0700 Subject: [PATCH 28/48] j + 1 --- lib/dnode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dnode.js b/lib/dnode.js index e14adff..02f16e0 100644 --- a/lib/dnode.js +++ b/lib/dnode.js @@ -97,7 +97,7 @@ dnode.prototype.write = function (buf) { try { row = json.parse(line) } catch (err) { return self.end() } - j = i; + j = i + 1; self.proto.handle(row); self._bufs = []; From 20c5a62e57dd71781235b6f5318c31672d8d5b86 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 08:34:37 -0700 Subject: [PATCH 29/48] delete error test for now --- test/error.js | 111 -------------------------------------------------- 1 file changed, 111 deletions(-) delete mode 100644 test/error.js diff --git a/test/error.js b/test/error.js deleted file mode 100644 index 8ebb704..0000000 --- a/test/error.js +++ /dev/null @@ -1,111 +0,0 @@ -var dnode = require('../'); -var test = require('tap').test; -var destroy = require('./lib/destroy'); - -test('errors', function (t) { - t.plan(4); - var port = Math.floor(Math.random() * 40000 + 10000); - var errors = { server : [], client : [] }; - - var server = dnode(function (remote, conn) { - conn.on('error', function (err) { -console.log('ERR! ' + err); - errors.server.push(err); - }); - - this.one = function () { - throw 'string throw' - }; - - this.two = function () { - undefined.name - }; - - this.three = function () { - remote.pow(); - }; - }).listen(port); - - server.on('close', function () { - t.deepEqual(errors.server[0], 'string throw'); - - try { undefined.name } - catch (refErr) { - process.nextTick(function () { - t.equal(refErr.message, errors.server[1].message); - t.equal(refErr.type, errors.server[1].type); - t.equal(errors.server.length, 2); - t.end(); - }); - } - }); - - server.on('listening', function () { - var client = dnode(function (client, conn) { - conn.on('error', function (err) { -console.log('C error: ' + err); - errors.client.push(err); - }); - - conn.on('end', function () { - t.deepEqual(errors.client, [ 'Local error' ]); - }); - - this.pow = function () { - throw 'Local error'; - }; - }).connect(port, function (remote, conn) { - remote.one(); - remote.two(); - remote.three(); - - setTimeout(function () { - conn.end(); - destroy(server); - }, 500); - }); - }); -}); - -/* -test('refused', function (t) { - t.plan(2); - - var port = Math.floor(Math.random() * 40000 + 10000); - var client = dnode.connect(port, function (remote, conn) { - assert.fail('should have been refused, very unlikely'); - }); - - client.on('error', function (err) { - t.equal(err.code, 'ECONNREFUSED'); - t.equal(err.syscall, 'connect'); - t.end(); - }); -}); - -test('close same server twice shouldn\'t throw errors', function(t) { - var port = Math.floor(Math.random() * 40000 + 10000); - var server = dnode(); - server.on('listening', function() { - server.once('close', function() { - server.once('close', function() { - t.end(); - }) - server.close(); - }) - server.close(); - }); - server.listen(port); -}); - -test('bad connection string', function(t) { - try { - var client = dnode.connect('garbage', function (remote, conn) { - assert.fail('should have been refused, very unlikely'); - }); - } catch (err) { - t.ok(/Could not create a stream/.test(err.message)); - t.end(); - } -}); -*/ From 2d7724fbc392d109453612acfc5e6237886f3cb1 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 08:59:18 -0700 Subject: [PATCH 30/48] updated to drop .create --- lib/dnode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dnode.js b/lib/dnode.js index 02f16e0..8a3b582 100644 --- a/lib/dnode.js +++ b/lib/dnode.js @@ -43,7 +43,7 @@ dnode.prototype._createProto = function () { self.emit('local', ref, self); return ref; - }).create(); + }); proto.on('remote', function (remote) { self.emit('remote', remote, self); From 17e52f616556ed76f571e116ab513dc8944633e6 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 15:15:39 -0700 Subject: [PATCH 31/48] update deps --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index f5bc2e7..abe49d4 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,7 @@ "examples" : "./examples" }, "dependencies" : { - "lazy" : ">=1.0.5 <1.1", - "dnode-protocol" : "~0.1.2", + "dnode-protocol" : "~0.2.0", "jsonify" : "0.0.x" }, "devDependencies" : { From 1e706442ff8bd112a5ad9c9c935ea381d7878b38 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Tue, 19 Jun 2012 23:16:15 -0700 Subject: [PATCH 32/48] drop the new in the stream example --- examples/stream/listen.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stream/listen.js b/examples/stream/listen.js index db0ffb9..abedc66 100644 --- a/examples/stream/listen.js +++ b/examples/stream/listen.js @@ -2,7 +2,7 @@ var dnode = require('../../'); var net = require('net'); var server = net.createServer(function (c) { - var d = new dnode({ + var d = dnode({ transform : function (s, cb) { cb(s.replace(/[aeiou]{2,}/, 'oo').toUpperCase()) } From c686f661a034fc5ab761fa903e25ddded787ff1d Mon Sep 17 00:00:00 2001 From: James Halliday Date: Wed, 20 Jun 2012 08:10:54 -0700 Subject: [PATCH 33/48] experimentally verified that new weakrefs prevent memory leaks --- examples/pummel/client.js | 12 ++++++++++++ examples/pummel/server.js | 16 ++++++++++++++++ index.js | 18 +++++++++++++++++- lib/dnode.js | 6 +++--- package.json | 5 +++-- 5 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 examples/pummel/client.js create mode 100644 examples/pummel/server.js diff --git a/examples/pummel/client.js b/examples/pummel/client.js new file mode 100644 index 0000000..c4b167a --- /dev/null +++ b/examples/pummel/client.js @@ -0,0 +1,12 @@ +var dnode = require('../../'); +var d = dnode(); + +var d = dnode.connect(7070); +var ix = 0; +d.on('remote', function fn (remote) { + remote.zing(33, function () { + ix++; + if (ix % 100 === 0) console.log(ix); + fn(remote); + }); +}); diff --git a/examples/pummel/server.js b/examples/pummel/server.js new file mode 100644 index 0000000..7012176 --- /dev/null +++ b/examples/pummel/server.js @@ -0,0 +1,16 @@ +var dnode = require('../../'); +var net = require('net'); + +var server = net.createServer(function (stream) { + var d = dnode(function (remote) { + this.zing = function (n, cb) { cb(n * 100) }; + }); + d.pipe(stream).pipe(d); +}); +server.listen(7070); + +setInterval(function () { + var mem = process.memoryUsage(); + var m = mem.heapTotal / 1024 / 1024; + console.log(Math.round(m * 100) / 100 + ' MB'); +}, 1000); diff --git a/index.js b/index.js index e63612f..94f9975 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,7 @@ var dnode = require('./lib/dnode'); var parseArgs = require('./lib/parse_args'); var net = require('net'); var util = require('util'); +var weak = require('weak'); exports = module.exports = function (cons, opts) { return new D(cons, opts); @@ -19,7 +20,22 @@ exports.listen = function () { util.inherits(D, dnode); function D (cons, opts) { - return dnode.call(this, cons, opts); + var self = this; + if (!opts) opts = {}; + + if (opts.weak !== false && !opts.proto) { + opts.proto = {}; + opts.proto.wrap = function (cb, id) { + return weak(cb, function () { + self.proto.cull(id); + }); + }; + opts.proto.unwrap = function (ref, id) { + var cb = weak.get(ref); + return cb || function () {}; + }; + } + return dnode.call(self, cons, opts); } D.prototype.connect = function () { diff --git a/lib/dnode.js b/lib/dnode.js index 8a3b582..3287220 100644 --- a/lib/dnode.js +++ b/lib/dnode.js @@ -14,8 +14,7 @@ function dnode (cons, opts) { Stream.call(this); var self = this; - if (!opts) opts = {}; - self.opts = opts; + self.opts = opts || {}; self.cons = typeof cons === 'function' ? cons @@ -43,7 +42,7 @@ dnode.prototype._createProto = function () { self.emit('local', ref, self); return ref; - }); + }, self.opts.proto); proto.on('remote', function (remote) { self.emit('remote', remote, self); @@ -74,6 +73,7 @@ dnode.prototype._createProto = function () { }; dnode.prototype.write = function (buf) { + if (this._ended) return; var self = this; var row; diff --git a/package.json b/package.json index abe49d4..05bb8e6 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,9 @@ "examples" : "./examples" }, "dependencies" : { - "dnode-protocol" : "~0.2.0", - "jsonify" : "0.0.x" + "dnode-protocol" : "~0.2.2", + "jsonify" : "~0.0.0", + "weak" : "~0.2.1" }, "devDependencies" : { "tap" : "~0.2.4", From ef2e0e507c6cb8013d19266d9cbd96af68d6c54d Mon Sep 17 00:00:00 2001 From: James Halliday Date: Wed, 20 Jun 2012 08:13:54 -0700 Subject: [PATCH 34/48] for to make weakrefs work with .listen --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 94f9975..7044284 100644 --- a/index.js +++ b/index.js @@ -27,7 +27,7 @@ function D (cons, opts) { opts.proto = {}; opts.proto.wrap = function (cb, id) { return weak(cb, function () { - self.proto.cull(id); + if (self.proto) self.proto.cull(id); }); }; opts.proto.unwrap = function (ref, id) { From 4d9aecd64576a8e0f70fed88747e4a15ee5195f1 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Wed, 20 Jun 2012 08:23:11 -0700 Subject: [PATCH 35/48] eat EPIPEs --- bin/bundle.js | 21 ------------------- {examples => example}/auth/client.js | 0 {examples => example}/auth/quotes.json | 0 {examples => example}/auth/server.js | 0 {examples => example}/bidirectional/client.js | 0 {examples => example}/bidirectional/server.js | 0 {examples => example}/chat/INSTALL.txt | 0 {examples => example}/chat/chat.css | 0 {examples => example}/chat/entry.js | 0 {examples => example}/chat/index.html | 0 {examples => example}/chat/package.json | 0 {examples => example}/chat/server.js | 0 {examples => example}/https/index.html | 0 {examples => example}/https/server.js | 0 {examples => example}/nested.js | 0 {examples => example}/perf/client.js | 0 {examples => example}/perf/emitter.js | 0 {examples => example}/pummel/client.js | 0 {examples => example}/pummel/server.js | 0 {examples => example}/saturate/index.html | 0 {examples => example}/saturate/saturate.js | 0 {examples => example}/simple/client.js | 0 {examples => example}/simple/server.js | 0 {examples => example}/stream/connect.js | 0 {examples => example}/stream/listen.js | 0 .../web-browserify/index.html | 0 .../web-browserify/server.js | 0 {examples => example}/web-connect/index.html | 0 {examples => example}/web-connect/server.js | 0 {examples => example}/web-express/index.html | 0 {examples => example}/web-express/server.js | 0 {examples => example}/web-http/index.html | 0 {examples => example}/web-http/server.js | 0 index.js | 6 ++++++ package.json | 4 +--- 35 files changed, 7 insertions(+), 24 deletions(-) delete mode 100755 bin/bundle.js rename {examples => example}/auth/client.js (100%) rename {examples => example}/auth/quotes.json (100%) rename {examples => example}/auth/server.js (100%) rename {examples => example}/bidirectional/client.js (100%) rename {examples => example}/bidirectional/server.js (100%) rename {examples => example}/chat/INSTALL.txt (100%) rename {examples => example}/chat/chat.css (100%) rename {examples => example}/chat/entry.js (100%) rename {examples => example}/chat/index.html (100%) rename {examples => example}/chat/package.json (100%) rename {examples => example}/chat/server.js (100%) rename {examples => example}/https/index.html (100%) rename {examples => example}/https/server.js (100%) rename {examples => example}/nested.js (100%) rename {examples => example}/perf/client.js (100%) rename {examples => example}/perf/emitter.js (100%) rename {examples => example}/pummel/client.js (100%) rename {examples => example}/pummel/server.js (100%) rename {examples => example}/saturate/index.html (100%) rename {examples => example}/saturate/saturate.js (100%) rename {examples => example}/simple/client.js (100%) rename {examples => example}/simple/server.js (100%) rename {examples => example}/stream/connect.js (100%) rename {examples => example}/stream/listen.js (100%) rename {examples => example}/web-browserify/index.html (100%) rename {examples => example}/web-browserify/server.js (100%) rename {examples => example}/web-connect/index.html (100%) rename {examples => example}/web-connect/server.js (100%) rename {examples => example}/web-express/index.html (100%) rename {examples => example}/web-express/server.js (100%) rename {examples => example}/web-http/index.html (100%) rename {examples => example}/web-http/server.js (100%) diff --git a/bin/bundle.js b/bin/bundle.js deleted file mode 100755 index dbd90f5..0000000 --- a/bin/bundle.js +++ /dev/null @@ -1,21 +0,0 @@ -var fs = require('fs'); -var path = require('path'); -var browserify = require('browserify'); - -var src = browserify({ filter : require('uglify-js') }) - .require(__dirname + '/../browser/index.js') - .bundle() -; - -var buf = new Buffer('var DNode = (function () {' - + src + '; return require("/index.js")' -+ '})()'); - -var file = __dirname + '/../browser/bundle.js'; -fs.writeFile(file, buf, function (err) { - if (err) console.error('Error: ' + err) - else { - var bytes = buf.length; - console.log(bytes + ' bytes written'); - } -}); diff --git a/examples/auth/client.js b/example/auth/client.js similarity index 100% rename from examples/auth/client.js rename to example/auth/client.js diff --git a/examples/auth/quotes.json b/example/auth/quotes.json similarity index 100% rename from examples/auth/quotes.json rename to example/auth/quotes.json diff --git a/examples/auth/server.js b/example/auth/server.js similarity index 100% rename from examples/auth/server.js rename to example/auth/server.js diff --git a/examples/bidirectional/client.js b/example/bidirectional/client.js similarity index 100% rename from examples/bidirectional/client.js rename to example/bidirectional/client.js diff --git a/examples/bidirectional/server.js b/example/bidirectional/server.js similarity index 100% rename from examples/bidirectional/server.js rename to example/bidirectional/server.js diff --git a/examples/chat/INSTALL.txt b/example/chat/INSTALL.txt similarity index 100% rename from examples/chat/INSTALL.txt rename to example/chat/INSTALL.txt diff --git a/examples/chat/chat.css b/example/chat/chat.css similarity index 100% rename from examples/chat/chat.css rename to example/chat/chat.css diff --git a/examples/chat/entry.js b/example/chat/entry.js similarity index 100% rename from examples/chat/entry.js rename to example/chat/entry.js diff --git a/examples/chat/index.html b/example/chat/index.html similarity index 100% rename from examples/chat/index.html rename to example/chat/index.html diff --git a/examples/chat/package.json b/example/chat/package.json similarity index 100% rename from examples/chat/package.json rename to example/chat/package.json diff --git a/examples/chat/server.js b/example/chat/server.js similarity index 100% rename from examples/chat/server.js rename to example/chat/server.js diff --git a/examples/https/index.html b/example/https/index.html similarity index 100% rename from examples/https/index.html rename to example/https/index.html diff --git a/examples/https/server.js b/example/https/server.js similarity index 100% rename from examples/https/server.js rename to example/https/server.js diff --git a/examples/nested.js b/example/nested.js similarity index 100% rename from examples/nested.js rename to example/nested.js diff --git a/examples/perf/client.js b/example/perf/client.js similarity index 100% rename from examples/perf/client.js rename to example/perf/client.js diff --git a/examples/perf/emitter.js b/example/perf/emitter.js similarity index 100% rename from examples/perf/emitter.js rename to example/perf/emitter.js diff --git a/examples/pummel/client.js b/example/pummel/client.js similarity index 100% rename from examples/pummel/client.js rename to example/pummel/client.js diff --git a/examples/pummel/server.js b/example/pummel/server.js similarity index 100% rename from examples/pummel/server.js rename to example/pummel/server.js diff --git a/examples/saturate/index.html b/example/saturate/index.html similarity index 100% rename from examples/saturate/index.html rename to example/saturate/index.html diff --git a/examples/saturate/saturate.js b/example/saturate/saturate.js similarity index 100% rename from examples/saturate/saturate.js rename to example/saturate/saturate.js diff --git a/examples/simple/client.js b/example/simple/client.js similarity index 100% rename from examples/simple/client.js rename to example/simple/client.js diff --git a/examples/simple/server.js b/example/simple/server.js similarity index 100% rename from examples/simple/server.js rename to example/simple/server.js diff --git a/examples/stream/connect.js b/example/stream/connect.js similarity index 100% rename from examples/stream/connect.js rename to example/stream/connect.js diff --git a/examples/stream/listen.js b/example/stream/listen.js similarity index 100% rename from examples/stream/listen.js rename to example/stream/listen.js diff --git a/examples/web-browserify/index.html b/example/web-browserify/index.html similarity index 100% rename from examples/web-browserify/index.html rename to example/web-browserify/index.html diff --git a/examples/web-browserify/server.js b/example/web-browserify/server.js similarity index 100% rename from examples/web-browserify/server.js rename to example/web-browserify/server.js diff --git a/examples/web-connect/index.html b/example/web-connect/index.html similarity index 100% rename from examples/web-connect/index.html rename to example/web-connect/index.html diff --git a/examples/web-connect/server.js b/example/web-connect/server.js similarity index 100% rename from examples/web-connect/server.js rename to example/web-connect/server.js diff --git a/examples/web-express/index.html b/example/web-express/index.html similarity index 100% rename from examples/web-express/index.html rename to example/web-express/index.html diff --git a/examples/web-express/server.js b/example/web-express/server.js similarity index 100% rename from examples/web-express/server.js rename to example/web-express/server.js diff --git a/examples/web-http/index.html b/example/web-http/index.html similarity index 100% rename from examples/web-http/index.html rename to example/web-http/index.html diff --git a/examples/web-http/server.js b/example/web-http/server.js similarity index 100% rename from examples/web-http/server.js rename to example/web-http/server.js diff --git a/index.js b/index.js index 7044284..34755e3 100644 --- a/index.js +++ b/index.js @@ -56,6 +56,7 @@ D.prototype.connect = function () { if (params.block) self.on('remote', params.block); stream.on('error', function (err) { + if (err && err.code === 'EPIPE') return; // eat EPIPEs self.emit('error', err); }); @@ -95,6 +96,11 @@ dnode.prototype.listen = function () { server.emit('remote', remote, d); }); + stream.on('error', function (err) { + if (err && err.code === 'EPIPE') return; // eat EPIPEs + d.emit('error', err); + }); + d.stream = stream; stream.pipe(d); d.pipe(stream); diff --git a/package.json b/package.json index 05bb8e6..5345d30 100644 --- a/package.json +++ b/package.json @@ -22,9 +22,7 @@ }, "devDependencies" : { "tap" : "~0.2.4", - "browserify" : "~1.10.7", - "uglify-js" : "~1.2.5", - "express" : "~2.5.9" + "browserify" : "~1.10.7" }, "scripts" : { "test" : "tap test/*.js" From 89399dc1c613bae095fa65367fc54c1e7338908f Mon Sep 17 00:00:00 2001 From: James Halliday Date: Wed, 20 Jun 2012 08:47:48 -0700 Subject: [PATCH 36/48] minimal browser version --- browser.js | 5 +++ browser/index.js | 69 ------------------------------ example/saturate/index.html | 83 ------------------------------------ example/saturate/saturate.js | 36 ---------------- package.json | 2 +- 5 files changed, 6 insertions(+), 189 deletions(-) create mode 100644 browser.js delete mode 100644 browser/index.js delete mode 100644 example/saturate/index.html delete mode 100755 example/saturate/saturate.js diff --git a/browser.js b/browser.js new file mode 100644 index 0000000..e0e5de7 --- /dev/null +++ b/browser.js @@ -0,0 +1,5 @@ +var dnode = require('./lib/dnode'); + +module.exports = function (cons, opts) { + return new dnode(cons, opts); +}; diff --git a/browser/index.js b/browser/index.js deleted file mode 100644 index 84cac3a..0000000 --- a/browser/index.js +++ /dev/null @@ -1,69 +0,0 @@ -var protocol = require('dnode-protocol'); -var EventEmitter = require('events').EventEmitter; -var io = require('socket.io-client'); -var json = typeof JSON === 'object' ? JSON : require('jsonify'); - -var exports = module.exports = dnode; - -function dnode (wrapper) { - if (!(this instanceof dnode)) return new dnode(wrapper); - this.proto = protocol(wrapper); - this.stack = []; - return this; -} - -dnode.prototype = new EventEmitter; - -dnode.prototype.use = function (middleware) { - this.stack.push(middleware); - return this; -}; - -dnode.prototype.connect = function () { - var self = this; - var params = protocol.parseArgs(arguments); - var client = self.proto.create(); - - var proto = (params.proto || window.location.protocol) - .replace(/:.*/, '') + '://'; - - var sock = client.socketio = io.connect( - proto + (params.host || window.location.host), - params - ); - - client.end = function () { - sock.disconnect(); - }; - - sock.on('connect', function () { - client.start(); - self.emit('connect'); - }); - - sock.on('disconnect', function () { - client.emit('end'); - self.emit('end'); - }); - - sock.on('message', client.parse); - - client.on('request', function (req) { - sock.send(json.stringify(req) + '\n'); - }); - - if (params.block) { - client.on('remote', function () { - params.block.call(client.instance, client.remote, client); - }); - } - - this.stack.forEach(function (middleware) { - middleware.call(client.instance, client.remote, client); - }); -}; - -exports.connect = function () { - var d = exports(); - return d.connect.apply(d, arguments); -}; diff --git a/example/saturate/index.html b/example/saturate/index.html deleted file mode 100644 index dc9be0c..0000000 --- a/example/saturate/index.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - -
- 0 - bytes - transferred -
- -
- ping X milliseconds -
- -
- 0 connections -
- - diff --git a/example/saturate/saturate.js b/example/saturate/saturate.js deleted file mode 100755 index 508d201..0000000 --- a/example/saturate/saturate.js +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env node - -var connect = require('connect'); -var server = connect.createServer( - connect.static(__dirname) -); - -var clients = {}; - -var Hash = require('traverse/hash'); -function publish () { - var args = [].slice.call(arguments); - Hash(clients).forEach( - function (emit) { emit.apply({}, args) } - ); -} - -var DNode = require('dnode'); -DNode(function (client, conn) { - conn.on('ready', function () { - clients[conn.id] = client.emit; - }); - - conn.on('end', function () { - delete clients[conn.id]; - }); -}).listen(server); - -console.log('http://localhost:6061/'); -server.listen(6061); - -setInterval(function () { - var n = Math.floor(Math.random() * 3e4); - var buf = new Buffer(n); - publish('data', buf.toString()); -}, 50); diff --git a/package.json b/package.json index 5345d30..382c859 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "scripts" : { "test" : "tap test/*.js" }, - "browserify" : "browser/index.js", + "browserify" : "browser.js", "engine" : { "node" : ">=0.6.0" }, From 3b759b39f073e37daaa2a5625109462d40ec2568 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Wed, 20 Jun 2012 21:33:57 -0700 Subject: [PATCH 37/48] sockjs example --- example/sockjs/Makefile | 2 ++ example/sockjs/client.js | 16 ++++++++++++++++ example/sockjs/package.json | 12 ++++++++++++ example/sockjs/server.js | 19 +++++++++++++++++++ example/sockjs/static/index.html | 2 ++ 5 files changed, 51 insertions(+) create mode 100644 example/sockjs/Makefile create mode 100644 example/sockjs/client.js create mode 100644 example/sockjs/package.json create mode 100644 example/sockjs/server.js create mode 100644 example/sockjs/static/index.html diff --git a/example/sockjs/Makefile b/example/sockjs/Makefile new file mode 100644 index 0000000..103950e --- /dev/null +++ b/example/sockjs/Makefile @@ -0,0 +1,2 @@ +all: + node_modules/.bin/browserify client.js -o static/bundle.js diff --git a/example/sockjs/client.js b/example/sockjs/client.js new file mode 100644 index 0000000..676259f --- /dev/null +++ b/example/sockjs/client.js @@ -0,0 +1,16 @@ +var domready = require('domready'); +var sockjs = require('sockjs'); +var dnode = require('../../'); + +domready(function () { + var result = document.getElementById('result'); + var stream = sockjs('/dnode'); + + var d = dnode(); + d.on('remote', function (remote) { + remote.transform('beep', function (s) { + result.textContent = 'beep => ' + s; + }); + }); + d.pipe(stream).pipe(d); +}); diff --git a/example/sockjs/package.json b/example/sockjs/package.json new file mode 100644 index 0000000..4d202a7 --- /dev/null +++ b/example/sockjs/package.json @@ -0,0 +1,12 @@ +{ + "name" : "dnode-sockjs-example", + "version" : "0.0.0", + "dependencies" : { + "sockjs" : "git://github.com/substack/sockjs-node.git#browserify-npm", + "sockjs-client" : "git://github.com/substack/sockjs-client.git#browserify-npm", + "ecstatic" : "~0.1.6", + "domready" : "~0.2.11", + "browserify" : "~1.12.3" + }, + "private" : true +} diff --git a/example/sockjs/server.js b/example/sockjs/server.js new file mode 100644 index 0000000..d40a09e --- /dev/null +++ b/example/sockjs/server.js @@ -0,0 +1,19 @@ +var http = require('http'); +var sockjs = require('sockjs'); +var ecstatic = require('ecstatic')(__dirname + '/static'); +var dnode = require('../../'); + +var server = http.createServer(ecstatic); +server.listen(9999); + +var sock = sockjs.createServer(); +sock.on('connection', function (stream) { + var d = dnode({ + transform : function (s, cb) { + var res = s.replace(/[aeiou]{2,}/, 'oo').toUpperCase(); + cb(res); + } + }); + d.pipe(stream).pipe(d); +}); +sock.installHandlers(server, { prefix : '/dnode' }); diff --git a/example/sockjs/static/index.html b/example/sockjs/static/index.html new file mode 100644 index 0000000..4ad0e26 --- /dev/null +++ b/example/sockjs/static/index.html @@ -0,0 +1,2 @@ + +
From 1379ed40de8e3deef9ca2dd25e6192dfd055e690 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Wed, 20 Jun 2012 21:38:00 -0700 Subject: [PATCH 38/48] delete terrible examples --- example/bidirectional/client.js | 21 ---------- example/bidirectional/server.js | 12 ------ example/https/index.html | 16 -------- example/https/server.js | 50 ----------------------- example/perf/client.js | 15 ------- example/perf/emitter.js | 20 --------- example/web-browserify/index.html | 19 --------- example/web-browserify/server.js | 21 ---------- example/web-connect/index.html | 17 -------- example/web-connect/server.js | 14 ------- example/web-express/index.html | 17 -------- example/web-express/server.js | 14 ------- example/web-http/index.html | 17 -------- example/web-http/server.js | 21 ---------- {example/pummel => perf/memory}/client.js | 0 {example/pummel => perf/memory}/server.js | 0 16 files changed, 274 deletions(-) delete mode 100644 example/bidirectional/client.js delete mode 100644 example/bidirectional/server.js delete mode 100644 example/https/index.html delete mode 100755 example/https/server.js delete mode 100644 example/perf/client.js delete mode 100644 example/perf/emitter.js delete mode 100644 example/web-browserify/index.html delete mode 100755 example/web-browserify/server.js delete mode 100644 example/web-connect/index.html delete mode 100755 example/web-connect/server.js delete mode 100644 example/web-express/index.html delete mode 100755 example/web-express/server.js delete mode 100644 example/web-http/index.html delete mode 100755 example/web-http/server.js rename {example/pummel => perf/memory}/client.js (100%) rename {example/pummel => perf/memory}/server.js (100%) diff --git a/example/bidirectional/client.js b/example/bidirectional/client.js deleted file mode 100644 index 5efe66e..0000000 --- a/example/bidirectional/client.js +++ /dev/null @@ -1,21 +0,0 @@ -var dnode = require('dnode'); - -var client = dnode({ - // Compute the client's temperature and stuff that value into the callback - - temperature : function (cb) { - var degC = Math.round(20 + Math.random() * 10 - 5); - console.log(degC + '° C'); - cb(degC); - } -}); - -client.connect(6060, function (remote, conn) { - // Call the server's conversion routine, which polls the client's - // temperature in celsius degrees and converts to fahrenheit - - remote.clientTempF(function (degF) { - console.log(degF + '° F'); - conn.end(); // all done! - }); -}); diff --git a/example/bidirectional/server.js b/example/bidirectional/server.js deleted file mode 100644 index 829c49f..0000000 --- a/example/bidirectional/server.js +++ /dev/null @@ -1,12 +0,0 @@ -var dnode = require('dnode'); - -dnode(function (client) { - // Poll the client's own temperature() in celsius and convert that value to - // fahrenheit in the supplied callback - this.clientTempF = function (cb) { - client.temperature(function (degC) { - var degF = Math.round(degC * 9 / 5 + 32); - cb(degF); - }); - }; -}).listen(6060); diff --git a/example/https/index.html b/example/https/index.html deleted file mode 100644 index 7912b90..0000000 --- a/example/https/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - -

timesTen(10) == ?

-

My name is ?.

diff --git a/example/https/server.js b/example/https/server.js deleted file mode 100755 index 055cf34..0000000 --- a/example/https/server.js +++ /dev/null @@ -1,50 +0,0 @@ -var https = require('https'); -var fs = require('fs'); -var dnode = require('dnode'); - -try { - var opts = { - key : fs.readFileSync(__dirname + '/privatekey.pem'), - cert : fs.readFileSync(__dirname + '/certificate.pem'), - }; -} -catch (e) { - console.log('# {privatekey,certificate}.pem missing, do this first:'); - console.log('openssl genrsa -out privatekey.pem 1024'); - console.log('openssl req -new -key privatekey.pem -out certrequest.csr'); - console.log('openssl x509 -req -in certrequest.csr ' - + '-signkey privatekey.pem -out certificate.pem'); - process.exit(); -} - -var index = fs.readFileSync(__dirname + '/index.html'); -var server = https.createServer(opts, function (req, res) { - if (req.url === '/') { - res.writeHead(200, { 'Content-Type' : 'text/html' }); - res.end(index); - } - else if (!res.finished) { - process.nextTick(function () { - if (!res.finished) { - res.writeHead(404, { 'Content-Type' : 'text/html' }); - res.end('Not found!'); - } - }); - } -}); - -dnode(function (client) { - this.timesTen = function (n,f) { f(n * 10) }; - this.whoAmI = function (reply) { - client.name(function (name) { - reply(name - .replace(/Mr\.?/,'Mister') - .replace(/Ms\.?/,'Miss') - .replace(/Mrs\.?/,'Misses') - ); - }) - }; -}).listen(server); -server.listen(8020); - -console.log('https://localhost:8020/'); diff --git a/example/perf/client.js b/example/perf/client.js deleted file mode 100644 index 9898119..0000000 --- a/example/perf/client.js +++ /dev/null @@ -1,15 +0,0 @@ -var frames = 0, last = 0; -setInterval(function () { - if (last) { - var fps = frames / (Date.now() - last) * 1000; - console.log('fps: ' + fps); - } - - last = Date.now(); - frames = 0; -}, 1000); - -var dnode = require('dnode'); -dnode({ - emit : function (i) { frames ++ } -}).connect(7575); diff --git a/example/perf/emitter.js b/example/perf/emitter.js deleted file mode 100644 index 4d716ab..0000000 --- a/example/perf/emitter.js +++ /dev/null @@ -1,20 +0,0 @@ -var dnode = require('dnode'); -var EventEmitter = require('events').EventEmitter; - -var clients = {}; -setInterval(function () { - Object.keys(clients).forEach(function (id) { - var i = Math.floor(Math.random() * Math.pow(2,32)); - clients[id].emit(i); - }); -}, 1000 / 50); // 50 fps - -dnode(function (client, conn) { - conn.on('ready', function () { - clients[conn.id] = client; - }); - - conn.on('end', function () { - delete clients[conn.id]; - }); -}).listen(7575); diff --git a/example/web-browserify/index.html b/example/web-browserify/index.html deleted file mode 100644 index b1d061a..0000000 --- a/example/web-browserify/index.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - The cat says ?. - - diff --git a/example/web-browserify/server.js b/example/web-browserify/server.js deleted file mode 100755 index 4bd568c..0000000 --- a/example/web-browserify/server.js +++ /dev/null @@ -1,21 +0,0 @@ -var connect = require('connect'); -var browserify = require('browserify'); -var dnode = require('dnode'); - -var server = connect.createServer(); - -server.use(connect.static(__dirname)); - -server.use(browserify({ - require : 'dnode', - mount : '/browserify.js' -})); - -dnode(function (client) { - this.cat = function (cb) { - cb('meow'); - }; -}).listen(server); - -server.listen(6857); -console.log('http://localhost:6857/'); diff --git a/example/web-connect/index.html b/example/web-connect/index.html deleted file mode 100644 index 095e186..0000000 --- a/example/web-connect/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - -The cat says ?. - - diff --git a/example/web-connect/server.js b/example/web-connect/server.js deleted file mode 100755 index 8ce6401..0000000 --- a/example/web-connect/server.js +++ /dev/null @@ -1,14 +0,0 @@ -var connect = require('connect'); -var dnode = require('dnode'); - -var server = connect.createServer(); -server.use(connect.static(__dirname)); - -dnode(function (client) { - this.cat = function (cb) { - cb('meow '); - }; -}).listen(server); - -server.listen(6857); -console.log('http://localhost:6857/'); diff --git a/example/web-express/index.html b/example/web-express/index.html deleted file mode 100644 index 095e186..0000000 --- a/example/web-express/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - -The cat says ?. - - diff --git a/example/web-express/server.js b/example/web-express/server.js deleted file mode 100755 index cb87c83..0000000 --- a/example/web-express/server.js +++ /dev/null @@ -1,14 +0,0 @@ -var express = require('express'); -var server = express.createServer(); -var dnode = require('dnode'); - -server.use(express.static(__dirname)); - -dnode(function (client) { - this.cat = function (cb) { - cb('meow'); - }; -}).listen(server); - -server.listen(6857); -console.log('http://localhost:6857/'); diff --git a/example/web-http/index.html b/example/web-http/index.html deleted file mode 100644 index 095e186..0000000 --- a/example/web-http/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - -The cat says ?. - - diff --git a/example/web-http/server.js b/example/web-http/server.js deleted file mode 100755 index 749989f..0000000 --- a/example/web-http/server.js +++ /dev/null @@ -1,21 +0,0 @@ -var http = require('http'); -var fs = require('fs'); -var dnode = require('dnode'); - -var index = fs.readFileSync(__dirname + '/index.html'); - -var server = http.createServer(function (req, res) { - if (req.url === '/') { - res.writeHead(200, { 'Content-Type' : 'text/html' }); - res.end(index); - } -}); - -dnode(function (client) { - this.cat = function (cb) { - cb('meow'); - }; -}).listen(server); - -server.listen(6857); -console.log('http://localhost:6857/'); diff --git a/example/pummel/client.js b/perf/memory/client.js similarity index 100% rename from example/pummel/client.js rename to perf/memory/client.js diff --git a/example/pummel/server.js b/perf/memory/server.js similarity index 100% rename from example/pummel/server.js rename to perf/memory/server.js From c201db26c1997b434e24be54b6afcd318276178a Mon Sep 17 00:00:00 2001 From: James Halliday Date: Wed, 20 Jun 2012 21:51:29 -0700 Subject: [PATCH 39/48] fixed old auth example --- example/auth/client.js | 34 +++++++++------------- example/auth/server.js | 65 ++++++++++++++++-------------------------- 2 files changed, 39 insertions(+), 60 deletions(-) mode change 100755 => 100644 example/auth/client.js mode change 100755 => 100644 example/auth/server.js diff --git a/example/auth/client.js b/example/auth/client.js old mode 100755 new mode 100644 index ff51fdd..5459a13 --- a/example/auth/client.js +++ b/example/auth/client.js @@ -1,30 +1,24 @@ -#!/usr/bin/env node -// Connect to the auth server and display a quote from it. - -var DNode = require('dnode'); -var sys = require('sys'); +var dnode = require('dnode'); if (process.argv.length < 4) { - sys.puts('Usage: ./client.js user pass'); - process.exit(); + return console.error('Usage: ./client.js user pass'); } var user = process.argv[2]; var pass = process.argv[3]; -DNode.connect(7007, function (remote,conn) { - remote.authenticate(user, pass, function (session) { - if (session) { - sys.puts('Authentication success'); - session.quote(function (q) { - sys.puts('\nAnd now for a quote by ' + q.who + ':\n\n'); - sys.puts(q.quote + '\n\n'); - conn.end(); - }); - } - else { - sys.puts('Authentication failure'); - conn.end(); +var d = dnode.connect(7007); +d.on('remote', function (remote) { + remote.auth(user, pass, function (err, session) { + if (err) { + console.error(err); + return d.end(); } + + session.quote(function (q) { + console.log('And now for a quote by ' + q.who + ':\n'); + console.log(q.quote + '\n'); + d.end(); + }); }); }); diff --git a/example/auth/server.js b/example/auth/server.js old mode 100755 new mode 100644 index a4ef38b..29b9891 --- a/example/auth/server.js +++ b/example/auth/server.js @@ -1,44 +1,29 @@ -#!/usr/bin/env node -// Serve an object with authentication - -var DNode = require('dnode'); -var sys = require('sys'); +var dnode = require('dnode'); var fs = require('fs'); +var net = require('net'); -var quotes = JSON.parse( - fs.readFileSync(__dirname + '/quotes.json').toString() -); - -// Serve up a Session object only after the client supplies the valid user/pass -DNode(function (client, connection) { - this.authenticate = function (user, pass, cb) { - if (user == 'moo' && pass == 'hax') { - sys.puts('Sign in as ' + sys.inspect(user) + ' succeeded!'); - cb(new Session({ - user : user, - client : client, - connection : connection, - })); - } - else { - sys.puts('Sign in as ' + sys.inspect(user) + ' failed!'); - cb(null); - } - }; -}).listen(7007); +var secretQuotes = require('./quotes.json'); +function randomQuote (cb) { + var ix = Math.floor(Math.random() * secretQuotes.length); + cb(secretQuotes[ix]); +} -// Clients who connect get an instance of this session object: -function Session (params) { - var conn = params.connection; - var user = params.user; - var client = params.client; - - conn.addListener('end', function () { - sys.puts('User ' + sys.inspect(user) + ' disconnected'); - }); +var server = net.createServer(function (stream) { + var d = dnode({ auth : auth }); + d.pipe(stream).pipe(d); - this.quote = function (f) { - var i = Math.floor(Math.random() * quotes.length); - f(quotes[i]); - }; -} + function auth (user, pass, cb) { + if (typeof cb !== 'function') return; + + if (user === 'moo' && pass === 'hax') { + console.log('signed in: ' + user); + d.on('end', function () { + console.log('disconnected: ' + user); + }); + + cb(null, { quote : randomQuote }); + } + else cb('ACCESS DENIED') + } +}); +server.listen(7007); From fb97aeea88ea40f9e230a783017b1a5fd2b9159e Mon Sep 17 00:00:00 2001 From: James Halliday Date: Wed, 20 Jun 2012 21:52:33 -0700 Subject: [PATCH 40/48] delete old chat example --- example/chat/INSTALL.txt | 4 --- example/chat/chat.css | 67 ----------------------------------- example/chat/entry.js | 73 --------------------------------------- example/chat/index.html | 23 ------------ example/chat/package.json | 7 ---- example/chat/server.js | 50 --------------------------- 6 files changed, 224 deletions(-) delete mode 100644 example/chat/INSTALL.txt delete mode 100644 example/chat/chat.css delete mode 100644 example/chat/entry.js delete mode 100644 example/chat/index.html delete mode 100644 example/chat/package.json delete mode 100755 example/chat/server.js diff --git a/example/chat/INSTALL.txt b/example/chat/INSTALL.txt deleted file mode 100644 index 159ee7d..0000000 --- a/example/chat/INSTALL.txt +++ /dev/null @@ -1,4 +0,0 @@ -Do this first: - - mkdir node_modules - npm install jquery-browserify diff --git a/example/chat/chat.css b/example/chat/chat.css deleted file mode 100644 index c8e7bda..0000000 --- a/example/chat/chat.css +++ /dev/null @@ -1,67 +0,0 @@ -div#prompt { - width: 600px; - height: 400px; - margin: auto; - display: table-cell; - vertical-align: middle; - text-align: center; - background-color: rgb(30,30,30); - color: white; -} - -div#chat { - width: 600px; - margin: auto; -} - -div#messages { - display: none; - overflow-x: hidden; - overflow-y: scroll; - padding: 0.5em; - width: 600px; - height: 400px; - border-color: black; - border-style: solid; - border-width: 2px; - background-color: rgb(30,30,30); -} - -form#post input { - width: 600px; - padding: 0.5em; - background-color: black; - color: white; - border-width: 0px; -} - -span.who { - font-weight: bold; - margin-right: 1em; - color: rgb(200,200,200); -} - -span.me { - font-weight: bold; - margin-right: 1em; - color: white; -} - -span.msg { - color: white; -} - -div.join { - color: rgb(200,250,150); - font-weight: bold; -} - -div.part { - color: rgb(250,200,150); - font-weight: bold; -} - -div.users { - color: rgb(150,200,250); - font-weight: bold; -} diff --git a/example/chat/entry.js b/example/chat/entry.js deleted file mode 100644 index a9c1e07..0000000 --- a/example/chat/entry.js +++ /dev/null @@ -1,73 +0,0 @@ -var $ = require('jquery-browserify'); -var dnode = require('dnode'); - -$(document).ready(function () { - // Fetch the user's name before the main chat code fires - $('form#name').submit(function (ev) { - ev.preventDefault(); - var name = $('input[name="name"]').val(); - $('#messages').show(); - $('#prompt').hide(); - chat(name); - }); -}); - -function chat (name) { - // Connect to the chat server now that we've got a name - dnode(function () { - this.name = name; - - this.joined = function (who) { - addLine($('
') - .addClass('join') - .text(who + ' has joined') - ); - }; - - this.parted = function (who) { - addLine($('
') - .addClass('part') - .text(who + ' has left') - ); - }; - - this.said = function (who,msg) { - addLine($('
').append( - $('') - .addClass(who == name ? 'me' : 'who') - .text('<' + who + '>') - , - $('').addClass('msg').text(msg) - )); - }; - }).connect(function (remote) { - $('form#post').submit(function (ev) { - ev.preventDefault(); - remote.say(this.elements.msg.value); - this.elements.msg.value = ''; - }); - - // fetch a list of all the connected users - remote.names(function (names) { - addLine($('
') - .addClass('users') - .text('Users: ' + ( - names.map(function (name) { - return '[ ' + name - .replace(/\\/g,'\\\\') - .replace(/\[/g,'\\[') - .replace(/\]/g,'\\]') - + ' ]' - }).join(' ') - || '(no users)' - )) - ); - }); - }); -} - -function addLine(elem) { - var div = $('#messages'); - div.append(elem); - div.animate({ scrollTop: div.attr('scrollHeight') }, 200); -} diff --git a/example/chat/index.html b/example/chat/index.html deleted file mode 100644 index 01dea65..0000000 --- a/example/chat/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - -dnode chat example - - - - -
-
-
- Name: - -
-
- -
- -
- -
-
- - diff --git a/example/chat/package.json b/example/chat/package.json deleted file mode 100644 index 00c483a..0000000 --- a/example/chat/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "dependencies" : { - "jquery-browserify" : "*", - "browserify" : "1.0.x", - "express" : "0.7.x" - } -} diff --git a/example/chat/server.js b/example/chat/server.js deleted file mode 100755 index 127c46d..0000000 --- a/example/chat/server.js +++ /dev/null @@ -1,50 +0,0 @@ -// simple dnode chat server with browserify - -var express = require('express'); -var app = express.createServer(); -app.use(express.static(__dirname)); - -var browserify = require('browserify'); -app.use(browserify(__dirname + '/entry.js', { watch : true })); - -app.listen(6061); -console.log('http://localhost:6061/'); - -var EventEmitter = require('events').EventEmitter; -var emitter = new EventEmitter; - -var clients = {}; - -var dnode = require('../../'); -dnode(ChatServer).listen(app); - -function ChatServer (client, con) { - var evNames = [ 'joined', 'said', 'parted' ]; - - con.on('ready', function () { - evNames.forEach(function (name) { - emitter.on(name, client[name]); - }); - emitter.emit('joined', client.name); - - clients[client.name] = client; - }); - - con.on('end', function () { - evNames.forEach(function (name) { - if (typeof client[name] === 'function') { - emitter.removeListener(name, client[name]); - } - }); - emitter.emit('parted', client.name); - delete clients[client.name]; - }); - - this.say = function (msg) { - emitter.emit('said', client.name, msg); - }; - - this.names = function (cb) { - cb(Object.keys(clients)) - }; -} From 619385624a4c36e9e61ce3d094788609818ee681 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Wed, 20 Jun 2012 21:52:48 -0700 Subject: [PATCH 41/48] delete terrible nested example --- example/nested.js | 61 ----------------------------------------------- 1 file changed, 61 deletions(-) delete mode 100644 example/nested.js diff --git a/example/nested.js b/example/nested.js deleted file mode 100644 index 22f5b2e..0000000 --- a/example/nested.js +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env node -var DNode = require('dnode'); -var sys = require('sys'); -var EventEmitter = require('events').EventEmitter; - -// server-side: -var server1 = DNode({ - timesTen : function (n,reply) { reply(n * 10) } -}).listen(6060); - -var server2 = DNode({ - timesTwenty : function (n,reply) { reply(n * 20) } -}).listen(6061); - -var moo = new EventEmitter; - -// client-side: -// Good thing this example is so contrived. Real code doesn't turn out this -// ugly very often on purpose. -server1.on('ready', function () { - server2.on('ready', function () { - DNode.connect(6060, function (remote1) { - DNode.connect(6061, function (remote2) { - moo.addListener('hi', function (x) { - remote1.timesTen(x, function (res) { - sys.puts(res); - remote2.timesTwenty(res, function (res2) { - sys.puts(res2); - }); - }); - }); - remote2.timesTwenty(5, function (n) { - sys.puts(n); // 100 - remote1.timesTen(0.1, function (n) { - sys.puts(n); // 1 - }); - }); - }); - }); - }); -}); - -setTimeout(function() { - moo.emit('hi', 100); -}, 500); - -setTimeout(function () { - // kill the service after a while - server1.end(); - server2.end(); -}, 1000); - -/* - Output: - 100 - 1 - ... after half a second ... - 1000 - 20000 -*/ - From c32386b9a6913275a60f81b24654ad3794522ad4 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Wed, 20 Jun 2012 22:36:55 -0700 Subject: [PATCH 42/48] rewrite the readme --- README.markdown | 410 ++++++++++++++++++++++++------------------------ lib/dnode.js | 1 - 2 files changed, 203 insertions(+), 208 deletions(-) diff --git a/README.markdown b/README.markdown index b853b00..2b56aa8 100644 --- a/README.markdown +++ b/README.markdown @@ -3,293 +3,296 @@ dnode ![dnode: freestyle rpc](http://substack.net/images/dnode.png) -DNode is an asynchronous object-oriented RPC system for node.js that lets you +dnode is an asynchronous rpc system for node.js that lets you call remote functions. -[![build status](https://secure.travis-ci.org/substack/dnode.png)](http://travis-ci.org/substack/dnode) +You can pass callbacks to remote functions, and the remote end can call +the functions you passed in with callbacks of its own and so on. +It's callbacks all the way down! -It works over network sockets and even in the browser with -[socket.io](https://github.com/LearnBoost/Socket.IO). +[![build status](https://secure.travis-ci.org/substack/dnode.png)](http://travis-ci.org/substack/dnode) -Plus, there are dnode implementations for -[perl](http://github.com/substack/dnode-perl), -[ruby](http://github.com/substack/dnode-ruby), -[php](https://github.com/bergie/dnode-php), -and -[java](https://github.com/aslakhellesoy/dnode-java), -so you can glue -together all your backend processes. +example +======= -dnode between two node.js processes ------------------------------------ +listen and connect +------------------ -Just write a server.js: +server: -```javascript +``` js var dnode = require('dnode'); - var server = dnode({ - zing : function (n, cb) { cb(n * 100) } + transform : function (s, cb) { + cb(s.replace(/[aeiou]{2,}/, 'oo').toUpperCase()) + } }); -server.listen(5050); -```` - -Run it... +server.listen(5004); +``` - $ node server.js +client: -Then you can whip up a client.js that calls the server's `zing` function! - -```javascript +``` js var dnode = require('dnode'); -dnode.connect(5050, function (remote) { - remote.zing(66, function (n) { - console.log('n = ' + n); +var d = dnode.connect(5004); +d.on('remote', function (remote) { + remote.transform('beep', function (s) { + console.log('beep => ' + s); + d.end(); }); }); -```` - -*** - - $ node client.js - n = 6600 - ^C +``` -dnode on the browser --------------------- +output: -We can retrofit the previous example to run in the browser. +``` +$ node server.js & +[1] 27574 +$ node client.js +beep => BOOP +``` -Just write a server.js: +streaming +--------- -````javascript -var express = require('express'); -var app = express.createServer(); -app.use(express.static(__dirname)); +The `.connect()` and `.listen()` calls in the previous example are just +convenience methods for piping to and from readable/writable streams. +Here's the previous example with the streams set up explicitly: -app.listen(8080); -console.log('http://localhost:8080/'); - -// then just pass the server app handle to .listen()! +server: +``` js var dnode = require('dnode'); -var server = dnode({ - zing : function (n, cb) { cb(n * 100) } +var net = require('net'); + +var server = net.createServer(function (c) { + var d = dnode({ + transform : function (s, cb) { + cb(s.replace(/[aeiou]{2,}/, 'oo').toUpperCase()) + } + }); + c.pipe(d).pipe(c); }); -server.listen(app); -```` - -and whip up an index.html: - -````html - - - - - - -n = ? +server.listen(5004); +``` - - -```` +client: -then just run the server.js: +``` js +var dnode = require('dnode'); +var net = require('net'); - $ node server.js - http://localhost:8080/ +var d = dnode(); +d.on('remote', function (remote) { + remote.transform('beep', function (s) { + console.log('beep => ' + s); + d.end(); + }); +}); -and navigate to http://localhost:8080: +var c = net.connect(5004); +c.pipe(d).pipe(c); +``` -![dnode in the browser](http://substack.net/images/dnode-slides/browser.png) +output: -Awesome it works! +``` +$ node server.js & +[1] 27586 +$ node client.js +beep => BOOP +``` -The dnode browser source automatically gets hosted at `/dnode.js` and it also -works with -[browserify](https://github.com/substack/node-browserify) -[out of the box](https://github.com/substack/dnode/tree/master/examples/web-browserify). +using socksjs in the browser +---------------------------- -how it works ------------- +Since dnode instances are just readable/writable streams, you can use them with +any streaming transport, including in the browser! -When you throw an object at dnode, a recursive traversal scrubs out all of the -`function` objects nested in your data structure and a secondary data structure -is sent along with remote requests that creates shim functions that create RPC -calls back to the side where the functions were originally defined. +This example uses the streaming interface provided by +[socksjs](http://sockjs.org/). -When you call a remote function, the same recursive traversal trick happens to -the arguments you pass along, so you can pass callbacks to your remote functions -that actually call you back over the wire when the remote side calls the shim -function on the other end. +NOTE: This socksjs interface is presently pending: +[sockjs-client/pull/76](https://github.com/sockjs/sockjs-client/pull/76) +[sockjs-node/pull/77](https://github.com/sockjs/sockjs-node/pull/77) -Basically, dnode lets you call remote functions as if they were defined locally -without using `eval()` or `Function.prototype.toString()`. Awesome! +Check example/sockjs/ for the package.json entries needed so you can use right +away. -The only catch is that because the function calls are traveling down the -high-latency network, the return values of your functions are ignored. Use -[continuation-passing -style](http://en.wikipedia.org/wiki/Continuation-passing_style) instead! +First whip up a server: -More features: +``` js +var http = require('http'); +var sockjs = require('sockjs'); +var ecstatic = require('ecstatic')(__dirname + '/static'); +var dnode = require('dnode'); -* symmetric design: both sides of the connection can host up methods for the - other side to call +var server = http.createServer(ecstatic); +server.listen(9999); -* use TCP streams, UNIX domain sockets, or websockets courtesy of socket.io! - (see below, just throw a webserver at `listen()`) +var sock = sockjs.createServer(); +sock.on('connection', function (stream) { + var d = dnode({ + transform : function (s, cb) { + var res = s.replace(/[aeiou]{2,}/, 'oo').toUpperCase(); + cb(res); + } + }); + d.pipe(stream).pipe(d); +}); +sock.installHandlers(server, { prefix : '/dnode' }); +``` -methods -======= +Then write som browser code: -dnode(wrapper) --------------- +``` js +var domready = require('domready'); +var sockjs = require('sockjs'); +var dnode = require('dnode'); -If `wrapper` is an object, serve this object up to the other side every time. +domready(function () { + var result = document.getElementById('result'); + var stream = sockjs('/dnode'); + + var d = dnode(); + d.on('remote', function (remote) { + remote.transform('beep', function (s) { + result.textContent = 'beep => ' + s; + }); + }); + d.pipe(stream).pipe(d); +}); +``` -If `wrapper` is a function, use it to build a new object for each new client. -The result of `new wrapper(remote, conn)` will be used, where `remote` is an -empty object that will be filled with the other side's methods once the initial -protocol phase finishes and where `conn` is the connection object. +Compile the browser code with +[browserify](https://github.com/substack/node-browserify): -Both client and server can call `dnode()` with a wrapper. -`dnode.connect()` and `dnode.listen()` are shortcut that set `wrapper` to `{}`. +``` +$ browserify client.js -o static/bundle.js +``` -.connect(...) -------------- +Then drop the bundle into some html with a "result" id: -Connect to a remote dnode service. Pass in a port, host, UNIX domain socket -path, block, or options object in any order. The block function if present will -be executed with the remote object and the connection object once the remote -object is ready. +``` html + +
+``` -You can reconnect when the connection is refused or drops by passing in a -`reconnect` option as the number of milliseconds to wait between reconnection -attempts. +and you should see `beep => BOOP` on the page! -Returns `this` so you can chain multiple connections. +Check out the +[complete sockjs example](https://github.com/substack/dnode/tree/master/example/sockjs). -.listen(...) ------------- +methods +======= -Listen for incoming dnode clients. Pass in a port, host, UNIX domain socket -path, block, or options object in any order. The block function if present will -be executed with the remote object and the connection object once the remote -object is ready for each client. +``` js +var dnode = require('dnode') +``` -If you pass a webserver (http.Server, https.Server, connect, express) to -listen(), socket.io will be bound to the webserver and the dnode browser source -will be hosted at `options.mount || "/dnode.js"`. +var d = dnode(cons, opts={}) +---------------------------- -You can pass options through to socket.io with the `io` parameter: +Create a new readable/writable dnode stream object `d`. +All the usual stream methods are at your disposal: pipe(), write(), end(). -````javascript -dnode(...).listen(webserver, { io : { flashPolicyServer : false } }); -```` +If `cons` is a function, it will be called `new cons(remote, d)` to create a new +instance object. Otherwise its value will be used directly. When `cons` is +called as a function, the `remote` ref will be an empty unpopulated object. -Returns `this` so you can chain multiple listeners. +By default, dnode uses weakmaps to garbage collect unused callbacks +automatically. This behavior prevents memory leaks in long-running connections. -.use(middleware) ----------------- +You can turn weakmaps off by setting `opts.weak = false`. -You can write your own dnode middleware with `.use()`. The `middleware` function -you pass will be called just like the constructor function that `dnode()` takes. -You can modify `this`, `remote`, and `conn` objects after the instance computed -with the `dnode()` constructor executes but before the methods are sent over the -wire. +d.connect(...) +-------------- -Returns `this` so you can chain middlewares. +This method is a shortcut for setting up a pipe between `d` and a new +`net.connect()` stream. -the connection object -===================== +The host, port, and callback arguments supplied will be inferred by their +types. -When you pass a constructor function to `dnode()` you'll get a connection -object as the second argument to your constructor. +If you pass a callback in as an argument, it will be added as a listener to the +`'remote'` event. -The connection object (`conn`) is an EventEmitter. +Returns the `d` object. -* conn.id is a random hex string that uniquely identifies clients +dnode.connect(...) +------------------ -* conn.end() closes the connection and won't reconnect +Shortcut to create a connection without a constructor. -* conn emits 'ready' when the remote object has been fully populated from - the methods exchange +d.listen(...) +------------- -* conn emits 'remote' at the same time as 'ready', except with the remote object - as an argument +This method is a shortcut for setting up a `net.createServer()` and piping +network streams to and from new dnode streams. -* conn emits 'end' when the connection drops +The host, port, and callback parameters will be inferred from the types of the +arguments. -* conn emits 'connect' when the connection is established +Returns a net server object that will also emit `'local'` and `'remote'` events +from the underlying dnode streams.. -* conn re-emits error events from the stream object +dnode.listen(...) +----------------- -* conn emits 'refused', 'drop', and 'reconnect' when reconnect is enabled +Shortcut to create a listener without a constructor. -more examples -------------- +events +====== -Check out -[the examples directory](https://github.com/substack/dnode/tree/master/examples/) -of this distribution. +d.on('remote', cb) +------------------ -You'll find examples for using dnode with -[connect](https://github.com/SenchaLabs/connect), -[express](http://expressjs.com/), -[https](https://github.com/substack/dnode/tree/master/examples/https), -and authentication. +This event fires with `cb(remote, d)` when the remote side of the connection +has constructed its instance. -There's a chat server too! +d.on('local', cb) +----------------- -installation -============ +This event fires right after the constructed instance has been created locally +but before it gets sent to the remote end so you can modify the ref object. -Using [npm](http://npmjs.org): +This event fires with `cb(ref, d)` where `ref` is the local instance object. + +d.on('fail', cb) +---------------- - npm install dnode +This event fires when the remote end causes errors in the protocol layer. -Or check out the repository and fetch the deps with npm, then build the bundle: +These are non-fatal and can probably be ignored but you could also terminate the +connection here. - git clone https://github.com/substack/dnode.git - cd dnode - npm install --dev - node bin/bundle.js +d.on('error', cb) +----------------- -The dnode dependencies are listed in the -[package.json](https://github.com/substack/dnode/tree/master/package.json). -If you install with npm they will be fetched automatically. +This event fires when local code causes errors in its callbacks. +Not all errors can be caught here since some might be in async functions. -read more -========= +d.on('end', cb) +--------------- -* [slides from my dnode talk at parisoma](http://substack.net/posts/9aabb1) +This event fires when the input stream finishes. -* [Roll your own PubSub with DNode](http://substack.net/posts/9bac3e/Roll-your-own-PubSub-with-DNode) - (Note: EventEmitters are no longer exported directly, use - [browserify](https://github.com/substack/node-browserify) to get them back) +install +======= -* [DNode: Asynchronous Remote Method Invocation for Node.js and the Browser](http://substack.net/posts/85e1bd/DNode-Asynchronous-Remote-Method-Invocation-for-Node-js-and-the-Browser) +With [npm](http://npmjs.org) do: -* [Simon Willison's Weblog](http://simonwillison.net/2010/Jul/11/dnode/) +``` +npm install dnode +``` protocol ======== -DNode uses a newline-terminated JSON protocol -[documented in the dnode-protocol -readme](https://github.com/substack/dnode-protocol). +dnode uses a newline-terminated JSON protocol +[documented in the dnode-protocol project](https://github.com/substack/dnode-protocol/blob/master/doc/protocol.markdown#the-protocol). dnode in other languages ======================== @@ -300,13 +303,9 @@ between scripts written in different languages. * [dnode-perl](http://github.com/substack/dnode-perl) * [dnode-ruby](http://github.com/substack/dnode-ruby) * [dnode-php](https://github.com/bergie/dnode-php) -* [dnode-php-sync-client](https://github.com/erasys/dnode-php-sync-client) - minimalistic synchronous client for PHP +* [dnode-php-sync-client](https://github.com/erasys/dnode-php-sync-client) * [dnode-java](https://github.com/aslakhellesoy/dnode-java) -There's a -[dnode-python](https://github.com/jesusabdullah/dnode-python) -in the works too but it's not finished yet. - shameless plug ============== @@ -314,6 +313,3 @@ Want to make sure your crazy javascript-heavy app still works in other browsers? Give [browserling](http://browserling.com) a spin! Browsers in your browser. Powered by dnode. - -[![build status](https://secure.travis-ci.org/substack/dnode.png)](http://travis-ci.org/substack/dnode) - diff --git a/lib/dnode.js b/lib/dnode.js index 3287220..c4628ec 100644 --- a/lib/dnode.js +++ b/lib/dnode.js @@ -61,7 +61,6 @@ dnode.prototype._createProto = function () { proto.on('fail', function (err) { // errors that the remote end was responsible for self.emit('fail', err); - self.end(); }); proto.on('error', function (err) { From af0cdc2f182c08339ac18f23492671632b249c2f Mon Sep 17 00:00:00 2001 From: James Halliday Date: Wed, 20 Jun 2012 22:40:06 -0700 Subject: [PATCH 43/48] fix typo --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 2b56aa8..914a9c7 100644 --- a/README.markdown +++ b/README.markdown @@ -145,7 +145,7 @@ sock.on('connection', function (stream) { sock.installHandlers(server, { prefix : '/dnode' }); ``` -Then write som browser code: +Then write some browser code: ``` js var domready = require('domready'); From bcfc3a565aa3af226769f39113f11454dfe60786 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Thu, 21 Jun 2012 03:09:55 -0700 Subject: [PATCH 44/48] s/socksjs/sockjs/g --- README.markdown | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.markdown b/README.markdown index 914a9c7..7281e5d 100644 --- a/README.markdown +++ b/README.markdown @@ -105,16 +105,16 @@ $ node client.js beep => BOOP ``` -using socksjs in the browser +using sockjs in the browser ---------------------------- Since dnode instances are just readable/writable streams, you can use them with any streaming transport, including in the browser! This example uses the streaming interface provided by -[socksjs](http://sockjs.org/). +[sockjs](http://sockjs.org/). -NOTE: This socksjs interface is presently pending: +NOTE: This sockjs interface is presently pending: [sockjs-client/pull/76](https://github.com/sockjs/sockjs-client/pull/76) [sockjs-node/pull/77](https://github.com/sockjs/sockjs-node/pull/77) From ac37ff0a427ac5317456d0f93caddccfb4b00d8d Mon Sep 17 00:00:00 2001 From: James Halliday Date: Thu, 21 Jun 2012 03:12:09 -0700 Subject: [PATCH 45/48] update the note about package.json entries --- README.markdown | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index 7281e5d..c05511e 100644 --- a/README.markdown +++ b/README.markdown @@ -118,8 +118,9 @@ NOTE: This sockjs interface is presently pending: [sockjs-client/pull/76](https://github.com/sockjs/sockjs-client/pull/76) [sockjs-node/pull/77](https://github.com/sockjs/sockjs-node/pull/77) -Check example/sockjs/ for the package.json entries needed so you can use right -away. +For now, put +[these entries](https://github.com/substack/dnode/blob/master/example/sockjs/package.json#L5-L6) +into your package.json. First whip up a server: From bd7d63ddc982309b9cc3ca7c5f70fcf9440e71cb Mon Sep 17 00:00:00 2001 From: James Halliday Date: Sat, 23 Jun 2012 03:16:41 -0700 Subject: [PATCH 46/48] shoes instead of socks --- README.markdown | 37 ++++++++++------------ example/{sockjs => shoe}/Makefile | 0 example/{sockjs => shoe}/client.js | 4 +-- example/shoe/package.json | 11 +++++++ example/{sockjs => shoe}/server.js | 7 ++-- example/{sockjs => shoe}/static/index.html | 0 example/sockjs/package.json | 12 ------- 7 files changed, 32 insertions(+), 39 deletions(-) rename example/{sockjs => shoe}/Makefile (100%) rename example/{sockjs => shoe}/client.js (84%) create mode 100644 example/shoe/package.json rename example/{sockjs => shoe}/server.js (70%) rename example/{sockjs => shoe}/static/index.html (100%) delete mode 100644 example/sockjs/package.json diff --git a/README.markdown b/README.markdown index c05511e..174bd97 100644 --- a/README.markdown +++ b/README.markdown @@ -105,36 +105,28 @@ $ node client.js beep => BOOP ``` -using sockjs in the browser ----------------------------- +dnode in the browser +-------------------- Since dnode instances are just readable/writable streams, you can use them with any streaming transport, including in the browser! This example uses the streaming interface provided by -[sockjs](http://sockjs.org/). - -NOTE: This sockjs interface is presently pending: -[sockjs-client/pull/76](https://github.com/sockjs/sockjs-client/pull/76) -[sockjs-node/pull/77](https://github.com/sockjs/sockjs-node/pull/77) - -For now, put -[these entries](https://github.com/substack/dnode/blob/master/example/sockjs/package.json#L5-L6) -into your package.json. +[shoe](https://github.com/substack/shoe), which is just a thin wrapper on top of +[sockjs](http://sockjs.org/) that provides websockets with fallbacks. First whip up a server: ``` js var http = require('http'); -var sockjs = require('sockjs'); +var shoe = require('shoe'); var ecstatic = require('ecstatic')(__dirname + '/static'); var dnode = require('dnode'); var server = http.createServer(ecstatic); server.listen(9999); -var sock = sockjs.createServer(); -sock.on('connection', function (stream) { +var sock = shoe(function (stream) { var d = dnode({ transform : function (s, cb) { var res = s.replace(/[aeiou]{2,}/, 'oo').toUpperCase(); @@ -143,19 +135,19 @@ sock.on('connection', function (stream) { }); d.pipe(stream).pipe(d); }); -sock.installHandlers(server, { prefix : '/dnode' }); +sock.install(server, '/dnode'); ``` Then write some browser code: ``` js var domready = require('domready'); -var sockjs = require('sockjs'); +var shoe = require('shoe'); var dnode = require('dnode'); domready(function () { var result = document.getElementById('result'); - var stream = sockjs('/dnode'); + var stream = shoe('/dnode'); var d = dnode(); d.on('remote', function (remote) { @@ -167,24 +159,27 @@ domready(function () { }); ``` -Compile the browser code with +Install the dependencies for this example then compile the browser code with [browserify](https://github.com/substack/node-browserify): ``` +$ npm install dnode shoe domready ecstatic +$ npm install -g browserify $ browserify client.js -o static/bundle.js ``` -Then drop the bundle into some html with a "result" id: +Now drop a script tag into static/index.html: ``` html
``` -and you should see `beep => BOOP` on the page! +and navigate to http://localhost:9999. +You should see `beep => BOOP` on the page! Check out the -[complete sockjs example](https://github.com/substack/dnode/tree/master/example/sockjs). +[complete shoe example](https://github.com/substack/dnode/tree/master/example/shoe). methods ======= diff --git a/example/sockjs/Makefile b/example/shoe/Makefile similarity index 100% rename from example/sockjs/Makefile rename to example/shoe/Makefile diff --git a/example/sockjs/client.js b/example/shoe/client.js similarity index 84% rename from example/sockjs/client.js rename to example/shoe/client.js index 676259f..46d8296 100644 --- a/example/sockjs/client.js +++ b/example/shoe/client.js @@ -1,10 +1,10 @@ var domready = require('domready'); -var sockjs = require('sockjs'); +var shoe = require('shoe'); var dnode = require('../../'); domready(function () { var result = document.getElementById('result'); - var stream = sockjs('/dnode'); + var stream = shoe('/dnode'); var d = dnode(); d.on('remote', function (remote) { diff --git a/example/shoe/package.json b/example/shoe/package.json new file mode 100644 index 0000000..d7c44f2 --- /dev/null +++ b/example/shoe/package.json @@ -0,0 +1,11 @@ +{ + "name" : "dnode-shoe-example", + "version" : "0.0.0", + "dependencies" : { + "shoe" : "~0.0.0", + "ecstatic" : "~0.1.6", + "domready" : "~0.2.11", + "browserify" : "~1.12.3" + }, + "private" : true +} diff --git a/example/sockjs/server.js b/example/shoe/server.js similarity index 70% rename from example/sockjs/server.js rename to example/shoe/server.js index d40a09e..b415cfa 100644 --- a/example/sockjs/server.js +++ b/example/shoe/server.js @@ -1,13 +1,12 @@ var http = require('http'); -var sockjs = require('sockjs'); +var shoe = require('shoe'); var ecstatic = require('ecstatic')(__dirname + '/static'); var dnode = require('../../'); var server = http.createServer(ecstatic); server.listen(9999); -var sock = sockjs.createServer(); -sock.on('connection', function (stream) { +var sock = shoe(function (stream) { var d = dnode({ transform : function (s, cb) { var res = s.replace(/[aeiou]{2,}/, 'oo').toUpperCase(); @@ -16,4 +15,4 @@ sock.on('connection', function (stream) { }); d.pipe(stream).pipe(d); }); -sock.installHandlers(server, { prefix : '/dnode' }); +sock.install(server, '/dnode'); diff --git a/example/sockjs/static/index.html b/example/shoe/static/index.html similarity index 100% rename from example/sockjs/static/index.html rename to example/shoe/static/index.html diff --git a/example/sockjs/package.json b/example/sockjs/package.json deleted file mode 100644 index 4d202a7..0000000 --- a/example/sockjs/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name" : "dnode-sockjs-example", - "version" : "0.0.0", - "dependencies" : { - "sockjs" : "git://github.com/substack/sockjs-node.git#browserify-npm", - "sockjs-client" : "git://github.com/substack/sockjs-client.git#browserify-npm", - "ecstatic" : "~0.1.6", - "domready" : "~0.2.11", - "browserify" : "~1.12.3" - }, - "private" : true -} From 9e9efffb5e3bed9b0006242ace8214b68b44d531 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Sat, 23 Jun 2012 03:17:26 -0700 Subject: [PATCH 47/48] bump to the latest browserify for the tests --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 382c859..ec90be3 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,8 @@ "weak" : "~0.2.1" }, "devDependencies" : { - "tap" : "~0.2.4", - "browserify" : "~1.10.7" + "tap" : "~0.2.5", + "browserify" : "~1.13.0" }, "scripts" : { "test" : "tap test/*.js" From 3aed87300c9fe3035b4bc81c52501477ac1783f1 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Thu, 19 Jul 2012 04:00:46 -0700 Subject: [PATCH 48/48] fix undefined proto errors by buffering proto.handle calls --- lib/dnode.js | 18 +++++++++++++++--- package.json | 2 +- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/dnode.js b/lib/dnode.js index c4628ec..55713c9 100644 --- a/lib/dnode.js +++ b/lib/dnode.js @@ -28,6 +28,10 @@ function dnode (cons, opts) { if (self._ended) return; self.proto = self._createProto(); self.proto.start(); + + (self._handleQueue || []).forEach(function (row) { + self.handle(row); + }); }); } @@ -98,7 +102,7 @@ dnode.prototype.write = function (buf) { j = i + 1; - self.proto.handle(row); + self.handle(row); self._bufs = []; } } @@ -108,7 +112,7 @@ dnode.prototype.write = function (buf) { else if (buf && typeof buf === 'object') { // .isBuffer() without the Buffer // Use self to pipe JSONStream.parse() streams. - self.proto.handle(buf); + self.handle(buf); } else { if (typeof buf !== 'string') buf = String(buf); @@ -119,7 +123,7 @@ dnode.prototype.write = function (buf) { try { row = json.parse(self._line) } catch (err) { return self.end() } - self.proto.handle(row); + self.handle(row); self._line = ''; } else self._line += buf.charAt(i) @@ -127,6 +131,14 @@ dnode.prototype.write = function (buf) { } }; +dnode.prototype.handle = function (row) { + if (!this.proto) { + if (!this._handleQueue) this._handleQueue = []; + this._handleQueue.push(row); + } + else this.proto.handle(row); +}; + dnode.prototype.end = function () { if (this._ended) return; this._ended = true; diff --git a/package.json b/package.json index ec90be3..1513609 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name" : "dnode", - "version" : "1.0.0", + "version" : "1.0.1", "description" : "freestyle rpc", "main" : "./index.js", "keywords" : [