// 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 #include #include #include #include #include "base/bind.h" #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* 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, ClientCertListCallback callback) override { *requested_authorities_ = cert_request_info.cert_authorities; ++(*request_count_); std::move(callback).Run( net::FakeClientCertIdentityListFromCertificateList(response_)); } private: const net::CertificateList response_; std::vector* 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* 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, 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_), std::move(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* loader, ClientCertListCallback cert_selected_callback, const base::Closure& on_loader_deleted_callback) { loader->reset(); std::move(cert_selected_callback).Run(net::ClientCertIdentityList()); on_loader_deleted_callback.Run(); } std::unique_ptr* 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) {} static std::vector test_authorities() { return std::vector(1, "dummy"); } // net::URLRequestTestJob: void Start() override { auto cert_request_info = base::MakeRefCounted(); 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 cert, scoped_refptr private_key) override { net::URLRequestTestJob::Start(); } private: ~MockClientCertURLRequestJob() override {} base::WeakPtrFactory weak_factory_{this}; 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(); } base::OnceClosure SelectClientCertificate( WebContents* web_contents, net::SSLCertRequestInfo* cert_request_info, net::ClientCertIdentityList client_certs, std::unique_ptr delegate) override { EXPECT_FALSE(delegate_.get()); ++call_count_; passed_identities_ = std::move(client_certs); delegate_ = std::move(delegate); select_certificate_run_loop_.Quit(); return base::OnceClosure(); } std::unique_ptr CreateClientCertStore( ResourceContext* resource_context) override { return std::move(dummy_cert_store_); } void SetClientCertStore(std::unique_ptr 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 cert, scoped_refptr 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 delegate_; std::unique_ptr 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 CreateProtocolHandler() { return net::URLRequestTestJob::CreateProtocolHandler(); } virtual std::unique_ptr WrapResourceHandler( std::unique_ptr leaf_handler, net::URLRequest* request) { return std::move(leaf_handler); } // Replaces loader_ with a new one for |request|. void SetUpResourceLoader(std::unique_ptr 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 != ResourceType::kMainFrame) || 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, ResourceInterceptPolicy::kAllowAll, false /* is_async */, PREVIEWS_OFF /* previews_state */, nullptr /* navigation_ui_data */); std::unique_ptr 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 request( test_url_request_context_.CreateRequest( test_url, net::DEFAULT_PRIORITY, nullptr /* delegate */, TRAFFIC_ANNOTATION_FOR_TESTS)); SetUpResourceLoader(std::move(request), ResourceType::kMainFrame, true); } void SetUp() override { job_factory_.SetProtocolHandler("test", CreateProtocolHandler()); net::URLRequestFailedJob::AddUrlHandler(); browser_context_.reset(new TestBrowserContext()); scoped_refptr 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: std::unique_ptr CreateLoginDelegate( ResourceLoader* loader, const 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 handle_external_protocol_results_{false}; net::URLRequestJobFactoryImpl job_factory_; net::TestNetworkQualityEstimator network_quality_estimator_; net::TestURLRequestContext test_url_request_context_; std::unique_ptr browser_context_; std::unique_ptr web_contents_; // The ResourceLoader owns the URLRequest and the ResourceHandler. TestResourceHandler* raw_ptr_resource_handler_; net::URLRequest* raw_ptr_to_request_; std::unique_ptr loader_; }; class ClientCertResourceLoaderTest : public ResourceLoaderTest { protected: std::unique_ptr 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( new MockHTTPSJobURLRequestInterceptor(false /* redirect */))); net::URLRequestFilter::GetInstance()->AddHostnameInterceptor( "https", "example-redirect.test", std::unique_ptr( 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 store_requested_authorities; scoped_refptr test_cert = net::ImportCertFromFile(net::GetTestCertsDirectory(), "ok_cert.pem"); ASSERT_TRUE(test_cert); net::CertificateList dummy_certs(1, test_cert); std::unique_ptr 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 ResourceType::kPrefetch request sets the LOAD_PREFETCH flag. TEST_F(ResourceLoaderTest, PrefetchFlag) { std::unique_ptr request( test_url_request_context_.CreateRequest( test_async_url(), net::DEFAULT_PRIORITY, nullptr /* delegate */, TRAFFIC_ANNOTATION_FOR_TESTS)); SetUpResourceLoader(std::move(request), ResourceType::kPrefetch, 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 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(ResourceType::kMainFrame, 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(ResourceType::kMainFrame, 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(ResourceType::kObject, 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(ResourceType::kObject, 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(); } // Tests that AuthChallengeInfo is present on a request that requests // authentication. TEST_F(ResourceLoaderTest, AuthChallengeInfo) { net::EmbeddedTestServer server; server.AddDefaultHandlers(base::FilePath()); ASSERT_TRUE(server.Start()); SetUpResourceLoaderForUrl(server.GetURL("/auth-basic")); loader_->StartRequest(); raw_ptr_resource_handler_->WaitUntilResponseComplete(); const base::Optional& auth_info = raw_ptr_resource_handler_->resource_response()->head.auth_challenge_info; ASSERT_TRUE(auth_info.has_value()); EXPECT_FALSE(auth_info->is_proxy); EXPECT_EQ(url::Origin::Create(server.GetURL("/")), auth_info->challenger); EXPECT_EQ("basic", auth_info->scheme); EXPECT_EQ("testrealm", auth_info->realm); EXPECT_EQ("Basic realm=\"testrealm\"", auth_info->challenge); EXPECT_EQ("/auth-basic", auth_info->path); } // Tests that no AuthChallengeInfo is present on a request that doesn't request // authentication. TEST_F(ResourceLoaderTest, NoAuthChallengeInfo) { net::EmbeddedTestServer server; server.AddDefaultHandlers(base::FilePath()); ASSERT_TRUE(server.Start()); SetUpResourceLoaderForUrl(server.GetURL("/")); loader_->StartRequest(); raw_ptr_resource_handler_->WaitUntilResponseComplete(); EXPECT_FALSE(raw_ptr_resource_handler_->resource_response() ->head.auth_challenge_info.has_value()); } } // namespace content