blob: 2b9ec1ba870666c41a3351f63ebf8ceca2f83682 [file] [log] [blame]
// Copyright (c) 2013 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/browser/loader/resource_loader.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <utility>
#include <vector>
#include "base/containers/circular_deque.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/statistics_recorder.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/browser/loader/resource_loader_delegate.h"
#include "content/browser/loader/test_resource_handler.h"
#include "content/public/browser/client_certificate_delegate.h"
#include "content/public/browser/login_delegate.h"
#include "content/public/browser/resource_request_info.h"
#include "content/public/common/content_paths.h"
#include "content/public/common/resource_type.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_renderer_host.h"
#include "content/test/test_content_browser_client.h"
#include "content/test/test_web_contents.h"
#include "net/base/chunked_upload_data_stream.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/request_priority.h"
#include "net/base/upload_bytes_element_reader.h"
#include "net/cert/x509_certificate.h"
#include "net/nqe/effective_connection_type.h"
#include "net/nqe/network_quality_estimator_test_util.h"
#include "net/ssl/client_cert_identity_test_util.h"
#include "net/ssl/client_cert_store.h"
#include "net/ssl/ssl_cert_request_info.h"
#include "net/ssl/ssl_private_key.h"
#include "net/test/cert_test_util.h"
#include "net/test/embedded_test_server/controllable_http_response.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/test_data_directory.h"
#include "net/test/url_request/url_request_failed_job.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_filter.h"
#include "net/url_request/url_request_interceptor.h"
#include "net/url_request/url_request_job_factory.h"
#include "net/url_request/url_request_job_factory_impl.h"
#include "net/url_request/url_request_test_job.h"
#include "net/url_request/url_request_test_util.h"
#include "services/network/public/cpp/resource_response.h"
#include "services/network/throttling/scoped_throttling_token.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
constexpr char kBodyReadFromNetBeforePausedHistogram[] =
"Network.URLLoader.BodyReadFromNetBeforePaused";
// Stub client certificate store that returns a preset list of certificates for
// each request and records the arguments of the most recent request for later
// inspection.
class ClientCertStoreStub : public net::ClientCertStore {
public:
// Creates a new ClientCertStoreStub that returns |response| on query. It
// saves the number of requests and most recently certificate authorities list
// in |requested_authorities| and |request_count|, respectively. The caller is
// responsible for ensuring those pointers outlive the ClientCertStoreStub.
//
// TODO(ppi): Make the stub independent from the internal representation of
// SSLCertRequestInfo. For now it seems that we can neither save the
// scoped_refptr<> (since it is never passed to us) nor copy the entire
// CertificateRequestInfo (since there is no copy constructor).
ClientCertStoreStub(const net::CertificateList& response,
int* request_count,
std::vector<std::string>* requested_authorities)
: response_(std::move(response)),
requested_authorities_(requested_authorities),
request_count_(request_count) {
requested_authorities_->clear();
*request_count_ = 0;
}
~ClientCertStoreStub() override {}
// net::ClientCertStore:
void GetClientCerts(const net::SSLCertRequestInfo& cert_request_info,
const ClientCertListCallback& callback) override {
*requested_authorities_ = cert_request_info.cert_authorities;
++(*request_count_);
callback.Run(net::FakeClientCertIdentityListFromCertificateList(response_));
}
private:
const net::CertificateList response_;
std::vector<std::string>* requested_authorities_;
int* request_count_;
};
// Client certificate store which destroys its resource loader before the
// asynchronous GetClientCerts callback is called.
class LoaderDestroyingCertStore : public net::ClientCertStore {
public:
// Creates a client certificate store which, when looked up, posts a task to
// reset |loader| and then call the callback. The caller is responsible for
// ensuring the pointers remain valid until the process is complete.
LoaderDestroyingCertStore(std::unique_ptr<ResourceLoader>* loader,
const base::Closure& on_loader_deleted_callback)
: loader_(loader),
on_loader_deleted_callback_(on_loader_deleted_callback) {}
// net::ClientCertStore:
void GetClientCerts(
const net::SSLCertRequestInfo& cert_request_info,
const ClientCertListCallback& cert_selected_callback) override {
// Don't destroy |loader_| while it's on the stack.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&LoaderDestroyingCertStore::DoCallback,
base::Unretained(loader_), cert_selected_callback,
on_loader_deleted_callback_));
}
private:
// This needs to be static because |loader| owns the
// LoaderDestroyingCertStore (ClientCertStores are actually handles, and not
// global cert stores).
static void DoCallback(std::unique_ptr<ResourceLoader>* loader,
const ClientCertListCallback& cert_selected_callback,
const base::Closure& on_loader_deleted_callback) {
loader->reset();
cert_selected_callback.Run(net::ClientCertIdentityList());
on_loader_deleted_callback.Run();
}
std::unique_ptr<ResourceLoader>* loader_;
base::Closure on_loader_deleted_callback_;
DISALLOW_COPY_AND_ASSIGN(LoaderDestroyingCertStore);
};
// A mock URLRequestJob which simulates an SSL client auth request.
class MockClientCertURLRequestJob : public net::URLRequestTestJob {
public:
MockClientCertURLRequestJob(net::URLRequest* request,
net::NetworkDelegate* network_delegate)
: net::URLRequestTestJob(request, network_delegate),
weak_factory_(this) {}
static std::vector<std::string> test_authorities() {
return std::vector<std::string>(1, "dummy");
}
// net::URLRequestTestJob:
void Start() override {
scoped_refptr<net::SSLCertRequestInfo> cert_request_info(
new net::SSLCertRequestInfo);
cert_request_info->cert_authorities = test_authorities();
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&MockClientCertURLRequestJob::NotifyCertificateRequested,
weak_factory_.GetWeakPtr(),
base::RetainedRef(cert_request_info)));
}
void ContinueWithCertificate(
scoped_refptr<net::X509Certificate> cert,
scoped_refptr<net::SSLPrivateKey> private_key) override {
net::URLRequestTestJob::Start();
}
private:
~MockClientCertURLRequestJob() override {}
base::WeakPtrFactory<MockClientCertURLRequestJob> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(MockClientCertURLRequestJob);
};
class MockClientCertJobProtocolHandler
: public net::URLRequestJobFactory::ProtocolHandler {
public:
// URLRequestJobFactory::ProtocolHandler implementation:
net::URLRequestJob* MaybeCreateJob(
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const override {
return new MockClientCertURLRequestJob(request, network_delegate);
}
};
// Set up dummy values to use in test HTTPS requests.
const net::CertStatus kTestCertError = net::CERT_STATUS_DATE_INVALID;
// SSL3 TLS_DHE_RSA_WITH_AES_256_CBC_SHA
const int kTestConnectionStatus = 0x300039;
// A mock URLRequestJob which simulates an HTTPS request.
class MockHTTPSURLRequestJob : public net::URLRequestTestJob {
public:
MockHTTPSURLRequestJob(net::URLRequest* request,
net::NetworkDelegate* network_delegate,
const std::string& response_headers,
const std::string& response_data,
bool auto_advance)
: net::URLRequestTestJob(request,
network_delegate,
response_headers,
response_data,
auto_advance) {}
// net::URLRequestTestJob:
void GetResponseInfo(net::HttpResponseInfo* info) override {
// Get the original response info, but override the SSL info.
net::URLRequestJob::GetResponseInfo(info);
info->ssl_info.cert =
net::ImportCertFromFile(net::GetTestCertsDirectory(), "ok_cert.pem");
info->ssl_info.cert_status = kTestCertError;
info->ssl_info.connection_status = kTestConnectionStatus;
}
private:
~MockHTTPSURLRequestJob() override {}
DISALLOW_COPY_AND_ASSIGN(MockHTTPSURLRequestJob);
};
const char kRedirectHeaders[] =
"HTTP/1.1 302 Found\n"
"Location: https://example.test\n"
"\n";
class MockHTTPSJobURLRequestInterceptor : public net::URLRequestInterceptor {
public:
explicit MockHTTPSJobURLRequestInterceptor(bool redirect)
: redirect_(redirect) {}
~MockHTTPSJobURLRequestInterceptor() override {}
// net::URLRequestInterceptor:
net::URLRequestJob* MaybeInterceptRequest(
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const override {
std::string headers =
redirect_ ? std::string(kRedirectHeaders, base::size(kRedirectHeaders))
: net::URLRequestTestJob::test_headers();
return new MockHTTPSURLRequestJob(request, network_delegate, headers,
"dummy response", true);
}
private:
bool redirect_;
};
// Test browser client that captures calls to SelectClientCertificates and
// records the arguments of the most recent call for later inspection.
class SelectCertificateBrowserClient : public TestContentBrowserClient {
public:
SelectCertificateBrowserClient() : call_count_(0) {}
// Waits until the first call to SelectClientCertificate.
void WaitForSelectCertificate() {
select_certificate_run_loop_.Run();
// Process any pending messages - just so tests can check if
// SelectClientCertificate was called more than once.
base::RunLoop().RunUntilIdle();
}
void SelectClientCertificate(
WebContents* web_contents,
net::SSLCertRequestInfo* cert_request_info,
net::ClientCertIdentityList client_certs,
std::unique_ptr<ClientCertificateDelegate> delegate) override {
EXPECT_FALSE(delegate_.get());
++call_count_;
passed_identities_ = std::move(client_certs);
delegate_ = std::move(delegate);
select_certificate_run_loop_.Quit();
}
std::unique_ptr<net::ClientCertStore> CreateClientCertStore(
ResourceContext* resource_context) override {
return std::move(dummy_cert_store_);
}
void SetClientCertStore(std::unique_ptr<net::ClientCertStore> store) {
dummy_cert_store_ = std::move(store);
}
int call_count() { return call_count_; }
const net::ClientCertIdentityList& passed_identities() {
return passed_identities_;
}
void ContinueWithCertificate(scoped_refptr<net::X509Certificate> cert,
scoped_refptr<net::SSLPrivateKey> private_key) {
delegate_->ContinueWithCertificate(std::move(cert), std::move(private_key));
delegate_.reset();
}
void CancelCertificateSelection() { delegate_.reset(); }
private:
net::ClientCertIdentityList passed_identities_;
int call_count_;
std::unique_ptr<ClientCertificateDelegate> delegate_;
std::unique_ptr<net::ClientCertStore> dummy_cert_store_;
base::RunLoop select_certificate_run_loop_;
DISALLOW_COPY_AND_ASSIGN(SelectCertificateBrowserClient);
};
// Wraps a ChunkedUploadDataStream to behave as non-chunked to enable upload
// progress reporting.
class NonChunkedUploadDataStream : public net::UploadDataStream {
public:
explicit NonChunkedUploadDataStream(uint64_t size)
: net::UploadDataStream(false, 0), stream_(0), size_(size) {}
void AppendData(const char* data) {
stream_.AppendData(data, strlen(data), false);
}
private:
int InitInternal(const net::NetLogWithSource& net_log) override {
SetSize(size_);
stream_.Init(base::BindOnce(&NonChunkedUploadDataStream::OnInitCompleted,
base::Unretained(this)),
net_log);
return net::OK;
}
int ReadInternal(net::IOBuffer* buf, int buf_len) override {
return stream_.Read(
buf, buf_len,
base::BindOnce(&NonChunkedUploadDataStream::OnReadCompleted,
base::Unretained(this)));
}
void ResetInternal() override { stream_.Reset(); }
net::ChunkedUploadDataStream stream_;
uint64_t size_;
DISALLOW_COPY_AND_ASSIGN(NonChunkedUploadDataStream);
};
// Returns whether monitoring was successfully set up. If yes,
// StopMonitorBodyReadFromNetBeforePausedHistogram() needs to be called later to
// stop monitoring.
//
// |*output_sample| needs to stay valid until monitoring is stopped.
WARN_UNUSED_RESULT bool StartMonitorBodyReadFromNetBeforePausedHistogram(
base::HistogramBase::Sample* output_sample) {
return base::StatisticsRecorder::SetCallback(
kBodyReadFromNetBeforePausedHistogram,
base::BindRepeating(
[](base::HistogramBase::Sample* output,
base::HistogramBase::Sample sample) { *output = sample; },
output_sample));
}
void StopMonitorBodyReadFromNetBeforePausedHistogram() {
base::StatisticsRecorder::ClearCallback(
kBodyReadFromNetBeforePausedHistogram);
}
} // namespace
class ResourceLoaderTest : public testing::Test,
public ResourceLoaderDelegate {
protected:
ResourceLoaderTest()
: thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
test_url_request_context_(true),
raw_ptr_resource_handler_(nullptr),
raw_ptr_to_request_(nullptr) {
test_url_request_context_.set_job_factory(&job_factory_);
test_url_request_context_.set_network_quality_estimator(
&network_quality_estimator_);
test_url_request_context_.Init();
}
// URL with a response body of test_data() where reads complete synchronously.
GURL test_sync_url() const { return net::URLRequestTestJob::test_url_1(); }
// URL with a response body of test_data() where reads complete
// asynchronously.
GURL test_async_url() const {
return net::URLRequestTestJob::test_url_auto_advance_async_reads_1();
}
// URL that redirects to test_sync_url(). The ResourceLoader is set up to
// use this URL by default.
GURL test_redirect_url() const {
return net::URLRequestTestJob::test_url_redirect_to_url_1();
}
std::string test_data() const {
return net::URLRequestTestJob::test_data_1();
}
net::TestNetworkQualityEstimator* network_quality_estimator() {
return &network_quality_estimator_;
}
virtual std::unique_ptr<net::URLRequestJobFactory::ProtocolHandler>
CreateProtocolHandler() {
return net::URLRequestTestJob::CreateProtocolHandler();
}
virtual std::unique_ptr<ResourceHandler> WrapResourceHandler(
std::unique_ptr<TestResourceHandler> leaf_handler,
net::URLRequest* request) {
return std::move(leaf_handler);
}
// Replaces loader_ with a new one for |request|.
void SetUpResourceLoader(std::unique_ptr<net::URLRequest> request,
ResourceType resource_type,
bool belongs_to_main_frame) {
raw_ptr_to_request_ = request.get();
// A request marked as a main frame request must also belong to a main
// frame.
ASSERT_TRUE((resource_type != RESOURCE_TYPE_MAIN_FRAME) ||
belongs_to_main_frame);
RenderFrameHost* rfh = web_contents_->GetMainFrame();
ResourceRequestInfo::AllocateForTesting(
request.get(), resource_type, nullptr /* resource_context */,
rfh->GetProcess()->GetID(), rfh->GetRenderViewHost()->GetRoutingID(),
rfh->GetRoutingID(), belongs_to_main_frame, true /* allow_download */,
false /* is_async */, PREVIEWS_OFF /* previews_state */,
nullptr /* navigation_ui_data */);
std::unique_ptr<TestResourceHandler> resource_handler(
new TestResourceHandler(nullptr, nullptr));
raw_ptr_resource_handler_ = resource_handler.get();
loader_.reset(new ResourceLoader(
std::move(request),
WrapResourceHandler(std::move(resource_handler), raw_ptr_to_request_),
this, nullptr /* resource_context */, nullptr /* throttling_token */));
}
void SetUpResourceLoaderForUrl(const GURL& test_url) {
std::unique_ptr<net::URLRequest> request(
test_url_request_context_.CreateRequest(
test_url, net::DEFAULT_PRIORITY, nullptr /* delegate */,
TRAFFIC_ANNOTATION_FOR_TESTS));
SetUpResourceLoader(std::move(request), RESOURCE_TYPE_MAIN_FRAME, true);
}
void SetUp() override {
job_factory_.SetProtocolHandler("test", CreateProtocolHandler());
net::URLRequestFailedJob::AddUrlHandler();
browser_context_.reset(new TestBrowserContext());
scoped_refptr<SiteInstance> site_instance =
SiteInstance::Create(browser_context_.get());
web_contents_ =
TestWebContents::Create(browser_context_.get(), site_instance.get());
SetUpResourceLoaderForUrl(test_redirect_url());
}
void TearDown() override {
// Destroy the WebContents and pump the event loop before destroying
// |rvh_test_enabler_| and |thread_bundle_|. This lets asynchronous cleanup
// tasks complete.
web_contents_.reset();
// Clean up handlers.
net::URLRequestFilter::GetInstance()->ClearHandlers();
base::RunLoop().RunUntilIdle();
}
// ResourceLoaderDelegate:
scoped_refptr<LoginDelegate> CreateLoginDelegate(
ResourceLoader* loader,
net::AuthChallengeInfo* auth_info) override {
return nullptr;
}
bool HandleExternalProtocol(ResourceLoader* loader,
const GURL& url) override {
EXPECT_EQ(loader, loader_.get());
++handle_external_protocol_;
// Check that calls to HandleExternalProtocol always happen after the calls
// to the ResourceHandler's OnWillStart and OnRequestRedirected.
EXPECT_EQ(handle_external_protocol_,
raw_ptr_resource_handler_->on_will_start_called() +
raw_ptr_resource_handler_->on_request_redirected_called());
bool return_value = handle_external_protocol_results_.front();
if (handle_external_protocol_results_.size() > 1)
handle_external_protocol_results_.pop_front();
return return_value;
}
void DidStartRequest(ResourceLoader* loader) override {
EXPECT_EQ(loader, loader_.get());
EXPECT_EQ(0, did_finish_loading_);
EXPECT_EQ(0, did_start_request_);
++did_start_request_;
}
void DidReceiveRedirect(ResourceLoader* loader,
const GURL& new_url,
network::ResourceResponse* response) override {
EXPECT_EQ(loader, loader_.get());
EXPECT_EQ(0, did_finish_loading_);
EXPECT_EQ(0, did_receive_response_);
EXPECT_EQ(1, did_start_request_);
++did_received_redirect_;
}
void DidReceiveResponse(ResourceLoader* loader,
network::ResourceResponse* response) override {
EXPECT_EQ(loader, loader_.get());
EXPECT_EQ(0, did_finish_loading_);
EXPECT_EQ(0, did_receive_response_);
EXPECT_EQ(1, did_start_request_);
++did_receive_response_;
}
void DidFinishLoading(ResourceLoader* loader) override {
EXPECT_EQ(loader, loader_.get());
EXPECT_EQ(0, did_finish_loading_);
// Shouldn't be in a recursive ResourceHandler call - this is normally where
// the ResourceLoader (And thus the ResourceHandler chain) is destroyed.
EXPECT_EQ(0, raw_ptr_resource_handler_->call_depth());
++did_finish_loading_;
}
TestBrowserThreadBundle thread_bundle_;
RenderViewHostTestEnabler rvh_test_enabler_;
// Record which ResourceDispatcherHostDelegate methods have been invoked.
int did_start_request_ = 0;
int did_received_redirect_ = 0;
int did_receive_response_ = 0;
int did_finish_loading_ = 0;
int handle_external_protocol_ = 0;
// Allows controlling the return values of sequential calls to
// HandleExternalProtocol. Values are removed by the measure they are used
// but the last one which is used for all following calls.
base::circular_deque<bool> handle_external_protocol_results_{false};
net::URLRequestJobFactoryImpl job_factory_;
net::TestNetworkQualityEstimator network_quality_estimator_;
net::TestURLRequestContext test_url_request_context_;
std::unique_ptr<TestBrowserContext> browser_context_;
std::unique_ptr<TestWebContents> web_contents_;
// The ResourceLoader owns the URLRequest and the ResourceHandler.
TestResourceHandler* raw_ptr_resource_handler_;
net::URLRequest* raw_ptr_to_request_;
std::unique_ptr<ResourceLoader> loader_;
};
class ClientCertResourceLoaderTest : public ResourceLoaderTest {
protected:
std::unique_ptr<net::URLRequestJobFactory::ProtocolHandler>
CreateProtocolHandler() override {
return base::WrapUnique(new MockClientCertJobProtocolHandler);
}
void SetUp() override {
ResourceLoaderTest::SetUp();
// These tests don't expect any redirects.
SetUpResourceLoaderForUrl(test_sync_url());
}
};
// A ResourceLoaderTest that intercepts https://example.test and
// https://example-redirect.test URLs and sets SSL info on the
// responses. The latter serves a Location: header in the response.
class HTTPSSecurityInfoResourceLoaderTest : public ResourceLoaderTest {
public:
HTTPSSecurityInfoResourceLoaderTest()
: ResourceLoaderTest(),
test_https_url_("https://example.test"),
test_https_redirect_url_("https://example-redirect.test") {}
~HTTPSSecurityInfoResourceLoaderTest() override {}
const GURL& test_https_url() const { return test_https_url_; }
const GURL& test_https_redirect_url() const {
return test_https_redirect_url_;
}
protected:
void SetUp() override {
ResourceLoaderTest::SetUp();
net::URLRequestFilter::GetInstance()->ClearHandlers();
net::URLRequestFilter::GetInstance()->AddHostnameInterceptor(
"https", "example.test",
std::unique_ptr<net::URLRequestInterceptor>(
new MockHTTPSJobURLRequestInterceptor(false /* redirect */)));
net::URLRequestFilter::GetInstance()->AddHostnameInterceptor(
"https", "example-redirect.test",
std::unique_ptr<net::URLRequestInterceptor>(
new MockHTTPSJobURLRequestInterceptor(true /* redirect */)));
}
private:
const GURL test_https_url_;
const GURL test_https_redirect_url_;
};
TEST_F(HTTPSSecurityInfoResourceLoaderTest, CertStatusOnResponse) {
SetUpResourceLoaderForUrl(test_https_url());
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(kTestCertError,
raw_ptr_resource_handler_->resource_response()->head.cert_status);
}
// Tests that client certificates are requested with ClientCertStore lookup.
TEST_F(ClientCertResourceLoaderTest, WithStoreLookup) {
// Set up the test client cert store.
int store_request_count;
std::vector<std::string> store_requested_authorities;
scoped_refptr<net::X509Certificate> test_cert =
net::ImportCertFromFile(net::GetTestCertsDirectory(), "ok_cert.pem");
ASSERT_TRUE(test_cert);
net::CertificateList dummy_certs(1, test_cert);
std::unique_ptr<ClientCertStoreStub> test_store(new ClientCertStoreStub(
dummy_certs, &store_request_count, &store_requested_authorities));
// Plug in test content browser client.
SelectCertificateBrowserClient test_client;
test_client.SetClientCertStore(std::move(test_store));
ContentBrowserClient* old_client = SetBrowserClientForTesting(&test_client);
// Start the request and wait for it to pause.
loader_->StartRequest();
test_client.WaitForSelectCertificate();
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
// Check if the test store was queried against correct |cert_authorities|.
EXPECT_EQ(1, store_request_count);
EXPECT_EQ(MockClientCertURLRequestJob::test_authorities(),
store_requested_authorities);
// Check if the retrieved certificates were passed to the content browser
// client.
EXPECT_EQ(1, test_client.call_count());
EXPECT_EQ(1U, test_client.passed_identities().size());
EXPECT_EQ(test_cert.get(), test_client.passed_identities()[0]->certificate());
// Continue the request.
test_client.ContinueWithCertificate(nullptr, nullptr);
raw_ptr_resource_handler_->WaitUntilResponseComplete();
EXPECT_EQ(net::OK, raw_ptr_resource_handler_->final_status().error());
// Restore the original content browser client.
SetBrowserClientForTesting(old_client);
}
// Tests that client certificates are requested on a platform with NULL
// ClientCertStore.
TEST_F(ClientCertResourceLoaderTest, WithNullStore) {
// Plug in test content browser client.
SelectCertificateBrowserClient test_client;
ContentBrowserClient* old_client = SetBrowserClientForTesting(&test_client);
// Start the request and wait for it to pause.
loader_->StartRequest();
test_client.WaitForSelectCertificate();
// Check if the SelectClientCertificate was called on the content browser
// client.
EXPECT_EQ(1, test_client.call_count());
EXPECT_EQ(net::ClientCertIdentityList(), test_client.passed_identities());
// Continue the request.
test_client.ContinueWithCertificate(nullptr, nullptr);
raw_ptr_resource_handler_->WaitUntilResponseComplete();
EXPECT_EQ(net::OK, raw_ptr_resource_handler_->final_status().error());
// Restore the original content browser client.
SetBrowserClientForTesting(old_client);
}
// Tests that the ContentBrowserClient may cancel a certificate request.
TEST_F(ClientCertResourceLoaderTest, CancelSelection) {
// Plug in test content browser client.
SelectCertificateBrowserClient test_client;
ContentBrowserClient* old_client = SetBrowserClientForTesting(&test_client);
// Start the request and wait for it to pause.
loader_->StartRequest();
test_client.WaitForSelectCertificate();
// Check if the SelectClientCertificate was called on the content browser
// client.
EXPECT_EQ(1, test_client.call_count());
EXPECT_EQ(net::ClientCertIdentityList(), test_client.passed_identities());
// Cancel the request.
test_client.CancelCertificateSelection();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
EXPECT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED,
raw_ptr_resource_handler_->final_status().error());
// Restore the original content browser client.
SetBrowserClientForTesting(old_client);
}
// Verifies that requests without WebContents attached abort.
TEST_F(ClientCertResourceLoaderTest, NoWebContents) {
// Destroy the WebContents before starting the request.
web_contents_.reset();
// Plug in test content browser client.
SelectCertificateBrowserClient test_client;
ContentBrowserClient* old_client = SetBrowserClientForTesting(&test_client);
// Start the request and wait for it to complete.
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
// Check that SelectClientCertificate wasn't called and the request aborted.
EXPECT_EQ(0, test_client.call_count());
EXPECT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED,
raw_ptr_resource_handler_->final_status().error());
// Restore the original content browser client.
SetBrowserClientForTesting(old_client);
}
// Verifies that ClientCertStore's callback doesn't crash if called after the
// loader is destroyed.
TEST_F(ClientCertResourceLoaderTest, StoreAsyncCancel) {
base::RunLoop loader_destroyed_run_loop;
LoaderDestroyingCertStore* test_store =
new LoaderDestroyingCertStore(&loader_,
loader_destroyed_run_loop.QuitClosure());
// Plug in test content browser client.
SelectCertificateBrowserClient test_client;
test_client.SetClientCertStore(base::WrapUnique(test_store));
ContentBrowserClient* old_client = SetBrowserClientForTesting(&test_client);
loader_->StartRequest();
loader_destroyed_run_loop.Run();
EXPECT_FALSE(loader_);
// Pump the event loop to ensure nothing asynchronous crashes either.
base::RunLoop().RunUntilIdle();
// Restore the original content browser client.
SetBrowserClientForTesting(old_client);
}
// Tests that a RESOURCE_TYPE_PREFETCH request sets the LOAD_PREFETCH flag.
TEST_F(ResourceLoaderTest, PrefetchFlag) {
std::unique_ptr<net::URLRequest> request(
test_url_request_context_.CreateRequest(
test_async_url(), net::DEFAULT_PRIORITY, nullptr /* delegate */,
TRAFFIC_ANNOTATION_FOR_TESTS));
SetUpResourceLoader(std::move(request), RESOURCE_TYPE_PREFETCH, true);
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
EXPECT_EQ(test_data(), raw_ptr_resource_handler_->body());
}
// Test the case the ResourceHandler defers nothing.
TEST_F(ResourceLoaderTest, SyncResourceHandler) {
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
EXPECT_EQ(1, did_start_request_);
EXPECT_EQ(1, did_received_redirect_);
EXPECT_EQ(1, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(2, handle_external_protocol_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_request_redirected_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::OK, raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ(test_data(), raw_ptr_resource_handler_->body());
}
// Same as above, except reads complete asynchronously, and there's no redirect.
TEST_F(ResourceLoaderTest, SyncResourceHandlerAsyncReads) {
SetUpResourceLoaderForUrl(test_async_url());
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
EXPECT_EQ(1, did_start_request_);
EXPECT_EQ(0, did_received_redirect_);
EXPECT_EQ(1, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, handle_external_protocol_);
EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::OK, raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ(test_data(), raw_ptr_resource_handler_->body());
}
// Test the case where ResourceHandler defers nothing and the request is handled
// as an external protocol on start.
TEST_F(ResourceLoaderTest, SyncExternalProtocolHandlingOnStart) {
handle_external_protocol_results_ = {true};
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
EXPECT_EQ(0, did_start_request_);
EXPECT_EQ(0, did_received_redirect_);
EXPECT_EQ(0, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, handle_external_protocol_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_ABORTED,
raw_ptr_resource_handler_->final_status().error());
EXPECT_TRUE(raw_ptr_resource_handler_->body().empty());
}
// Test the case where ResourceHandler defers nothing and the request is handled
// as an external protocol on redirect.
TEST_F(ResourceLoaderTest, SyncExternalProtocolHandlingOnRedirect) {
handle_external_protocol_results_ = {false, true};
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
EXPECT_EQ(1, did_start_request_);
EXPECT_EQ(1, did_received_redirect_);
EXPECT_EQ(0, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(2, handle_external_protocol_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_request_redirected_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_ABORTED,
raw_ptr_resource_handler_->final_status().error());
EXPECT_TRUE(raw_ptr_resource_handler_->body().empty());
}
// Test the case the ResourceHandler defers everything.
TEST_F(ResourceLoaderTest, AsyncResourceHandler) {
raw_ptr_resource_handler_->set_defer_on_will_start(true);
raw_ptr_resource_handler_->set_defer_on_request_redirected(true);
raw_ptr_resource_handler_->set_defer_on_response_started(true);
raw_ptr_resource_handler_->set_defer_on_will_read(true);
raw_ptr_resource_handler_->set_defer_on_read_completed(true);
raw_ptr_resource_handler_->set_defer_on_read_eof(true);
raw_ptr_resource_handler_->set_defer_on_response_completed(true);
// Start and run until OnWillStart.
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilDeferred();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
// Spinning the message loop should not advance the state further.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, did_start_request_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(0, handle_external_protocol_);
// Resume and run until OnRequestRedirected.
raw_ptr_resource_handler_->Resume();
raw_ptr_resource_handler_->WaitUntilDeferred();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_request_redirected_called());
EXPECT_EQ(1, handle_external_protocol_);
// Spinning the message loop should not advance the state further.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, did_received_redirect_);
EXPECT_EQ(0, did_receive_response_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_request_redirected_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_will_read_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(1, handle_external_protocol_);
// Resume and run until OnResponseStarted.
raw_ptr_resource_handler_->Resume();
raw_ptr_resource_handler_->WaitUntilDeferred();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_started_called());
EXPECT_EQ(2, handle_external_protocol_);
// Spinning the message loop should not advance the state further.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, did_receive_response_);
EXPECT_EQ(0, did_finish_loading_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_started_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_will_read_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
// Resume and run until OnWillRead.
raw_ptr_resource_handler_->Resume();
raw_ptr_resource_handler_->WaitUntilDeferred();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_read_called());
// Spinning the message loop should not advance the state further.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_read_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_eof_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
// Resume and run until OnReadCompleted.
raw_ptr_resource_handler_->Resume();
raw_ptr_resource_handler_->WaitUntilDeferred();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_completed_called());
// Spinning the message loop should not advance the state further.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_read_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_completed_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_eof_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
// Defer on the next OnWillRead call, for the EOF.
raw_ptr_resource_handler_->set_defer_on_will_read(true);
// Resume and run until the next OnWillRead call.
raw_ptr_resource_handler_->Resume();
raw_ptr_resource_handler_->WaitUntilDeferred();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_completed_called());
// Spinning the message loop should not advance the state further.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2, raw_ptr_resource_handler_->on_will_read_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_completed_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_eof_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
// Resume and run until the final 0-byte read, signaling EOF.
raw_ptr_resource_handler_->Resume();
raw_ptr_resource_handler_->WaitUntilDeferred();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_eof_called());
// Spinning the message loop should not advance the state further.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_eof_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(test_data(), raw_ptr_resource_handler_->body());
// Resume and run until OnResponseCompleted is called, which again defers the
// request.
raw_ptr_resource_handler_->Resume();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
EXPECT_EQ(0, did_finish_loading_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::OK, raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ(test_data(), raw_ptr_resource_handler_->body());
// Resume and run until all pending tasks. Note that OnResponseCompleted was
// invoked in the previous section, so can't use RunUntilCompleted().
raw_ptr_resource_handler_->Resume();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::OK, raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ(test_data(), raw_ptr_resource_handler_->body());
EXPECT_EQ(2, handle_external_protocol_);
}
// Same as above, except reads complete asynchronously and there's no redirect.
TEST_F(ResourceLoaderTest, AsyncResourceHandlerAsyncReads) {
SetUpResourceLoaderForUrl(test_async_url());
raw_ptr_resource_handler_->set_defer_on_will_start(true);
raw_ptr_resource_handler_->set_defer_on_response_started(true);
raw_ptr_resource_handler_->set_defer_on_will_read(true);
raw_ptr_resource_handler_->set_defer_on_read_completed(true);
raw_ptr_resource_handler_->set_defer_on_read_eof(true);
raw_ptr_resource_handler_->set_defer_on_response_completed(true);
// Start and run until OnWillStart.
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilDeferred();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
// Spinning the message loop should not advance the state further.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, did_start_request_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(0, handle_external_protocol_);
// Resume and run until OnResponseStarted.
raw_ptr_resource_handler_->Resume();
raw_ptr_resource_handler_->WaitUntilDeferred();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_started_called());
EXPECT_EQ(1, handle_external_protocol_);
// Spinning the message loop should not advance the state further.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, did_receive_response_);
EXPECT_EQ(0, did_finish_loading_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_started_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_will_read_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
// Resume and run until OnWillRead.
raw_ptr_resource_handler_->Resume();
raw_ptr_resource_handler_->WaitUntilDeferred();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_read_called());
// Spinning the message loop should not advance the state further.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_read_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_eof_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
// Resume and run until OnReadCompleted.
raw_ptr_resource_handler_->Resume();
raw_ptr_resource_handler_->WaitUntilDeferred();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_completed_called());
// Spinning the message loop should not advance the state further.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_read_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_completed_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_eof_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
// Defer on the next OnWillRead call, for the EOF.
raw_ptr_resource_handler_->set_defer_on_will_read(true);
// Resume and run until the next OnWillRead.
raw_ptr_resource_handler_->Resume();
raw_ptr_resource_handler_->WaitUntilDeferred();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_completed_called());
// Spinning the message loop should not advance the state further.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2, raw_ptr_resource_handler_->on_will_read_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_completed_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_eof_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
// Resume and run until the final 0-byte read, signalling EOF.
raw_ptr_resource_handler_->Resume();
raw_ptr_resource_handler_->WaitUntilDeferred();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_eof_called());
// Spinning the message loop should not advance the state further.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_eof_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(test_data(), raw_ptr_resource_handler_->body());
// Resume and run until OnResponseCompleted is called, which again defers the
// request.
raw_ptr_resource_handler_->Resume();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
EXPECT_EQ(0, did_finish_loading_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::OK, raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ(test_data(), raw_ptr_resource_handler_->body());
// Resume and run until all pending tasks. Note that OnResponseCompleted was
// invoked in the previous section, so can't use RunUntilCompleted().
raw_ptr_resource_handler_->Resume();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::OK, raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ(test_data(), raw_ptr_resource_handler_->body());
EXPECT_EQ(1, handle_external_protocol_);
}
TEST_F(ResourceLoaderTest, SyncCancelOnWillStart) {
raw_ptr_resource_handler_->set_on_will_start_result(false);
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, did_start_request_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(0, handle_external_protocol_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_started_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_ABORTED,
raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ("", raw_ptr_resource_handler_->body());
}
TEST_F(ResourceLoaderTest, SyncCancelOnRequestRedirected) {
raw_ptr_resource_handler_->set_on_request_redirected_result(false);
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, did_received_redirect_);
EXPECT_EQ(0, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, handle_external_protocol_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_request_redirected_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_will_read_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_ABORTED,
raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ("", raw_ptr_resource_handler_->body());
}
TEST_F(ResourceLoaderTest, SyncCancelOnResponseStarted) {
raw_ptr_resource_handler_->set_on_response_started_result(false);
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_started_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_will_read_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_ABORTED,
raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ("", raw_ptr_resource_handler_->body());
}
TEST_F(ResourceLoaderTest, SyncCancelOnWillRead) {
raw_ptr_resource_handler_->set_on_will_read_result(false);
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_read_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_ABORTED,
raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ("", raw_ptr_resource_handler_->body());
}
TEST_F(ResourceLoaderTest, SyncCancelOnReadCompleted) {
raw_ptr_resource_handler_->set_on_read_completed_result(false);
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_completed_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_eof_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_ABORTED,
raw_ptr_resource_handler_->final_status().error());
EXPECT_LT(0u, raw_ptr_resource_handler_->body().size());
}
TEST_F(ResourceLoaderTest, SyncCancelOnReceivedEof) {
raw_ptr_resource_handler_->set_on_read_eof_result(false);
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_eof_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_ABORTED,
raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ(test_data(), raw_ptr_resource_handler_->body());
}
TEST_F(ResourceLoaderTest, SyncCancelOnAsyncReadCompleted) {
SetUpResourceLoaderForUrl(test_async_url());
raw_ptr_resource_handler_->set_on_read_completed_result(false);
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_completed_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_eof_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_ABORTED,
raw_ptr_resource_handler_->final_status().error());
EXPECT_LT(0u, raw_ptr_resource_handler_->body().size());
}
TEST_F(ResourceLoaderTest, SyncCancelOnAsyncReceivedEof) {
SetUpResourceLoaderForUrl(test_async_url());
raw_ptr_resource_handler_->set_on_read_eof_result(false);
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_eof_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_ABORTED,
raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ(test_data(), raw_ptr_resource_handler_->body());
}
TEST_F(ResourceLoaderTest, AsyncCancelOnWillStart) {
raw_ptr_resource_handler_->set_defer_on_will_start(true);
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilDeferred();
raw_ptr_resource_handler_->CancelWithError(net::ERR_FAILED);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, did_start_request_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(0, handle_external_protocol_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_started_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ("", raw_ptr_resource_handler_->body());
}
TEST_F(ResourceLoaderTest, AsyncCancelOnRequestRedirected) {
raw_ptr_resource_handler_->set_defer_on_request_redirected(true);
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilDeferred();
raw_ptr_resource_handler_->CancelWithError(net::ERR_FAILED);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, did_received_redirect_);
EXPECT_EQ(0, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, handle_external_protocol_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_request_redirected_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_will_read_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ("", raw_ptr_resource_handler_->body());
}
TEST_F(ResourceLoaderTest, AsyncCancelOnResponseStarted) {
raw_ptr_resource_handler_->set_defer_on_response_started(true);
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilDeferred();
raw_ptr_resource_handler_->CancelWithError(net::ERR_FAILED);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_started_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_will_read_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ("", raw_ptr_resource_handler_->body());
}
TEST_F(ResourceLoaderTest, AsyncCancelOnWillRead) {
raw_ptr_resource_handler_->set_defer_on_will_read(true);
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilDeferred();
raw_ptr_resource_handler_->CancelWithError(net::ERR_FAILED);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_read_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_eof_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error());
}
TEST_F(ResourceLoaderTest, AsyncCancelOnReadCompleted) {
raw_ptr_resource_handler_->set_defer_on_read_completed(true);
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilDeferred();
raw_ptr_resource_handler_->CancelWithError(net::ERR_FAILED);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_completed_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_eof_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error());
EXPECT_LT(0u, raw_ptr_resource_handler_->body().size());
}
TEST_F(ResourceLoaderTest, AsyncCancelOnReceivedEof) {
raw_ptr_resource_handler_->set_defer_on_read_eof(true);
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilDeferred();
raw_ptr_resource_handler_->CancelWithError(net::ERR_FAILED);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_eof_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ(test_data(), raw_ptr_resource_handler_->body());
}
TEST_F(ResourceLoaderTest, AsyncCancelOnAsyncReadCompleted) {
SetUpResourceLoaderForUrl(test_async_url());
raw_ptr_resource_handler_->set_defer_on_read_completed(true);
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilDeferred();
raw_ptr_resource_handler_->CancelWithError(net::ERR_FAILED);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_completed_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_eof_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error());
EXPECT_LT(0u, raw_ptr_resource_handler_->body().size());
}
TEST_F(ResourceLoaderTest, AsyncCancelOnAsyncReceivedEof) {
SetUpResourceLoaderForUrl(test_async_url());
raw_ptr_resource_handler_->set_defer_on_read_eof(true);
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilDeferred();
raw_ptr_resource_handler_->CancelWithError(net::ERR_FAILED);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_eof_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ(test_data(), raw_ptr_resource_handler_->body());
}
// Tests the request being deferred and then being handled as an external
// protocol, both on start.
TEST_F(ResourceLoaderTest, AsyncExternalProtocolHandlingOnStart) {
handle_external_protocol_results_ = {true};
raw_ptr_resource_handler_->set_defer_on_will_start(true);
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilDeferred();
EXPECT_EQ(0, did_finish_loading_);
EXPECT_EQ(0, handle_external_protocol_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
raw_ptr_resource_handler_->Resume();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
EXPECT_EQ(0, did_start_request_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, handle_external_protocol_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_started_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_ABORTED,
raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ("", raw_ptr_resource_handler_->body());
}
// Tests the request being deferred and then being handled as an external
// protocol, both on redirect.
TEST_F(ResourceLoaderTest, AsyncExternalProtocolHandlingOnRedirect) {
handle_external_protocol_results_ = {false, true};
raw_ptr_resource_handler_->set_defer_on_request_redirected(true);
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilDeferred();
EXPECT_EQ(1, did_start_request_);
EXPECT_EQ(1, did_received_redirect_);
EXPECT_EQ(0, did_finish_loading_);
EXPECT_EQ(1, handle_external_protocol_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_request_redirected_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
raw_ptr_resource_handler_->Resume();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
EXPECT_EQ(1, did_start_request_);
EXPECT_EQ(1, did_received_redirect_);
EXPECT_EQ(0, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(2, handle_external_protocol_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_request_redirected_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_started_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_ABORTED,
raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ("", raw_ptr_resource_handler_->body());
}
TEST_F(ResourceLoaderTest, RequestFailsOnStart) {
SetUpResourceLoaderForUrl(
net::URLRequestFailedJob::GetMockHttpUrlWithFailurePhase(
net::URLRequestFailedJob::START, net::ERR_FAILED));
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, did_received_redirect_);
EXPECT_EQ(0, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, handle_external_protocol_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_started_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ("", raw_ptr_resource_handler_->body());
}
TEST_F(ResourceLoaderTest, RequestFailsOnReadSync) {
SetUpResourceLoaderForUrl(
net::URLRequestFailedJob::GetMockHttpUrlWithFailurePhase(
net::URLRequestFailedJob::READ_SYNC, net::ERR_FAILED));
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, did_received_redirect_);
EXPECT_EQ(1, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_started_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ("", raw_ptr_resource_handler_->body());
}
TEST_F(ResourceLoaderTest, RequestFailsOnReadAsync) {
SetUpResourceLoaderForUrl(
net::URLRequestFailedJob::GetMockHttpUrlWithFailurePhase(
net::URLRequestFailedJob::READ_ASYNC, net::ERR_FAILED));
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, did_received_redirect_);
EXPECT_EQ(1, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_started_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ("", raw_ptr_resource_handler_->body());
}
TEST_F(ResourceLoaderTest, OutOfBandCancelDuringStart) {
SetUpResourceLoaderForUrl(
net::URLRequestFailedJob::GetMockHttpUrlWithFailurePhase(
net::URLRequestFailedJob::START, net::ERR_IO_PENDING));
loader_->StartRequest();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_started_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(1, handle_external_protocol_);
// Can't cancel through the ResourceHandler, since that depends on
// ResourceDispatachHost, which these tests don't use.
loader_->CancelRequest(false);
raw_ptr_resource_handler_->WaitUntilResponseComplete();
EXPECT_EQ(0, did_received_redirect_);
EXPECT_EQ(0, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, handle_external_protocol_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_started_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_ABORTED,
raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ("", raw_ptr_resource_handler_->body());
}
TEST_F(ResourceLoaderTest, OutOfBandCancelDuringRead) {
SetUpResourceLoaderForUrl(
net::URLRequestFailedJob::GetMockHttpUrlWithFailurePhase(
net::URLRequestFailedJob::READ_SYNC, net::ERR_IO_PENDING));
loader_->StartRequest();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_started_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(1, handle_external_protocol_);
// Can't cancel through the ResourceHandler, since that depends on
// ResourceDispatachHost, which these tests don't use.
loader_->CancelRequest(false);
raw_ptr_resource_handler_->WaitUntilResponseComplete();
EXPECT_EQ(0, did_received_redirect_);
EXPECT_EQ(1, did_receive_response_);
EXPECT_EQ(1, did_finish_loading_);
EXPECT_EQ(1, handle_external_protocol_);
EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_started_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called());
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
EXPECT_EQ(net::ERR_ABORTED,
raw_ptr_resource_handler_->final_status().error());
EXPECT_EQ("", raw_ptr_resource_handler_->body());
}
TEST_F(ResourceLoaderTest, ResumeCanceledRequest) {
raw_ptr_resource_handler_->set_defer_on_will_start(true);
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilDeferred();
loader_->CancelRequest(true);
raw_ptr_resource_handler_->Resume();
}
class EffectiveConnectionTypeResourceLoaderTest : public ResourceLoaderTest {
public:
void VerifyEffectiveConnectionType(
ResourceType resource_type,
bool belongs_to_main_frame,
net::EffectiveConnectionType set_type,
net::EffectiveConnectionType expected_type) {
network_quality_estimator()->set_effective_connection_type(set_type);
// Start the request and wait for it to finish.
std::unique_ptr<net::URLRequest> request(
test_url_request_context_.CreateRequest(
test_redirect_url(), net::DEFAULT_PRIORITY, nullptr /* delegate */,
TRAFFIC_ANNOTATION_FOR_TESTS));
SetUpResourceLoader(std::move(request), resource_type,
belongs_to_main_frame);
// Send the request and wait until it completes.
loader_->StartRequest();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
ASSERT_EQ(net::URLRequestStatus::SUCCESS,
raw_ptr_to_request_->status().status());
EXPECT_EQ(expected_type, raw_ptr_resource_handler_->resource_response()
->head.effective_connection_type);
}
};
// Tests that the effective connection type is set on main frame requests.
TEST_F(EffectiveConnectionTypeResourceLoaderTest, Slow2G) {
VerifyEffectiveConnectionType(RESOURCE_TYPE_MAIN_FRAME, true,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
}
// Tests that the effective connection type is set on main frame requests.
TEST_F(EffectiveConnectionTypeResourceLoaderTest, 3G) {
VerifyEffectiveConnectionType(RESOURCE_TYPE_MAIN_FRAME, true,
net::EFFECTIVE_CONNECTION_TYPE_3G,
net::EFFECTIVE_CONNECTION_TYPE_3G);
}
// Tests that the effective connection type is not set on requests that belong
// to main frame.
TEST_F(EffectiveConnectionTypeResourceLoaderTest, BelongsToMainFrame) {
VerifyEffectiveConnectionType(RESOURCE_TYPE_OBJECT, true,
net::EFFECTIVE_CONNECTION_TYPE_3G,
net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN);
}
// Tests that the effective connection type is not set on non-main frame
// requests.
TEST_F(EffectiveConnectionTypeResourceLoaderTest, DoesNotBelongToMainFrame) {
VerifyEffectiveConnectionType(RESOURCE_TYPE_OBJECT, false,
net::EFFECTIVE_CONNECTION_TYPE_3G,
net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN);
}
TEST_F(ResourceLoaderTest, PauseReadingBodyFromNetBeforeRespnoseHeaders) {
static constexpr char kPath[] = "/hello.html";
static constexpr char kBodyContents[] = "This is the data as you requested.";
base::HistogramBase::Sample output_sample = -1;
EXPECT_TRUE(StartMonitorBodyReadFromNetBeforePausedHistogram(&output_sample));
net::EmbeddedTestServer server;
net::test_server::ControllableHttpResponse response_controller(&server,
kPath);
ASSERT_TRUE(server.Start());
SetUpResourceLoaderForUrl(server.GetURL(kPath));
loader_->StartRequest();
// Pausing reading response body from network stops future reads from the
// underlying URLRequest. So no data should be sent using the response body
// data pipe.
loader_->PauseReadingBodyFromNet();
response_controller.WaitForRequest();
response_controller.Send(
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n\r\n" +
std::string(kBodyContents));
response_controller.Done();
// We will still receive the response header, although there won't be any data
// available until ResumeReadBodyFromNet() is called.
raw_ptr_resource_handler_->WaitUntilResponseStarted();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_started_called());
EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called());
// Wait for a little amount of time so that if the loader mistakenly reads
// response body from the underlying URLRequest, it is easier to find out.
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(),
base::TimeDelta::FromMilliseconds(100));
run_loop.Run();
EXPECT_TRUE(raw_ptr_resource_handler_->body().empty());
loader_->ResumeReadingBodyFromNet();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
EXPECT_EQ(kBodyContents, raw_ptr_resource_handler_->body());
loader_.reset();
EXPECT_EQ(0, output_sample);
StopMonitorBodyReadFromNetBeforePausedHistogram();
}
TEST_F(ResourceLoaderTest, PauseReadingBodyFromNetWhenReadIsPending) {
static constexpr char kPath[] = "/hello.html";
static constexpr char kBodyContentsFirstHalf[] = "This is the first half.";
static constexpr char kBodyContentsSecondHalf[] = "This is the second half.";
base::HistogramBase::Sample output_sample = -1;
EXPECT_TRUE(StartMonitorBodyReadFromNetBeforePausedHistogram(&output_sample));
net::EmbeddedTestServer server;
net::test_server::ControllableHttpResponse response_controller(&server,
kPath);
ASSERT_TRUE(server.Start());
SetUpResourceLoaderForUrl(server.GetURL(kPath));
loader_->StartRequest();
response_controller.WaitForRequest();
response_controller.Send(
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n\r\n" +
std::string(kBodyContentsFirstHalf));
raw_ptr_resource_handler_->WaitUntilResponseStarted();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_started_called());
loader_->PauseReadingBodyFromNet();
response_controller.Send(kBodyContentsSecondHalf);
response_controller.Done();
// It is uncertain how much data has been read before reading is actually
// paused, because if there is a pending read when PauseReadingBodyFromNet()
// arrives, the pending read won't be cancelled. Therefore, this test only
// checks that after ResumeReadingBodyFromNet() we should be able to get the
// whole response body.
loader_->ResumeReadingBodyFromNet();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
EXPECT_EQ(std::string(kBodyContentsFirstHalf) +
std::string(kBodyContentsSecondHalf),
raw_ptr_resource_handler_->body());
loader_.reset();
EXPECT_LE(0, output_sample);
StopMonitorBodyReadFromNetBeforePausedHistogram();
}
TEST_F(ResourceLoaderTest, MultiplePauseResumeReadingBodyFromNet) {
static constexpr char kPath[] = "/hello.html";
static constexpr char kBodyContentsFirstHalf[] = "This is the first half.";
static constexpr char kBodyContentsSecondHalf[] = "This is the second half.";
base::HistogramBase::Sample output_sample = -1;
EXPECT_TRUE(StartMonitorBodyReadFromNetBeforePausedHistogram(&output_sample));
net::EmbeddedTestServer server;
net::test_server::ControllableHttpResponse response_controller(&server,
kPath);
ASSERT_TRUE(server.Start());
SetUpResourceLoaderForUrl(server.GetURL(kPath));
loader_->StartRequest();
// It is okay to call ResumeReadingBodyFromNet() even if there is no prior
// PauseReadingBodyFromNet().
loader_->ResumeReadingBodyFromNet();
response_controller.WaitForRequest();
response_controller.Send(
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n\r\n" +
std::string(kBodyContentsFirstHalf));
loader_->PauseReadingBodyFromNet();
raw_ptr_resource_handler_->WaitUntilResponseStarted();
EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_started_called());
loader_->PauseReadingBodyFromNet();
loader_->PauseReadingBodyFromNet();
response_controller.Send(kBodyContentsSecondHalf);
response_controller.Done();
// One ResumeReadingBodyFromNet() call will resume reading even if there are
// multiple PauseReadingBodyFromNet() calls before it.
loader_->ResumeReadingBodyFromNet();
raw_ptr_resource_handler_->WaitUntilResponseComplete();
EXPECT_EQ(std::string(kBodyContentsFirstHalf) +
std::string(kBodyContentsSecondHalf),
raw_ptr_resource_handler_->body());
loader_.reset();
EXPECT_LE(0, output_sample);
StopMonitorBodyReadFromNetBeforePausedHistogram();
}
} // namespace content