Commit 82271d29 authored by Matt Menke's avatar Matt Menke Committed by Commit Bot

SimpleURLLoader: Add download to file support.

The file is written to on another thread, and the data pipe is also
read from off the consumer's thread, so this should be save to use on
named threads without causing significant jank.

Bug: 746977
Change-Id: I8e38945a569e4cd97ff4381ecf43acc73c731cea
Reviewed-on: https://chromium-review.googlesource.com/673043Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Reviewed-by: default avatarRandy Smith <rdsmith@chromium.org>
Commit-Queue: Matt Menke <mmenke@chromium.org>
Cr-Commit-Position: refs/heads/master@{#504880}
parent b42f9674
...@@ -10,12 +10,18 @@ ...@@ -10,12 +10,18 @@
#include <limits> #include <limits>
#include "base/bind.h" #include "base/bind.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/location.h" #include "base/location.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/sequence_checker.h" #include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h"
#include "base/task_scheduler/post_task.h"
#include "base/task_scheduler/task_traits.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "content/public/common/resource_request.h" #include "content/public/common/resource_request.h"
#include "content/public/common/resource_response.h" #include "content/public/common/resource_response.h"
#include "content/public/common/url_loader.mojom.h" #include "content/public/common/url_loader.mojom.h"
...@@ -24,6 +30,7 @@ ...@@ -24,6 +30,7 @@
#include "mojo/public/cpp/system/data_pipe.h" #include "mojo/public/cpp/system/data_pipe.h"
#include "mojo/public/cpp/system/simple_watcher.h" #include "mojo/public/cpp/system/simple_watcher.h"
#include "net/base/net_errors.h" #include "net/base/net_errors.h"
#include "net/base/request_priority.h"
#include "net/traffic_annotation/network_traffic_annotation.h" #include "net/traffic_annotation/network_traffic_annotation.h"
namespace content { namespace content {
...@@ -64,9 +71,7 @@ class BodyReader { ...@@ -64,9 +71,7 @@ class BodyReader {
}; };
BodyReader(Delegate* delegate, int64_t max_body_size) BodyReader(Delegate* delegate, int64_t max_body_size)
: handle_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL), : delegate_(delegate), max_body_size_(max_body_size) {
delegate_(delegate),
max_body_size_(max_body_size) {
DCHECK_GE(max_body_size_, 0); DCHECK_GE(max_body_size_, 0);
} }
...@@ -80,7 +85,9 @@ class BodyReader { ...@@ -80,7 +85,9 @@ class BodyReader {
void Start(mojo::ScopedDataPipeConsumerHandle body_data_pipe) { void Start(mojo::ScopedDataPipeConsumerHandle body_data_pipe) {
DCHECK(!body_data_pipe_.is_valid()); DCHECK(!body_data_pipe_.is_valid());
body_data_pipe_ = std::move(body_data_pipe); body_data_pipe_ = std::move(body_data_pipe);
handle_watcher_.Watch( handle_watcher_ = std::make_unique<mojo::SimpleWatcher>(
FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL);
handle_watcher_->Watch(
body_data_pipe_.get(), body_data_pipe_.get(),
MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
MOJO_WATCH_CONDITION_SATISFIED, MOJO_WATCH_CONDITION_SATISFIED,
...@@ -104,7 +111,7 @@ class BodyReader { ...@@ -104,7 +111,7 @@ class BodyReader {
MojoResult result = body_data_pipe_->BeginReadData( MojoResult result = body_data_pipe_->BeginReadData(
&body_data, &read_size, MOJO_READ_DATA_FLAG_NONE); &body_data, &read_size, MOJO_READ_DATA_FLAG_NONE);
if (result == MOJO_RESULT_SHOULD_WAIT) { if (result == MOJO_RESULT_SHOULD_WAIT) {
handle_watcher_.ArmOrNotify(); handle_watcher_->ArmOrNotify();
return; return;
} }
...@@ -144,12 +151,12 @@ class BodyReader { ...@@ -144,12 +151,12 @@ class BodyReader {
// Frees Mojo resources and prevents any more Mojo messages from arriving. // Frees Mojo resources and prevents any more Mojo messages from arriving.
void ClosePipe() { void ClosePipe() {
handle_watcher_.Cancel(); handle_watcher_.reset();
body_data_pipe_.reset(); body_data_pipe_.reset();
} }
mojo::ScopedDataPipeConsumerHandle body_data_pipe_; mojo::ScopedDataPipeConsumerHandle body_data_pipe_;
mojo::SimpleWatcher handle_watcher_; std::unique_ptr<mojo::SimpleWatcher> handle_watcher_;
Delegate* const delegate_; Delegate* const delegate_;
...@@ -228,6 +235,285 @@ class SaveToStringBodyHandler : public BodyHandler, ...@@ -228,6 +235,285 @@ class SaveToStringBodyHandler : public BodyHandler,
DISALLOW_COPY_AND_ASSIGN(SaveToStringBodyHandler); DISALLOW_COPY_AND_ASSIGN(SaveToStringBodyHandler);
}; };
// BodyHandler implementation for saving the response to a file
class SaveToFileBodyHandler : public BodyHandler {
public:
// |net_priority| is the priority from the ResourceRequest, and is used to
// determine the TaskPriority of the sequence used to read from the response
// body and write to the file.
SaveToFileBodyHandler(SimpleURLLoaderImpl* simple_url_loader,
SimpleURLLoader::DownloadToFileCompleteCallback
download_to_file_complete_callback,
const base::FilePath& path,
uint64_t max_body_size,
net::RequestPriority request_priority)
: BodyHandler(simple_url_loader),
download_to_file_complete_callback_(
std::move(download_to_file_complete_callback)),
weak_ptr_factory_(this) {
// Choose the TaskPriority based on the net request priority.
// TODO(mmenke): Can something better be done here?
base::TaskPriority task_priority;
if (request_priority >= net::MEDIUM) {
task_priority = base::TaskPriority::USER_BLOCKING;
} else if (request_priority >= net::LOW) {
task_priority = base::TaskPriority::USER_VISIBLE;
} else {
task_priority = base::TaskPriority::BACKGROUND;
}
// Can only do this after initializing the WeakPtrFactory.
file_writer_ =
std::make_unique<FileWriter>(path, max_body_size, task_priority);
}
~SaveToFileBodyHandler() override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (file_writer_)
FileWriter::Destroy(std::move(file_writer_), base::OnceClosure());
}
// BodyHandler implementation:
void OnStartLoadingResponseBody(
mojo::ScopedDataPipeConsumerHandle body_data_pipe) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
file_writer_->StartWriting(std::move(body_data_pipe),
base::BindOnce(&SaveToFileBodyHandler::OnDone,
weak_ptr_factory_.GetWeakPtr()));
}
void NotifyConsumerOfCompletion(bool destroy_results) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!download_to_file_complete_callback_.is_null());
if (destroy_results) {
// Prevent the FileWriter from calling OnDone().
weak_ptr_factory_.InvalidateWeakPtrs();
// To avoid any issues if the consumer tries to re-download a file to the
// same location, don't invoke the callback until any partially downloaded
// file has been destroyed.
FileWriter::Destroy(
std::move(file_writer_),
base::Bind(&SaveToFileBodyHandler::InvokeCallbackAsynchronously,
weak_ptr_factory_.GetWeakPtr()));
return;
}
file_writer_->ReleaseFile();
std::move(download_to_file_complete_callback_).Run(path_);
}
private:
// Class to read from a mojo::ScopedDataPipeConsumerHandle and write the
// contents to a file. Does all reading and writing on a separate file
// SequencedTaskRunner. All public methods except the destructor are called on
// BodyHandler's TaskRunner. All private methods and the destructor are run
// on the file TaskRunner.
//
// Destroys the file that it's saving to on destruction, unless ReleaseFile()
// is called first, so on cancellation and errors, the default behavior is to
// clean up after itself.
//
// FileWriter is owned by the SaveToFileBodyHandler and destroyed by a task
// moving its unique_ptr to the |file_writer_task_runner_|. As a result, tasks
// posted to |file_writer_task_runner_| can always use base::Unretained. Tasks
// posted the other way, however, require the SaveToFileBodyHandler to use
// WeakPtrs, since the SaveToFileBodyHandler can be destroyed at any time.
class FileWriter : public BodyReader::Delegate {
public:
using OnDoneCallback = base::OnceCallback<void(net::Error error,
int64_t total_bytes,
const base::FilePath& path)>;
explicit FileWriter(const base::FilePath& path,
int64_t max_body_size,
base::TaskPriority priority)
: body_handler_task_runner_(base::SequencedTaskRunnerHandle::Get()),
file_writer_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
{base::MayBlock(), priority,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN})),
path_(path),
body_reader_(std::make_unique<BodyReader>(this, max_body_size)) {
DCHECK(body_handler_task_runner_->RunsTasksInCurrentSequence());
}
// Starts reading from |body_data_pipe| and writing to the file.
void StartWriting(mojo::ScopedDataPipeConsumerHandle body_data_pipe,
OnDoneCallback on_done_callback) {
DCHECK(body_handler_task_runner_->RunsTasksInCurrentSequence());
file_writer_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&FileWriter::StartWritingOnFileSequence,
base::Unretained(this), std::move(body_data_pipe),
std::move(on_done_callback)));
}
// Releases ownership of the downloaded file. If not called before
// destruction, file will automatically be destroyed in the destructor.
void ReleaseFile() {
DCHECK(body_handler_task_runner_->RunsTasksInCurrentSequence());
file_writer_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&FileWriter::ReleaseFileOnFileSequence,
base::Unretained(this)));
}
// Destroys the FileWriter on the file TaskRunner.
//
// If |on_destroyed_closure| is non-null, it will be invoked on the caller's
// task runner once the FileWriter has been destroyed.
static void Destroy(std::unique_ptr<FileWriter> file_writer,
base::OnceClosure on_destroyed_closure) {
DCHECK(
file_writer->body_handler_task_runner_->RunsTasksInCurrentSequence());
// Have to stash this pointer before posting a task, since |file_writer|
// is bound to the callback that's posted to the TaskRunner.
base::SequencedTaskRunner* task_runner =
file_writer->file_writer_task_runner_.get();
task_runner->PostTask(FROM_HERE,
base::BindOnce(&FileWriter::DestroyOnFileSequence,
std::move(file_writer),
std::move(on_destroyed_closure)));
}
// Destructor is only public so the consumer can keep it in a unique_ptr.
// Class must be destroyed by using Destroy().
~FileWriter() override {
DCHECK(file_writer_task_runner_->RunsTasksInCurrentSequence());
file_.Close();
if (owns_file_) {
DCHECK(!path_.empty());
base::DeleteFile(path_, false /* recursive */);
}
}
private:
void StartWritingOnFileSequence(
mojo::ScopedDataPipeConsumerHandle body_data_pipe,
OnDoneCallback on_done_callback) {
DCHECK(file_writer_task_runner_->RunsTasksInCurrentSequence());
DCHECK(!file_.IsValid());
// Try to create the file.
file_.Initialize(path_,
base::File::FLAG_WRITE | base::File::FLAG_CREATE_ALWAYS);
if (!file_.IsValid()) {
body_handler_task_runner_->PostTask(
FROM_HERE, base::BindOnce(std::move(on_done_callback),
net::MapSystemError(
logging::GetLastSystemErrorCode()),
0, base::FilePath()));
return;
}
on_done_callback_ = std::move(on_done_callback);
owns_file_ = true;
body_reader_->Start(std::move(body_data_pipe));
}
// BodyReader::Delegate implementation:
net::Error OnDataRead(uint32_t length, const char* data) override {
DCHECK(file_writer_task_runner_->RunsTasksInCurrentSequence());
while (length > 0) {
int written = file_.WriteAtCurrentPos(
data, std::min(length, static_cast<uint32_t>(
std::numeric_limits<int>::max())));
if (written < 0)
return net::MapSystemError(logging::GetLastSystemErrorCode());
length -= written;
data += written;
}
return net::OK;
}
void OnDone(net::Error error, int64_t total_bytes) override {
DCHECK(file_writer_task_runner_->RunsTasksInCurrentSequence());
// This should only be called if the file was successfully created.
DCHECK(file_.IsValid());
// Close the file so that there's no ownership contention when the
// consumer uses it.
file_.Close();
body_handler_task_runner_->PostTask(
FROM_HERE, base::BindOnce(std::move(on_done_callback_), error,
total_bytes, path_));
}
// Closes the file, so it won't be deleted on destruction.
void ReleaseFileOnFileSequence() {
DCHECK(file_writer_task_runner_->RunsTasksInCurrentSequence());
owns_file_ = false;
}
static void DestroyOnFileSequence(std::unique_ptr<FileWriter> file_writer,
base::OnceClosure on_destroyed_closure) {
DCHECK(
file_writer->file_writer_task_runner_->RunsTasksInCurrentSequence());
// Need to grab this before deleting |file_writer|.
scoped_refptr<base::SequencedTaskRunner> body_handler_task_runner =
file_writer->body_handler_task_runner_;
// Need to delete |FileWriter| before posting a task to invoke the
// callback.
file_writer.reset();
if (on_destroyed_closure)
body_handler_task_runner->PostTask(FROM_HERE,
std::move(on_destroyed_closure));
}
// These are set on cosntruction and accessed on both task runners.
const scoped_refptr<base::SequencedTaskRunner> body_handler_task_runner_;
const scoped_refptr<base::SequencedTaskRunner> file_writer_task_runner_;
// After construction, all other values are only read and written on the
// |file_writer_task_runner_|.
base::FilePath path_;
// File being downloaded to. Created just before reading from the data pipe.
base::File file_;
OnDoneCallback on_done_callback_;
std::unique_ptr<BodyReader> body_reader_;
// If true, destroys the file in the destructor. Set to true once the file
// is created.
bool owns_file_ = false;
DISALLOW_COPY_AND_ASSIGN(FileWriter);
};
// Called by FileWriter::Destroy after deleting a partially downloaded file.
void InvokeCallbackAsynchronously() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::move(download_to_file_complete_callback_).Run(base::FilePath());
}
void OnDone(net::Error error,
int64_t total_bytes,
const base::FilePath& path);
// Path of the file. Set in OnDone().
base::FilePath path_;
SimpleURLLoader::DownloadToFileCompleteCallback
download_to_file_complete_callback_;
std::unique_ptr<FileWriter> file_writer_;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<SaveToFileBodyHandler> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(SaveToFileBodyHandler);
};
class SimpleURLLoaderImpl : public SimpleURLLoader, class SimpleURLLoaderImpl : public SimpleURLLoader,
public mojom::URLLoaderClient { public mojom::URLLoaderClient {
public: public:
...@@ -245,6 +531,13 @@ class SimpleURLLoaderImpl : public SimpleURLLoader, ...@@ -245,6 +531,13 @@ class SimpleURLLoaderImpl : public SimpleURLLoader,
mojom::URLLoaderFactory* url_loader_factory, mojom::URLLoaderFactory* url_loader_factory,
const net::NetworkTrafficAnnotationTag& annotation_tag, const net::NetworkTrafficAnnotationTag& annotation_tag,
BodyAsStringCallback body_as_string_callback) override; BodyAsStringCallback body_as_string_callback) override;
void DownloadToFile(
const ResourceRequest& resource_request,
mojom::URLLoaderFactory* url_loader_factory,
const net::NetworkTrafficAnnotationTag& annotation_tag,
DownloadToFileCompleteCallback download_to_file_complete_callback,
const base::FilePath& file_path,
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;
int NetError() const override; int NetError() const override;
...@@ -352,6 +645,13 @@ void SaveToStringBodyHandler::NotifyConsumerOfCompletion(bool destroy_results) { ...@@ -352,6 +645,13 @@ void SaveToStringBodyHandler::NotifyConsumerOfCompletion(bool destroy_results) {
std::move(body_as_string_callback_).Run(std::move(body_)); std::move(body_as_string_callback_).Run(std::move(body_));
} }
void SaveToFileBodyHandler::OnDone(net::Error error,
int64_t total_bytes,
const base::FilePath& path) {
path_ = path;
simple_url_loader()->OnBodyHandlerDone(error, total_bytes);
}
SimpleURLLoaderImpl::SimpleURLLoaderImpl() : client_binding_(this) { SimpleURLLoaderImpl::SimpleURLLoaderImpl() : client_binding_(this) {
// Allow creation and use on different threads. // Allow creation and use on different threads.
DETACH_FROM_SEQUENCE(sequence_checker_); DETACH_FROM_SEQUENCE(sequence_checker_);
...@@ -384,6 +684,19 @@ void SimpleURLLoaderImpl::DownloadToStringOfUnboundedSizeUntilCrashAndDie( ...@@ -384,6 +684,19 @@ void SimpleURLLoaderImpl::DownloadToStringOfUnboundedSizeUntilCrashAndDie(
StartInternal(resource_request, url_loader_factory, annotation_tag); StartInternal(resource_request, url_loader_factory, annotation_tag);
} }
void SimpleURLLoaderImpl::DownloadToFile(
const ResourceRequest& resource_request,
mojom::URLLoaderFactory* url_loader_factory,
const net::NetworkTrafficAnnotationTag& annotation_tag,
DownloadToFileCompleteCallback download_to_file_complete_callback,
const base::FilePath& file_path,
int64_t max_body_size) {
body_handler_ = std::make_unique<SaveToFileBodyHandler>(
this, std::move(download_to_file_complete_callback), file_path,
max_body_size, resource_request.priority);
StartInternal(resource_request, url_loader_factory, annotation_tag);
}
void SimpleURLLoaderImpl::SetAllowPartialResults(bool allow_partial_results) { void SimpleURLLoaderImpl::SetAllowPartialResults(bool allow_partial_results) {
// Simplest way to check if a request has not yet been started. // Simplest way to check if a request has not yet been started.
DCHECK(!body_handler_); DCHECK(!body_handler_);
......
...@@ -7,12 +7,17 @@ ...@@ -7,12 +7,17 @@
#include <stdint.h> #include <stdint.h>
#include <limits>
#include <memory> #include <memory>
#include <string> #include <string>
#include "base/callback_forward.h" #include "base/callback_forward.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
namespace base {
class FilePath;
}
namespace net { namespace net {
struct NetworkTrafficAnnotationTag; struct NetworkTrafficAnnotationTag;
} // namespace net } // namespace net
...@@ -53,6 +58,11 @@ class CONTENT_EXPORT SimpleURLLoader { ...@@ -53,6 +58,11 @@ class CONTENT_EXPORT SimpleURLLoader {
using BodyAsStringCallback = using BodyAsStringCallback =
base::OnceCallback<void(std::unique_ptr<std::string> response_body)>; base::OnceCallback<void(std::unique_ptr<std::string> response_body)>;
// Callback used when download the response body to a file. On failure, |path|
// will be empty.
using DownloadToFileCompleteCallback =
base::OnceCallback<void(const base::FilePath& path)>;
static std::unique_ptr<SimpleURLLoader> Create(); static std::unique_ptr<SimpleURLLoader> Create();
virtual ~SimpleURLLoader(); virtual ~SimpleURLLoader();
...@@ -65,7 +75,9 @@ class CONTENT_EXPORT SimpleURLLoader { ...@@ -65,7 +75,9 @@ class CONTENT_EXPORT SimpleURLLoader {
// or consume the data as it is received. // or consume the data as it is received.
// //
// Whether the request succeeds or fails, or the body exceeds |max_body_size|, // Whether the request succeeds or fails, or the body exceeds |max_body_size|,
// |body_as_string_callback| will be invoked on completion. // |body_as_string_callback| will be invoked on completion. Deleting the
// SimpleURLLoader before the callback is invoked will return in cancelling
// the request, and the callback will not be called.
virtual void DownloadToString( virtual void DownloadToString(
const ResourceRequest& resource_request, const ResourceRequest& resource_request,
mojom::URLLoaderFactory* url_loader_factory, mojom::URLLoaderFactory* url_loader_factory,
...@@ -75,13 +87,36 @@ class CONTENT_EXPORT SimpleURLLoader { ...@@ -75,13 +87,36 @@ class CONTENT_EXPORT SimpleURLLoader {
// Same as DownloadToString, but downloads to a buffer of unbounded size, // Same as DownloadToString, but downloads to a buffer of unbounded size,
// potentially causing a crash if the amount of addressable memory is // potentially causing a crash if the amount of addressable memory is
// exceeded. It's recommended consumers use DownloadToString instead. // exceeded. It's recommended consumers use one of the other download methods
// instead (DownloadToString if the body is expected to be of reasonable
// length, or DownloadToFile otherwise).
virtual void DownloadToStringOfUnboundedSizeUntilCrashAndDie( virtual void DownloadToStringOfUnboundedSizeUntilCrashAndDie(
const ResourceRequest& resource_request, const ResourceRequest& resource_request,
mojom::URLLoaderFactory* url_loader_factory, mojom::URLLoaderFactory* url_loader_factory,
const net::NetworkTrafficAnnotationTag& annotation_tag, const net::NetworkTrafficAnnotationTag& annotation_tag,
BodyAsStringCallback body_as_string_callback) = 0; BodyAsStringCallback body_as_string_callback) = 0;
// SimpleURLLoader will download the entire response to a file at the
// specified path. File I/O will happen on another sequence, so it's safe to
// use this on any sequence.
//
// If there's a file, network, or http error, or the max limit
// is exceeded, the file will be automatically destroyed before the callback
// is invoked and en empty path passed to the callback, unless
// SetAllowPartialResults() and/or SetAllowHttpErrorResults() were used to
// indicate partial results are allowed.
//
// If the SimpleURLLoader is destroyed before it has invoked the callback, the
// downloaded file will be deleted asynchronously and the callback will not be
// invoked, regardless of other settings.
virtual void DownloadToFile(
const ResourceRequest& resource_request,
mojom::URLLoaderFactory* url_loader_factory,
const net::NetworkTrafficAnnotationTag& annotation_tag,
DownloadToFileCompleteCallback download_to_file_complete_callback,
const base::FilePath& file_path,
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
......
...@@ -9,6 +9,9 @@ ...@@ -9,6 +9,9 @@
#include <string> #include <string>
#include "base/bind.h" #include "base/bind.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/format_macros.h" #include "base/format_macros.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/macros.h" #include "base/macros.h"
...@@ -16,6 +19,7 @@ ...@@ -16,6 +19,7 @@
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/optional.h" #include "base/optional.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/sequence_checker.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/test/scoped_task_environment.h" #include "base/test/scoped_task_environment.h"
...@@ -57,21 +61,83 @@ const char kTruncatedBodyPath[] = "/truncated-body"; ...@@ -57,21 +61,83 @@ const char kTruncatedBodyPath[] = "/truncated-body";
// The body of the truncated response (After truncation). // The body of the truncated response (After truncation).
const char kTruncatedBody[] = "Truncated Body"; const char kTruncatedBody[] = "Truncated Body";
// Class to make it easier to start a SimpleURLLoader and wait for it to // Class to make it easier to start a SimpleURLLoader, wait for it to complete,
// complete. // and check the result.
class WaitForStringHelper { class SimpleLoaderTestHelper {
public: public:
WaitForStringHelper() : simple_url_loader_(SimpleURLLoader::Create()) {} // What the response should be downloaded to.
enum class DownloadType { TO_STRING, TO_FILE };
explicit SimpleLoaderTestHelper(DownloadType download_type)
: download_type_(download_type),
simple_url_loader_(SimpleURLLoader::Create()) {
// Create a desistination directory, if downloading to a file.
if (download_type_ == DownloadType::TO_FILE) {
EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
dest_path_ = temp_dir_.GetPath().AppendASCII("foo");
}
}
~SimpleLoaderTestHelper() {}
~WaitForStringHelper() {} // File path that will be written to.
const base::FilePath& dest_path() const {
DCHECK_EQ(DownloadType::TO_FILE, download_type_);
return dest_path_;
}
// Starts a SimpleURLLoader using the method corresponding to the
// DownloadType, but does not wait for it to complete. The default
// |max_body_size| of -1 means don't use a max body size (Use
// DownloadToStringOfUnboundedSizeUntilCrashAndDie for string downloads, and
// don't specify a size for other types of downloads).
void StartRequest(mojom::URLLoaderFactory* url_loader_factory,
const ResourceRequest& resource_request,
int64_t max_body_size = -1) {
EXPECT_FALSE(done_);
switch (download_type_) {
case DownloadType::TO_STRING:
if (max_body_size < 0) {
simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
resource_request, url_loader_factory,
TRAFFIC_ANNOTATION_FOR_TESTS,
base::BindOnce(&SimpleLoaderTestHelper::DownloadedToString,
base::Unretained(this)));
} else {
simple_url_loader_->DownloadToString(
resource_request, url_loader_factory,
TRAFFIC_ANNOTATION_FOR_TESTS,
base::BindOnce(&SimpleLoaderTestHelper::DownloadedToString,
base::Unretained(this)),
max_body_size);
}
break;
case DownloadType::TO_FILE:
if (max_body_size < 0) {
simple_url_loader_->DownloadToFile(
resource_request, url_loader_factory,
TRAFFIC_ANNOTATION_FOR_TESTS,
base::BindOnce(&SimpleLoaderTestHelper::DownloadedToFile,
base::Unretained(this)),
dest_path_);
} else {
simple_url_loader_->DownloadToFile(
resource_request, url_loader_factory,
TRAFFIC_ANNOTATION_FOR_TESTS,
base::BindOnce(&SimpleLoaderTestHelper::DownloadedToFile,
base::Unretained(this)),
dest_path_, max_body_size);
}
break;
}
}
// Runs a SimpleURLLoader using the provided ResourceRequest and waits for // Runs a SimpleURLLoader using the provided ResourceRequest and waits for
// completion. // completion.
void RunRequest(mojom::URLLoaderFactory* url_loader_factory, void RunRequest(mojom::URLLoaderFactory* url_loader_factory,
const ResourceRequest& resource_request) { const ResourceRequest& resource_request,
simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie( int64_t max_body_size = -1) {
resource_request, url_loader_factory, TRAFFIC_ANNOTATION_FOR_TESTS, StartRequest(url_loader_factory, resource_request, max_body_size);
GetCallback());
Wait(); Wait();
} }
...@@ -87,11 +153,8 @@ class WaitForStringHelper { ...@@ -87,11 +153,8 @@ class WaitForStringHelper {
// provided max size and waits for completion. // provided max size and waits for completion.
void RunRequestWithBoundedSize(mojom::URLLoaderFactory* url_loader_factory, void RunRequestWithBoundedSize(mojom::URLLoaderFactory* url_loader_factory,
const ResourceRequest& resource_request, const ResourceRequest& resource_request,
size_t max_size) { int64_t max_body_size) {
simple_url_loader_->DownloadToString(resource_request, url_loader_factory, RunRequest(url_loader_factory, resource_request, max_body_size);
TRAFFIC_ANNOTATION_FOR_TESTS,
GetCallback(), max_size);
Wait();
} }
// Simpler version of RunRequestWithBoundedSize that takes a URL instead. // Simpler version of RunRequestWithBoundedSize that takes a URL instead.
...@@ -104,24 +167,35 @@ class WaitForStringHelper { ...@@ -104,24 +167,35 @@ class WaitForStringHelper {
RunRequestWithBoundedSize(url_loader_factory, resource_request, max_size); RunRequestWithBoundedSize(url_loader_factory, resource_request, max_size);
} }
// Callback to be invoked once the SimpleURLLoader has received the response
// body. Exposed so that some tests can start the SimpleURLLoader directly.
SimpleURLLoader::BodyAsStringCallback GetCallback() {
return base::BindOnce(&WaitForStringHelper::BodyReceived,
base::Unretained(this));
}
// Waits until the request is completed. Automatically called by RunRequest // Waits until the request is completed. Automatically called by RunRequest
// methods, but exposed so some tests can start the SimpleURLLoader directly. // methods, but exposed so some tests can start the SimpleURLLoader directly.
void Wait() { run_loop_.Run(); } void Wait() { run_loop_.Run(); }
// Received response body, if any. // Sets whether a file should still exists on download-to-file errors.
const std::string* response_body() const { return response_body_.get(); } // Defaults to false.
void set_expect_path_exists_on_error(bool expect_path_exists_on_error) {
EXPECT_EQ(DownloadType::TO_FILE, download_type_);
expect_path_exists_on_error_ = expect_path_exists_on_error;
}
// Received response body, if any. Returns nullptr if no body was received
// (Which is different from a 0-length body). For DownloadType::TO_STRING,
// this is just the value passed to the callback. For DownloadType::TO_FILE,
// it is nullptr if an empty FilePath was passed to the callback, or the
// contents of the file, otherwise.
const std::string* response_body() const {
EXPECT_TRUE(done_);
return response_body_.get();
}
// Returns true if the callback has been invoked.
bool done() const { return done_; }
SimpleURLLoader* simple_url_loader() { return simple_url_loader_.get(); } SimpleURLLoader* simple_url_loader() { return simple_url_loader_.get(); }
// Returns the HTTP response code. Fails if there isn't one. // Returns the HTTP response code. Fails if there isn't one.
int GetResponseCode() const { int GetResponseCode() const {
EXPECT_TRUE(done_);
if (!simple_url_loader_->ResponseInfo()) { if (!simple_url_loader_->ResponseInfo()) {
ADD_FAILURE() << "No response info."; ADD_FAILURE() << "No response info.";
return -1; return -1;
...@@ -134,17 +208,55 @@ class WaitForStringHelper { ...@@ -134,17 +208,55 @@ class WaitForStringHelper {
} }
private: private:
void BodyReceived(std::unique_ptr<std::string> response_body) { void DownloadedToString(std::unique_ptr<std::string> response_body) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
EXPECT_FALSE(done_);
EXPECT_EQ(DownloadType::TO_STRING, download_type_);
EXPECT_FALSE(response_body_);
response_body_ = std::move(response_body); response_body_ = std::move(response_body);
done_ = true;
run_loop_.Quit(); run_loop_.Quit();
} }
void DownloadedToFile(const base::FilePath& file_path) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
EXPECT_FALSE(done_);
EXPECT_EQ(DownloadType::TO_FILE, download_type_);
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()) {
EXPECT_EQ(dest_path_, file_path);
response_body_ = std::make_unique<std::string>();
EXPECT_TRUE(base::ReadFileToString(dest_path_, response_body_.get()));
}
done_ = true;
run_loop_.Quit();
}
DownloadType download_type_;
bool done_ = false;
bool expect_path_exists_on_error_ = false;
std::unique_ptr<SimpleURLLoader> simple_url_loader_; std::unique_ptr<SimpleURLLoader> simple_url_loader_;
base::RunLoop run_loop_; base::RunLoop run_loop_;
std::unique_ptr<std::string> response_body_; std::unique_ptr<std::string> response_body_;
DISALLOW_COPY_AND_ASSIGN(WaitForStringHelper); base::ScopedTempDir temp_dir_;
base::FilePath dest_path_;
SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(SimpleLoaderTestHelper);
}; };
// Request handler for the embedded test server that returns a response body // Request handler for the embedded test server that returns a response body
...@@ -199,9 +311,10 @@ std::unique_ptr<net::test_server::HttpResponse> HandleTruncatedBody( ...@@ -199,9 +311,10 @@ std::unique_ptr<net::test_server::HttpResponse> HandleTruncatedBody(
return std::move(response); return std::move(response);
} }
class SimpleURLLoaderTest : public testing::Test { // Base class with shared setup logic.
class SimpleURLLoaderTestBase {
public: public:
SimpleURLLoaderTest() SimpleURLLoaderTestBase()
: scoped_task_environment_( : scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::IO), base::test::ScopedTaskEnvironment::MainThreadType::IO),
network_service_(NetworkService::Create()) { network_service_(NetworkService::Create()) {
...@@ -222,7 +335,7 @@ class SimpleURLLoaderTest : public testing::Test { ...@@ -222,7 +335,7 @@ class SimpleURLLoaderTest : public testing::Test {
EXPECT_TRUE(test_server_.Start()); EXPECT_TRUE(test_server_.Start());
} }
~SimpleURLLoaderTest() override {} virtual ~SimpleURLLoaderTestBase() {}
protected: protected:
base::test::ScopedTaskEnvironment scoped_task_environment_; base::test::ScopedTaskEnvironment scoped_task_environment_;
...@@ -234,341 +347,329 @@ class SimpleURLLoaderTest : public testing::Test { ...@@ -234,341 +347,329 @@ class SimpleURLLoaderTest : public testing::Test {
net::test_server::EmbeddedTestServer test_server_; net::test_server::EmbeddedTestServer test_server_;
}; };
TEST_F(SimpleURLLoaderTest, BasicRequest) { class SimpleURLLoaderTest
: public SimpleURLLoaderTestBase,
public testing::TestWithParam<SimpleLoaderTestHelper::DownloadType> {
public:
SimpleURLLoaderTest() : test_helper_(GetParam()) {}
~SimpleURLLoaderTest() override {}
SimpleLoaderTestHelper* test_helper() { return &test_helper_; }
private:
SimpleLoaderTestHelper test_helper_;
};
TEST_P(SimpleURLLoaderTest, BasicRequest) {
ResourceRequest resource_request; ResourceRequest resource_request;
// Use a more interesting request than "/echo", just to verify more than the // Use a more interesting request than "/echo", just to verify more than the
// request URL is hooked up. // request URL is hooked up.
resource_request.url = test_server_.GetURL("/echoheader?foo"); resource_request.url = test_server_.GetURL("/echoheader?foo");
resource_request.headers.SetHeader("foo", "Expected Response"); resource_request.headers.SetHeader("foo", "Expected Response");
WaitForStringHelper string_helper; test_helper()->RunRequest(url_loader_factory_.get(), resource_request);
string_helper.RunRequest(url_loader_factory_.get(), resource_request);
EXPECT_EQ(net::OK, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::OK, test_helper()->simple_url_loader()->NetError());
EXPECT_EQ(200, string_helper.GetResponseCode()); EXPECT_EQ(200, test_helper()->GetResponseCode());
ASSERT_TRUE(string_helper.response_body()); ASSERT_TRUE(test_helper()->response_body());
EXPECT_EQ("Expected Response", *string_helper.response_body()); EXPECT_EQ("Expected Response", *test_helper()->response_body());
} }
// Test that SimpleURLLoader handles data URLs, which don't have headers. // Test that SimpleURLLoader handles data URLs, which don't have headers.
TEST_F(SimpleURLLoaderTest, DataURL) { TEST_P(SimpleURLLoaderTest, DataURL) {
ResourceRequest resource_request; ResourceRequest resource_request;
// Use a more interesting request than "/echo", just to verify more than the
// request URL is hooked up.
resource_request.url = GURL("data:text/plain,foo"); resource_request.url = GURL("data:text/plain,foo");
WaitForStringHelper string_helper; test_helper()->RunRequest(url_loader_factory_.get(), resource_request);
string_helper.RunRequest(url_loader_factory_.get(), resource_request);
EXPECT_EQ(net::OK, test_helper()->simple_url_loader()->NetError());
EXPECT_EQ(net::OK, string_helper.simple_url_loader()->NetError()); ASSERT_TRUE(test_helper()->simple_url_loader()->ResponseInfo());
ASSERT_TRUE(string_helper.simple_url_loader()->ResponseInfo()); EXPECT_FALSE(test_helper()->simple_url_loader()->ResponseInfo()->headers);
EXPECT_FALSE(string_helper.simple_url_loader()->ResponseInfo()->headers); ASSERT_TRUE(test_helper()->response_body());
ASSERT_TRUE(string_helper.response_body()); EXPECT_EQ("foo", *test_helper()->response_body());
EXPECT_EQ("foo", *string_helper.response_body());
} }
// Make sure the class works when the size of the encoded and decoded bodies are // Make sure the class works when the size of the encoded and decoded bodies are
// different. // different.
TEST_F(SimpleURLLoaderTest, GzipBody) { TEST_P(SimpleURLLoaderTest, GzipBody) {
ResourceRequest resource_request; ResourceRequest resource_request;
resource_request.url = test_server_.GetURL("/gzip-body?foo"); resource_request.url = test_server_.GetURL("/gzip-body?foo");
WaitForStringHelper string_helper; test_helper()->RunRequest(url_loader_factory_.get(), resource_request);
string_helper.RunRequest(url_loader_factory_.get(), resource_request);
EXPECT_EQ(net::OK, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::OK, test_helper()->simple_url_loader()->NetError());
EXPECT_EQ(200, string_helper.GetResponseCode()); EXPECT_EQ(200, test_helper()->GetResponseCode());
ASSERT_TRUE(string_helper.response_body()); ASSERT_TRUE(test_helper()->response_body());
EXPECT_EQ("foo", *string_helper.response_body()); EXPECT_EQ("foo", *test_helper()->response_body());
} }
// Make sure redirects are followed. // Make sure redirects are followed.
TEST_F(SimpleURLLoaderTest, Redirect) { TEST_P(SimpleURLLoaderTest, Redirect) {
WaitForStringHelper string_helper; test_helper()->RunRequestForURL(
string_helper.RunRequestForURL(
url_loader_factory_.get(), url_loader_factory_.get(),
test_server_.GetURL("/server-redirect?" + test_server_.GetURL("/server-redirect?" +
test_server_.GetURL("/echo").spec())); test_server_.GetURL("/echo").spec()));
EXPECT_EQ(net::OK, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::OK, test_helper()->simple_url_loader()->NetError());
EXPECT_EQ(200, string_helper.GetResponseCode()); EXPECT_EQ(200, test_helper()->GetResponseCode());
ASSERT_TRUE(string_helper.response_body()); ASSERT_TRUE(test_helper()->response_body());
EXPECT_EQ("Echo", *string_helper.response_body()); EXPECT_EQ("Echo", *test_helper()->response_body());
} }
// Check that no body is returned with an HTTP error response. // Check that no body is returned with an HTTP error response.
TEST_F(SimpleURLLoaderTest, HttpErrorStatusCodeResponse) { TEST_P(SimpleURLLoaderTest, HttpErrorStatusCodeResponse) {
WaitForStringHelper string_helper; test_helper()->RunRequestForURL(url_loader_factory_.get(),
string_helper.RunRequestForURL(url_loader_factory_.get(),
test_server_.GetURL("/echo?status=400")); test_server_.GetURL("/echo?status=400"));
EXPECT_EQ(net::ERR_FAILED, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::ERR_FAILED, test_helper()->simple_url_loader()->NetError());
EXPECT_EQ(400, string_helper.GetResponseCode()); EXPECT_EQ(400, test_helper()->GetResponseCode());
EXPECT_FALSE(string_helper.response_body()); EXPECT_FALSE(test_helper()->response_body());
} }
// Check that the body is returned with an HTTP error response, when // Check that the body is returned with an HTTP error response, when
// SetAllowHttpErrorResults(true) is called. // SetAllowHttpErrorResults(true) is called.
TEST_F(SimpleURLLoaderTest, HttpErrorStatusCodeResponseAllowed) { TEST_P(SimpleURLLoaderTest, HttpErrorStatusCodeResponseAllowed) {
WaitForStringHelper string_helper; test_helper()->simple_url_loader()->SetAllowHttpErrorResults(true);
string_helper.simple_url_loader()->SetAllowHttpErrorResults(true); test_helper()->RunRequestForURL(url_loader_factory_.get(),
string_helper.RunRequestForURL(url_loader_factory_.get(),
test_server_.GetURL("/echo?status=400")); test_server_.GetURL("/echo?status=400"));
EXPECT_EQ(net::OK, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::OK, test_helper()->simple_url_loader()->NetError());
EXPECT_EQ(400, string_helper.GetResponseCode()); EXPECT_EQ(400, test_helper()->GetResponseCode());
ASSERT_TRUE(string_helper.response_body()); ASSERT_TRUE(test_helper()->response_body());
EXPECT_EQ("Echo", *string_helper.response_body()); EXPECT_EQ("Echo", *test_helper()->response_body());
} }
TEST_F(SimpleURLLoaderTest, EmptyResponseBody) { TEST_P(SimpleURLLoaderTest, EmptyResponseBody) {
WaitForStringHelper string_helper; test_helper()->RunRequestForURL(url_loader_factory_.get(),
string_helper.RunRequestForURL(url_loader_factory_.get(),
test_server_.GetURL("/nocontent")); test_server_.GetURL("/nocontent"));
EXPECT_EQ(net::OK, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::OK, test_helper()->simple_url_loader()->NetError());
EXPECT_EQ(204, string_helper.GetResponseCode()); EXPECT_EQ(204, test_helper()->GetResponseCode());
ASSERT_TRUE(string_helper.response_body()); ASSERT_TRUE(test_helper()->response_body());
// A response body is sent from the NetworkService, but it's empty. // A response body is sent from the NetworkService, but it's empty.
EXPECT_EQ("", *string_helper.response_body()); EXPECT_EQ("", *test_helper()->response_body());
} }
TEST_F(SimpleURLLoaderTest, BigResponseBody) { TEST_P(SimpleURLLoaderTest, BigResponseBody) {
WaitForStringHelper string_helper;
// Big response that requires multiple reads, and exceeds the maximum size // Big response that requires multiple reads, and exceeds the maximum size
// limit of SimpleURLLoader::DownloadToString(). That is, this test make sure // limit of SimpleURLLoader::DownloadToString(). That is, this test make sure
// that DownloadToStringOfUnboundedSizeUntilCrashAndDie() can receive strings // that DownloadToStringOfUnboundedSizeUntilCrashAndDie() can receive strings
// longer than DownloadToString() allows. // longer than DownloadToString() allows.
const uint32_t kResponseSize = 2 * 1024 * 1024; const uint32_t kResponseSize = 2 * 1024 * 1024;
string_helper.RunRequestForURL(url_loader_factory_.get(), test_helper()->RunRequestForURL(url_loader_factory_.get(),
test_server_.GetURL(base::StringPrintf( test_server_.GetURL(base::StringPrintf(
"/response-size?%u", kResponseSize))); "/response-size?%u", kResponseSize)));
EXPECT_EQ(net::OK, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::OK, test_helper()->simple_url_loader()->NetError());
EXPECT_EQ(200, string_helper.GetResponseCode()); EXPECT_EQ(200, test_helper()->GetResponseCode());
ASSERT_TRUE(string_helper.response_body()); ASSERT_TRUE(test_helper()->response_body());
EXPECT_EQ(kResponseSize, string_helper.response_body()->length()); EXPECT_EQ(kResponseSize, test_helper()->response_body()->length());
EXPECT_EQ(std::string(kResponseSize, 'a'), *string_helper.response_body()); EXPECT_EQ(std::string(kResponseSize, 'a'), *test_helper()->response_body());
} }
TEST_F(SimpleURLLoaderTest, ResponseBodyWithSizeMatchingLimit) { TEST_P(SimpleURLLoaderTest, ResponseBodyWithSizeMatchingLimit) {
const uint32_t kResponseSize = 16; const uint32_t kResponseSize = 16;
WaitForStringHelper string_helper; test_helper()->RunRequestForURLWithBoundedSize(
string_helper.RunRequestForURLWithBoundedSize(
url_loader_factory_.get(), url_loader_factory_.get(),
test_server_.GetURL( test_server_.GetURL(
base::StringPrintf("/response-size?%u", kResponseSize)), base::StringPrintf("/response-size?%u", kResponseSize)),
kResponseSize); kResponseSize);
EXPECT_EQ(net::OK, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::OK, test_helper()->simple_url_loader()->NetError());
EXPECT_EQ(200, string_helper.GetResponseCode()); EXPECT_EQ(200, test_helper()->GetResponseCode());
ASSERT_TRUE(string_helper.response_body()); ASSERT_TRUE(test_helper()->response_body());
EXPECT_EQ(kResponseSize, string_helper.response_body()->length()); EXPECT_EQ(kResponseSize, test_helper()->response_body()->length());
EXPECT_EQ(std::string(kResponseSize, 'a'), *string_helper.response_body()); EXPECT_EQ(std::string(kResponseSize, 'a'), *test_helper()->response_body());
} }
TEST_F(SimpleURLLoaderTest, ResponseBodyWithSizeBelowLimit) { TEST_P(SimpleURLLoaderTest, ResponseBodyWithSizeBelowLimit) {
WaitForStringHelper string_helper;
const uint32_t kResponseSize = 16; const uint32_t kResponseSize = 16;
const uint32_t kMaxResponseSize = kResponseSize + 1; const uint32_t kMaxResponseSize = kResponseSize + 1;
string_helper.RunRequestForURLWithBoundedSize( test_helper()->RunRequestForURLWithBoundedSize(
url_loader_factory_.get(), url_loader_factory_.get(),
test_server_.GetURL( test_server_.GetURL(
base::StringPrintf("/response-size?%u", kResponseSize)), base::StringPrintf("/response-size?%u", kResponseSize)),
kMaxResponseSize); kMaxResponseSize);
EXPECT_EQ(net::OK, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::OK, test_helper()->simple_url_loader()->NetError());
EXPECT_EQ(200, string_helper.GetResponseCode()); EXPECT_EQ(200, test_helper()->GetResponseCode());
ASSERT_TRUE(string_helper.response_body()); ASSERT_TRUE(test_helper()->response_body());
EXPECT_EQ(kResponseSize, string_helper.response_body()->length()); EXPECT_EQ(kResponseSize, test_helper()->response_body()->length());
EXPECT_EQ(std::string(kResponseSize, 'a'), *string_helper.response_body()); EXPECT_EQ(std::string(kResponseSize, 'a'), *test_helper()->response_body());
} }
TEST_F(SimpleURLLoaderTest, ResponseBodyWithSizeAboveLimit) { TEST_P(SimpleURLLoaderTest, ResponseBodyWithSizeAboveLimit) {
const uint32_t kResponseSize = 16; const uint32_t kResponseSize = 16;
WaitForStringHelper string_helper; test_helper()->RunRequestForURLWithBoundedSize(
string_helper.RunRequestForURLWithBoundedSize(
url_loader_factory_.get(), url_loader_factory_.get(),
test_server_.GetURL( test_server_.GetURL(
base::StringPrintf("/response-size?%u", kResponseSize)), base::StringPrintf("/response-size?%u", kResponseSize)),
kResponseSize - 1); kResponseSize - 1);
EXPECT_EQ(net::ERR_INSUFFICIENT_RESOURCES, EXPECT_EQ(net::ERR_INSUFFICIENT_RESOURCES,
string_helper.simple_url_loader()->NetError()); test_helper()->simple_url_loader()->NetError());
EXPECT_FALSE(string_helper.response_body()); EXPECT_FALSE(test_helper()->response_body());
} }
// Same as above, but with setting allow_partial_results to true. // Same as above, but with setting allow_partial_results to true.
TEST_F(SimpleURLLoaderTest, ResponseBodyWithSizeAboveLimitPartialResponse) { TEST_P(SimpleURLLoaderTest, ResponseBodyWithSizeAboveLimitPartialResponse) {
WaitForStringHelper string_helper;
const uint32_t kResponseSize = 16; const uint32_t kResponseSize = 16;
const uint32_t kMaxResponseSize = kResponseSize - 1; const uint32_t kMaxResponseSize = kResponseSize - 1;
string_helper.simple_url_loader()->SetAllowPartialResults(true); test_helper()->simple_url_loader()->SetAllowPartialResults(true);
string_helper.RunRequestForURLWithBoundedSize( test_helper()->RunRequestForURLWithBoundedSize(
url_loader_factory_.get(), url_loader_factory_.get(),
test_server_.GetURL( test_server_.GetURL(
base::StringPrintf("/response-size?%u", kResponseSize)), base::StringPrintf("/response-size?%u", kResponseSize)),
kMaxResponseSize); kMaxResponseSize);
EXPECT_EQ(net::ERR_INSUFFICIENT_RESOURCES, EXPECT_EQ(net::ERR_INSUFFICIENT_RESOURCES,
string_helper.simple_url_loader()->NetError()); test_helper()->simple_url_loader()->NetError());
ASSERT_TRUE(string_helper.response_body()); ASSERT_TRUE(test_helper()->response_body());
EXPECT_EQ(std::string(kMaxResponseSize, 'a'), *string_helper.response_body()); EXPECT_EQ(std::string(kMaxResponseSize, 'a'),
EXPECT_EQ(kMaxResponseSize, string_helper.response_body()->length()); *test_helper()->response_body());
EXPECT_EQ(kMaxResponseSize, test_helper()->response_body()->length());
} }
// The next 4 tests duplicate the above 4, but with larger response sizes. This // The next 4 tests duplicate the above 4, but with larger response sizes. This
// means the size limit will not be exceeded on the first read. // means the size limit will not be exceeded on the first read.
TEST_F(SimpleURLLoaderTest, BigResponseBodyWithSizeMatchingLimit) { TEST_P(SimpleURLLoaderTest, BigResponseBodyWithSizeMatchingLimit) {
const uint32_t kResponseSize = 512 * 1024; const uint32_t kResponseSize = 512 * 1024;
WaitForStringHelper string_helper; test_helper()->RunRequestForURLWithBoundedSize(
string_helper.RunRequestForURLWithBoundedSize(
url_loader_factory_.get(), url_loader_factory_.get(),
test_server_.GetURL( test_server_.GetURL(
base::StringPrintf("/response-size?%u", kResponseSize)), base::StringPrintf("/response-size?%u", kResponseSize)),
kResponseSize); kResponseSize);
EXPECT_EQ(net::OK, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::OK, test_helper()->simple_url_loader()->NetError());
ASSERT_TRUE(string_helper.response_body()); ASSERT_TRUE(test_helper()->response_body());
EXPECT_EQ(kResponseSize, string_helper.response_body()->length()); EXPECT_EQ(kResponseSize, test_helper()->response_body()->length());
EXPECT_EQ(std::string(kResponseSize, 'a'), *string_helper.response_body()); EXPECT_EQ(std::string(kResponseSize, 'a'), *test_helper()->response_body());
} }
TEST_F(SimpleURLLoaderTest, BigResponseBodyWithSizeBelowLimit) { TEST_P(SimpleURLLoaderTest, BigResponseBodyWithSizeBelowLimit) {
const uint32_t kResponseSize = 512 * 1024; const uint32_t kResponseSize = 512 * 1024;
const uint32_t kMaxResponseSize = kResponseSize + 1; const uint32_t kMaxResponseSize = kResponseSize + 1;
WaitForStringHelper string_helper; test_helper()->RunRequestForURLWithBoundedSize(
string_helper.RunRequestForURLWithBoundedSize(
url_loader_factory_.get(), url_loader_factory_.get(),
test_server_.GetURL( test_server_.GetURL(
base::StringPrintf("/response-size?%u", kResponseSize)), base::StringPrintf("/response-size?%u", kResponseSize)),
kMaxResponseSize); kMaxResponseSize);
EXPECT_EQ(net::OK, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::OK, test_helper()->simple_url_loader()->NetError());
ASSERT_TRUE(string_helper.response_body()); ASSERT_TRUE(test_helper()->response_body());
EXPECT_EQ(kResponseSize, string_helper.response_body()->length()); EXPECT_EQ(kResponseSize, test_helper()->response_body()->length());
EXPECT_EQ(std::string(kResponseSize, 'a'), *string_helper.response_body()); EXPECT_EQ(std::string(kResponseSize, 'a'), *test_helper()->response_body());
} }
TEST_F(SimpleURLLoaderTest, BigResponseBodyWithSizeAboveLimit) { TEST_P(SimpleURLLoaderTest, BigResponseBodyWithSizeAboveLimit) {
WaitForStringHelper string_helper;
const uint32_t kResponseSize = 512 * 1024; const uint32_t kResponseSize = 512 * 1024;
string_helper.RunRequestForURLWithBoundedSize( test_helper()->RunRequestForURLWithBoundedSize(
url_loader_factory_.get(), url_loader_factory_.get(),
test_server_.GetURL( test_server_.GetURL(
base::StringPrintf("/response-size?%u", kResponseSize)), base::StringPrintf("/response-size?%u", kResponseSize)),
kResponseSize - 1); kResponseSize - 1);
EXPECT_EQ(net::ERR_INSUFFICIENT_RESOURCES, EXPECT_EQ(net::ERR_INSUFFICIENT_RESOURCES,
string_helper.simple_url_loader()->NetError()); test_helper()->simple_url_loader()->NetError());
EXPECT_FALSE(string_helper.response_body()); EXPECT_FALSE(test_helper()->response_body());
} }
TEST_F(SimpleURLLoaderTest, BigResponseBodyWithSizeAboveLimitPartialResponse) { TEST_P(SimpleURLLoaderTest, BigResponseBodyWithSizeAboveLimitPartialResponse) {
const uint32_t kResponseSize = 512 * 1024; const uint32_t kResponseSize = 512 * 1024;
const uint32_t kMaxResponseSize = kResponseSize - 1; const uint32_t kMaxResponseSize = kResponseSize - 1;
WaitForStringHelper string_helper; test_helper()->simple_url_loader()->SetAllowPartialResults(true);
string_helper.simple_url_loader()->SetAllowPartialResults(true); test_helper()->RunRequestForURLWithBoundedSize(
string_helper.RunRequestForURLWithBoundedSize(
url_loader_factory_.get(), url_loader_factory_.get(),
test_server_.GetURL( test_server_.GetURL(
base::StringPrintf("/response-size?%u", kResponseSize)), base::StringPrintf("/response-size?%u", kResponseSize)),
kMaxResponseSize); kMaxResponseSize);
EXPECT_EQ(net::ERR_INSUFFICIENT_RESOURCES, EXPECT_EQ(net::ERR_INSUFFICIENT_RESOURCES,
string_helper.simple_url_loader()->NetError()); test_helper()->simple_url_loader()->NetError());
ASSERT_TRUE(string_helper.response_body()); ASSERT_TRUE(test_helper()->response_body());
EXPECT_EQ(std::string(kMaxResponseSize, 'a'), *string_helper.response_body()); EXPECT_EQ(std::string(kMaxResponseSize, 'a'),
EXPECT_EQ(kMaxResponseSize, string_helper.response_body()->length()); *test_helper()->response_body());
EXPECT_EQ(kMaxResponseSize, test_helper()->response_body()->length());
} }
TEST_F(SimpleURLLoaderTest, NetErrorBeforeHeaders) { TEST_P(SimpleURLLoaderTest, NetErrorBeforeHeaders) {
WaitForStringHelper string_helper; test_helper()->RunRequestForURL(url_loader_factory_.get(),
string_helper.RunRequestForURL(url_loader_factory_.get(),
test_server_.GetURL("/close-socket")); test_server_.GetURL("/close-socket"));
EXPECT_EQ(net::ERR_EMPTY_RESPONSE, EXPECT_EQ(net::ERR_EMPTY_RESPONSE,
string_helper.simple_url_loader()->NetError()); test_helper()->simple_url_loader()->NetError());
EXPECT_FALSE(string_helper.simple_url_loader()->ResponseInfo()); EXPECT_FALSE(test_helper()->simple_url_loader()->ResponseInfo());
EXPECT_FALSE(string_helper.response_body()); EXPECT_FALSE(test_helper()->response_body());
} }
TEST_F(SimpleURLLoaderTest, NetErrorBeforeHeadersWithPartialResults) { TEST_P(SimpleURLLoaderTest, NetErrorBeforeHeadersWithPartialResults) {
WaitForStringHelper string_helper;
// Allow response body on error. There should still be no response body, since // Allow response body on error. There should still be no response body, since
// the error is before body reading starts. // the error is before body reading starts.
string_helper.simple_url_loader()->SetAllowPartialResults(true); test_helper()->simple_url_loader()->SetAllowPartialResults(true);
string_helper.RunRequestForURL(url_loader_factory_.get(), test_helper()->RunRequestForURL(url_loader_factory_.get(),
test_server_.GetURL("/close-socket")); test_server_.GetURL("/close-socket"));
EXPECT_FALSE(string_helper.response_body()); EXPECT_FALSE(test_helper()->response_body());
EXPECT_EQ(net::ERR_EMPTY_RESPONSE, EXPECT_EQ(net::ERR_EMPTY_RESPONSE,
string_helper.simple_url_loader()->NetError()); test_helper()->simple_url_loader()->NetError());
EXPECT_FALSE(string_helper.simple_url_loader()->ResponseInfo()); EXPECT_FALSE(test_helper()->simple_url_loader()->ResponseInfo());
} }
TEST_F(SimpleURLLoaderTest, NetErrorAfterHeaders) { TEST_P(SimpleURLLoaderTest, NetErrorAfterHeaders) {
WaitForStringHelper string_helper; test_helper()->RunRequestForURL(url_loader_factory_.get(),
string_helper.RunRequestForURL(url_loader_factory_.get(),
test_server_.GetURL(kInvalidGzipPath)); test_server_.GetURL(kInvalidGzipPath));
EXPECT_EQ(net::ERR_CONTENT_DECODING_FAILED, EXPECT_EQ(net::ERR_CONTENT_DECODING_FAILED,
string_helper.simple_url_loader()->NetError()); test_helper()->simple_url_loader()->NetError());
EXPECT_EQ(200, string_helper.GetResponseCode()); EXPECT_EQ(200, test_helper()->GetResponseCode());
EXPECT_FALSE(string_helper.response_body()); EXPECT_FALSE(test_helper()->response_body());
} }
TEST_F(SimpleURLLoaderTest, NetErrorAfterHeadersWithPartialResults) { TEST_P(SimpleURLLoaderTest, NetErrorAfterHeadersWithPartialResults) {
WaitForStringHelper string_helper;
// Allow response body on error. This case results in a 0-byte response body. // Allow response body on error. This case results in a 0-byte response body.
string_helper.simple_url_loader()->SetAllowPartialResults(true); test_helper()->simple_url_loader()->SetAllowPartialResults(true);
string_helper.RunRequestForURL(url_loader_factory_.get(), test_helper()->RunRequestForURL(url_loader_factory_.get(),
test_server_.GetURL(kInvalidGzipPath)); test_server_.GetURL(kInvalidGzipPath));
EXPECT_EQ(net::ERR_CONTENT_DECODING_FAILED, EXPECT_EQ(net::ERR_CONTENT_DECODING_FAILED,
string_helper.simple_url_loader()->NetError()); test_helper()->simple_url_loader()->NetError());
EXPECT_EQ(200, string_helper.GetResponseCode()); EXPECT_EQ(200, test_helper()->GetResponseCode());
ASSERT_TRUE(string_helper.response_body()); ASSERT_TRUE(test_helper()->response_body());
EXPECT_EQ("", *string_helper.response_body()); EXPECT_EQ("", *test_helper()->response_body());
} }
TEST_F(SimpleURLLoaderTest, TruncatedBody) { TEST_P(SimpleURLLoaderTest, TruncatedBody) {
WaitForStringHelper string_helper; test_helper()->RunRequestForURL(url_loader_factory_.get(),
string_helper.RunRequestForURL(url_loader_factory_.get(),
test_server_.GetURL(kTruncatedBodyPath)); test_server_.GetURL(kTruncatedBodyPath));
EXPECT_EQ(net::ERR_CONTENT_LENGTH_MISMATCH, EXPECT_EQ(net::ERR_CONTENT_LENGTH_MISMATCH,
string_helper.simple_url_loader()->NetError()); test_helper()->simple_url_loader()->NetError());
EXPECT_EQ(200, string_helper.GetResponseCode()); EXPECT_EQ(200, test_helper()->GetResponseCode());
EXPECT_FALSE(string_helper.response_body()); EXPECT_FALSE(test_helper()->response_body());
} }
TEST_F(SimpleURLLoaderTest, TruncatedBodyWithPartialResults) { TEST_P(SimpleURLLoaderTest, TruncatedBodyWithPartialResults) {
WaitForStringHelper string_helper; test_helper()->simple_url_loader()->SetAllowPartialResults(true);
string_helper.simple_url_loader()->SetAllowPartialResults(true); test_helper()->RunRequestForURL(url_loader_factory_.get(),
string_helper.RunRequestForURL(url_loader_factory_.get(),
test_server_.GetURL(kTruncatedBodyPath)); test_server_.GetURL(kTruncatedBodyPath));
EXPECT_EQ(net::ERR_CONTENT_LENGTH_MISMATCH, EXPECT_EQ(net::ERR_CONTENT_LENGTH_MISMATCH,
string_helper.simple_url_loader()->NetError()); test_helper()->simple_url_loader()->NetError());
EXPECT_EQ(200, string_helper.GetResponseCode()); EXPECT_EQ(200, test_helper()->GetResponseCode());
ASSERT_TRUE(string_helper.response_body()); ASSERT_TRUE(test_helper()->response_body());
EXPECT_EQ(kTruncatedBody, *string_helper.response_body()); EXPECT_EQ(kTruncatedBody, *test_helper()->response_body());
} }
// Test case where NetworkService is destroyed before headers are received (and // Test case where NetworkService is destroyed before headers are received (and
// before the request is even made, for that matter). // before the request is even made, for that matter).
TEST_F(SimpleURLLoaderTest, DestroyServiceBeforeResponseStarts) { TEST_P(SimpleURLLoaderTest, DestroyServiceBeforeResponseStarts) {
ResourceRequest resource_request; ResourceRequest resource_request;
resource_request.url = test_server_.GetURL("/hung"); resource_request.url = test_server_.GetURL("/hung");
WaitForStringHelper string_helper; test_helper()->StartRequest(url_loader_factory_.get(), resource_request);
string_helper.simple_url_loader()
->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
resource_request, url_loader_factory_.get(),
TRAFFIC_ANNOTATION_FOR_TESTS, string_helper.GetCallback());
network_service_ = nullptr; network_service_ = nullptr;
string_helper.Wait(); test_helper()->Wait();
EXPECT_EQ(net::ERR_FAILED, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::ERR_FAILED, test_helper()->simple_url_loader()->NetError());
EXPECT_FALSE(string_helper.response_body()); EXPECT_FALSE(test_helper()->response_body());
ASSERT_FALSE(string_helper.simple_url_loader()->ResponseInfo()); ASSERT_FALSE(test_helper()->simple_url_loader()->ResponseInfo());
} }
enum class TestLoaderEvent { enum class TestLoaderEvent {
...@@ -773,47 +874,46 @@ class MockURLLoaderFactory : public mojom::URLLoaderFactory { ...@@ -773,47 +874,46 @@ class MockURLLoaderFactory : public mojom::URLLoaderFactory {
}; };
// Check that the request fails if OnComplete() is called before anything else. // Check that the request fails if OnComplete() is called before anything else.
TEST_F(SimpleURLLoaderTest, ResponseCompleteBeforeReceivedResponse) { TEST_P(SimpleURLLoaderTest, ResponseCompleteBeforeReceivedResponse) {
MockURLLoaderFactory loader_factory(&scoped_task_environment_); MockURLLoaderFactory loader_factory(&scoped_task_environment_);
loader_factory.AddEvent(TestLoaderEvent::kResponseComplete); loader_factory.AddEvent(TestLoaderEvent::kResponseComplete);
WaitForStringHelper string_helper; test_helper()->RunRequestForURL(&loader_factory, GURL("foo://bar/"));
string_helper.RunRequestForURL(&loader_factory, GURL("foo://bar/"));
EXPECT_EQ(net::ERR_UNEXPECTED, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::ERR_UNEXPECTED,
EXPECT_FALSE(string_helper.simple_url_loader()->ResponseInfo()); test_helper()->simple_url_loader()->NetError());
EXPECT_FALSE(string_helper.response_body()); EXPECT_FALSE(test_helper()->simple_url_loader()->ResponseInfo());
EXPECT_FALSE(test_helper()->response_body());
} }
// Check that the request fails if OnComplete() is called before the body pipe // Check that the request fails if OnComplete() is called before the body pipe
// is received. // is received.
TEST_F(SimpleURLLoaderTest, ResponseCompleteAfterReceivedResponse) { TEST_P(SimpleURLLoaderTest, ResponseCompleteAfterReceivedResponse) {
MockURLLoaderFactory loader_factory(&scoped_task_environment_); MockURLLoaderFactory loader_factory(&scoped_task_environment_);
loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse); loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse);
loader_factory.AddEvent(TestLoaderEvent::kResponseComplete); loader_factory.AddEvent(TestLoaderEvent::kResponseComplete);
WaitForStringHelper string_helper; test_helper()->RunRequestForURL(&loader_factory, GURL("foo://bar/"));
string_helper.RunRequestForURL(&loader_factory, GURL("foo://bar/"));
EXPECT_EQ(net::ERR_UNEXPECTED, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::ERR_UNEXPECTED,
EXPECT_EQ(200, string_helper.GetResponseCode()); test_helper()->simple_url_loader()->NetError());
EXPECT_FALSE(string_helper.response_body()); EXPECT_EQ(200, test_helper()->GetResponseCode());
EXPECT_FALSE(test_helper()->response_body());
} }
TEST_F(SimpleURLLoaderTest, CloseClientPipeBeforeBodyStarts) { TEST_P(SimpleURLLoaderTest, CloseClientPipeBeforeBodyStarts) {
MockURLLoaderFactory loader_factory(&scoped_task_environment_); MockURLLoaderFactory loader_factory(&scoped_task_environment_);
loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse); loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse);
loader_factory.AddEvent(TestLoaderEvent::kClientPipeClosed); loader_factory.AddEvent(TestLoaderEvent::kClientPipeClosed);
WaitForStringHelper string_helper; test_helper()->RunRequestForURL(&loader_factory, GURL("foo://bar/"));
string_helper.RunRequestForURL(&loader_factory, GURL("foo://bar/"));
EXPECT_EQ(net::ERR_FAILED, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::ERR_FAILED, test_helper()->simple_url_loader()->NetError());
EXPECT_EQ(200, string_helper.GetResponseCode()); EXPECT_EQ(200, test_helper()->GetResponseCode());
EXPECT_FALSE(string_helper.response_body()); EXPECT_FALSE(test_helper()->response_body());
} }
// This test tries closing the client pipe / completing the request in most // This test tries closing the client pipe / completing the request in most
// possible valid orders relative to read events (Which always occur in the same // possible valid orders relative to read events (Which always occur in the same
// order). // order).
TEST_F(SimpleURLLoaderTest, CloseClientPipeOrder) { TEST_P(SimpleURLLoaderTest, CloseClientPipeOrder) {
enum class ClientCloseOrder { enum class ClientCloseOrder {
kBeforeData, kBeforeData,
kDuringData, kDuringData,
...@@ -863,33 +963,33 @@ TEST_F(SimpleURLLoaderTest, CloseClientPipeOrder) { ...@@ -863,33 +963,33 @@ TEST_F(SimpleURLLoaderTest, CloseClientPipeOrder) {
if (close_client_order == ClientCloseOrder::kAfterBufferClosed) if (close_client_order == ClientCloseOrder::kAfterBufferClosed)
loader_factory.AddEvent(close_client_event); loader_factory.AddEvent(close_client_event);
WaitForStringHelper string_helper; SimpleLoaderTestHelper test_helper(GetParam());
string_helper.simple_url_loader()->SetAllowPartialResults( test_helper.simple_url_loader()->SetAllowPartialResults(
allow_partial_results); allow_partial_results);
string_helper.RunRequestForURL(&loader_factory, GURL("foo://bar/")); test_helper.RunRequestForURL(&loader_factory, GURL("foo://bar/"));
EXPECT_EQ(200, string_helper.GetResponseCode()); EXPECT_EQ(200, test_helper.GetResponseCode());
if (close_client_event != TestLoaderEvent::kResponseComplete) { if (close_client_event != TestLoaderEvent::kResponseComplete) {
if (close_client_event == if (close_client_event ==
TestLoaderEvent::kResponseCompleteFailed) { TestLoaderEvent::kResponseCompleteFailed) {
EXPECT_EQ(net::ERR_TIMED_OUT, EXPECT_EQ(net::ERR_TIMED_OUT,
string_helper.simple_url_loader()->NetError()); test_helper.simple_url_loader()->NetError());
} else { } else {
EXPECT_EQ(net::ERR_FAILED, EXPECT_EQ(net::ERR_FAILED,
string_helper.simple_url_loader()->NetError()); test_helper.simple_url_loader()->NetError());
} }
if (!allow_partial_results) { if (!allow_partial_results) {
EXPECT_FALSE(string_helper.response_body()); EXPECT_FALSE(test_helper.response_body());
} else { } else {
ASSERT_TRUE(string_helper.response_body()); ASSERT_TRUE(test_helper.response_body());
EXPECT_EQ(std::string(bytes_received, 'a'), EXPECT_EQ(std::string(bytes_received, 'a'),
*string_helper.response_body()); *test_helper.response_body());
} }
} else { } else {
EXPECT_EQ(net::OK, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::OK, test_helper.simple_url_loader()->NetError());
ASSERT_TRUE(string_helper.response_body()); ASSERT_TRUE(test_helper.response_body());
EXPECT_EQ(std::string(bytes_received, 'a'), EXPECT_EQ(std::string(bytes_received, 'a'),
*string_helper.response_body()); *test_helper.response_body());
} }
} }
} }
...@@ -898,21 +998,20 @@ TEST_F(SimpleURLLoaderTest, CloseClientPipeOrder) { ...@@ -898,21 +998,20 @@ TEST_F(SimpleURLLoaderTest, CloseClientPipeOrder) {
} }
// Make sure the close client pipe message doesn't cause any issues. // Make sure the close client pipe message doesn't cause any issues.
TEST_F(SimpleURLLoaderTest, ErrorAndCloseClientPipeBeforeBodyStarts) { TEST_P(SimpleURLLoaderTest, ErrorAndCloseClientPipeBeforeBodyStarts) {
MockURLLoaderFactory loader_factory(&scoped_task_environment_); MockURLLoaderFactory loader_factory(&scoped_task_environment_);
loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse); loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse);
loader_factory.AddEvent(TestLoaderEvent::kResponseCompleteFailed); loader_factory.AddEvent(TestLoaderEvent::kResponseCompleteFailed);
loader_factory.AddEvent(TestLoaderEvent::kClientPipeClosed); loader_factory.AddEvent(TestLoaderEvent::kClientPipeClosed);
WaitForStringHelper string_helper; test_helper()->RunRequestForURL(&loader_factory, GURL("foo://bar/"));
string_helper.RunRequestForURL(&loader_factory, GURL("foo://bar/"));
EXPECT_EQ(net::ERR_TIMED_OUT, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::ERR_TIMED_OUT, test_helper()->simple_url_loader()->NetError());
EXPECT_EQ(200, string_helper.GetResponseCode()); EXPECT_EQ(200, test_helper()->GetResponseCode());
EXPECT_FALSE(string_helper.response_body()); EXPECT_FALSE(test_helper()->response_body());
} }
// Make sure the close client pipe message doesn't cause any issues. // Make sure the close client pipe message doesn't cause any issues.
TEST_F(SimpleURLLoaderTest, SuccessAndCloseClientPipeBeforeBodyComplete) { TEST_P(SimpleURLLoaderTest, SuccessAndCloseClientPipeBeforeBodyComplete) {
MockURLLoaderFactory loader_factory(&scoped_task_environment_); MockURLLoaderFactory loader_factory(&scoped_task_environment_);
loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse); loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse);
loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived); loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived);
...@@ -920,17 +1019,16 @@ TEST_F(SimpleURLLoaderTest, SuccessAndCloseClientPipeBeforeBodyComplete) { ...@@ -920,17 +1019,16 @@ TEST_F(SimpleURLLoaderTest, SuccessAndCloseClientPipeBeforeBodyComplete) {
loader_factory.AddEvent(TestLoaderEvent::kClientPipeClosed); loader_factory.AddEvent(TestLoaderEvent::kClientPipeClosed);
loader_factory.AddEvent(TestLoaderEvent::kBodyDataRead); loader_factory.AddEvent(TestLoaderEvent::kBodyDataRead);
loader_factory.AddEvent(TestLoaderEvent::kBodyBufferClosed); loader_factory.AddEvent(TestLoaderEvent::kBodyBufferClosed);
WaitForStringHelper string_helper; test_helper()->RunRequestForURL(&loader_factory, GURL("foo://bar/"));
string_helper.RunRequestForURL(&loader_factory, GURL("foo://bar/"));
EXPECT_EQ(net::OK, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::OK, test_helper()->simple_url_loader()->NetError());
EXPECT_EQ(200, string_helper.GetResponseCode()); EXPECT_EQ(200, test_helper()->GetResponseCode());
ASSERT_TRUE(string_helper.response_body()); ASSERT_TRUE(test_helper()->response_body());
EXPECT_EQ("a", *string_helper.response_body()); EXPECT_EQ("a", *test_helper()->response_body());
} }
// Make sure the close client pipe message doesn't cause any issues. // Make sure the close client pipe message doesn't cause any issues.
TEST_F(SimpleURLLoaderTest, SuccessAndCloseClientPipeAfterBodyComplete) { TEST_P(SimpleURLLoaderTest, SuccessAndCloseClientPipeAfterBodyComplete) {
MockURLLoaderFactory loader_factory(&scoped_task_environment_); MockURLLoaderFactory loader_factory(&scoped_task_environment_);
loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse); loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse);
loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived); loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived);
...@@ -938,107 +1036,106 @@ TEST_F(SimpleURLLoaderTest, SuccessAndCloseClientPipeAfterBodyComplete) { ...@@ -938,107 +1036,106 @@ TEST_F(SimpleURLLoaderTest, SuccessAndCloseClientPipeAfterBodyComplete) {
loader_factory.AddEvent(TestLoaderEvent::kBodyBufferClosed); loader_factory.AddEvent(TestLoaderEvent::kBodyBufferClosed);
loader_factory.AddEvent(TestLoaderEvent::kResponseComplete); loader_factory.AddEvent(TestLoaderEvent::kResponseComplete);
loader_factory.AddEvent(TestLoaderEvent::kClientPipeClosed); loader_factory.AddEvent(TestLoaderEvent::kClientPipeClosed);
WaitForStringHelper string_helper; test_helper()->RunRequestForURL(&loader_factory, GURL("foo://bar/"));
string_helper.RunRequestForURL(&loader_factory, GURL("foo://bar/"));
EXPECT_EQ(net::OK, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::OK, test_helper()->simple_url_loader()->NetError());
EXPECT_EQ(200, string_helper.GetResponseCode()); EXPECT_EQ(200, test_helper()->GetResponseCode());
ASSERT_TRUE(string_helper.response_body()); ASSERT_TRUE(test_helper()->response_body());
EXPECT_EQ("a", *string_helper.response_body()); EXPECT_EQ("a", *test_helper()->response_body());
} }
TEST_F(SimpleURLLoaderTest, DoubleReceivedResponse) { TEST_P(SimpleURLLoaderTest, DoubleReceivedResponse) {
MockURLLoaderFactory loader_factory(&scoped_task_environment_); MockURLLoaderFactory loader_factory(&scoped_task_environment_);
loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse); loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse);
loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse); loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse);
WaitForStringHelper string_helper; test_helper()->RunRequestForURL(&loader_factory, GURL("foo://bar/"));
string_helper.RunRequestForURL(&loader_factory, GURL("foo://bar/"));
EXPECT_EQ(net::ERR_UNEXPECTED, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::ERR_UNEXPECTED,
EXPECT_EQ(200, string_helper.GetResponseCode()); test_helper()->simple_url_loader()->NetError());
EXPECT_FALSE(string_helper.response_body()); EXPECT_EQ(200, test_helper()->GetResponseCode());
EXPECT_FALSE(test_helper()->response_body());
} }
TEST_F(SimpleURLLoaderTest, RedirectAfterReseivedResponse) { TEST_P(SimpleURLLoaderTest, RedirectAfterReseivedResponse) {
MockURLLoaderFactory loader_factory(&scoped_task_environment_); MockURLLoaderFactory loader_factory(&scoped_task_environment_);
loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse); loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse);
loader_factory.AddEvent(TestLoaderEvent::kReceivedRedirect); loader_factory.AddEvent(TestLoaderEvent::kReceivedRedirect);
WaitForStringHelper string_helper; test_helper()->RunRequestForURL(&loader_factory, GURL("foo://bar/"));
string_helper.RunRequestForURL(&loader_factory, GURL("foo://bar/"));
EXPECT_EQ(net::ERR_UNEXPECTED, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::ERR_UNEXPECTED,
EXPECT_EQ(200, string_helper.GetResponseCode()); test_helper()->simple_url_loader()->NetError());
EXPECT_FALSE(string_helper.response_body()); EXPECT_EQ(200, test_helper()->GetResponseCode());
EXPECT_FALSE(test_helper()->response_body());
} }
TEST_F(SimpleURLLoaderTest, UnexpectedBodyBufferReceived) { TEST_P(SimpleURLLoaderTest, UnexpectedBodyBufferReceived) {
MockURLLoaderFactory loader_factory(&scoped_task_environment_); MockURLLoaderFactory loader_factory(&scoped_task_environment_);
loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived); loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived);
WaitForStringHelper string_helper; test_helper()->RunRequestForURL(&loader_factory, GURL("foo://bar/"));
string_helper.RunRequestForURL(&loader_factory, GURL("foo://bar/"));
EXPECT_EQ(net::ERR_UNEXPECTED, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::ERR_UNEXPECTED,
EXPECT_FALSE(string_helper.simple_url_loader()->ResponseInfo()); test_helper()->simple_url_loader()->NetError());
EXPECT_FALSE(string_helper.response_body()); EXPECT_FALSE(test_helper()->simple_url_loader()->ResponseInfo());
EXPECT_FALSE(test_helper()->response_body());
} }
TEST_F(SimpleURLLoaderTest, DoubleBodyBufferReceived) { TEST_P(SimpleURLLoaderTest, DoubleBodyBufferReceived) {
MockURLLoaderFactory loader_factory(&scoped_task_environment_); MockURLLoaderFactory loader_factory(&scoped_task_environment_);
loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse); loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse);
loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived); loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived);
loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived); loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived);
WaitForStringHelper string_helper; test_helper()->RunRequestForURL(&loader_factory, GURL("foo://bar/"));
string_helper.RunRequestForURL(&loader_factory, GURL("foo://bar/"));
EXPECT_EQ(net::ERR_UNEXPECTED, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::ERR_UNEXPECTED,
EXPECT_EQ(200, string_helper.GetResponseCode()); test_helper()->simple_url_loader()->NetError());
EXPECT_FALSE(string_helper.response_body()); EXPECT_EQ(200, test_helper()->GetResponseCode());
EXPECT_FALSE(test_helper()->response_body());
} }
TEST_F(SimpleURLLoaderTest, UnexpectedMessageAfterBodyStarts) { TEST_P(SimpleURLLoaderTest, UnexpectedMessageAfterBodyStarts) {
MockURLLoaderFactory loader_factory(&scoped_task_environment_); MockURLLoaderFactory loader_factory(&scoped_task_environment_);
loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse); loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse);
loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived); loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived);
loader_factory.AddEvent(TestLoaderEvent::kBodyDataRead); loader_factory.AddEvent(TestLoaderEvent::kBodyDataRead);
loader_factory.AddEvent(TestLoaderEvent::kReceivedRedirect); loader_factory.AddEvent(TestLoaderEvent::kReceivedRedirect);
WaitForStringHelper string_helper; test_helper()->RunRequestForURL(&loader_factory, GURL("foo://bar/"));
string_helper.RunRequestForURL(&loader_factory, GURL("foo://bar/"));
EXPECT_EQ(net::ERR_UNEXPECTED, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::ERR_UNEXPECTED,
EXPECT_EQ(200, string_helper.GetResponseCode()); test_helper()->simple_url_loader()->NetError());
EXPECT_FALSE(string_helper.response_body()); EXPECT_EQ(200, test_helper()->GetResponseCode());
EXPECT_FALSE(test_helper()->response_body());
} }
TEST_F(SimpleURLLoaderTest, UnexpectedMessageAfterBodyStarts2) { TEST_P(SimpleURLLoaderTest, UnexpectedMessageAfterBodyStarts2) {
MockURLLoaderFactory loader_factory(&scoped_task_environment_); MockURLLoaderFactory loader_factory(&scoped_task_environment_);
loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse); loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse);
loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived); loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived);
loader_factory.AddEvent(TestLoaderEvent::kBodyDataRead); loader_factory.AddEvent(TestLoaderEvent::kBodyDataRead);
loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived); loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived);
WaitForStringHelper string_helper; test_helper()->RunRequestForURL(&loader_factory, GURL("foo://bar/"));
string_helper.RunRequestForURL(&loader_factory, GURL("foo://bar/"));
EXPECT_EQ(net::ERR_UNEXPECTED, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::ERR_UNEXPECTED,
EXPECT_EQ(200, string_helper.GetResponseCode()); test_helper()->simple_url_loader()->NetError());
EXPECT_FALSE(string_helper.response_body()); EXPECT_EQ(200, test_helper()->GetResponseCode());
EXPECT_FALSE(test_helper()->response_body());
} }
TEST_F(SimpleURLLoaderTest, UnexpectedMessageAfterBodyComplete) { TEST_P(SimpleURLLoaderTest, UnexpectedMessageAfterBodyComplete) {
MockURLLoaderFactory loader_factory(&scoped_task_environment_); MockURLLoaderFactory loader_factory(&scoped_task_environment_);
loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse); loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse);
loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived); loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived);
loader_factory.AddEvent(TestLoaderEvent::kBodyDataRead); loader_factory.AddEvent(TestLoaderEvent::kBodyDataRead);
loader_factory.AddEvent(TestLoaderEvent::kBodyBufferClosed); loader_factory.AddEvent(TestLoaderEvent::kBodyBufferClosed);
loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived); loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived);
WaitForStringHelper string_helper; test_helper()->RunRequestForURL(&loader_factory, GURL("foo://bar/"));
string_helper.RunRequestForURL(&loader_factory, GURL("foo://bar/"));
EXPECT_EQ(net::ERR_UNEXPECTED, string_helper.simple_url_loader()->NetError()); EXPECT_EQ(net::ERR_UNEXPECTED,
EXPECT_EQ(200, string_helper.GetResponseCode()); test_helper()->simple_url_loader()->NetError());
EXPECT_FALSE(string_helper.response_body()); EXPECT_EQ(200, test_helper()->GetResponseCode());
EXPECT_FALSE(test_helper()->response_body());
} }
TEST_F(SimpleURLLoaderTest, MoreDataThanExpected) { TEST_P(SimpleURLLoaderTest, MoreDataThanExpected) {
MockURLLoaderFactory loader_factory(&scoped_task_environment_); MockURLLoaderFactory loader_factory(&scoped_task_environment_);
loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse); loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse);
loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived); loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived);
...@@ -1046,12 +1143,114 @@ TEST_F(SimpleURLLoaderTest, MoreDataThanExpected) { ...@@ -1046,12 +1143,114 @@ TEST_F(SimpleURLLoaderTest, MoreDataThanExpected) {
loader_factory.AddEvent(TestLoaderEvent::kBodyDataRead); loader_factory.AddEvent(TestLoaderEvent::kBodyDataRead);
loader_factory.AddEvent(TestLoaderEvent::kBodyBufferClosed); loader_factory.AddEvent(TestLoaderEvent::kBodyBufferClosed);
loader_factory.AddEvent(TestLoaderEvent::kResponseCompleteWithExtraData); loader_factory.AddEvent(TestLoaderEvent::kResponseCompleteWithExtraData);
WaitForStringHelper string_helper; test_helper()->RunRequestForURL(&loader_factory, GURL("foo://bar/"));
string_helper.RunRequestForURL(&loader_factory, GURL("foo://bar/"));
EXPECT_EQ(net::ERR_UNEXPECTED,
test_helper()->simple_url_loader()->NetError());
EXPECT_EQ(200, test_helper()->GetResponseCode());
EXPECT_FALSE(test_helper()->response_body());
}
INSTANTIATE_TEST_CASE_P(
/* No prefix */,
SimpleURLLoaderTest,
testing::Values(SimpleLoaderTestHelper::DownloadType::TO_STRING,
SimpleLoaderTestHelper::DownloadType::TO_FILE));
class SimpleURLLoaderFileTest : public SimpleURLLoaderTestBase,
public testing::Test {
public:
SimpleURLLoaderFileTest()
: test_helper_(SimpleLoaderTestHelper::DownloadType::TO_FILE) {}
~SimpleURLLoaderFileTest() override {}
SimpleLoaderTestHelper* test_helper() { return &test_helper_; }
EXPECT_EQ(net::ERR_UNEXPECTED, string_helper.simple_url_loader()->NetError()); private:
EXPECT_EQ(200, string_helper.GetResponseCode()); SimpleLoaderTestHelper test_helper_;
EXPECT_FALSE(string_helper.response_body()); };
// Make sure that an existing file will be completely overwritten.
TEST_F(SimpleURLLoaderFileTest, OverwriteFile) {
std::string junk_data(100, '!');
ASSERT_EQ(static_cast<int>(junk_data.size()),
base::WriteFile(test_helper()->dest_path(), junk_data.data(),
junk_data.size()));
ASSERT_TRUE(base::PathExists(test_helper()->dest_path()));
ResourceRequest resource_request;
resource_request.url = GURL("data:text/plain,foo");
test_helper()->RunRequest(url_loader_factory_.get(), resource_request);
EXPECT_EQ(net::OK, test_helper()->simple_url_loader()->NetError());
ASSERT_TRUE(test_helper()->simple_url_loader()->ResponseInfo());
ASSERT_TRUE(test_helper()->response_body());
EXPECT_EQ("foo", *test_helper()->response_body());
}
// Make sure that file creation errors are handled correctly.
TEST_F(SimpleURLLoaderFileTest, FileCreateError) {
ASSERT_TRUE(base::CreateDirectory(test_helper()->dest_path()));
ASSERT_TRUE(base::PathExists(test_helper()->dest_path()));
ResourceRequest resource_request;
resource_request.url = GURL("data:text/plain,foo");
// The directory should still exist after the download fails.
test_helper()->set_expect_path_exists_on_error(true);
test_helper()->RunRequest(url_loader_factory_.get(), resource_request);
EXPECT_EQ(net::ERR_ACCESS_DENIED,
test_helper()->simple_url_loader()->NetError());
EXPECT_TRUE(test_helper()->simple_url_loader()->ResponseInfo());
EXPECT_FALSE(test_helper()->response_body());
}
// Make sure that destroying the loader destroys a partially downloaded file.
TEST_F(SimpleURLLoaderFileTest, DeleteLoaderDuringRequestDestroysFile) {
for (bool body_data_read : {false, true}) {
for (bool body_buffer_closed : {false, true}) {
for (bool client_pipe_closed : {false, true}) {
// If both pipes were closed cleanly, the file shouldn't be deleted, as
// that indicates success.
if (body_buffer_closed && client_pipe_closed)
continue;
MockURLLoaderFactory loader_factory(&scoped_task_environment_);
loader_factory.AddEvent(TestLoaderEvent::kReceivedResponse);
loader_factory.AddEvent(TestLoaderEvent::kBodyBufferReceived);
if (body_data_read)
loader_factory.AddEvent(TestLoaderEvent::kBodyDataRead);
if (body_buffer_closed)
loader_factory.AddEvent(TestLoaderEvent::kBodyBufferClosed);
if (client_pipe_closed)
loader_factory.AddEvent(TestLoaderEvent::kClientPipeClosed);
// The request just hangs after receiving some body data.
ResourceRequest resource_request;
resource_request.url = GURL("foo://bar/");
std::unique_ptr<SimpleLoaderTestHelper> test_helper =
std::make_unique<SimpleLoaderTestHelper>(
SimpleLoaderTestHelper::DownloadType::TO_FILE);
test_helper->StartRequest(&loader_factory, resource_request);
// Wait for the request to advance as far as it's going to.
scoped_task_environment_.RunUntilIdle();
// Destination file should have been created, and request should still
// be in progress.
base::FilePath dest_path = test_helper->dest_path();
EXPECT_TRUE(base::PathExists(dest_path));
EXPECT_FALSE(test_helper->done());
// Destroying the SimpleURLLoader now should post a task to destroy the
// file.
test_helper.reset();
scoped_task_environment_.RunUntilIdle();
EXPECT_FALSE(base::PathExists(dest_path));
}
}
}
} }
} // namespace } // namespace
......
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