1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/loader/resource_loader.h"
7 #include "base/run_loop.h"
8 #include "content/browser/browser_thread_impl.h"
9 #include "content/browser/loader/resource_loader_delegate.h"
10 #include "content/public/browser/resource_request_info.h"
11 #include "content/public/test/mock_resource_context.h"
12 #include "content/public/test/test_browser_thread_bundle.h"
13 #include "content/test/test_content_browser_client.h"
14 #include "ipc/ipc_message.h"
15 #include "net/base/request_priority.h"
16 #include "net/cert/x509_certificate.h"
17 #include "net/ssl/client_cert_store.h"
18 #include "net/ssl/ssl_cert_request_info.h"
19 #include "net/url_request/url_request.h"
20 #include "net/url_request/url_request_test_util.h"
21 #include "testing/gtest/include/gtest/gtest.h"
26 // Stub client certificate store that returns a preset list of certificates for
27 // each request and records the arguments of the most recent request for later
29 class ClientCertStoreStub
: public net::ClientCertStore
{
31 ClientCertStoreStub(const net::CertificateList
& certs
)
35 virtual ~ClientCertStoreStub() {}
37 // Returns |cert_authorities| field of the certificate request passed in the
38 // most recent call to GetClientCerts().
39 // TODO(ppi): Make the stub independent from the internal representation of
40 // SSLCertRequestInfo. For now it seems that we cannot neither save the
41 // scoped_refptr<> (since it is never passed to us) nor copy the entire
42 // CertificateRequestInfo (since there is no copy constructor).
43 std::vector
<std::string
> requested_authorities() {
44 return requested_authorities_
;
47 // Returns the number of calls to GetClientCerts().
49 return request_count_
;
52 // net::ClientCertStore:
53 virtual void GetClientCerts(const net::SSLCertRequestInfo
& cert_request_info
,
54 net::CertificateList
* selected_certs
,
55 const base::Closure
& callback
) OVERRIDE
{
57 requested_authorities_
= cert_request_info
.cert_authorities
;
58 *selected_certs
= response_
;
63 const net::CertificateList response_
;
65 std::vector
<std::string
> requested_authorities_
;
68 // Dummy implementation of ResourceHandler, instance of which is needed to
69 // initialize ResourceLoader.
70 class ResourceHandlerStub
: public ResourceHandler
{
72 ResourceHandlerStub() : ResourceHandler(NULL
) {}
74 virtual bool OnUploadProgress(int request_id
,
76 uint64 size
) OVERRIDE
{
80 virtual bool OnRequestRedirected(int request_id
,
82 ResourceResponse
* response
,
83 bool* defer
) OVERRIDE
{
87 virtual bool OnResponseStarted(int request_id
,
88 ResourceResponse
* response
,
89 bool* defer
) OVERRIDE
{ return true; }
91 virtual bool OnWillStart(int request_id
,
93 bool* defer
) OVERRIDE
{
97 virtual bool OnBeforeNetworkStart(int request_id
,
99 bool* defer
) OVERRIDE
{
103 virtual bool OnWillRead(int request_id
,
104 scoped_refptr
<net::IOBuffer
>* buf
,
106 int min_size
) OVERRIDE
{
110 virtual bool OnReadCompleted(int request_id
,
112 bool* defer
) OVERRIDE
{
116 virtual void OnResponseCompleted(int request_id
,
117 const net::URLRequestStatus
& status
,
118 const std::string
& security_info
,
119 bool* defer
) OVERRIDE
{
122 virtual void OnDataDownloaded(int request_id
,
123 int bytes_downloaded
) OVERRIDE
{}
126 // Test browser client that captures calls to SelectClientCertificates and
127 // records the arguments of the most recent call for later inspection.
128 class SelectCertificateBrowserClient
: public TestContentBrowserClient
{
130 SelectCertificateBrowserClient() : call_count_(0) {}
132 virtual void SelectClientCertificate(
133 int render_process_id
,
135 const net::HttpNetworkSession
* network_session
,
136 net::SSLCertRequestInfo
* cert_request_info
,
137 const base::Callback
<void(net::X509Certificate
*)>& callback
) OVERRIDE
{
139 passed_certs_
= cert_request_info
->client_certs
;
146 net::CertificateList
passed_certs() {
147 return passed_certs_
;
151 net::CertificateList passed_certs_
;
155 class ResourceContextStub
: public MockResourceContext
{
157 explicit ResourceContextStub(net::URLRequestContext
* test_request_context
)
158 : MockResourceContext(test_request_context
) {}
160 virtual scoped_ptr
<net::ClientCertStore
> CreateClientCertStore() OVERRIDE
{
161 return dummy_cert_store_
.Pass();
164 void SetClientCertStore(scoped_ptr
<net::ClientCertStore
> store
) {
165 dummy_cert_store_
= store
.Pass();
169 scoped_ptr
<net::ClientCertStore
> dummy_cert_store_
;
174 class ResourceLoaderTest
: public testing::Test
,
175 public ResourceLoaderDelegate
{
178 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP
),
179 resource_context_(&test_url_request_context_
) {
182 // ResourceLoaderDelegate:
183 virtual ResourceDispatcherHostLoginDelegate
* CreateLoginDelegate(
184 ResourceLoader
* loader
,
185 net::AuthChallengeInfo
* auth_info
) OVERRIDE
{
188 virtual bool HandleExternalProtocol(ResourceLoader
* loader
,
189 const GURL
& url
) OVERRIDE
{
192 virtual void DidStartRequest(ResourceLoader
* loader
) OVERRIDE
{}
193 virtual void DidReceiveRedirect(ResourceLoader
* loader
,
194 const GURL
& new_url
) OVERRIDE
{}
195 virtual void DidReceiveResponse(ResourceLoader
* loader
) OVERRIDE
{}
196 virtual void DidFinishLoading(ResourceLoader
* loader
) OVERRIDE
{}
198 content::TestBrowserThreadBundle thread_bundle_
;
200 net::TestURLRequestContext test_url_request_context_
;
201 ResourceContextStub resource_context_
;
204 // Verifies if a call to net::UrlRequest::Delegate::OnCertificateRequested()
205 // causes client cert store to be queried for certificates and if the returned
206 // certificates are correctly passed to the content browser client for
208 TEST_F(ResourceLoaderTest
, ClientCertStoreLookup
) {
209 const int kRenderProcessId
= 1;
210 const int kRenderViewId
= 2;
212 scoped_ptr
<net::URLRequest
> request(
213 new net::URLRequest(GURL("dummy"),
214 net::DEFAULT_PRIORITY
,
216 resource_context_
.GetRequestContext()));
217 ResourceRequestInfo::AllocateForTesting(request
.get(),
218 ResourceType::MAIN_FRAME
,
225 // Set up the test client cert store.
226 net::CertificateList
dummy_certs(1, scoped_refptr
<net::X509Certificate
>(
227 new net::X509Certificate("test", "test", base::Time(), base::Time())));
228 scoped_ptr
<ClientCertStoreStub
> test_store(
229 new ClientCertStoreStub(dummy_certs
));
230 EXPECT_EQ(0, test_store
->request_count());
232 // Ownership of the |request| and |test_store| is about to be turned over to
233 // ResourceLoader. We need to keep raw pointer copies to access these objects
235 net::URLRequest
* raw_ptr_to_request
= request
.get();
236 ClientCertStoreStub
* raw_ptr_to_store
= test_store
.get();
237 resource_context_
.SetClientCertStore(
238 test_store
.PassAs
<net::ClientCertStore
>());
240 scoped_ptr
<ResourceHandler
> resource_handler(new ResourceHandlerStub());
241 ResourceLoader
loader(request
.Pass(), resource_handler
.Pass(), this);
243 // Prepare a dummy certificate request.
244 scoped_refptr
<net::SSLCertRequestInfo
> cert_request_info(
245 new net::SSLCertRequestInfo());
246 std::vector
<std::string
> dummy_authority(1, "dummy");
247 cert_request_info
->cert_authorities
= dummy_authority
;
249 // Plug in test content browser client.
250 SelectCertificateBrowserClient test_client
;
251 ContentBrowserClient
* old_client
= SetBrowserClientForTesting(&test_client
);
253 // Everything is set up. Trigger the resource loader certificate request event
254 // and run the message loop.
255 loader
.OnCertificateRequested(raw_ptr_to_request
, cert_request_info
.get());
256 base::RunLoop().RunUntilIdle();
258 // Restore the original content browser client.
259 SetBrowserClientForTesting(old_client
);
261 // Check if the test store was queried against correct |cert_authorities|.
262 EXPECT_EQ(1, raw_ptr_to_store
->request_count());
263 EXPECT_EQ(dummy_authority
, raw_ptr_to_store
->requested_authorities());
265 // Check if the retrieved certificates were passed to the content browser
267 EXPECT_EQ(1, test_client
.call_count());
268 EXPECT_EQ(dummy_certs
, test_client
.passed_certs());
271 // Verifies if a call to net::URLRequest::Delegate::OnCertificateRequested()
272 // on a platform with a NULL client cert store still calls the content browser
273 // client for selection.
274 TEST_F(ResourceLoaderTest
, ClientCertStoreNull
) {
275 const int kRenderProcessId
= 1;
276 const int kRenderViewId
= 2;
278 scoped_ptr
<net::URLRequest
> request(
279 new net::URLRequest(GURL("dummy"),
280 net::DEFAULT_PRIORITY
,
282 resource_context_
.GetRequestContext()));
283 ResourceRequestInfo::AllocateForTesting(request
.get(),
284 ResourceType::MAIN_FRAME
,
291 // Ownership of the |request| is about to be turned over to ResourceLoader. We
292 // need to keep a raw pointer copy to access this object later.
293 net::URLRequest
* raw_ptr_to_request
= request
.get();
295 scoped_ptr
<ResourceHandler
> resource_handler(new ResourceHandlerStub());
296 ResourceLoader
loader(request
.Pass(), resource_handler
.Pass(), this);
298 // Prepare a dummy certificate request.
299 scoped_refptr
<net::SSLCertRequestInfo
> cert_request_info(
300 new net::SSLCertRequestInfo());
301 std::vector
<std::string
> dummy_authority(1, "dummy");
302 cert_request_info
->cert_authorities
= dummy_authority
;
304 // Plug in test content browser client.
305 SelectCertificateBrowserClient test_client
;
306 ContentBrowserClient
* old_client
= SetBrowserClientForTesting(&test_client
);
308 // Everything is set up. Trigger the resource loader certificate request event
309 // and run the message loop.
310 loader
.OnCertificateRequested(raw_ptr_to_request
, cert_request_info
.get());
311 base::RunLoop().RunUntilIdle();
313 // Restore the original content browser client.
314 SetBrowserClientForTesting(old_client
);
316 // Check if the SelectClientCertificate was called on the content browser
318 EXPECT_EQ(1, test_client
.call_count());
319 EXPECT_EQ(net::CertificateList(), test_client
.passed_certs());
322 } // namespace content