Commit 4cfd3cf3 authored by Matt Menke's avatar Matt Menke Committed by Commit Bot

Add download to temp file support to SimpleURLLoader.

Bug: 746977
Change-Id: Ib0439337359486f1523e85963d8549fbae58ee08
Reviewed-on: https://chromium-review.googlesource.com/705695
Commit-Queue: Matt Menke <mmenke@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarRandy Smith <rdsmith@chromium.org>
Cr-Commit-Position: refs/heads/master@{#509669}
parent 57198199
...@@ -252,17 +252,21 @@ class SaveToFileBodyHandler : public BodyHandler { ...@@ -252,17 +252,21 @@ class SaveToFileBodyHandler : public BodyHandler {
public: public:
// |net_priority| is the priority from the ResourceRequest, and is used to // |net_priority| is the priority from the ResourceRequest, and is used to
// determine the TaskPriority of the sequence used to read from the response // determine the TaskPriority of the sequence used to read from the response
// body and write to the file. // body and write to the file. If |create_temp_file| is true, a temp file is
// created instead of using |path|.
SaveToFileBodyHandler(SimpleURLLoaderImpl* simple_url_loader, SaveToFileBodyHandler(SimpleURLLoaderImpl* simple_url_loader,
SimpleURLLoader::DownloadToFileCompleteCallback SimpleURLLoader::DownloadToFileCompleteCallback
download_to_file_complete_callback, download_to_file_complete_callback,
const base::FilePath& path, const base::FilePath& path,
bool create_temp_file,
uint64_t max_body_size, uint64_t max_body_size,
net::RequestPriority request_priority) net::RequestPriority request_priority)
: BodyHandler(simple_url_loader), : BodyHandler(simple_url_loader),
download_to_file_complete_callback_( download_to_file_complete_callback_(
std::move(download_to_file_complete_callback)), std::move(download_to_file_complete_callback)),
weak_ptr_factory_(this) { weak_ptr_factory_(this) {
DCHECK(create_temp_file || !path.empty());
// Choose the TaskPriority based on the net request priority. // Choose the TaskPriority based on the net request priority.
// TODO(mmenke): Can something better be done here? // TODO(mmenke): Can something better be done here?
base::TaskPriority task_priority; base::TaskPriority task_priority;
...@@ -275,8 +279,8 @@ class SaveToFileBodyHandler : public BodyHandler { ...@@ -275,8 +279,8 @@ class SaveToFileBodyHandler : public BodyHandler {
} }
// Can only do this after initializing the WeakPtrFactory. // Can only do this after initializing the WeakPtrFactory.
file_writer_ = file_writer_ = std::make_unique<FileWriter>(path, create_temp_file,
std::make_unique<FileWriter>(path, max_body_size, task_priority); max_body_size, task_priority);
} }
~SaveToFileBodyHandler() override { ~SaveToFileBodyHandler() override {
...@@ -356,16 +360,19 @@ class SaveToFileBodyHandler : public BodyHandler { ...@@ -356,16 +360,19 @@ class SaveToFileBodyHandler : public BodyHandler {
int64_t total_bytes, int64_t total_bytes,
const base::FilePath& path)>; const base::FilePath& path)>;
explicit FileWriter(const base::FilePath& path, FileWriter(const base::FilePath& path,
int64_t max_body_size, bool create_temp_file,
base::TaskPriority priority) int64_t max_body_size,
base::TaskPriority priority)
: body_handler_task_runner_(base::SequencedTaskRunnerHandle::Get()), : body_handler_task_runner_(base::SequencedTaskRunnerHandle::Get()),
file_writer_task_runner_(base::CreateSequencedTaskRunnerWithTraits( file_writer_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
{base::MayBlock(), priority, {base::MayBlock(), priority,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN})), base::TaskShutdownBehavior::BLOCK_SHUTDOWN})),
path_(path), path_(path),
create_temp_file_(create_temp_file),
max_body_size_(max_body_size) { max_body_size_(max_body_size) {
DCHECK(body_handler_task_runner_->RunsTasksInCurrentSequence()); DCHECK(body_handler_task_runner_->RunsTasksInCurrentSequence());
DCHECK(create_temp_file_ || !path_.empty());
} }
// Starts reading from |body_data_pipe| and writing to the file. // Starts reading from |body_data_pipe| and writing to the file.
...@@ -420,9 +427,22 @@ class SaveToFileBodyHandler : public BodyHandler { ...@@ -420,9 +427,22 @@ class SaveToFileBodyHandler : public BodyHandler {
DCHECK(!file_.IsValid()); DCHECK(!file_.IsValid());
DCHECK(!body_reader_); DCHECK(!body_reader_);
// Try to create the file. bool have_path = !create_temp_file_;
file_.Initialize(path_, if (!have_path) {
base::File::FLAG_WRITE | base::File::FLAG_CREATE_ALWAYS); DCHECK(create_temp_file_);
have_path = base::CreateTemporaryFile(&path_);
// CreateTemporaryFile() creates an empty file.
if (have_path)
owns_file_ = true;
}
if (have_path) {
// Try to initialize |file_|, creating the file if needed.
file_.Initialize(
path_, base::File::FLAG_WRITE | base::File::FLAG_CREATE_ALWAYS);
}
// If CreateTemporaryFile() or File::Initialize() failed, report failure.
if (!file_.IsValid()) { if (!file_.IsValid()) {
body_handler_task_runner_->PostTask( body_handler_task_runner_->PostTask(
FROM_HERE, base::BindOnce(std::move(on_done_callback), FROM_HERE, base::BindOnce(std::move(on_done_callback),
...@@ -502,6 +522,7 @@ class SaveToFileBodyHandler : public BodyHandler { ...@@ -502,6 +522,7 @@ class SaveToFileBodyHandler : public BodyHandler {
// |file_writer_task_runner_|. // |file_writer_task_runner_|.
base::FilePath path_; base::FilePath path_;
const bool create_temp_file_;
const int64_t max_body_size_; const int64_t max_body_size_;
// File being downloaded to. Created just before reading from the data pipe. // File being downloaded to. Created just before reading from the data pipe.
...@@ -567,6 +588,12 @@ class SimpleURLLoaderImpl : public SimpleURLLoader, ...@@ -567,6 +588,12 @@ class SimpleURLLoaderImpl : public SimpleURLLoader,
DownloadToFileCompleteCallback download_to_file_complete_callback, DownloadToFileCompleteCallback download_to_file_complete_callback,
const base::FilePath& file_path, const base::FilePath& file_path,
int64_t max_body_size) override; int64_t max_body_size) override;
void DownloadToTempFile(
const ResourceRequest& resource_request,
mojom::URLLoaderFactory* url_loader_factory,
const net::NetworkTrafficAnnotationTag& annotation_tag,
DownloadToFileCompleteCallback download_to_file_complete_callback,
int64_t max_body_size) override;
void SetAllowPartialResults(bool allow_partial_results) override; void SetAllowPartialResults(bool allow_partial_results) override;
void SetAllowHttpErrorResults(bool allow_http_error_results) override; void SetAllowHttpErrorResults(bool allow_http_error_results) override;
void SetRetryOptions(int max_retries, int retry_mode) override; void SetRetryOptions(int max_retries, int retry_mode) override;
...@@ -767,9 +794,22 @@ void SimpleURLLoaderImpl::DownloadToFile( ...@@ -767,9 +794,22 @@ void SimpleURLLoaderImpl::DownloadToFile(
DownloadToFileCompleteCallback download_to_file_complete_callback, DownloadToFileCompleteCallback download_to_file_complete_callback,
const base::FilePath& file_path, const base::FilePath& file_path,
int64_t max_body_size) { int64_t max_body_size) {
DCHECK(!file_path.empty());
body_handler_ = std::make_unique<SaveToFileBodyHandler>( body_handler_ = std::make_unique<SaveToFileBodyHandler>(
this, std::move(download_to_file_complete_callback), file_path, this, std::move(download_to_file_complete_callback), file_path,
max_body_size, resource_request.priority); false /* create_temp_file */, max_body_size, resource_request.priority);
Start(resource_request, url_loader_factory, annotation_tag);
}
void SimpleURLLoaderImpl::DownloadToTempFile(
const ResourceRequest& resource_request,
mojom::URLLoaderFactory* url_loader_factory,
const net::NetworkTrafficAnnotationTag& annotation_tag,
DownloadToFileCompleteCallback download_to_file_complete_callback,
int64_t max_body_size) {
body_handler_ = std::make_unique<SaveToFileBodyHandler>(
this, std::move(download_to_file_complete_callback), base::FilePath(),
true /* create_temp_file */, max_body_size, resource_request.priority);
Start(resource_request, url_loader_factory, annotation_tag); Start(resource_request, url_loader_factory, annotation_tag);
} }
......
...@@ -41,7 +41,6 @@ class URLLoaderFactory; ...@@ -41,7 +41,6 @@ class URLLoaderFactory;
// Each SimpleURLLoader can only be used for a single request. // Each SimpleURLLoader can only be used for a single request.
// //
// TODO(mmenke): Support the following: // TODO(mmenke): Support the following:
// * Save to temp file.
// * Consumer-provided methods to receive streaming (with backpressure). // * Consumer-provided methods to receive streaming (with backpressure).
// * Monitoring (And cancelling during) redirects. // * Monitoring (And cancelling during) redirects.
// * Uploads (Fixed strings, files, data streams (with backpressure), chunked // * Uploads (Fixed strings, files, data streams (with backpressure), chunked
...@@ -129,6 +128,15 @@ class CONTENT_EXPORT SimpleURLLoader { ...@@ -129,6 +128,15 @@ class CONTENT_EXPORT SimpleURLLoader {
const base::FilePath& file_path, const base::FilePath& file_path,
int64_t max_body_size = std::numeric_limits<int64_t>::max()) = 0; int64_t max_body_size = std::numeric_limits<int64_t>::max()) = 0;
// Same as DownloadToFile, but creates a temporary file instead of taking a
// FilePath.
virtual void DownloadToTempFile(
const ResourceRequest& resource_request,
mojom::URLLoaderFactory* url_loader_factory,
const net::NetworkTrafficAnnotationTag& annotation_tag,
DownloadToFileCompleteCallback download_to_file_complete_callback,
int64_t max_body_size = std::numeric_limits<int64_t>::max()) = 0;
// Sets whether partially received results are allowed. Defaults to false. // Sets whether partially received results are allowed. Defaults to false.
// When true, if an error is received after reading the body starts or the max // When true, if an error is received after reading the body starts or the max
// allowed body size exceeded, the partial response body that was received // allowed body size exceeded, the partial response body that was received
......
...@@ -66,8 +66,9 @@ const char kTruncatedBody[] = "Truncated Body"; ...@@ -66,8 +66,9 @@ const char kTruncatedBody[] = "Truncated Body";
// and check the result. // and check the result.
class SimpleLoaderTestHelper { class SimpleLoaderTestHelper {
public: public:
// What the response should be downloaded to. // What the response should be downloaded to. Running all tests for all types
enum class DownloadType { TO_STRING, TO_FILE }; // is more than strictly needed, but simplest just to cover all cases.
enum class DownloadType { TO_STRING, TO_FILE, TO_TEMP_FILE };
explicit SimpleLoaderTestHelper(DownloadType download_type) explicit SimpleLoaderTestHelper(DownloadType download_type)
: download_type_(download_type), : download_type_(download_type),
...@@ -130,6 +131,22 @@ class SimpleLoaderTestHelper { ...@@ -130,6 +131,22 @@ class SimpleLoaderTestHelper {
dest_path_, max_body_size); dest_path_, max_body_size);
} }
break; break;
case DownloadType::TO_TEMP_FILE:
if (max_body_size < 0) {
simple_url_loader_->DownloadToTempFile(
resource_request, url_loader_factory,
TRAFFIC_ANNOTATION_FOR_TESTS,
base::BindOnce(&SimpleLoaderTestHelper::DownloadedToFile,
base::Unretained(this)));
} else {
simple_url_loader_->DownloadToTempFile(
resource_request, url_loader_factory,
TRAFFIC_ANNOTATION_FOR_TESTS,
base::BindOnce(&SimpleLoaderTestHelper::DownloadedToFile,
base::Unretained(this)),
max_body_size);
}
break;
} }
} }
...@@ -224,20 +241,34 @@ class SimpleLoaderTestHelper { ...@@ -224,20 +241,34 @@ class SimpleLoaderTestHelper {
void DownloadedToFile(const base::FilePath& file_path) { void DownloadedToFile(const base::FilePath& file_path) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
EXPECT_FALSE(done_); EXPECT_FALSE(done_);
EXPECT_EQ(DownloadType::TO_FILE, download_type_); EXPECT_TRUE(download_type_ == DownloadType::TO_FILE ||
download_type_ == DownloadType::TO_TEMP_FILE);
EXPECT_FALSE(response_body_); EXPECT_FALSE(response_body_);
// Make sure the destination file exists if |file_path| is non-empty, or the
// file is expected to exist on error.
EXPECT_EQ(!file_path.empty() || expect_path_exists_on_error_,
base::PathExists(dest_path_));
if (!file_path.empty()) { if (!file_path.empty()) {
EXPECT_EQ(dest_path_, file_path); EXPECT_TRUE(base::PathExists(file_path));
response_body_ = std::make_unique<std::string>(); response_body_ = std::make_unique<std::string>();
EXPECT_TRUE(base::ReadFileToString(dest_path_, response_body_.get())); EXPECT_TRUE(base::ReadFileToString(file_path, response_body_.get()));
} }
// Can do some additional checks in the TO_FILE case. Unfortunately, in the
// temp file case, can't check that temp files were cleaned up, since it's
// a shared directory.
if (download_type_ == DownloadType::TO_FILE) {
// Make sure the destination file exists if |file_path| is non-empty, or
// the file is expected to exist on error.
EXPECT_EQ(!file_path.empty() || expect_path_exists_on_error_,
base::PathExists(dest_path_));
if (!file_path.empty())
EXPECT_EQ(dest_path_, file_path);
}
// Clean up file, so tests don't leave around files in the temp directory.
// Only matters in the TO_TEMP_FILE case.
if (!file_path.empty())
base::DeleteFile(file_path, false);
done_ = true; done_ = true;
run_loop_.Quit(); run_loop_.Quit();
} }
...@@ -1457,7 +1488,8 @@ INSTANTIATE_TEST_CASE_P( ...@@ -1457,7 +1488,8 @@ INSTANTIATE_TEST_CASE_P(
/* No prefix */, /* No prefix */,
SimpleURLLoaderTest, SimpleURLLoaderTest,
testing::Values(SimpleLoaderTestHelper::DownloadType::TO_STRING, testing::Values(SimpleLoaderTestHelper::DownloadType::TO_STRING,
SimpleLoaderTestHelper::DownloadType::TO_FILE)); SimpleLoaderTestHelper::DownloadType::TO_FILE,
SimpleLoaderTestHelper::DownloadType::TO_TEMP_FILE));
class SimpleURLLoaderFileTest : public SimpleURLLoaderTestBase, class SimpleURLLoaderFileTest : public SimpleURLLoaderTestBase,
public testing::Test { public testing::Test {
......
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