2 ReconnectingSocket
: function ReconnectingSocket(server_uri
, socket_options
) {
4 var is_reconnecting
= false;
6 var reconnect_delay
= 2000;
7 var reconnect_last_delay
= 0;
8 var reconnect_delay_exponential
= true;
9 var reconnect_max_attempts
= 5;
10 var reconnect_step
= 0;
11 var reconnect_tmr
= null;
13 var original_disconnect
;
14 var planned_disconnect
= false;
16 var socket
= eio
.apply(eio
, arguments
);
17 socket
.on('open', onOpen
);
18 socket
.on('close', onClose
);
19 socket
.on('error', onError
);
21 original_disconnect
= socket
.close
;
24 // Apply any custom reconnection config
26 if (typeof socket_options
.reconnect_delay
=== 'number')
27 reconnect_delay
= socket_options
.reconnect_delay
;
29 if (typeof socket_options
.reconnect_max_attempts
=== 'number')
30 reconnect_max_attempts
= socket_options
.reconnect_max_attempts
;
32 if (typeof socket_options
.reconnect_delay_exponential
!== 'undefined')
33 reconnect_delay_exponential
= !!socket_options
.reconnect_delay_exponential
;
39 is_reconnecting
= false;
40 planned_disconnect
= false;
43 reconnect_last_delay
= 0;
45 clearTimeout(reconnect_tmr
);
52 if (!planned_disconnect
)
58 // This will be called when a reconnect fails
65 planned_disconnect
= true;
66 original_disconnect
.call(socket
);
70 function reconnect() {
71 if (reconnect_step
>= reconnect_max_attempts
) {
72 socket
.emit('reconnecting_failed');
76 var delay
= reconnect_delay_exponential
?
77 (reconnect_last_delay
|| reconnect_delay
/ 2) * 2 :
78 reconnect_delay
* reconnect_step
;
80 is_reconnecting
= true;
82 reconnect_tmr
= setTimeout(function() {
86 reconnect_last_delay
= delay
;
88 socket
.emit('reconnecting', {
89 attempt
: reconnect_step
+ 1,
90 max_attempts
: reconnect_max_attempts
,
106 Create a document explaining the protocol
107 Some way to expire unused callbacks? TTL? expireCallback() function?
110 function WebsocketRpc(eio_socket
) {
114 this._rpc_callbacks
= {};
115 this._socket
= eio_socket
;
117 this._mixinEmitter();
118 this._bindSocketListeners();
122 WebsocketRpc
.prototype._bindSocketListeners = function() {
125 // Proxy the onMessage listener
126 this._onMessageProxy
= function rpcOnMessageBoundFunction(){
127 self
._onMessage
.apply(self
, arguments
);
129 this._socket
.on('message', this._onMessageProxy
);
134 WebsocketRpc
.prototype.dispose = function() {
135 if (this._onMessageProxy
) {
136 this._socket
.removeListener('message', this._onMessageProxy
);
137 delete this._onMessageProxy
;
140 this.removeAllListeners();
147 * The engine.io socket already has an emitter mixin so steal it from there
149 WebsocketRpc
.prototype._mixinEmitter = function() {
150 var funcs
= ['on', 'once', 'off', 'removeListener', 'removeAllListeners', 'emit', 'listeners', 'hasListeners'];
152 for (var i
=0; i
<funcs
.length
; i
++) {
153 if (typeof this._socket
[funcs
[i
]] === 'function')
154 this[funcs
[i
]] = this._socket
[funcs
[i
]];
160 * Check if a packet is a valid RPC call
162 WebsocketRpc
.prototype._isCall = function(packet
) {
163 return (typeof packet
.method
!== 'undefined' &&
164 typeof packet
.params
!== 'undefined');
169 * Check if a packet is a valid RPC response
171 WebsocketRpc
.prototype._isResponse = function(packet
) {
172 return (typeof packet
.id
!== 'undefined' &&
173 typeof packet
.response
!== 'undefined');
180 * First argument must be the method name to call
181 * If the last argument is a function, it is used as a callback
182 * All other arguments are passed to the RPC method
183 * Eg. Rpc.call('namespace.method_name', 1, 2, 3, callbackFn)
185 WebsocketRpc
.prototype.call = function(method
) {
186 var params
, callback
, packet
;
188 // Get a normal array of passed in arguments
189 params
= Array
.prototype.slice
.call(arguments
, 1, arguments
.length
);
191 // If the last argument is a function, take it as a callback and strip it out
192 if (typeof params
[params
.length
-1] === 'function') {
193 callback
= params
[params
.length
-1];
194 params
= params
.slice(0, params
.length
-1);
202 if (typeof callback
=== 'function') {
203 packet
.id
= this._next_id
;
206 this._rpc_callbacks
[packet
.id
] = callback
;
214 * Encode the packet into JSON and send it over the websocket
216 WebsocketRpc
.prototype.send = function(packet
) {
218 this._socket
.send(JSON
.stringify(packet
));
223 * Handler for the websocket `message` event
225 WebsocketRpc
.prototype._onMessage = function(message_raw
) {
232 packet
= JSON
.parse(message_raw
);
233 if (!packet
) throw 'Corrupt packet';
238 if (this._isResponse(packet
)) {
239 // If we have no callback waiting for this response, don't do anything
240 if (typeof this._rpc_callbacks
[packet
.id
] !== 'function')
243 // Delete the callback before calling it. If any exceptions accur within the callback
244 // we don't have to worry about the delete not happening
245 callback
= this._rpc_callbacks
[packet
.id
];
246 delete this._rpc_callbacks
[packet
.id
];
248 callback
.apply(this, packet
.response
);
250 } else if (this._isCall(packet
)) {
251 // Calls with an ID may be responded to
252 if (typeof packet
.id
!== 'undefined') {
253 returnFn
= this._createReturnCallFn(packet
.id
);
255 returnFn
= this._noop
;
258 this.emit
.apply(this, [packet
.method
, returnFn
].concat(packet
.params
));
264 * Returns a function used as a callback when responding to a call
266 WebsocketRpc
.prototype._createReturnCallFn = function(packet_id
) {
269 return function returnCallFn() {
270 var value
= Array
.prototype.slice
.call(arguments
, 0);
277 self
.send(ret_packet
);
283 WebsocketRpc
.prototype._noop = function() {};