[polari] application: Try alternative servers on connection failures



commit 88fca63df7759072e234c0fb5bbe59c8ba6a44a1
Author: Kunaal Jain <kunaalus gmail com>
Date:   Sat Apr 2 10:24:42 2016 +0530

    application: Try alternative servers on connection failures
    
    Predefined networks can have more than a single server, so when a
    connection fails, we have a list of servers we can fall back to
    instead of failing immediately. This is particularly important as
    we hide the network details in the properties dialog for predefined
    connections, so users cannot manually change the server themselves.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=762335

 src/application.js     |   71 +++++++++++++++++++++++++++++++++++++++---------
 src/networksManager.js |   11 +++++--
 2 files changed, 66 insertions(+), 16 deletions(-)
---
diff --git a/src/application.js b/src/application.js
index b6fa847..a2d7242 100644
--- a/src/application.js
+++ b/src/application.js
@@ -20,7 +20,11 @@ const IRC_SCHEMA_REGEX = /^(irc?:\/\/)([\da-z\.-]+):?(\d+)?\/(?:%23)?([\w\.\+-]+
 
 const ConnectionError = {
     CANCELLED: Tp.error_get_dbus_name(Tp.Error.CANCELLED),
-    ALREADY_CONNECTED: Tp.error_get_dbus_name(Tp.Error.ALREADY_CONNECTED)
+    ALREADY_CONNECTED: Tp.error_get_dbus_name(Tp.Error.ALREADY_CONNECTED),
+    DISCONNECTED: Tp.error_get_dbus_name(Tp.Error.DISCONNECTED),
+    NETWORK_ERROR: Tp.error_get_dbus_name(Tp.Error.NETWORK_ERROR),
+    NOT_AVAILABLE: Tp.error_get_dbus_name(Tp.Error.NOT_AVAILABLE),
+    SERVICE_BUSY: Tp.error_get_dbus_name(Tp.Error.SERVICE_BUSY)
 };
 
 const Application = new Lang.Class({
@@ -347,6 +351,13 @@ const Application = new Lang.Class({
         account.update_parameters_vardict_async(asv, [], callback);
     },
 
+    _updateAccountServer: function(account, server, callback) {
+        let sv = { server: GLib.Variant.new('s', server.address),
+                   port: GLib.Variant.new('u', server.port) };
+        let asv = GLib.Variant.new('a{sv}', sv);
+        account.update_parameters_vardict_async(asv, [], callback);
+    },
+
     _requestChannel: function(accountPath, targetType, targetId, time, callback) {
         // have this in AccountMonitor?
         let factory = Tp.AccountManager.dup().get_factory();
@@ -366,6 +377,14 @@ const Application = new Lang.Class({
 
         let roomId = Polari.create_room_id(account,  targetId, targetType);
 
+        let params = account.dup_parameters_vardict().deep_unpack();
+        let server = params['server'].deep_unpack();
+        let accountServers = [];
+
+        // If predefined network, get alternate servers list
+        if (this._networksManager.getAccountIsPredefined(account))
+            accountServers = this._networksManager.getNetworkServers(account.service);
+
         let requestData = {
           account: account,
           targetHandleType: targetType,
@@ -375,7 +394,9 @@ const Application = new Lang.Class({
           time: time,
           retry: 0,
           originalNick: account.nickname,
-          callback: callback };
+          callback: callback,
+          alternateServers: accountServers.filter(s => s != server)
+        };
 
         this._pendingRequests[roomId] = requestData;
 
@@ -394,7 +415,7 @@ const Application = new Lang.Class({
                                            this._onEnsureChannel, requestData));
     },
 
-    _retryRequest: function(requestData) {
+    _retryNickRequest: function(requestData) {
         let account = requestData.account;
 
         // Try again with a different nick
@@ -407,6 +428,17 @@ const Application = new Lang.Class({
             }));
     },
 
+    _retryServerRequest: function(requestData) {
+        let account = requestData.account;
+
+        // Try again with a alternate server
+        let server = requestData.alternateServers.shift();
+        this._updateAccountServer(account, server, Lang.bind(this,
+            function() {
+                this._ensureChannel(requestData);
+            }));
+    },
+
     _onEnsureChannel: function(req, res, requestData) {
         let account = req.account;
         let channel = null;
@@ -414,22 +446,35 @@ const Application = new Lang.Class({
         try {
             channel = req.ensure_and_observe_channel_finish(res);
         } catch (e if e.matches(Tp.Error, Tp.Error.DISCONNECTED)) {
-            let error = account.connection_error;
             // If we receive a disconnect error and the network is unavailable,
             // then the error is not specific to polari and polari will
             // just be in offline state.
             if (!this._networkMonitor.network_available)
                 return;
-            if (error == ConnectionError.ALREADY_CONNECTED &&
-                requestData.retry++ < MAX_RETRIES) {
-                    this._retryRequest(requestData);
-                    return;
-            }
 
-            if (error && error != ConnectionError.CANCELLED)
-                Utils.debug('Account %s disconnected with error %s'.format(
-                            account.get_path_suffix(),
-                            error.replace(Tp.ERROR_PREFIX + '.', '')));
+            let error = account.connection_error;
+            switch (error) {
+                case ConnectionError.CANCELLED:
+                    break; // disconnected due to user request, ignore
+                case ConnectionError.ALREADY_CONNECTED:
+                    if (requestData.retry++ < MAX_RETRIES) {
+                        this._retryNickRequest(requestData);
+                        return;
+                    }
+                    break;
+                case ConnectionError.NETWORK_ERROR:
+                case ConnectionError.NOT_AVAILABLE:
+                case ConnectionError.DISCONNECTED:
+                case ConnectionError.SERVICE_BUSY:
+                    if (requestData.alternateServers.length > 0) {
+                        this._retryServerRequest(requestData);
+                        return;
+                    }
+                default:
+                    Utils.debug('Account %s disconnected with error %s'.format(
+                                account.get_path_suffix(),
+                                error.replace(Tp.ERROR_PREFIX + '.', '')));
+            }
         } catch (e if e.matches(Tp.Error, Tp.Error.CANCELLED)) {
             // interrupted by user request, don't log
         } catch (e) {
diff --git a/src/networksManager.js b/src/networksManager.js
index af482f4..c4499ee 100644
--- a/src/networksManager.js
+++ b/src/networksManager.js
@@ -82,9 +82,7 @@ const NetworksManager = new Lang.Class({
         if (!network.servers || !network.servers.length)
             throw new Error('No servers for network ' + id);
 
-        let sslServers = network.servers.filter(s => s.ssl);
-        let server = sslServers.length > 0 ? sslServers[0]
-                                           : network.servers[0];
+        let server = this.getNetworkServers(id)[0];
         return {
             'account': new GLib.Variant('s', GLib.get_user_name()),
             'server': new GLib.Variant('s', server.address),
@@ -93,6 +91,13 @@ const NetworksManager = new Lang.Class({
         };
     },
 
+    getNetworkServers: function(id) {
+        let network = this._lookupNetwork(id);
+        let sslServers = network.servers.filter(s => s.ssl);
+        return sslServers.length > 0 ? sslServers
+                                     : network.servers.slice();
+    },
+
     getNetworkMatchTerms: function(id) {
         let network = this._lookupNetwork(id);
         let servers = network.servers.map(function(s) {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]