From f6fe6ff1ff92dff72e8da1556da7a8f6570f680c Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Mon, 25 Jan 2016 01:34:25 +0200 Subject: [PATCH 01/32] fix default ports in examples --- examples/basic.js | 4 +++- package.json | 4 ++-- test/backend.js | 13 ++++++++----- test/frontend.js | 9 ++++++--- test/pubsub/publisher.js | 7 +++++-- test/pubsub/subscriber.js | 7 +++++-- test/rpc/client.js | 9 ++++++--- test/rpc/server.js | 9 ++++++--- 8 files changed, 41 insertions(+), 21 deletions(-) diff --git a/examples/basic.js b/examples/basic.js index d9369d6..e56f821 100644 --- a/examples/basic.js +++ b/examples/basic.js @@ -15,8 +15,10 @@ var program = require('commander'); program - .option('-p, --port ', 'Server IP port', parseInt,9000); + .option('-p, --port ', 'Server IP port', 9000) + .parse(process.argv); +console.log('Listening port:', program.port); function onRPCRegistered(uri) { console.log('onRPCRegistered RPC registered', uri); diff --git a/package.json b/package.json index 78afbd8..5da429a 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,8 @@ "url": "https://github.com/Orange-OpenSource/wamp.rt.git" }, "dependencies": { - "ws": "~0.4.31", - "commander": "~2.0.0" + "ws": "*", + "commander": "*" }, "devDependencies": { "autobahn": "*", diff --git a/test/backend.js b/test/backend.js index d5ad504..4d96031 100644 --- a/test/backend.js +++ b/test/backend.js @@ -3,13 +3,16 @@ var autobahn = require('autobahn'); var program = require('commander'); program - .option('-p, --port ', 'Server IP port', parseInt,9000) + .option('-p, --port ', 'Server IP port', 9000) .option('-i, --ip ', 'Server IP address','127.0.0.1') .parse(process.argv); +var connectUrl = 'ws://' + program.ip + ':' + program.port; +console.log('connectUrl:', connectUrl); + var connection = new autobahn.Connection({ - url: 'ws://' + program.ip + ':' + program.port, - realm: 'realm1'} + url: connectUrl, + realm: 'realm1'} ); connection.onopen = function (session) { @@ -53,7 +56,7 @@ connection.onopen = function (session) { // Define an event handler function onEvent(publishArgs, kwargs) { - console.log('Event received args', publishArgs, 'kwargs ',kwargs); + console.log('Event', currentSubscription.topic,'received args', publishArgs, 'kwargs ',kwargs); counter++; if (counter > 20) { session.unsubscribe(currentSubscription).then(function(gone) { @@ -61,7 +64,7 @@ connection.onopen = function (session) { }, function(error) { console.log("unsubscribe failed", error); }); - } + } } // Subscribe to a topic diff --git a/test/frontend.js b/test/frontend.js index 0518505..f9eeb71 100644 --- a/test/frontend.js +++ b/test/frontend.js @@ -3,13 +3,16 @@ var autobahn = require('autobahn'); var program = require('commander'); program - .option('-p, --port ', 'Server IP port', parseInt,9000) + .option('-p, --port ', 'Server IP port', 9000) .option('-i, --ip ', 'Server IP address','127.0.0.1') .parse(process.argv); +var connectUrl = 'ws://' + program.ip + ':' + program.port; +console.log('connectUrl:', connectUrl); + var connection = new autobahn.Connection({ - url: 'ws://' + program.ip + ':' + program.port, - realm: 'realm1'} + url: connectUrl, + realm: 'realm1'} ); var session = null; diff --git a/test/pubsub/publisher.js b/test/pubsub/publisher.js index 412d6d0..5428c8f 100644 --- a/test/pubsub/publisher.js +++ b/test/pubsub/publisher.js @@ -4,12 +4,15 @@ var program = require('commander'); var when = require('when'); program - .option('-p, --port ', 'Server IP port', parseInt,9000) + .option('-p, --port ', 'Server IP port', 9000) .option('-i, --ip ', 'Server IP address','127.0.0.1') .parse(process.argv); +var connectUrl = 'ws://' + program.ip + ':' + program.port; +console.log('connectUrl:', connectUrl); + var connection = new autobahn.Connection({ - url: 'ws://' + program.ip + ':' + program.port, + url: connectUrl, realm: 'realm1'} ); diff --git a/test/pubsub/subscriber.js b/test/pubsub/subscriber.js index 25ce560..39d7d09 100644 --- a/test/pubsub/subscriber.js +++ b/test/pubsub/subscriber.js @@ -3,12 +3,15 @@ var autobahn = require('autobahn'); var program = require('commander'); program - .option('-p, --port ', 'Server IP port', parseInt,9000) + .option('-p, --port ', 'Server IP port', 9000) .option('-i, --ip ', 'Server IP address','127.0.0.1') .parse(process.argv); +var connectUrl = 'ws://' + program.ip + ':' + program.port; +console.log('connectUrl:', connectUrl); + var connection = new autobahn.Connection({ - url: 'ws://' + program.ip + ':' + program.port, + url: connectUrl, realm: 'realm1'} ); diff --git a/test/rpc/client.js b/test/rpc/client.js index 41b313b..404745c 100644 --- a/test/rpc/client.js +++ b/test/rpc/client.js @@ -4,13 +4,16 @@ var program = require('commander'); var when = require('when'); program - .option('-p, --port ', 'Server IP port', parseInt,9000) + .option('-p, --port ', 'Server IP port', 9000) .option('-i, --ip ', 'Server IP address','127.0.0.1') .parse(process.argv); +var connectUrl = 'ws://' + program.ip + ':' + program.port; +console.log('connectUrl:', connectUrl); + var connection = new autobahn.Connection({ - url: 'ws://' + program.ip + ':' + program.port, - realm: 'realm1'} + url: connectUrl, + realm: 'realm1'} ); connection.onopen = function (session) { diff --git a/test/rpc/server.js b/test/rpc/server.js index d6e59b9..eb6339c 100644 --- a/test/rpc/server.js +++ b/test/rpc/server.js @@ -3,13 +3,16 @@ var autobahn = require('autobahn'); var program = require('commander'); program - .option('-p, --port ', 'Server IP port', parseInt,9000) + .option('-p, --port ', 'Server IP port', 9000) .option('-i, --ip ', 'Server IP address','127.0.0.1') .parse(process.argv); +var connectUrl = 'ws://' + program.ip + ':' + program.port; +console.log('connectUrl:', connectUrl); + var connection = new autobahn.Connection({ - url: 'ws://' + program.ip + ':' + program.port, - realm: 'realm1'} + url: connectUrl, + realm: 'realm1'} ); connection.onopen = function (session) { From eeaf6f2c47f62785f62d19646e2a9b3506a37a24 Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Tue, 8 Mar 2016 11:42:44 +0200 Subject: [PATCH 02/32] realm + unit test --- {test => democli}/backend.js | 0 {test => democli}/frontend.js | 0 {test => democli}/pubsub/publisher.js | 0 {test => democli}/pubsub/subscriber.js | 0 {test => democli}/rpc/client.js | 0 {test => democli}/rpc/server.js | 0 examples/basic.js | 26 +---- lib/handlers.js | 108 ++++++++++---------- lib/realm.js | 70 +++++++++++++ lib/router.js | 133 +++---------------------- lib/session.js | 76 ++++++-------- lib/transport.js | 94 +++++++++++++++++ lib/wamp.rt.js | 12 ++- package.json | 5 +- test/router.js | 63 ++++++++++++ 15 files changed, 349 insertions(+), 238 deletions(-) rename {test => democli}/backend.js (100%) rename {test => democli}/frontend.js (100%) rename {test => democli}/pubsub/publisher.js (100%) rename {test => democli}/pubsub/subscriber.js (100%) rename {test => democli}/rpc/client.js (100%) rename {test => democli}/rpc/server.js (100%) create mode 100644 lib/realm.js create mode 100644 lib/transport.js create mode 100644 test/router.js diff --git a/test/backend.js b/democli/backend.js similarity index 100% rename from test/backend.js rename to democli/backend.js diff --git a/test/frontend.js b/democli/frontend.js similarity index 100% rename from test/frontend.js rename to democli/frontend.js diff --git a/test/pubsub/publisher.js b/democli/pubsub/publisher.js similarity index 100% rename from test/pubsub/publisher.js rename to democli/pubsub/publisher.js diff --git a/test/pubsub/subscriber.js b/democli/pubsub/subscriber.js similarity index 100% rename from test/pubsub/subscriber.js rename to democli/pubsub/subscriber.js diff --git a/test/rpc/client.js b/democli/rpc/client.js similarity index 100% rename from test/rpc/client.js rename to democli/rpc/client.js diff --git a/test/rpc/server.js b/democli/rpc/server.js similarity index 100% rename from test/rpc/server.js rename to democli/rpc/server.js diff --git a/examples/basic.js b/examples/basic.js index e56f821..29a49d6 100644 --- a/examples/basic.js +++ b/examples/basic.js @@ -10,32 +10,19 @@ WAMPRT_TRACE = true; -var Router = require('../lib/wamp.rt'); +var WampRouter = require('../lib/wamp.rt'); var program = require('commander'); - program .option('-p, --port ', 'Server IP port', 9000) .parse(process.argv); console.log('Listening port:', program.port); -function onRPCRegistered(uri) { - console.log('onRPCRegistered RPC registered', uri); -} - -function onRPCUnregistered(uri) { - console.log('onRPCUnregistered RPC unregistered', uri); -} - -function onPublish(topicUri, args) { - console.log('onPublish Publish', topicUri, args); -} - // // WebSocket server // -var app = new Router( +var app = new WampRouter( { port: program.port, // The router will select the appropriate protocol, // but we can still deny the connection @@ -48,11 +35,8 @@ var app = new Router( } ); -app.on('RPCRegistered', onRPCRegistered); -app.on('RPCUnregistered', onRPCUnregistered); -app.on('Publish', onPublish); - -app.regrpc('wamp.rt.foo', function(id,args) { +var realm = app.getRealm('realm1'); +realm.regrpc('wamp.rt.foo', function(id,args) { console.log('called with ' + args); - app.resrpc(id,["bar", "bar2"], {"key1": "bar1", "key2": "bar2"}); + realm.resrpc(id,["bar", "bar2"], {"key1": "bar1", "key2": "bar2"}); }); diff --git a/lib/handlers.js b/lib/handlers.js index 59f6459..72b08d1 100644 --- a/lib/handlers.js +++ b/lib/handlers.js @@ -7,50 +7,52 @@ var WAMP = require('./protocol'), var handlers = {}; -// This handlers are meant to be called in the context of the router object +// This handlers are meant to be called in the context of the SESSION object -handlers[WAMP.HELLO] = function(session, args) { - var realm = args.shift(); +handlers[WAMP.HELLO] = function(args) { + var realmName = args.shift(); var details = args.shift(); - if (typeof session.id === 'undefined') { - session.id = util.randomId(); - log.trace('New session :' + session.id); + if (typeof this.id === 'undefined') { + this.id = util.randomId(); + this.initRealm(realmName); + log.trace(realmName+': New session :' + this.id); // Send welcome message var msg = [ WAMP.WELCOME, - session.id, + this.id, { "roles": { "dealer": {} } }]; - session.send(msg); + this.send(msg); } else { - session.terminate(1002, "protocol violation"); + this.terminate(1002, "protocol violation"); } }; -handlers[WAMP.GOODBYE] = function(session, args) { +handlers[WAMP.GOODBYE] = function(args) { // Ack the goodbye var msg = [ WAMP.GOODBYE, {}, "wamp.error.goodbye_and_out" ]; - session.send(msg, function (error) { - session.terminate(1000, "Client closed WAMP session"); - }); + this.send(msg, function (error) { + this.terminate(1000, "Client closed WAMP session"); + }.bind(this)); }; -handlers[WAMP.REGISTER] = function (session, args) { +handlers[WAMP.REGISTER] = function (args) { var request = args.shift(); var options = args.shift(); var procUri = args.shift(); args = args || []; var msg; - if (typeof this.getrpc(procUri) === 'undefined') { - var regId = session.register(procUri); - this.regrpc(procUri, function (invId, args) { + var realm = this.getRealm(); + if (typeof realm.getrpc(procUri) === 'undefined') { + var regId = this.register(procUri); + realm.regrpc(procUri, function (invId, args) { log.trace('Invoking RPC ' + procUri, args); var msg = [ WAMP.INVOCATION, @@ -62,8 +64,8 @@ handlers[WAMP.REGISTER] = function (session, args) { for(var i = 0; i < args.length && i < 2; i++) { msg.push(args[i]); } - session.send(msg); - }); + this.send(msg); + }.bind(this)); msg = [ WAMP.REGISTERED, request, @@ -78,10 +80,10 @@ handlers[WAMP.REGISTER] = function (session, args) { "wamp.error.procedure_already_exists" ]; } - session.send(msg); + this.send(msg); }; -handlers[WAMP.CALL] = function (session, args) { +handlers[WAMP.CALL] = function (args) { var callId = args.shift(); var options = args.shift(); var procUri = args.shift(); @@ -95,7 +97,7 @@ handlers[WAMP.CALL] = function (session, args) { {}, "wamp.error.callee_failure" ]; - session.send(msg); + this.send(msg); } else { var msg = [ WAMP.RESULT, @@ -106,10 +108,11 @@ handlers[WAMP.CALL] = function (session, args) { for(var i = 0; i < args.length && i < 2; i++) { msg.push(args[i]); } - session.send(msg); + this.send(msg); } - }; - if (!this.callrpc(procUri, args, resultCallback)) { + }.bind(this); + var realm = this.getRealm(); + if (!realm.callrpc(procUri, args, resultCallback)) { var msg = [ WAMP.ERROR, WAMP.CALL, @@ -117,15 +120,16 @@ handlers[WAMP.CALL] = function (session, args) { {}, "wamp.error.no_such_procedure" ]; - session.send(msg); + this.send(msg); } }; -handlers[WAMP.UNREGISTER] = function (session, args) { +handlers[WAMP.UNREGISTER] = function (args) { var requestId = args.shift(); var registrationId = args.shift(); var msg; - var uri = session.unregister(registrationId); + var uri = this.unregister(registrationId); + var realm = this.getRealm(); if (typeof uri === 'undefined') { msg = [ WAMP.ERROR, @@ -135,23 +139,24 @@ handlers[WAMP.UNREGISTER] = function (session, args) { "wamp.error.no_such_registration" ]; } else { - this.unregrpc(uri); + realm.unregrpc(uri); msg = [ WAMP.UNREGISTERED, requestId ]; } - session.send(msg); + this.send(msg); }; -handlers[WAMP.YIELD] = function (session, args) { +handlers[WAMP.YIELD] = function (args) { var invId = args.shift(); var options = args.shift(); args = args || []; - this.resrpc(invId, null, args); + var realm = this.getRealm(); + realm.resrpc(invId, null, args); }; -handlers[WAMP.SUBSCRIBE] = function(session, args) { +handlers[WAMP.SUBSCRIBE] = function(args) { var requestId = args.shift(); var options = args.shift(); var topicUri = args.shift(); @@ -173,28 +178,30 @@ handlers[WAMP.SUBSCRIBE] = function(session, args) { if (kwargs !== undefined) { msg.push(kwargs); } - session.send(msg); - }; + this.send(msg); + }.bind(this); - var subsId = session.subscribe(topicUri); - this.substopic(topicUri, subsId, eventCallback); + var realm = this.getRealm(); + var subsId = this.subscribe(topicUri); + realm.substopic(topicUri, subsId, eventCallback); msg = [ WAMP.SUBSCRIBED, requestId, subsId ]; log.trace('Subscribe Topic ' + topicUri); - session.send(msg); + this.send(msg); }; -handlers[WAMP.UNSUBSCRIBE] = function(session, args) { +handlers[WAMP.UNSUBSCRIBE] = function(args) { var requestId = args.shift(); var subsid = args.shift(); - var topicUri = session.unsubscribe(subsid); + var topicUri = this.unsubscribe(subsid); args = args || []; var msg; - if (typeof this.gettopic(topicUri) === 'undefined') { + var realm = this.getRealm(); + if (typeof realm.gettopic(topicUri) === 'undefined') { msg = [ WAMP.ERROR, WAMP.UNSUBSCRIBE, @@ -204,17 +211,17 @@ handlers[WAMP.UNSUBSCRIBE] = function(session, args) { ]; log.trace('Unsubscription error ' + topicUri); } else { - this.unsubstopic(topicUri, subsid); + realm.unsubstopic(topicUri, subsid); msg = [ WAMP.UNSUBSCRIBED, requestId ]; log.trace('Unsubscribe Topic ' + topicUri); } - session.send(msg); + this.send(msg); }; -handlers[WAMP.PUBLISH] = function(session, msg) { +handlers[WAMP.PUBLISH] = function(msg) { var requestId = msg.shift(); var options = msg.shift(); var topicUri = msg.shift(); @@ -229,17 +236,18 @@ handlers[WAMP.PUBLISH] = function(session, msg) { requestId, publicationId ]; - session.send(msg); + this.send(msg); log.trace('Publish Topic with ack ' + topicUri + ' ' + publicationId); } else { log.trace('Publish Topic without ack ' + topicUri + ' ' + publicationId); } + var realm = this.getRealm(); // Router (this) is in charge of the events dispatching - this.publish(topicUri, publicationId, args, kwargs); + realm.publish(topicUri, publicationId, args, kwargs); }; -handlers[WAMP.EVENT] = function(session, args) { +handlers[WAMP.EVENT] = function(args) { var subscriptionId = args.shift(); var publicationId = args.shift(); args = args || []; @@ -248,7 +256,7 @@ handlers[WAMP.EVENT] = function(session, args) { + ' publicationId ' + publicationId); }; -handlers[WAMP.ERROR] = function(session, msg) { +handlers[WAMP.ERROR] = function(msg) { var requestType = msg.shift(); var requestId = msg.shift(); var details = msg.shift(); @@ -257,12 +265,12 @@ handlers[WAMP.ERROR] = function(session, msg) { var kwargs = msg.shift() || {}; var err = new Error(details); + var realm = this.getRealm(); if (requestType === WAMP.INVOCATION) { // An invocation failed var invId = requestId; - this.resrpc(invId, err, args); + realm.resrpc(invId, err, args); } - } module.exports = handlers; diff --git a/lib/realm.js b/lib/realm.js new file mode 100644 index 0000000..35865d9 --- /dev/null +++ b/lib/realm.js @@ -0,0 +1,70 @@ +var + util = require('./util'); + +function Realm() { + var _rpcs = {}; + var _pending = {}; + var _topics = {}; + + // RPC Management + this.getrpc = function(uri) { + return _rpcs[uri]; + }; + + this.regrpc = function(uri, rpc) { + _rpcs[uri] = rpc; + }; + + this.unregrpc = function(uri) { + delete _rpcs[uri]; + }; + + this.callrpc = function(uri, args, callback) { + if (typeof this.getrpc(uri) !== 'undefined') { + var invId = util.randomId(); + _pending[invId] = callback; + this.getrpc(uri).apply(this ,[invId, args]); + return true; + } else { + return false; + } + }; + + this.resrpc = function(invId, err, args) { + if (typeof _pending[invId] !== 'undefined') { + _pending[invId].apply(this, [err, args]); + delete _pending[invId]; + } + }; + + // Topic Management + this.gettopic = function(topicUri) { + return _topics[topicUri]; + }; + + this.substopic = function(topicUri, subscriptionId, callback) { + if (typeof _topics[topicUri] === 'undefined') { + _topics[topicUri] = {}; + } + _topics[topicUri][subscriptionId] = callback; + }; + + this.unsubstopic = function(topicUri, subscriptionId) { + delete _topics[topicUri][subscriptionId]; + }; + + this.publish = function(topicUri, publicationId, args, kwargs) { + if (typeof _topics[topicUri] !== 'undefined') { + for(var key in _topics[topicUri]) { + if(typeof _topics[topicUri][key] !== 'undefined') { + _topics[topicUri][key].apply(this, [publicationId, args, kwargs]); + } + } + return true; + } else { + return false; + } + }; +} + +module.exports = Realm; diff --git a/lib/router.js b/lib/router.js index 3257148..a3f5bd9 100644 --- a/lib/router.js +++ b/lib/router.js @@ -1,131 +1,26 @@ // wamp.rt // Copyright Orange 2014 -var WebSocketServer = require('ws').Server, +var Session = require('./session'), - WAMP = require('./protocol'), - tools = require('./util'), - log = require('./log'), - util = require('util'), - eventEmitter = require('events').EventEmitter; + Realm = require('./realm'); module.exports = Router; - -util.inherits(Router, eventEmitter); - -function Router(options) { - var _options = options || {}; - - if ( !_options.disableProtocolCheck ) { - // We need to verify that the subprotocol is wamp.2.json - var cb = _options.handleProtocols; - _options.handleProtocols = function (protocols, callback) { - var i=0; - var result = false; - while(i < protocols.length && result === false) { - result = (protocols[i] == "wamp.2.json"); - i++; - } - if (result && typeof cb == 'function') { - // If a handleProtocol function was provided by the - // calling script, just filter out the results - cb([protocols[i-1]], callback); - } else { - callback(result, result ? protocols[i-1] : null); - } - }; - } - var _rpcs = {}; - var _pending = {}; - var _sessions = {}; - var _topics = {}; - var _trace = function (msg) { - log.trace('[ROUTER] ' + msg); - }; - // Instantiate WebSocketServer - var _wss = new WebSocketServer(_options); - // Create a Session object for the lifetime of each - // WebSocket client object - _wss.on('connection', function (wsclient) { - var id = tools.randomId(); - _sessions[id] = new Session(this, wsclient); - wsclient.on('close', function() { - _sessions[id].cleanup(); - delete _sessions[id]; - }); - }.bind(this)); - - this.close = function() { - _wss.close(); - }; - - // RPC Management - this.getrpc = function(uri) { - return _rpcs[uri]; - }; - - this.regrpc = function(uri, rpc) { - _trace("Registering " + uri); - _rpcs[uri] = rpc; - this.emit('RPCRegistered', [uri]) - }; - - this.unregrpc = function(uri) { - _trace("Unregistering " + uri); - delete _rpcs[uri]; - this.emit('RPCUnregistered', [uri]) - }; - - this.callrpc = function(uri, args, callback) { - if (typeof this.getrpc(uri) !== 'undefined') { - var invId = tools.randomId(); - _pending[invId] = callback; - this.getrpc(uri).apply(this ,[invId, args]); - return true; +function Router() { + // Realm management + var _realms = {}; + this.getRealm = function(realm) { + if (_realms.hasOwnProperty(realm)) { + return _realms[realm]; } else { - return false; + var r = new Realm(); + _realms[realm] = r; + return r; } }; - this.resrpc = function(invId, err, args) { - if (typeof _pending[invId] !== 'undefined') { - _pending[invId].apply(this, [err, args]); - delete _pending[invId]; - } - }; - - // Topic Management - this.gettopic = function(topicUri) { - return _topics[topicUri]; - }; - - this.substopic = function(topicUri, subscriptionId, callback) { - _trace("Registering topic " + topicUri+ " subsc id " + subscriptionId); - if (typeof _topics[topicUri] === 'undefined') { - _topics[topicUri] = {}; - } - _topics[topicUri][subscriptionId] = callback; - }; - - this.unsubstopic = function(topicUri, subscriptionId) { - _trace("Unregistering topic " + topicUri + " subsc id " + subscriptionId); - delete _topics[topicUri][subscriptionId]; - }; - - this.publish = function(topicUri, publicationId, args, kwargs) { - _trace("Publish " + topicUri + " " + publicationId); - this.emit('Publish', topicUri, args, kwargs); - if (typeof _topics[topicUri] !== 'undefined') { - for(var key in _topics[topicUri]) { - if(typeof _topics[topicUri][key] !== 'undefined') { - _topics[topicUri][key].apply(this, [publicationId, args, kwargs]); - } - } - return true; - } else { - _trace("Undefined topic "); - return false; - } - }; + this.createSession = function(sender) { + return new Session(this, sender); + } } diff --git a/lib/session.js b/lib/session.js index 53dfef4..e7453b0 100644 --- a/lib/session.js +++ b/lib/session.js @@ -3,20 +3,25 @@ var WAMP = require('./protocol'), handlers = require('./handlers'), - util = require('./util'), - log = require('./log'); + util = require('./util'); module.exports = Session; -function Session (router, wsclient) { +// requires sender with +// sender.send(msg, sessionId, callback) +// sender.close(code, reason) + +function Session (router, sender) { var _registeredUris = {}; var _subscribedUris = {}; - var _trace = function (msg) { - var trace = "[SESSION][" + - ((typeof this.id === 'undefined') ? "?" : this.id) + - "] " + msg; - log.trace(trace); - }.bind(this); + var _realm = null; + + this.initRealm = function (realm) { + _realm = router.getRealm(realm); + }; + this.getRealm = function () { + return _realm; + }; this.register = function (uri) { var registrationId = util.randomId(); _registeredUris[registrationId] = uri; @@ -44,41 +49,21 @@ function Session (router, wsclient) { }; this.send = function (msg, callback) { - data = JSON.stringify(msg); - var defaultCallback = function (error) { - if (error) { - log.trace("Failed to send message: " + error); - this.terminate(1011, "Unexpected error"); - } - }.bind(this); - _trace('TX > ' + data); - wsclient.send(data, (typeof callback === 'function') ? - callback : defaultCallback); + sender.send(msg, this.id, callback); }; - wsclient.on('message', function(data) { - var msg; - - try { - msg = JSON.parse(data); - } catch (e) { - log.trace('invalid json'); - this.terminate(1003, "protocol violation"); - return; - } + this.handle = function (msg) { if (!Array.isArray(msg)) { - log.trace('msg not a list'); this.terminate(1003, "protocol violation"); return; } var type = msg.shift(); if (!handlers[type]) { - log.trace('unknown message type'); this.terminate(1003, "protocol violation"); return; } - _trace('RX < ' + data); - handlers[type].apply(router, [this, msg]); - }.bind(this)); + handlers[type].call(this, msg); + }; + this.close = function () { // Graceful termination var msg = [ @@ -87,23 +72,22 @@ function Session (router, wsclient) { "wamp.error.close_realm" ]; this.send(msg,function (error) { - session.terminate(1000, "Server closed WAMP session"); + sender.close(1000, "Server closed WAMP session"); }); }; this.terminate = function (code, reason) { - log.trace('Closing WebSocket connection: [' + - code + '] ' + reason); - wsclient.close(code, reason); + sender.close(code, reason); }; this.cleanup = function () { - _trace('Cleaning up session'); - for( var regId in _registeredUris) { - router.unregrpc(_registeredUris[regId]); - delete _registeredUris[regId]; - } - for (var subId in _subscribedUris) { - router.unsubstopic(_subscribedUris[subId],subId); - delete _subscribedUris[subId]; + if (_realm) { + for(var regId in _registeredUris) { + _realm.unregrpc(_registeredUris[regId]); + delete _registeredUris[regId]; + } + for (var subId in _subscribedUris) { + _realm.unsubstopic(_subscribedUris[subId],subId); + delete _subscribedUris[subId]; + } } }; } diff --git a/lib/transport.js b/lib/transport.js new file mode 100644 index 0000000..d0b1e23 --- /dev/null +++ b/lib/transport.js @@ -0,0 +1,94 @@ +var WebSocketServer = require('ws').Server, + log = require('./log'); + +module.exports = Transport; + +var _trace = function (msg, id) { + var trace = "[SESSION][" + + ((typeof id === 'undefined') ? "?" : id) + + "] " + msg; + log.trace(trace); +}.bind(this); + +function wsParser(wsclient, session) { + wsclient.on('message', function(data) { + var msg; + + try { + msg = JSON.parse(data); + } catch (e) { + log.trace('invalid json'); + session.terminate(1003, "protocol violation"); + return; + } + _trace('RX < ' + data, session.id); + session.handle(msg); + }); + + wsclient.on('close', function() { + log.trace('WebSocket is closed.'); + session.cleanup(); + }); +} + +function wsSender(wsclient) { + this.send = function (msg, id, callback) { + data = JSON.stringify(msg); + var defaultCallback = function (error) { + if (error) { + log.trace("Failed to send message: " + error); + this.close(1011, "Unexpected error"); + } + }.bind(this); + _trace('TX > ' + data, id); + wsclient.send(data, (typeof callback === 'function') ? + callback : defaultCallback); + }; + + this.close = function (code, reason) { + log.trace('Closing WebSocket connection: [' + + code + '] ' + reason); + wsclient.close(code, reason); + } +} + +function Transport(router, options) { + var _options = options || {}; + + if ( !_options.disableProtocolCheck ) { + // We need to verify that the subprotocol is wamp.2.json + var cb = _options.handleProtocols; + _options.handleProtocols = function (protocols, callback) { + var i=0; + var result = false; + while(i < protocols.length && result === false) { + result = (protocols[i] == "wamp.2.json"); + i++; + } + if (result && typeof cb == 'function') { + // If a handleProtocol function was provided by the + // calling script, just filter out the results + cb([protocols[i-1]], callback); + } else { + callback(result, result ? protocols[i-1] : null); + } + }; + } + // Instantiate WebSocketServer + var _wss = new WebSocketServer(_options); + // Create a Session object for the lifetime of each + // WebSocket client object + _wss.on('connection', function (wsclient) { + var sender = new wsSender(wsclient); + var session = router.createSession(sender); + var parser = new wsParser(wsclient, session); + }.bind(this)); + + this.getRealm = function (reaml) { + return router.getRealm(reaml); + }; + + this.close = function() { + _wss.close(); + }; +} diff --git a/lib/wamp.rt.js b/lib/wamp.rt.js index 6bf4312..b815abd 100644 --- a/lib/wamp.rt.js +++ b/lib/wamp.rt.js @@ -1 +1,11 @@ -module.exports = require('./router'); +var + inherits = require('util').inherits, + Transport = require('./transport'), + Router = require('./router'); + +RouterTransport = function (options) { + Transport.call(this, new Router(), options); +}; + +inherits(RouterTransport, Transport); +module.exports = RouterTransport; diff --git a/package.json b/package.json index 5da429a..09f0843 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,10 @@ }, "devDependencies": { "autobahn": "*", - "when": "*" + "when": "*", + "chai": "*", + "chai-as-promised": "*", + "chai-spies": "*" }, "main": "lib/wamp.rt", "engines": { diff --git a/test/router.js b/test/router.js new file mode 100644 index 0000000..06fe8ab --- /dev/null +++ b/test/router.js @@ -0,0 +1,63 @@ +var + chai = require('chai'), + spies = require('chai-spies'), + expect = chai.expect, + WAMP = require('../lib/protocol'), + Router = require('../lib/router'); + +chai.use(spies); + +describe('protocol', function() { + var + router, + sender, + client; + + beforeEach(function(){ + sender = {}; + router = new Router(); + cli = router.createSession(sender); + cli.initRealm('test'); + }); + + afterEach(function(){ + }) + + it('HELLO/WELCOME', function () { + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.WELCOME); + } + ); + cli.handle([WAMP.HELLO, 1, 'test', {}]); + expect(sender.send).to.have.been.called.once; + }); + + it('GOODBYE', function () { + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.GOODBYE); + callback(); + } + ); + sender.close = chai.spy( + function (error) {} + ); + cli.handle([WAMP.GOODBYE]); + expect(sender.send).to.have.been.called.once; + expect(sender.close).to.have.been.called.once; + }); + + it('CALL to RPC not exist', function () { + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.ERROR); + expect(msg[1]).to.equal(WAMP.CALL); + expect(msg[2]).to.equal(1234); + expect(msg[4]).to.equal('wamp.error.no_such_procedure'); + } + ); + cli.handle([WAMP.CALL, 1234, {}, 'any.function.name', []]); + expect(sender.send).to.have.been.called.once; + }); +}); From a5ffb7d30db715207cf9acd2ef1ea188ebe416e4 Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Wed, 9 Mar 2016 00:53:49 +0200 Subject: [PATCH 03/32] CALL and PUBLISH tests --- examples/basic.js | 2 +- lib/handlers.js | 4 +- lib/realm.js | 9 +-- lib/router.js | 2 + lib/session.js | 2 + lib/transport.js | 9 ++- test/router.js | 145 +++++++++++++++++++++++++++++++++++++++++++++- 7 files changed, 162 insertions(+), 11 deletions(-) diff --git a/examples/basic.js b/examples/basic.js index 29a49d6..0842dcd 100644 --- a/examples/basic.js +++ b/examples/basic.js @@ -38,5 +38,5 @@ var app = new WampRouter( var realm = app.getRealm('realm1'); realm.regrpc('wamp.rt.foo', function(id,args) { console.log('called with ' + args); - realm.resrpc(id,["bar", "bar2"], {"key1": "bar1", "key2": "bar2"}); + realm.resrpc(id, undefined /* no error */, [["bar", "bar2"], {"key1": "bar1", "key2": "bar2"}]); }); diff --git a/lib/handlers.js b/lib/handlers.js index 72b08d1..ffa2a57 100644 --- a/lib/handlers.js +++ b/lib/handlers.js @@ -1,6 +1,8 @@ // wamp.rt // Copyright Orange 2014 +'use strict'; + var WAMP = require('./protocol'), util = require('./util'), log = require('./log'); @@ -160,7 +162,7 @@ handlers[WAMP.SUBSCRIBE] = function(args) { var requestId = args.shift(); var options = args.shift(); var topicUri = args.shift(); - args = args || []; + var args = args || []; var msg; var eventCallback = function(publicationId, args, kwargs) { diff --git a/lib/realm.js b/lib/realm.js index 35865d9..4dce92f 100644 --- a/lib/realm.js +++ b/lib/realm.js @@ -1,5 +1,6 @@ -var - util = require('./util'); +'use strict'; + +var util = require('./util'); function Realm() { var _rpcs = {}; @@ -23,7 +24,7 @@ function Realm() { if (typeof this.getrpc(uri) !== 'undefined') { var invId = util.randomId(); _pending[invId] = callback; - this.getrpc(uri).apply(this ,[invId, args]); + this.getrpc(uri).call(this , invId, args); return true; } else { return false; @@ -32,7 +33,7 @@ function Realm() { this.resrpc = function(invId, err, args) { if (typeof _pending[invId] !== 'undefined') { - _pending[invId].apply(this, [err, args]); + _pending[invId].call(this, err, args); delete _pending[invId]; } }; diff --git a/lib/router.js b/lib/router.js index a3f5bd9..d31fe32 100644 --- a/lib/router.js +++ b/lib/router.js @@ -1,6 +1,8 @@ // wamp.rt // Copyright Orange 2014 +'use strict'; + var Session = require('./session'), Realm = require('./realm'); diff --git a/lib/session.js b/lib/session.js index e7453b0..6a2fd33 100644 --- a/lib/session.js +++ b/lib/session.js @@ -1,6 +1,8 @@ // wamp.rt // Copyright Orange 2014 +'use strict'; + var WAMP = require('./protocol'), handlers = require('./handlers'), util = require('./util'); diff --git a/lib/transport.js b/lib/transport.js index d0b1e23..159e7e8 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -1,5 +1,8 @@ -var WebSocketServer = require('ws').Server, - log = require('./log'); +'use strict'; + +var + WebSocketServer = require('ws').Server, + log = require('./log'); module.exports = Transport; @@ -33,7 +36,7 @@ function wsParser(wsclient, session) { function wsSender(wsclient) { this.send = function (msg, id, callback) { - data = JSON.stringify(msg); + var data = JSON.stringify(msg); var defaultCallback = function (error) { if (error) { log.trace("Failed to send message: " + error); diff --git a/test/router.js b/test/router.js index 06fe8ab..d0e8334 100644 --- a/test/router.js +++ b/test/router.js @@ -1,3 +1,5 @@ +'use strict'; + var chai = require('chai'), spies = require('chai-spies'), @@ -11,13 +13,12 @@ describe('protocol', function() { var router, sender, - client; + cli; beforeEach(function(){ sender = {}; router = new Router(); cli = router.createSession(sender); - cli.initRealm('test'); }); afterEach(function(){ @@ -57,7 +58,147 @@ describe('protocol', function() { expect(msg[4]).to.equal('wamp.error.no_such_procedure'); } ); + cli.initRealm('test'); cli.handle([WAMP.CALL, 1234, {}, 'any.function.name', []]); expect(sender.send).to.have.been.called.once; }); + + it('CALL to router', function () { + cli.initRealm('test'); + var realm = cli.getRealm('test'); + var procSpy = chai.spy(function(id, args, kwargs) { +// console.log(id, args, kwargs); + realm.resrpc(id, undefined, [['result.1','result.2'], {kVal:'kRes'}]); + }); + realm.regrpc('func1', procSpy) + + sender.send = chai.spy( + function (msg, id, callback) { +// console.log(msg); + expect(msg[0]).to.equal(WAMP.RESULT); + expect(msg[1]).to.equal(1234); + expect(msg[3]).to.deep.equal(['result.1','result.2']); + expect(msg[4]).to.deep.equal({kVal:'kRes'}); + } + ); + cli.initRealm('test'); + cli.handle([WAMP.CALL, 1234, {}, 'func1', ['arg1', 'arg2'], {'kArg':'kVal'}]); + expect(procSpy).to.have.been.called.once; + expect(sender.send).to.have.been.called.once; + }); + + it('CALL to router with error', function () { + cli.initRealm('test'); + var realm = cli.getRealm('test'); + var callId = null; + var procSpy = chai.spy(function(id, args, kwargs) { + callId = id; + }); + realm.regrpc('func1', procSpy); + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.ERROR); + expect(msg[1]).to.equal(WAMP.CALL); + expect(msg[2]).to.equal(1234); + expect(msg[4]).to.deep.equal('wamp.error.callee_failure'); + } + ); + cli.initRealm('test'); + cli.handle([WAMP.CALL, 1234, {}, 'func1', ['arg1', 'arg2'], {'kArg':'kVal'}]); + realm.resrpc(callId, 1, [['result.1','result.2'], {kVal:'kRes'}]); + expect(procSpy).to.have.been.called.once; + expect(sender.send).to.have.been.called.once; + }); + + it('CALL to remote', function () { + cli.initRealm('test'); + var realm = cli.getRealm('test'); + var registrationId = null; + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.REGISTERED); + expect(msg[1]).to.equal(1234); + registrationId = msg[2]; + } + ); + cli.handle([WAMP.REGISTER, 1234, {}, 'func1']); + expect(sender.send, 'registration confirmed').to.have.been.called.once; + + var callId = null; + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.INVOCATION); + callId = msg[1]; + expect(msg[2]).to.equal(registrationId); + // 3 options? + expect(msg[4]).to.deep.equal(['arg.1','arg.2']); + expect(msg[5]).to.deep.equal({kVal:'kRes'}); + } + ); + var callSpy = chai.spy(function(id, args, kwargs) { +// console.log('call result', id, args, kwargs); + expect(args).to.deep.equal([['result.1','result.2'],{foo:'bar'}]); + }); + realm.callrpc('func1', [['arg.1','arg.2'], {kVal:'kRes'}], callSpy); + expect(sender.send, 'invocation received').to.have.been.called.once; + + cli.handle([WAMP.YIELD, callId, {}, ['result.1','result.2'], {foo:'bar'}]); + + expect(callSpy, 'result delivered').to.have.been.called.once; + }); + + it('PUBLISH to remote', function () { + cli.initRealm('test'); + var realm = cli.getRealm('test'); + var publicationId = null; + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.SUBSCRIBED); + expect(msg[1]).to.equal(1234); + publicationId = msg[2]; + } + ); + cli.handle([WAMP.SUBSCRIBE, 1234, {}, 'topic1']); + expect(sender.send, 'subscription confirmed').to.have.been.called.once; + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.EVENT); + expect(msg[1]).to.equal(publicationId); + expect(msg[2]).to.equal(2345); + // 3 options? + expect(msg[4]).to.deep.equal(['arg.1','arg.2']); + expect(msg[5]).to.deep.equal({foo:'bar'}); + } + ); + realm.publish('topic1', 2345, ['arg.1','arg.2'], {foo:'bar'}); + expect(sender.send, 'publication received').to.have.been.called.once; + }); + + it('SUBSCRIBE to remote', function () { + cli.initRealm('test'); + var realm = cli.getRealm('test'); + var subscriptionId = cli.subscribe('topic1'); + + var subSpy = chai.spy( + function (publicationId, args, kwargs) { + expect(args).to.deep.equal(['arg.1','arg.2']); + expect(kwargs).to.deep.equal({foo:'bar'}); + } + ); + realm.substopic('topic1', subscriptionId, subSpy); + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.PUBLISHED); + expect(msg[1]).to.equal(2345); + } + ); + cli.handle([WAMP.PUBLISH, 1234, {}, "topic1", ['arg.1','arg.2'],{foo:'bar'}]); + expect(sender.send, 'published').to.not.have.been.called; + cli.handle([WAMP.PUBLISH, 2345, {"acknowledge":true}, "topic1", ['arg.1','arg.2'],{foo:'bar'}]); + expect(sender.send, 'published').to.have.been.called.once; + }); }); From be81c11cfa8f8490bccb3ad510c324960527c548 Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Wed, 9 Mar 2016 21:58:20 +0200 Subject: [PATCH 04/32] router callrpc with args and kwargs --- democli/frontend.js | 3 +-- examples/basic.js | 6 +++--- lib/handlers.js | 16 ++++++++-------- lib/realm.js | 4 ++-- test/router.js | 2 +- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/democli/frontend.js b/democli/frontend.js index f9eeb71..7c69540 100644 --- a/democli/frontend.js +++ b/democli/frontend.js @@ -93,7 +93,7 @@ connection.onopen = function (new_session) { } ); - session.call('wamp.rt.foo',["test"]).then( + session.call('wamp.rt.foo', ["test"], {foo:'bar'}).then( function (res) { session.log("Call wamp.rt.foo completed in " + (Date.now() - starttime) + @@ -130,4 +130,3 @@ connection.onclose = function (reason, details) { }; connection.open(); - diff --git a/examples/basic.js b/examples/basic.js index 0842dcd..3b39e91 100644 --- a/examples/basic.js +++ b/examples/basic.js @@ -36,7 +36,7 @@ var app = new WampRouter( ); var realm = app.getRealm('realm1'); -realm.regrpc('wamp.rt.foo', function(id,args) { - console.log('called with ' + args); - realm.resrpc(id, undefined /* no error */, [["bar", "bar2"], {"key1": "bar1", "key2": "bar2"}]); +realm.regrpc('wamp.rt.foo', function(id, args, kwargs) { + console.log('called with ', args, kwargs); + realm.resrpc(id, null /* no error */, [["bar", "bar2"], {"key1": "bar1", "key2": "bar2"}]); }); diff --git a/lib/handlers.js b/lib/handlers.js index ffa2a57..7ed6cba 100644 --- a/lib/handlers.js +++ b/lib/handlers.js @@ -54,8 +54,8 @@ handlers[WAMP.REGISTER] = function (args) { var realm = this.getRealm(); if (typeof realm.getrpc(procUri) === 'undefined') { var regId = this.register(procUri); - realm.regrpc(procUri, function (invId, args) { - log.trace('Invoking RPC ' + procUri, args); + realm.regrpc(procUri, function (invId, args, kwargs) { + log.trace('Invoking RPC ' + procUri, args, kwargs); var msg = [ WAMP.INVOCATION, invId, @@ -63,9 +63,8 @@ handlers[WAMP.REGISTER] = function (args) { {}, ]; // Manage optional parameters args + kwargs - for(var i = 0; i < args.length && i < 2; i++) { - msg.push(args[i]); - } + if (undefined !== args) msg.push(args); + if (undefined !== kwargs) msg.push(kwargs); this.send(msg); }.bind(this)); msg = [ @@ -89,7 +88,8 @@ handlers[WAMP.CALL] = function (args) { var callId = args.shift(); var options = args.shift(); var procUri = args.shift(); - args = args || []; + var fArgs = args.shift() || []; + var kwArgs = args.shift() || {}; var resultCallback = function(err, args) { if (err) { var msg = [ @@ -114,7 +114,7 @@ handlers[WAMP.CALL] = function (args) { } }.bind(this); var realm = this.getRealm(); - if (!realm.callrpc(procUri, args, resultCallback)) { + if (!realm.callrpc(procUri, fArgs, kwArgs, resultCallback)) { var msg = [ WAMP.ERROR, WAMP.CALL, @@ -162,7 +162,7 @@ handlers[WAMP.SUBSCRIBE] = function(args) { var requestId = args.shift(); var options = args.shift(); var topicUri = args.shift(); - var args = args || []; + args = args || []; var msg; var eventCallback = function(publicationId, args, kwargs) { diff --git a/lib/realm.js b/lib/realm.js index 4dce92f..68230b3 100644 --- a/lib/realm.js +++ b/lib/realm.js @@ -20,11 +20,11 @@ function Realm() { delete _rpcs[uri]; }; - this.callrpc = function(uri, args, callback) { + this.callrpc = function(uri, args, kwargs, callback) { if (typeof this.getrpc(uri) !== 'undefined') { var invId = util.randomId(); _pending[invId] = callback; - this.getrpc(uri).call(this , invId, args); + this.getrpc(uri).call(this, invId, args, kwargs); return true; } else { return false; diff --git a/test/router.js b/test/router.js index d0e8334..ffe7ff4 100644 --- a/test/router.js +++ b/test/router.js @@ -140,7 +140,7 @@ describe('protocol', function() { // console.log('call result', id, args, kwargs); expect(args).to.deep.equal([['result.1','result.2'],{foo:'bar'}]); }); - realm.callrpc('func1', [['arg.1','arg.2'], {kVal:'kRes'}], callSpy); + realm.callrpc('func1', ['arg.1','arg.2'], {kVal:'kRes'}, callSpy); expect(sender.send, 'invocation received').to.have.been.called.once; cli.handle([WAMP.YIELD, callId, {}, ['result.1','result.2'], {foo:'bar'}]); From b6818d71bbbf06f991edcf36441a8da8560ae28b Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Wed, 9 Mar 2016 23:18:17 +0200 Subject: [PATCH 05/32] optimize publish arguments --- README.md | 6 ++++++ lib/handlers.js | 8 +++----- lib/realm.js | 9 ++++----- test/router.js | 10 +++++----- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 06f12da..033f22b 100644 --- a/README.md +++ b/README.md @@ -17,3 +17,9 @@ wamp.rt has been inspired by the following Open Source projects: - [wamp.io](https://github.com/nicokaiser/wamp.io) + +## Changes to internal api +2016-03-09: +- internal api moved to realm +- callrpc method has args & kwargs arguments +- publish method does not require message id diff --git a/lib/handlers.js b/lib/handlers.js index 7ed6cba..2764966 100644 --- a/lib/handlers.js +++ b/lib/handlers.js @@ -228,10 +228,12 @@ handlers[WAMP.PUBLISH] = function(msg) { var options = msg.shift(); var topicUri = msg.shift(); var ack = options && options.acknowledge; - var publicationId = util.randomId(); var args = msg.shift() || []; var kwargs = msg.shift() || {}; + var realm = this.getRealm(); + // Router (this) is in charge of the events dispatching + var publicationId = realm.publish(topicUri, args, kwargs); if (ack) { msg = [ WAMP.PUBLISHED, @@ -243,10 +245,6 @@ handlers[WAMP.PUBLISH] = function(msg) { } else { log.trace('Publish Topic without ack ' + topicUri + ' ' + publicationId); } - - var realm = this.getRealm(); - // Router (this) is in charge of the events dispatching - realm.publish(topicUri, publicationId, args, kwargs); }; handlers[WAMP.EVENT] = function(args) { diff --git a/lib/realm.js b/lib/realm.js index 68230b3..d31a1cf 100644 --- a/lib/realm.js +++ b/lib/realm.js @@ -54,17 +54,16 @@ function Realm() { delete _topics[topicUri][subscriptionId]; }; - this.publish = function(topicUri, publicationId, args, kwargs) { + this.publish = function(topicUri, args, kwargs) { + var publicationId = util.randomId(); if (typeof _topics[topicUri] !== 'undefined') { for(var key in _topics[topicUri]) { if(typeof _topics[topicUri][key] !== 'undefined') { - _topics[topicUri][key].apply(this, [publicationId, args, kwargs]); + _topics[topicUri][key].call(this, publicationId, args, kwargs); } } - return true; - } else { - return false; } + return publicationId; }; } diff --git a/test/router.js b/test/router.js index ffe7ff4..e2cf2cc 100644 --- a/test/router.js +++ b/test/router.js @@ -151,13 +151,13 @@ describe('protocol', function() { it('PUBLISH to remote', function () { cli.initRealm('test'); var realm = cli.getRealm('test'); - var publicationId = null; + var subscriptionId = null; sender.send = chai.spy( function (msg, id, callback) { expect(msg[0]).to.equal(WAMP.SUBSCRIBED); expect(msg[1]).to.equal(1234); - publicationId = msg[2]; + subscriptionId = msg[2]; } ); cli.handle([WAMP.SUBSCRIBE, 1234, {}, 'topic1']); @@ -166,14 +166,14 @@ describe('protocol', function() { sender.send = chai.spy( function (msg, id, callback) { expect(msg[0]).to.equal(WAMP.EVENT); - expect(msg[1]).to.equal(publicationId); - expect(msg[2]).to.equal(2345); + expect(msg[1]).to.equal(subscriptionId); + // 2 published message Id // 3 options? expect(msg[4]).to.deep.equal(['arg.1','arg.2']); expect(msg[5]).to.deep.equal({foo:'bar'}); } ); - realm.publish('topic1', 2345, ['arg.1','arg.2'], {foo:'bar'}); + realm.publish('topic1', ['arg.1','arg.2'], {foo:'bar'}); expect(sender.send, 'publication received').to.have.been.called.once; }); From bc9e679d893234b3802668edebc2ed6277bb125d Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Thu, 10 Mar 2016 21:00:10 +0200 Subject: [PATCH 06/32] move sessionId generator to create session --- lib/handlers.js | 8 +++----- lib/router.js | 4 ++-- lib/session.js | 7 +++++-- lib/transport.js | 2 +- test/router.js | 7 +++++++ 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/lib/handlers.js b/lib/handlers.js index 2764966..25f9fe0 100644 --- a/lib/handlers.js +++ b/lib/handlers.js @@ -4,7 +4,6 @@ 'use strict'; var WAMP = require('./protocol'), - util = require('./util'), log = require('./log'); var handlers = {}; @@ -14,14 +13,13 @@ var handlers = {}; handlers[WAMP.HELLO] = function(args) { var realmName = args.shift(); var details = args.shift(); - if (typeof this.id === 'undefined') { - this.id = util.randomId(); + if (this.getRealm() === null) { this.initRealm(realmName); - log.trace(realmName+': New session :' + this.id); + log.trace(realmName+': New session :' + this.getSessionId()); // Send welcome message var msg = [ WAMP.WELCOME, - this.id, + this.getSessionId(), { "roles": { "dealer": {} diff --git a/lib/router.js b/lib/router.js index d31fe32..943e7f9 100644 --- a/lib/router.js +++ b/lib/router.js @@ -3,7 +3,7 @@ 'use strict'; -var +var util = require('./util'), Session = require('./session'), Realm = require('./realm'); @@ -23,6 +23,6 @@ function Router() { }; this.createSession = function(sender) { - return new Session(this, sender); + return new Session(this, sender, util.randomId()); } } diff --git a/lib/session.js b/lib/session.js index 6a2fd33..4ec784c 100644 --- a/lib/session.js +++ b/lib/session.js @@ -13,11 +13,14 @@ module.exports = Session; // sender.send(msg, sessionId, callback) // sender.close(code, reason) -function Session (router, sender) { +function Session (router, sender, sessionId) { var _registeredUris = {}; var _subscribedUris = {}; var _realm = null; + this.getSessionId = function () { + return sessionId; + }; this.initRealm = function (realm) { _realm = router.getRealm(realm); }; @@ -51,7 +54,7 @@ function Session (router, sender) { }; this.send = function (msg, callback) { - sender.send(msg, this.id, callback); + sender.send(msg, sessionId, callback); }; this.handle = function (msg) { if (!Array.isArray(msg)) { diff --git a/lib/transport.js b/lib/transport.js index 159e7e8..23b6798 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -24,7 +24,7 @@ function wsParser(wsclient, session) { session.terminate(1003, "protocol violation"); return; } - _trace('RX < ' + data, session.id); + _trace('RX < ' + data, session.getSessionId()); session.handle(msg); }); diff --git a/test/router.js b/test/router.js index e2cf2cc..e75521f 100644 --- a/test/router.js +++ b/test/router.js @@ -32,6 +32,13 @@ describe('protocol', function() { ); cli.handle([WAMP.HELLO, 1, 'test', {}]); expect(sender.send).to.have.been.called.once; + + // second hello to logout + sender.send = chai.spy(function (msg, id, callback) {}); + sender.close = chai.spy(function (error, reason) {}); + cli.handle([WAMP.HELLO, 1, 'test', {}]); + expect(sender.send).to.not.have.been.called; + expect(sender.close).to.have.been.called.once; }); it('GOODBYE', function () { From 787df5d99650a016c362e17a89ed21c2f4ae77c5 Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Sat, 19 Mar 2016 13:35:13 +0200 Subject: [PATCH 07/32] refactoring step 1 --- lib/handlers.js | 147 ++++-------------------- lib/realm.js | 233 +++++++++++++++++++++++++++++++------- lib/router.js | 32 ++++-- lib/session.js | 112 ++++++++++-------- lib/{util.js => tools.js} | 0 test/router.js | 64 +++++++---- 6 files changed, 349 insertions(+), 239 deletions(-) rename lib/{util.js => tools.js} (100%) diff --git a/lib/handlers.js b/lib/handlers.js index 25f9fe0..b7615aa 100644 --- a/lib/handlers.js +++ b/lib/handlers.js @@ -1,6 +1,3 @@ -// wamp.rt -// Copyright Orange 2014 - 'use strict'; var WAMP = require('./protocol'), @@ -13,13 +10,13 @@ var handlers = {}; handlers[WAMP.HELLO] = function(args) { var realmName = args.shift(); var details = args.shift(); - if (this.getRealm() === null) { + if (this.realm === null) { this.initRealm(realmName); - log.trace(realmName+': New session :' + this.getSessionId()); + log.trace(realmName+': New session :' + this.sessionId); // Send welcome message var msg = [ WAMP.WELCOME, - this.getSessionId(), + this.sessionId, { "roles": { "dealer": {} @@ -44,42 +41,12 @@ handlers[WAMP.GOODBYE] = function(args) { }; handlers[WAMP.REGISTER] = function (args) { - var request = args.shift(); - var options = args.shift(); - var procUri = args.shift(); - args = args || []; - var msg; - var realm = this.getRealm(); - if (typeof realm.getrpc(procUri) === 'undefined') { - var regId = this.register(procUri); - realm.regrpc(procUri, function (invId, args, kwargs) { - log.trace('Invoking RPC ' + procUri, args, kwargs); - var msg = [ - WAMP.INVOCATION, - invId, - regId, - {}, - ]; - // Manage optional parameters args + kwargs - if (undefined !== args) msg.push(args); - if (undefined !== kwargs) msg.push(kwargs); - this.send(msg); - }.bind(this)); - msg = [ - WAMP.REGISTERED, - request, - regId - ]; - } else { - msg = [ - WAMP.ERROR, - WAMP.REGISTER, - request, - {}, - "wamp.error.procedure_already_exists" - ]; - } - this.send(msg); + var request = args.shift(); + var options = args.shift(); + var procUri = args.shift(); + + if (this.checkRealm(WAMP.REGISTER, request)) + this.realm.regrpc(this, request, procUri, options); }; handlers[WAMP.CALL] = function (args) { @@ -88,48 +55,17 @@ handlers[WAMP.CALL] = function (args) { var procUri = args.shift(); var fArgs = args.shift() || []; var kwArgs = args.shift() || {}; - var resultCallback = function(err, args) { - if (err) { - var msg = [ - WAMP.ERROR, - WAMP.CALL, - callId, - {}, - "wamp.error.callee_failure" - ]; - this.send(msg); - } else { - var msg = [ - WAMP.RESULT, - callId, - {}, - ]; - // Manage optional parameters args + kwargs - for(var i = 0; i < args.length && i < 2; i++) { - msg.push(args[i]); - } - this.send(msg); - } - }.bind(this); - var realm = this.getRealm(); - if (!realm.callrpc(procUri, fArgs, kwArgs, resultCallback)) { - var msg = [ - WAMP.ERROR, - WAMP.CALL, - callId, - {}, - "wamp.error.no_such_procedure" - ]; - this.send(msg); - } + + if (this.checkRealm(WAMP.CALL, callId)) + this.realm.callrpc(this, callId, procUri, fArgs, kwArgs); }; handlers[WAMP.UNREGISTER] = function (args) { var requestId = args.shift(); var registrationId = args.shift(); var msg; - var uri = this.unregister(registrationId); var realm = this.getRealm(); + var uri = this.unregister(registrationId); if (typeof uri === 'undefined') { msg = [ WAMP.ERROR, @@ -152,45 +88,16 @@ handlers[WAMP.YIELD] = function (args) { var invId = args.shift(); var options = args.shift(); args = args || []; - var realm = this.getRealm(); - realm.resrpc(invId, null, args); + if (this.checkRealm(WAMP.CALL, invId)) + this.realm.resrpc(this, invId, null, args); }; handlers[WAMP.SUBSCRIBE] = function(args) { var requestId = args.shift(); var options = args.shift(); var topicUri = args.shift(); - args = args || []; - var msg; - - var eventCallback = function(publicationId, args, kwargs) { - log.trace('eventCallback', publicationId, args, kwargs); - var msg = [ - WAMP.EVENT, - subsId, - publicationId, - {} - ]; - // Manage optional parameters args + kwargs - if (args !== undefined) { - msg.push(args); - } - if (kwargs !== undefined) { - msg.push(kwargs); - } - this.send(msg); - }.bind(this); - - var realm = this.getRealm(); - var subsId = this.subscribe(topicUri); - realm.substopic(topicUri, subsId, eventCallback); - msg = [ - WAMP.SUBSCRIBED, - requestId, - subsId - ]; - log.trace('Subscribe Topic ' + topicUri); - this.send(msg); + if (this.checkRealm(WAMP.SUBSCRIBE, requestId)) + this.realm.substopic(this, requestId, topicUri, options); }; handlers[WAMP.UNSUBSCRIBE] = function(args) { @@ -225,24 +132,10 @@ handlers[WAMP.PUBLISH] = function(msg) { var requestId = msg.shift(); var options = msg.shift(); var topicUri = msg.shift(); - var ack = options && options.acknowledge; var args = msg.shift() || []; var kwargs = msg.shift() || {}; - - var realm = this.getRealm(); - // Router (this) is in charge of the events dispatching - var publicationId = realm.publish(topicUri, args, kwargs); - if (ack) { - msg = [ - WAMP.PUBLISHED, - requestId, - publicationId - ]; - this.send(msg); - log.trace('Publish Topic with ack ' + topicUri + ' ' + publicationId); - } else { - log.trace('Publish Topic without ack ' + topicUri + ' ' + publicationId); - } + if (this.checkRealm(WAMP.PUBLISH, requestId)) + this.realm.publish(this, requestId, topicUri, options, args, kwargs); }; handlers[WAMP.EVENT] = function(args) { @@ -267,7 +160,7 @@ handlers[WAMP.ERROR] = function(msg) { if (requestType === WAMP.INVOCATION) { // An invocation failed var invId = requestId; - realm.resrpc(invId, err, args); + realm.resrpc(this, invId, err, args); } } diff --git a/lib/realm.js b/lib/realm.js index d31a1cf..f6f70ba 100644 --- a/lib/realm.js +++ b/lib/realm.js @@ -1,70 +1,227 @@ 'use strict'; -var util = require('./util'); +var WAMP = require('./protocol'), + tools = require('./tools'), + log = require('./log'); -function Realm() { - var _rpcs = {}; - var _pending = {}; - var _topics = {}; +function Api(router, realm) { + var _callback = {}; + var _rpc = {}; - // RPC Management - this.getrpc = function(uri) { - return _rpcs[uri]; - }; + this.sessionId = tools.randomId(); + router.registerSession(this); - this.regrpc = function(uri, rpc) { - _rpcs[uri] = rpc; + this.regrpc = function(uri, callback) { + var regId = realm.regrpc(this, tools.randomId(), uri); + if (regId) { + _rpc[regId] = callback; + } + return regId; + }; + this.unregrpc = function(regId) { + var uri = realm.unregrpc(this, tools.randomId(), regId); + delete _rpc[regId]; + return uri; + }; + this.callrpc = function (uri, args, kwargs, callback) { + var callId = tools.randomId(); + if (realm.callrpc(this, callId, uri, args, kwargs)) { + _callback[callId] = callback; + } + }; + this.resrpc = function (invId, err, args) { + return realm.resrpc(this, invId, err, args); + }; + this.substopic = function(topicUri, callback) { + var topicId = realm.substopic(this, tools.randomId(), topicUri, {}); + _rpc[topicId] = callback; + return topicId; + }; + this.unsubstopic = function(topicId) { + delete _rpc[topicId]; + return realm.unsubstopic(this, tools.randomId(), topicId); + }; + this.publish = function (topicUri, args, kwargs) { + var requestId = tools.randomId(); + realm.publish(this, requestId, topicUri, {}, args, kwargs); }; - this.unregrpc = function(uri) { - delete _rpcs[uri]; + // internal part + this.invoke = function (regId, invId, args, kwargs) { + if (_rpc.hasOwnProperty(regId)) { + _rpc[regId](invId, args, kwargs); + } + }; + this.yield = function (callId, err, args) { + var callback = _callback[callId]; + delete _callback[callId]; + callback(err, args); }; + this.event = function (subscriptionId, publicationId, args, kwargs) { + if (_rpc.hasOwnProperty(subscriptionId)) { + _rpc[subscriptionId](publicationId, args, kwargs); + } + }; + this.send = function (msg) {}; +}; - this.callrpc = function(uri, args, kwargs, callback) { - if (typeof this.getrpc(uri) !== 'undefined') { - var invId = util.randomId(); - _pending[invId] = callback; - this.getrpc(uri).call(this, invId, args, kwargs); - return true; - } else { +function Realm(router) { + var _sessRPC = {}; + var _sessTopic = {}; + + var _rpcs = {}; + var _topics = {}; + var _pending = {}; + var _api = null; + + this.api = function () { + if (!_api) { + _api = new Api(router, this); + } + return _api + } + + // RPC Management + this.regrpc = function(session, invID, procUri, options) { + if (_rpcs.hasOwnProperty(procUri)) { + session.sendError(WAMP.REGISTER, invID, "wamp.error.procedure_already_exists"); return false; } + var registrationId = tools.randomId(); + _rpcs[procUri] = {sessionId:session.sessionId, regId:registrationId}; + if (!_sessRPC.hasOwnProperty(session.sessionId)) + _sessRPC[session.sessionId] = {}; + _sessRPC[session.sessionId][registrationId] = procUri; + router.emit('RPCRegistered', procUri, this); + session.send([ + WAMP.REGISTERED, + invID, + registrationId + ]); + return registrationId; + }; + this.unregrpc = function(session, requestId, regId) { + var procUri = ''; + if (_sessRPC.hasOwnProperty(session.sessionId) && _sessRPC[session.sessionId].hasOwnProperty(regId)) { + procUri = _sessRPC[session.sessionId][regId]; + delete _rpcs[procUri]; + delete _sessRPC[session.sessionId][regId]; + } + return procUri; }; - this.resrpc = function(invId, err, args) { - if (typeof _pending[invId] !== 'undefined') { - _pending[invId].call(this, err, args); - delete _pending[invId]; + this.callrpc = function(session, callId, procUri, args, kwargs) { + if (!_rpcs.hasOwnProperty(procUri)) { + session.sendError(WAMP.CALL, callId, "wamp.error.no_such_procedure"); + return false; } + var destSession = router.getSession(_rpcs[procUri].sessionId); + if (destSession) { + var invId = tools.randomId(); + _pending[invId] = [callId, session.sessionId]; + log.trace('Invoking RPC ' + procUri, args, kwargs); + destSession.invoke(_rpcs[procUri].regId, invId, args, kwargs); + return invId; + } else { + // delete + } + return false; }; - // Topic Management - this.gettopic = function(topicUri) { - return _topics[topicUri]; + this.resrpc = function(session, invId, err, args) { + if (_pending.hasOwnProperty(invId)) { + var destSession = router.getSession(_pending[invId][1]); + if (destSession) + destSession.yield(_pending[invId][0], err, args); + } + delete _pending[invId]; }; - this.substopic = function(topicUri, subscriptionId, callback) { - if (typeof _topics[topicUri] === 'undefined') { + // Topic Management + this.substopic = function(session, requestId, topicUri, options) { + var topicId = tools.randomId(); + if (!_sessTopic.hasOwnProperty(session.sessionId)) { + _sessTopic[session.sessionId] = {}; + } + _sessTopic[session.sessionId][topicId] = topicUri; + if (!_topics.hasOwnProperty(topicUri)) { _topics[topicUri] = {}; } - _topics[topicUri][subscriptionId] = callback; + _topics[topicUri][topicId] = session.sessionId; + + router.emit('Subscribed', topicUri, this); + session.send([ + WAMP.SUBSCRIBED, + requestId, + topicId + ]); + return topicId; }; - this.unsubstopic = function(topicUri, subscriptionId) { - delete _topics[topicUri][subscriptionId]; + this.unsubstopic = function(session, requestId, topicId) { + var topicUri = ''; + if (_sessTopic.hasOwnProperty(session.sessionId) && _sessTopic[session.sessionId].hasOwnProperty(topicId)) { + topicUri = _sessTopic[session.sessionId][topicId]; + delete _topics[topicUri][topicId]; + delete _sessTopic[session.sessionId][topicId]; + } + return topicUri; }; - this.publish = function(topicUri, args, kwargs) { - var publicationId = util.randomId(); - if (typeof _topics[topicUri] !== 'undefined') { - for(var key in _topics[topicUri]) { - if(typeof _topics[topicUri][key] !== 'undefined') { - _topics[topicUri][key].call(this, publicationId, args, kwargs); + this.publish = function(session, requestId, topicUri, options, args, kwargs) { + var publicationId = tools.randomId(); + if (_topics.hasOwnProperty(topicUri)) { + for(var subscriptionId in _topics[topicUri]) { + var destSession = router.getSession(_topics[topicUri][subscriptionId]); + if (destSession) { + destSession.event(parseInt(subscriptionId), publicationId, args, kwargs); + } else { +// delete _topics[topicUri][subscriptionId]; } } } + var ack = options && options.acknowledge; + router.emit('Publish', topicUri, this, args, kwargs, ack); + if (ack) { + session.send([ + WAMP.PUBLISHED, + requestId, + publicationId + ]); + } return publicationId; }; + + this.cleanupRPC = function (session) { + var procIds = []; + var procUris = []; + if (_sessRPC.hasOwnProperty(session.sessionId)) { + for (var regId in _sessRPC[session.sessionId]) + procIds.push(regId); + for (var i=0; i Date: Sat, 19 Mar 2016 14:03:09 +0200 Subject: [PATCH 08/32] test unsubscribe and unregister --- test/router.js | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/test/router.js b/test/router.js index e75521f..11da7d1 100644 --- a/test/router.js +++ b/test/router.js @@ -117,6 +117,31 @@ describe('protocol', function() { expect(sender.send).to.have.been.called.once; }); + it('UNREGISTER', function () { + cli.initRealm('test'); + var realm = cli.getRealm('test'); + var registrationId = null; + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.REGISTERED); + expect(msg[1]).to.equal(1234); + registrationId = msg[2]; + } + ); + cli.handle([WAMP.REGISTER, 1234, {}, 'func1']); + expect(sender.send, 'registration confirmed').to.have.been.called.once; + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.UNREGISTERED); + expect(msg[1]).to.equal(2345); + } + ); + cli.handle([WAMP.UNREGISTER, 2345, registrationId]); + expect(sender.send, 'unregistration confirmed').to.have.been.called.once; + }); + it('CALL to remote', function () { cli.initRealm('test'); var realm = cli.getRealm('test'); @@ -155,6 +180,31 @@ describe('protocol', function() { expect(callSpy, 'result delivered').to.have.been.called.once; }); + it('UNSUBSCRIBE', function () { + cli.initRealm('test'); + var realm = cli.getRealm('test'); + var subscriptionId = null; + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.SUBSCRIBED); + expect(msg[1]).to.equal(1234); + subscriptionId = msg[2]; + } + ); + cli.handle([WAMP.SUBSCRIBE, 1234, {}, 'topic1']); + expect(sender.send, 'subscription confirmed').to.have.been.called.once; + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.UNSUBSCRIBED); + expect(msg[1]).to.equal(2345); + } + ); + cli.handle([WAMP.UNSUBSCRIBE, 2345, subscriptionId]); + expect(sender.send, 'unsubscription confirmed').to.have.been.called.once; + }); + it('PUBLISH to remote', function () { cli.initRealm('test'); var realm = cli.getRealm('test'); From 7be5fd00ca809dc196acbab7cd99605fc889987e Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Sat, 19 Mar 2016 14:45:44 +0200 Subject: [PATCH 09/32] unregister error test --- test/router.js | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/test/router.js b/test/router.js index 11da7d1..8a7c656 100644 --- a/test/router.js +++ b/test/router.js @@ -117,6 +117,23 @@ describe('protocol', function() { expect(sender.send).to.have.been.called.once; }); + it('UNREGISTER error', function () { + cli.initRealm('test'); + var realm = cli.getRealm('test'); + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.ERROR); + expect(msg[1]).to.equal(WAMP.UNREGISTER); + expect(msg[2]).to.equal(2345); + // 3 options + expect(msg[4]).to.equal('wamp.error.no_such_registration'); + } + ); + cli.handle([WAMP.UNREGISTER, 2345, 1234567890]); + expect(sender.send, 'unregistration confirmed').to.have.been.called.once; + }); + it('UNREGISTER', function () { cli.initRealm('test'); var realm = cli.getRealm('test'); @@ -180,9 +197,24 @@ describe('protocol', function() { expect(callSpy, 'result delivered').to.have.been.called.once; }); + it('UNSUBSCRIBE error', function () { + cli.initRealm('test'); + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.ERROR); + expect(msg[1]).to.equal(WAMP.UNSUBSCRIBE); + expect(msg[2]).to.equal(2345); + // 3 options + expect(msg[4]).to.equal('wamp.error.no_such_subscription'); + } + ); + cli.handle([WAMP.UNSUBSCRIBE, 2345, 1234567890]); + expect(sender.send, 'unsubscription confirmed').to.have.been.called.once; + }); + it('UNSUBSCRIBE', function () { cli.initRealm('test'); - var realm = cli.getRealm('test'); var subscriptionId = null; sender.send = chai.spy( From aa574e8d520cffcfd1cc1f0c5eb2e636ca871995 Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Sat, 19 Mar 2016 14:49:11 +0200 Subject: [PATCH 10/32] merge fix --- test/router.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/router.js b/test/router.js index 04646e8..f7d4160 100644 --- a/test/router.js +++ b/test/router.js @@ -133,8 +133,7 @@ describe('protocol', function() { it('UNREGISTER error', function () { cli.initRealm('test'); - var realm = cli.getRealm('test'); - + sender.send = chai.spy( function (msg, id, callback) { expect(msg[0]).to.equal(WAMP.ERROR); From f6c0c94e0cea31153e784d65dc382eb5f87ee145 Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Sat, 19 Mar 2016 15:32:41 +0200 Subject: [PATCH 11/32] fix demo --- examples/basic.js | 6 +++--- lib/handlers.js | 2 -- lib/session.js | 2 +- lib/transport.js | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/examples/basic.js b/examples/basic.js index 3b39e91..a740b7d 100644 --- a/examples/basic.js +++ b/examples/basic.js @@ -35,8 +35,8 @@ var app = new WampRouter( } ); -var realm = app.getRealm('realm1'); -realm.regrpc('wamp.rt.foo', function(id, args, kwargs) { +var api = app.getRealm('realm1').api(); +api.regrpc('wamp.rt.foo', function(id, args, kwargs) { console.log('called with ', args, kwargs); - realm.resrpc(id, null /* no error */, [["bar", "bar2"], {"key1": "bar1", "key2": "bar2"}]); + api.resrpc(id, null /* no error */, [["bar", "bar2"], {"key1": "bar1", "key2": "bar2"}]); }); diff --git a/lib/handlers.js b/lib/handlers.js index dff6a3d..62e47c1 100644 --- a/lib/handlers.js +++ b/lib/handlers.js @@ -44,7 +44,6 @@ handlers[WAMP.REGISTER] = function (args) { var requestId = args.shift(); var options = args.shift(); var procUri = args.shift(); - if (this.checkRealm(WAMP.REGISTER, requestId)) this.realm.regrpc(this, requestId, procUri, options); }; @@ -55,7 +54,6 @@ handlers[WAMP.CALL] = function (args) { var procUri = args.shift(); var fArgs = args.shift() || []; var kwArgs = args.shift() || {}; - if (this.checkRealm(WAMP.CALL, callId)) this.realm.callrpc(this, callId, procUri, fArgs, kwArgs); }; diff --git a/lib/session.js b/lib/session.js index 507da3d..3a12e7b 100644 --- a/lib/session.js +++ b/lib/session.js @@ -112,7 +112,7 @@ function Session (router, sender, sessionId) { }; this.cleanup = function () { if (this.realm) { - this.realm.cleanup(this.sessionId); + this.realm.cleanup(this); } }; } diff --git a/lib/transport.js b/lib/transport.js index 23b6798..ab1305e 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -24,7 +24,7 @@ function wsParser(wsclient, session) { session.terminate(1003, "protocol violation"); return; } - _trace('RX < ' + data, session.getSessionId()); + _trace('RX < ' + data, session.sessionId); session.handle(msg); }); From 8d9739f05a825e012ded97ee081c5415a47a5138 Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Sat, 19 Mar 2016 17:10:12 +0200 Subject: [PATCH 12/32] test error in remote call --- test/router.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/router.js b/test/router.js index 8a7c656..798a88d 100644 --- a/test/router.js +++ b/test/router.js @@ -197,6 +197,29 @@ describe('protocol', function() { expect(callSpy, 'result delivered').to.have.been.called.once; }); + it('CALL to remote error', function () { + cli.initRealm('test'); + var realm = cli.getRealm('test'); + + sender.send = function () {}; + cli.handle([WAMP.REGISTER, 1234, {}, 'func1']); + + var callId = null; + sender.send = chai.spy( + function (msg, id, callback) { + callId = msg[1]; + } + ); + var callSpy = chai.spy(function(id, args, kwargs) { + expect(args).to.deep.equal(['err.detail.1','err.detail.2']); + }); + realm.callrpc('func1', ['arg.1','arg.2'], {kVal:'kRes'}, callSpy); + expect(sender.send, 'invocation received').to.have.been.called.once; + + cli.handle([WAMP.ERROR, WAMP.INVOCATION, callId, {}, 'wamp.error.runtime_error', ['err.detail.1','err.detail.2']]); + expect(callSpy, 'error delivered').to.have.been.called.once; + }); + it('UNSUBSCRIBE error', function () { cli.initRealm('test'); From 7230dcdafd934eb26fe83f81f60fa1d29c8ddae9 Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Sat, 19 Mar 2016 18:17:12 +0200 Subject: [PATCH 13/32] post refactoring cleaning --- lib/handlers.js | 23 ++++------------------- lib/realm.js | 9 ++------- lib/transport.js | 1 + test/router.js | 12 +++++++----- 4 files changed, 14 insertions(+), 31 deletions(-) diff --git a/lib/handlers.js b/lib/handlers.js index 62e47c1..1f82442 100644 --- a/lib/handlers.js +++ b/lib/handlers.js @@ -1,7 +1,6 @@ 'use strict'; -var WAMP = require('./protocol'), - log = require('./log'); +var WAMP = require('./protocol'); var handlers = {}; @@ -12,7 +11,6 @@ handlers[WAMP.HELLO] = function(args) { var details = args.shift(); if (this.realm === null) { this.initRealm(realmName); - log.trace(realmName+': New session :' + this.sessionId); // Send welcome message var msg = [ WAMP.WELCOME, @@ -98,15 +96,6 @@ handlers[WAMP.PUBLISH] = function(msg) { this.realm.publish(this, requestId, topicUri, options, args, kwargs); }; -handlers[WAMP.EVENT] = function(args) { - var subscriptionId = args.shift(); - var publicationId = args.shift(); - args = args || []; - - log.trace('Event received subscriptionId ' + subscriptionId - + ' publicationId ' + publicationId); -}; - handlers[WAMP.ERROR] = function(msg) { var requestType = msg.shift(); var requestId = msg.shift(); @@ -115,13 +104,9 @@ handlers[WAMP.ERROR] = function(msg) { var args = msg.shift() || []; var kwargs = msg.shift() || {}; - var err = new Error(details); - var realm = this.getRealm(); - if (requestType === WAMP.INVOCATION) { - // An invocation failed - var invId = requestId; - realm.resrpc(this, invId, err, args); - } + // An invocation failed + if (this.checkRealm(WAMP.ERROR, requestId) && requestType === WAMP.INVOCATION) + this.realm.resrpc(this, requestId, new Error(details), args); } module.exports = handlers; diff --git a/lib/realm.js b/lib/realm.js index fc49d59..b72e27d 100644 --- a/lib/realm.js +++ b/lib/realm.js @@ -1,8 +1,7 @@ 'use strict'; var WAMP = require('./protocol'), - tools = require('./tools'), - log = require('./log'); + tools = require('./tools'); function Api(router, realm) { var _callback = {}; @@ -107,10 +106,7 @@ function Realm(router) { delete _rpcs[procUri]; delete _sessRPC[session.sessionId][regId]; router.emit('RPCUnRegistered', procUri, this); - session.send([ - WAMP.UNREGISTERED, - requestId - ]); + session.send([WAMP.UNREGISTERED, requestId]); } else { session.sendError(WAMP.UNREGISTER, requestId, "wamp.error.no_such_registration"); } @@ -126,7 +122,6 @@ function Realm(router) { if (destSession) { var invId = tools.randomId(); _pending[invId] = [callId, session.sessionId]; - log.trace('Invoking RPC ' + procUri, args, kwargs); destSession.invoke(_rpcs[procUri].regId, invId, args, kwargs); return invId; } else { diff --git a/lib/transport.js b/lib/transport.js index ab1305e..b3534df 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -85,6 +85,7 @@ function Transport(router, options) { var sender = new wsSender(wsclient); var session = router.createSession(sender); var parser = new wsParser(wsclient, session); + log.trace('New session :' + session.sessionId); }.bind(this)); this.getRealm = function (reaml) { diff --git a/test/router.js b/test/router.js index 4c0a082..93c4883 100644 --- a/test/router.js +++ b/test/router.js @@ -133,7 +133,7 @@ describe('protocol', function() { it('UNREGISTER error', function () { cli.initRealm('test'); - + sender.send = chai.spy( function (msg, id, callback) { expect(msg[0]).to.equal(WAMP.ERROR); @@ -197,7 +197,8 @@ describe('protocol', function() { expect(msg[5]).to.deep.equal({kVal:'kRes'}); } ); - var callSpy = chai.spy(function(id, args, kwargs) { + var callSpy = chai.spy(function(err, args) { + expect(err).to.equal(null); expect(args).to.deep.equal([['result.1','result.2'],{foo:'bar'}]); }); api.callrpc('func1', ['arg.1','arg.2'], {kVal:'kRes'}, callSpy); @@ -210,7 +211,7 @@ describe('protocol', function() { it('CALL to remote error', function () { cli.initRealm('test'); - var realm = cli.getRealm('test'); + var api = cli.realm.api(); sender.send = function () {}; cli.handle([WAMP.REGISTER, 1234, {}, 'func1']); @@ -221,10 +222,11 @@ describe('protocol', function() { callId = msg[1]; } ); - var callSpy = chai.spy(function(id, args, kwargs) { + var callSpy = chai.spy(function(err, args) { + expect(err).to.be.an('error'); expect(args).to.deep.equal(['err.detail.1','err.detail.2']); }); - realm.callrpc('func1', ['arg.1','arg.2'], {kVal:'kRes'}, callSpy); + api.callrpc('func1', ['arg.1','arg.2'], {kVal:'kRes'}, callSpy); expect(sender.send, 'invocation received').to.have.been.called.once; cli.handle([WAMP.ERROR, WAMP.INVOCATION, callId, {}, 'wamp.error.runtime_error', ['err.detail.1','err.detail.2']]); From 7ab86536d42f0853ba82f92ff72b1ba8c0b960f1 Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Mon, 21 Mar 2016 21:39:51 +0200 Subject: [PATCH 14/32] welcome back event emiter --- democli/frontend.js | 5 ++- examples/basic.js | 20 ++++++++++ lib/handlers.js | 91 ++++++++++++++++++++++++++++++++++++++++++- lib/realm.js | 56 ++++++++++----------------- lib/router.js | 28 +++++++------- lib/session.js | 94 +++++---------------------------------------- lib/transport.js | 11 ++---- lib/wamp.rt.js | 5 ++- package.json | 13 ++++--- 9 files changed, 171 insertions(+), 152 deletions(-) diff --git a/democli/frontend.js b/democli/frontend.js index 7c69540..d8bb4a7 100644 --- a/democli/frontend.js +++ b/democli/frontend.js @@ -119,8 +119,9 @@ connection.onopen = function (new_session) { console.log("published, publication ID is ", publication); connection.close(); }, - function(error) { console.log("publication error", error); - connection.close(); + function(error) { + console.log("publication error", error); + connection.close(); } ); }; diff --git a/examples/basic.js b/examples/basic.js index a740b7d..2b2d731 100644 --- a/examples/basic.js +++ b/examples/basic.js @@ -19,6 +19,18 @@ program console.log('Listening port:', program.port); +function onRPCRegistered(realm, uri) { + console.log('onRPCRegistered RPC registered', uri); +} + +function onRPCUnregistered(realm, uri) { + console.log('onRPCUnregistered RPC unregistered', uri); +} + +function onPublish(realm, topicUri, args) { + console.log('onPublish Publish', topicUri, args); +} + // // WebSocket server // @@ -35,6 +47,14 @@ var app = new WampRouter( } ); +app.on('RPCRegistered', onRPCRegistered); +app.on('RPCUnregistered', onRPCUnregistered); +app.on('Publish', onPublish); + +app.on('RealmCreated', function (realm, realmName) { + console.log('new Relm:', realmName); +}); + var api = app.getRealm('realm1').api(); api.regrpc('wamp.rt.foo', function(id, args, kwargs) { console.log('called with ', args, kwargs); diff --git a/lib/handlers.js b/lib/handlers.js index 1f82442..905f178 100644 --- a/lib/handlers.js +++ b/lib/handlers.js @@ -4,6 +4,95 @@ var WAMP = require('./protocol'); var handlers = {}; +var Facade = function () { + this.sendRegistered = function (requestId, registrationId) { + this.send([WAMP.REGISTERED, requestId, registrationId]); + }; + this.sendUnregistered = function (requestId, registrationId) { + this.send([WAMP.UNREGISTERED, requestId]); + }; + this.sendInvoke = function (regId, invId, args, kwargs) { + var msg = [ + WAMP.INVOCATION, + invId, + regId, + {}, + ]; + // Manage optional parameters args + kwargs + if (undefined !== args) msg.push(args); + if (undefined !== kwargs) msg.push(kwargs); + this.send(msg); + }; + this.sendYield = function (invId, err, args) { + if (err) { + this.sendError(WAMP.CALL, invId, "wamp.error.callee_failure"); + } else { + var msg = [ + WAMP.RESULT, + invId, + {}, + ]; + // Manage optional parameters args + kwargs + for(var i = 0; i < args.length && i < 2; i++) { + msg.push(args[i]); + } + this.send(msg); + } + }; + this.sendSubscribed = function (requestId, topicId) { + this.send([WAMP.SUBSCRIBED, requestId, topicId]); + }; + this.sendUnsubscribed = function (requestId) { + this.send([WAMP.UNSUBSCRIBED, requestId]); + }; + this.sendPublished = function (requestId, publicationId) { + this.send([WAMP.PUBLISHED, requestId, publicationId]); + }; + this.sendEvent = function (subscriptionId, publicationId, args, kwargs) { + var msg = [ + WAMP.EVENT, + subscriptionId, + publicationId, + {} + ]; + // Manage optional parameters args + kwargs + if (args !== undefined) { + msg.push(args); + } + if (kwargs !== undefined) { + msg.push(kwargs); + } + this.send(msg); + }; + this.sendError = function (cmd, callId, txt) { + this.send([WAMP.ERROR, cmd, callId, {}, txt]); + }; + this.sendClose = function () { + // Graceful termination + // has not been used + var msg = [ + WAMP.GOODBYE, + {}, + "wamp.error.close_realm" + ]; + this.send(msg,function (error) { + this.terminate(1000, "Server closed WAMP session"); + }); + }; + this.handle = function (msg) { + if (!Array.isArray(msg)) { + this.terminate(1003, "protocol violation"); + return; + } + var type = msg.shift(); + if (!handlers[type]) { + this.terminate(1003, "protocol violation"); + return; + } + handlers[type].call(this, msg); + }; +}; + // This handlers are meant to be called in the context of the SESSION object handlers[WAMP.HELLO] = function(args) { @@ -109,4 +198,4 @@ handlers[WAMP.ERROR] = function(msg) { this.realm.resrpc(this, requestId, new Error(details), args); } -module.exports = handlers; +module.exports = Facade; diff --git a/lib/realm.js b/lib/realm.js index b72e27d..75c437f 100644 --- a/lib/realm.js +++ b/lib/realm.js @@ -1,12 +1,14 @@ 'use strict'; var WAMP = require('./protocol'), + handlers = require('./handlers'), tools = require('./tools'); function Api(router, realm) { var _callback = {}; var _rpc = {}; + handlers.call(this); this.sessionId = tools.randomId(); router.registerSession(this); @@ -46,17 +48,17 @@ function Api(router, realm) { }; // internal part - this.invoke = function (regId, invId, args, kwargs) { + this.sendInvoke = function (regId, invId, args, kwargs) { if (_rpc.hasOwnProperty(regId)) { _rpc[regId](invId, args, kwargs); } }; - this.yield = function (callId, err, args) { + this.sendYield = function (callId, err, args) { var callback = _callback[callId]; delete _callback[callId]; callback(err, args); }; - this.event = function (subscriptionId, publicationId, args, kwargs) { + this.sendEvent = function (subscriptionId, publicationId, args, kwargs) { if (_rpc.hasOwnProperty(subscriptionId)) { _rpc[subscriptionId](publicationId, args, kwargs); } @@ -81,9 +83,9 @@ function Realm(router) { } // RPC Management - this.regrpc = function(session, invID, procUri, options) { + this.regrpc = function(session, requestId, procUri, options) { if (_rpcs.hasOwnProperty(procUri)) { - session.sendError(WAMP.REGISTER, invID, "wamp.error.procedure_already_exists"); + session.sendError(WAMP.REGISTER, requestId, "wamp.error.procedure_already_exists"); return false; } var registrationId = tools.randomId(); @@ -91,12 +93,8 @@ function Realm(router) { if (!_sessRPC.hasOwnProperty(session.sessionId)) _sessRPC[session.sessionId] = {}; _sessRPC[session.sessionId][registrationId] = procUri; - router.emit('RPCRegistered', procUri, this); - session.send([ - WAMP.REGISTERED, - invID, - registrationId - ]); + router.emit('RPCRegistered', this, procUri); + session.sendRegistered(requestId, registrationId); return registrationId; }; this.unregrpc = function(session, requestId, regId) { @@ -105,8 +103,8 @@ function Realm(router) { procUri = _sessRPC[session.sessionId][regId]; delete _rpcs[procUri]; delete _sessRPC[session.sessionId][regId]; - router.emit('RPCUnRegistered', procUri, this); - session.send([WAMP.UNREGISTERED, requestId]); + router.emit('RPCUnRegistered', this, procUri); + session.sendUnregistered(requestId); } else { session.sendError(WAMP.UNREGISTER, requestId, "wamp.error.no_such_registration"); } @@ -122,7 +120,7 @@ function Realm(router) { if (destSession) { var invId = tools.randomId(); _pending[invId] = [callId, session.sessionId]; - destSession.invoke(_rpcs[procUri].regId, invId, args, kwargs); + destSession.sendInvoke(_rpcs[procUri].regId, invId, args, kwargs); return invId; } else { // delete @@ -134,7 +132,7 @@ function Realm(router) { if (_pending.hasOwnProperty(invId)) { var destSession = router.getSession(_pending[invId][1]); if (destSession) - destSession.yield(_pending[invId][0], err, args); + destSession.sendYield(_pending[invId][0], err, args); } delete _pending[invId]; }; @@ -151,12 +149,8 @@ function Realm(router) { } _topics[topicUri][topicId] = session.sessionId; - router.emit('Subscribed', topicUri, this); - session.send([ - WAMP.SUBSCRIBED, - requestId, - topicId - ]); + router.emit('Subscribed', this, topicUri); + session.sendSubscribed(requestId, topicId); return topicId; }; @@ -166,11 +160,8 @@ function Realm(router) { topicUri = _sessTopic[session.sessionId][topicId]; delete _topics[topicUri][topicId]; delete _sessTopic[session.sessionId][topicId]; - router.emit('UnSubscribed', topicUri, this); - session.send([ - WAMP.UNSUBSCRIBED, - requestId - ]); + router.emit('UnSubscribed', this, topicUri); + session.sendUnsubscribed(requestId); } else { session.sendError(WAMP.UNSUBSCRIBE, requestId, "wamp.error.no_such_subscription"); } @@ -183,21 +174,16 @@ function Realm(router) { for(var subscriptionId in _topics[topicUri]) { var destSession = router.getSession(_topics[topicUri][subscriptionId]); if (destSession) { - destSession.event(parseInt(subscriptionId), publicationId, args, kwargs); + destSession.sendEvent(parseInt(subscriptionId), publicationId, args, kwargs); } else { // delete _topics[topicUri][subscriptionId]; } } } var ack = options && options.acknowledge; - router.emit('Publish', topicUri, this, args, kwargs, ack); - if (ack) { - session.send([ - WAMP.PUBLISHED, - requestId, - publicationId - ]); - } + router.emit('Publish', this, topicUri, args, kwargs, ack); + if (ack) + session.sendPublished(requestId, publicationId); return publicationId; }; diff --git a/lib/router.js b/lib/router.js index e139d14..91966c4 100644 --- a/lib/router.js +++ b/lib/router.js @@ -4,24 +4,24 @@ var tools = require('./tools'), Session = require('./session'), Realm = require('./realm'), util = require('util'), - eventEmitter = require('events').EventEmitter; + EventEmitter = require('events').EventEmitter; -module.exports = Router; - -util.inherits(Router, eventEmitter); +util.inherits(Router, EventEmitter); function Router() { // Realm management var _realms = {}; var _sessions = {}; + EventEmitter.call(this); - this.getRealm = function(realm) { - if (_realms.hasOwnProperty(realm)) { - return _realms[realm]; + this.getRealm = function(realmName) { + if (_realms.hasOwnProperty(realmName)) { + return _realms[realmName]; } else { - var r = new Realm(this); - _realms[realm] = r; - return r; + var realm = new Realm(this); + _realms[realmName] = realm; + this.emit('RealmCreated', realm, realmName); + return realm; } }; @@ -36,9 +36,9 @@ function Router() { this.getSession = function(sessionId) { return _sessions[sessionId]; }; - - this.send = function(sessionId, msg, callback) { - if (_sessions.hasOwnProperty(sessionId)) - _sessions[sessionId].send(msg, callback); + this.removeSession = function (session) { + delete _sessions[session.sessionId]; }; } + +module.exports = Router; diff --git a/lib/session.js b/lib/session.js index 3a12e7b..d5cffaf 100644 --- a/lib/session.js +++ b/lib/session.js @@ -1,9 +1,11 @@ 'use strict'; var WAMP = require('./protocol'), - handlers = require('./handlers'); + handlers = require('./handlers'), + inherits = require('util').inherits; module.exports = Session; +inherits(Session, handlers); // requires sender with // sender.send(msg, sessionId, callback) @@ -12,107 +14,29 @@ module.exports = Session; function Session (router, sender, sessionId) { this.realm = null; this.sessionId = sessionId; + handlers.call(this); this.initRealm = function (realm) { this.realm = router.getRealm(realm); }; - this.checkRealm = function (wampCommand, callId) { + this.checkRealm = function (wampCommand, requestId) { if (this.realm) { - return true; + return true; } else { - this.send([ - WAMP.ERROR, - wampCommand, - callId, - {}, - "wamp.error.not_authorized" - ]); - return false; + this.sendError(wampCommand, requestId, "wamp.error.not_authorized"); + return false; } }; - this.invoke = function (regId, invId, args, kwargs) { - var msg = [ - WAMP.INVOCATION, - invId, - regId, - {}, - ]; - // Manage optional parameters args + kwargs - if (undefined !== args) msg.push(args); - if (undefined !== kwargs) msg.push(kwargs); - this.send(msg); - }; this.send = function (msg, callback) { sender.send(msg, sessionId, callback); }; - this.sendError = function (cmd, callId, txt) { - this.send([WAMP.ERROR, cmd, callId, {}, txt]); - }; - this.yield = function (invId, err, args) { - if (err) { - this.sendError(WAMP.CALL, invId, "wamp.error.callee_failure"); - } else { - var msg = [ - WAMP.RESULT, - invId, - {}, - ]; - // Manage optional parameters args + kwargs - for(var i = 0; i < args.length && i < 2; i++) { - msg.push(args[i]); - } - this.send(msg); - } - }; - this.event = function (subscriptionId, publicationId, args, kwargs) { - var msg = [ - WAMP.EVENT, - subscriptionId, - publicationId, - {} - ]; - // Manage optional parameters args + kwargs - if (args !== undefined) { - msg.push(args); - } - if (kwargs !== undefined) { - msg.push(kwargs); - } - this.send(msg); - }; - this.handle = function (msg) { - if (!Array.isArray(msg)) { - this.terminate(1003, "protocol violation"); - return; - } - var type = msg.shift(); - if (!handlers[type]) { - this.terminate(1003, "protocol violation"); - return; - } - handlers[type].call(this, msg); - }; - - this.decodeError = function (err) { - return err; - }; - this.close = function () { - // Graceful termination - var msg = [ - WAMP.GOODBYE, - {}, - "wamp.error.close_realm" - ]; - this.send(msg,function (error) { - sender.close(1000, "Server closed WAMP session"); - }); - }; this.terminate = function (code, reason) { sender.close(code, reason); }; this.cleanup = function () { if (this.realm) { this.realm.cleanup(this); + this.realm = null; } }; } diff --git a/lib/transport.js b/lib/transport.js index b3534df..d9a9ef2 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -13,7 +13,7 @@ var _trace = function (msg, id) { log.trace(trace); }.bind(this); -function wsParser(wsclient, session) { +function wsParser(wsclient, router, session) { wsclient.on('message', function(data) { var msg; @@ -31,6 +31,7 @@ function wsParser(wsclient, session) { wsclient.on('close', function() { log.trace('WebSocket is closed.'); session.cleanup(); + router.removeSession(session); }); } @@ -84,13 +85,9 @@ function Transport(router, options) { _wss.on('connection', function (wsclient) { var sender = new wsSender(wsclient); var session = router.createSession(sender); - var parser = new wsParser(wsclient, session); + var parser = new wsParser(wsclient, router, session); log.trace('New session :' + session.sessionId); - }.bind(this)); - - this.getRealm = function (reaml) { - return router.getRealm(reaml); - }; + }); this.close = function() { _wss.close(); diff --git a/lib/wamp.rt.js b/lib/wamp.rt.js index b815abd..d85cfa7 100644 --- a/lib/wamp.rt.js +++ b/lib/wamp.rt.js @@ -4,8 +4,9 @@ var Router = require('./router'); RouterTransport = function (options) { - Transport.call(this, new Router(), options); + Router.call(this); + _transport = new Transport(this, options); }; -inherits(RouterTransport, Transport); +inherits(RouterTransport, Router); module.exports = RouterTransport; diff --git a/package.json b/package.json index 09f0843..3176006 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,18 @@ { - "name": "wamp.rt", - "version": "0.1.4", + "name": "gyper.wamp", + "version": "0.2.0", "description": "Basic WebSocket Application Messaging Protocol (WAMP) V2 router library", "author": { - "name": "David Corvoysier", - "email": "david.corvoysier@orange.com" + "name": "Anatoly Tsapkov", + "email": "anatoly.tsapkov@gmail.com" }, "contributors": [ - "Dominique ALOË " + "Dominique ALOË ", + "David Corvoysier " ], "repository": { "type": "git", - "url": "https://github.com/Orange-OpenSource/wamp.rt.git" + "url": "https://github.com/kalmyk/wamp.rt.git" }, "dependencies": { "ws": "*", From 04d6d4cdd593cbf8f3b06738c53ddcec633cbce5 Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Sun, 3 Apr 2016 23:53:44 +0300 Subject: [PATCH 15/32] ticket auth added --- democli/backend.js | 2 -- democli/frontend.js | 26 ++++++++++----- examples/basic.js | 7 +++++ lib/handlers.js | 56 ++++++++++++++++----------------- lib/realm.js | 3 +- lib/session.js | 32 ++++++++++++++++++- lib/transport.js | 23 +------------- lib/wamp.rt.js | 24 +++++++++++++- test/auth.js | 77 +++++++++++++++++++++++++++++++++++++++++++++ test/router.js | 6 ++-- 10 files changed, 191 insertions(+), 65 deletions(-) create mode 100644 test/auth.js diff --git a/democli/backend.js b/democli/backend.js index 4d96031..f8bce45 100644 --- a/democli/backend.js +++ b/democli/backend.js @@ -79,8 +79,6 @@ connection.onopen = function (session) { ); setTimeout(function() {console.log("Unregistration");session.unregister(reg);session.unregister(reg2);},20000); - - }; connection.open(); diff --git a/democli/frontend.js b/democli/frontend.js index d8bb4a7..1011202 100644 --- a/democli/frontend.js +++ b/democli/frontend.js @@ -10,16 +10,28 @@ program var connectUrl = 'ws://' + program.ip + ':' + program.port; console.log('connectUrl:', connectUrl); +var user = "joe"; +var key = "joe-secret"; + +// this callback is fired during authentication +function onchallenge (session, method, extra) { + if (method === "ticket") { + return key; + } else { + throw "don't know how to authenticate using '" + method + "'"; + } +} + var connection = new autobahn.Connection({ url: connectUrl, - realm: 'realm1'} -); - -var session = null; + realm: 'realm1', + authmethods: ["ticket", "wampcra"], + authid: user, + onchallenge: onchallenge +}); -connection.onopen = function (new_session) { +connection.onopen = function (session, details) { - session = new_session; session.log("Session open."); var starttime = Date.now(); @@ -127,7 +139,7 @@ connection.onopen = function (new_session) { }; connection.onclose = function (reason, details) { - console.log("connection 1", reason, details); + console.log("close connection:", reason, details); }; connection.open(); diff --git a/examples/basic.js b/examples/basic.js index 2b2d731..8220969 100644 --- a/examples/basic.js +++ b/examples/basic.js @@ -53,6 +53,13 @@ app.on('Publish', onPublish); app.on('RealmCreated', function (realm, realmName) { console.log('new Relm:', realmName); +// realm.isSecured = true; + realm.authenticate = function (secureDetails, secret, callback) { + if (secureDetails.authid+'-secret' === secret) + callback(); + else + callback('authorization_failed'); + } }); var api = app.getRealm('realm1').api(); diff --git a/lib/handlers.js b/lib/handlers.js index 905f178..294ff6b 100644 --- a/lib/handlers.js +++ b/lib/handlers.js @@ -5,6 +5,13 @@ var WAMP = require('./protocol'); var handlers = {}; var Facade = function () { + this.sendWelcome = function (details) { + details.roles = {"dealer": {}}; + this.send([WAMP.WELCOME, this.sessionId, details]); + }; + this.sendChallenge = function (authmethod) { + this.send([WAMP.CHALLENGE, authmethod, {}]); + }; this.sendRegistered = function (requestId, registrationId) { this.send([WAMP.REGISTERED, requestId, registrationId]); }; @@ -67,17 +74,18 @@ var Facade = function () { this.sendError = function (cmd, callId, txt) { this.send([WAMP.ERROR, cmd, callId, {}, txt]); }; - this.sendClose = function () { + this.sendGoodbye = function () { // Graceful termination - // has not been used - var msg = [ - WAMP.GOODBYE, - {}, - "wamp.error.close_realm" - ]; - this.send(msg,function (error) { + var msg = [WAMP.GOODBYE, {}, "wamp.error.goodbye_and_out"]; + this.send(msg, function (error) { + this.terminate(1000, "Server closed WAMP session"); + }.bind(this)); + }; + this.sendAbort = function (reason) { // auth failed + var msg = [WAMP.ABORT, {}, reason]; + this.send(msg, function (error) { this.terminate(1000, "Server closed WAMP session"); - }); + }.bind(this)); }; this.handle = function (msg) { if (!Array.isArray(msg)) { @@ -99,17 +107,16 @@ handlers[WAMP.HELLO] = function(args) { var realmName = args.shift(); var details = args.shift(); if (this.realm === null) { - this.initRealm(realmName); - // Send welcome message - var msg = [ - WAMP.WELCOME, - this.sessionId, - { - "roles": { - "dealer": {} - } - }]; - this.send(msg); + this.hello(realmName, details); + } else { + this.terminate(1002, "protocol violation"); + } +}; + +handlers[WAMP.AUTHENTICATE] = function(args) { + var secret = args.shift(); + if (this.realm === null) { + this.authenticate(secret); } else { this.terminate(1002, "protocol violation"); } @@ -117,14 +124,7 @@ handlers[WAMP.HELLO] = function(args) { handlers[WAMP.GOODBYE] = function(args) { // Ack the goodbye - var msg = [ - WAMP.GOODBYE, - {}, - "wamp.error.goodbye_and_out" - ]; - this.send(msg, function (error) { - this.terminate(1000, "Client closed WAMP session"); - }.bind(this)); + this.sendGoodbye(); }; handlers[WAMP.REGISTER] = function (args) { diff --git a/lib/realm.js b/lib/realm.js index 75c437f..6a242a3 100644 --- a/lib/realm.js +++ b/lib/realm.js @@ -74,13 +74,14 @@ function Realm(router) { var _topics = {}; var _pending = {}; var _api = null; + this.isSecured = false; this.api = function () { if (!_api) { _api = new Api(router, this); } return _api - } + }; // RPC Management this.regrpc = function(session, requestId, procUri, options) { diff --git a/lib/session.js b/lib/session.js index d5cffaf..f374b0b 100644 --- a/lib/session.js +++ b/lib/session.js @@ -12,6 +12,8 @@ inherits(Session, handlers); // sender.close(code, reason) function Session (router, sender, sessionId) { + var secureRealm; + var secureDetails; this.realm = null; this.sessionId = sessionId; handlers.call(this); @@ -19,6 +21,34 @@ function Session (router, sender, sessionId) { this.initRealm = function (realm) { this.realm = router.getRealm(realm); }; + this.hello = function (realm, details) { + secureRealm = router.getRealm(realm); + if (secureRealm.isSecured) { + secureDetails = details; + if (details.hasOwnProperty('authmethods') && details.authmethods.indexOf('ticket') >= 0) { + this.sendChallenge('ticket', {}); + } else { + this.sendAbort("wamp.error.authorization_failed"); + } + } else { + this.realm = secureRealm; + this.sendWelcome({}); + } + }; + this.authenticate = function (secret) { + secureRealm.authenticate(secureDetails, secret, function (err) { + if (err) { + this.sendAbort("wamp.error.authorization_failed"); + } else { + this.realm = secureRealm; + var details = { + authid:secureDetails.authid, + authmethod:"ticket" + }; + this.sendWelcome(details); + } + }.bind(this)); + }; this.checkRealm = function (wampCommand, requestId) { if (this.realm) { return true; @@ -28,7 +58,7 @@ function Session (router, sender, sessionId) { } }; this.send = function (msg, callback) { - sender.send(msg, sessionId, callback); + sender.send(msg, sessionId, callback); }; this.terminate = function (code, reason) { sender.close(code, reason); diff --git a/lib/transport.js b/lib/transport.js index d9a9ef2..25588ba 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -57,29 +57,8 @@ function wsSender(wsclient) { } function Transport(router, options) { - var _options = options || {}; - - if ( !_options.disableProtocolCheck ) { - // We need to verify that the subprotocol is wamp.2.json - var cb = _options.handleProtocols; - _options.handleProtocols = function (protocols, callback) { - var i=0; - var result = false; - while(i < protocols.length && result === false) { - result = (protocols[i] == "wamp.2.json"); - i++; - } - if (result && typeof cb == 'function') { - // If a handleProtocol function was provided by the - // calling script, just filter out the results - cb([protocols[i-1]], callback); - } else { - callback(result, result ? protocols[i-1] : null); - } - }; - } // Instantiate WebSocketServer - var _wss = new WebSocketServer(_options); + var _wss = new WebSocketServer(options); // Create a Session object for the lifetime of each // WebSocket client object _wss.on('connection', function (wsclient) { diff --git a/lib/wamp.rt.js b/lib/wamp.rt.js index d85cfa7..54b0aa8 100644 --- a/lib/wamp.rt.js +++ b/lib/wamp.rt.js @@ -5,7 +5,29 @@ var RouterTransport = function (options) { Router.call(this); - _transport = new Transport(this, options); + + var _options = options || {}; + + if ( !_options.disableProtocolCheck ) { + // We need to verify that the subprotocol is wamp.2.json + var cb = _options.handleProtocols; + _options.handleProtocols = function (protocols, callback) { + var i=0; + var result = false; + while(i < protocols.length && result === false) { + result = (protocols[i] == "wamp.2.json"); + i++; + } + if (result && typeof cb == 'function') { + // If a handleProtocol function was provided by the + // calling script, just filter out the results + cb([protocols[i-1]], callback); + } else { + callback(result, result ? protocols[i-1] : null); + } + }; + } + _transport = new Transport(this, _options); }; inherits(RouterTransport, Router); diff --git a/test/auth.js b/test/auth.js new file mode 100644 index 0000000..c8deb3d --- /dev/null +++ b/test/auth.js @@ -0,0 +1,77 @@ +'use strict'; + +var + chai = require('chai'), + spies = require('chai-spies'), + expect = chai.expect, + WAMP = require('../lib/protocol'), + Router = require('../lib/router'); + +chai.use(spies); + +describe('authenticate', function() { + var + router, + sender, + cli; + + beforeEach(function(){ + sender = {}; + router = new Router(); + + router.on('RealmCreated', function (realm, realmName) { + realm.isSecured = true; + realm.authenticate = function (secureDetails, secret, callback) { + if (secureDetails.authid+'-secret' === secret) + callback(); + else + callback('authorization_failed'); + } + }); + + cli = router.createSession(sender); + }); + + afterEach(function(){ + }) + + it('Joe AUTH:FAIL', function () { + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.CHALLENGE); + expect(msg[1]).to.equal('ticket'); + } + ); + cli.handle([WAMP.HELLO, 'test', {authid: 'joe', authmethods:['ticket']}]); + expect(sender.send).to.have.been.called.once; + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.ABORT); +// callback(); + } + ); + cli.handle([WAMP.AUTHENTICATE, 'incorrect-secret']); + expect(sender.send).to.have.been.called.once; + }); + + it('Joe AUTH:OK', function () { + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.CHALLENGE); + expect(msg[1]).to.equal('ticket'); + } + ); + cli.handle([WAMP.HELLO, 'test', {authid: 'joe', authmethods:['ticket']}]); + expect(sender.send).to.have.been.called.once; + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.WELCOME); + } + ); + cli.handle([WAMP.AUTHENTICATE, 'joe-secret']); + expect(sender.send).to.have.been.called.once; + }); + +}); diff --git a/test/router.js b/test/router.js index 93c4883..fd630aa 100644 --- a/test/router.js +++ b/test/router.js @@ -30,13 +30,13 @@ describe('protocol', function() { expect(msg[0]).to.equal(WAMP.WELCOME); } ); - cli.handle([WAMP.HELLO, 1, 'test', {}]); + cli.handle([WAMP.HELLO, 'test', {}]); expect(sender.send).to.have.been.called.once; - // second hello to logout + // second hello command raises error and disconnects the user sender.send = chai.spy(function (msg, id, callback) {}); sender.close = chai.spy(function (error, reason) {}); - cli.handle([WAMP.HELLO, 1, 'test', {}]); + cli.handle([WAMP.HELLO, 'test', {}]); expect(sender.send).to.not.have.been.called; expect(sender.close).to.have.been.called.once; }); From 364892d6b8b45a95298c99d42a50ba4f35af3101 Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Sun, 3 Apr 2016 23:57:24 +0300 Subject: [PATCH 16/32] read me change --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 033f22b..04a8793 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,9 @@ wamp.rt has been inspired by the following Open Source projects: ## Changes to internal api +2016-04-03: +- ticket auth support added + 2016-03-09: - internal api moved to realm - callrpc method has args & kwargs arguments From 347e41a65596160ca0bad55bde4abdfee882e202 Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Mon, 11 Apr 2016 23:27:49 +0300 Subject: [PATCH 17/32] session optimization --- lib/router.js | 9 +- lib/transport.js | 11 +- lib/wamp.rt.js | 9 +- test/auth.js | 16 +- test/router.js | 648 ++++++++++++++++++++++++----------------------- 5 files changed, 347 insertions(+), 346 deletions(-) diff --git a/lib/router.js b/lib/router.js index 91966c4..a466a02 100644 --- a/lib/router.js +++ b/lib/router.js @@ -1,7 +1,6 @@ 'use strict'; var tools = require('./tools'), - Session = require('./session'), Realm = require('./realm'), util = require('util'), EventEmitter = require('events').EventEmitter; @@ -25,14 +24,12 @@ function Router() { } }; + this.getNewSessionId = function () { + return tools.randomId(); + }; this.registerSession = function(session) { _sessions[session.sessionId] = session; }; - this.createSession = function(sender) { - var session = new Session(this, sender, tools.randomId()); - this.registerSession(session); - return session; - }; this.getSession = function(sessionId) { return _sessions[sessionId]; }; diff --git a/lib/transport.js b/lib/transport.js index 25588ba..0063967 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -11,12 +11,11 @@ var _trace = function (msg, id) { ((typeof id === 'undefined') ? "?" : id) + "] " + msg; log.trace(trace); -}.bind(this); +}; function wsParser(wsclient, router, session) { wsclient.on('message', function(data) { var msg; - try { msg = JSON.parse(data); } catch (e) { @@ -50,20 +49,20 @@ function wsSender(wsclient) { }; this.close = function (code, reason) { - log.trace('Closing WebSocket connection: [' + - code + '] ' + reason); + log.trace('Closing WebSocket connection: [' + code + '] ' + reason); wsclient.close(code, reason); } } -function Transport(router, options) { +function Transport(router, sessionCtrl, SessionClass, options) { // Instantiate WebSocketServer var _wss = new WebSocketServer(options); // Create a Session object for the lifetime of each // WebSocket client object _wss.on('connection', function (wsclient) { var sender = new wsSender(wsclient); - var session = router.createSession(sender); + var session = new SessionClass(router, sender, sessionCtrl.getNewSessionId()) + sessionCtrl.registerSession(session); var parser = new wsParser(wsclient, router, session); log.trace('New session :' + session.sessionId); }); diff --git a/lib/wamp.rt.js b/lib/wamp.rt.js index 54b0aa8..9258b48 100644 --- a/lib/wamp.rt.js +++ b/lib/wamp.rt.js @@ -1,7 +1,8 @@ var - inherits = require('util').inherits, - Transport = require('./transport'), - Router = require('./router'); + inherits = require('util').inherits, + Session = require('./session'), + Transport = require('./transport'), + Router = require('./router'); RouterTransport = function (options) { Router.call(this); @@ -27,7 +28,7 @@ RouterTransport = function (options) { } }; } - _transport = new Transport(this, _options); + _transport = new Transport(this, this, Session, _options); }; inherits(RouterTransport, Router); diff --git a/test/auth.js b/test/auth.js index c8deb3d..839c563 100644 --- a/test/auth.js +++ b/test/auth.js @@ -1,11 +1,12 @@ 'use strict'; var - chai = require('chai'), - spies = require('chai-spies'), - expect = chai.expect, - WAMP = require('../lib/protocol'), - Router = require('../lib/router'); + chai = require('chai'), + spies = require('chai-spies'), + expect = chai.expect, + WAMP = require('../lib/protocol'), + Session = require('../lib/session'), + Router = require('../lib/router'); chai.use(spies); @@ -29,7 +30,8 @@ describe('authenticate', function() { } }); - cli = router.createSession(sender); + cli = new Session(router, sender, router.getNewSessionId()); + router.registerSession(cli); }); afterEach(function(){ @@ -48,7 +50,7 @@ describe('authenticate', function() { sender.send = chai.spy( function (msg, id, callback) { expect(msg[0]).to.equal(WAMP.ABORT); -// callback(); +// callback(); } ); cli.handle([WAMP.AUTHENTICATE, 'incorrect-secret']); diff --git a/test/router.js b/test/router.js index fd630aa..5da2ca0 100644 --- a/test/router.js +++ b/test/router.js @@ -1,11 +1,12 @@ 'use strict'; var - chai = require('chai'), - spies = require('chai-spies'), - expect = chai.expect, - WAMP = require('../lib/protocol'), - Router = require('../lib/router'); + chai = require('chai'), + spies = require('chai-spies'), + expect = chai.expect, + WAMP = require('../lib/protocol'), + Session = require('../lib/session'), + Router = require('../lib/router'); chai.use(spies); @@ -15,326 +16,327 @@ describe('protocol', function() { sender, cli; - beforeEach(function(){ - sender = {}; - router = new Router(); - cli = router.createSession(sender); - }); - - afterEach(function(){ - }) - - it('HELLO/WELCOME', function () { - sender.send = chai.spy( - function (msg, id, callback) { - expect(msg[0]).to.equal(WAMP.WELCOME); - } - ); - cli.handle([WAMP.HELLO, 'test', {}]); - expect(sender.send).to.have.been.called.once; - - // second hello command raises error and disconnects the user - sender.send = chai.spy(function (msg, id, callback) {}); - sender.close = chai.spy(function (error, reason) {}); - cli.handle([WAMP.HELLO, 'test', {}]); - expect(sender.send).to.not.have.been.called; - expect(sender.close).to.have.been.called.once; - }); - - it('GOODBYE', function () { - sender.send = chai.spy( - function (msg, id, callback) { - expect(msg[0]).to.equal(WAMP.GOODBYE); - callback(); - } - ); - sender.close = chai.spy( - function (error) {} - ); - cli.handle([WAMP.GOODBYE]); - expect(sender.send).to.have.been.called.once; - expect(sender.close).to.have.been.called.once; - }); - - it('empty cleanup', function () { - cli.initRealm('test'); - var api = cli.realm.api(); - cli.realm.cleanup(api); - }); - - it('CALL to RPC not exist', function () { - sender.send = chai.spy( - function (msg, id, callback) { - expect(msg[0]).to.equal(WAMP.ERROR); - expect(msg[1]).to.equal(WAMP.CALL); - expect(msg[2]).to.equal(1234); - expect(msg[4]).to.equal('wamp.error.no_such_procedure'); - } - ); - cli.initRealm('test'); - cli.handle([WAMP.CALL, 1234, {}, 'any.function.name', []]); - expect(sender.send).to.have.been.called.once; - }); - - it('cleanup RPC API', function () { - cli.initRealm('test'); - var api = cli.realm.api(); - var procSpy = chai.spy(function() {}); - api.regrpc('func1', procSpy) - expect(cli.realm.cleanupRPC(api)).to.deep.equal(['func1']); - expect(cli.realm.cleanupRPC(api)).to.deep.equal([]); - expect(procSpy).to.not.have.been.called; - }); - - it('CALL to router', function () { - cli.initRealm('test'); - var api = cli.realm.api(); - var procSpy = chai.spy(function(id, args, kwargs) { - api.resrpc(id, undefined, [['result.1','result.2'], {kVal:'kRes'}]); + beforeEach(function(){ + sender = {}; + router = new Router(); + cli = new Session(router, sender, router.getNewSessionId()); + router.registerSession(cli); }); - var regId = api.regrpc('func1', procSpy) - - sender.send = chai.spy( - function (msg, id, callback) { - expect(msg[0]).to.equal(WAMP.RESULT); - expect(msg[1]).to.equal(1234); - expect(msg[3]).to.deep.equal(['result.1','result.2']); - expect(msg[4]).to.deep.equal({kVal:'kRes'}); - } - ); - cli.handle([WAMP.CALL, 1234, {}, 'func1', ['arg1', 'arg2'], {'kArg':'kVal'}]); - expect(procSpy, 'RPC delivered').to.have.been.called.once; - expect(sender.send, 'result delivered').to.have.been.called.once; - expect(api.unregrpc(regId)).to.equal('func1'); - }); - - it('CALL to router with error', function () { - cli.initRealm('test'); - var api = cli.realm.api(); - var callId = null; - var procSpy = chai.spy(function(id, args, kwargs) { - callId = id; + + afterEach(function(){ + }) + + it('HELLO/WELCOME', function () { + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.WELCOME); + } + ); + cli.handle([WAMP.HELLO, 'test', {}]); + expect(sender.send).to.have.been.called.once; + + // second hello command raises error and disconnects the user + sender.send = chai.spy(function (msg, id, callback) {}); + sender.close = chai.spy(function (error, reason) {}); + cli.handle([WAMP.HELLO, 'test', {}]); + expect(sender.send).to.not.have.been.called; + expect(sender.close).to.have.been.called.once; + }); + + it('GOODBYE', function () { + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.GOODBYE); + callback(); + } + ); + sender.close = chai.spy( + function (error) {} + ); + cli.handle([WAMP.GOODBYE]); + expect(sender.send).to.have.been.called.once; + expect(sender.close).to.have.been.called.once; + }); + + it('empty cleanup', function () { + cli.initRealm('test'); + var api = cli.realm.api(); + cli.realm.cleanup(api); + }); + + it('CALL to RPC not exist', function () { + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.ERROR); + expect(msg[1]).to.equal(WAMP.CALL); + expect(msg[2]).to.equal(1234); + expect(msg[4]).to.equal('wamp.error.no_such_procedure'); + } + ); + cli.initRealm('test'); + cli.handle([WAMP.CALL, 1234, {}, 'any.function.name', []]); + expect(sender.send).to.have.been.called.once; }); - api.regrpc('func1', procSpy); - sender.send = chai.spy( - function (msg, id, callback) { - expect(msg[0]).to.equal(WAMP.ERROR); - expect(msg[1]).to.equal(WAMP.CALL); - expect(msg[2]).to.equal(1234); - expect(msg[4]).to.deep.equal('wamp.error.callee_failure'); - } - ); - cli.initRealm('test'); - cli.handle([WAMP.CALL, 1234, {}, 'func1', ['arg1', 'arg2'], {'kArg':'kVal'}]); - api.resrpc(callId, 1, [['result.1','result.2'], {kVal:'kRes'}]); - expect(procSpy).to.have.been.called.once; - expect(sender.send).to.have.been.called.once; - }); - - it('UNREGISTER error', function () { - cli.initRealm('test'); - - sender.send = chai.spy( - function (msg, id, callback) { - expect(msg[0]).to.equal(WAMP.ERROR); - expect(msg[1]).to.equal(WAMP.UNREGISTER); - expect(msg[2]).to.equal(2345); - // 3 options - expect(msg[4]).to.equal('wamp.error.no_such_registration'); - } - ); - cli.handle([WAMP.UNREGISTER, 2345, 1234567890]); - expect(sender.send, 'unregistration confirmed').to.have.been.called.once; - }); - - it('UNREGISTER', function () { - cli.initRealm('test'); - var registrationId = null; - - sender.send = chai.spy( - function (msg, id, callback) { - expect(msg[0]).to.equal(WAMP.REGISTERED); - expect(msg[1]).to.equal(1234); - registrationId = msg[2]; - } - ); - cli.handle([WAMP.REGISTER, 1234, {}, 'func1']); - expect(sender.send, 'registration confirmed').to.have.been.called.once; - - sender.send = chai.spy( - function (msg, id, callback) { - expect(msg[0]).to.equal(WAMP.UNREGISTERED); - expect(msg[1]).to.equal(2345); - } - ); - cli.handle([WAMP.UNREGISTER, 2345, registrationId]); - expect(sender.send, 'unregistration confirmed').to.have.been.called.once; - }); - - it('CALL to remote', function () { - cli.initRealm('test'); - var api = cli.realm.api(); - var registrationId = null; - - sender.send = chai.spy( - function (msg, id, callback) { - expect(msg[0]).to.equal(WAMP.REGISTERED); - expect(msg[1]).to.equal(1234); - registrationId = msg[2]; - } - ); - cli.handle([WAMP.REGISTER, 1234, {}, 'func1']); - expect(sender.send, 'registration confirmed').to.have.been.called.once; - - var callId = null; - sender.send = chai.spy( - function (msg, id, callback) { - expect(msg[0]).to.equal(WAMP.INVOCATION); - callId = msg[1]; - expect(msg[2]).to.equal(registrationId); - // 3 options? - expect(msg[4]).to.deep.equal(['arg.1','arg.2']); - expect(msg[5]).to.deep.equal({kVal:'kRes'}); - } - ); - var callSpy = chai.spy(function(err, args) { - expect(err).to.equal(null); - expect(args).to.deep.equal([['result.1','result.2'],{foo:'bar'}]); + + it('cleanup RPC API', function () { + cli.initRealm('test'); + var api = cli.realm.api(); + var procSpy = chai.spy(function() {}); + api.regrpc('func1', procSpy) + expect(cli.realm.cleanupRPC(api)).to.deep.equal(['func1']); + expect(cli.realm.cleanupRPC(api)).to.deep.equal([]); + expect(procSpy).to.not.have.been.called; }); - api.callrpc('func1', ['arg.1','arg.2'], {kVal:'kRes'}, callSpy); - expect(sender.send, 'invocation received').to.have.been.called.once; - - cli.handle([WAMP.YIELD, callId, {}, ['result.1','result.2'], {foo:'bar'}]); - - expect(callSpy, 'result delivered').to.have.been.called.once; - }); - - it('CALL to remote error', function () { - cli.initRealm('test'); - var api = cli.realm.api(); - - sender.send = function () {}; - cli.handle([WAMP.REGISTER, 1234, {}, 'func1']); - - var callId = null; - sender.send = chai.spy( - function (msg, id, callback) { - callId = msg[1]; - } - ); - var callSpy = chai.spy(function(err, args) { - expect(err).to.be.an('error'); - expect(args).to.deep.equal(['err.detail.1','err.detail.2']); + + it('CALL to router', function () { + cli.initRealm('test'); + var api = cli.realm.api(); + var procSpy = chai.spy(function(id, args, kwargs) { + api.resrpc(id, undefined, [['result.1','result.2'], {kVal:'kRes'}]); + }); + var regId = api.regrpc('func1', procSpy) + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.RESULT); + expect(msg[1]).to.equal(1234); + expect(msg[3]).to.deep.equal(['result.1','result.2']); + expect(msg[4]).to.deep.equal({kVal:'kRes'}); + } + ); + cli.handle([WAMP.CALL, 1234, {}, 'func1', ['arg1', 'arg2'], {'kArg':'kVal'}]); + expect(procSpy, 'RPC delivered').to.have.been.called.once; + expect(sender.send, 'result delivered').to.have.been.called.once; + expect(api.unregrpc(regId)).to.equal('func1'); + }); + + it('CALL to router with error', function () { + cli.initRealm('test'); + var api = cli.realm.api(); + var callId = null; + var procSpy = chai.spy(function(id, args, kwargs) { + callId = id; + }); + api.regrpc('func1', procSpy); + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.ERROR); + expect(msg[1]).to.equal(WAMP.CALL); + expect(msg[2]).to.equal(1234); + expect(msg[4]).to.deep.equal('wamp.error.callee_failure'); + } + ); + cli.initRealm('test'); + cli.handle([WAMP.CALL, 1234, {}, 'func1', ['arg1', 'arg2'], {'kArg':'kVal'}]); + api.resrpc(callId, 1, [['result.1','result.2'], {kVal:'kRes'}]); + expect(procSpy).to.have.been.called.once; + expect(sender.send).to.have.been.called.once; + }); + + it('UNREGISTER error', function () { + cli.initRealm('test'); + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.ERROR); + expect(msg[1]).to.equal(WAMP.UNREGISTER); + expect(msg[2]).to.equal(2345); + // 3 options + expect(msg[4]).to.equal('wamp.error.no_such_registration'); + } + ); + cli.handle([WAMP.UNREGISTER, 2345, 1234567890]); + expect(sender.send, 'unregistration confirmed').to.have.been.called.once; + }); + + it('UNREGISTER', function () { + cli.initRealm('test'); + var registrationId = null; + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.REGISTERED); + expect(msg[1]).to.equal(1234); + registrationId = msg[2]; + } + ); + cli.handle([WAMP.REGISTER, 1234, {}, 'func1']); + expect(sender.send, 'registration confirmed').to.have.been.called.once; + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.UNREGISTERED); + expect(msg[1]).to.equal(2345); + } + ); + cli.handle([WAMP.UNREGISTER, 2345, registrationId]); + expect(sender.send, 'unregistration confirmed').to.have.been.called.once; + }); + + it('CALL to remote', function () { + cli.initRealm('test'); + var api = cli.realm.api(); + var registrationId = null; + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.REGISTERED); + expect(msg[1]).to.equal(1234); + registrationId = msg[2]; + } + ); + cli.handle([WAMP.REGISTER, 1234, {}, 'func1']); + expect(sender.send, 'registration confirmed').to.have.been.called.once; + + var callId = null; + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.INVOCATION); + callId = msg[1]; + expect(msg[2]).to.equal(registrationId); + // 3 options? + expect(msg[4]).to.deep.equal(['arg.1','arg.2']); + expect(msg[5]).to.deep.equal({kVal:'kRes'}); + } + ); + var callSpy = chai.spy(function(err, args) { + expect(err).to.equal(null); + expect(args).to.deep.equal([['result.1','result.2'],{foo:'bar'}]); + }); + api.callrpc('func1', ['arg.1','arg.2'], {kVal:'kRes'}, callSpy); + expect(sender.send, 'invocation received').to.have.been.called.once; + + cli.handle([WAMP.YIELD, callId, {}, ['result.1','result.2'], {foo:'bar'}]); + + expect(callSpy, 'result delivered').to.have.been.called.once; + }); + + it('CALL to remote error', function () { + cli.initRealm('test'); + var api = cli.realm.api(); + + sender.send = function () {}; + cli.handle([WAMP.REGISTER, 1234, {}, 'func1']); + + var callId = null; + sender.send = chai.spy( + function (msg, id, callback) { + callId = msg[1]; + } + ); + var callSpy = chai.spy(function(err, args) { + expect(err).to.be.an('error'); + expect(args).to.deep.equal(['err.detail.1','err.detail.2']); + }); + api.callrpc('func1', ['arg.1','arg.2'], {kVal:'kRes'}, callSpy); + expect(sender.send, 'invocation received').to.have.been.called.once; + + cli.handle([WAMP.ERROR, WAMP.INVOCATION, callId, {}, 'wamp.error.runtime_error', ['err.detail.1','err.detail.2']]); + expect(callSpy, 'error delivered').to.have.been.called.once; + }); + + it('UNSUBSCRIBE error', function () { + cli.initRealm('test'); + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.ERROR); + expect(msg[1]).to.equal(WAMP.UNSUBSCRIBE); + expect(msg[2]).to.equal(2345); + // 3 options + expect(msg[4]).to.equal('wamp.error.no_such_subscription'); + } + ); + cli.handle([WAMP.UNSUBSCRIBE, 2345, 1234567890]); + expect(sender.send, 'unsubscription confirmed').to.have.been.called.once; + }); + + it('UNSUBSCRIBE', function () { + cli.initRealm('test'); + var subscriptionId = null; + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.SUBSCRIBED); + expect(msg[1]).to.equal(1234); + subscriptionId = msg[2]; + } + ); + cli.handle([WAMP.SUBSCRIBE, 1234, {}, 'topic1']); + expect(sender.send, 'subscription confirmed').to.have.been.called.once; + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.UNSUBSCRIBED); + expect(msg[1]).to.equal(2345); + } + ); + cli.handle([WAMP.UNSUBSCRIBE, 2345, subscriptionId]); + expect(sender.send, 'unsubscription confirmed').to.have.been.called.once; + }); + + it('cleanup Topic API', function () { + cli.initRealm('test'); + var api = cli.realm.api(); + var subSpy = chai.spy(function () {}); + api.substopic('topic1', subSpy); + expect(cli.realm.cleanupTopic(api)).to.deep.equal(['topic1']); + expect(cli.realm.cleanupTopic(api)).to.deep.equal([]); + expect(subSpy).to.not.have.been.called; + }); + + it('PUBLISH to remote', function () { + cli.initRealm('test'); + var api = cli.realm.api(); + var subscriptionId = null; + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.SUBSCRIBED); + expect(msg[1]).to.equal(1234); + subscriptionId = msg[2]; + } + ); + cli.handle([WAMP.SUBSCRIBE, 1234, {}, 'topic1']); + expect(sender.send, 'subscription confirmed').to.have.been.called.once; + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.EVENT); + expect(msg[1]).to.equal(subscriptionId); + // 2 published message Id + // 3 options? + expect(msg[4]).to.deep.equal(['arg.1','arg.2']); + expect(msg[5]).to.deep.equal({foo:'bar'}); + } + ); + api.publish('topic1', ['arg.1','arg.2'], {foo:'bar'}); + expect(sender.send, 'publication received').to.have.been.called.once; + }); + + it('SUBSCRIBE to remote', function () { + cli.initRealm('test'); + var api = cli.realm.api(); + var subSpy = chai.spy( + function (publicationId, args, kwargs) { + expect(args).to.deep.equal(['arg.1','arg.2']); + expect(kwargs).to.deep.equal({foo:'bar'}); + } + ); + var subId = api.substopic('topic1', subSpy); + + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.PUBLISHED); + expect(msg[1]).to.equal(2345); + } + ); + cli.handle([WAMP.PUBLISH, 1234, {}, "topic1", ['arg.1','arg.2'],{foo:'bar'}]); + expect(sender.send, 'published').to.not.have.been.called; + cli.handle([WAMP.PUBLISH, 2345, {"acknowledge":true}, "topic1", ['arg.1','arg.2'],{foo:'bar'}]); + expect(sender.send, 'published').to.have.been.called.once; + + expect(subSpy, 'publication done').to.have.been.called.twice; + expect(api.unsubstopic(subId)).to.equal('topic1'); }); - api.callrpc('func1', ['arg.1','arg.2'], {kVal:'kRes'}, callSpy); - expect(sender.send, 'invocation received').to.have.been.called.once; - - cli.handle([WAMP.ERROR, WAMP.INVOCATION, callId, {}, 'wamp.error.runtime_error', ['err.detail.1','err.detail.2']]); - expect(callSpy, 'error delivered').to.have.been.called.once; - }); - - it('UNSUBSCRIBE error', function () { - cli.initRealm('test'); - - sender.send = chai.spy( - function (msg, id, callback) { - expect(msg[0]).to.equal(WAMP.ERROR); - expect(msg[1]).to.equal(WAMP.UNSUBSCRIBE); - expect(msg[2]).to.equal(2345); - // 3 options - expect(msg[4]).to.equal('wamp.error.no_such_subscription'); - } - ); - cli.handle([WAMP.UNSUBSCRIBE, 2345, 1234567890]); - expect(sender.send, 'unsubscription confirmed').to.have.been.called.once; - }); - - it('UNSUBSCRIBE', function () { - cli.initRealm('test'); - var subscriptionId = null; - - sender.send = chai.spy( - function (msg, id, callback) { - expect(msg[0]).to.equal(WAMP.SUBSCRIBED); - expect(msg[1]).to.equal(1234); - subscriptionId = msg[2]; - } - ); - cli.handle([WAMP.SUBSCRIBE, 1234, {}, 'topic1']); - expect(sender.send, 'subscription confirmed').to.have.been.called.once; - - sender.send = chai.spy( - function (msg, id, callback) { - expect(msg[0]).to.equal(WAMP.UNSUBSCRIBED); - expect(msg[1]).to.equal(2345); - } - ); - cli.handle([WAMP.UNSUBSCRIBE, 2345, subscriptionId]); - expect(sender.send, 'unsubscription confirmed').to.have.been.called.once; - }); - - it('cleanup Topic API', function () { - cli.initRealm('test'); - var api = cli.realm.api(); - var subSpy = chai.spy(function () {}); - api.substopic('topic1', subSpy); - expect(cli.realm.cleanupTopic(api)).to.deep.equal(['topic1']); - expect(cli.realm.cleanupTopic(api)).to.deep.equal([]); - expect(subSpy).to.not.have.been.called; - }); - - it('PUBLISH to remote', function () { - cli.initRealm('test'); - var api = cli.realm.api(); - var subscriptionId = null; - - sender.send = chai.spy( - function (msg, id, callback) { - expect(msg[0]).to.equal(WAMP.SUBSCRIBED); - expect(msg[1]).to.equal(1234); - subscriptionId = msg[2]; - } - ); - cli.handle([WAMP.SUBSCRIBE, 1234, {}, 'topic1']); - expect(sender.send, 'subscription confirmed').to.have.been.called.once; - - sender.send = chai.spy( - function (msg, id, callback) { - expect(msg[0]).to.equal(WAMP.EVENT); - expect(msg[1]).to.equal(subscriptionId); - // 2 published message Id - // 3 options? - expect(msg[4]).to.deep.equal(['arg.1','arg.2']); - expect(msg[5]).to.deep.equal({foo:'bar'}); - } - ); - api.publish('topic1', ['arg.1','arg.2'], {foo:'bar'}); - expect(sender.send, 'publication received').to.have.been.called.once; - }); - - it('SUBSCRIBE to remote', function () { - cli.initRealm('test'); - var api = cli.realm.api(); - var subSpy = chai.spy( - function (publicationId, args, kwargs) { - expect(args).to.deep.equal(['arg.1','arg.2']); - expect(kwargs).to.deep.equal({foo:'bar'}); - } - ); - var subId = api.substopic('topic1', subSpy); - - sender.send = chai.spy( - function (msg, id, callback) { - expect(msg[0]).to.equal(WAMP.PUBLISHED); - expect(msg[1]).to.equal(2345); - } - ); - cli.handle([WAMP.PUBLISH, 1234, {}, "topic1", ['arg.1','arg.2'],{foo:'bar'}]); - expect(sender.send, 'published').to.not.have.been.called; - cli.handle([WAMP.PUBLISH, 2345, {"acknowledge":true}, "topic1", ['arg.1','arg.2'],{foo:'bar'}]); - expect(sender.send, 'published').to.have.been.called.once; - - expect(subSpy, 'publication done').to.have.been.called.twice; - expect(api.unsubstopic(subId)).to.equal('topic1'); - }); }); From aef3ee838b809bcb8555f1389c1edef7220f6d0d Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Sat, 16 Apr 2016 15:59:36 +0300 Subject: [PATCH 18/32] tests cleaning --- lib/session.js | 7 +--- test/{router.js => realm.js} | 73 +++++------------------------------- test/session.js | 60 +++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 68 deletions(-) rename test/{router.js => realm.js} (83%) create mode 100644 test/session.js diff --git a/lib/session.js b/lib/session.js index f374b0b..e20d07f 100644 --- a/lib/session.js +++ b/lib/session.js @@ -18,11 +18,8 @@ function Session (router, sender, sessionId) { this.sessionId = sessionId; handlers.call(this); - this.initRealm = function (realm) { - this.realm = router.getRealm(realm); - }; - this.hello = function (realm, details) { - secureRealm = router.getRealm(realm); + this.hello = function (realmName, details) { + secureRealm = router.getRealm(realmName); if (secureRealm.isSecured) { secureDetails = details; if (details.hasOwnProperty('authmethods') && details.authmethods.indexOf('ticket') >= 0) { diff --git a/test/router.js b/test/realm.js similarity index 83% rename from test/router.js rename to test/realm.js index 5da2ca0..15138e8 100644 --- a/test/router.js +++ b/test/realm.js @@ -5,6 +5,7 @@ var spies = require('chai-spies'), expect = chai.expect, WAMP = require('../lib/protocol'), + Realm = require('../lib/realm'), Session = require('../lib/session'), Router = require('../lib/router'); @@ -13,55 +14,26 @@ chai.use(spies); describe('protocol', function() { var router, + realm, sender, - cli; + cli, + api; beforeEach(function(){ sender = {}; router = new Router(); + realm = new Realm(router); + api = realm.api(); cli = new Session(router, sender, router.getNewSessionId()); router.registerSession(cli); + cli.realm = realm; }); afterEach(function(){ }) - it('HELLO/WELCOME', function () { - sender.send = chai.spy( - function (msg, id, callback) { - expect(msg[0]).to.equal(WAMP.WELCOME); - } - ); - cli.handle([WAMP.HELLO, 'test', {}]); - expect(sender.send).to.have.been.called.once; - - // second hello command raises error and disconnects the user - sender.send = chai.spy(function (msg, id, callback) {}); - sender.close = chai.spy(function (error, reason) {}); - cli.handle([WAMP.HELLO, 'test', {}]); - expect(sender.send).to.not.have.been.called; - expect(sender.close).to.have.been.called.once; - }); - - it('GOODBYE', function () { - sender.send = chai.spy( - function (msg, id, callback) { - expect(msg[0]).to.equal(WAMP.GOODBYE); - callback(); - } - ); - sender.close = chai.spy( - function (error) {} - ); - cli.handle([WAMP.GOODBYE]); - expect(sender.send).to.have.been.called.once; - expect(sender.close).to.have.been.called.once; - }); - it('empty cleanup', function () { - cli.initRealm('test'); - var api = cli.realm.api(); - cli.realm.cleanup(api); + realm.cleanup(api); }); it('CALL to RPC not exist', function () { @@ -73,24 +45,19 @@ describe('protocol', function() { expect(msg[4]).to.equal('wamp.error.no_such_procedure'); } ); - cli.initRealm('test'); cli.handle([WAMP.CALL, 1234, {}, 'any.function.name', []]); expect(sender.send).to.have.been.called.once; }); it('cleanup RPC API', function () { - cli.initRealm('test'); - var api = cli.realm.api(); var procSpy = chai.spy(function() {}); api.regrpc('func1', procSpy) - expect(cli.realm.cleanupRPC(api)).to.deep.equal(['func1']); - expect(cli.realm.cleanupRPC(api)).to.deep.equal([]); + expect(realm.cleanupRPC(api)).to.deep.equal(['func1']); + expect(realm.cleanupRPC(api)).to.deep.equal([]); expect(procSpy).to.not.have.been.called; }); it('CALL to router', function () { - cli.initRealm('test'); - var api = cli.realm.api(); var procSpy = chai.spy(function(id, args, kwargs) { api.resrpc(id, undefined, [['result.1','result.2'], {kVal:'kRes'}]); }); @@ -111,8 +78,6 @@ describe('protocol', function() { }); it('CALL to router with error', function () { - cli.initRealm('test'); - var api = cli.realm.api(); var callId = null; var procSpy = chai.spy(function(id, args, kwargs) { callId = id; @@ -126,7 +91,6 @@ describe('protocol', function() { expect(msg[4]).to.deep.equal('wamp.error.callee_failure'); } ); - cli.initRealm('test'); cli.handle([WAMP.CALL, 1234, {}, 'func1', ['arg1', 'arg2'], {'kArg':'kVal'}]); api.resrpc(callId, 1, [['result.1','result.2'], {kVal:'kRes'}]); expect(procSpy).to.have.been.called.once; @@ -134,8 +98,6 @@ describe('protocol', function() { }); it('UNREGISTER error', function () { - cli.initRealm('test'); - sender.send = chai.spy( function (msg, id, callback) { expect(msg[0]).to.equal(WAMP.ERROR); @@ -150,7 +112,6 @@ describe('protocol', function() { }); it('UNREGISTER', function () { - cli.initRealm('test'); var registrationId = null; sender.send = chai.spy( @@ -174,8 +135,6 @@ describe('protocol', function() { }); it('CALL to remote', function () { - cli.initRealm('test'); - var api = cli.realm.api(); var registrationId = null; sender.send = chai.spy( @@ -212,9 +171,6 @@ describe('protocol', function() { }); it('CALL to remote error', function () { - cli.initRealm('test'); - var api = cli.realm.api(); - sender.send = function () {}; cli.handle([WAMP.REGISTER, 1234, {}, 'func1']); @@ -236,8 +192,6 @@ describe('protocol', function() { }); it('UNSUBSCRIBE error', function () { - cli.initRealm('test'); - sender.send = chai.spy( function (msg, id, callback) { expect(msg[0]).to.equal(WAMP.ERROR); @@ -252,7 +206,6 @@ describe('protocol', function() { }); it('UNSUBSCRIBE', function () { - cli.initRealm('test'); var subscriptionId = null; sender.send = chai.spy( @@ -276,8 +229,6 @@ describe('protocol', function() { }); it('cleanup Topic API', function () { - cli.initRealm('test'); - var api = cli.realm.api(); var subSpy = chai.spy(function () {}); api.substopic('topic1', subSpy); expect(cli.realm.cleanupTopic(api)).to.deep.equal(['topic1']); @@ -286,8 +237,6 @@ describe('protocol', function() { }); it('PUBLISH to remote', function () { - cli.initRealm('test'); - var api = cli.realm.api(); var subscriptionId = null; sender.send = chai.spy( @@ -315,8 +264,6 @@ describe('protocol', function() { }); it('SUBSCRIBE to remote', function () { - cli.initRealm('test'); - var api = cli.realm.api(); var subSpy = chai.spy( function (publicationId, args, kwargs) { expect(args).to.deep.equal(['arg.1','arg.2']); diff --git a/test/session.js b/test/session.js new file mode 100644 index 0000000..382fffa --- /dev/null +++ b/test/session.js @@ -0,0 +1,60 @@ +'use strict'; + +var + chai = require('chai'), + spies = require('chai-spies'), + expect = chai.expect, + WAMP = require('../lib/protocol'), + Session = require('../lib/session'), + Router = require('../lib/router'); + +chai.use(spies); + +describe('protocol', function() { + var + router, + sender, + cli; + + beforeEach(function(){ + sender = {}; + router = new Router(); + cli = new Session(router, sender, router.getNewSessionId()); + router.registerSession(cli); + }); + + afterEach(function(){ + }) + + it('HELLO/WELCOME', function () { + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.WELCOME); + } + ); + cli.handle([WAMP.HELLO, 'test', {}]); + expect(sender.send).to.have.been.called.once; + + // second hello command raises error and disconnects the user + sender.send = chai.spy(function (msg, id, callback) {}); + sender.close = chai.spy(function (error, reason) {}); + cli.handle([WAMP.HELLO, 'test', {}]); + expect(sender.send).to.not.have.been.called; + expect(sender.close).to.have.been.called.once; + }); + + it('GOODBYE', function () { + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.GOODBYE); + callback(); + } + ); + sender.close = chai.spy( + function (error) {} + ); + cli.handle([WAMP.GOODBYE]); + expect(sender.send).to.have.been.called.once; + expect(sender.close).to.have.been.called.once; + }); +}); From 47284c8c317aab173b79b7bdf82aa93f045f0889 Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Sat, 16 Apr 2016 21:28:58 +0300 Subject: [PATCH 19/32] dedicated auth object added --- examples/basic.js | 33 ++++++++++----------------------- examples/with_auth.js | 42 ++++++++++++++++++++++++++++++++++++++++++ lib/handlers.js | 8 ++++++++ lib/session.js | 24 ++++++++++-------------- lib/transport.js | 5 +++-- lib/wamp.rt.js | 4 ++-- test/auth.js | 22 +++++++++++----------- test/session.js | 26 ++++++++++++++++++++++++++ 8 files changed, 112 insertions(+), 52 deletions(-) create mode 100644 examples/with_auth.js diff --git a/examples/basic.js b/examples/basic.js index 8220969..a817cb3 100644 --- a/examples/basic.js +++ b/examples/basic.js @@ -19,18 +19,6 @@ program console.log('Listening port:', program.port); -function onRPCRegistered(realm, uri) { - console.log('onRPCRegistered RPC registered', uri); -} - -function onRPCUnregistered(realm, uri) { - console.log('onRPCUnregistered RPC unregistered', uri); -} - -function onPublish(realm, topicUri, args) { - console.log('onPublish Publish', topicUri, args); -} - // // WebSocket server // @@ -47,19 +35,17 @@ var app = new WampRouter( } ); -app.on('RPCRegistered', onRPCRegistered); -app.on('RPCUnregistered', onRPCUnregistered); -app.on('Publish', onPublish); - +app.on('RPCRegistered', function (realm, uri) { + console.log('onRPCRegistered RPC registered', uri); +}); +app.on('RPCUnregistered', function (realm, uri) { + console.log('onRPCUnregistered RPC unregistered', uri); +}); +app.on('Publish', function onPublish(realm, topicUri, args) { + console.log('onPublish Publish', topicUri, args); +}); app.on('RealmCreated', function (realm, realmName) { console.log('new Relm:', realmName); -// realm.isSecured = true; - realm.authenticate = function (secureDetails, secret, callback) { - if (secureDetails.authid+'-secret' === secret) - callback(); - else - callback('authorization_failed'); - } }); var api = app.getRealm('realm1').api(); @@ -67,3 +53,4 @@ api.regrpc('wamp.rt.foo', function(id, args, kwargs) { console.log('called with ', args, kwargs); api.resrpc(id, null /* no error */, [["bar", "bar2"], {"key1": "bar1", "key2": "bar2"}]); }); + diff --git a/examples/with_auth.js b/examples/with_auth.js new file mode 100644 index 0000000..9952854 --- /dev/null +++ b/examples/with_auth.js @@ -0,0 +1,42 @@ +// +// This is authenticate router example +// + +WAMPRT_TRACE = true; + +var WampRouter = require('../lib/wamp.rt'); +var program = require('commander'); + +program + .option('-p, --port ', 'Server IP port', 9000) + .parse(process.argv); + +console.log('Listening port:', program.port); + +var Auth = function () { + this.authenticate = function (realmName, secureDetails, secret, callback) { + console.log('AUTH:', secureDetails, secret); + if (secureDetails.authid+'-secret' === secret) + callback(); + else + callback('authorization_failed'); + } +}; + +// +// WebSocket server +// +var app = new WampRouter( + { port: program.port, + // The router will select the appropriate protocol, + // but we can still deny the connection + // TODO: this should be the other way round, really ... + handleProtocols: function(protocols,cb) { + console.log(protocols); + cb(true,protocols[0]); + //cb(false); + } + }, + new Auth +); + diff --git a/lib/handlers.js b/lib/handlers.js index 294ff6b..ad8915b 100644 --- a/lib/handlers.js +++ b/lib/handlers.js @@ -5,6 +5,14 @@ var WAMP = require('./protocol'); var handlers = {}; var Facade = function () { + this.checkRealm = function (wampCommand, requestId) { + if (this.realm) { + return true; + } else { + this.sendError(wampCommand, requestId, "wamp.error.not_authorized"); + return false; + } + }; this.sendWelcome = function (details) { details.roles = {"dealer": {}}; this.send([WAMP.WELCOME, this.sessionId, details]); diff --git a/lib/session.js b/lib/session.js index e20d07f..d858ff6 100644 --- a/lib/session.js +++ b/lib/session.js @@ -12,32 +12,36 @@ inherits(Session, handlers); // sender.close(code, reason) function Session (router, sender, sessionId) { - var secureRealm; + var secureRealmName; var secureDetails; + var auth = null; this.realm = null; this.sessionId = sessionId; handlers.call(this); + this.setAuth = function (inAuth) { + auth = inAuth; + }; this.hello = function (realmName, details) { - secureRealm = router.getRealm(realmName); - if (secureRealm.isSecured) { + if (auth) { secureDetails = details; + secureRealmName = realmName; if (details.hasOwnProperty('authmethods') && details.authmethods.indexOf('ticket') >= 0) { this.sendChallenge('ticket', {}); } else { this.sendAbort("wamp.error.authorization_failed"); } } else { - this.realm = secureRealm; + this.realm = router.getRealm(realmName); this.sendWelcome({}); } }; this.authenticate = function (secret) { - secureRealm.authenticate(secureDetails, secret, function (err) { + auth.authenticate(secureRealmName, secureDetails, secret, function (err) { if (err) { this.sendAbort("wamp.error.authorization_failed"); } else { - this.realm = secureRealm; + this.realm = router.getRealm(secureRealmName); var details = { authid:secureDetails.authid, authmethod:"ticket" @@ -46,14 +50,6 @@ function Session (router, sender, sessionId) { } }.bind(this)); }; - this.checkRealm = function (wampCommand, requestId) { - if (this.realm) { - return true; - } else { - this.sendError(wampCommand, requestId, "wamp.error.not_authorized"); - return false; - } - }; this.send = function (msg, callback) { sender.send(msg, sessionId, callback); }; diff --git a/lib/transport.js b/lib/transport.js index 0063967..546f887 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -54,14 +54,15 @@ function wsSender(wsclient) { } } -function Transport(router, sessionCtrl, SessionClass, options) { +function Transport(router, auth, sessionCtrl, SessionClass, options) { // Instantiate WebSocketServer var _wss = new WebSocketServer(options); // Create a Session object for the lifetime of each // WebSocket client object _wss.on('connection', function (wsclient) { var sender = new wsSender(wsclient); - var session = new SessionClass(router, sender, sessionCtrl.getNewSessionId()) + var session = new SessionClass(router, sender, sessionCtrl.getNewSessionId()); + session.setAuth(auth); sessionCtrl.registerSession(session); var parser = new wsParser(wsclient, router, session); log.trace('New session :' + session.sessionId); diff --git a/lib/wamp.rt.js b/lib/wamp.rt.js index 9258b48..31fd5d1 100644 --- a/lib/wamp.rt.js +++ b/lib/wamp.rt.js @@ -4,7 +4,7 @@ var Transport = require('./transport'), Router = require('./router'); -RouterTransport = function (options) { +RouterTransport = function (options, auth) { Router.call(this); var _options = options || {}; @@ -28,7 +28,7 @@ RouterTransport = function (options) { } }; } - _transport = new Transport(this, this, Session, _options); + _transport = new Transport(this, auth, this, Session, _options); }; inherits(RouterTransport, Router); diff --git a/test/auth.js b/test/auth.js index 839c563..3e3c461 100644 --- a/test/auth.js +++ b/test/auth.js @@ -10,6 +10,15 @@ var chai.use(spies); +var Auth = function () { + this.authenticate = function (realmName, secureDetails, secret, callback) { + if (realmName+'-'+secureDetails.authid+'-secret' === secret) + callback(); + else + callback('authorization_failed'); + } +}; + describe('authenticate', function() { var router, @@ -20,17 +29,8 @@ describe('authenticate', function() { sender = {}; router = new Router(); - router.on('RealmCreated', function (realm, realmName) { - realm.isSecured = true; - realm.authenticate = function (secureDetails, secret, callback) { - if (secureDetails.authid+'-secret' === secret) - callback(); - else - callback('authorization_failed'); - } - }); - cli = new Session(router, sender, router.getNewSessionId()); + cli.setAuth(new Auth); router.registerSession(cli); }); @@ -72,7 +72,7 @@ describe('authenticate', function() { expect(msg[0]).to.equal(WAMP.WELCOME); } ); - cli.handle([WAMP.AUTHENTICATE, 'joe-secret']); + cli.handle([WAMP.AUTHENTICATE, 'test-joe-secret']); expect(sender.send).to.have.been.called.once; }); diff --git a/test/session.js b/test/session.js index 382fffa..de4c443 100644 --- a/test/session.js +++ b/test/session.js @@ -57,4 +57,30 @@ describe('protocol', function() { expect(sender.send).to.have.been.called.once; expect(sender.close).to.have.been.called.once; }); + + it('CALL to no realm RPC', function () { + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.ERROR); + expect(msg[1]).to.equal(WAMP.CALL); + expect(msg[2]).to.equal(1234); + expect(msg[4]).to.equal('wamp.error.not_authorized'); + } + ); + cli.handle([WAMP.CALL, 1234, {}, 'any.function.name', []]); + expect(sender.send).to.have.been.called.once; + }); + + it('REGISTER to no realm', function () { + sender.send = chai.spy( + function (msg, id, callback) { + expect(msg[0]).to.equal(WAMP.ERROR); + expect(msg[1]).to.equal(WAMP.REGISTER); + expect(msg[2]).to.equal(1234); + expect(msg[4]).to.equal('wamp.error.not_authorized'); + } + ); + cli.handle([WAMP.REGISTER, 1234, {}, 'func1']); + expect(sender.send, 'registration failed').to.have.been.called.once; + }); }); From 87c409335dbaebaefa048d536429290fdcc74e8b Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Tue, 19 Apr 2016 01:09:15 +0300 Subject: [PATCH 20/32] small optimization --- lib/transport.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/transport.js b/lib/transport.js index 546f887..9e80353 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -35,14 +35,14 @@ function wsParser(wsclient, router, session) { } function wsSender(wsclient) { + var defaultCallback = function (error) { + if (error) { + log.trace("Failed to send message: " + error); + this.close(1011, "Unexpected error"); + } + }.bind(this); this.send = function (msg, id, callback) { var data = JSON.stringify(msg); - var defaultCallback = function (error) { - if (error) { - log.trace("Failed to send message: " + error); - this.close(1011, "Unexpected error"); - } - }.bind(this); _trace('TX > ' + data, id); wsclient.send(data, (typeof callback === 'function') ? callback : defaultCallback); From d98c9c3f785a41b9e8ef5373423688c50f0ee894 Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Sun, 8 May 2016 14:35:02 +0300 Subject: [PATCH 21/32] jshint parsed --- lib/handlers.js | 3 ++- lib/realm.js | 5 +++-- lib/router.js | 1 + lib/session.js | 1 + lib/transport.js | 12 +++++++----- package.json | 5 +++++ test/auth.js | 9 ++++++--- test/realm.js | 9 ++++++--- test/session.js | 15 +++++++++------ 9 files changed, 40 insertions(+), 20 deletions(-) diff --git a/lib/handlers.js b/lib/handlers.js index ad8915b..7e9fd7a 100644 --- a/lib/handlers.js +++ b/lib/handlers.js @@ -1,3 +1,4 @@ +/*jshint node: true */ 'use strict'; var WAMP = require('./protocol'); @@ -204,6 +205,6 @@ handlers[WAMP.ERROR] = function(msg) { // An invocation failed if (this.checkRealm(WAMP.ERROR, requestId) && requestType === WAMP.INVOCATION) this.realm.resrpc(this, requestId, new Error(details), args); -} +}; module.exports = Facade; diff --git a/lib/realm.js b/lib/realm.js index 6a242a3..8b19550 100644 --- a/lib/realm.js +++ b/lib/realm.js @@ -1,3 +1,4 @@ +/*jshint node: true */ 'use strict'; var WAMP = require('./protocol'), @@ -64,7 +65,7 @@ function Api(router, realm) { } }; this.send = function (msg) {}; -}; +} function Realm(router) { var _sessRPC = {}; @@ -80,7 +81,7 @@ function Realm(router) { if (!_api) { _api = new Api(router, this); } - return _api + return _api; }; // RPC Management diff --git a/lib/router.js b/lib/router.js index a466a02..4ef18b5 100644 --- a/lib/router.js +++ b/lib/router.js @@ -1,3 +1,4 @@ +/*jshint node: true */ 'use strict'; var tools = require('./tools'), diff --git a/lib/session.js b/lib/session.js index d858ff6..f261ac8 100644 --- a/lib/session.js +++ b/lib/session.js @@ -1,3 +1,4 @@ +/*jshint node: true */ 'use strict'; var WAMP = require('./protocol'), diff --git a/lib/transport.js b/lib/transport.js index 9e80353..6ce9904 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -1,3 +1,4 @@ +/*jshint node: true */ 'use strict'; var @@ -13,7 +14,7 @@ var _trace = function (msg, id) { log.trace(trace); }; -function wsParser(wsclient, router, session) { +function WsParser(wsclient, router, session) { wsclient.on('message', function(data) { var msg; try { @@ -34,13 +35,14 @@ function wsParser(wsclient, router, session) { }); } -function wsSender(wsclient) { +function WsSender(wsclient) { var defaultCallback = function (error) { if (error) { log.trace("Failed to send message: " + error); this.close(1011, "Unexpected error"); } }.bind(this); + this.send = function (msg, id, callback) { var data = JSON.stringify(msg); _trace('TX > ' + data, id); @@ -51,7 +53,7 @@ function wsSender(wsclient) { this.close = function (code, reason) { log.trace('Closing WebSocket connection: [' + code + '] ' + reason); wsclient.close(code, reason); - } + }; } function Transport(router, auth, sessionCtrl, SessionClass, options) { @@ -60,11 +62,11 @@ function Transport(router, auth, sessionCtrl, SessionClass, options) { // Create a Session object for the lifetime of each // WebSocket client object _wss.on('connection', function (wsclient) { - var sender = new wsSender(wsclient); + var sender = new WsSender(wsclient); var session = new SessionClass(router, sender, sessionCtrl.getNewSessionId()); session.setAuth(auth); sessionCtrl.registerSession(session); - var parser = new wsParser(wsclient, router, session); + var parser = new WsParser(wsclient, router, session); log.trace('New session :' + session.sessionId); }); diff --git a/package.json b/package.json index 3176006..4c828cb 100644 --- a/package.json +++ b/package.json @@ -28,5 +28,10 @@ "main": "lib/wamp.rt", "engines": { "node": ">=0.10.0" + }, + "scripts": { + "mocha-node-test": "mocha", + "lint": "jshint lib/*.js test/*.js", + "test": "npm run-script lint && npm run-script mocha-node-test" } } diff --git a/test/auth.js b/test/auth.js index 3e3c461..6f867fc 100644 --- a/test/auth.js +++ b/test/auth.js @@ -1,3 +1,6 @@ +/*jshint mocha: true */ +/*jshint node: true */ +/*jshint expr: true */ 'use strict'; var @@ -16,7 +19,7 @@ var Auth = function () { callback(); else callback('authorization_failed'); - } + }; }; describe('authenticate', function() { @@ -30,12 +33,12 @@ describe('authenticate', function() { router = new Router(); cli = new Session(router, sender, router.getNewSessionId()); - cli.setAuth(new Auth); + cli.setAuth(new Auth()); router.registerSession(cli); }); afterEach(function(){ - }) + }); it('Joe AUTH:FAIL', function () { sender.send = chai.spy( diff --git a/test/realm.js b/test/realm.js index 15138e8..583aa4f 100644 --- a/test/realm.js +++ b/test/realm.js @@ -1,3 +1,6 @@ +/*jshint mocha: true */ +/*jshint node: true */ +/*jshint expr: true */ 'use strict'; var @@ -30,7 +33,7 @@ describe('protocol', function() { }); afterEach(function(){ - }) + }); it('empty cleanup', function () { realm.cleanup(api); @@ -51,7 +54,7 @@ describe('protocol', function() { it('cleanup RPC API', function () { var procSpy = chai.spy(function() {}); - api.regrpc('func1', procSpy) + api.regrpc('func1', procSpy); expect(realm.cleanupRPC(api)).to.deep.equal(['func1']); expect(realm.cleanupRPC(api)).to.deep.equal([]); expect(procSpy).to.not.have.been.called; @@ -61,7 +64,7 @@ describe('protocol', function() { var procSpy = chai.spy(function(id, args, kwargs) { api.resrpc(id, undefined, [['result.1','result.2'], {kVal:'kRes'}]); }); - var regId = api.regrpc('func1', procSpy) + var regId = api.regrpc('func1', procSpy); sender.send = chai.spy( function (msg, id, callback) { diff --git a/test/session.js b/test/session.js index de4c443..97615d2 100644 --- a/test/session.js +++ b/test/session.js @@ -1,3 +1,6 @@ +/*jshint mocha: true */ +/*jshint node: true */ +/*jshint expr: true */ 'use strict'; var @@ -10,11 +13,11 @@ var chai.use(spies); -describe('protocol', function() { - var - router, - sender, - cli; +describe('session', function() { + var + router, + sender, + cli; beforeEach(function(){ sender = {}; @@ -24,7 +27,7 @@ describe('protocol', function() { }); afterEach(function(){ - }) + }); it('HELLO/WELCOME', function () { sender.send = chai.spy( From a858d380b1bf8eb8fa532a1461475efd7537ab42 Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Tue, 31 May 2016 01:45:08 +0300 Subject: [PATCH 22/32] callback to get realm --- examples/basic.js | 11 ++++++----- examples/with_auth.js | 5 ++--- lib/realm.js | 3 ++- lib/router.js | 6 +++--- lib/session.js | 31 ++++++++++++++++++------------- lib/transport.js | 2 +- test/auth.js | 2 +- 7 files changed, 33 insertions(+), 27 deletions(-) diff --git a/examples/basic.js b/examples/basic.js index a817cb3..60f4e0c 100644 --- a/examples/basic.js +++ b/examples/basic.js @@ -48,9 +48,10 @@ app.on('RealmCreated', function (realm, realmName) { console.log('new Relm:', realmName); }); -var api = app.getRealm('realm1').api(); -api.regrpc('wamp.rt.foo', function(id, args, kwargs) { - console.log('called with ', args, kwargs); - api.resrpc(id, null /* no error */, [["bar", "bar2"], {"key1": "bar1", "key2": "bar2"}]); +app.getRealm('realm1', function (realm) { + var api = realm.api(); + api.regrpc('wamp.rt.foo', function(id, args, kwargs) { + console.log('called with ', args, kwargs); + api.resrpc(id, null /* no error */, [["bar", "bar2"], {"key1": "bar1", "key2": "bar2"}]); + }); }); - diff --git a/examples/with_auth.js b/examples/with_auth.js index 9952854..11476ad 100644 --- a/examples/with_auth.js +++ b/examples/with_auth.js @@ -20,7 +20,7 @@ var Auth = function () { callback(); else callback('authorization_failed'); - } + }; }; // @@ -37,6 +37,5 @@ var app = new WampRouter( //cb(false); } }, - new Auth + new Auth() ); - diff --git a/lib/realm.js b/lib/realm.js index 8b19550..4515eb5 100644 --- a/lib/realm.js +++ b/lib/realm.js @@ -13,6 +13,7 @@ function Api(router, realm) { this.sessionId = tools.randomId(); router.registerSession(this); + // API functions this.regrpc = function(uri, callback) { var regId = realm.regrpc(this, tools.randomId(), uri); if (regId) { @@ -48,7 +49,7 @@ function Api(router, realm) { realm.publish(this, requestId, topicUri, {}, args, kwargs); }; - // internal part + // override/internal part this.sendInvoke = function (regId, invId, args, kwargs) { if (_rpc.hasOwnProperty(regId)) { _rpc[regId](invId, args, kwargs); diff --git a/lib/router.js b/lib/router.js index 4ef18b5..4b97eda 100644 --- a/lib/router.js +++ b/lib/router.js @@ -14,14 +14,14 @@ function Router() { var _sessions = {}; EventEmitter.call(this); - this.getRealm = function(realmName) { + this.getRealm = function(realmName, callback) { if (_realms.hasOwnProperty(realmName)) { - return _realms[realmName]; + callback(_realms[realmName]); } else { var realm = new Realm(this); _realms[realmName] = realm; this.emit('RealmCreated', realm, realmName); - return realm; + callback(realm); } }; diff --git a/lib/session.js b/lib/session.js index f261ac8..00b9c85 100644 --- a/lib/session.js +++ b/lib/session.js @@ -15,16 +15,17 @@ inherits(Session, handlers); function Session (router, sender, sessionId) { var secureRealmName; var secureDetails; - var auth = null; + var authHandler = null; this.realm = null; this.sessionId = sessionId; handlers.call(this); - this.setAuth = function (inAuth) { - auth = inAuth; + // setup auth module that is configured in transport + this.setAuthHandler = function (auth) { + authHandler = auth; }; this.hello = function (realmName, details) { - if (auth) { + if (authHandler) { secureDetails = details; secureRealmName = realmName; if (details.hasOwnProperty('authmethods') && details.authmethods.indexOf('ticket') >= 0) { @@ -33,21 +34,25 @@ function Session (router, sender, sessionId) { this.sendAbort("wamp.error.authorization_failed"); } } else { - this.realm = router.getRealm(realmName); - this.sendWelcome({}); + router.getRealm(realmName, function (realm) { + this.realm = realm; + this.sendWelcome({}); + }.bind(this)); } }; this.authenticate = function (secret) { - auth.authenticate(secureRealmName, secureDetails, secret, function (err) { + authHandler.authenticate(secureRealmName, secureDetails, secret, function (err) { if (err) { this.sendAbort("wamp.error.authorization_failed"); } else { - this.realm = router.getRealm(secureRealmName); - var details = { - authid:secureDetails.authid, - authmethod:"ticket" - }; - this.sendWelcome(details); + router.getRealm(secureRealmName, function (realm) { + this.realm = realm; + var details = { + authid:secureDetails.authid, + authmethod:"ticket" + }; + this.sendWelcome(details); + }.bind(this)); } }.bind(this)); }; diff --git a/lib/transport.js b/lib/transport.js index 6ce9904..0f69950 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -64,7 +64,7 @@ function Transport(router, auth, sessionCtrl, SessionClass, options) { _wss.on('connection', function (wsclient) { var sender = new WsSender(wsclient); var session = new SessionClass(router, sender, sessionCtrl.getNewSessionId()); - session.setAuth(auth); + session.setAuthHandler(auth); sessionCtrl.registerSession(session); var parser = new WsParser(wsclient, router, session); log.trace('New session :' + session.sessionId); diff --git a/test/auth.js b/test/auth.js index 6f867fc..eff1fb6 100644 --- a/test/auth.js +++ b/test/auth.js @@ -33,7 +33,7 @@ describe('authenticate', function() { router = new Router(); cli = new Session(router, sender, router.getNewSessionId()); - cli.setAuth(new Auth()); + cli.setAuthHandler(new Auth()); router.registerSession(cli); }); From f74c4fd82c02a4b2c28164d63ae9a9d98ca8131d Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Fri, 10 Jun 2016 01:03:32 +0300 Subject: [PATCH 23/32] alternative logging --- lib/handlers.js | 6 +++--- lib/router.js | 19 +++++++++++++------ lib/session.js | 30 +++++++++++++++++++++--------- lib/transport.js | 31 ++++++++++++------------------- lib/wamp.rt.js | 2 +- 5 files changed, 50 insertions(+), 38 deletions(-) diff --git a/lib/handlers.js b/lib/handlers.js index 7e9fd7a..3c360c9 100644 --- a/lib/handlers.js +++ b/lib/handlers.js @@ -101,12 +101,12 @@ var Facade = function () { this.terminate(1003, "protocol violation"); return; } - var type = msg.shift(); - if (!handlers[type]) { + var msgType = msg.shift(); + if (!handlers[msgType]) { this.terminate(1003, "protocol violation"); return; } - handlers[type].call(this, msg); + handlers[msgType].call(this, msg); }; }; diff --git a/lib/router.js b/lib/router.js index 4b97eda..28cc6da 100644 --- a/lib/router.js +++ b/lib/router.js @@ -3,7 +3,8 @@ var tools = require('./tools'), Realm = require('./realm'), - util = require('util'), + log = require('./log'), + util = require('util'), EventEmitter = require('events').EventEmitter; util.inherits(Router, EventEmitter); @@ -16,15 +17,21 @@ function Router() { this.getRealm = function(realmName, callback) { if (_realms.hasOwnProperty(realmName)) { - callback(_realms[realmName]); + callback(_realms[realmName]); } else { - var realm = new Realm(this); - _realms[realmName] = realm; - this.emit('RealmCreated', realm, realmName); - callback(realm); + var realm = new Realm(this); + _realms[realmName] = realm; + this.emit('RealmCreated', realm, realmName); + callback(realm); } }; + this.traceTx = function (msg, id) { + log.trace("["+id+"] TX > "+msg); + }; + this.traceRx = function (msg, id) { + log.trace("["+id+"] RX > "+msg); + }; this.getNewSessionId = function () { return tools.randomId(); }; diff --git a/lib/session.js b/lib/session.js index 00b9c85..5eda02b 100644 --- a/lib/session.js +++ b/lib/session.js @@ -5,13 +5,12 @@ var WAMP = require('./protocol'), handlers = require('./handlers'), inherits = require('util').inherits; -module.exports = Session; -inherits(Session, handlers); - // requires sender with // sender.send(msg, sessionId, callback) // sender.close(code, reason) +// authHandler.authenticate(realmName, secureDetails, secret, callback) + function Session (router, sender, sessionId) { var secureRealmName; var secureDetails; @@ -57,15 +56,28 @@ function Session (router, sender, sessionId) { }.bind(this)); }; this.send = function (msg, callback) { - sender.send(msg, sessionId, callback); + sender.send(msg, callback); }; this.terminate = function (code, reason) { sender.close(code, reason); }; - this.cleanup = function () { - if (this.realm) { - this.realm.cleanup(this); - this.realm = null; - } + this.logRx = function (data) { + router.logRx(this, this.tealm, data); + }; + this.logTx = function (data) { + router.logTx(this, this.tealm, data); + }; + this.logDebug = function (msg) { + router.logDebug(this, this.tealm, msg); }; } + +module.exports = Session; +inherits(Session, handlers); + +Session.prototype.cleanup = function () { + if (this.realm) { + this.realm.cleanup(this); + this.realm = null; + } +}; diff --git a/lib/transport.js b/lib/transport.js index 0f69950..32dd277 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -7,35 +7,27 @@ var module.exports = Transport; -var _trace = function (msg, id) { - var trace = "[SESSION][" + - ((typeof id === 'undefined') ? "?" : id) + - "] " + msg; - log.trace(trace); -}; - function WsParser(wsclient, router, session) { wsclient.on('message', function(data) { var msg; try { msg = JSON.parse(data); } catch (e) { - log.trace('invalid json'); + session.logWarn('invalid json', data); session.terminate(1003, "protocol violation"); return; } - _trace('RX < ' + data, session.sessionId); + session.logRx(data); session.handle(msg); }); - wsclient.on('close', function() { - log.trace('WebSocket is closed.'); + session.logDebug('WebSocket is closed.'); session.cleanup(); router.removeSession(session); }); } -function WsSender(wsclient) { +function WsSender(wsclient, sessionId) { var defaultCallback = function (error) { if (error) { log.trace("Failed to send message: " + error); @@ -43,9 +35,9 @@ function WsSender(wsclient) { } }.bind(this); - this.send = function (msg, id, callback) { + this.send = function (msg, callback) { var data = JSON.stringify(msg); - _trace('TX > ' + data, id); + this.session.logTx(data); wsclient.send(data, (typeof callback === 'function') ? callback : defaultCallback); }; @@ -56,16 +48,17 @@ function WsSender(wsclient) { }; } -function Transport(router, auth, sessionCtrl, SessionClass, options) { - // Instantiate WebSocketServer +function Transport(router, auth, SessionClass, options) { var _wss = new WebSocketServer(options); // Create a Session object for the lifetime of each // WebSocket client object _wss.on('connection', function (wsclient) { - var sender = new WsSender(wsclient); - var session = new SessionClass(router, sender, sessionCtrl.getNewSessionId()); + var sessionId = router.getNewSessionId(); + var sender = new WsSender(wsclient, sessionId); + var session = new SessionClass(router, sender, sessionId); + sender.session = session; session.setAuthHandler(auth); - sessionCtrl.registerSession(session); + router.registerSession(session); var parser = new WsParser(wsclient, router, session); log.trace('New session :' + session.sessionId); }); diff --git a/lib/wamp.rt.js b/lib/wamp.rt.js index 31fd5d1..3120aa0 100644 --- a/lib/wamp.rt.js +++ b/lib/wamp.rt.js @@ -28,7 +28,7 @@ RouterTransport = function (options, auth) { } }; } - _transport = new Transport(this, auth, this, Session, _options); + _transport = new Transport(this, auth, Session, _options); }; inherits(RouterTransport, Router); From 39eaa3ad0bf97c4de8db1c68c9e6884897cb0472 Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Wed, 22 Jun 2016 00:35:23 +0300 Subject: [PATCH 24/32] test log --- lib/session.js | 2 +- test/auth.js | 8 ++++---- test/realm.js | 30 +++++++++++++++--------------- test/session.js | 10 +++++----- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/lib/session.js b/lib/session.js index 5eda02b..56a400f 100644 --- a/lib/session.js +++ b/lib/session.js @@ -6,7 +6,7 @@ var WAMP = require('./protocol'), inherits = require('util').inherits; // requires sender with -// sender.send(msg, sessionId, callback) +// sender.send(msg, callback) // sender.close(code, reason) // authHandler.authenticate(realmName, secureDetails, secret, callback) diff --git a/test/auth.js b/test/auth.js index eff1fb6..23079cc 100644 --- a/test/auth.js +++ b/test/auth.js @@ -42,7 +42,7 @@ describe('authenticate', function() { it('Joe AUTH:FAIL', function () { sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { expect(msg[0]).to.equal(WAMP.CHALLENGE); expect(msg[1]).to.equal('ticket'); } @@ -51,7 +51,7 @@ describe('authenticate', function() { expect(sender.send).to.have.been.called.once; sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { expect(msg[0]).to.equal(WAMP.ABORT); // callback(); } @@ -62,7 +62,7 @@ describe('authenticate', function() { it('Joe AUTH:OK', function () { sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { expect(msg[0]).to.equal(WAMP.CHALLENGE); expect(msg[1]).to.equal('ticket'); } @@ -71,7 +71,7 @@ describe('authenticate', function() { expect(sender.send).to.have.been.called.once; sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { expect(msg[0]).to.equal(WAMP.WELCOME); } ); diff --git a/test/realm.js b/test/realm.js index 583aa4f..f074f17 100644 --- a/test/realm.js +++ b/test/realm.js @@ -41,7 +41,7 @@ describe('protocol', function() { it('CALL to RPC not exist', function () { sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { expect(msg[0]).to.equal(WAMP.ERROR); expect(msg[1]).to.equal(WAMP.CALL); expect(msg[2]).to.equal(1234); @@ -67,7 +67,7 @@ describe('protocol', function() { var regId = api.regrpc('func1', procSpy); sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { expect(msg[0]).to.equal(WAMP.RESULT); expect(msg[1]).to.equal(1234); expect(msg[3]).to.deep.equal(['result.1','result.2']); @@ -87,7 +87,7 @@ describe('protocol', function() { }); api.regrpc('func1', procSpy); sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { expect(msg[0]).to.equal(WAMP.ERROR); expect(msg[1]).to.equal(WAMP.CALL); expect(msg[2]).to.equal(1234); @@ -102,7 +102,7 @@ describe('protocol', function() { it('UNREGISTER error', function () { sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { expect(msg[0]).to.equal(WAMP.ERROR); expect(msg[1]).to.equal(WAMP.UNREGISTER); expect(msg[2]).to.equal(2345); @@ -118,7 +118,7 @@ describe('protocol', function() { var registrationId = null; sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { expect(msg[0]).to.equal(WAMP.REGISTERED); expect(msg[1]).to.equal(1234); registrationId = msg[2]; @@ -128,7 +128,7 @@ describe('protocol', function() { expect(sender.send, 'registration confirmed').to.have.been.called.once; sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { expect(msg[0]).to.equal(WAMP.UNREGISTERED); expect(msg[1]).to.equal(2345); } @@ -141,7 +141,7 @@ describe('protocol', function() { var registrationId = null; sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { expect(msg[0]).to.equal(WAMP.REGISTERED); expect(msg[1]).to.equal(1234); registrationId = msg[2]; @@ -152,7 +152,7 @@ describe('protocol', function() { var callId = null; sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { expect(msg[0]).to.equal(WAMP.INVOCATION); callId = msg[1]; expect(msg[2]).to.equal(registrationId); @@ -179,7 +179,7 @@ describe('protocol', function() { var callId = null; sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { callId = msg[1]; } ); @@ -196,7 +196,7 @@ describe('protocol', function() { it('UNSUBSCRIBE error', function () { sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { expect(msg[0]).to.equal(WAMP.ERROR); expect(msg[1]).to.equal(WAMP.UNSUBSCRIBE); expect(msg[2]).to.equal(2345); @@ -212,7 +212,7 @@ describe('protocol', function() { var subscriptionId = null; sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { expect(msg[0]).to.equal(WAMP.SUBSCRIBED); expect(msg[1]).to.equal(1234); subscriptionId = msg[2]; @@ -222,7 +222,7 @@ describe('protocol', function() { expect(sender.send, 'subscription confirmed').to.have.been.called.once; sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { expect(msg[0]).to.equal(WAMP.UNSUBSCRIBED); expect(msg[1]).to.equal(2345); } @@ -243,7 +243,7 @@ describe('protocol', function() { var subscriptionId = null; sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { expect(msg[0]).to.equal(WAMP.SUBSCRIBED); expect(msg[1]).to.equal(1234); subscriptionId = msg[2]; @@ -253,7 +253,7 @@ describe('protocol', function() { expect(sender.send, 'subscription confirmed').to.have.been.called.once; sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { expect(msg[0]).to.equal(WAMP.EVENT); expect(msg[1]).to.equal(subscriptionId); // 2 published message Id @@ -276,7 +276,7 @@ describe('protocol', function() { var subId = api.substopic('topic1', subSpy); sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { expect(msg[0]).to.equal(WAMP.PUBLISHED); expect(msg[1]).to.equal(2345); } diff --git a/test/session.js b/test/session.js index 97615d2..6d90abf 100644 --- a/test/session.js +++ b/test/session.js @@ -31,7 +31,7 @@ describe('session', function() { it('HELLO/WELCOME', function () { sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { expect(msg[0]).to.equal(WAMP.WELCOME); } ); @@ -39,7 +39,7 @@ describe('session', function() { expect(sender.send).to.have.been.called.once; // second hello command raises error and disconnects the user - sender.send = chai.spy(function (msg, id, callback) {}); + sender.send = chai.spy(function (msg, callback) {}); sender.close = chai.spy(function (error, reason) {}); cli.handle([WAMP.HELLO, 'test', {}]); expect(sender.send).to.not.have.been.called; @@ -48,7 +48,7 @@ describe('session', function() { it('GOODBYE', function () { sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { expect(msg[0]).to.equal(WAMP.GOODBYE); callback(); } @@ -63,7 +63,7 @@ describe('session', function() { it('CALL to no realm RPC', function () { sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { expect(msg[0]).to.equal(WAMP.ERROR); expect(msg[1]).to.equal(WAMP.CALL); expect(msg[2]).to.equal(1234); @@ -76,7 +76,7 @@ describe('session', function() { it('REGISTER to no realm', function () { sender.send = chai.spy( - function (msg, id, callback) { + function (msg, callback) { expect(msg[0]).to.equal(WAMP.ERROR); expect(msg[1]).to.equal(WAMP.REGISTER); expect(msg[2]).to.equal(1234); From cafdf0c44a82f750d1bd39cf71a00c3956786951 Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Sun, 23 Apr 2017 23:45:15 -0400 Subject: [PATCH 25/32] requirements --- lib/session.js | 6 +++--- lib/transport.js | 5 ++--- package.json | 7 ++++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/session.js b/lib/session.js index 00b9c85..001434e 100644 --- a/lib/session.js +++ b/lib/session.js @@ -1,9 +1,9 @@ /*jshint node: true */ 'use strict'; -var WAMP = require('./protocol'), - handlers = require('./handlers'), - inherits = require('util').inherits; +var + handlers = require('./handlers'), + inherits = require('util').inherits; module.exports = Session; inherits(Session, handlers); diff --git a/lib/transport.js b/lib/transport.js index 0f69950..43c221f 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -56,9 +56,8 @@ function WsSender(wsclient) { }; } -function Transport(router, auth, sessionCtrl, SessionClass, options) { - // Instantiate WebSocketServer - var _wss = new WebSocketServer(options); +function Transport(router, auth, sessionCtrl, SessionClass, wsOptions) { + var _wss = new WebSocketServer(wsOptions); // Create a Session object for the lifetime of each // WebSocket client object _wss.on('connection', function (wsclient) { diff --git a/package.json b/package.json index 4c828cb..0087328 100644 --- a/package.json +++ b/package.json @@ -15,11 +15,12 @@ "url": "https://github.com/kalmyk/wamp.rt.git" }, "dependencies": { - "ws": "*", - "commander": "*" + "ws": "~1", + "commander": "*", + "node-statsd": "*" }, "devDependencies": { - "autobahn": "*", + "autobahn": "~0.9.4", "when": "*", "chai": "*", "chai-as-promised": "*", From 43d4ab9c3870f433fc51f1785458e7842dbb3748 Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Mon, 24 Apr 2017 21:37:47 -0400 Subject: [PATCH 26/32] event emiter instead of log --- lib/handlers.js | 13 ++++--- lib/log.js | 12 ------ lib/realm.js | 4 +- lib/router.js | 98 +++++++++++++++++++++++++++++------------------- lib/session.js | 9 ----- lib/transport.js | 22 +++++------ 6 files changed, 80 insertions(+), 78 deletions(-) delete mode 100644 lib/log.js diff --git a/lib/handlers.js b/lib/handlers.js index 3c360c9..4cd00c3 100644 --- a/lib/handlers.js +++ b/lib/handlers.js @@ -24,8 +24,9 @@ var Facade = function () { this.sendRegistered = function (requestId, registrationId) { this.send([WAMP.REGISTERED, requestId, registrationId]); }; - this.sendUnregistered = function (requestId, registrationId) { - this.send([WAMP.UNREGISTERED, requestId]); + this.sendUnregistered = function (requestId) { + if (requestId) // do not send on disconnect + this.send([WAMP.UNREGISTERED, requestId]); }; this.sendInvoke = function (regId, invId, args, kwargs) { var msg = [ @@ -59,7 +60,8 @@ var Facade = function () { this.send([WAMP.SUBSCRIBED, requestId, topicId]); }; this.sendUnsubscribed = function (requestId) { - this.send([WAMP.UNSUBSCRIBED, requestId]); + if (requestId) // do not send on disconnect + this.send([WAMP.UNSUBSCRIBED, requestId]); }; this.sendPublished = function (requestId, publicationId) { this.send([WAMP.PUBLISHED, requestId, publicationId]); @@ -80,8 +82,9 @@ var Facade = function () { } this.send(msg); }; - this.sendError = function (cmd, callId, txt) { - this.send([WAMP.ERROR, cmd, callId, {}, txt]); + this.sendError = function (cmd, requestId, txt) { + if (requestId) // do not send on disconnect + this.send([WAMP.ERROR, cmd, requestId, {}, txt]); }; this.sendGoodbye = function () { // Graceful termination diff --git a/lib/log.js b/lib/log.js deleted file mode 100644 index de8bd53..0000000 --- a/lib/log.js +++ /dev/null @@ -1,12 +0,0 @@ -// wamp.rt -// Copyright Orange 2014 - -var trace = function () {}; - -if ('WAMPRT_TRACE' in global && WAMPRT_TRACE && 'console' in global) { - trace = function () { - console.log.apply(console, arguments); - }; -} - -exports.trace = trace; diff --git a/lib/realm.js b/lib/realm.js index 4515eb5..e5ec9af 100644 --- a/lib/realm.js +++ b/lib/realm.js @@ -70,10 +70,10 @@ function Api(router, realm) { function Realm(router) { var _sessRPC = {}; - var _sessTopic = {}; + var _sessTopic = {}; // topics by sessionId var _rpcs = {}; - var _topics = {}; + var _topics = {}; // topics by uri var _pending = {}; var _api = null; this.isSecured = false; diff --git a/lib/router.js b/lib/router.js index 28cc6da..30fee61 100644 --- a/lib/router.js +++ b/lib/router.js @@ -1,49 +1,69 @@ /*jshint node: true */ 'use strict'; -var tools = require('./tools'), - Realm = require('./realm'), - log = require('./log'), - util = require('util'), - EventEmitter = require('events').EventEmitter; +var + tools = require('./tools'), + Realm = require('./realm'), + util = require('util'), + EventEmitter = require('events').EventEmitter; + +var trace = function () {}; + +if ('WAMPRT_TRACE' in global && WAMPRT_TRACE && 'console' in global) { + trace = function () { + console.log.apply(console, arguments); + }; +} util.inherits(Router, EventEmitter); function Router() { - // Realm management - var _realms = {}; - var _sessions = {}; - EventEmitter.call(this); - - this.getRealm = function(realmName, callback) { - if (_realms.hasOwnProperty(realmName)) { - callback(_realms[realmName]); - } else { - var realm = new Realm(this); - _realms[realmName] = realm; - this.emit('RealmCreated', realm, realmName); - callback(realm); - } - }; - - this.traceTx = function (msg, id) { - log.trace("["+id+"] TX > "+msg); - }; - this.traceRx = function (msg, id) { - log.trace("["+id+"] RX > "+msg); - }; - this.getNewSessionId = function () { - return tools.randomId(); - }; - this.registerSession = function(session) { - _sessions[session.sessionId] = session; - }; - this.getSession = function(sessionId) { - return _sessions[sessionId]; - }; - this.removeSession = function (session) { - delete _sessions[session.sessionId]; - }; + // Realm management + var _realms = {}; + var _sessions = {}; + EventEmitter.call(this); + + this.getRealm = function(realmName, callback) { + if (_realms.hasOwnProperty(realmName)) { + callback(_realms[realmName]); + } else { + var realm = new Realm(this); + _realms[realmName] = realm; + this.emit('RealmCreated', realm, realmName); + callback(realm); + } + }; + + this.getNewSessionId = function () { + return tools.randomId(); + }; + + this.registerSession = function(session) { + _sessions[session.sessionId] = session; + }; + + this.getSession = function(sessionId) { + return _sessions[sessionId]; + }; + this.removeSession = function (session) { + delete _sessions[session.sessionId]; + }; + + this.on('session.Tx', function (session, data) { + trace("["+session.sessionId+"] TX > "+data); + }); + + this.on('session.Rx', function (session, data) { + trace("["+session.sessionId+"] RX > "+data); + }); + + this.on('session.debug', function (session, msg) { + trace("["+session.sessionId+"] "+msg); + }); + + this.on('session.warning', function (session, msg, data) { + trace("["+session.sessionId+"] "+msg+' '+data); + }); } module.exports = Router; diff --git a/lib/session.js b/lib/session.js index 56a400f..6b68cb7 100644 --- a/lib/session.js +++ b/lib/session.js @@ -61,15 +61,6 @@ function Session (router, sender, sessionId) { this.terminate = function (code, reason) { sender.close(code, reason); }; - this.logRx = function (data) { - router.logRx(this, this.tealm, data); - }; - this.logTx = function (data) { - router.logTx(this, this.tealm, data); - }; - this.logDebug = function (msg) { - router.logDebug(this, this.tealm, msg); - }; } module.exports = Session; diff --git a/lib/transport.js b/lib/transport.js index 32dd277..e31447f 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -2,8 +2,7 @@ 'use strict'; var - WebSocketServer = require('ws').Server, - log = require('./log'); + WebSocketServer = require('ws').Server; module.exports = Transport; @@ -13,37 +12,38 @@ function WsParser(wsclient, router, session) { try { msg = JSON.parse(data); } catch (e) { - session.logWarn('invalid json', data); + router.emit('session.warning', session, 'invalid json', data); session.terminate(1003, "protocol violation"); return; } - session.logRx(data); + router.emit('session.Rx', session, data); session.handle(msg); }); + wsclient.on('close', function() { - session.logDebug('WebSocket is closed.'); + router.emit('session.debug', session, 'WebSocket is closed.'); session.cleanup(); router.removeSession(session); }); } -function WsSender(wsclient, sessionId) { +function WsSender(wsclient, router, sessionId) { var defaultCallback = function (error) { if (error) { - log.trace("Failed to send message: " + error); + router.emit('session.warning', "Failed to send message:", error); this.close(1011, "Unexpected error"); } }.bind(this); this.send = function (msg, callback) { var data = JSON.stringify(msg); - this.session.logTx(data); + router.emit('session.Tx', this.session, data); wsclient.send(data, (typeof callback === 'function') ? callback : defaultCallback); }; this.close = function (code, reason) { - log.trace('Closing WebSocket connection: [' + code + '] ' + reason); + router.emit('session.debug', this.session, 'Closing WebSocket connection: [' + code + '] ' + reason); wsclient.close(code, reason); }; } @@ -54,13 +54,13 @@ function Transport(router, auth, SessionClass, options) { // WebSocket client object _wss.on('connection', function (wsclient) { var sessionId = router.getNewSessionId(); - var sender = new WsSender(wsclient, sessionId); + var sender = new WsSender(wsclient, router, sessionId); var session = new SessionClass(router, sender, sessionId); sender.session = session; session.setAuthHandler(auth); router.registerSession(session); var parser = new WsParser(wsclient, router, session); - log.trace('New session :' + session.sessionId); + router.emit('session.debug', session, 'New session'); }); this.close = function() { From 44d20cb71310e6e2efaeccffca11221f48be48a7 Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Wed, 26 Apr 2017 21:25:21 -0400 Subject: [PATCH 27/32] add statsd integration --- democli/backend.js | 11 +++++++++-- democli/frontend.js | 5 ++--- examples/basic.js | 6 +++--- examples/with_statsd.js | 18 ++++++++++++++++++ ext/statsd.js | 42 +++++++++++++++++++++++++++++++++++++++++ lib/handlers.js | 7 ++++--- lib/realm.js | 4 ++-- lib/router.js | 2 +- lib/session.js | 8 ++++++-- 9 files changed, 87 insertions(+), 16 deletions(-) create mode 100644 examples/with_statsd.js create mode 100644 ext/statsd.js diff --git a/democli/backend.js b/democli/backend.js index f8bce45..50fc761 100644 --- a/democli/backend.js +++ b/democli/backend.js @@ -58,7 +58,8 @@ connection.onopen = function (session) { function onEvent(publishArgs, kwargs) { console.log('Event', currentSubscription.topic,'received args', publishArgs, 'kwargs ',kwargs); counter++; - if (counter > 20) { + + if (counter >= 20) { session.unsubscribe(currentSubscription).then(function(gone) { console.log("unsubscribe successfull"); }, function(error) { @@ -78,7 +79,13 @@ connection.onopen = function (session) { } ); - setTimeout(function() {console.log("Unregistration");session.unregister(reg);session.unregister(reg2);},20000); + setTimeout(function() { + console.log("Unregistration"); + session.unregister(reg); + session.unregister(reg2); + }, + 20000 + ); }; connection.open(); diff --git a/democli/frontend.js b/democli/frontend.js index 1011202..635c78c 100644 --- a/democli/frontend.js +++ b/democli/frontend.js @@ -105,12 +105,11 @@ connection.onopen = function (session, details) { } ); - session.call('wamp.rt.foo', ["test"], {foo:'bar'}).then( + session.call('test.foo', ["test"], {foo:'bar'}).then( function (res) { - session.log("Call wamp.rt.foo completed in " + + session.log("Call test.foo completed in " + (Date.now() - starttime) + " ms: result =", res); - }, function (error) { console.log("Call failed:", error); diff --git a/examples/basic.js b/examples/basic.js index 60f4e0c..280ea42 100644 --- a/examples/basic.js +++ b/examples/basic.js @@ -14,8 +14,8 @@ var WampRouter = require('../lib/wamp.rt'); var program = require('commander'); program - .option('-p, --port ', 'Server IP port', 9000) - .parse(process.argv); + .option('-p, --port ', 'Server IP port', 9000) + .parse(process.argv); console.log('Listening port:', program.port); @@ -50,7 +50,7 @@ app.on('RealmCreated', function (realm, realmName) { app.getRealm('realm1', function (realm) { var api = realm.api(); - api.regrpc('wamp.rt.foo', function(id, args, kwargs) { + api.regrpc('test.foo', function(id, args, kwargs) { console.log('called with ', args, kwargs); api.resrpc(id, null /* no error */, [["bar", "bar2"], {"key1": "bar1", "key2": "bar2"}]); }); diff --git a/examples/with_statsd.js b/examples/with_statsd.js new file mode 100644 index 0000000..ae435e3 --- /dev/null +++ b/examples/with_statsd.js @@ -0,0 +1,18 @@ +// +// This is a basic router example with sonnectivity to the statsd server +// + +var WampRouter = require('../lib/wamp.rt'); +var program = require('commander'); +var StatsD = require('../ext/statsd'); + +StatsD.init(program); + +program + .option('-p, --port ', 'Server IP port', 9000) + .parse(process.argv); + +console.log('Listening port:', program.port); + +var app = new WampRouter({port: program.port}); +var trace = new StatsD.TraceRouter(program, app); diff --git a/ext/statsd.js b/ext/statsd.js new file mode 100644 index 0000000..9925a5b --- /dev/null +++ b/ext/statsd.js @@ -0,0 +1,42 @@ +/*jshint node: true */ +'use strict'; + +var + StatsD = require('node-statsd'); + +function init(program) { + program + .option('-t, --statsd-port ', 'StatsD Server IP port', 8125) + .option('-s, --statsd-server ', 'StatsD Server IP', 'localhost'); +}; + +function TraceRouter(program, router) { + + var client = new StatsD({ + host: program.statsdServer, + port: program.statsdPort, + prefix: 'wamp.' + }); + + router.on('session.Tx', function (session, data) { + var realmName = 'UNKNOWN'; + if (session.realm) + realmName = session.getRealmName(); + + client.increment(realmName+'.Tx.count', 1); + client.increment(realmName+'.Tx.size', data.length); + }); + + router.on('session.Rx', function (session, data) { + var realmName = 'UNKNOWN'; + if (session.realm) + realmName = session.getRealmName(); + + client.increment(realmName+'.Rx.count', 1); + client.increment(realmName+'.Rx.size', data.length); + }); + +}; + +exports.init = init; +exports.TraceRouter = TraceRouter; diff --git a/lib/handlers.js b/lib/handlers.js index 4cd00c3..3ab1098 100644 --- a/lib/handlers.js +++ b/lib/handlers.js @@ -1,7 +1,8 @@ /*jshint node: true */ 'use strict'; -var WAMP = require('./protocol'); +var + WAMP = require('./protocol'); var handlers = {}; @@ -33,7 +34,7 @@ var Facade = function () { WAMP.INVOCATION, invId, regId, - {}, + {}, // details ]; // Manage optional parameters args + kwargs if (undefined !== args) msg.push(args); @@ -154,7 +155,7 @@ handlers[WAMP.CALL] = function (args) { var fArgs = args.shift() || []; var kwArgs = args.shift() || {}; if (this.checkRealm(WAMP.CALL, callId)) - this.realm.callrpc(this, callId, procUri, fArgs, kwArgs); + this.realm.callrpc(this, callId, procUri, options, fArgs, kwArgs); }; handlers[WAMP.UNREGISTER] = function (args) { diff --git a/lib/realm.js b/lib/realm.js index e5ec9af..a377b19 100644 --- a/lib/realm.js +++ b/lib/realm.js @@ -28,7 +28,7 @@ function Api(router, realm) { }; this.callrpc = function (uri, args, kwargs, callback) { var callId = tools.randomId(); - if (realm.callrpc(this, callId, uri, args, kwargs)) { + if (realm.callrpc(this, callId, uri, {}, args, kwargs)) { _callback[callId] = callback; } }; @@ -114,7 +114,7 @@ function Realm(router) { return procUri; }; - this.callrpc = function(session, callId, procUri, args, kwargs) { + this.callrpc = function(session, callId, procUri, options, args, kwargs) { if (!_rpcs.hasOwnProperty(procUri)) { session.sendError(WAMP.CALL, callId, "wamp.error.no_such_procedure"); return false; diff --git a/lib/router.js b/lib/router.js index 30fee61..402daac 100644 --- a/lib/router.js +++ b/lib/router.js @@ -9,7 +9,7 @@ var var trace = function () {}; -if ('WAMPRT_TRACE' in global && WAMPRT_TRACE && 'console' in global) { +if ('WAMPRT_TRACE' in global && WAMPRT_TRACE && 'console' in global) { // jshint ignore:line trace = function () { console.log.apply(console, arguments); }; diff --git a/lib/session.js b/lib/session.js index 38d8b8a..edb4dcf 100644 --- a/lib/session.js +++ b/lib/session.js @@ -24,15 +24,16 @@ function Session (router, sender, sessionId) { authHandler = auth; }; this.hello = function (realmName, details) { + secureRealmName = realmName; if (authHandler) { secureDetails = details; - secureRealmName = realmName; if (details.hasOwnProperty('authmethods') && details.authmethods.indexOf('ticket') >= 0) { this.sendChallenge('ticket', {}); } else { this.sendAbort("wamp.error.authorization_failed"); } - } else { + } + else { router.getRealm(realmName, function (realm) { this.realm = realm; this.sendWelcome({}); @@ -61,6 +62,9 @@ function Session (router, sender, sessionId) { this.terminate = function (code, reason) { sender.close(code, reason); }; + this.getRealmName = function() { + return secureRealmName; + } } module.exports = Session; From 7a92a9a06edee8ead08ae8ae4a9c05e23e66a0d8 Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Wed, 26 Apr 2017 22:07:13 -0400 Subject: [PATCH 28/32] add statsd integration --- README.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 04a8793..c9f60e2 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,6 @@ -# wamp.rt: A WAMP V2 nodejs router -##Copyright Orange 2014, All Rights Reserved +# FOX.WAMP is a WAMP v2 message router implementation -wamp.rt is a WebSocket Application Messaging Protocol [WAMP](http://wamp.ws/) V2 router implementation based on nodejs. - -The router is compliant with the WAMP V2 [Basic Profile](https://github.com/tavendo/WAMP/blob/master/spec/basic.md). - -wamp.rt implements both [Dealer](https://github.com/tavendo/WAMP/blob/master/spec/basic.md#peers-and-roles) and [Broker](https://github.com/tavendo/WAMP/blob/master/spec/basic.md#peers-and-roles) roles. +The message router is compliant with the [WAMP V2 Basic Profile](http://wamp-proto.org/). ## Build Instructions @@ -13,12 +8,13 @@ Install using npm. Depending on what you want to do, your mileage may vary. ## Credits -wamp.rt has been inspired by the following Open Source projects: +fox.wamp has been inspired by the following Open Source projects: - [wamp.io](https://github.com/nicokaiser/wamp.io) +- [wamp.rt](https://github.com/Orange-OpenSource/wamp.rt) -## Changes to internal api +## Changes: 2016-04-03: - ticket auth support added @@ -26,3 +22,6 @@ wamp.rt has been inspired by the following Open Source projects: - internal api moved to realm - callrpc method has args & kwargs arguments - publish method does not require message id + +2017-04-26: +- integration with [StatsD](https://github.com/etsy/statsd) From 53590958eaf0167cdd7247a1916620140c3d5d8f Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Fri, 28 Apr 2017 22:28:57 -0400 Subject: [PATCH 29/32] kw to rpc result --- examples/basic.js | 5 +++-- lib/handlers.js | 31 +++++++++++++++++-------------- lib/messages.js | 8 ++++++++ lib/realm.js | 22 ++++++++++++++-------- lib/router.js | 3 ++- lib/session.js | 5 +++++ test/realm.js | 17 ++++++++++------- 7 files changed, 59 insertions(+), 32 deletions(-) create mode 100644 lib/messages.js diff --git a/examples/basic.js b/examples/basic.js index 280ea42..e0225ff 100644 --- a/examples/basic.js +++ b/examples/basic.js @@ -10,6 +10,7 @@ WAMPRT_TRACE = true; +var MSG = require('../lib/messages'); var WampRouter = require('../lib/wamp.rt'); var program = require('commander'); @@ -44,7 +45,7 @@ app.on('RPCUnregistered', function (realm, uri) { app.on('Publish', function onPublish(realm, topicUri, args) { console.log('onPublish Publish', topicUri, args); }); -app.on('RealmCreated', function (realm, realmName) { +app.on(MSG.REALM_CREATED, function (realm, realmName) { console.log('new Relm:', realmName); }); @@ -52,6 +53,6 @@ app.getRealm('realm1', function (realm) { var api = realm.api(); api.regrpc('test.foo', function(id, args, kwargs) { console.log('called with ', args, kwargs); - api.resrpc(id, null /* no error */, [["bar", "bar2"], {"key1": "bar1", "key2": "bar2"}]); + api.resrpc(id, null /* no error */, ["bar", "bar2"], {"key1": "bar1", "key2": "bar2"}); }); }); diff --git a/lib/handlers.js b/lib/handlers.js index 3ab1098..21980f1 100644 --- a/lib/handlers.js +++ b/lib/handlers.js @@ -36,24 +36,22 @@ var Facade = function () { regId, {}, // details ]; - // Manage optional parameters args + kwargs if (undefined !== args) msg.push(args); if (undefined !== kwargs) msg.push(kwargs); this.send(msg); }; - this.sendYield = function (invId, err, args) { + this.sendYield = function (invId, err, args, kwargs) { if (err) { this.sendError(WAMP.CALL, invId, "wamp.error.callee_failure"); - } else { + } + else { var msg = [ WAMP.RESULT, invId, {}, ]; - // Manage optional parameters args + kwargs - for(var i = 0; i < args.length && i < 2; i++) { - msg.push(args[i]); - } + if (undefined !== args) msg.push(args); + if (undefined !== kwargs) msg.push(kwargs); this.send(msg); } }; @@ -83,9 +81,13 @@ var Facade = function () { } this.send(msg); }; - this.sendError = function (cmd, requestId, txt) { - if (requestId) // do not send on disconnect - this.send([WAMP.ERROR, cmd, requestId, {}, txt]); + this.sendError = function (cmd, requestId, txt, args) { + if (requestId) { // do not send on disconnect + var msg = [WAMP.ERROR, cmd, requestId, {}, txt]; + if (args) + msg.push(args); + this.send(msg); + } }; this.sendGoodbye = function () { // Graceful termination @@ -168,9 +170,10 @@ handlers[WAMP.UNREGISTER] = function (args) { handlers[WAMP.YIELD] = function (args) { var invId = args.shift(); var options = args.shift(); - args = args || []; + var fArgs = args.shift() || []; + var kwArgs = args.shift(); if (this.checkRealm(WAMP.CALL, invId)) - this.realm.resrpc(this, invId, null, args); + this.realm.resrpc(this, invId, null, fArgs, kwArgs); }; handlers[WAMP.SUBSCRIBE] = function(args) { @@ -204,11 +207,11 @@ handlers[WAMP.ERROR] = function(msg) { var details = msg.shift(); var errorUri = msg.shift(); var args = msg.shift() || []; - var kwargs = msg.shift() || {}; + var kwargs = msg.shift(); // An invocation failed if (this.checkRealm(WAMP.ERROR, requestId) && requestType === WAMP.INVOCATION) - this.realm.resrpc(this, requestId, new Error(details), args); + this.realm.resrpc(this, requestId, new Error(details), args, kwargs); }; module.exports = Facade; diff --git a/lib/messages.js b/lib/messages.js new file mode 100644 index 0000000..9128f51 --- /dev/null +++ b/lib/messages.js @@ -0,0 +1,8 @@ +module.exports = { + REALM_CREATED: 'realm.created', + SESSION_JOIN: 'session.join', + SESSION_LEAVE: 'session.leave', + SESSION_COUNT: 'session.count', + SESSION_LIST: 'session.list', + SESSION_GET: 'session.get', +}; diff --git a/lib/realm.js b/lib/realm.js index a377b19..ed60fac 100644 --- a/lib/realm.js +++ b/lib/realm.js @@ -14,6 +14,7 @@ function Api(router, realm) { router.registerSession(this); // API functions + // regrpc callback = function(id, args, kwargs) this.regrpc = function(uri, callback) { var regId = realm.regrpc(this, tools.randomId(), uri); if (regId) { @@ -32,8 +33,8 @@ function Api(router, realm) { _callback[callId] = callback; } }; - this.resrpc = function (invId, err, args) { - return realm.resrpc(this, invId, err, args); + this.resrpc = function (invId, err, args, kwargs) { + return realm.resrpc(this, invId, err, args, kwargs); }; this.substopic = function(topicUri, callback) { var topicId = realm.substopic(this, tools.randomId(), topicUri, {}); @@ -55,10 +56,10 @@ function Api(router, realm) { _rpc[regId](invId, args, kwargs); } }; - this.sendYield = function (callId, err, args) { + this.sendYield = function (callId, err, args, kwargs) { var callback = _callback[callId]; delete _callback[callId]; - callback(err, args); + callback(err, args, kwargs); }; this.sendEvent = function (subscriptionId, publicationId, args, kwargs) { if (_rpc.hasOwnProperty(subscriptionId)) { @@ -116,7 +117,7 @@ function Realm(router) { this.callrpc = function(session, callId, procUri, options, args, kwargs) { if (!_rpcs.hasOwnProperty(procUri)) { - session.sendError(WAMP.CALL, callId, "wamp.error.no_such_procedure"); + session.sendError(WAMP.CALL, callId, "wamp.error.no_such_procedure", ['no callee registered for procedure <'+procUri+'>']); return false; } var destSession = router.getSession(_rpcs[procUri].sessionId); @@ -131,11 +132,12 @@ function Realm(router) { return false; }; - this.resrpc = function(session, invId, err, args) { + this.resrpc = function(session, invId, err, args, kwargs) { if (_pending.hasOwnProperty(invId)) { var destSession = router.getSession(_pending[invId][1]); - if (destSession) - destSession.sendYield(_pending[invId][0], err, args); + if (destSession) { + destSession.sendYield(_pending[invId][0], err, args, kwargs); + } } delete _pending[invId]; }; @@ -220,6 +222,10 @@ function Realm(router) { this.cleanupTopic(session); this.cleanupRPC(session); }; + + this.getSessionCount = function () { + return 15; + }; } module.exports = Realm; diff --git a/lib/router.js b/lib/router.js index 402daac..ff08abb 100644 --- a/lib/router.js +++ b/lib/router.js @@ -2,6 +2,7 @@ 'use strict'; var + MSG = require('./messages'), tools = require('./tools'), Realm = require('./realm'), util = require('util'), @@ -29,7 +30,7 @@ function Router() { } else { var realm = new Realm(this); _realms[realmName] = realm; - this.emit('RealmCreated', realm, realmName); + this.emit(MSG.REALM_CREATED, realm, realmName); callback(realm); } }; diff --git a/lib/session.js b/lib/session.js index edb4dcf..4eb450b 100644 --- a/lib/session.js +++ b/lib/session.js @@ -2,6 +2,7 @@ 'use strict'; var + MSG = require('./messages'), handlers = require('./handlers'), inherits = require('util').inherits; @@ -36,6 +37,7 @@ function Session (router, sender, sessionId) { else { router.getRealm(realmName, function (realm) { this.realm = realm; + router.emit(MSG.SESSION_JOIN, this, realm); this.sendWelcome({}); }.bind(this)); } @@ -47,6 +49,7 @@ function Session (router, sender, sessionId) { } else { router.getRealm(secureRealmName, function (realm) { this.realm = realm; + router.emit(MSG.SESSION_JOIN, this, realm); var details = { authid:secureDetails.authid, authmethod:"ticket" @@ -60,6 +63,8 @@ function Session (router, sender, sessionId) { sender.send(msg, callback); }; this.terminate = function (code, reason) { + if (this.realm) + router.emit(MSG.SESSION_LEAVE, this, this.realm); sender.close(code, reason); }; this.getRealmName = function() { diff --git a/test/realm.js b/test/realm.js index f074f17..cdda6f0 100644 --- a/test/realm.js +++ b/test/realm.js @@ -46,6 +46,7 @@ describe('protocol', function() { expect(msg[1]).to.equal(WAMP.CALL); expect(msg[2]).to.equal(1234); expect(msg[4]).to.equal('wamp.error.no_such_procedure'); + expect(msg[5]).to.deep.equal([ 'no callee registered for procedure ' ]); } ); cli.handle([WAMP.CALL, 1234, {}, 'any.function.name', []]); @@ -62,7 +63,7 @@ describe('protocol', function() { it('CALL to router', function () { var procSpy = chai.spy(function(id, args, kwargs) { - api.resrpc(id, undefined, [['result.1','result.2'], {kVal:'kRes'}]); + api.resrpc(id, undefined, ['result.1','result.2'], {kVal:'kRes'}); }); var regId = api.regrpc('func1', procSpy); @@ -95,7 +96,7 @@ describe('protocol', function() { } ); cli.handle([WAMP.CALL, 1234, {}, 'func1', ['arg1', 'arg2'], {'kArg':'kVal'}]); - api.resrpc(callId, 1, [['result.1','result.2'], {kVal:'kRes'}]); + api.resrpc(callId, 1, ['result.1','result.2'], {kVal:'kRes'}); expect(procSpy).to.have.been.called.once; expect(sender.send).to.have.been.called.once; }); @@ -161,19 +162,21 @@ describe('protocol', function() { expect(msg[5]).to.deep.equal({kVal:'kRes'}); } ); - var callSpy = chai.spy(function(err, args) { + var callResponse = chai.spy(function(err, args, kwargs) { expect(err).to.equal(null); - expect(args).to.deep.equal([['result.1','result.2'],{foo:'bar'}]); + expect(args).to.deep.equal(['result.1','result.2'], 'args call spy response'); + expect(kwargs).to.deep.equal({foo:'bar'}, 'kwargs call spy response'); }); - api.callrpc('func1', ['arg.1','arg.2'], {kVal:'kRes'}, callSpy); + api.callrpc('func1', ['arg.1','arg.2'], {kVal:'kRes'}, callResponse); expect(sender.send, 'invocation received').to.have.been.called.once; + // return the function result cli.handle([WAMP.YIELD, callId, {}, ['result.1','result.2'], {foo:'bar'}]); - expect(callSpy, 'result delivered').to.have.been.called.once; + expect(callResponse, 'result delivered').to.have.been.called.once; }); - it('CALL to remote error', function () { + it('CALL error to remote', function () { sender.send = function () {}; cli.handle([WAMP.REGISTER, 1234, {}, 'func1']); From 458dcc63d017ffb1fcdefd718c2819d972691cb4 Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Sun, 7 May 2017 23:43:13 -0400 Subject: [PATCH 30/32] exclude_me option of publish --- lib/realm.js | 15 ++++++++---- package.json | 4 ++-- test/auth.js | 8 +++---- test/realm.js | 64 ++++++++++++++++++++++++++++++------------------- test/session.js | 14 +++++------ 5 files changed, 63 insertions(+), 42 deletions(-) diff --git a/lib/realm.js b/lib/realm.js index ed60fac..33da2f3 100644 --- a/lib/realm.js +++ b/lib/realm.js @@ -45,9 +45,9 @@ function Api(router, realm) { delete _rpc[topicId]; return realm.unsubstopic(this, tools.randomId(), topicId); }; - this.publish = function (topicUri, args, kwargs) { + this.publish = function (topicUri, args, kwargs, options) { var requestId = tools.randomId(); - realm.publish(this, requestId, topicUri, {}, args, kwargs); + realm.publish(this, requestId, topicUri, options, args, kwargs); }; // override/internal part @@ -173,15 +173,22 @@ function Realm(router) { return topicUri; }; + // By default, a Publisher of an event will not itself receive an event published, even when subscribed to the topic the Publisher is publishing to. + // If supported by the Broker, this behavior can be overridden via the option exclude_me set to false. + // session.publish('com.myapp.hello', ['Hello, world!'], {}, {exclude_me: false}); + this.publish = function(session, requestId, topicUri, options, args, kwargs) { var publicationId = tools.randomId(); if (_topics.hasOwnProperty(topicUri)) { for(var subscriptionId in _topics[topicUri]) { var destSession = router.getSession(_topics[topicUri][subscriptionId]); if (destSession) { - destSession.sendEvent(parseInt(subscriptionId), publicationId, args, kwargs); + if (session.sessionId !== destSession.sessionId || + (options && false === options.exclude_me) + ) + destSession.sendEvent(parseInt(subscriptionId), publicationId, args, kwargs); } else { -// delete _topics[topicUri][subscriptionId]; + delete _topics[topicUri][subscriptionId]; } } } diff --git a/package.json b/package.json index 0087328..0396841 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "gyper.wamp", + "name": "fox.wamp", "version": "0.2.0", "description": "Basic WebSocket Application Messaging Protocol (WAMP) V2 router library", "author": { @@ -12,7 +12,7 @@ ], "repository": { "type": "git", - "url": "https://github.com/kalmyk/wamp.rt.git" + "url": "https://github.com/kalmyk/fox.wamp.git" }, "dependencies": { "ws": "~1", diff --git a/test/auth.js b/test/auth.js index 23079cc..c63f6b3 100644 --- a/test/auth.js +++ b/test/auth.js @@ -48,7 +48,7 @@ describe('authenticate', function() { } ); cli.handle([WAMP.HELLO, 'test', {authid: 'joe', authmethods:['ticket']}]); - expect(sender.send).to.have.been.called.once; + expect(sender.send).to.have.been.called.once(); sender.send = chai.spy( function (msg, callback) { @@ -57,7 +57,7 @@ describe('authenticate', function() { } ); cli.handle([WAMP.AUTHENTICATE, 'incorrect-secret']); - expect(sender.send).to.have.been.called.once; + expect(sender.send).to.have.been.called.once(); }); it('Joe AUTH:OK', function () { @@ -68,7 +68,7 @@ describe('authenticate', function() { } ); cli.handle([WAMP.HELLO, 'test', {authid: 'joe', authmethods:['ticket']}]); - expect(sender.send).to.have.been.called.once; + expect(sender.send).to.have.been.called.once(); sender.send = chai.spy( function (msg, callback) { @@ -76,7 +76,7 @@ describe('authenticate', function() { } ); cli.handle([WAMP.AUTHENTICATE, 'test-joe-secret']); - expect(sender.send).to.have.been.called.once; + expect(sender.send).to.have.been.called.once(); }); }); diff --git a/test/realm.js b/test/realm.js index cdda6f0..981971e 100644 --- a/test/realm.js +++ b/test/realm.js @@ -50,7 +50,7 @@ describe('protocol', function() { } ); cli.handle([WAMP.CALL, 1234, {}, 'any.function.name', []]); - expect(sender.send).to.have.been.called.once; + expect(sender.send).to.have.been.called.once(); }); it('cleanup RPC API', function () { @@ -58,7 +58,7 @@ describe('protocol', function() { api.regrpc('func1', procSpy); expect(realm.cleanupRPC(api)).to.deep.equal(['func1']); expect(realm.cleanupRPC(api)).to.deep.equal([]); - expect(procSpy).to.not.have.been.called; + expect(procSpy).to.not.have.been.called(); }); it('CALL to router', function () { @@ -76,8 +76,8 @@ describe('protocol', function() { } ); cli.handle([WAMP.CALL, 1234, {}, 'func1', ['arg1', 'arg2'], {'kArg':'kVal'}]); - expect(procSpy, 'RPC delivered').to.have.been.called.once; - expect(sender.send, 'result delivered').to.have.been.called.once; + expect(procSpy, 'RPC delivered').to.have.been.called.once(); + expect(sender.send, 'result delivered').to.have.been.called.once(); expect(api.unregrpc(regId)).to.equal('func1'); }); @@ -97,8 +97,8 @@ describe('protocol', function() { ); cli.handle([WAMP.CALL, 1234, {}, 'func1', ['arg1', 'arg2'], {'kArg':'kVal'}]); api.resrpc(callId, 1, ['result.1','result.2'], {kVal:'kRes'}); - expect(procSpy).to.have.been.called.once; - expect(sender.send).to.have.been.called.once; + expect(procSpy).to.have.been.called.once(); + expect(sender.send).to.have.been.called.once(); }); it('UNREGISTER error', function () { @@ -112,7 +112,7 @@ describe('protocol', function() { } ); cli.handle([WAMP.UNREGISTER, 2345, 1234567890]); - expect(sender.send, 'unregistration confirmed').to.have.been.called.once; + expect(sender.send, 'unregistration confirmed').to.have.been.called.once(); }); it('UNREGISTER', function () { @@ -126,7 +126,7 @@ describe('protocol', function() { } ); cli.handle([WAMP.REGISTER, 1234, {}, 'func1']); - expect(sender.send, 'registration confirmed').to.have.been.called.once; + expect(sender.send, 'registration confirmed').to.have.been.called.once(); sender.send = chai.spy( function (msg, callback) { @@ -135,7 +135,7 @@ describe('protocol', function() { } ); cli.handle([WAMP.UNREGISTER, 2345, registrationId]); - expect(sender.send, 'unregistration confirmed').to.have.been.called.once; + expect(sender.send, 'unregistration confirmed').to.have.been.called.once(); }); it('CALL to remote', function () { @@ -149,7 +149,7 @@ describe('protocol', function() { } ); cli.handle([WAMP.REGISTER, 1234, {}, 'func1']); - expect(sender.send, 'registration confirmed').to.have.been.called.once; + expect(sender.send, 'registration confirmed').to.have.been.called.once(); var callId = null; sender.send = chai.spy( @@ -168,12 +168,12 @@ describe('protocol', function() { expect(kwargs).to.deep.equal({foo:'bar'}, 'kwargs call spy response'); }); api.callrpc('func1', ['arg.1','arg.2'], {kVal:'kRes'}, callResponse); - expect(sender.send, 'invocation received').to.have.been.called.once; + expect(sender.send, 'invocation received').to.have.been.called.once(); // return the function result cli.handle([WAMP.YIELD, callId, {}, ['result.1','result.2'], {foo:'bar'}]); - expect(callResponse, 'result delivered').to.have.been.called.once; + expect(callResponse, 'result delivered').to.have.been.called.once(); }); it('CALL error to remote', function () { @@ -191,13 +191,13 @@ describe('protocol', function() { expect(args).to.deep.equal(['err.detail.1','err.detail.2']); }); api.callrpc('func1', ['arg.1','arg.2'], {kVal:'kRes'}, callSpy); - expect(sender.send, 'invocation received').to.have.been.called.once; + expect(sender.send, 'invocation received').to.have.been.called.once(); cli.handle([WAMP.ERROR, WAMP.INVOCATION, callId, {}, 'wamp.error.runtime_error', ['err.detail.1','err.detail.2']]); - expect(callSpy, 'error delivered').to.have.been.called.once; + expect(callSpy, 'error delivered').to.have.been.called.once(); }); - it('UNSUBSCRIBE error', function () { + it('UNSUBSCRIBE-ERROR', function () { sender.send = chai.spy( function (msg, callback) { expect(msg[0]).to.equal(WAMP.ERROR); @@ -208,10 +208,10 @@ describe('protocol', function() { } ); cli.handle([WAMP.UNSUBSCRIBE, 2345, 1234567890]); - expect(sender.send, 'unsubscription confirmed').to.have.been.called.once; + expect(sender.send, 'unsubscription confirmed').to.have.been.called.once(); }); - it('UNSUBSCRIBE', function () { + it('UNSUBSCRIBE-OK', function () { var subscriptionId = null; sender.send = chai.spy( @@ -222,7 +222,7 @@ describe('protocol', function() { } ); cli.handle([WAMP.SUBSCRIBE, 1234, {}, 'topic1']); - expect(sender.send, 'subscription confirmed').to.have.been.called.once; + expect(sender.send, 'subscription confirmed').to.have.been.called.once(); sender.send = chai.spy( function (msg, callback) { @@ -231,7 +231,7 @@ describe('protocol', function() { } ); cli.handle([WAMP.UNSUBSCRIBE, 2345, subscriptionId]); - expect(sender.send, 'unsubscription confirmed').to.have.been.called.once; + expect(sender.send, 'unsubscription confirmed').to.have.been.called.once(); }); it('cleanup Topic API', function () { @@ -239,7 +239,21 @@ describe('protocol', function() { api.substopic('topic1', subSpy); expect(cli.realm.cleanupTopic(api)).to.deep.equal(['topic1']); expect(cli.realm.cleanupTopic(api)).to.deep.equal([]); - expect(subSpy).to.not.have.been.called; + expect(subSpy).to.not.have.been.called(); + }); + + it('PUBLISH default exclude_me:true', function () { + var subSpy = chai.spy(function () {}); + api.substopic('topic1', subSpy); + api.publish('topic1', [], {}); + expect(subSpy).to.not.have.been.called(); + }); + + it('PUBLISH exclude_me:false', function () { + var subSpy = chai.spy(function () {}); + api.substopic('topic1', subSpy); + api.publish('topic1', [], {}, {exclude_me:false}); + expect(subSpy).to.have.been.called.once(); }); it('PUBLISH to remote', function () { @@ -253,7 +267,7 @@ describe('protocol', function() { } ); cli.handle([WAMP.SUBSCRIBE, 1234, {}, 'topic1']); - expect(sender.send, 'subscription confirmed').to.have.been.called.once; + expect(sender.send, 'subscription confirmed').to.have.been.called.once(); sender.send = chai.spy( function (msg, callback) { @@ -266,7 +280,7 @@ describe('protocol', function() { } ); api.publish('topic1', ['arg.1','arg.2'], {foo:'bar'}); - expect(sender.send, 'publication received').to.have.been.called.once; + expect(sender.send, 'publication received').to.have.been.called.once(); }); it('SUBSCRIBE to remote', function () { @@ -285,11 +299,11 @@ describe('protocol', function() { } ); cli.handle([WAMP.PUBLISH, 1234, {}, "topic1", ['arg.1','arg.2'],{foo:'bar'}]); - expect(sender.send, 'published').to.not.have.been.called; + expect(sender.send, 'published').to.not.have.been.called(); cli.handle([WAMP.PUBLISH, 2345, {"acknowledge":true}, "topic1", ['arg.1','arg.2'],{foo:'bar'}]); - expect(sender.send, 'published').to.have.been.called.once; + expect(sender.send, 'published').to.have.been.called.once(); - expect(subSpy, 'publication done').to.have.been.called.twice; + expect(subSpy, 'publication done').to.have.been.called.twice(); expect(api.unsubstopic(subId)).to.equal('topic1'); }); }); diff --git a/test/session.js b/test/session.js index 6d90abf..683f157 100644 --- a/test/session.js +++ b/test/session.js @@ -36,14 +36,14 @@ describe('session', function() { } ); cli.handle([WAMP.HELLO, 'test', {}]); - expect(sender.send).to.have.been.called.once; + expect(sender.send).to.have.been.called.once(); // second hello command raises error and disconnects the user sender.send = chai.spy(function (msg, callback) {}); sender.close = chai.spy(function (error, reason) {}); cli.handle([WAMP.HELLO, 'test', {}]); - expect(sender.send).to.not.have.been.called; - expect(sender.close).to.have.been.called.once; + expect(sender.send).to.not.have.been.called(); + expect(sender.close).to.have.been.called.once(); }); it('GOODBYE', function () { @@ -57,8 +57,8 @@ describe('session', function() { function (error) {} ); cli.handle([WAMP.GOODBYE]); - expect(sender.send).to.have.been.called.once; - expect(sender.close).to.have.been.called.once; + expect(sender.send).to.have.been.called.once(); + expect(sender.close).to.have.been.called.once(); }); it('CALL to no realm RPC', function () { @@ -71,7 +71,7 @@ describe('session', function() { } ); cli.handle([WAMP.CALL, 1234, {}, 'any.function.name', []]); - expect(sender.send).to.have.been.called.once; + expect(sender.send).to.have.been.called.once(); }); it('REGISTER to no realm', function () { @@ -84,6 +84,6 @@ describe('session', function() { } ); cli.handle([WAMP.REGISTER, 1234, {}, 'func1']); - expect(sender.send, 'registration failed').to.have.been.called.once; + expect(sender.send, 'registration failed').to.have.been.called.once(); }); }); From 5dcf0ed24643abba1718f2ad09b65384c37297da Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Wed, 17 May 2017 21:06:05 -0400 Subject: [PATCH 31/32] Concrete topic published to --- README.md | 16 +++++++++++----- democli/backend.js | 35 ++++++++++++++++++++--------------- lib/handlers.js | 4 ++-- lib/realm.js | 3 ++- test/realm.js | 2 +- 5 files changed, 36 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index c9f60e2..40580ff 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,20 @@ Install using npm. Depending on what you want to do, your mileage may vary. fox.wamp has been inspired by the following Open Source projects: -- [wamp.io](https://github.com/nicokaiser/wamp.io) -- [wamp.rt](https://github.com/Orange-OpenSource/wamp.rt) +- [wamp.rt](https://github.com/Orange-OpenSource/wamp.rt) +- [wamp.io](https://github.com/nicokaiser/wamp.io) ## Changes: +2017-05-17: +- Concrete topic published to + +2017-05-07: +- exclude_me option of publish + +2017-04-26: +- integration with [StatsD](https://github.com/etsy/statsd) + 2016-04-03: - ticket auth support added @@ -22,6 +31,3 @@ fox.wamp has been inspired by the following Open Source projects: - internal api moved to realm - callrpc method has args & kwargs arguments - publish method does not require message id - -2017-04-26: -- integration with [StatsD](https://github.com/etsy/statsd) diff --git a/democli/backend.js b/democli/backend.js index 50fc761..317652a 100644 --- a/democli/backend.js +++ b/democli/backend.js @@ -51,28 +51,33 @@ connection.onopen = function (session) { } ); - var currentSubscription = null; - var counter = 0; - // Define an event handler - function onEvent(publishArgs, kwargs) { - console.log('Event', currentSubscription.topic,'received args', publishArgs, 'kwargs ',kwargs); - counter++; - - if (counter >= 20) { - session.unsubscribe(currentSubscription).then(function(gone) { - console.log("unsubscribe successfull"); - }, function(error) { - console.log("unsubscribe failed", error); - }); - } + function onEvent(publishArgs, kwargs, opts) { + console.log('Event', opts.topic, 'received args', publishArgs, 'kwargs ',kwargs); } // Subscribe to a topic session.subscribe('com.myapp.topic1', onEvent).then( function(subscription) { console.log("subscription successfull", subscription); - currentSubscription = subscription; + }, + function(error) { + console.log("subscription failed", error); + } + ); + + session.subscribe('wamp.session.on_join', onEvent).then( + function(subscription) { + console.log("subscription successfull", subscription); + }, + function(error) { + console.log("subscription failed", error); + } + ); + + session.subscribe('wamp.session.on_leave', onEvent).then( + function(subscription) { + console.log("subscription successfull", subscription); }, function(error) { console.log("subscription failed", error); diff --git a/lib/handlers.js b/lib/handlers.js index 21980f1..3bc4a41 100644 --- a/lib/handlers.js +++ b/lib/handlers.js @@ -65,12 +65,12 @@ var Facade = function () { this.sendPublished = function (requestId, publicationId) { this.send([WAMP.PUBLISHED, requestId, publicationId]); }; - this.sendEvent = function (subscriptionId, publicationId, args, kwargs) { + this.sendEvent = function (subscriptionId, publicationId, args, kwargs, eventOpts) { var msg = [ WAMP.EVENT, subscriptionId, publicationId, - {} + eventOpts ]; // Manage optional parameters args + kwargs if (args !== undefined) { diff --git a/lib/realm.js b/lib/realm.js index 33da2f3..f282dd5 100644 --- a/lib/realm.js +++ b/lib/realm.js @@ -180,13 +180,14 @@ function Realm(router) { this.publish = function(session, requestId, topicUri, options, args, kwargs) { var publicationId = tools.randomId(); if (_topics.hasOwnProperty(topicUri)) { + var eventOpts = {topic:topicUri}; for(var subscriptionId in _topics[topicUri]) { var destSession = router.getSession(_topics[topicUri][subscriptionId]); if (destSession) { if (session.sessionId !== destSession.sessionId || (options && false === options.exclude_me) ) - destSession.sendEvent(parseInt(subscriptionId), publicationId, args, kwargs); + destSession.sendEvent(parseInt(subscriptionId), publicationId, args, kwargs, eventOpts); } else { delete _topics[topicUri][subscriptionId]; } diff --git a/test/realm.js b/test/realm.js index 981971e..2a28c2f 100644 --- a/test/realm.js +++ b/test/realm.js @@ -274,7 +274,7 @@ describe('protocol', function() { expect(msg[0]).to.equal(WAMP.EVENT); expect(msg[1]).to.equal(subscriptionId); // 2 published message Id - // 3 options? + expect(msg[3]).to.deep.equal({topic:'topic1'}); expect(msg[4]).to.deep.equal(['arg.1','arg.2']); expect(msg[5]).to.deep.equal({foo:'bar'}); } From 3aa61badd79860ca28641f4b4dccdb5d4f14c414 Mon Sep 17 00:00:00 2001 From: Anatoly Tsapkov Date: Thu, 18 May 2017 00:01:50 -0400 Subject: [PATCH 32/32] Progressive Calls --- README.md | 1 + democli/backend.js | 28 +++++++++++++++++++++++----- democli/frontend.js | 23 +++++++++++++++-------- lib/handlers.js | 10 +++++----- lib/realm.js | 42 +++++++++++++++++++++++++++--------------- test/realm.js | 42 +++++++++++++++++++++++++++++++++++++++++- 6 files changed, 112 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 40580ff..d224455 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ fox.wamp has been inspired by the following Open Source projects: ## Changes: 2017-05-17: - Concrete topic published to +- Progressive Calls (receive_progress & progress) 2017-05-07: - exclude_me option of publish diff --git a/democli/backend.js b/democli/backend.js index 317652a..3e26e96 100644 --- a/democli/backend.js +++ b/democli/backend.js @@ -20,13 +20,31 @@ connection.onopen = function (session) { var reg = null; var reg2 = null; - function utcnow() { - console.log("Someone is calling me;)"); - now = new Date(); - return now.toISOString(); + function utcprogress(args, kwargs, options) { + console.log("Someone is calling utc function", args, kwargs, options); + if (options.progress) { + setTimeout(function () { + var now = new Date(); + options.progress(now.toISOString()); + }, 100); + setTimeout(function () { + var now = new Date(); + options.progress(now.toISOString()); + }, 200); + return new Promise((resolve, reject) => { + setTimeout(function () { + var now = new Date(); + resolve(now.toISOString()); + }, 300); + }); + } + else { + var now = new Date(); + return now.toISOString(); + } } - session.register('com.timeservice.now', utcnow).then( + session.register('com.timeservice.now', utcprogress).then( function (registration) { console.log("Procedure registered:", registration.id); reg = registration; diff --git a/democli/frontend.js b/democli/frontend.js index 635c78c..9b2faae 100644 --- a/democli/frontend.js +++ b/democli/frontend.js @@ -7,7 +7,7 @@ program .option('-i, --ip ', 'Server IP address','127.0.0.1') .parse(process.argv); -var connectUrl = 'ws://' + program.ip + ':' + program.port; +var connectUrl = 'ws://' + program.ip + ':' + program.port + '/ws'; console.log('connectUrl:', connectUrl); var user = "joe"; @@ -25,7 +25,7 @@ function onchallenge (session, method, extra) { var connection = new autobahn.Connection({ url: connectUrl, realm: 'realm1', - authmethods: ["ticket", "wampcra"], +// authmethods: ["ticket", "wampcra"], authid: user, onchallenge: onchallenge }); @@ -35,7 +35,7 @@ connection.onopen = function (session, details) { session.log("Session open."); var starttime = Date.now(); - session.call('com.timeservice.now').then( + var c1 = session.call('com.timeservice.now', [], {}, {receive_progress:true}).then( function (now) { // this method returns a plain value session.log("Call com.timeservice.now completed in " + @@ -43,8 +43,12 @@ connection.onopen = function (session, details) { " ms: result =", now); }, function (error) { - console.log("Call failed:", error); - }); + console.log("Call failed:", error); + }, + function (progress) { + console.log("Call progress:", progress); + } + ); session.call('com.echoservice.echo').then( function (res) { @@ -125,16 +129,19 @@ connection.onopen = function (session, details) { session.publish('com.myapp.topic1', ["Arg1", "Arg2" ], { "kwarg1": "kwarg1", "kwarg2": "kwarg2"}, { acknowledge : false }); - session.publish('com.myapp.topic1', [ "Arg_1", "Arg_2" ], {}, { acknowledge : true }).then( + var p1 = session.publish('com.myapp.topic1', [ "Arg_1", "Arg_2" ], {}, { acknowledge : true }).then( function(publication) { console.log("published, publication ID is ", publication); - connection.close(); }, function(error) { console.log("publication error", error); - connection.close(); + return Promise.resolve(true); } ); + + Promise.all([c1,p1]).then(function () { + connection.close(); + }); }; connection.onclose = function (reason, details) { diff --git a/lib/handlers.js b/lib/handlers.js index 3bc4a41..61bdd35 100644 --- a/lib/handlers.js +++ b/lib/handlers.js @@ -29,18 +29,18 @@ var Facade = function () { if (requestId) // do not send on disconnect this.send([WAMP.UNREGISTERED, requestId]); }; - this.sendInvoke = function (regId, invId, args, kwargs) { + this.sendInvoke = function (regId, invId, args, kwargs, options) { var msg = [ WAMP.INVOCATION, invId, regId, - {}, // details + options, ]; if (undefined !== args) msg.push(args); if (undefined !== kwargs) msg.push(kwargs); this.send(msg); }; - this.sendYield = function (invId, err, args, kwargs) { + this.sendResult = function (invId, err, args, kwargs, options) { if (err) { this.sendError(WAMP.CALL, invId, "wamp.error.callee_failure"); } @@ -48,7 +48,7 @@ var Facade = function () { var msg = [ WAMP.RESULT, invId, - {}, + options, ]; if (undefined !== args) msg.push(args); if (undefined !== kwargs) msg.push(kwargs); @@ -173,7 +173,7 @@ handlers[WAMP.YIELD] = function (args) { var fArgs = args.shift() || []; var kwArgs = args.shift(); if (this.checkRealm(WAMP.CALL, invId)) - this.realm.resrpc(this, invId, null, fArgs, kwArgs); + this.realm.resrpc(this, invId, null /* no error */, fArgs, kwArgs, options); }; handlers[WAMP.SUBSCRIBE] = function(args) { diff --git a/lib/realm.js b/lib/realm.js index f282dd5..33ed003 100644 --- a/lib/realm.js +++ b/lib/realm.js @@ -14,7 +14,7 @@ function Api(router, realm) { router.registerSession(this); // API functions - // regrpc callback = function(id, args, kwargs) + // regrpc callback = function(id, args, kwargs, options) this.regrpc = function(uri, callback) { var regId = realm.regrpc(this, tools.randomId(), uri); if (regId) { @@ -27,14 +27,14 @@ function Api(router, realm) { delete _rpc[regId]; return uri; }; - this.callrpc = function (uri, args, kwargs, callback) { + this.callrpc = function (uri, args, kwargs, callback, options) { var callId = tools.randomId(); - if (realm.callrpc(this, callId, uri, {}, args, kwargs)) { + if (realm.callrpc(this, callId, uri, options, args, kwargs)) { _callback[callId] = callback; } }; - this.resrpc = function (invId, err, args, kwargs) { - return realm.resrpc(this, invId, err, args, kwargs); + this.resrpc = function (invId, err, args, kwargs, options) { + return realm.resrpc(this, invId, err, args, kwargs, options); }; this.substopic = function(topicUri, callback) { var topicId = realm.substopic(this, tools.randomId(), topicUri, {}); @@ -51,15 +51,17 @@ function Api(router, realm) { }; // override/internal part - this.sendInvoke = function (regId, invId, args, kwargs) { + this.sendInvoke = function (regId, invId, args, kwargs, options) { if (_rpc.hasOwnProperty(regId)) { - _rpc[regId](invId, args, kwargs); + _rpc[regId](invId, args, kwargs, options); } }; - this.sendYield = function (callId, err, args, kwargs) { + this.sendResult = function (callId, err, args, kwargs, options) { var callback = _callback[callId]; - delete _callback[callId]; - callback(err, args, kwargs); + if (!options || !options.progress) { + delete _callback[callId]; + } + callback(err, args, kwargs, options); }; this.sendEvent = function (subscriptionId, publicationId, args, kwargs) { if (_rpc.hasOwnProperty(subscriptionId)) { @@ -124,22 +126,32 @@ function Realm(router) { if (destSession) { var invId = tools.randomId(); _pending[invId] = [callId, session.sessionId]; - destSession.sendInvoke(_rpcs[procUri].regId, invId, args, kwargs); + var invOpts = {}; + if (options && options.receive_progress) { + invOpts.receive_progress = true; + } + destSession.sendInvoke(_rpcs[procUri].regId, invId, args, kwargs, invOpts); return invId; } else { - // delete + delete _rpcs[procUri]; } return false; }; - this.resrpc = function(session, invId, err, args, kwargs) { + this.resrpc = function(session, invId, err, args, kwargs, options) { + var resOpts = {}; + if (options && options.progress) { + resOpts.progress = true; + }; if (_pending.hasOwnProperty(invId)) { var destSession = router.getSession(_pending[invId][1]); if (destSession) { - destSession.sendYield(_pending[invId][0], err, args, kwargs); + destSession.sendResult(_pending[invId][0], err, args, kwargs, resOpts); } } - delete _pending[invId]; + if (!resOpts.progress) { + delete _pending[invId]; + } }; // Topic Management diff --git a/test/realm.js b/test/realm.js index 2a28c2f..bc86462 100644 --- a/test/realm.js +++ b/test/realm.js @@ -157,7 +157,7 @@ describe('protocol', function() { expect(msg[0]).to.equal(WAMP.INVOCATION); callId = msg[1]; expect(msg[2]).to.equal(registrationId); - // 3 options? + expect(msg[3]).to.deep.equal({}); // options expect(msg[4]).to.deep.equal(['arg.1','arg.2']); expect(msg[5]).to.deep.equal({kVal:'kRes'}); } @@ -197,6 +197,46 @@ describe('protocol', function() { expect(callSpy, 'error delivered').to.have.been.called.once(); }); + it('Progress remote CALL', function () { + sender.send = function (msg, callback) {}; + cli.handle([WAMP.REGISTER, 1234, {}, 'func1']); + + var callId = null; + sender.send = chai.spy( + function (msg, callback) { + expect(msg[0]).to.equal(WAMP.INVOCATION); + callId = msg[1]; + // registrationId + expect(msg[3]).to.deep.equal({receive_progress:true}); + } + ); + var result; + var options; + var callResponse = chai.spy(function(err, args, kwargs, options) { + expect(err).to.equal(null); + expect(args).to.deep.equal(result, 'args call spy response'); + expect(options).to.deep.equal(options, 'progress 1'); + }); + api.callrpc('func1', [], {}, callResponse, {receive_progress:1}); + expect(sender.send, 'invocation received').to.have.been.called.once(); + + result = ['result.1']; + options = {progress:true}; + cli.handle([WAMP.YIELD, callId, {progress:true}, ['result.1']]); + + result = ['result.2']; + options = {progress:true}; + cli.handle([WAMP.YIELD, callId, {progress:true}, ['result.2']]); + + result = ['result.3.final']; + options = {}; + cli.handle([WAMP.YIELD, callId, {}, ['result.3.final']]); + + cli.handle([WAMP.YIELD, callId, {}, ['result.not_delivered']]); + + expect(callResponse, 'result delivered').to.have.been.called.exactly(3); + }); + it('UNSUBSCRIBE-ERROR', function () { sender.send = chai.spy( function (msg, callback) {