Add followRedirects: false option to Zotero.HTTP.request()
Currently only .status and .getResponseHeader() (for getting 'Location') are available in the returned object, but we could make the body available if necessary.
This commit is contained in:
parent
b8db83af08
commit
b782120840
3 changed files with 83 additions and 16 deletions
|
@ -201,6 +201,8 @@ Zotero.HTTP = new function() {
|
|||
// Send cookie even if "Allow third-party cookies" is disabled (>=Fx3.6 only)
|
||||
var channel = xmlhttp.channel,
|
||||
isFile = channel instanceof Components.interfaces.nsIFileChannel;
|
||||
var redirectStatus;
|
||||
var redirectLocation;
|
||||
if(channel instanceof Components.interfaces.nsIHttpChannelInternal) {
|
||||
channel.forceAllowThirdPartyCookie = true;
|
||||
|
||||
|
@ -218,8 +220,22 @@ Zotero.HTTP = new function() {
|
|||
if (options.dontCache) {
|
||||
channel.loadFlags |= Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE;
|
||||
}
|
||||
|
||||
// Don't follow redirects
|
||||
if (options.followRedirects === false) {
|
||||
channel.notificationCallbacks = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIInterfaceRequestor, Ci.nsIChannelEventSync]),
|
||||
getInterface: XPCOMUtils.generateQI([Ci.nsIChannelEventSink]),
|
||||
asyncOnChannelRedirect: function (oldChannel, newChannel, flags, callback) {
|
||||
redirectStatus = (flags & Ci.nsIChannelEventSink.REDIRECT_PERMANENT) ? 301 : 302;
|
||||
redirectLocation = newChannel.URI.spec;
|
||||
oldChannel.cancel(Cr.NS_BINDING_ABORTED);
|
||||
callback.onRedirectVerifyCallback(Cr.NS_BINDING_ABORTED);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Set responseType
|
||||
if (options.responseType) {
|
||||
xmlhttp.responseType = options.responseType;
|
||||
|
@ -276,17 +292,24 @@ Zotero.HTTP = new function() {
|
|||
deferred.reject(new Zotero.HTTP.TimeoutException(options.timeout));
|
||||
};
|
||||
|
||||
xmlhttp.onloadend = function() {
|
||||
var status = xmlhttp.status;
|
||||
xmlhttp.onloadend = async function() {
|
||||
var status = xmlhttp.status || redirectStatus;
|
||||
|
||||
// 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;
|
||||
if (!status) {
|
||||
let responseStatus = xmlhttp.channel.responseStatus;
|
||||
// If we cancelled a redirect, get the 3xx status from the channel
|
||||
if (responseStatus >= 300 && responseStatus < 400) {
|
||||
status = responseStatus;
|
||||
}
|
||||
// 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)
|
||||
else if (responseStatus >= 400) {
|
||||
Zotero.warn(`Overriding status for invalid response for ${dispURL} `
|
||||
+ `(${xmlhttp.channel.status})`);
|
||||
status = responseStatus;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {}
|
||||
|
@ -301,6 +324,21 @@ Zotero.HTTP = new function() {
|
|||
else if(isFile) {
|
||||
var success = status == 200 || status == 0;
|
||||
}
|
||||
else if (redirectStatus) {
|
||||
var success = true;
|
||||
let channel = xmlhttp.channel;
|
||||
xmlhttp = {
|
||||
status,
|
||||
getResponseHeader: function (header) {
|
||||
if (header.toLowerCase() == 'location') {
|
||||
return redirectLocation;
|
||||
}
|
||||
Zotero.debug("Warning: Attempt to get response header other than Location "
|
||||
+ "for redirect", 2);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
var success = status >= 200 && status < 300;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
/** XPCOM files to be loaded for all modes **/
|
||||
const xpcomFilesAll = [
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
describe("Zotero.HTTP", function () {
|
||||
var httpd;
|
||||
var port = 16213;
|
||||
var baseURL = `http://127.0.0.1:${port}/`
|
||||
var testURL = baseURL + 'test.html';
|
||||
var redirectLocation = baseURL + 'test2.html';
|
||||
|
||||
before(function* () {
|
||||
Components.utils.import("resource://zotero-unit/httpd.js");
|
||||
|
@ -16,6 +19,16 @@ describe("Zotero.HTTP", function () {
|
|||
}
|
||||
}
|
||||
);
|
||||
httpd.registerPathHandler(
|
||||
'/redirect',
|
||||
{
|
||||
handle: function (request, response) {
|
||||
response.setHeader('Location', redirectLocation);
|
||||
response.setStatusLine(null, 301, "Moved Permanently");
|
||||
response.write(`<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">\n<html><head>\n<title>301 Moved Permanently</title>\n</head><body>\n<h1>Moved Permanently</h1>\n<p>The document has moved <a href="${redirectLocation}">here</a>.</p>\n</body></html>`);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
after(function* () {
|
||||
|
@ -24,14 +37,29 @@ describe("Zotero.HTTP", function () {
|
|||
yield defer.promise;
|
||||
});
|
||||
|
||||
|
||||
describe("#request()", function () {
|
||||
it("should succeed with 3xx status if followRedirects is false", async function () {
|
||||
var req = await Zotero.HTTP.request(
|
||||
'GET',
|
||||
baseURL + 'redirect',
|
||||
{
|
||||
followRedirects: false
|
||||
}
|
||||
);
|
||||
assert.equal(req.status, 301);
|
||||
assert.equal(req.getResponseHeader('Location'), redirectLocation);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("#processDocuments()", function () {
|
||||
it("should provide a document object", function* () {
|
||||
var called = false;
|
||||
var url = `http://127.0.0.1:${port}/test.html`;
|
||||
yield Zotero.HTTP.processDocuments(
|
||||
url,
|
||||
testURL,
|
||||
function (doc) {
|
||||
assert.equal(doc.location.href, url);
|
||||
assert.equal(doc.location.href, testURL);
|
||||
assert.equal(doc.querySelector('p').textContent, 'Test');
|
||||
var p = doc.evaluate('//p', doc, null, XPathResult.ANY_TYPE, null).iterateNext();
|
||||
assert.equal(p.textContent, 'Test');
|
||||
|
@ -56,12 +84,11 @@ describe("Zotero.HTTP", function () {
|
|||
|
||||
it("should provide a document object", function* () {
|
||||
var called = false;
|
||||
var url = `http://127.0.0.1:${port}/test.html`;
|
||||
yield new Zotero.Promise((resolve) => {
|
||||
Zotero.HTTP.loadDocuments(
|
||||
url,
|
||||
testURL,
|
||||
function (doc) {
|
||||
assert.equal(doc.location.href, url);
|
||||
assert.equal(doc.location.href, testURL);
|
||||
assert.equal(doc.querySelector('p').textContent, 'Test');
|
||||
var p = doc.evaluate('//p', doc, null, XPathResult.ANY_TYPE, null).iterateNext();
|
||||
assert.equal(p.textContent, 'Test');
|
||||
|
|
Loading…
Reference in a new issue