diff options
Diffstat (limited to 'chromium/content/browser/loader/async_revalidation_driver_unittest.cc')
-rw-r--r-- | chromium/content/browser/loader/async_revalidation_driver_unittest.cc | 397 |
1 files changed, 397 insertions, 0 deletions
diff --git a/chromium/content/browser/loader/async_revalidation_driver_unittest.cc b/chromium/content/browser/loader/async_revalidation_driver_unittest.cc new file mode 100644 index 00000000000..1f5a7e4b1f0 --- /dev/null +++ b/chromium/content/browser/loader/async_revalidation_driver_unittest.cc @@ -0,0 +1,397 @@ +// Copyright 2015 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/async_revalidation_driver.h" + +#include <string> +#include <type_traits> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/location.h" +#include "base/macros.h" +#include "base/run_loop.h" +#include "content/public/browser/client_certificate_delegate.h" +#include "content/public/common/content_client.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "content/test/test_content_browser_client.h" +#include "ipc/ipc_message.h" +#include "net/base/net_errors.h" +#include "net/base/network_delegate_impl.h" +#include "net/base/request_priority.h" +#include "net/ssl/ssl_cert_request_info.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_status.h" +#include "net/url_request/url_request_test_job.h" +#include "net/url_request/url_request_test_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { +namespace { + +// Dummy implementation of ResourceThrottle, an instance of which is needed to +// initialize AsyncRevalidationDriver. +class ResourceThrottleStub : public ResourceThrottle { + public: + ResourceThrottleStub() {} + + // If true, defers the request in WillStartRequest. + void set_defer_request_on_will_start_request( + bool defer_request_on_will_start_request) { + defer_request_on_will_start_request_ = defer_request_on_will_start_request; + } + + // ResourceThrottler implementation: + void WillStartRequest(bool* defer) override { + *defer = defer_request_on_will_start_request_; + } + + const char* GetNameForLogging() const override { + return "ResourceThrottleStub"; + } + + private: + bool defer_request_on_will_start_request_ = false; + + DISALLOW_COPY_AND_ASSIGN(ResourceThrottleStub); +}; + +// There are multiple layers of boilerplate needed to use a URLRequestTestJob +// subclass. Subclasses of AsyncRevalidationDriverTest can use +// BindCreateProtocolHandlerCallback() to bypass most of that boilerplate. +using CreateProtocolHandlerCallback = + base::Callback<scoped_ptr<net::URLRequestJobFactory::ProtocolHandler>()>; + +template <typename T> +CreateProtocolHandlerCallback BindCreateProtocolHandlerCallback() { + static_assert(std::is_base_of<net::URLRequestJob, T>::value, + "Template argument to BindCreateProtocolHandlerCallback() must " + "be a subclass of URLRequestJob."); + + class TemplatedProtocolHandler + : public net::URLRequestJobFactory::ProtocolHandler { + public: + static scoped_ptr<net::URLRequestJobFactory::ProtocolHandler> Create() { + return make_scoped_ptr(new TemplatedProtocolHandler()); + } + + // URLRequestJobFactory::ProtocolHandler implementation: + net::URLRequestJob* MaybeCreateJob( + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const override { + return new T(request, network_delegate); + } + }; + + return base::Bind(&TemplatedProtocolHandler::Create); +} + +// An implementation of NetworkDelegate that captures the status of the last +// URLRequest to be destroyed. +class StatusCapturingNetworkDelegate : public net::NetworkDelegateImpl { + public: + const net::URLRequestStatus& last_status() { return last_status_; } + + private: + // net::NetworkDelegate implementation. + void OnURLRequestDestroyed(net::URLRequest* request) override { + last_status_ = request->status(); + } + + net::URLRequestStatus last_status_; +}; + +class AsyncRevalidationDriverTest : public testing::Test { + protected: + // Constructor for test fixtures that subclass this one. + AsyncRevalidationDriverTest( + const CreateProtocolHandlerCallback& create_protocol_handler_callback) + : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP), + create_protocol_handler_callback_(create_protocol_handler_callback), + raw_ptr_resource_throttle_(nullptr), + raw_ptr_request_(nullptr) { + test_url_request_context_.set_job_factory(&job_factory_); + test_url_request_context_.set_network_delegate(&network_delegate_); + } + + // Constructor for tests that use this fixture directly. + AsyncRevalidationDriverTest() + : AsyncRevalidationDriverTest( + base::Bind(net::URLRequestTestJob::CreateProtocolHandler)) {} + + bool async_revalidation_complete_called() const { + return async_revalidation_complete_called_; + } + + const net::URLRequestStatus& last_status() { + return network_delegate_.last_status(); + } + + void SetUpAsyncRevalidationDriverWithRequestToUrl(const GURL& url) { + scoped_ptr<net::URLRequest> request(test_url_request_context_.CreateRequest( + url, net::DEFAULT_PRIORITY, nullptr /* delegate */)); + raw_ptr_request_ = request.get(); + raw_ptr_resource_throttle_ = new ResourceThrottleStub(); + // This use of base::Unretained() is safe because |driver_|, and the closure + // passed to it, will be destroyed before this object is. + driver_.reset(new AsyncRevalidationDriver( + std::move(request), make_scoped_ptr(raw_ptr_resource_throttle_), + base::Bind(&AsyncRevalidationDriverTest::OnAsyncRevalidationComplete, + base::Unretained(this)))); + } + + void SetUpAsyncRevalidationDriverWithDefaultRequest() { + SetUpAsyncRevalidationDriverWithRequestToUrl( + net::URLRequestTestJob::test_url_1()); + } + + void SetUp() override { + job_factory_.SetProtocolHandler("test", + create_protocol_handler_callback_.Run()); + SetUpAsyncRevalidationDriverWithDefaultRequest(); + } + + void OnAsyncRevalidationComplete() { + EXPECT_FALSE(async_revalidation_complete_called_); + async_revalidation_complete_called_ = true; + driver_.reset(); + } + + TestBrowserThreadBundle thread_bundle_; + net::URLRequestJobFactoryImpl job_factory_; + net::TestURLRequestContext test_url_request_context_; + StatusCapturingNetworkDelegate network_delegate_; + CreateProtocolHandlerCallback create_protocol_handler_callback_; + + // The AsyncRevalidationDriver owns the URLRequest and the ResourceThrottle. + ResourceThrottleStub* raw_ptr_resource_throttle_; + net::URLRequest* raw_ptr_request_; + scoped_ptr<AsyncRevalidationDriver> driver_; + bool async_revalidation_complete_called_ = false; +}; + +TEST_F(AsyncRevalidationDriverTest, NormalRequestCompletes) { + driver_->StartRequest(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(async_revalidation_complete_called()); +} + +// Verifies that request that should be deferred at start is deferred. +TEST_F(AsyncRevalidationDriverTest, DeferOnStart) { + raw_ptr_resource_throttle_->set_defer_request_on_will_start_request(true); + + driver_->StartRequest(); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(raw_ptr_request_->is_pending()); + EXPECT_FALSE(async_revalidation_complete_called()); +} + +// Verifies that resuming a deferred request works. Assumes that DeferOnStart +// passes. +TEST_F(AsyncRevalidationDriverTest, ResumeDeferredRequestWorks) { + raw_ptr_resource_throttle_->set_defer_request_on_will_start_request(true); + + driver_->StartRequest(); + base::RunLoop().RunUntilIdle(); + + ResourceController* driver_as_resource_controller = driver_.get(); + driver_as_resource_controller->Resume(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(async_revalidation_complete_called()); +} + +// Verifies that redirects are not followed. +TEST_F(AsyncRevalidationDriverTest, RedirectsAreNotFollowed) { + SetUpAsyncRevalidationDriverWithRequestToUrl( + net::URLRequestTestJob::test_url_redirect_to_url_2()); + + driver_->StartRequest(); + while (net::URLRequestTestJob::ProcessOnePendingMessage()) + base::RunLoop().RunUntilIdle(); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(last_status().is_success()); + EXPECT_EQ(net::ERR_ABORTED, last_status().error()); + EXPECT_TRUE(async_revalidation_complete_called()); +} + +// 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, true), + weak_factory_(this) {} + + // net::URLRequestTestJob implementation: + void Start() override { + scoped_refptr<net::SSLCertRequestInfo> cert_request_info( + new net::SSLCertRequestInfo); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&MockClientCertURLRequestJob::NotifyCertificateRequested, + weak_factory_.GetWeakPtr(), cert_request_info)); + } + + void ContinueWithCertificate( + net::X509Certificate* cert, + net::SSLPrivateKey* client_private_key) override { + ADD_FAILURE() << "Certificate supplied."; + } + + void Kill() override { + weak_factory_.InvalidateWeakPtrs(); + URLRequestJob::Kill(); + } + + private: + base::WeakPtrFactory<MockClientCertURLRequestJob> weak_factory_; +}; + +class AsyncRevalidationDriverClientCertTest + : public AsyncRevalidationDriverTest { + protected: + AsyncRevalidationDriverClientCertTest() + : AsyncRevalidationDriverTest( + BindCreateProtocolHandlerCallback<MockClientCertURLRequestJob>()) {} +}; + +// Test browser client that causes the test to fail if SelectClientCertificate() +// is called. Automatically sets itself as the browser client when constructed +// and restores the old browser client in the destructor. +class ScopedDontSelectCertificateBrowserClient + : public TestContentBrowserClient { + public: + ScopedDontSelectCertificateBrowserClient() { + old_client_ = SetBrowserClientForTesting(this); + } + + ~ScopedDontSelectCertificateBrowserClient() override { + SetBrowserClientForTesting(old_client_); + } + + void SelectClientCertificate( + WebContents* web_contents, + net::SSLCertRequestInfo* cert_request_info, + scoped_ptr<ClientCertificateDelegate> delegate) override { + ADD_FAILURE() << "SelectClientCertificate was called."; + } + + private: + ContentBrowserClient* old_client_; + + DISALLOW_COPY_AND_ASSIGN(ScopedDontSelectCertificateBrowserClient); +}; + +// Verifies that async revalidation requests do not attempt to provide client +// certificates. +TEST_F(AsyncRevalidationDriverClientCertTest, RequestRejected) { + // Ensure that SelectClientCertificate is not called during this test. + ScopedDontSelectCertificateBrowserClient test_client; + + // Start the request and wait for it to pause. + driver_->StartRequest(); + + // Because TestBrowserThreadBundle only uses one real thread, this is + // sufficient to ensure that tasks posted to the "UI thread" have run. + base::RunLoop().RunUntilIdle(); + + // Check that the request aborted. + EXPECT_FALSE(last_status().is_success()); + EXPECT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED, last_status().error()); + EXPECT_TRUE(async_revalidation_complete_called()); +} + +// A mock URLRequestJob which simulates an SSL certificate error. +class MockSSLErrorURLRequestJob : public net::URLRequestTestJob { + public: + MockSSLErrorURLRequestJob(net::URLRequest* request, + net::NetworkDelegate* network_delegate) + : net::URLRequestTestJob(request, network_delegate, true), + weak_factory_(this) {} + + // net::URLRequestTestJob implementation: + void Start() override { + // This SSLInfo isn't really valid, but it is good enough for testing. + net::SSLInfo ssl_info; + ssl_info.SetCertError(net::ERR_CERT_DATE_INVALID); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&MockSSLErrorURLRequestJob::NotifySSLCertificateError, + weak_factory_.GetWeakPtr(), ssl_info, false)); + } + + void ContinueDespiteLastError() override { + ADD_FAILURE() << "ContinueDespiteLastError called."; + } + + private: + base::WeakPtrFactory<MockSSLErrorURLRequestJob> weak_factory_; +}; + +class AsyncRevalidationDriverSSLErrorTest : public AsyncRevalidationDriverTest { + protected: + AsyncRevalidationDriverSSLErrorTest() + : AsyncRevalidationDriverTest( + BindCreateProtocolHandlerCallback<MockSSLErrorURLRequestJob>()) {} +}; + +// Verifies that async revalidation requests do not attempt to recover from SSL +// certificate errors. +TEST_F(AsyncRevalidationDriverSSLErrorTest, RequestWithSSLErrorRejected) { + // Start the request and wait for it to pause. + driver_->StartRequest(); + base::RunLoop().RunUntilIdle(); + + // Check that the request has been aborted. + EXPECT_FALSE(last_status().is_success()); + EXPECT_EQ(net::ERR_ABORTED, last_status().error()); + EXPECT_TRUE(async_revalidation_complete_called()); +} + +// A URLRequestTestJob that sets |request_time| and |was_cached| on their +// response_info, and causes the test to fail if Read() is called. +class FromCacheURLRequestJob : public net::URLRequestTestJob { + public: + FromCacheURLRequestJob(net::URLRequest* request, + net::NetworkDelegate* network_delegate) + : net::URLRequestTestJob(request, network_delegate, true) {} + + void GetResponseInfo(net::HttpResponseInfo* info) override { + URLRequestTestJob::GetResponseInfo(info); + info->request_time = base::Time::Now(); + info->was_cached = true; + } + + int ReadRawData(net::IOBuffer* buf, int buf_size) override { + ADD_FAILURE() << "ReadRawData() was called."; + return URLRequestTestJob::ReadRawData(buf, buf_size); + } + + private: + ~FromCacheURLRequestJob() override {} + + DISALLOW_COPY_AND_ASSIGN(FromCacheURLRequestJob); +}; + +class AsyncRevalidationDriverFromCacheTest + : public AsyncRevalidationDriverTest { + protected: + AsyncRevalidationDriverFromCacheTest() + : AsyncRevalidationDriverTest( + BindCreateProtocolHandlerCallback<FromCacheURLRequestJob>()) {} +}; + +TEST_F(AsyncRevalidationDriverFromCacheTest, + CacheNotReadOnSuccessfulRevalidation) { + driver_->StartRequest(); + base::RunLoop().RunUntilIdle(); + + EXPECT_TRUE(async_revalidation_complete_called()); +} + +} // namespace +} // namespace content |