bug 481733 - provide better error messages, handle errors better, make autoconnect more robust, r=edilee

This commit is contained in:
Mike Connor 2009-08-19 23:27:22 -04:00
parent 02a2121431
commit ce19e59b95
4 changed files with 109 additions and 36 deletions

View File

@ -7,3 +7,4 @@ loginStart.label = Signing in, please wait...
loginError.label = Invalid username, password or passphrase.
loginSuccess.label = Signed In
hide.label = Hide
error.login.description = Error: %1$S

View File

@ -6,7 +6,7 @@ weaveButtonOnline.label = Weave
shareBookmark.menuItem = Share This Folder...
unShareBookmark.menuItem = Stop Sharing This Folder
status.offline = Sign in
status.offline = Not Signed In
# The next two are not normally used, as we now display the username
# when the user is logged in. But if for some reason we can't get the username,
@ -18,6 +18,8 @@ error.login.title = Error While Signing In
error.login.description = Weave encountered an error while signing you in: %1$S. Please try again.
error.login.reason.password = Your username/password failed
error.login.reason.unknown = Unknown error
error.login.reason.passphrase = Invalid passphrase
error.login.reason.network = Network error
error.logout.title = Error While Signing Out
error.logout.description = Weave encountered an error while signing you out. It's probably ok, and you don't have to do anything about it (then why are we bugging you with this info?).
error.sync.title = Error While Syncing

View File

@ -45,7 +45,8 @@ const EXPORTED_SYMBOLS = ["WEAVE_VERSION", "COMPATIBLE_VERSION",
'WEAVE_STATUS_PARTIAL', 'SERVER_LOW_QUOTA',
'SERVER_DOWNTIME', 'SERVER_UNREACHABLE',
'LOGIN_FAILED_NO_USERNAME', 'LOGIN_FAILED_NO_PASSWORD',
'LOGIN_FAILED_REJECTED', 'METARECORD_DOWNLOAD_FAIL',
'LOGIN_FAILED_NETWORK_ERROR','LOGIN_FAILED_INVALID_PASSPHRASE',
'LOGIN_FAILED_LOGIN_REJECTED', 'METARECORD_DOWNLOAD_FAIL',
'VERSION_OUT_OF_DATE', 'DESKTOP_VERSION_OUT_OF_DATE',
'KEYS_DOWNLOAD_FAIL', 'NO_KEYS_NO_KEYGEN', 'KEYS_UPLOAD_FAIL',
'SETUP_FAILED_NO_PASSPHRASE', 'ABORT_SYNC_COMMAND',
@ -97,7 +98,9 @@ const SERVER_UNREACHABLE = "Weave server is unreachable.";
// Ways that a sync can fail during setup or login:
const LOGIN_FAILED_NO_USERNAME = "No username set, login failed.";
const LOGIN_FAILED_NO_PASSWORD = "No password set, login failed.";
const LOGIN_FAILED_REJECTED = "Incorrect username or password.";
const LOGIN_FAILED_NETWORK_ERROR = "Weave failed to connect to the server.";
const LOGIN_FAILED_INVALID_PASSPHRASE = "Incorrect passphrase given.";
const LOGIN_FAILED_LOGIN_REJECTED = "Incorrect username or password.";
const METARECORD_DOWNLOAD_FAIL = "Can't download metadata record, HTTP error.";
const VERSION_OUT_OF_DATE = "This copy of Weave needs to be updated.";
const DESKTOP_VERSION_OUT_OF_DATE = "Weave needs updating on your desktop browser.";

View File

@ -340,12 +340,8 @@ WeaveSvc.prototype = {
this._genKeyURLs();
if (Svc.Prefs.get("autoconnect") && this.username) {
try {
if (this.login())
this.syncOnIdle();
} catch (e) {}
}
if (Svc.Prefs.get("autoconnect"))
this._autoConnect();
},
_initLogs: function WeaveSvc__initLogs() {
@ -476,24 +472,30 @@ WeaveSvc.prototype = {
let res = new Resource(this.baseURL + "api/register/chknode/" + username);
try {
res.get();
}
catch(ex) { /* we check status below */ }
if (res.lastChannel.responseStatus == 404) {
this._log.debug("Using serverURL as data cluster (multi-cluster support disabled)");
return Svc.Prefs.get("serverURL");
}
switch (res.lastChannel.responseStatus) {
case 404:
this._log.debug("Using serverURL as data cluster (multi-cluster support disabled)");
return Svc.Prefs.get("serverURL");
case 200:
return "https://" + res.data + "/";
default:
this._log.debug("Unexpected response code trying to find cluster: " + res.lastChannel.responseStatus);
break;
}
} catch(ex) { /* if the channel failed to start we'll get here, just return false */}
if (res.lastChannel.responseStatus == 200)
return "https://" + res.data + "/";
return null;
return false;
},
// gets cluster from central LDAP server and sets this.clusterURL
setCluster: function WeaveSvc_setCluster(username) {
let cluster = this.findCluster(username);
if (cluster) {
if (cluster == this.clusterURL)
return false;
this._log.debug("Saving cluster setting");
this.clusterURL = cluster;
return true;
@ -508,9 +510,10 @@ WeaveSvc.prototype = {
let cTime = Date.now();
let lastUp = parseFloat(Svc.Prefs.get("lastClusterUpdate"));
if (!lastUp || ((cTime - lastUp) >= CLUSTER_BACKOFF)) {
this.setCluster(username);
Svc.Prefs.set("lastClusterUpdate", cTime.toString());
return true;
if (this.setCluster(username)) {
Svc.Prefs.set("lastClusterUpdate", cTime.toString());
return true;
}
}
return false;
},
@ -538,18 +541,32 @@ WeaveSvc.prototype = {
// login may fail because of cluster change
try {
res.get();
} catch (e) {
if (res.lastChannel.responseStatus == 401) {
if (this.updateCluster(username))
return this.verifyLogin(username, password, passphrase, isLogin);
} catch (e) {}
try {
switch (res.lastChannel.responseStatus) {
case 200:
if (passphrase && !this.verifyPassphrase(username, password, passphrase)) {
this._setSyncFailure(LOGIN_FAILED_INVALID_PASSPHRASE);
return false;
}
return true;
case 401:
if (this.updateCluster(username))
return this.verifyLogin(username, password, passphrase, isLogin);
this._setSyncFailure(LOGIN_FAILED_LOGIN_REJECTED);
this._log.debug("verifyLogin failed: login failed")
return false;
default:
throw "unexpected HTTP response: " + res.lastChannel.responseStatus;
}
} catch (e) {
// if we get here, we have either a busted channel or a network error
this._log.debug("verifyLogin failed: " + e)
this._setSyncFailure(LOGIN_FAILED_NETWORK_ERROR);
throw e;
}
if (passphrase)
return this.verifyPassphrase(username, password, passphrase);
else
return true;
}))(),
verifyPassphrase: function WeaveSvc_verifyPassphrase(username, password, passphrase)
@ -636,10 +653,51 @@ WeaveSvc.prototype = {
return true;
}))(),
_autoConnectAttempts: 0,
_autoConnect: function WeaveSvc__attemptAutoConnect() {
try {
if (!this.username || !this.password || !this.passphrase)
return;
let failureReason;
if (Svc.IO.offline)
failureReason = "Application is offline";
else if (this.login()) {
this.syncOnIdle();
return;
}
failureReason = this.detailedStatus.sync;
}
catch (ex) {
failureReason = ex;
}
this._log.debug("Autoconnect failed: " + failureReason);
let listener = new Utils.EventListener(Utils.bind2(this,
function WeaveSvc__autoConnectCallback(timer) {
this._autoConnectTimer = null;
this._autoConnect();
}));
this._autoConnectTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
// back off slowly, with some random fuzz to avoid repeated collisions
var interval = Math.floor(Math.random() * SCHEDULED_SYNC_INTERVAL +
SCHEDULED_SYNC_INTERVAL * this._autoConnectAttempts);
this._autoConnectAttempts++;
this._autoConnectTimer.initWithCallback(listener, interval,
Ci.nsITimer.TYPE_ONE_SHOT);
this._log.debug("Scheduling next autoconnect attempt in " +
interval / 1000 + " seconds.");
},
login: function WeaveSvc_login(username, password, passphrase)
this._catch(this._lock(this._notify("login", "", function() {
this._loggedIn = false;
this._detailedStatus = new StatusRecord();
if (Svc.IO.offline)
throw "Application is offline, login should not be called";
if (typeof(username) != "undefined")
this.username = username;
@ -660,13 +718,17 @@ WeaveSvc.prototype = {
if (!(this.verifyLogin(this.username, this.password,
passphrase, true))) {
this._setSyncFailure(LOGIN_FAILED_REJECTED);
throw "Login failed";
// verifyLogin sets the failure states here
throw "Login failed: " + this.detailedStatus.sync;
}
// Try starting the sync timer now that we're logged in
this._loggedIn = true;
this._checkSyncStatus();
if (this._autoConnectTimer) {
this._autoConnectTimer.cancel();
this._autoConnectTimer = null;
}
return true;
})))(),
@ -957,9 +1019,14 @@ WeaveSvc.prototype = {
// specifcally handle 500, 502, 503, 504 errors
// xxxmpc: what else should be in this list?
// this is sort of pseudocode, need a way to get at the
if (!shouldBackoff &&
Utils.checkStatus(Records.lastResource.lastChannel.responseStatus, null, [500,[502,504]])) {
shouldBackoff = true;
if (!shouldBackoff) {
try {
shouldBackoff = Utils.checkStatus(Records.lastResource.lastChannel.responseStatus, null, [500,[502,504]]);
}
catch (e) {
// if responseStatus throws, we have a network issue in play
shouldBackoff = true;
}
}
// if this is a client error, do the next sync as normal and return