3 Create a document explaining the protocol
4 Some way to expire unused callbacks? TTL? expireCallback() function?
8 * Wrapper around creating a new WebsocketRpcCaller
9 * This lets us use the WebsocketRpc object as a function
11 function WebsocketRpc(eio_socket
) {
12 var caller
= new WebsocketRpcCaller(eio_socket
);
13 var ret
= function WebsocketRpcInstance() {
14 return ret
.makeCall
.apply(ret
, arguments
);
17 for(var prop
in caller
){
18 ret
[prop
] = caller
[prop
];
22 ret
._bindSocketListeners();
28 function WebsocketRpcCaller(eio_socket
) {
30 this._rpc_callbacks
= {};
31 this._socket
= eio_socket
;
35 WebsocketRpcCaller
.prototype._bindSocketListeners = function() {
38 // Proxy the onMessage listener
39 this._onMessageProxy
= function rpcOnMessageBoundFunction(){
40 self
._onMessage
.apply(self
, arguments
);
42 this._socket
.on('message', this._onMessageProxy
);
47 WebsocketRpcCaller
.prototype.dispose = function() {
48 if (this._onMessageProxy
) {
49 this._socket
.removeListener('message', this._onMessageProxy
);
50 delete this._onMessageProxy
;
53 this.removeAllListeners();
60 * The engine.io socket already has an emitter mixin so steal it from there
62 WebsocketRpcCaller
.prototype._mixinEmitter = function() {
63 var funcs
= ['on', 'once', 'off', 'removeListener', 'removeAllListeners', 'emit', 'listeners', 'hasListeners'];
65 for (var i
=0; i
<funcs
.length
; i
++) {
66 if (typeof this._socket
[funcs
[i
]] === 'function')
67 this[funcs
[i
]] = this._socket
[funcs
[i
]];
73 * Check if a packet is a valid RPC call
75 WebsocketRpcCaller
.prototype._isCall = function(packet
) {
76 return (typeof packet
.method
!== 'undefined' &&
77 typeof packet
.params
!== 'undefined');
82 * Check if a packet is a valid RPC response
84 WebsocketRpcCaller
.prototype._isResponse = function(packet
) {
85 return (typeof packet
.id
!== 'undefined' &&
86 typeof packet
.response
!== 'undefined');
93 * First argument must be the method name to call
94 * If the last argument is a function, it is used as a callback
95 * All other arguments are passed to the RPC method
96 * Eg. Rpc.makeCall('namespace.method_name', 1, 2, 3, callbackFn)
98 WebsocketRpcCaller
.prototype.makeCall = function(method
) {
99 var params
, callback
, packet
;
101 // Get a normal array of passed in arguments
102 params
= Array
.prototype.slice
.call(arguments
, 1, arguments
.length
);
104 // If the last argument is a function, take it as a callback and strip it out
105 if (typeof params
[params
.length
-1] === 'function') {
106 callback
= params
[params
.length
-1];
107 params
= params
.slice(0, params
.length
-1);
115 if (typeof callback
=== 'function') {
116 packet
.id
= this._next_id
;
119 this._rpc_callbacks
[packet
.id
] = callback
;
127 * Encode the packet into JSON and send it over the websocket
129 WebsocketRpcCaller
.prototype.send = function(packet
) {
131 this._socket
.send(JSON
.stringify(packet
));
136 * Handler for the websocket `message` event
138 WebsocketRpcCaller
.prototype._onMessage = function(message_raw
) {
145 packet
= JSON
.parse(message_raw
);
146 if (!packet
) throw 'Corrupt packet';
151 if (this._isResponse(packet
)) {
152 // If we have no callback waiting for this response, don't do anything
153 if (typeof this._rpc_callbacks
[packet
.id
] !== 'function')
156 // Delete the callback before calling it. If any exceptions accur within the callback
157 // we don't have to worry about the delete not happening
158 callback
= this._rpc_callbacks
[packet
.id
];
159 delete this._rpc_callbacks
[packet
.id
];
161 callback
.apply(this, packet
.response
);
163 } else if (this._isCall(packet
)) {
164 // Calls with an ID may be responded to
165 if (typeof packet
.id
!== 'undefined') {
166 returnFn
= this._createReturnCallFn(packet
.id
);
168 returnFn
= this._noop
;
171 this.emit
.apply(this, ['all', packet
.method
, returnFn
].concat(packet
.params
));
172 this.emit
.apply(this, [packet
.method
, returnFn
].concat(packet
.params
));
178 * Returns a function used as a callback when responding to a call
180 WebsocketRpcCaller
.prototype._createReturnCallFn = function(packet_id
) {
183 return function returnCallFn() {
184 var value
= Array
.prototype.slice
.call(arguments
, 0);
191 self
.send(ret_packet
);
197 WebsocketRpcCaller
.prototype._noop = function() {};
202 // If running a node module, set the exports
203 if (typeof module
=== 'object' && typeof module
.exports
!== 'undefined') {
204 module
.exports
= WebsocketRpc
;