From defeabe56593ce6163d0f66cf1c00ff5b8528043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Mill=C3=A1n?= Date: Fri, 7 Dec 2012 19:44:44 +0100 Subject: [PATCH] Automatic connection recovery. New configuration parameter: 'connection_recovery_min_interval' (seconds) New configuration parameter: 'connection_recovery_max_interval' (seconds) When all outbound proxies get unreachable, User Agent attemps to reconnect applying an exponential backoff algorithm, which takes the minimum and maximum reconnection interval values from those new configuration parameters. --- src/Transport.js | 6 +-- src/UA.js | 98 +++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 82 insertions(+), 22 deletions(-) diff --git a/src/Transport.js b/src/Transport.js index 897019e7f..b1ffe687d 100644 --- a/src/Transport.js +++ b/src/Transport.js @@ -239,16 +239,14 @@ JsSIP.Transport.prototype = { this.reconnection_attempts += 1; - if(this.reconnection_attempts > this.ua.configuration.max_reconnection) { + if(this.reconnection_attempts > this.ua.configuration.ws_server_max_reconnection) { console.log(JsSIP.c.LOG_TRANSPORT +'Maximum reconnection attempts for: ' + this.server.ws_uri); this.ua.onTransportError(this); } else { console.log(JsSIP.c.LOG_TRANSPORT +'Trying to reconnect to: ' + this.server.ws_uri + '. Reconnection attempt number ' + this.reconnection_attempts); this.reconnectTimer = window.setTimeout(function() { - transport.reConnect();}, this.ua.configuration.reconnection_timeout * 1000); - - this.connect(); + transport.connect();}, this.ua.configuration.ws_server_reconnection_timeout * 1000); } } }; \ No newline at end of file diff --git a/src/UA.js b/src/UA.js index c7bcad72e..ae2ba4bd3 100644 --- a/src/UA.js +++ b/src/UA.js @@ -42,6 +42,8 @@ JsSIP.UA = function(configuration) { ict: {} }; + this.transportRecoverAttempts = 0; + /** * Load configuration * @@ -61,17 +63,6 @@ JsSIP.UA.prototype = new JsSIP.EventEmitter(); // High Level API //================= -/** - * Notify the UA about network availability. - */ -JsSIP.UA.prototype.networkIsReady = function() { - console.log('Network Ready notification received'); - // Stablish connection if needed. - if(this.status === JsSIP.c.UA_STATUS_NOT_READY && this.error === JsSIP.c.UA_NETWORK_ERROR) { - this.transport.connect(); - } -}; - /** * Register. * @@ -316,9 +307,14 @@ JsSIP.UA.prototype.onTransportError = function(transport) { new JsSIP.Transport(this, server); }else { this.closeSessionsOnTransportError(); - this.status = JsSIP.c.UA_STATUS_NOT_READY; - this.error = JsSIP.c.UA_NETWORK_ERROR; - this.emit('disconnected'); + if (!this.error || this.error !== JsSIP.c.UA_NETWORK_ERROR) { + this.status = JsSIP.c.UA_STATUS_NOT_READY; + this.error = JsSIP.c.UA_NETWORK_ERROR; + this.emit('disconnected'); + } + + // Transport Recovery process + this.recoverTransport(); } }; @@ -331,6 +327,9 @@ JsSIP.UA.prototype.onTransportError = function(transport) { JsSIP.UA.prototype.onTransportConnected = function(transport) { this.transport = transport; + // Reset transport recovery counter + this.transportRecoverAttempts = 0; + transport.server.status = JsSIP.c.WS_SERVER_READY; console.log(JsSIP.c.LOG_UA +'connection status set to: '+ JsSIP.c.WS_SERVER_READY); @@ -569,6 +568,36 @@ JsSIP.UA.prototype.closeSessionsOnTransportError = function() { } }; +JsSIP.UA.prototype.recoverTransport = function(ua) { + var idx, k, nextRetry, count, server; + + ua = ua || this; + count = ua.transportRecoverAttempts; + + for (idx in ua.configuration.outbound_proxy_set) { + ua.configuration.outbound_proxy_set[idx].status = 0; + } + + server = ua.getNextWsServer(); + + k = Math.floor((Math.random() * Math.pow(2,count)) +1); + nextRetry = k * ua.configuration.connection_recovery_min_interval; + + if (nextRetry > ua.configuration.connection_recovery_max_interval) { + console.log(JsSIP.c.LOG_UA + 'Time for next connection attempt exceeds connection_recovery_max_interval. Resetting counter'); + nextRetry = ua.configuration.connection_recovery_min_interval; + count = 0; + } + + console.log(JsSIP.c.LOG_UA + 'Next connection attempt in: '+ nextRetry +' seconds'); + + window.setTimeout( + function(){ + ua.transportRecoverAttempts = count + 1; + new JsSIP.Transport(ua, server); + }, nextRetry * 1000); +}; + /** * Configuration load. * @private @@ -592,8 +621,11 @@ JsSIP.UA.prototype.loadConfig = function(configuration) { register: true, // Transport related parameters - max_reconnection: 3, - reconnection_timeout: 4, + ws_server_max_reconnection: 3, + ws_server_reconnection_timeout: 4, + + connection_recovery_min_interval: 2, + connection_recovery_max_interval: 30, // Session parameters no_answer_timeout: 60, @@ -654,6 +686,14 @@ JsSIP.UA.prototype.loadConfig = function(configuration) { } } + // Sanity Checks + + // Connection recovery intervals + if(settings.connection_recovery_max_interval < settings.connection_recovery_min_interval) { + console.error('"connection_recovery_max_interval" parameter is lower than "connection_recovery_min_interval"'); + return false; + } + // Post Configuration Process // Instance-id for GRUU @@ -731,9 +771,13 @@ JsSIP.UA.configuration_skeleton = (function() { // Internal parameters "instance_id", "jssip_id", - "max_reconnection", - "reconnection_timeout", + "ws_server_max_reconnection", + "ws_server_reconnection_timeout", + + "connection_recovery_min_interval", + "connection_recovery_max_interval", + "register_min_expires", // Mandatory user configurable parameters @@ -894,6 +938,24 @@ JsSIP.UA.configuration_check = { return true; } }, + connection_recovery_min_interval: function(connection_recovery_min_interval) { + if(!Number(connection_recovery_min_interval)) { + return false; + } else if(connection_recovery_min_interval < 0) { + return false; + } else { + return true; + } + }, + connection_recovery_max_interval: function(connection_recovery_max_interval) { + if(!Number(connection_recovery_max_interval)) { + return false; + } else if(connection_recovery_max_interval < 0) { + return false; + } else { + return true; + } + }, hack_via_tcp: function(hack_via_tcp) { if(typeof hack_via_tcp !== 'boolean') { return false;