diff --git a/chrome/content/zotero/xpcom/http.js b/chrome/content/zotero/xpcom/http.js index 7954d04a5d..808507f092 100644 --- a/chrome/content/zotero/xpcom/http.js +++ b/chrome/content/zotero/xpcom/http.js @@ -355,6 +355,48 @@ Zotero.HTTP = new function() { if (options.debug) { Zotero.debug(xmlhttp.responseText); } + + // Follow meta redirects + if (options.responseType === 'document' && + (!options.numRedirects || options.numRedirects < 3)) { + let contentType = xmlhttp.getResponseHeader('Content-Type'); + if (contentType && contentType.startsWith('text/html')) { + let meta = xmlhttp.response.querySelector('meta[http-equiv="refresh" i]'); + if (meta) { + let content = meta.getAttribute('content'); + if (content) { + let parts = content.split(/;\s*url=/); + // If there's a redirect to another URL in less than 15 seconds, + // follow it + if (parts.length === 2 && parseInt(parts[0]) <= 15) { + let url = parts[1].trim().replace(/^'(.+)'/, '$1'); + + // Resolve URL. P.S.: For unknown reason this only works + // if server returns 'Content-Type: text/html' header + let a = xmlhttp.response.createElement('a'); + a.href = url; + let resolvedUrl = a.href; + + // Make sure the absolute URL is actually resolved + if (/^https?:\/\//.test(resolvedUrl)) { + if (options.numRedirects) { + options.numRedirects++; + } + else { + options.numRedirects = 1; + } + + // Meta redirect is always GET + return Zotero.HTTP.request("GET", resolvedUrl, options) + .then(xmlhttp => deferred.resolve(xmlhttp)) + .catch(e => deferred.reject(e)); + } + } + } + } + } + } + deferred.resolve(xmlhttp); } else { let msg = "HTTP " + method + " " + dispURL + " failed with status code " + status; @@ -863,8 +905,8 @@ Zotero.HTTP = new function() { } ) .then((xhr) => { - var doc = this.wrapDocument(xhr.response, url); - return processor(doc, url); + var doc = this.wrapDocument(xhr.response, xhr.responseURL); + return processor(doc, xhr.responseURL); }); }); diff --git a/test/tests/httpTest.js b/test/tests/httpTest.js index 0ad5ae505c..7fee7963d1 100644 --- a/test/tests/httpTest.js +++ b/test/tests/httpTest.js @@ -29,6 +29,16 @@ describe("Zotero.HTTP", function () { } } ); + httpd.registerPathHandler( + '/test-redirect.html', + { + handle: function (request, response) { + response.setHeader("Content-Type", 'text/html', false); + response.setStatusLine(null, 200, "OK"); + response.write(""); + } + } + ); }); after(function* () { @@ -68,6 +78,20 @@ describe("Zotero.HTTP", function () { ); assert.isTrue(called); }); + + it("should follow meta redirect for a document", async function () { + let url1 = `http://127.0.0.1:${port}/test-redirect.html`; + let url2 = `http://127.0.0.1:${port}/test.html`; + let called = false; + await Zotero.HTTP.processDocuments( + url1, + function (doc) { + assert.equal(doc.location.href, url2); + called = true; + } + ); + assert.isTrue(called); + }); }); describe("#loadDocuments()", function () {