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 "net/base/request_priority.h"
15 #include "net/cert/x509_certificate.h"
16 #include "net/ssl/client_cert_store.h"
17 #include "net/ssl/ssl_cert_request_info.h"
18 #include "net/url_request/url_request.h"
19 #include "net/url_request/url_request_test_util.h"
20 #include "testing/gtest/include/gtest/gtest.h"
25 // Stub client certificate store that returns a preset list of certificates for
26 // each request and records the arguments of the most recent request for later
28 class ClientCertStoreStub : public net::ClientCertStore {
30 ClientCertStoreStub(const net::CertificateList& certs)
34 virtual ~ClientCertStoreStub() {}
36 // Returns |cert_authorities| field of the certificate request passed in the
37 // most recent call to GetClientCerts().
38 // TODO(ppi): Make the stub independent from the internal representation of
39 // SSLCertRequestInfo. For now it seems that we cannot neither save the
40 // scoped_refptr<> (since it is never passed to us) nor copy the entire
41 // CertificateRequestInfo (since there is no copy constructor).
42 std::vector<std::string> requested_authorities() {
43 return requested_authorities_;
46 // Returns the number of calls to GetClientCerts().
48 return request_count_;
51 // net::ClientCertStore:
52 virtual void GetClientCerts(const net::SSLCertRequestInfo& cert_request_info,
53 net::CertificateList* selected_certs,
54 const base::Closure& callback) OVERRIDE {
56 requested_authorities_ = cert_request_info.cert_authorities;
57 *selected_certs = response_;
62 const net::CertificateList response_;
64 std::vector<std::string> requested_authorities_;
67 // Dummy implementation of ResourceHandler, instance of which is needed to
68 // initialize ResourceLoader.
69 class ResourceHandlerStub : public ResourceHandler {
71 ResourceHandlerStub() : ResourceHandler(NULL) {}
73 virtual bool OnUploadProgress(int request_id,
75 uint64 size) OVERRIDE {
79 virtual bool OnRequestRedirected(int request_id,
81 ResourceResponse* response,
82 bool* defer) OVERRIDE {
86 virtual bool OnResponseStarted(int request_id,
87 ResourceResponse* response,
88 bool* defer) OVERRIDE { return true; }
90 virtual bool OnWillStart(int request_id,
92 bool* defer) OVERRIDE {
96 virtual bool OnWillRead(int request_id,
97 scoped_refptr<net::IOBuffer>* buf,
99 int min_size) OVERRIDE {
103 virtual bool OnReadCompleted(int request_id,
105 bool* defer) OVERRIDE {
109 virtual bool OnResponseCompleted(int request_id,
110 const net::URLRequestStatus& status,
111 const std::string& security_info) OVERRIDE {
115 virtual void OnDataDownloaded(int request_id,
116 int bytes_downloaded) OVERRIDE {}
119 // Test browser client that captures calls to SelectClientCertificates and
120 // records the arguments of the most recent call for later inspection.
121 class SelectCertificateBrowserClient : public TestContentBrowserClient {
123 SelectCertificateBrowserClient() : call_count_(0) {}
125 virtual void SelectClientCertificate(
126 int render_process_id,
128 const net::HttpNetworkSession* network_session,
129 net::SSLCertRequestInfo* cert_request_info,
130 const base::Callback<void(net::X509Certificate*)>& callback) OVERRIDE {
132 passed_certs_ = cert_request_info->client_certs;
139 net::CertificateList passed_certs() {
140 return passed_certs_;
144 net::CertificateList passed_certs_;
148 class ResourceContextStub : public MockResourceContext {
150 explicit ResourceContextStub(net::URLRequestContext* test_request_context)
151 : MockResourceContext(test_request_context) {}
153 virtual scoped_ptr<net::ClientCertStore> CreateClientCertStore() OVERRIDE {
154 return dummy_cert_store_.Pass();
157 void SetClientCertStore(scoped_ptr<net::ClientCertStore> store) {
158 dummy_cert_store_ = store.Pass();
162 scoped_ptr<net::ClientCertStore> dummy_cert_store_;
167 class ResourceLoaderTest : public testing::Test,
168 public ResourceLoaderDelegate {
171 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
172 resource_context_(&test_url_request_context_) {
175 // ResourceLoaderDelegate:
176 virtual ResourceDispatcherHostLoginDelegate* CreateLoginDelegate(
177 ResourceLoader* loader,
178 net::AuthChallengeInfo* auth_info) OVERRIDE {
181 virtual bool AcceptAuthRequest(
182 ResourceLoader* loader,
183 net::AuthChallengeInfo* auth_info) OVERRIDE {
186 virtual bool AcceptSSLClientCertificateRequest(
187 ResourceLoader* loader,
188 net::SSLCertRequestInfo* cert_info) OVERRIDE {
191 virtual bool HandleExternalProtocol(ResourceLoader* loader,
192 const GURL& url) OVERRIDE {
195 virtual void DidStartRequest(ResourceLoader* loader) OVERRIDE {}
196 virtual void DidReceiveRedirect(ResourceLoader* loader,
197 const GURL& new_url) OVERRIDE {}
198 virtual void DidReceiveResponse(ResourceLoader* loader) OVERRIDE {}
199 virtual void DidFinishLoading(ResourceLoader* loader) OVERRIDE {}
201 content::TestBrowserThreadBundle thread_bundle_;
203 net::TestURLRequestContext test_url_request_context_;
204 ResourceContextStub resource_context_;
207 // Verifies if a call to net::UrlRequest::Delegate::OnCertificateRequested()
208 // causes client cert store to be queried for certificates and if the returned
209 // certificates are correctly passed to the content browser client for
211 TEST_F(ResourceLoaderTest, ClientCertStoreLookup) {
212 const int kRenderProcessId = 1;
213 const int kRenderViewId = 2;
215 scoped_ptr<net::URLRequest> request(
216 new net::URLRequest(GURL("dummy"),
217 net::DEFAULT_PRIORITY,
219 resource_context_.GetRequestContext()));
220 ResourceRequestInfo::AllocateForTesting(request.get(),
221 ResourceType::MAIN_FRAME,
227 // Set up the test client cert store.
228 net::CertificateList dummy_certs(1, scoped_refptr<net::X509Certificate>(
229 new net::X509Certificate("test", "test", base::Time(), base::Time())));
230 scoped_ptr<ClientCertStoreStub> test_store(
231 new ClientCertStoreStub(dummy_certs));
232 EXPECT_EQ(0, test_store->request_count());
234 // Ownership of the |request| and |test_store| is about to be turned over to
235 // ResourceLoader. We need to keep raw pointer copies to access these objects
237 net::URLRequest* raw_ptr_to_request = request.get();
238 ClientCertStoreStub* raw_ptr_to_store = test_store.get();
239 resource_context_.SetClientCertStore(
240 test_store.PassAs<net::ClientCertStore>());
242 scoped_ptr<ResourceHandler> resource_handler(new ResourceHandlerStub());
243 ResourceLoader loader(request.Pass(), resource_handler.Pass(), this);
245 // Prepare a dummy certificate request.
246 scoped_refptr<net::SSLCertRequestInfo> cert_request_info(
247 new net::SSLCertRequestInfo());
248 std::vector<std::string> dummy_authority(1, "dummy");
249 cert_request_info->cert_authorities = dummy_authority;
251 // Plug in test content browser client.
252 SelectCertificateBrowserClient test_client;
253 ContentBrowserClient* old_client = SetBrowserClientForTesting(&test_client);
255 // Everything is set up. Trigger the resource loader certificate request event
256 // and run the message loop.
257 loader.OnCertificateRequested(raw_ptr_to_request, cert_request_info.get());
258 base::RunLoop().RunUntilIdle();
260 // Restore the original content browser client.
261 SetBrowserClientForTesting(old_client);
263 // Check if the test store was queried against correct |cert_authorities|.
264 EXPECT_EQ(1, raw_ptr_to_store->request_count());
265 EXPECT_EQ(dummy_authority, raw_ptr_to_store->requested_authorities());
267 // Check if the retrieved certificates were passed to the content browser
269 EXPECT_EQ(1, test_client.call_count());
270 EXPECT_EQ(dummy_certs, test_client.passed_certs());
273 // Verifies if a call to net::URLRequest::Delegate::OnCertificateRequested()
274 // on a platform with a NULL client cert store still calls the content browser
275 // client for selection.
276 TEST_F(ResourceLoaderTest, ClientCertStoreNull) {
277 const int kRenderProcessId = 1;
278 const int kRenderViewId = 2;
280 scoped_ptr<net::URLRequest> request(
281 new net::URLRequest(GURL("dummy"),
282 net::DEFAULT_PRIORITY,
284 resource_context_.GetRequestContext()));
285 ResourceRequestInfo::AllocateForTesting(request.get(),
286 ResourceType::MAIN_FRAME,
292 // Ownership of the |request| is about to be turned over to ResourceLoader. We
293 // need to keep a raw pointer copy to access this object later.
294 net::URLRequest* raw_ptr_to_request = request.get();
296 scoped_ptr<ResourceHandler> resource_handler(new ResourceHandlerStub());
297 ResourceLoader loader(request.Pass(), resource_handler.Pass(), this);
299 // Prepare a dummy certificate request.
300 scoped_refptr<net::SSLCertRequestInfo> cert_request_info(
301 new net::SSLCertRequestInfo());
302 std::vector<std::string> dummy_authority(1, "dummy");
303 cert_request_info->cert_authorities = dummy_authority;
305 // Plug in test content browser client.
306 SelectCertificateBrowserClient test_client;
307 ContentBrowserClient* old_client = SetBrowserClientForTesting(&test_client);
309 // Everything is set up. Trigger the resource loader certificate request event
310 // and run the message loop.
311 loader.OnCertificateRequested(raw_ptr_to_request, cert_request_info.get());
312 base::RunLoop().RunUntilIdle();
314 // Restore the original content browser client.
315 SetBrowserClientForTesting(old_client);
317 // Check if the SelectClientCertificate was called on the content browser
319 EXPECT_EQ(1, test_client.call_count());
320 EXPECT_EQ(net::CertificateList(), test_client.passed_certs());
323 } // namespace content