| 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 |