RPC callbacks refactor
authorDarren <darren@darrenwhitlen.com>
Sun, 1 Sep 2013 13:03:30 +0000 (14:03 +0100)
committerDarren <darren@darrenwhitlen.com>
Sun, 1 Sep 2013 13:18:22 +0000 (14:18 +0100)
client/assets/libs/engine.io.tools.js
server/websocketrpc.js

index 47e1a15b14ab339991df1227fccddc0fa31f52b1..9852e8a0f2aa7f8555ef5d21969aa3664658b2ae 100644 (file)
@@ -1,5 +1,5 @@
 var EngineioTools = {
-       ReconnectingSocket: function ReconnectingSocket(server_uri, socket_options) {
+    ReconnectingSocket: function ReconnectingSocket(server_uri, socket_options) {
         var connected = false;
         var is_reconnecting = false;
 
@@ -101,166 +101,185 @@ var EngineioTools = {
 
 
     Rpc: (function(){
-               /*
-                   Create a document explaining the protocol
-               */
+        /*
+            TODO:
+            Create a document explaining the protocol
+            Some way to expire unused callbacks? TTL? expireCallback() function?
+        */
 
-               function WebsocketRpc(eio_socket) {
-                   var self = this;
+        function WebsocketRpc(eio_socket) {
+            var self = this;
 
-                   this._next_id = 0;
-                   this._callbacks = {};
-                   this._socket = eio_socket;
+            this._next_id = 0;
+            this._callbacks = {};
+            this._socket = eio_socket;
 
-                   this._mixinEmitter();
-                   this._bindSocketListeners();
-               }
+            this._mixinEmitter();
+            this._bindSocketListeners();
+        }
 
 
-               WebsocketRpc.prototype._bindSocketListeners = function() {
-                   var self = this;
+        WebsocketRpc.prototype._bindSocketListeners = function() {
+            var self = this;
 
-                   // Proxy the onMessage listener
-                   this._onMessageProxy = function rpcOnMessageBoundFunction(){
-                       self._onMessage.apply(self, arguments);
-                   };
-                   this._socket.on('message', this._onMessageProxy);
-               };
+            // Proxy the onMessage listener
+            this._onMessageProxy = function rpcOnMessageBoundFunction(){
+                self._onMessage.apply(self, arguments);
+            };
+            this._socket.on('message', this._onMessageProxy);
+        };
 
 
 
-               WebsocketRpc.prototype.dispose = function() {
-                   if (this._onMessageProxy) {
-                       this._socket.removeListener('message', this._onMessageProxy);
-                       delete this._onMessageProxy;
-                   }
+        WebsocketRpc.prototype.dispose = function() {
+            if (this._onMessageProxy) {
+                this._socket.removeListener('message', this._onMessageProxy);
+                delete this._onMessageProxy;
+            }
 
-                   this.removeAllListeners();
-               };
+            this.removeAllListeners();
+        };
 
 
 
 
-               /**
-                * The engine.io socket already has an emitter mixin so steal it from there
-                */
-               WebsocketRpc.prototype._mixinEmitter = function() {
-                   var funcs = ['on', 'once', 'off', 'removeListener', 'removeAllListeners', 'emit', 'listeners', 'hasListeners'];
+        /**
+         * The engine.io socket already has an emitter mixin so steal it from there
+         */
+        WebsocketRpc.prototype._mixinEmitter = function() {
+            var funcs = ['on', 'once', 'off', 'removeListener', 'removeAllListeners', 'emit', 'listeners', 'hasListeners'];
 
-                   for (var i=0; i<funcs.length; i++) {
-                       if (typeof this._socket[funcs[i]] === 'function')
-                           this[funcs[i]] = this._socket[funcs[i]];
-                   }
-               };
+            for (var i=0; i<funcs.length; i++) {
+                if (typeof this._socket[funcs[i]] === 'function')
+                    this[funcs[i]] = this._socket[funcs[i]];
+            }
+        };
 
 
-               /**
-                * Check if a packet is a valid RPC call
-                */
-               WebsocketRpc.prototype._isCall = function(packet) {
-                   return (typeof packet.method !== 'undefined' &&
-                           typeof packet.params !== 'undefined');
-               };
+        /**
+         * Check if a packet is a valid RPC call
+         */
+        WebsocketRpc.prototype._isCall = function(packet) {
+            return (typeof packet.method !== 'undefined' &&
+                    typeof packet.params !== 'undefined');
+        };
 
 
-               /**
-                * Check if a packet is a valid RPC response
-                */
-               WebsocketRpc.prototype._isResponse = function(packet) {
-                   return (typeof packet.id !== 'undefined' &&
-                           typeof packet.response !== 'undefined');
-               };
+        /**
+         * Check if a packet is a valid RPC response
+         */
+        WebsocketRpc.prototype._isResponse = function(packet) {
+            return (typeof packet.id !== 'undefined' &&
+                    typeof packet.response !== 'undefined');
+        };
 
 
 
-               /**
-                * Make an RPC call
-                * First argument must be the method name to call
-                * If the last argument is a function, it is used as a callback
-                * All other arguments are passed to the RPC method
-                * Eg. Rpc.call('namespace.method_name', 1, 2, 3, callbackFn)
-                */
-               WebsocketRpc.prototype.call = function(method) {
-                   var params, callback, packet;
+        /**
+         * Make an RPC call
+         * First argument must be the method name to call
+         * If the last argument is a function, it is used as a callback
+         * All other arguments are passed to the RPC method
+         * Eg. Rpc.call('namespace.method_name', 1, 2, 3, callbackFn)
+         */
+        WebsocketRpc.prototype.call = function(method) {
+            var params, callback, packet;
 
-                   // Get a normal array of passed in params
-                   params = Array.prototype.slice.call(arguments, 1, arguments.length);
+            // Get a normal array of passed in arguments
+            params = Array.prototype.slice.call(arguments, 1, arguments.length);
+
+            // If the last argument is a function, take it as a callback and strip it out
+            if (typeof params[params.length-1] === 'function') {
+                callback = params[params.length-1];
+                params = params.slice(0, params.length-1);
+            }
+
+            packet = {
+                method: method,
+                params: params
+            };
+
+            if (typeof callback === 'function') {
+                packet.id = this._next_id;
+
+                this._next_id++;
+                this._callbacks[packet.id] = callback;
+            }
 
-                   // If the last param is a function, take it as a callback and strip it out
-                   if (typeof params[params.length-1] === 'function') {
-                       callback = params[params.length-1];
-                       params = params.slice(0, params.length-1);
-                   }
+            this.send(packet);
+        };
 
-                   packet = {
-                       method: method,
-                       params: params
-                   };
 
-                   if (typeof callback === 'function') {
-                       packet.id = this._next_id;
+        /**
+         * Encode the packet into JSON and send it over the websocket
+         */
+        WebsocketRpc.prototype.send = function(packet) {
+            if (this._socket)
+                this._socket.send(JSON.stringify(packet));
+        };
 
-                       this._next_id++;
-                       this._callbacks[packet.id] = callback;
-                   }
 
-                   this.send(packet);
-               };
+        /**
+         * Handler for the websocket `message` event
+         */
+        WebsocketRpc.prototype._onMessage = function(message_raw) {
+            var self = this,
+                packet,
+                returnFn;
+
+            try {
+                packet = JSON.parse(message_raw);
+                if (!packet) throw 'Corrupt packet';
+            } catch(err) {
+                return;
+            }
+
+            if (this._isResponse(packet)) {
+                // If we have no callback waiting for this response, don't do anything
+                if (typeof this._callbacks[packet.id] !== 'function')
+                    return;
+
+                // Call and delete this callback once finished with it
+                this._callbacks[packet.id].apply(this, packet.response);
+                delete this._callbacks[packet.id];
+
+            } else if (this._isCall(packet)) {
+                // Calls with an ID may be responded to
+                if (typeof packet.id !== 'undefined') {
+                    returnFn = this._createReturnCallFn(packet.id);
+                } else {
+                    returnFn = this._noop;
+                }
+
+                this.emit.apply(this, [packet.method, returnFn].concat(packet.params));
+            }
+        };
 
 
-               /**
-                * Encode the packet into JSON and send it over the websocket
-                */
-               WebsocketRpc.prototype.send = function(packet) {
-                   if (this._socket)
-                       this._socket.send(JSON.stringify(packet));
-               };
+        /**
+         * Returns a function used as a callback when responding to a call
+         */
+        WebsocketRpc.prototype._createReturnCallFn = function(packet_id) {
+            var self = this;
 
+            return function returnCallFn() {
+                var value = Array.prototype.slice.call(arguments, 0);
 
-               /**
-                * Handler for the websocket `message` event
-                */
-               WebsocketRpc.prototype._onMessage = function(message_raw) {
-                   var self = this,
-                       packet,
-                       returnFn;
+                var ret_packet = {
+                    id: packet_id,
+                    response: value
+                };
 
-                   try {
-                       packet = JSON.parse(message_raw);
-                       if (!packet) throw 'Corrupt packet';
-                   } catch(err) {
-                       return;
-                   }
+                self.send(ret_packet);
+            };
+        };
 
-                   if (this._isResponse(packet)) {
-                       this._callbacks[packet.id].apply(this, packet.response);
-                       delete this._callbacks[packet.id];
 
-                   } else if (this._isCall(packet)) {
-                       // Calls with an ID may be responded to
-                       if (typeof packet.id !== 'undefined') {
-                           returnFn = function returnCallFn() {
-                               var value = Array.prototype.slice.call(arguments, 0);
-
-                               var ret_packet = {
-                                   id: packet.id,
-                                   response: value
-                               };
-
-                               self.send(ret_packet);
-                           };
-
-                       } else {
-                           returnFn = function noop(){};
-                       }
-
-                       this.emit.apply(this, [packet.method, returnFn].concat(packet.params));
-                   }
-               };
 
+        WebsocketRpc.prototype._noop = function() {};
 
 
-               return WebsocketRpc;
+        return WebsocketRpc;
 
     }())
 };
index 01554f82b6ad5bc0fd7c2041041304f923f2670f..01c3ef9f434a5171f19f08353d61894398f2c4ee 100644 (file)
@@ -1,5 +1,7 @@
 /*
+    TODO:
     Create a document explaining the protocol
+    Some way to expire unused callbacks? TTL? expireCallback() function?
 */
 
 function WebsocketRpc(eio_socket) {
@@ -80,10 +82,10 @@ WebsocketRpc.prototype._isResponse = function(packet) {
 WebsocketRpc.prototype.call = function(method) {
     var params, callback, packet;
 
-    // Get a normal array of passed in params
+    // Get a normal array of passed in arguments
     params = Array.prototype.slice.call(arguments, 1, arguments.length);
 
-    // If the last param is a function, take it as a callback and strip it out
+    // If the last argument is a function, take it as a callback and strip it out
     if (typeof params[params.length-1] === 'function') {
         callback = params[params.length-1];
         params = params.slice(0, params.length-1);
@@ -130,25 +132,20 @@ WebsocketRpc.prototype._onMessage = function(message_raw) {
     }
 
     if (this._isResponse(packet)) {
+        // If we have no callback waiting for this response, don't do anything
+        if (typeof this._callbacks[packet.id] !== 'function')
+            return;
+
+        // Call and delete this callback once finished with it
         this._callbacks[packet.id].apply(this, packet.response);
         delete this._callbacks[packet.id];
 
     } else if (this._isCall(packet)) {
         // Calls with an ID may be responded to
         if (typeof packet.id !== 'undefined') {
-            returnFn = function returnCallFn() {
-                var value = Array.prototype.slice.call(arguments, 0);
-
-                var ret_packet = {
-                    id: packet.id,
-                    response: value
-                };
-
-                self.send(ret_packet);
-            };
-
+            returnFn = this._createReturnCallFn(packet.id);
         } else {
-            returnFn = function noop(){};
+            returnFn = this._noop;
         }
 
         this.emit.apply(this, [packet.method, returnFn].concat(packet.params));
@@ -156,6 +153,29 @@ WebsocketRpc.prototype._onMessage = function(message_raw) {
 };
 
 
+/**
+ * Returns a function used as a callback when responding to a call
+ */
+WebsocketRpc.prototype._createReturnCallFn = function(packet_id) {
+    var self = this;
+
+    return function returnCallFn() {
+        var value = Array.prototype.slice.call(arguments, 0);
+
+        var ret_packet = {
+            id: packet_id,
+            response: value
+        };
+
+        self.send(ret_packet);
+    };
+};
+
+
+
+WebsocketRpc.prototype._noop = function() {};
+
+
 
 
 // If running a node module, set the exports