| Index: content/browser/loader/async_revalidation_driver_unittest.cc | 
| diff --git a/content/browser/loader/async_revalidation_driver_unittest.cc b/content/browser/loader/async_revalidation_driver_unittest.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..0e858e07eed8caf77fd33371e81fc790d3869584 | 
| --- /dev/null | 
| +++ b/content/browser/loader/async_revalidation_driver_unittest.cc | 
| @@ -0,0 +1,402 @@ | 
| +// 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: | 
| +  ~MockClientCertURLRequestJob() override {} | 
| + | 
| +  base::WeakPtrFactory<MockClientCertURLRequestJob> weak_factory_; | 
| + | 
| +  DISALLOW_COPY_AND_ASSIGN(MockClientCertURLRequestJob); | 
| +}; | 
| + | 
| +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) {} | 
| + | 
| +  // 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, this, | 
| +                   ssl_info, false)); | 
| +  } | 
| + | 
| +  void ContinueDespiteLastError() override { | 
| +    ADD_FAILURE() << "ContinueDespiteLastError called."; | 
| +  } | 
| + | 
| + private: | 
| +  ~MockSSLErrorURLRequestJob() override {} | 
| + | 
| +  DISALLOW_COPY_AND_ASSIGN(MockSSLErrorURLRequestJob); | 
| +}; | 
| + | 
| +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 | 
|  |