Commit b7591fb6 authored by Marijn Kruisselbrink's avatar Marijn Kruisselbrink Committed by Commit Bot

Failure in reading Blob URL should result in network error.

Intent to Implement and Ship: https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/zm0ZUCINXBk

Bug: 732750
Change-Id: I07b45276f3e0ddf991f1f8a6c390bf5b843ec22e
Reviewed-on: https://chromium-review.googlesource.com/893942Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Commit-Queue: Marijn Kruisselbrink <mek@chromium.org>
Cr-Commit-Position: refs/heads/master@{#533340}
parent 634d5f59
...@@ -20,13 +20,17 @@ var streamDetails; ...@@ -20,13 +20,17 @@ var streamDetails;
function fetchUrl(url) { function fetchUrl(url) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
var request = new XMLHttpRequest(); var request = new XMLHttpRequest();
request.onreadystatechange = function() { request.onload = function() {
if (request.readyState == 4) { resolve({
resolve({ status: request.status,
status: request.status, data: request.responseText,
data: request.responseText, });
}); };
} request.onerror = function() {
resolve({
status: request.status,
data: 'error',
});
}; };
request.open('GET', streamDetails.streamUrl, true); request.open('GET', streamDetails.streamUrl, true);
request.send(); request.send();
...@@ -88,8 +92,8 @@ var tests = [ ...@@ -88,8 +92,8 @@ var tests = [
checkStreamDetails('testAbort.csv', false); checkStreamDetails('testAbort.csv', false);
chrome.mimeHandlerPrivate.abortStream(function() { chrome.mimeHandlerPrivate.abortStream(function() {
fetchUrl(streamDetails.streamUrl).then(function(response) { fetchUrl(streamDetails.streamUrl).then(function(response) {
chrome.test.assertEq(404, response.status); chrome.test.assertEq(0, response.status);
chrome.test.assertEq('', response.data); chrome.test.assertEq('error', response.data);
chrome.test.succeed(); chrome.test.succeed();
}); });
}); });
......
...@@ -75,14 +75,15 @@ chrome.streamsPrivate.onExecuteMimeTypeHandler.addListener( ...@@ -75,14 +75,15 @@ chrome.streamsPrivate.onExecuteMimeTypeHandler.addListener(
chrome.streamsPrivate.abort(params.streamUrl, function() { chrome.streamsPrivate.abort(params.streamUrl, function() {
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.open("GET", params.streamUrl, false); xhr.open("GET", params.streamUrl, false);
xhr.send(null); try {
if (xhr.status == 404) { xhr.send(null);
} catch (e) {
chrome.test.notifyPass(); chrome.test.notifyPass();
} else { return;
chrome.test.notifyFail(
'Expected a stream URL response of 404, got ' + xhr.status + '.');
hasFailed = true;
} }
chrome.test.notifyFail(
'Expected a network error, got ' + xhr.status + '.');
hasFailed = true;
}); });
} }
return; return;
......
...@@ -162,6 +162,7 @@ class BlobURLRequestJobTest : public testing::TestWithParam<RequestTestType> { ...@@ -162,6 +162,7 @@ class BlobURLRequestJobTest : public testing::TestWithParam<RequestTestType> {
blob_data_(new BlobDataBuilder("uuid")), blob_data_(new BlobDataBuilder("uuid")),
blob_uuid_(blob_data_->uuid()), blob_uuid_(blob_data_->uuid()),
response_error_code_(net::OK), response_error_code_(net::OK),
expected_error_code_(net::OK),
expected_status_code_(0) {} expected_status_code_(0) {}
void SetUp() override { void SetUp() override {
...@@ -260,14 +261,15 @@ class BlobURLRequestJobTest : public testing::TestWithParam<RequestTestType> { ...@@ -260,14 +261,15 @@ class BlobURLRequestJobTest : public testing::TestWithParam<RequestTestType> {
void TestSuccessNonrangeRequest(const std::string& expected_response, void TestSuccessNonrangeRequest(const std::string& expected_response,
int64_t expected_content_length) { int64_t expected_content_length) {
expected_error_code_ = net::OK;
expected_status_code_ = 200; expected_status_code_ = 200;
expected_response_ = expected_response; expected_response_ = expected_response;
TestRequest("GET", net::HttpRequestHeaders()); TestRequest("GET", net::HttpRequestHeaders());
EXPECT_EQ(expected_content_length, response_headers_->GetContentLength()); EXPECT_EQ(expected_content_length, response_headers_->GetContentLength());
} }
void TestErrorRequest(int expected_status_code) { void TestErrorRequest(int expected_error_code) {
expected_status_code_ = expected_status_code; expected_error_code_ = expected_error_code;
expected_response_ = ""; expected_response_ = "";
TestRequest("GET", net::HttpRequestHeaders()); TestRequest("GET", net::HttpRequestHeaders());
EXPECT_TRUE(response_metadata_.empty()); EXPECT_TRUE(response_metadata_.empty());
...@@ -355,9 +357,11 @@ class BlobURLRequestJobTest : public testing::TestWithParam<RequestTestType> { ...@@ -355,9 +357,11 @@ class BlobURLRequestJobTest : public testing::TestWithParam<RequestTestType> {
} }
// Verify response. // Verify response.
EXPECT_EQ(net::OK, response_error_code_); EXPECT_EQ(expected_error_code_, response_error_code_);
EXPECT_EQ(expected_status_code_, response_headers_->response_code()); if (response_error_code_ == net::OK) {
EXPECT_EQ(expected_response_, response_); EXPECT_EQ(expected_status_code_, response_headers_->response_code());
EXPECT_EQ(expected_response_, response_);
}
} }
void BuildComplicatedData(std::string* expected_result) { void BuildComplicatedData(std::string* expected_result) {
...@@ -446,6 +450,7 @@ class BlobURLRequestJobTest : public testing::TestWithParam<RequestTestType> { ...@@ -446,6 +450,7 @@ class BlobURLRequestJobTest : public testing::TestWithParam<RequestTestType> {
scoped_refptr<net::HttpResponseHeaders> response_headers_; scoped_refptr<net::HttpResponseHeaders> response_headers_;
std::string response_metadata_; std::string response_metadata_;
int expected_error_code_;
int expected_status_code_; int expected_status_code_;
std::string expected_response_; std::string expected_response_;
}; };
...@@ -481,14 +486,14 @@ TEST_P(BlobURLRequestJobTest, TestGetNonExistentFileRequest) { ...@@ -481,14 +486,14 @@ TEST_P(BlobURLRequestJobTest, TestGetNonExistentFileRequest) {
temp_file1_.InsertBeforeExtension(FILE_PATH_LITERAL("-na")); temp_file1_.InsertBeforeExtension(FILE_PATH_LITERAL("-na"));
blob_data_->AppendFile(non_existent_file, 0, blob_data_->AppendFile(non_existent_file, 0,
std::numeric_limits<uint64_t>::max(), base::Time()); std::numeric_limits<uint64_t>::max(), base::Time());
TestErrorRequest(404); TestErrorRequest(net::ERR_FILE_NOT_FOUND);
} }
TEST_P(BlobURLRequestJobTest, TestGetChangedFileRequest) { TEST_P(BlobURLRequestJobTest, TestGetChangedFileRequest) {
base::Time old_time = base::Time old_time =
temp_file_modification_time1_ - base::TimeDelta::FromSeconds(10); temp_file_modification_time1_ - base::TimeDelta::FromSeconds(10);
blob_data_->AppendFile(temp_file1_, 0, 3, old_time); blob_data_->AppendFile(temp_file1_, 0, 3, old_time);
TestErrorRequest(404); TestErrorRequest(net::ERR_FILE_NOT_FOUND);
} }
TEST_P(BlobURLRequestJobTest, TestGetSlicedFileRequest) { TEST_P(BlobURLRequestJobTest, TestGetSlicedFileRequest) {
...@@ -528,7 +533,7 @@ TEST_P(BlobURLRequestJobTest, TestGetNonExistentFileSystemFileRequest) { ...@@ -528,7 +533,7 @@ TEST_P(BlobURLRequestJobTest, TestGetNonExistentFileSystemFileRequest) {
blob_data_->AppendFileSystemFile(non_existent_file, 0, blob_data_->AppendFileSystemFile(non_existent_file, 0,
std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max(),
base::Time(), file_system_context_); base::Time(), file_system_context_);
TestErrorRequest(404); TestErrorRequest(net::ERR_FILE_NOT_FOUND);
} }
TEST_P(BlobURLRequestJobTest, TestGetInvalidFileSystemFileRequest) { TEST_P(BlobURLRequestJobTest, TestGetInvalidFileSystemFileRequest) {
...@@ -537,7 +542,7 @@ TEST_P(BlobURLRequestJobTest, TestGetInvalidFileSystemFileRequest) { ...@@ -537,7 +542,7 @@ TEST_P(BlobURLRequestJobTest, TestGetInvalidFileSystemFileRequest) {
blob_data_->AppendFileSystemFile(invalid_file, 0, blob_data_->AppendFileSystemFile(invalid_file, 0,
std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max(),
base::Time(), file_system_context_); base::Time(), file_system_context_);
TestErrorRequest(500); TestErrorRequest(net::ERR_FAILED);
} }
TEST_P(BlobURLRequestJobTest, TestGetChangedFileSystemFileRequest) { TEST_P(BlobURLRequestJobTest, TestGetChangedFileSystemFileRequest) {
...@@ -546,7 +551,7 @@ TEST_P(BlobURLRequestJobTest, TestGetChangedFileSystemFileRequest) { ...@@ -546,7 +551,7 @@ TEST_P(BlobURLRequestJobTest, TestGetChangedFileSystemFileRequest) {
base::TimeDelta::FromSeconds(10); base::TimeDelta::FromSeconds(10);
blob_data_->AppendFileSystemFile(temp_file_system_file1_, 0, 3, old_time, blob_data_->AppendFileSystemFile(temp_file_system_file1_, 0, 3, old_time,
file_system_context_); file_system_context_);
TestErrorRequest(404); TestErrorRequest(net::ERR_FILE_NOT_FOUND);
} }
TEST_P(BlobURLRequestJobTest, TestGetSlicedFileSystemFileRequest) { TEST_P(BlobURLRequestJobTest, TestGetSlicedFileSystemFileRequest) {
...@@ -693,7 +698,7 @@ TEST_P(BlobURLRequestJobTest, TestZeroSizeSideData) { ...@@ -693,7 +698,7 @@ TEST_P(BlobURLRequestJobTest, TestZeroSizeSideData) {
TEST_P(BlobURLRequestJobTest, BrokenBlob) { TEST_P(BlobURLRequestJobTest, BrokenBlob) {
blob_handle_ = blob_context_.AddBrokenBlob( blob_handle_ = blob_context_.AddBrokenBlob(
"uuid", "", "", storage::BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS); "uuid", "", "", storage::BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS);
TestErrorRequest(500); TestErrorRequest(net::ERR_FAILED);
} }
// The parameter's value determines whether BlobURLLoaderFactory is used. // The parameter's value determines whether BlobURLLoaderFactory is used.
......
...@@ -140,18 +140,7 @@ void BlobURLLoader::DidRead(int num_bytes) { ...@@ -140,18 +140,7 @@ void BlobURLLoader::DidRead(int num_bytes) {
void BlobURLLoader::OnComplete(net::Error error_code, void BlobURLLoader::OnComplete(net::Error error_code,
uint64_t total_written_bytes) { uint64_t total_written_bytes) {
if (error_code != net::OK && !sent_headers_) { network::URLLoaderCompletionStatus status(error_code);
net::HttpStatusCode status_code =
storage::BlobURLRequestJob::NetErrorToHttpStatusCode(error_code);
network::ResourceResponseHead response;
response.headers = storage::BlobURLRequestJob::GenerateHeaders(
status_code, nullptr, nullptr, 0, 0);
client_->OnReceiveResponse(response, base::nullopt, nullptr);
}
network::URLLoaderCompletionStatus status;
// TODO(kinuko): We should probably set the error_code here,
// while it makes existing tests fail. https://crbug.com/732750
status.completion_time = base::TimeTicks::Now();
status.encoded_body_length = total_written_bytes; status.encoded_body_length = total_written_bytes;
status.decoded_body_length = total_written_bytes; status.decoded_body_length = total_written_bytes;
client_->OnComplete(status); client_->OnComplete(status);
......
...@@ -182,37 +182,6 @@ scoped_refptr<net::HttpResponseHeaders> BlobURLRequestJob::GenerateHeaders( ...@@ -182,37 +182,6 @@ scoped_refptr<net::HttpResponseHeaders> BlobURLRequestJob::GenerateHeaders(
return headers; return headers;
} }
net::HttpStatusCode BlobURLRequestJob::NetErrorToHttpStatusCode(
int error_code) {
net::HttpStatusCode status_code = net::HTTP_INTERNAL_SERVER_ERROR;
switch (error_code) {
case net::ERR_ACCESS_DENIED:
status_code = net::HTTP_FORBIDDEN;
break;
case net::ERR_FILE_NOT_FOUND:
status_code = net::HTTP_NOT_FOUND;
break;
case net::ERR_METHOD_NOT_SUPPORTED:
status_code = net::HTTP_METHOD_NOT_ALLOWED;
break;
case net::ERR_REQUEST_RANGE_NOT_SATISFIABLE:
status_code = net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE;
break;
case net::ERR_INVALID_ARGUMENT:
status_code = net::HTTP_BAD_REQUEST;
break;
case net::ERR_CACHE_READ_FAILURE:
case net::ERR_CACHE_CHECKSUM_READ_FAILURE:
case net::ERR_UNEXPECTED:
case net::ERR_FAILED:
break;
default:
DCHECK(false) << "Error code not supported: " << error_code;
break;
}
return status_code;
}
BlobURLRequestJob::~BlobURLRequestJob() { BlobURLRequestJob::~BlobURLRequestJob() {
TRACE_EVENT_ASYNC_END1("Blob", "BlobRequest", this, "uuid", TRACE_EVENT_ASYNC_END1("Blob", "BlobRequest", this, "uuid",
blob_handle_ ? blob_handle_->uuid() : "NotFound"); blob_handle_ ? blob_handle_->uuid() : "NotFound");
...@@ -314,7 +283,7 @@ void BlobURLRequestJob::NotifyFailure(int error_code) { ...@@ -314,7 +283,7 @@ void BlobURLRequestJob::NotifyFailure(int error_code) {
// now. Instead, we just error out. // now. Instead, we just error out.
DCHECK(!response_info_) << "Cannot NotifyFailure after headers."; DCHECK(!response_info_) << "Cannot NotifyFailure after headers.";
HeadersCompleted(NetErrorToHttpStatusCode(error_code)); NotifyStartError(net::URLRequestStatus::FromError(error_code));
} }
void BlobURLRequestJob::HeadersCompleted(net::HttpStatusCode status_code) { void BlobURLRequestJob::HeadersCompleted(net::HttpStatusCode status_code) {
......
...@@ -55,9 +55,6 @@ class STORAGE_EXPORT BlobURLRequestJob ...@@ -55,9 +55,6 @@ class STORAGE_EXPORT BlobURLRequestJob
uint64_t total_size, uint64_t total_size,
uint64_t content_size); uint64_t content_size);
// Helper method to map from a net error to an http status code.
static net::HttpStatusCode NetErrorToHttpStatusCode(int error_code);
protected: protected:
~BlobURLRequestJob() override; ~BlobURLRequestJob() override;
......
This is a testharness.js-based test. This is a testharness.js-based test.
PASS Blob URLs can be used in XHR PASS Blob URLs can be used in XHR
PASS XHR with a fragment should succeed PASS XHR with a fragment should succeed
FAIL XHR of a revoked URL should fail assert_equals: expected 0 but got 404 PASS XHR of a revoked URL should fail
PASS Only exact matches should revoke URLs, using XHR PASS Only exact matches should revoke URLs, using XHR
FAIL Appending a query string should cause XHR to fail assert_equals: expected 0 but got 404 PASS Appending a query string should cause XHR to fail
FAIL Appending a path should cause XHR to fail assert_equals: expected 0 but got 404 PASS Appending a path should cause XHR to fail
PASS XHR with method "HEAD" should fail PASS XHR with method "HEAD" should fail
PASS XHR with method "POST" should fail PASS XHR with method "POST" should fail
PASS XHR with method "DELETE" should fail PASS XHR with method "DELETE" should fail
...@@ -12,6 +12,6 @@ PASS XHR with method "OPTIONS" should fail ...@@ -12,6 +12,6 @@ PASS XHR with method "OPTIONS" should fail
PASS XHR with method "PUT" should fail PASS XHR with method "PUT" should fail
PASS XHR with method "CUSTOM" should fail PASS XHR with method "CUSTOM" should fail
PASS XHR should return Content-Type from Blob PASS XHR should return Content-Type from Blob
FAIL Revoke blob URL after open(), will fetch assert_equals: expected "test blob contents" but got "" FAIL Revoke blob URL after open(), will fetch assert_unreached: Got unexpected error event Reached unreachable code
Harness: the test ran to completion. Harness: the test ran to completion.
...@@ -6,8 +6,7 @@ Response: Hello ...@@ -6,8 +6,7 @@ Response: Hello
Test that sync XMLHttpRequest POST fails. Test that sync XMLHttpRequest POST fails.
Received exception, code: 19, name: NetworkError, message: Failed to execute 'send' on 'XMLHttpRequest': Failed to load 'blob:file:///UUID': 'GET' is the only method allowed for 'blob:' URLs. Received exception, code: 19, name: NetworkError, message: Failed to execute 'send' on 'XMLHttpRequest': Failed to load 'blob:file:///UUID': 'GET' is the only method allowed for 'blob:' URLs.
Test that sync XMLHttpRequest GET fails after the blob URL is revoked. Test that sync XMLHttpRequest GET fails after the blob URL is revoked.
Status: 404 Received exception, code: 19, name: NetworkError, message: Failed to execute 'send' on 'XMLHttpRequest': Failed to load 'blob:file:///UUID'.
Response:
Test that async XMLHttpRequest GET succeeds. Test that async XMLHttpRequest GET succeeds.
Status: 200 Status: 200
Response: Hello Response: Hello
...@@ -17,7 +16,6 @@ Test the slicing the blob response doesn't crash the browser. ...@@ -17,7 +16,6 @@ Test the slicing the blob response doesn't crash the browser.
Status: 200 Status: 200
First byte of response: H First byte of response: H
Test that async XMLHttpRequest GET fails after the blob URL is revoked. Test that async XMLHttpRequest GET fails after the blob URL is revoked.
Status: 404 Error event is dispatched
Response:
DONE DONE
...@@ -5,8 +5,7 @@ Response: Hello ...@@ -5,8 +5,7 @@ Response: Hello
Test that sync XMLHttpRequest POST fails. Test that sync XMLHttpRequest POST fails.
Received exception, code: 19, name: NetworkError, message: Failed to execute 'send' on 'XMLHttpRequest': Failed to load 'blob:file:///UUID': 'GET' is the only method allowed for 'blob:' URLs. Received exception, code: 19, name: NetworkError, message: Failed to execute 'send' on 'XMLHttpRequest': Failed to load 'blob:file:///UUID': 'GET' is the only method allowed for 'blob:' URLs.
Test that sync XMLHttpRequest GET fails after the blob URL is revoked. Test that sync XMLHttpRequest GET fails after the blob URL is revoked.
Status: 404 Received exception, code: 19, name: NetworkError, message: Failed to execute 'send' on 'XMLHttpRequest': Failed to load 'blob:file:///UUID'.
Response:
Test that async XMLHttpRequest GET succeeds. Test that async XMLHttpRequest GET succeeds.
Status: 200 Status: 200
Response: Hello Response: Hello
...@@ -16,7 +15,6 @@ Test the slicing the blob response doesn't crash the browser. ...@@ -16,7 +15,6 @@ Test the slicing the blob response doesn't crash the browser.
Status: 200 Status: 200
First byte of response: H First byte of response: H
Test that async XMLHttpRequest GET fails after the blob URL is revoked. Test that async XMLHttpRequest GET fails after the blob URL is revoked.
Status: 404 Error event is dispatched
Response:
DONE DONE
CONSOLE WARNING: line 1: Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/. CONSOLE WARNING: line 1: Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/.
CONSOLE ERROR: line 1: Uncaught NetworkError: Failed to execute 'send' on 'XMLHttpRequest': Failed to load 'blob:https://cloud-cuckoo-land.google:2112/456789'.
This tests an isolated script's ability to XHR a blob that is in its security origin, which is not the same as the document's security origin. This tests an isolated script's ability to XHR a blob that is in its security origin, which is not the same as the document's security origin.
We pass if there are no console errors. We pass if there are no 'Not allowed to load' console errors. (This will still show one console error as the blob is not available)
<body> <body>
This tests an isolated script's ability to XHR a blob that is in its security origin, which is not the same as the document's security origin.<br> This tests an isolated script's ability to XHR a blob that is in its security origin, which is not the same as the document's security origin.<br>
We pass if there are no console errors. We pass if there are no 'Not allowed to load' console errors.
(This will still show one console error as the blob is not available)
<script> <script>
if (!window.testRunner) { if (!window.testRunner) {
document.body.appendChild(document.createTextNode("This test requires window.testRunner")); document.body.appendChild(document.createTextNode("This test requires window.testRunner"));
......
...@@ -347,18 +347,6 @@ void FetchManager::Loader::DidReceiveResponse( ...@@ -347,18 +347,6 @@ void FetchManager::Loader::DidReceiveResponse(
ScriptState* script_state = resolver_->GetScriptState(); ScriptState* script_state = resolver_->GetScriptState();
ScriptState::Scope scope(script_state); ScriptState::Scope scope(script_state);
if (response.Url().ProtocolIs("blob") && response.HttpStatusCode() == 404) {
// "If |blob| is null, return a network error."
// https://fetch.spec.whatwg.org/#concept-scheme-fetch
PerformNetworkError("Blob not found.");
return;
}
if (response.Url().ProtocolIs("blob") && response.HttpStatusCode() == 405) {
PerformNetworkError("Only 'GET' method is allowed for blob URLs.");
return;
}
response_http_status_code_ = response.HttpStatusCode(); response_http_status_code_ = response.HttpStatusCode();
FetchRequestData::Tainting tainting = request_->ResponseTainting(); FetchRequestData::Tainting tainting = request_->ResponseTainting();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment