Improve error messages on certificate and connection errors

- Show a clearer message on a certificate error that includes the
  underlying error, which should make debugging much simpler. (No more
  checking in a browser and hoping it's the same connection.)
- Mention proxy server in message on startup proxy-check failure
- Include link to connection-error KB page on sync connection failure

Closes #1191
Closes #1513
This commit is contained in:
Dan Stillman 2018-08-12 02:29:58 -04:00
parent ea8b15a44a
commit ff8df06c75
7 changed files with 91 additions and 96 deletions

View file

@ -319,17 +319,19 @@ Zotero.HTTP = new function() {
}
Zotero.debug(msg, 1);
try {
_checkSecurity(xmlhttp, channel);
}
catch (e) {
deferred.reject(e);
return;
if (xmlhttp.status == 0) {
try {
this.checkSecurity(channel);
}
catch (e) {
deferred.reject(e);
return;
}
}
deferred.reject(new Zotero.HTTP.UnexpectedStatusException(xmlhttp, msg));
}
};
}.bind(this);
if (options.cookieSandbox) {
options.cookieSandbox.attachToInterfaceRequestor(xmlhttp);
@ -663,9 +665,17 @@ Zotero.HTTP = new function() {
})
.catch(function (e) {
// Show error icon at startup
if (e instanceof Zotero.HTTP.SecurityException) {
Zotero.proxyFailure = e;
if (!e.dialogHeader) {
e.dialogHeader = Zotero.getString('networkError.errorViaProxy');
}
e.message += "\n\n" + Zotero.getString('startupError.internetFunctionalityMayNotWork');
if (!e.dialogButtonText) {
e.dialogButtonText = Zotero.getString('general.moreInformation');
e.dialogButtonCallback = () => {
Zotero.launchURL('https://www.zotero.org/support/kb/connection_error');
};
}
Zotero.proxyFailure = e;
Zotero.logError(e);
let msg = "Error connecting to proxy -- proxied requests may not work";
Zotero.logError(msg);
@ -992,8 +1002,8 @@ Zotero.HTTP = new function() {
}
}
function _checkSecurity(xmlhttp, channel) {
if (xmlhttp.status != 0 || !channel) {
this.checkSecurity = function (channel) {
if (!channel) {
return;
}
@ -1005,37 +1015,27 @@ Zotero.HTTP = new function() {
secInfo.QueryInterface(Ci.nsITransportSecurityInfo);
if ((secInfo.securityState & Ci.nsIWebProgressListener.STATE_IS_INSECURE)
== Ci.nsIWebProgressListener.STATE_IS_INSECURE) {
let url = channel.name;
let ios = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
try {
var uri = ios.newURI(url, null, null);
var host = uri.host;
}
catch (e) {
Zotero.debug(e);
}
let kbURL = 'https://www.zotero.org/support/kb/ssl_certificate_error';
msg = Zotero.getString(
'sync.storage.error.webdav.sslCertificateError',
'https://' + host
);
// Show actual error from the networking stack, with the hyperlink around the
// error code removed
msg = Zotero.Utilities.unescapeHTML(secInfo.errorMessage);
dialogButtonText = Zotero.getString('general.moreInformation');
dialogButtonCallback = function () {
let wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
let win = wm.getMostRecentWindow("navigator:browser");
win.ZoteroPane.loadURI(kbURL, { metaKey: true, shiftKey: true });
Zotero.launchURL('https://www.zotero.org/support/kb/ssl_certificate_error');
};
}
else if ((secInfo.securityState & Ci.nsIWebProgressListener.STATE_IS_BROKEN)
== Ci.nsIWebProgressListener.STATE_IS_BROKEN) {
msg = Zotero.getString('sync.error.sslConnectionError');
msg = Zotero.getString('networkError.connectionNotSecure')
+ Zotero.Utilities.unescapeHTML(secInfo.errorMessage);
}
if (msg) {
throw new Zotero.HTTP.SecurityException(
msg,
{
dialogHeader: Zotero.getString(
'networkError.connectionNotSecure',
Zotero.clientName
),
dialogButtonText,
dialogButtonCallback
}

View file

@ -158,6 +158,17 @@ Zotero.Sync.Storage.Mode.ZFS.prototype = {
return;
}
// Check for SSL certificate error
if (status == 0) {
try {
Zotero.HTTP.checkSecurity(req);
}
catch (e) {
deferred.reject(e);
return;
}
}
// If S3 connection is interrupted, delay and retry, or bail if too many
// consecutive failures
if (status == 0 || status == 500 || status == 503) {

View file

@ -788,12 +788,9 @@ Zotero.Sync.APIClient.prototype = {
/**
* Check connection for certificate errors, interruptions, and empty responses and
* throw an appropriate error
* Check connection for interruptions and empty responses and throw an appropriate error
*/
_checkConnection: function (xmlhttp, channel) {
const Ci = Components.interfaces;
if (!xmlhttp.responseText && (xmlhttp.status == 0 || xmlhttp.status == 200)) {
let msg = null;
let dialogButtonText = null;
@ -801,8 +798,11 @@ Zotero.Sync.APIClient.prototype = {
if (xmlhttp.status === 0) {
msg = Zotero.getString('sync.error.checkConnection');
dialogButtonText = Zotero.getString('general.moreInformation');
let url = 'https://www.zotero.org/support/kb/connection_error';
dialogButtonCallback = () => Zotero.launchURL(url);
}
if (!msg) {
else if (!msg) {
msg = Zotero.getString('sync.error.emptyResponseServer')
+ Zotero.getString('general.tryAgainLater');
}

View file

@ -1404,26 +1404,31 @@ Zotero.Sync.Runner_Module = function (options = {}) {
}
label.setAttribute('value', libraryName);
}
var content = doc.createElement('hbox');
var content = doc.createElement('vbox');
var buttons = doc.createElement('hbox');
buttons.pack = 'end';
box.appendChild(label);
box.appendChild(content);
box.appendChild(buttons);
if (e.dialogHeader) {
let header = doc.createElement('description');
header.className = 'error-header';
header.textContent = e.dialogHeader;
content.appendChild(header);
}
// Show our own error mesages directly
var msg;
if (e instanceof Zotero.Error) {
var msg = e.message;
msg = e.message;
}
// For unexpected ones, just show a generic message
else if (e instanceof Zotero.HTTP.UnexpectedStatusException && e.xmlhttp.responseText) {
msg = Zotero.Utilities.ellipsize(e.xmlhttp.responseText, 1000, true);
}
else {
if (e instanceof Zotero.HTTP.UnexpectedStatusException) {
msg = Zotero.Utilities.ellipsize(e.xmlhttp.responseText, 1000, true);
}
else {
// TODO: Localize
var msg = "An error occurred during syncing:\n\n" + e.message;
}
msg = e.message;
}
var desc = doc.createElement('description');

View file

@ -483,10 +483,7 @@ var ZoteroPane = new function()
if (Zotero.proxyFailure) {
try {
Zotero.proxyFailure.message += "\n\n"
+ Zotero.getString('startupError.internetFunctionalityMayNotWork');
Zotero.Sync.Runner.updateIcons(Zotero.proxyFailure);
Zotero.proxyFailure = null;
}
catch (e) {
Zotero.logError(e);
@ -4611,64 +4608,33 @@ var ZoteroPane = new function()
this.syncAlert = function (e) {
e = Zotero.Sync.Runner.parseError(e);
var ps = Services.prompt;
var buttonText = e.dialogButtonText;
var buttonCallback = e.dialogButtonCallback;
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
var buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_OK
+ ps.BUTTON_POS_1 * ps.BUTTON_TITLE_IS_STRING;
// Warning
if (e.errorType == 'warning') {
var title = Zotero.getString('general.warning');
if (e.errorType == 'warning' || e.errorType == 'error') {
let title = Zotero.getString('general.' + e.errorType);
// TODO: Display header in bold
let msg = (e.dialogHeader ? e.dialogHeader + '\n\n' : '') + e.message;
// If secondary button not specified, just use an alert
if (e.buttonText) {
var buttonText = e.buttonText;
}
else {
if (e.errorType == 'warning' || buttonText === null) {
ps.alert(null, title, e.message);
return;
}
var index = ps.confirmEx(
null,
title,
e.message,
buttonFlags,
"",
buttonText,
"", null, {}
);
if (index == 1) {
setTimeout(function () { buttonCallback(); }, 1);
}
}
// Error
else if (e.errorType == 'error') {
var title = Zotero.getString('general.error');
// If secondary button is explicitly null, just use an alert
if (buttonText === null) {
ps.alert(null, title, e.message);
return;
}
if (typeof buttonText == 'undefined') {
var buttonText = Zotero.getString('errorReport.reportError');
var buttonCallback = function () {
if (!buttonText) {
buttonText = Zotero.getString('errorReport.reportError');
buttonCallback = function () {
ZoteroPane.reportErrors();
};
}
else {
var buttonText = e.buttonText;
var buttonCallback = e.buttonCallback;
}
var index = ps.confirmEx(
let buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_OK
+ ps.BUTTON_POS_1 * ps.BUTTON_TITLE_IS_STRING;
let index = ps.confirmEx(
null,
title,
e.message,
msg,
buttonFlags,
"",
buttonText,
@ -4676,12 +4642,13 @@ var ZoteroPane = new function()
);
if (index == 1) {
setTimeout(function () { buttonCallback(); }, 1);
setTimeout(buttonCallback, 1);
}
}
// Upgrade
// Upgrade message
else if (e.errorType == 'upgrade') {
ps.alert(null, "", e.message);
return;
}
};

View file

@ -75,6 +75,9 @@ general.operationInProgress = A Zotero operation is currently in progress.
general.operationInProgress.waitUntilFinished = Please wait until it has finished.
general.operationInProgress.waitUntilFinishedAndTryAgain = Please wait until it has finished and try again.
networkError.connectionNotSecure = %S could not make a secure connection.
networkError.errorViaProxy = Error connecting via proxy server
about.createdBy = Zotero is a project of the [Roy Rosenzweig Center for History and New Media] and is developed by a [global community].
about.getInvolved = Want to help? [Get involved] today!

View file

@ -645,6 +645,15 @@
margin-right: 0;
}
#zotero-sync-error-panel .error-header {
font-size: 14px;
font-weight: bold;
margin-bottom: 1em;
-moz-user-select: text;
user-select: text;
cursor: text
}
.zotero-sync-error-panel-library-name {
font-size: 12px;
font-weight: bold;