blob: ea3d9759b2dda5414649159fec3cdb0bd578f01e [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/common/throttling_url_loader.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "content/public/common/url_loader.mojom.h"
#include "content/public/common/url_loader_factory.mojom.h"
#include "content/public/common/url_loader_throttle.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
class TestURLLoaderFactory : public mojom::URLLoaderFactory {
public:
TestURLLoaderFactory() : binding_(this) {
binding_.Bind(mojo::MakeRequest(&factory_ptr_));
}
mojom::URLLoaderFactoryPtr& factory_ptr() { return factory_ptr_; }
mojom::URLLoaderClientPtr& client_ptr() { return client_ptr_; }
size_t create_loader_and_start_called() const {
return create_loader_and_start_called_;
}
void NotifyClientOnReceiveResponse() {
client_ptr_->OnReceiveResponse(ResourceResponseHead(), base::nullopt,
nullptr);
}
void NotifyClientOnReceiveRedirect() {
client_ptr_->OnReceiveRedirect(net::RedirectInfo(), ResourceResponseHead());
}
void NotifyClientOnComplete(int error_code) {
ResourceRequestCompletionStatus data;
data.error_code = error_code;
client_ptr_->OnComplete(data);
}
private:
// mojom::URLLoaderFactory implementation.
void CreateLoaderAndStart(mojom::URLLoaderRequest request,
int32_t routing_id,
int32_t request_id,
uint32_t options,
const ResourceRequest& url_request,
mojom::URLLoaderClientPtr client,
const net::MutableNetworkTrafficAnnotationTag&
traffic_annotation) override {
create_loader_and_start_called_++;
client_ptr_ = std::move(client);
}
void SyncLoad(int32_t routing_id,
int32_t request_id,
const ResourceRequest& request,
SyncLoadCallback callback) override {
NOTREACHED();
}
size_t create_loader_and_start_called_ = 0;
mojo::Binding<mojom::URLLoaderFactory> binding_;
mojom::URLLoaderFactoryPtr factory_ptr_;
mojom::URLLoaderClientPtr client_ptr_;
DISALLOW_COPY_AND_ASSIGN(TestURLLoaderFactory);
};
class TestURLLoaderClient : public mojom::URLLoaderClient {
public:
TestURLLoaderClient() {}
size_t on_received_response_called() const {
return on_received_response_called_;
}
size_t on_received_redirect_called() const {
return on_received_redirect_called_;
}
size_t on_complete_called() const { return on_complete_called_; }
using OnCompleteCallback = base::Callback<void(int error_code)>;
void set_on_complete_callback(const OnCompleteCallback& callback) {
on_complete_callback_ = callback;
}
private:
// mojom::URLLoaderClient implementation:
void OnReceiveResponse(
const ResourceResponseHead& response_head,
const base::Optional<net::SSLInfo>& ssl_info,
mojom::DownloadedTempFilePtr downloaded_file) override {
on_received_response_called_++;
}
void OnReceiveRedirect(const net::RedirectInfo& redirect_info,
const ResourceResponseHead& response_head) override {
on_received_redirect_called_++;
}
void OnDataDownloaded(int64_t data_len, int64_t encoded_data_len) override {}
void OnUploadProgress(int64_t current_position,
int64_t total_size,
OnUploadProgressCallback ack_callback) override {}
void OnReceiveCachedMetadata(const std::vector<uint8_t>& data) override {}
void OnTransferSizeUpdated(int32_t transfer_size_diff) override {}
void OnStartLoadingResponseBody(
mojo::ScopedDataPipeConsumerHandle body) override {}
void OnComplete(const ResourceRequestCompletionStatus& status) override {
on_complete_called_++;
if (on_complete_callback_)
on_complete_callback_.Run(status.error_code);
}
size_t on_received_response_called_ = 0;
size_t on_received_redirect_called_ = 0;
size_t on_complete_called_ = 0;
OnCompleteCallback on_complete_callback_;
DISALLOW_COPY_AND_ASSIGN(TestURLLoaderClient);
};
class TestURLLoaderThrottle : public URLLoaderThrottle {
public:
TestURLLoaderThrottle() {}
using ThrottleCallback =
base::Callback<void(URLLoaderThrottle::Delegate* delegate, bool* defer)>;
size_t will_start_request_called() const {
return will_start_request_called_;
}
size_t will_redirect_request_called() const {
return will_redirect_request_called_;
}
size_t will_process_response_called() const {
return will_process_response_called_;
}
void set_will_start_request_callback(const ThrottleCallback& callback) {
will_start_request_callback_ = callback;
}
void set_will_redirect_request_callback(const ThrottleCallback& callback) {
will_redirect_request_callback_ = callback;
}
void set_will_process_response_callback(const ThrottleCallback& callback) {
will_process_response_callback_ = callback;
}
Delegate* delegate() const { return delegate_; }
private:
// URLLoaderThrottle implementation.
void WillStartRequest(const ResourceRequest& request, bool* defer) override {
will_start_request_called_++;
if (will_start_request_callback_)
will_start_request_callback_.Run(delegate_, defer);
}
void WillRedirectRequest(const net::RedirectInfo& redirect_info,
bool* defer) override {
will_redirect_request_called_++;
if (will_redirect_request_callback_)
will_redirect_request_callback_.Run(delegate_, defer);
}
void WillProcessResponse(bool* defer) override {
will_process_response_called_++;
if (will_process_response_callback_)
will_process_response_callback_.Run(delegate_, defer);
}
size_t will_start_request_called_ = 0;
size_t will_redirect_request_called_ = 0;
size_t will_process_response_called_ = 0;
ThrottleCallback will_start_request_callback_;
ThrottleCallback will_redirect_request_callback_;
ThrottleCallback will_process_response_callback_;
DISALLOW_COPY_AND_ASSIGN(TestURLLoaderThrottle);
};
class ThrottlingURLLoaderTest : public testing::Test {
public:
ThrottlingURLLoaderTest() {}
protected:
// testing::Test implementation.
void SetUp() override {
auto throttle = base::MakeUnique<TestURLLoaderThrottle>();
throttle_ = throttle.get();
throttles_.push_back(std::move(throttle));
}
void CreateLoaderAndStart() {
ResourceRequest request;
request.url = GURL("http://example.org");
loader_ = ThrottlingURLLoader::CreateLoaderAndStart(
factory_.factory_ptr().get(), std::move(throttles_), 0, 0, 0, request,
&client_, TRAFFIC_ANNOTATION_FOR_TESTS);
factory_.factory_ptr().FlushForTesting();
}
// Be the first member so it is destroyed last.
base::MessageLoop message_loop_;
std::unique_ptr<ThrottlingURLLoader> loader_;
std::vector<std::unique_ptr<URLLoaderThrottle>> throttles_;
TestURLLoaderFactory factory_;
TestURLLoaderClient client_;
// Owned by |throttles_| or |loader_|.
TestURLLoaderThrottle* throttle_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(ThrottlingURLLoaderTest);
};
TEST_F(ThrottlingURLLoaderTest, CancelBeforeStart) {
throttle_->set_will_start_request_callback(
base::Bind([](URLLoaderThrottle::Delegate* delegate, bool* defer) {
delegate->CancelWithError(net::ERR_ACCESS_DENIED);
}));
base::RunLoop run_loop;
client_.set_on_complete_callback(base::Bind(
[](const base::Closure& quit_closure, int error) {
EXPECT_EQ(net::ERR_ACCESS_DENIED, error);
quit_closure.Run();
},
run_loop.QuitClosure()));
CreateLoaderAndStart();
run_loop.Run();
EXPECT_EQ(1u, throttle_->will_start_request_called());
EXPECT_EQ(0u, throttle_->will_redirect_request_called());
EXPECT_EQ(0u, throttle_->will_process_response_called());
EXPECT_EQ(0u, factory_.create_loader_and_start_called());
EXPECT_EQ(0u, client_.on_received_response_called());
EXPECT_EQ(0u, client_.on_received_redirect_called());
EXPECT_EQ(1u, client_.on_complete_called());
}
TEST_F(ThrottlingURLLoaderTest, DeferBeforeStart) {
throttle_->set_will_start_request_callback(
base::Bind([](URLLoaderThrottle::Delegate* delegate, bool* defer) {
*defer = true;
}));
base::RunLoop run_loop;
client_.set_on_complete_callback(base::Bind(
[](const base::Closure& quit_closure, int error) {
EXPECT_EQ(net::OK, error);
quit_closure.Run();
},
run_loop.QuitClosure()));
CreateLoaderAndStart();
EXPECT_EQ(1u, throttle_->will_start_request_called());
EXPECT_EQ(0u, throttle_->will_redirect_request_called());
EXPECT_EQ(0u, throttle_->will_process_response_called());
EXPECT_EQ(0u, factory_.create_loader_and_start_called());
EXPECT_EQ(0u, client_.on_received_response_called());
EXPECT_EQ(0u, client_.on_received_redirect_called());
EXPECT_EQ(0u, client_.on_complete_called());
throttle_->delegate()->Resume();
factory_.factory_ptr().FlushForTesting();
EXPECT_EQ(1u, factory_.create_loader_and_start_called());
factory_.NotifyClientOnReceiveResponse();
factory_.NotifyClientOnComplete(net::OK);
run_loop.Run();
EXPECT_EQ(1u, throttle_->will_start_request_called());
EXPECT_EQ(0u, throttle_->will_redirect_request_called());
EXPECT_EQ(1u, throttle_->will_process_response_called());
EXPECT_EQ(1u, client_.on_received_response_called());
EXPECT_EQ(0u, client_.on_received_redirect_called());
EXPECT_EQ(1u, client_.on_complete_called());
}
TEST_F(ThrottlingURLLoaderTest, CancelBeforeRedirect) {
throttle_->set_will_redirect_request_callback(
base::Bind([](URLLoaderThrottle::Delegate* delegate, bool* defer) {
delegate->CancelWithError(net::ERR_ACCESS_DENIED);
}));
base::RunLoop run_loop;
client_.set_on_complete_callback(base::Bind(
[](const base::Closure& quit_closure, int error) {
EXPECT_EQ(net::ERR_ACCESS_DENIED, error);
quit_closure.Run();
},
run_loop.QuitClosure()));
CreateLoaderAndStart();
factory_.NotifyClientOnReceiveRedirect();
run_loop.Run();
EXPECT_EQ(1u, throttle_->will_start_request_called());
EXPECT_EQ(1u, throttle_->will_redirect_request_called());
EXPECT_EQ(0u, throttle_->will_process_response_called());
EXPECT_EQ(0u, client_.on_received_response_called());
EXPECT_EQ(0u, client_.on_received_redirect_called());
EXPECT_EQ(1u, client_.on_complete_called());
}
TEST_F(ThrottlingURLLoaderTest, DeferBeforeRedirect) {
base::RunLoop run_loop1;
throttle_->set_will_redirect_request_callback(base::Bind(
[](const base::Closure& quit_closure,
URLLoaderThrottle::Delegate* delegate, bool* defer) {
*defer = true;
quit_closure.Run();
},
run_loop1.QuitClosure()));
base::RunLoop run_loop2;
client_.set_on_complete_callback(base::Bind(
[](const base::Closure& quit_closure, int error) {
EXPECT_EQ(net::ERR_UNEXPECTED, error);
quit_closure.Run();
},
run_loop2.QuitClosure()));
CreateLoaderAndStart();
factory_.NotifyClientOnReceiveRedirect();
run_loop1.Run();
EXPECT_EQ(1u, throttle_->will_start_request_called());
EXPECT_EQ(1u, throttle_->will_redirect_request_called());
EXPECT_EQ(0u, throttle_->will_process_response_called());
factory_.NotifyClientOnComplete(net::ERR_UNEXPECTED);
base::RunLoop run_loop3;
run_loop3.RunUntilIdle();
EXPECT_EQ(0u, client_.on_received_response_called());
EXPECT_EQ(0u, client_.on_received_redirect_called());
EXPECT_EQ(0u, client_.on_complete_called());
throttle_->delegate()->Resume();
run_loop2.Run();
EXPECT_EQ(1u, throttle_->will_start_request_called());
EXPECT_EQ(1u, throttle_->will_redirect_request_called());
EXPECT_EQ(0u, throttle_->will_process_response_called());
EXPECT_EQ(0u, client_.on_received_response_called());
EXPECT_EQ(1u, client_.on_received_redirect_called());
EXPECT_EQ(1u, client_.on_complete_called());
}
TEST_F(ThrottlingURLLoaderTest, CancelBeforeResponse) {
throttle_->set_will_process_response_callback(
base::Bind([](URLLoaderThrottle::Delegate* delegate, bool* defer) {
delegate->CancelWithError(net::ERR_ACCESS_DENIED);
}));
base::RunLoop run_loop;
client_.set_on_complete_callback(base::Bind(
[](const base::Closure& quit_closure, int error) {
EXPECT_EQ(net::ERR_ACCESS_DENIED, error);
quit_closure.Run();
},
run_loop.QuitClosure()));
CreateLoaderAndStart();
factory_.NotifyClientOnReceiveResponse();
run_loop.Run();
EXPECT_EQ(1u, throttle_->will_start_request_called());
EXPECT_EQ(0u, throttle_->will_redirect_request_called());
EXPECT_EQ(1u, throttle_->will_process_response_called());
EXPECT_EQ(0u, client_.on_received_response_called());
EXPECT_EQ(0u, client_.on_received_redirect_called());
EXPECT_EQ(1u, client_.on_complete_called());
}
TEST_F(ThrottlingURLLoaderTest, DeferBeforeResponse) {
base::RunLoop run_loop1;
throttle_->set_will_process_response_callback(base::Bind(
[](const base::Closure& quit_closure,
URLLoaderThrottle::Delegate* delegate, bool* defer) {
*defer = true;
quit_closure.Run();
},
run_loop1.QuitClosure()));
base::RunLoop run_loop2;
client_.set_on_complete_callback(base::Bind(
[](const base::Closure& quit_closure, int error) {
EXPECT_EQ(net::ERR_UNEXPECTED, error);
quit_closure.Run();
},
run_loop2.QuitClosure()));
CreateLoaderAndStart();
factory_.NotifyClientOnReceiveResponse();
run_loop1.Run();
EXPECT_EQ(1u, throttle_->will_start_request_called());
EXPECT_EQ(0u, throttle_->will_redirect_request_called());
EXPECT_EQ(1u, throttle_->will_process_response_called());
factory_.NotifyClientOnComplete(net::ERR_UNEXPECTED);
base::RunLoop run_loop3;
run_loop3.RunUntilIdle();
EXPECT_EQ(0u, client_.on_received_response_called());
EXPECT_EQ(0u, client_.on_received_redirect_called());
EXPECT_EQ(0u, client_.on_complete_called());
throttle_->delegate()->Resume();
run_loop2.Run();
EXPECT_EQ(1u, throttle_->will_start_request_called());
EXPECT_EQ(0u, throttle_->will_redirect_request_called());
EXPECT_EQ(1u, throttle_->will_process_response_called());
EXPECT_EQ(1u, client_.on_received_response_called());
EXPECT_EQ(0u, client_.on_received_redirect_called());
EXPECT_EQ(1u, client_.on_complete_called());
}
TEST_F(ThrottlingURLLoaderTest, ResumeNoOpIfNotDeferred) {
auto resume_callback =
base::Bind([](URLLoaderThrottle::Delegate* delegate, bool* defer) {
delegate->Resume();
delegate->Resume();
});
throttle_->set_will_start_request_callback(resume_callback);
throttle_->set_will_redirect_request_callback(resume_callback);
throttle_->set_will_process_response_callback(resume_callback);
base::RunLoop run_loop;
client_.set_on_complete_callback(base::Bind(
[](const base::Closure& quit_closure, int error) {
EXPECT_EQ(net::OK, error);
quit_closure.Run();
},
run_loop.QuitClosure()));
CreateLoaderAndStart();
factory_.NotifyClientOnReceiveRedirect();
factory_.NotifyClientOnReceiveResponse();
factory_.NotifyClientOnComplete(net::OK);
run_loop.Run();
EXPECT_EQ(1u, throttle_->will_start_request_called());
EXPECT_EQ(1u, throttle_->will_redirect_request_called());
EXPECT_EQ(1u, throttle_->will_process_response_called());
EXPECT_EQ(1u, client_.on_received_response_called());
EXPECT_EQ(1u, client_.on_received_redirect_called());
EXPECT_EQ(1u, client_.on_complete_called());
}
TEST_F(ThrottlingURLLoaderTest, CancelNoOpIfAlreadyCanceled) {
throttle_->set_will_start_request_callback(
base::Bind([](URLLoaderThrottle::Delegate* delegate, bool* defer) {
delegate->CancelWithError(net::ERR_ACCESS_DENIED);
delegate->CancelWithError(net::ERR_UNEXPECTED);
}));
base::RunLoop run_loop;
client_.set_on_complete_callback(base::Bind(
[](const base::Closure& quit_closure, int error) {
EXPECT_EQ(net::ERR_ACCESS_DENIED, error);
quit_closure.Run();
},
run_loop.QuitClosure()));
CreateLoaderAndStart();
throttle_->delegate()->CancelWithError(net::ERR_INVALID_ARGUMENT);
run_loop.Run();
EXPECT_EQ(1u, throttle_->will_start_request_called());
EXPECT_EQ(0u, throttle_->will_redirect_request_called());
EXPECT_EQ(0u, throttle_->will_process_response_called());
EXPECT_EQ(0u, factory_.create_loader_and_start_called());
EXPECT_EQ(0u, client_.on_received_response_called());
EXPECT_EQ(0u, client_.on_received_redirect_called());
EXPECT_EQ(1u, client_.on_complete_called());
}
TEST_F(ThrottlingURLLoaderTest, ResumeNoOpIfAlreadyCanceled) {
throttle_->set_will_process_response_callback(
base::Bind([](URLLoaderThrottle::Delegate* delegate, bool* defer) {
delegate->CancelWithError(net::ERR_ACCESS_DENIED);
delegate->Resume();
}));
base::RunLoop run_loop1;
client_.set_on_complete_callback(base::Bind(
[](const base::Closure& quit_closure, int error) {
EXPECT_EQ(net::ERR_ACCESS_DENIED, error);
quit_closure.Run();
},
run_loop1.QuitClosure()));
CreateLoaderAndStart();
factory_.NotifyClientOnReceiveResponse();
run_loop1.Run();
throttle_->delegate()->Resume();
base::RunLoop run_loop2;
run_loop2.RunUntilIdle();
EXPECT_EQ(1u, throttle_->will_start_request_called());
EXPECT_EQ(0u, throttle_->will_redirect_request_called());
EXPECT_EQ(1u, throttle_->will_process_response_called());
EXPECT_EQ(0u, client_.on_received_response_called());
EXPECT_EQ(0u, client_.on_received_redirect_called());
EXPECT_EQ(1u, client_.on_complete_called());
}
} // namespace
} // namespace content