Better handling of invalid HTTP responses

If a server returns an invalid HTTP response (e.g., Content-Encoding:
gzip with a plaintext body, a.k.a. NS_ERROR_INVALID_CONTENT_ENCODING)
but we can still parse a 4xx or 5xx HTTP response code, use that for the
XHR status, since it might be enough for what we need to do (e.g.,
verify a 404 from a WebDAV server). This fixes a current problem with
Box [1].

Also fix a "msg is not defined" error when the XHR status is 0 but the
SSL connection is fine and include the channel and response status on
the UnexpectedStatusException object (though both of these would now
only happen on an invalid 2xx response, when the XHR status would remain
as 0).

[1] https://forums.zotero.org/discussion/comment/301014/#Comment_301014
This commit is contained in:
Dan Stillman 2018-02-10 22:14:01 -05:00
parent 1aade0f268
commit 4bbae6e17a

View file

@ -12,6 +12,8 @@ Zotero.HTTP = new function() {
this.UnexpectedStatusException = function(xmlhttp, msg) {
this.xmlhttp = xmlhttp;
this.status = xmlhttp.status;
this.channelStatus = null;
this.responseStatus = null;
this.channel = xmlhttp.channel;
this.message = msg;
this.stack = new Error().stack;
@ -33,6 +35,24 @@ Zotero.HTTP = new function() {
catch (e) {
Zotero.debug(e, 1);
}
// If the connection failed, try to find out what really happened
if (!this.status) {
try {
if (xmlhttp.channel.status) {
this.channelStatus = xmlhttp.channel.status;
Zotero.debug("Channel status was " + this.channelStatus, 2);
}
}
catch (e) {}
try {
if (xmlhttp.channel.responseStatus) {
this.responseStatus = xmlhttp.channel.responseStatus;
Zotero.debug("Response status was " + this.responseStatus, 2);
}
}
catch (e) {}
}
};
this.UnexpectedStatusException.prototype = Object.create(Error.prototype);
this.UnexpectedStatusException.prototype.is4xx = function () {
@ -248,6 +268,18 @@ Zotero.HTTP = new function() {
xmlhttp.onloadend = function() {
var status = xmlhttp.status;
// If an invalid HTTP response (e.g., NS_ERROR_INVALID_CONTENT_ENCODING) includes a
// 4xx or 5xx HTTP response code, swap it in, since it might be enough info to do
// what we need (e.g., verify a 404 from a WebDAV server)
try {
if (!status && xmlhttp.channel.responseStatus >= 400) {
Zotero.warn(`Overriding status for invalid response for ${dispURL} `
+ `(${xmlhttp.channel.status})`);
status = xmlhttp.channel.responseStatus;
}
}
catch (e) {}
if (options.successCodes) {
var success = options.successCodes.indexOf(status) != -1;
}
@ -264,13 +296,13 @@ Zotero.HTTP = new function() {
if(success) {
Zotero.debug("HTTP " + method + " " + dispURL
+ " succeeded with " + xmlhttp.status);
+ " succeeded with " + status);
if (options.debug) {
Zotero.debug(xmlhttp.responseText);
}
deferred.resolve(xmlhttp);
} else {
let msg = "HTTP " + method + " " + dispURL + " failed with status code " + xmlhttp.status;
let msg = "HTTP " + method + " " + dispURL + " failed with status code " + status;
if (!xmlhttp.responseType && xmlhttp.responseText) {
msg += ":\n\n" + xmlhttp.responseText;
}
@ -1025,6 +1057,8 @@ Zotero.HTTP = new function() {
let secInfo = channel.securityInfo;
let msg;
let dialogButtonText;
let dialogButtonCallback;
if (secInfo instanceof Ci.nsITransportSecurityInfo) {
secInfo.QueryInterface(Ci.nsITransportSecurityInfo);
if ((secInfo.securityState & Ci.nsIWebProgressListener.STATE_IS_INSECURE)
@ -1053,17 +1087,15 @@ Zotero.HTTP = new function() {
== Ci.nsIWebProgressListener.STATE_IS_BROKEN) {
msg = Zotero.getString('sync.error.sslConnectionError');
}
else {
Zotero.debug(secInfo.securityState, 1);
msg = Zotero.getString('sync.error.sslConnectionError');
if (msg) {
throw new Zotero.HTTP.SecurityException(
msg,
{
dialogButtonText,
dialogButtonCallback
}
);
}
throw new Zotero.HTTP.SecurityException(
msg,
{
dialogButtonText,
dialogButtonCallback
}
);
}
}