| Index: content/browser/loader/resource_loader_unittest.cc
|
| diff --git a/content/browser/loader/resource_loader_unittest.cc b/content/browser/loader/resource_loader_unittest.cc
|
| index 92241b68d0beab40f4d03c6512289017c9578e5b..89f82360d5be36547be4f8fc1927fd1b09b6b08d 100644
|
| --- a/content/browser/loader/resource_loader_unittest.cc
|
| +++ b/content/browser/loader/resource_loader_unittest.cc
|
| @@ -6,24 +6,31 @@
|
|
|
| #include "base/files/file.h"
|
| #include "base/files/file_util.h"
|
| +#include "base/macros.h"
|
| #include "base/message_loop/message_loop_proxy.h"
|
| #include "base/run_loop.h"
|
| #include "content/browser/browser_thread_impl.h"
|
| #include "content/browser/loader/redirect_to_file_resource_handler.h"
|
| #include "content/browser/loader/resource_loader_delegate.h"
|
| +#include "content/public/browser/client_certificate_delegate.h"
|
| #include "content/public/browser/resource_request_info.h"
|
| #include "content/public/common/resource_response.h"
|
| #include "content/public/test/mock_resource_context.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 "ipc/ipc_message.h"
|
| #include "net/base/io_buffer.h"
|
| #include "net/base/mock_file_stream.h"
|
| +#include "net/base/net_errors.h"
|
| #include "net/base/request_priority.h"
|
| #include "net/cert/x509_certificate.h"
|
| #include "net/ssl/client_cert_store.h"
|
| #include "net/ssl/ssl_cert_request_info.h"
|
| #include "net/url_request/url_request.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"
|
| @@ -53,7 +60,6 @@ class ClientCertStoreStub : public net::ClientCertStore {
|
| int* request_count,
|
| std::vector<std::string>* requested_authorities)
|
| : response_(response),
|
| - async_(false),
|
| requested_authorities_(requested_authorities),
|
| request_count_(request_count) {
|
| requested_authorities_->clear();
|
| @@ -62,9 +68,6 @@ class ClientCertStoreStub : public net::ClientCertStore {
|
|
|
| ~ClientCertStoreStub() override {}
|
|
|
| - // Configures whether the certificates are returned asynchronously or not.
|
| - void set_async(bool async) { async_ = async; }
|
| -
|
| // net::ClientCertStore:
|
| void GetClientCerts(const net::SSLCertRequestInfo& cert_request_info,
|
| net::CertificateList* selected_certs,
|
| @@ -73,20 +76,88 @@ class ClientCertStoreStub : public net::ClientCertStore {
|
| ++(*request_count_);
|
|
|
| *selected_certs = response_;
|
| - if (async_) {
|
| - base::MessageLoop::current()->PostTask(FROM_HERE, callback);
|
| - } else {
|
| - callback.Run();
|
| - }
|
| + callback.Run();
|
| }
|
|
|
| private:
|
| const net::CertificateList response_;
|
| - bool async_;
|
| std::vector<std::string>* requested_authorities_;
|
| int* request_count_;
|
| };
|
|
|
| +// Client certificate store which destroys its resource loader before the
|
| +// asynchronous GetClientCerts callback is called.
|
| +class LoaderDestroyingCertStore : public net::ClientCertStore {
|
| + public:
|
| + // Creates a client certificate store which, when looked up, posts a task to
|
| + // reset |loader| and then call the callback. The caller is responsible for
|
| + // ensuring the pointers remain valid until the process is complete.
|
| + explicit LoaderDestroyingCertStore(scoped_ptr<ResourceLoader>* loader)
|
| + : loader_(loader) {}
|
| +
|
| + // net::ClientCertStore:
|
| + void GetClientCerts(const net::SSLCertRequestInfo& cert_request_info,
|
| + net::CertificateList* selected_certs,
|
| + const base::Closure& callback) override {
|
| + // Don't destroy |loader_| while it's on the stack.
|
| + base::MessageLoop::current()->PostTask(
|
| + FROM_HERE, base::Bind(&LoaderDestroyingCertStore::DoCallback,
|
| + base::Unretained(loader_), callback));
|
| + }
|
| +
|
| + private:
|
| + static void DoCallback(scoped_ptr<ResourceLoader>* loader,
|
| + const base::Closure& callback) {
|
| + loader->reset();
|
| + callback.Run();
|
| + }
|
| +
|
| + scoped_ptr<ResourceLoader>* loader_;
|
| +};
|
| +
|
| +// 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<std::string> test_authorities() {
|
| + return std::vector<std::string>(1, "dummy");
|
| + }
|
| +
|
| + // net::URLRequestTestJob:
|
| + void Start() override {
|
| + scoped_refptr<net::SSLCertRequestInfo> cert_request_info(
|
| + new net::SSLCertRequestInfo);
|
| + cert_request_info->cert_authorities = test_authorities();
|
| + base::MessageLoop::current()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&MockClientCertURLRequestJob::NotifyCertificateRequested,
|
| + this, cert_request_info));
|
| + }
|
| +
|
| + void ContinueWithCertificate(net::X509Certificate* cert) override {
|
| + net::URLRequestTestJob::Start();
|
| + }
|
| +
|
| + private:
|
| + ~MockClientCertURLRequestJob() override {}
|
| +
|
| + 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);
|
| + }
|
| +};
|
| +
|
| // Arbitrary read buffer size.
|
| const int kReadBufSize = 1024;
|
|
|
| @@ -239,25 +310,28 @@ class SelectCertificateBrowserClient : public TestContentBrowserClient {
|
| SelectCertificateBrowserClient() : call_count_(0) {}
|
|
|
| void SelectClientCertificate(
|
| - int render_process_id,
|
| - int render_view_id,
|
| + WebContents* web_contents,
|
| net::SSLCertRequestInfo* cert_request_info,
|
| - const base::Callback<void(net::X509Certificate*)>& callback) override {
|
| + scoped_ptr<ClientCertificateDelegate> delegate) override {
|
| ++call_count_;
|
| passed_certs_ = cert_request_info->client_certs;
|
| + delegate_ = delegate.Pass();
|
| }
|
|
|
| - int call_count() {
|
| - return call_count_;
|
| - }
|
| + int call_count() { return call_count_; }
|
| + net::CertificateList passed_certs() { return passed_certs_; }
|
|
|
| - net::CertificateList passed_certs() {
|
| - return passed_certs_;
|
| + void ContinueWithCertificate(net::X509Certificate* cert) {
|
| + delegate_->ContinueWithCertificate(cert);
|
| + delegate_.reset();
|
| }
|
|
|
| + void CancelCertificateSelection() { delegate_.reset(); }
|
| +
|
| private:
|
| net::CertificateList passed_certs_;
|
| int call_count_;
|
| + scoped_ptr<ClientCertificateDelegate> delegate_;
|
| };
|
|
|
| class ResourceContextStub : public MockResourceContext {
|
| @@ -297,19 +371,19 @@ class ResourceLoaderTest : public testing::Test,
|
| resource_context_(&test_url_request_context_),
|
| raw_ptr_resource_handler_(NULL),
|
| raw_ptr_to_request_(NULL) {
|
| - job_factory_.SetProtocolHandler(
|
| - "test", net::URLRequestTestJob::CreateProtocolHandler());
|
| test_url_request_context_.set_job_factory(&job_factory_);
|
| }
|
|
|
| - GURL test_url() const {
|
| - return net::URLRequestTestJob::test_url_1();
|
| - }
|
| + GURL test_url() const { return net::URLRequestTestJob::test_url_1(); }
|
|
|
| std::string test_data() const {
|
| return net::URLRequestTestJob::test_data_1();
|
| }
|
|
|
| + virtual net::URLRequestJobFactory::ProtocolHandler* CreateProtocolHandler() {
|
| + return net::URLRequestTestJob::CreateProtocolHandler();
|
| + }
|
| +
|
| virtual scoped_ptr<ResourceHandler> WrapResourceHandler(
|
| scoped_ptr<ResourceHandlerStub> leaf_handler,
|
| net::URLRequest* request) {
|
| @@ -317,8 +391,14 @@ class ResourceLoaderTest : public testing::Test,
|
| }
|
|
|
| void SetUp() override {
|
| - const int kRenderProcessId = 1;
|
| - const int kRenderViewId = 2;
|
| + job_factory_.SetProtocolHandler("test", CreateProtocolHandler());
|
| +
|
| + browser_context_.reset(new TestBrowserContext());
|
| + scoped_refptr<SiteInstance> site_instance =
|
| + SiteInstance::Create(browser_context_.get());
|
| + web_contents_.reset(
|
| + TestWebContents::Create(browser_context_.get(), site_instance.get()));
|
| + RenderFrameHost* rfh = web_contents_->GetMainFrame();
|
|
|
| scoped_ptr<net::URLRequest> request(
|
| resource_context_.GetRequestContext()->CreateRequest(
|
| @@ -327,16 +407,12 @@ class ResourceLoaderTest : public testing::Test,
|
| NULL /* delegate */,
|
| NULL /* cookie_store */));
|
| raw_ptr_to_request_ = request.get();
|
| - ResourceRequestInfo::AllocateForTesting(request.get(),
|
| - RESOURCE_TYPE_MAIN_FRAME,
|
| - &resource_context_,
|
| - kRenderProcessId,
|
| - kRenderViewId,
|
| - MSG_ROUTING_NONE,
|
| - true, // is_main_frame
|
| - false, // parent_is_main_frame
|
| - true, // allow_download
|
| - false); // is_async
|
| + ResourceRequestInfo::AllocateForTesting(
|
| + request.get(), RESOURCE_TYPE_MAIN_FRAME, &resource_context_,
|
| + rfh->GetProcess()->GetID(), rfh->GetRenderViewHost()->GetRoutingID(),
|
| + rfh->GetRoutingID(), true /* is_main_frame */,
|
| + false /* parent_is_main_frame */, true /* allow_download */,
|
| + false /* is_async */);
|
| scoped_ptr<ResourceHandlerStub> resource_handler(
|
| new ResourceHandlerStub(request.get()));
|
| raw_ptr_resource_handler_ = resource_handler.get();
|
| @@ -346,6 +422,14 @@ class ResourceLoaderTest : public testing::Test,
|
| this));
|
| }
|
|
|
| + 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();
|
| + base::RunLoop().RunUntilIdle();
|
| + }
|
| +
|
| // ResourceLoaderDelegate:
|
| ResourceDispatcherHostLoginDelegate* CreateLoginDelegate(
|
| ResourceLoader* loader,
|
| @@ -362,11 +446,14 @@ class ResourceLoaderTest : public testing::Test,
|
| void DidReceiveResponse(ResourceLoader* loader) override {}
|
| void DidFinishLoading(ResourceLoader* loader) override {}
|
|
|
| - content::TestBrowserThreadBundle thread_bundle_;
|
| + TestBrowserThreadBundle thread_bundle_;
|
| + RenderViewHostTestEnabler rvh_test_enabler_;
|
|
|
| net::URLRequestJobFactoryImpl job_factory_;
|
| net::TestURLRequestContext test_url_request_context_;
|
| ResourceContextStub resource_context_;
|
| + scoped_ptr<TestBrowserContext> browser_context_;
|
| + scoped_ptr<TestWebContents> web_contents_;
|
|
|
| // The ResourceLoader owns the URLRequest and the ResourceHandler.
|
| ResourceHandlerStub* raw_ptr_resource_handler_;
|
| @@ -374,11 +461,15 @@ class ResourceLoaderTest : public testing::Test,
|
| scoped_ptr<ResourceLoader> loader_;
|
| };
|
|
|
| -// Verifies if a call to net::UrlRequest::Delegate::OnCertificateRequested()
|
| -// causes client cert store to be queried for certificates and if the returned
|
| -// certificates are correctly passed to the content browser client for
|
| -// selection.
|
| -TEST_F(ResourceLoaderTest, ClientCertStoreLookup) {
|
| +class ClientCertResourceLoaderTest : public ResourceLoaderTest {
|
| + protected:
|
| + net::URLRequestJobFactory::ProtocolHandler* CreateProtocolHandler() override {
|
| + return new MockClientCertJobProtocolHandler;
|
| + }
|
| +};
|
| +
|
| +// Tests that client certificates are requested with ClientCertStore lookup.
|
| +TEST_F(ClientCertResourceLoaderTest, WithStoreLookup) {
|
| // Set up the test client cert store.
|
| int store_request_count;
|
| std::vector<std::string> store_requested_authorities;
|
| @@ -388,91 +479,125 @@ TEST_F(ResourceLoaderTest, ClientCertStoreLookup) {
|
| dummy_certs, &store_request_count, &store_requested_authorities));
|
| resource_context_.SetClientCertStore(test_store.Pass());
|
|
|
| - // Prepare a dummy certificate request.
|
| - scoped_refptr<net::SSLCertRequestInfo> cert_request_info(
|
| - new net::SSLCertRequestInfo());
|
| - std::vector<std::string> dummy_authority(1, "dummy");
|
| - cert_request_info->cert_authorities = dummy_authority;
|
| -
|
| // Plug in test content browser client.
|
| SelectCertificateBrowserClient test_client;
|
| ContentBrowserClient* old_client = SetBrowserClientForTesting(&test_client);
|
|
|
| - // Everything is set up. Trigger the resource loader certificate request event
|
| - // and run the message loop.
|
| - loader_->OnCertificateRequested(raw_ptr_to_request_, cert_request_info.get());
|
| + // Start the request and wait for it to pause.
|
| + loader_->StartRequest();
|
| base::RunLoop().RunUntilIdle();
|
|
|
| - // Restore the original content browser client.
|
| - SetBrowserClientForTesting(old_client);
|
| + EXPECT_FALSE(raw_ptr_resource_handler_->received_response_completed());
|
|
|
| // Check if the test store was queried against correct |cert_authorities|.
|
| EXPECT_EQ(1, store_request_count);
|
| - EXPECT_EQ(dummy_authority, store_requested_authorities);
|
| + 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(dummy_certs, test_client.passed_certs());
|
| -}
|
|
|
| -// Verifies if a call to net::URLRequest::Delegate::OnCertificateRequested()
|
| -// on a platform with a NULL client cert store still calls the content browser
|
| -// client for selection.
|
| -TEST_F(ResourceLoaderTest, ClientCertStoreNull) {
|
| - // Prepare a dummy certificate request.
|
| - scoped_refptr<net::SSLCertRequestInfo> cert_request_info(
|
| - new net::SSLCertRequestInfo());
|
| - std::vector<std::string> dummy_authority(1, "dummy");
|
| - cert_request_info->cert_authorities = dummy_authority;
|
| + // Continue the request.
|
| + test_client.ContinueWithCertificate(dummy_certs[0].get());
|
| + base::RunLoop().RunUntilIdle();
|
| + EXPECT_TRUE(raw_ptr_resource_handler_->received_response_completed());
|
| + EXPECT_EQ(net::OK, raw_ptr_resource_handler_->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);
|
|
|
| - // Everything is set up. Trigger the resource loader certificate request event
|
| - // and run the message loop.
|
| - loader_->OnCertificateRequested(raw_ptr_to_request_, cert_request_info.get());
|
| + // Start the request and wait for it to pause.
|
| + loader_->StartRequest();
|
| base::RunLoop().RunUntilIdle();
|
|
|
| + // Check if the SelectClientCertificate was called on the content browser
|
| + // client.
|
| + EXPECT_EQ(1, test_client.call_count());
|
| + EXPECT_EQ(net::CertificateList(), test_client.passed_certs());
|
| +
|
| + // Continue the request.
|
| + scoped_refptr<net::X509Certificate> cert(
|
| + new net::X509Certificate("test", "test", base::Time(), base::Time()));
|
| + test_client.ContinueWithCertificate(cert.get());
|
| + base::RunLoop().RunUntilIdle();
|
| + EXPECT_TRUE(raw_ptr_resource_handler_->received_response_completed());
|
| + EXPECT_EQ(net::OK, raw_ptr_resource_handler_->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();
|
| + base::RunLoop().RunUntilIdle();
|
|
|
| // Check if the SelectClientCertificate was called on the content browser
|
| // client.
|
| EXPECT_EQ(1, test_client.call_count());
|
| EXPECT_EQ(net::CertificateList(), test_client.passed_certs());
|
| +
|
| + // Cancel the request.
|
| + test_client.CancelCertificateSelection();
|
| + base::RunLoop().RunUntilIdle();
|
| + EXPECT_TRUE(raw_ptr_resource_handler_->received_response_completed());
|
| + EXPECT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED,
|
| + raw_ptr_resource_handler_->status().error());
|
| +
|
| + // Restore the original content browser client.
|
| + SetBrowserClientForTesting(old_client);
|
| }
|
|
|
| -TEST_F(ResourceLoaderTest, ClientCertStoreAsyncCancel) {
|
| - // Set up the test client cert store.
|
| - int store_request_count;
|
| - std::vector<std::string> store_requested_authorities;
|
| - scoped_ptr<ClientCertStoreStub> test_store(
|
| - new ClientCertStoreStub(net::CertificateList(), &store_request_count,
|
| - &store_requested_authorities));
|
| - test_store->set_async(true);
|
| - EXPECT_EQ(0, store_request_count);
|
| - resource_context_.SetClientCertStore(test_store.Pass());
|
| +// Verifies that requests without WebContents attached abort.
|
| +TEST_F(ClientCertResourceLoaderTest, NoWebContents) {
|
| + // Destroy the WebContents before starting the request.
|
| + web_contents_.reset();
|
|
|
| - // Prepare a dummy certificate request.
|
| - scoped_refptr<net::SSLCertRequestInfo> cert_request_info(
|
| - new net::SSLCertRequestInfo());
|
| - std::vector<std::string> dummy_authority(1, "dummy");
|
| - cert_request_info->cert_authorities = dummy_authority;
|
| + // Plug in test content browser client.
|
| + SelectCertificateBrowserClient test_client;
|
| + ContentBrowserClient* old_client = SetBrowserClientForTesting(&test_client);
|
|
|
| - // Everything is set up. Trigger the resource loader certificate request
|
| - // event.
|
| - loader_->OnCertificateRequested(raw_ptr_to_request_, cert_request_info.get());
|
| + // Start the request and wait for it to pause.
|
| + loader_->StartRequest();
|
| + base::RunLoop().RunUntilIdle();
|
|
|
| - // Check if the test store was queried against correct |cert_authorities|.
|
| - EXPECT_EQ(1, store_request_count);
|
| - EXPECT_EQ(dummy_authority, store_requested_authorities);
|
| + // Check that SelectClientCertificate wasn't called and the request aborted.
|
| + EXPECT_EQ(0, test_client.call_count());
|
| + EXPECT_TRUE(raw_ptr_resource_handler_->received_response_completed());
|
| + EXPECT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED,
|
| + raw_ptr_resource_handler_->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) {
|
| + scoped_ptr<LoaderDestroyingCertStore> test_store(
|
| + new LoaderDestroyingCertStore(&loader_));
|
| + resource_context_.SetClientCertStore(test_store.Pass());
|
|
|
| - // Cancel the request before the store calls the callback.
|
| - loader_.reset();
|
| + loader_->StartRequest();
|
| + base::RunLoop().RunUntilIdle();
|
| + EXPECT_FALSE(loader_);
|
|
|
| - // Pump the event loop. There shouldn't be a crash when the callback is run.
|
| + // Pump the event loop to ensure nothing asynchronous crashes either.
|
| base::RunLoop().RunUntilIdle();
|
| }
|
|
|
|
|