Bug 1576333 - Part 1: Allow Downloads users to inspect HTTP codes r=mak

Differential Revision: https://phabricator.services.mozilla.com/D43342

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Nils Maier 2019-09-06 03:08:44 +00:00
Родитель e3584dcd2b
Коммит 37528b94d0
3 изменённых файлов: 125 добавлений и 0 удалений

Просмотреть файл

@ -1329,6 +1329,23 @@ this.DownloadSource.prototype = {
*/
adjustChannel: null,
/**
* For downloads handled by the (default) DownloadCopySaver, this function
* will determine, if provided, if a download can progress or has to be
* cancelled based on the HTTP status code of the network channel.
*
* @note If this is defined this object will not be serializable, thus the
* Download object will not be persisted across sessions.
*
* @param aDownload
* The download asking.
* @param aStatus
* The HTTP status in question
*
* @return {Boolean} Download can progress
*/
allowHttpStatus: null,
/**
* Returns a static representation of the current object state.
*
@ -1340,6 +1357,11 @@ this.DownloadSource.prototype = {
return null;
}
if (this.allowHttpStatus) {
// If the callback was used, we can't reproduce this across sessions.
return null;
}
// Simplify the representation if we don't have other details.
if (!this.isPrivate && !this.referrerInfo && !this._unknownProperties) {
return this.url;
@ -1381,6 +1403,11 @@ this.DownloadSource.prototype = {
* this function can adjust the network channel before
* it is opened, for example to change the HTTP headers
* or to upload a stream as POST data. Optional.
* allowHttpStatus: For downloads handled by the (default)
* DownloadCopySaver, this function will determine, if
* provided, if a download can progress or has to be
* cancelled based on the HTTP status code of the
* network channel.
* }
*
* @return The newly created DownloadSource object.
@ -1419,6 +1446,10 @@ this.DownloadSource.fromSerializable = function(aSerializable) {
source.adjustChannel = aSerializable.adjustChannel;
}
if ("allowHttpStatus" in aSerializable) {
source.allowHttpStatus = aSerializable.allowHttpStatus;
}
deserializeUnknownProperties(
source,
aSerializable,
@ -2103,6 +2134,19 @@ this.DownloadCopySaver.prototype = {
return;
}
// Check back with the initiator if we should allow a certain
// HTTP code. By default, we'll just save error pages too,
// however a consumer down the line, such as the WebExtensions
// downloads API might want to handle this differently.
if (
download.source.allowHttpStatus &&
aRequest instanceof Ci.nsIHttpChannel &&
!download.source.allowHttpStatus(download, aRequest.responseStatus)
) {
aRequest.cancel(Cr.NS_BINDING_ABORTED);
return;
}
aSetPropertiesFn({ contentType: channel.contentType });
// Ensure we report the value of "Content-Length", if available,

Просмотреть файл

@ -916,6 +916,13 @@ add_task(function test_common_initialize() {
}
);
gHttpServer.registerPathHandler("/busy.txt", function(aRequest, aResponse) {
aResponse.setStatusLine("1.1", 504, "Gateway Timeout");
aResponse.setHeader("Content-Type", "text/plain", false);
aResponse.setHeader("Content-Length", "" + TEST_DATA_SHORT.length, false);
aResponse.write(TEST_DATA_SHORT);
});
// This URL will emulate being blocked by Windows Parental controls
gHttpServer.registerPathHandler("/parentalblocked.zip", function(
aRequest,

Просмотреть файл

@ -46,6 +46,80 @@ add_task(async function test_error_target_downloadingToSameFile() {
);
});
/**
* Tests allowHttpStatus allowing requests
*/
add_task(async function test_error_notfound() {
const targetFile = getTempFile(TEST_TARGET_FILE_NAME);
let called = false;
const download = await Downloads.createDownload({
source: {
url: httpUrl("notfound.gone"),
allowHttpStatus(aDownload, aStatusCode) {
Assert.strictEqual(download, aDownload, "Check Download objects");
Assert.strictEqual(aStatusCode, 404, "The status should be correct");
called = true;
return true;
},
},
target: targetFile,
});
await download.start();
Assert.ok(called, "allowHttpStatus should have been called");
});
/**
* Tests allowHttpStatus rejecting requests
*/
add_task(async function test_error_notfound_reject() {
const targetFile = getTempFile(TEST_TARGET_FILE_NAME);
let called = false;
const download = await Downloads.createDownload({
source: {
url: httpUrl("notfound.gone"),
allowHttpStatus(aDownload, aStatusCode) {
Assert.strictEqual(download, aDownload, "Check Download objects");
Assert.strictEqual(aStatusCode, 404, "The status should be correct");
called = true;
return false;
},
},
target: targetFile,
});
await Assert.rejects(
download.start(),
ex => ex instanceof Downloads.Error && ex.becauseSourceFailed,
"Download should have been rejected"
);
Assert.ok(called, "allowHttpStatus should have been called");
});
/**
* Tests allowHttpStatus rejecting requests other than 404
*/
add_task(async function test_error_busy_reject() {
const targetFile = getTempFile(TEST_TARGET_FILE_NAME);
let called = false;
const download = await Downloads.createDownload({
source: {
url: httpUrl("busy.txt"),
allowHttpStatus(aDownload, aStatusCode) {
Assert.strictEqual(download, aDownload, "Check Download objects");
Assert.strictEqual(aStatusCode, 504, "The status should be correct");
called = true;
return false;
},
},
target: targetFile,
});
await Assert.rejects(
download.start(),
ex => ex instanceof Downloads.Error && ex.becauseSourceFailed,
"Download should have been rejected"
);
Assert.ok(called, "allowHttpStatus should have been called");
});
/**
* Tests the DownloadError object.
*/