[polari/wip/fmuellner/misc-fixes: 10/16] app: Handle errors on connection status changes
- From: Florian Müllner <fmuellner src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [polari/wip/fmuellner/misc-fixes: 10/16] app: Handle errors on connection status changes
- Date: Mon, 8 Aug 2016 09:49:16 +0000 (UTC)
commit 291f4f1cd63aa6917140094538dac3b64ff10c7e
Author: Florian Müllner <fmuellner gnome org>
Date: Mon Aug 1 02:25:14 2016 +0200
app: Handle errors on connection status changes
We try to recover from failed connection attempts by trying alternative
servers or using different account names, but only when handling the
'join-room' and 'message-user' actions - if the attempt was initiated
by any other mean, for instance the (re)connect items in the room list,
our error handling will be sidestepped completely.
So rather than handling exceptions that occur in the ensure_channels()
call, handle errors when the connection status changes from CONNECTING
to DISCONNECTED, which works independently from how the connection
was initiated.
https://bugzilla.gnome.org/show_bug.cgi?id=769583
src/application.js | 210 +++++++++++++++++++++++++---------------------------
1 files changed, 100 insertions(+), 110 deletions(-)
---
diff --git a/src/application.js b/src/application.js
index cd12a8a..e87fa56 100644
--- a/src/application.js
+++ b/src/application.js
@@ -18,15 +18,6 @@ const MAX_RETRIES = 3;
const IRC_SCHEMA_REGEX = /^(irc?:\/\/)([\da-z\.-]+):?(\d+)?\/(?:%23)?([\w\.\+-]+)/i;
-const ConnectionError = {
- CANCELLED: Tp.error_get_dbus_name(Tp.Error.CANCELLED),
- 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({
Name: 'Application',
Extends: Gtk.Application,
@@ -39,6 +30,7 @@ const Application = new Lang.Class({
GLib.set_application_name('Polari');
this._window = null;
this._pendingRequests = new Map();
+ this._retryData = new Map();
},
vfunc_startup: function() {
@@ -53,6 +45,8 @@ const Application = new Lang.Class({
function(am, account) {
this._removeSavedChannelsForAccount(account.object_path);
}));
+ this._accountsMonitor.connect('account-status-changed',
+ Lang.bind(this, this._onAccountStatusChanged));
this._settings = new Gio.Settings({ schema_id: 'org.gnome.Polari' });
@@ -333,21 +327,6 @@ const Application = new Lang.Class({
GLib.Variant.new('aa{sv}', savedChannels));
},
- _updateAccountName: function(account, name, callback) {
- let sv = { account: GLib.Variant.new('s', name) };
- let asv = GLib.Variant.new('a{sv}', sv);
- 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),
- 'use-ssl': GLib.Variant.new('b', server.ssl)
- };
- let asv = GLib.Variant.new('a{sv}', sv);
- account.update_parameters_vardict_async(asv, [], callback);
- },
-
_requestChannel: function(accountPath, targetType, targetId, time, callback) {
let account = this._accountsMonitor.lookupAccount(accountPath);
@@ -362,117 +341,128 @@ const Application = new Lang.Class({
return;
let roomId = Polari.create_room_id(account, targetId, targetType);
+ let cancellable = new Gio.Cancellable();
+ this._pendingRequests.set(roomId, cancellable);
+
+ let req = Tp.AccountChannelRequest.new_text(account, time);
+ req.set_target_id(targetType, targetId);
+ req.set_delegate_to_preferred_handler(true);
+ let preferredHandler = Tp.CLIENT_BUS_NAME_BASE + 'Polari';
+ req.ensure_and_observe_channel_async(preferredHandler, cancellable,
+ (o, res) => {
+ let channel = null;
+ try {
+ channel = req.ensure_and_observe_channel_finish(res);
+ } catch(e) {
+ Utils.debug('Failed to ensure channel: ' + e.message);
+ }
+
+ if (callback)
+ callback(channel);
+ delete this._pendingRequests[roomId];
+ });
+ },
+
+ _ensureRetryData: function(account) {
+ let data = this._retryData.get(account.object_path);
+ if (data)
+ return data;
let params = account.dup_parameters_vardict().deep_unpack();
let server = params['server'].deep_unpack();
- let accountServers = [];
+ let nick = params['account'].deep_unpack();
+ Utils.debug('Failed to connect to %s with username %s'.format(server, nick));
- // If predefined network, get alternate servers list
+ let accountServers = [];
if (this._networksManager.getAccountIsPredefined(account))
accountServers = this._networksManager.getNetworkServers(account.service);
- let requestData = {
- account: account,
- targetHandleType: targetType,
- targetId: targetId,
- roomId: roomId,
- cancellable: new Gio.Cancellable(),
- time: time,
- retry: 0,
- originalNick: params['account'].deep_unpack(),
- callback: callback,
- alternateServers: accountServers.filter(s => s.address != server)
+ data = {
+ retry: 0,
+ originalNick: nick,
+ alternateServers: accountServers.filter(s => s.address != server)
};
+ this._retryData.set(account.object_path, data);
+ return data;
+ },
- this._pendingRequests.set(roomId, requestData.cancellable);
+ _clearRetryData: function(account) {
+ let data = this._retryData.get(account.object_path);
+ if (!data)
+ return;
- this._ensureChannel(requestData);
+ if (data.retry > 0) {
+ let params = { account: new GLib.Variant('s', data.originalNick) };
+ let asv = new GLib.Variant('a{sv}', params);
+ account.update_parameters_vardict_async(asv, [], null);
+ }
+ this._retryData.delete(account.object_path);
},
- _ensureChannel: function(requestData) {
- let account = requestData.account;
-
- let req = Tp.AccountChannelRequest.new_text(account, requestData.time);
- req.set_target_id(requestData.targetHandleType, requestData.targetId);
- req.set_delegate_to_preferred_handler(true);
- let preferredHandler = Tp.CLIENT_BUS_NAME_BASE + 'Polari';
- req.ensure_and_observe_channel_async(preferredHandler, requestData.cancellable,
- Lang.bind(this,
- this._onEnsureChannel, requestData));
+ _retryWithParams: function(account, params) {
+ account.update_parameters_vardict_async(params, [], () => {
+ let presence = Tp.ConnectionPresenceType.AVAILABLE;
+ let msg = account.requested_status_message;
+ account.request_presence_async(presence, 'available', msg, null);
+ });
},
- _retryNickRequest: function(requestData) {
- let account = requestData.account;
+ _retryNickRequest: function(account) {
+ let retryData = this._ensureRetryData(account);
- // Try again with a different nick
- let params = account.dup_parameters_vardict().deep_unpack();
- let oldNick = params['account'].deep_unpack();
- let nick = oldNick + '_';
- this._updateAccountName(account, nick, Lang.bind(this,
- function() {
- this._ensureChannel(requestData);
- }));
+ if (retryData.retry++ >= MAX_RETRIES)
+ return false;
+
+ let oldParams = account.dup_parameters_vardict().deep_unpack();
+ let nick = oldParams['account'].deep_unpack();
+
+ Utils.debug('Retrying with nickname %s'.format(nick + '_'));
+ let params = { account: new GLib.Variant('s', nick + '_') };
+ this._retryWithParams(account, new GLib.Variant('a{sv}', params));
+ return true;
},
- _retryServerRequest: function(requestData) {
- let account = requestData.account;
+ _retryServerRequest: function(account) {
+ let retryData = this._ensureRetryData(account);
- // Try again with a alternate server
- let server = requestData.alternateServers.shift();
- this._updateAccountServer(account, server, Lang.bind(this,
- function() {
- this._ensureChannel(requestData);
- }));
+ let server = retryData.alternateServers.shift();
+ if (!server)
+ return false;
+
+ Utils.debug('Retrying with %s:%d'.format(server.address, server.port));
+ let params = { server: new GLib.Variant('s', server.address),
+ port: new GLib.Variant('u', server.port),
+ 'use-ssl': new GLib.Variant('b', server.ssl) };
+ this._retryWithParams(account, new GLib.Variant('a{sv}', params));
+ return true;
},
- _onEnsureChannel: function(req, res, requestData) {
- let account = req.account;
- let channel = null;
+ _onAccountStatusChanged: function(mon, account) {
+ let status = account.connection_status;
- try {
- channel = req.ensure_and_observe_channel_finish(res);
- } catch (e if e.matches(Tp.Error, Tp.Error.DISCONNECTED)) {
- // 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 (status == Tp.ConnectionStatus.CONNECTING)
+ return;
- 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 + '.', '')));
+ if (status == Tp.ConnectionStatus.DISCONNECTED) {
+ let reason = account.connection_status_reason;
+
+ if (reason == Tp.ConnectionStatusReason.NAME_IN_USE)
+ if (this._retryNickRequest(account))
+ return;
+
+ if (reason == Tp.ConnectionStatusReason.NETWORK_ERROR ||
+ reason == Tp.ConnectionStatusReason.NONE_SPECIFIED)
+ if (this._retryServerRequest(account))
+ return;
+
+ if (reason != Tp.ConnectionStatusReason.REQUESTED) {
+ let strReasons = Object.keys(Tp.ConnectionStatusReason);
+ Utils.debug('Account %s disconnected with reason %s'.format(
+ account.display_name, strReasons[reason]));
}
- } catch (e if e.matches(Tp.Error, Tp.Error.CANCELLED)) {
- // interrupted by user request, don't log
- } catch (e) {
- Utils.debug('Failed to ensure channel: ' + e.message);
}
- if (requestData.callback)
- requestData.callback(channel);
-
- if (requestData.retry > 0)
- this._updateAccountName(account, requestData.originalNick, null);
- this._pendingRequests.delete(requestData.roomId);
+ this._clearRetryData(account);
},
_onJoinRoom: function(action, parameter) {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]