OLD | NEW |
| (Empty) |
1 // Copyright 2015 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. | |
4 | |
5 #include "content/browser/loader/async_revalidation_driver.h" | |
6 | |
7 #include <string> | |
8 #include <type_traits> | |
9 #include <utility> | |
10 #include <vector> | |
11 | |
12 #include "base/bind.h" | |
13 #include "base/callback.h" | |
14 #include "base/location.h" | |
15 #include "base/macros.h" | |
16 #include "base/memory/ptr_util.h" | |
17 #include "base/run_loop.h" | |
18 #include "base/threading/thread_task_runner_handle.h" | |
19 #include "content/public/browser/client_certificate_delegate.h" | |
20 #include "content/public/common/content_client.h" | |
21 #include "content/public/test/test_browser_thread_bundle.h" | |
22 #include "content/test/test_content_browser_client.h" | |
23 #include "ipc/ipc_message.h" | |
24 #include "net/base/net_errors.h" | |
25 #include "net/base/network_delegate_impl.h" | |
26 #include "net/base/request_priority.h" | |
27 #include "net/ssl/ssl_cert_request_info.h" | |
28 #include "net/url_request/url_request_job_factory.h" | |
29 #include "net/url_request/url_request_job_factory_impl.h" | |
30 #include "net/url_request/url_request_status.h" | |
31 #include "net/url_request/url_request_test_job.h" | |
32 #include "net/url_request/url_request_test_util.h" | |
33 #include "testing/gtest/include/gtest/gtest.h" | |
34 | |
35 namespace content { | |
36 namespace { | |
37 | |
38 // Dummy implementation of ResourceThrottle, an instance of which is needed to | |
39 // initialize AsyncRevalidationDriver. | |
40 class ResourceThrottleStub : public ResourceThrottle { | |
41 public: | |
42 ResourceThrottleStub() {} | |
43 | |
44 // If true, defers the request in WillStartRequest. | |
45 void set_defer_request_on_will_start_request( | |
46 bool defer_request_on_will_start_request) { | |
47 defer_request_on_will_start_request_ = defer_request_on_will_start_request; | |
48 } | |
49 | |
50 // ResourceThrottler implementation: | |
51 void WillStartRequest(bool* defer) override { | |
52 *defer = defer_request_on_will_start_request_; | |
53 } | |
54 | |
55 const char* GetNameForLogging() const override { | |
56 return "ResourceThrottleStub"; | |
57 } | |
58 | |
59 private: | |
60 bool defer_request_on_will_start_request_ = false; | |
61 | |
62 DISALLOW_COPY_AND_ASSIGN(ResourceThrottleStub); | |
63 }; | |
64 | |
65 // There are multiple layers of boilerplate needed to use a URLRequestTestJob | |
66 // subclass. Subclasses of AsyncRevalidationDriverTest can use | |
67 // BindCreateProtocolHandlerCallback() to bypass most of that boilerplate. | |
68 using CreateProtocolHandlerCallback = base::Callback< | |
69 std::unique_ptr<net::URLRequestJobFactory::ProtocolHandler>()>; | |
70 | |
71 template <typename T> | |
72 CreateProtocolHandlerCallback BindCreateProtocolHandlerCallback() { | |
73 static_assert(std::is_base_of<net::URLRequestJob, T>::value, | |
74 "Template argument to BindCreateProtocolHandlerCallback() must " | |
75 "be a subclass of URLRequestJob."); | |
76 | |
77 class TemplatedProtocolHandler | |
78 : public net::URLRequestJobFactory::ProtocolHandler { | |
79 public: | |
80 static std::unique_ptr<net::URLRequestJobFactory::ProtocolHandler> | |
81 Create() { | |
82 return base::MakeUnique<TemplatedProtocolHandler>(); | |
83 } | |
84 | |
85 // URLRequestJobFactory::ProtocolHandler implementation: | |
86 net::URLRequestJob* MaybeCreateJob( | |
87 net::URLRequest* request, | |
88 net::NetworkDelegate* network_delegate) const override { | |
89 return new T(request, network_delegate); | |
90 } | |
91 }; | |
92 | |
93 return base::Bind(&TemplatedProtocolHandler::Create); | |
94 } | |
95 | |
96 // An implementation of NetworkDelegate that captures the status of the last | |
97 // URLRequest to be destroyed. | |
98 class StatusCapturingNetworkDelegate : public net::NetworkDelegateImpl { | |
99 public: | |
100 const net::URLRequestStatus& last_status() { return last_status_; } | |
101 | |
102 private: | |
103 // net::NetworkDelegate implementation. | |
104 void OnURLRequestDestroyed(net::URLRequest* request) override { | |
105 last_status_ = request->status(); | |
106 } | |
107 | |
108 net::URLRequestStatus last_status_; | |
109 }; | |
110 | |
111 class AsyncRevalidationDriverTest : public testing::Test { | |
112 protected: | |
113 // Constructor for test fixtures that subclass this one. | |
114 AsyncRevalidationDriverTest( | |
115 const CreateProtocolHandlerCallback& create_protocol_handler_callback) | |
116 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP), | |
117 create_protocol_handler_callback_(create_protocol_handler_callback), | |
118 raw_ptr_resource_throttle_(nullptr), | |
119 raw_ptr_request_(nullptr) { | |
120 test_url_request_context_.set_job_factory(&job_factory_); | |
121 test_url_request_context_.set_network_delegate(&network_delegate_); | |
122 } | |
123 | |
124 // Constructor for tests that use this fixture directly. | |
125 AsyncRevalidationDriverTest() | |
126 : AsyncRevalidationDriverTest( | |
127 base::Bind(net::URLRequestTestJob::CreateProtocolHandler)) {} | |
128 | |
129 bool async_revalidation_complete_called() const { | |
130 return async_revalidation_complete_called_; | |
131 } | |
132 | |
133 const net::URLRequestStatus& last_status() { | |
134 return network_delegate_.last_status(); | |
135 } | |
136 | |
137 void SetUpAsyncRevalidationDriverWithRequestToUrl(const GURL& url) { | |
138 std::unique_ptr<net::URLRequest> request( | |
139 test_url_request_context_.CreateRequest(url, net::DEFAULT_PRIORITY, | |
140 nullptr /* delegate */)); | |
141 raw_ptr_request_ = request.get(); | |
142 raw_ptr_resource_throttle_ = new ResourceThrottleStub(); | |
143 // This use of base::Unretained() is safe because |driver_|, and the closure | |
144 // passed to it, will be destroyed before this object is. | |
145 driver_.reset(new AsyncRevalidationDriver( | |
146 std::move(request), base::WrapUnique(raw_ptr_resource_throttle_), | |
147 base::Bind(&AsyncRevalidationDriverTest::OnAsyncRevalidationComplete, | |
148 base::Unretained(this)))); | |
149 } | |
150 | |
151 void SetUpAsyncRevalidationDriverWithDefaultRequest() { | |
152 SetUpAsyncRevalidationDriverWithRequestToUrl( | |
153 net::URLRequestTestJob::test_url_1()); | |
154 } | |
155 | |
156 void SetUp() override { | |
157 job_factory_.SetProtocolHandler("test", | |
158 create_protocol_handler_callback_.Run()); | |
159 SetUpAsyncRevalidationDriverWithDefaultRequest(); | |
160 } | |
161 | |
162 void OnAsyncRevalidationComplete() { | |
163 EXPECT_FALSE(async_revalidation_complete_called_); | |
164 async_revalidation_complete_called_ = true; | |
165 driver_.reset(); | |
166 } | |
167 | |
168 TestBrowserThreadBundle thread_bundle_; | |
169 net::URLRequestJobFactoryImpl job_factory_; | |
170 net::TestURLRequestContext test_url_request_context_; | |
171 StatusCapturingNetworkDelegate network_delegate_; | |
172 CreateProtocolHandlerCallback create_protocol_handler_callback_; | |
173 | |
174 // The AsyncRevalidationDriver owns the URLRequest and the ResourceThrottle. | |
175 ResourceThrottleStub* raw_ptr_resource_throttle_; | |
176 net::URLRequest* raw_ptr_request_; | |
177 std::unique_ptr<AsyncRevalidationDriver> driver_; | |
178 bool async_revalidation_complete_called_ = false; | |
179 }; | |
180 | |
181 TEST_F(AsyncRevalidationDriverTest, NormalRequestCompletes) { | |
182 driver_->StartRequest(); | |
183 base::RunLoop().RunUntilIdle(); | |
184 EXPECT_TRUE(async_revalidation_complete_called()); | |
185 } | |
186 | |
187 // Verifies that request that should be deferred at start is deferred. | |
188 TEST_F(AsyncRevalidationDriverTest, DeferOnStart) { | |
189 raw_ptr_resource_throttle_->set_defer_request_on_will_start_request(true); | |
190 | |
191 driver_->StartRequest(); | |
192 base::RunLoop().RunUntilIdle(); | |
193 EXPECT_FALSE(raw_ptr_request_->is_pending()); | |
194 EXPECT_FALSE(async_revalidation_complete_called()); | |
195 } | |
196 | |
197 // Verifies that resuming a deferred request works. Assumes that DeferOnStart | |
198 // passes. | |
199 TEST_F(AsyncRevalidationDriverTest, ResumeDeferredRequestWorks) { | |
200 raw_ptr_resource_throttle_->set_defer_request_on_will_start_request(true); | |
201 | |
202 driver_->StartRequest(); | |
203 base::RunLoop().RunUntilIdle(); | |
204 | |
205 ResourceThrottle::Delegate* driver_as_resource_throttle_delegate = | |
206 driver_.get(); | |
207 driver_as_resource_throttle_delegate->Resume(); | |
208 base::RunLoop().RunUntilIdle(); | |
209 EXPECT_TRUE(async_revalidation_complete_called()); | |
210 } | |
211 | |
212 // Verifies that redirects are not followed. | |
213 TEST_F(AsyncRevalidationDriverTest, RedirectsAreNotFollowed) { | |
214 SetUpAsyncRevalidationDriverWithRequestToUrl( | |
215 net::URLRequestTestJob::test_url_redirect_to_url_2()); | |
216 | |
217 driver_->StartRequest(); | |
218 while (net::URLRequestTestJob::ProcessOnePendingMessage()) | |
219 base::RunLoop().RunUntilIdle(); | |
220 base::RunLoop().RunUntilIdle(); | |
221 EXPECT_FALSE(last_status().is_success()); | |
222 EXPECT_EQ(net::ERR_ABORTED, last_status().error()); | |
223 EXPECT_TRUE(async_revalidation_complete_called()); | |
224 } | |
225 | |
226 // A mock URLRequestJob which simulates an SSL client auth request. | |
227 class MockClientCertURLRequestJob : public net::URLRequestTestJob { | |
228 public: | |
229 MockClientCertURLRequestJob(net::URLRequest* request, | |
230 net::NetworkDelegate* network_delegate) | |
231 : net::URLRequestTestJob(request, network_delegate, true), | |
232 weak_factory_(this) {} | |
233 | |
234 // net::URLRequestTestJob implementation: | |
235 void Start() override { | |
236 scoped_refptr<net::SSLCertRequestInfo> cert_request_info( | |
237 new net::SSLCertRequestInfo); | |
238 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
239 FROM_HERE, | |
240 base::Bind(&MockClientCertURLRequestJob::NotifyCertificateRequested, | |
241 weak_factory_.GetWeakPtr(), | |
242 base::RetainedRef(cert_request_info))); | |
243 } | |
244 | |
245 void ContinueWithCertificate( | |
246 net::X509Certificate* cert, | |
247 net::SSLPrivateKey* client_private_key) override { | |
248 ADD_FAILURE() << "Certificate supplied."; | |
249 } | |
250 | |
251 void Kill() override { | |
252 weak_factory_.InvalidateWeakPtrs(); | |
253 URLRequestJob::Kill(); | |
254 } | |
255 | |
256 private: | |
257 base::WeakPtrFactory<MockClientCertURLRequestJob> weak_factory_; | |
258 }; | |
259 | |
260 class AsyncRevalidationDriverClientCertTest | |
261 : public AsyncRevalidationDriverTest { | |
262 protected: | |
263 AsyncRevalidationDriverClientCertTest() | |
264 : AsyncRevalidationDriverTest( | |
265 BindCreateProtocolHandlerCallback<MockClientCertURLRequestJob>()) {} | |
266 }; | |
267 | |
268 // Test browser client that causes the test to fail if SelectClientCertificate() | |
269 // is called. Automatically sets itself as the browser client when constructed | |
270 // and restores the old browser client in the destructor. | |
271 class ScopedDontSelectCertificateBrowserClient | |
272 : public TestContentBrowserClient { | |
273 public: | |
274 ScopedDontSelectCertificateBrowserClient() { | |
275 old_client_ = SetBrowserClientForTesting(this); | |
276 } | |
277 | |
278 ~ScopedDontSelectCertificateBrowserClient() override { | |
279 SetBrowserClientForTesting(old_client_); | |
280 } | |
281 | |
282 void SelectClientCertificate( | |
283 WebContents* web_contents, | |
284 net::SSLCertRequestInfo* cert_request_info, | |
285 std::unique_ptr<ClientCertificateDelegate> delegate) override { | |
286 ADD_FAILURE() << "SelectClientCertificate was called."; | |
287 } | |
288 | |
289 private: | |
290 ContentBrowserClient* old_client_; | |
291 | |
292 DISALLOW_COPY_AND_ASSIGN(ScopedDontSelectCertificateBrowserClient); | |
293 }; | |
294 | |
295 // Verifies that async revalidation requests do not attempt to provide client | |
296 // certificates. | |
297 TEST_F(AsyncRevalidationDriverClientCertTest, RequestRejected) { | |
298 // Ensure that SelectClientCertificate is not called during this test. | |
299 ScopedDontSelectCertificateBrowserClient test_client; | |
300 | |
301 // Start the request and wait for it to pause. | |
302 driver_->StartRequest(); | |
303 | |
304 // Because TestBrowserThreadBundle only uses one real thread, this is | |
305 // sufficient to ensure that tasks posted to the "UI thread" have run. | |
306 base::RunLoop().RunUntilIdle(); | |
307 | |
308 // Check that the request aborted. | |
309 EXPECT_FALSE(last_status().is_success()); | |
310 EXPECT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED, last_status().error()); | |
311 EXPECT_TRUE(async_revalidation_complete_called()); | |
312 } | |
313 | |
314 // A mock URLRequestJob which simulates an SSL certificate error. | |
315 class MockSSLErrorURLRequestJob : public net::URLRequestTestJob { | |
316 public: | |
317 MockSSLErrorURLRequestJob(net::URLRequest* request, | |
318 net::NetworkDelegate* network_delegate) | |
319 : net::URLRequestTestJob(request, network_delegate, true), | |
320 weak_factory_(this) {} | |
321 | |
322 // net::URLRequestTestJob implementation: | |
323 void Start() override { | |
324 // This SSLInfo isn't really valid, but it is good enough for testing. | |
325 net::SSLInfo ssl_info; | |
326 ssl_info.SetCertError(net::ERR_CERT_DATE_INVALID); | |
327 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
328 FROM_HERE, | |
329 base::Bind(&MockSSLErrorURLRequestJob::NotifySSLCertificateError, | |
330 weak_factory_.GetWeakPtr(), ssl_info, false)); | |
331 } | |
332 | |
333 void ContinueDespiteLastError() override { | |
334 ADD_FAILURE() << "ContinueDespiteLastError called."; | |
335 } | |
336 | |
337 private: | |
338 base::WeakPtrFactory<MockSSLErrorURLRequestJob> weak_factory_; | |
339 }; | |
340 | |
341 class AsyncRevalidationDriverSSLErrorTest : public AsyncRevalidationDriverTest { | |
342 protected: | |
343 AsyncRevalidationDriverSSLErrorTest() | |
344 : AsyncRevalidationDriverTest( | |
345 BindCreateProtocolHandlerCallback<MockSSLErrorURLRequestJob>()) {} | |
346 }; | |
347 | |
348 // Verifies that async revalidation requests do not attempt to recover from SSL | |
349 // certificate errors. | |
350 TEST_F(AsyncRevalidationDriverSSLErrorTest, RequestWithSSLErrorRejected) { | |
351 // Start the request and wait for it to pause. | |
352 driver_->StartRequest(); | |
353 base::RunLoop().RunUntilIdle(); | |
354 | |
355 // Check that the request has been aborted. | |
356 EXPECT_FALSE(last_status().is_success()); | |
357 EXPECT_EQ(net::ERR_ABORTED, last_status().error()); | |
358 EXPECT_TRUE(async_revalidation_complete_called()); | |
359 } | |
360 | |
361 // A URLRequestTestJob that sets |request_time| and |was_cached| on their | |
362 // response_info, and causes the test to fail if Read() is called. | |
363 class FromCacheURLRequestJob : public net::URLRequestTestJob { | |
364 public: | |
365 FromCacheURLRequestJob(net::URLRequest* request, | |
366 net::NetworkDelegate* network_delegate) | |
367 : net::URLRequestTestJob(request, network_delegate, true) {} | |
368 | |
369 void GetResponseInfo(net::HttpResponseInfo* info) override { | |
370 URLRequestTestJob::GetResponseInfo(info); | |
371 info->request_time = base::Time::Now(); | |
372 info->was_cached = true; | |
373 } | |
374 | |
375 int ReadRawData(net::IOBuffer* buf, int buf_size) override { | |
376 ADD_FAILURE() << "ReadRawData() was called."; | |
377 return URLRequestTestJob::ReadRawData(buf, buf_size); | |
378 } | |
379 | |
380 private: | |
381 ~FromCacheURLRequestJob() override {} | |
382 | |
383 DISALLOW_COPY_AND_ASSIGN(FromCacheURLRequestJob); | |
384 }; | |
385 | |
386 class AsyncRevalidationDriverFromCacheTest | |
387 : public AsyncRevalidationDriverTest { | |
388 protected: | |
389 AsyncRevalidationDriverFromCacheTest() | |
390 : AsyncRevalidationDriverTest( | |
391 BindCreateProtocolHandlerCallback<FromCacheURLRequestJob>()) {} | |
392 }; | |
393 | |
394 TEST_F(AsyncRevalidationDriverFromCacheTest, | |
395 CacheNotReadOnSuccessfulRevalidation) { | |
396 driver_->StartRequest(); | |
397 base::RunLoop().RunUntilIdle(); | |
398 | |
399 EXPECT_TRUE(async_revalidation_complete_called()); | |
400 } | |
401 | |
402 } // namespace | |
403 } // namespace content | |
OLD | NEW |