1 // Copyright (c) 2012 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 "chrome/browser/net/connection_tester.h" 6 7 #include "base/bind.h" 8 #include "base/command_line.h" 9 #include "base/compiler_specific.h" 10 #include "base/logging.h" 11 #include "base/memory/weak_ptr.h" 12 #include "base/message_loop/message_loop.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "base/thread_task_runner_handle.h" 15 #include "base/threading/thread_restrictions.h" 16 #include "chrome/common/chrome_switches.h" 17 #include "content/public/browser/browser_thread.h" 18 #include "content/public/browser/cookie_store_factory.h" 19 #include "net/base/io_buffer.h" 20 #include "net/base/net_errors.h" 21 #include "net/base/net_util.h" 22 #include "net/base/request_priority.h" 23 #include "net/cert/cert_verifier.h" 24 #include "net/dns/host_resolver.h" 25 #include "net/http/http_auth_handler_factory.h" 26 #include "net/http/http_cache.h" 27 #include "net/http/http_network_session.h" 28 #include "net/http/http_server_properties_impl.h" 29 #include "net/http/transport_security_state.h" 30 #include "net/proxy/dhcp_proxy_script_fetcher_factory.h" 31 #include "net/proxy/proxy_config_service_fixed.h" 32 #include "net/proxy/proxy_script_fetcher_impl.h" 33 #include "net/proxy/proxy_service.h" 34 #include "net/proxy/proxy_service_v8.h" 35 #include "net/ssl/ssl_config_service_defaults.h" 36 #include "net/url_request/url_request.h" 37 #include "net/url_request/url_request_context.h" 38 #include "net/url_request/url_request_context_storage.h" 39 #include "net/url_request/url_request_job_factory_impl.h" 40 41 #if !defined(OS_ANDROID) && !defined(OS_IOS) 42 #include "chrome/browser/net/firefox_proxy_settings.h" 43 #endif 44 45 namespace { 46 47 // ExperimentURLRequestContext ------------------------------------------------ 48 49 // An instance of ExperimentURLRequestContext is created for each experiment 50 // run by ConnectionTester. The class initializes network dependencies according 51 // to the specified "experiment". 52 class ExperimentURLRequestContext : public net::URLRequestContext { 53 public: 54 explicit ExperimentURLRequestContext( 55 net::URLRequestContext* proxy_request_context) : 56 #if !defined(OS_IOS) 57 proxy_request_context_(proxy_request_context), 58 #endif 59 storage_(this), 60 weak_factory_(this) {} 61 62 virtual ~ExperimentURLRequestContext() { 63 AssertNoURLRequests(); 64 } 65 66 // Creates a proxy config service for |experiment|. On success returns net::OK 67 // and fills |config_service| with a new pointer. Otherwise returns a network 68 // error code. 69 int CreateProxyConfigService( 70 ConnectionTester::ProxySettingsExperiment experiment, 71 scoped_ptr<net::ProxyConfigService>* config_service, 72 base::Callback<void(int)> callback) { 73 switch (experiment) { 74 case ConnectionTester::PROXY_EXPERIMENT_USE_SYSTEM_SETTINGS: 75 return CreateSystemProxyConfigService(config_service); 76 case ConnectionTester::PROXY_EXPERIMENT_USE_FIREFOX_SETTINGS: 77 return CreateFirefoxProxyConfigService(config_service, callback); 78 case ConnectionTester::PROXY_EXPERIMENT_USE_AUTO_DETECT: 79 config_service->reset(new net::ProxyConfigServiceFixed( 80 net::ProxyConfig::CreateAutoDetect())); 81 return net::OK; 82 case ConnectionTester::PROXY_EXPERIMENT_USE_DIRECT: 83 config_service->reset(new net::ProxyConfigServiceFixed( 84 net::ProxyConfig::CreateDirect())); 85 return net::OK; 86 default: 87 NOTREACHED(); 88 return net::ERR_UNEXPECTED; 89 } 90 } 91 92 int Init(const ConnectionTester::Experiment& experiment, 93 scoped_ptr<net::ProxyConfigService>* proxy_config_service, 94 net::NetLog* net_log) { 95 int rv; 96 97 // Create a custom HostResolver for this experiment. 98 scoped_ptr<net::HostResolver> host_resolver_tmp; 99 rv = CreateHostResolver(experiment.host_resolver_experiment, 100 &host_resolver_tmp); 101 if (rv != net::OK) 102 return rv; // Failure. 103 storage_.set_host_resolver(host_resolver_tmp.Pass()); 104 105 // Create a custom ProxyService for this this experiment. 106 scoped_ptr<net::ProxyService> experiment_proxy_service; 107 rv = CreateProxyService(experiment.proxy_settings_experiment, 108 proxy_config_service, &experiment_proxy_service); 109 if (rv != net::OK) 110 return rv; // Failure. 111 storage_.set_proxy_service(experiment_proxy_service.release()); 112 113 // The rest of the dependencies are standard, and don't depend on the 114 // experiment being run. 115 storage_.set_cert_verifier(net::CertVerifier::CreateDefault()); 116 storage_.set_transport_security_state(new net::TransportSecurityState); 117 storage_.set_ssl_config_service(new net::SSLConfigServiceDefaults); 118 storage_.set_http_auth_handler_factory( 119 net::HttpAuthHandlerFactory::CreateDefault(host_resolver())); 120 storage_.set_http_server_properties( 121 scoped_ptr<net::HttpServerProperties>( 122 new net::HttpServerPropertiesImpl())); 123 124 net::HttpNetworkSession::Params session_params; 125 session_params.host_resolver = host_resolver(); 126 session_params.cert_verifier = cert_verifier(); 127 session_params.transport_security_state = transport_security_state(); 128 session_params.proxy_service = proxy_service(); 129 session_params.ssl_config_service = ssl_config_service(); 130 session_params.http_auth_handler_factory = http_auth_handler_factory(); 131 session_params.http_server_properties = http_server_properties(); 132 session_params.net_log = net_log; 133 scoped_refptr<net::HttpNetworkSession> network_session( 134 new net::HttpNetworkSession(session_params)); 135 storage_.set_http_transaction_factory(new net::HttpCache( 136 network_session.get(), net::HttpCache::DefaultBackend::InMemory(0))); 137 // In-memory cookie store. 138 storage_.set_cookie_store( 139 content::CreateCookieStore(content::CookieStoreConfig())); 140 // Creating a new job factory avoids added ProtocolHandlers and 141 // layered URLRequestInterceptingJobFactories. 142 storage_.set_job_factory(new net::URLRequestJobFactoryImpl()); 143 144 return net::OK; 145 } 146 147 private: 148 // Creates a host resolver for |experiment|. On success returns net::OK and 149 // fills |host_resolver| with a new pointer. Otherwise returns a network 150 // error code. 151 int CreateHostResolver( 152 ConnectionTester::HostResolverExperiment experiment, 153 scoped_ptr<net::HostResolver>* host_resolver) { 154 // Create a vanilla HostResolver that disables caching. 155 const size_t kMaxJobs = 50u; 156 const size_t kMaxRetryAttempts = 4u; 157 net::HostResolver::Options options; 158 options.max_concurrent_resolves = kMaxJobs; 159 options.max_retry_attempts = kMaxRetryAttempts; 160 options.enable_caching = false; 161 scoped_ptr<net::HostResolver> resolver( 162 net::HostResolver::CreateSystemResolver(options, NULL /* NetLog */)); 163 164 // Modify it slightly based on the experiment being run. 165 switch (experiment) { 166 case ConnectionTester::HOST_RESOLVER_EXPERIMENT_PLAIN: 167 break; 168 case ConnectionTester::HOST_RESOLVER_EXPERIMENT_DISABLE_IPV6: 169 resolver->SetDefaultAddressFamily(net::ADDRESS_FAMILY_IPV4); 170 break; 171 case ConnectionTester::HOST_RESOLVER_EXPERIMENT_IPV6_PROBE: { 172 // The system HostResolver will probe by default. 173 break; 174 } 175 default: 176 NOTREACHED(); 177 return net::ERR_UNEXPECTED; 178 } 179 host_resolver->swap(resolver); 180 return net::OK; 181 } 182 183 // Creates a proxy service for |experiment|. On success returns net::OK 184 // and fills |experiment_proxy_service| with a new pointer. Otherwise returns 185 // a network error code. 186 int CreateProxyService( 187 ConnectionTester::ProxySettingsExperiment experiment, 188 scoped_ptr<net::ProxyConfigService>* proxy_config_service, 189 scoped_ptr<net::ProxyService>* experiment_proxy_service) { 190 if (CommandLine::ForCurrentProcess()->HasSwitch( 191 switches::kSingleProcess)) { 192 // We can't create a standard proxy resolver in single-process mode. 193 // Rather than falling-back to some other implementation, fail. 194 return net::ERR_NOT_IMPLEMENTED; 195 } 196 197 net::DhcpProxyScriptFetcherFactory dhcp_factory; 198 199 #if defined(OS_IOS) 200 experiment_proxy_service->reset( 201 net::ProxyService::CreateUsingSystemProxyResolver( 202 proxy_config_service->release(), 0u, NULL)); 203 #else 204 experiment_proxy_service->reset( 205 net::CreateProxyServiceUsingV8ProxyResolver( 206 proxy_config_service->release(), 207 new net::ProxyScriptFetcherImpl(proxy_request_context_), 208 dhcp_factory.Create(proxy_request_context_), 209 host_resolver(), 210 NULL, 211 NULL)); 212 #endif 213 214 return net::OK; 215 } 216 217 // Creates a proxy config service that pulls from the system proxy settings. 218 // On success returns net::OK and fills |config_service| with a new pointer. 219 // Otherwise returns a network error code. 220 int CreateSystemProxyConfigService( 221 scoped_ptr<net::ProxyConfigService>* config_service) { 222 #if defined(OS_LINUX) || defined(OS_OPENBSD) 223 // TODO(eroman): This is not supported on Linux yet, because of how 224 // construction needs ot happen on the UI thread. 225 return net::ERR_NOT_IMPLEMENTED; 226 #else 227 config_service->reset(net::ProxyService::CreateSystemProxyConfigService( 228 base::ThreadTaskRunnerHandle::Get().get(), NULL)); 229 return net::OK; 230 #endif 231 } 232 233 #if !defined(OS_ANDROID) && !defined(OS_IOS) 234 static int FirefoxProxySettingsTask( 235 FirefoxProxySettings* firefox_settings) { 236 if (!FirefoxProxySettings::GetSettings(firefox_settings)) 237 return net::ERR_FILE_NOT_FOUND; 238 return net::OK; 239 } 240 241 void FirefoxProxySettingsReply( 242 scoped_ptr<net::ProxyConfigService>* config_service, 243 FirefoxProxySettings* firefox_settings, 244 base::Callback<void(int)> callback, 245 int rv) { 246 if (rv == net::OK) { 247 if (FirefoxProxySettings::SYSTEM == firefox_settings->config_type()) { 248 rv = CreateSystemProxyConfigService(config_service); 249 } else { 250 net::ProxyConfig config; 251 if (firefox_settings->ToProxyConfig(&config)) 252 config_service->reset(new net::ProxyConfigServiceFixed(config)); 253 else 254 rv = net::ERR_FAILED; 255 } 256 } 257 callback.Run(rv); 258 } 259 #endif 260 261 // Creates a fixed proxy config service that is initialized using Firefox's 262 // current proxy settings. On success returns net::OK and fills 263 // |config_service| with a new pointer. Otherwise returns a network error 264 // code. 265 int CreateFirefoxProxyConfigService( 266 scoped_ptr<net::ProxyConfigService>* config_service, 267 base::Callback<void(int)> callback) { 268 #if defined(OS_ANDROID) || defined(OS_IOS) 269 // Chrome on Android and iOS do not support Firefox settings. 270 return net::ERR_NOT_IMPLEMENTED; 271 #else 272 // Fetch Firefox's proxy settings (can fail if Firefox is not installed). 273 FirefoxProxySettings* ff_settings = new FirefoxProxySettings(); 274 base::Callback<int(void)> task = base::Bind( 275 &FirefoxProxySettingsTask, ff_settings); 276 base::Callback<void(int)> reply = base::Bind( 277 &ExperimentURLRequestContext::FirefoxProxySettingsReply, 278 weak_factory_.GetWeakPtr(), config_service, 279 base::Owned(ff_settings), callback); 280 if (!content::BrowserThread::PostTaskAndReplyWithResult<int>( 281 content::BrowserThread::FILE, FROM_HERE, task, reply)) 282 return net::ERR_FAILED; 283 return net::ERR_IO_PENDING; 284 #endif 285 } 286 287 #if !defined(OS_IOS) 288 net::URLRequestContext* const proxy_request_context_; 289 #endif 290 net::URLRequestContextStorage storage_; 291 base::WeakPtrFactory<ExperimentURLRequestContext> weak_factory_; 292 }; 293 294 } // namespace 295 296 // ConnectionTester::TestRunner ---------------------------------------------- 297 298 // TestRunner is a helper class for running an individual experiment. It can 299 // be deleted any time after it is started, and this will abort the request. 300 class ConnectionTester::TestRunner : public net::URLRequest::Delegate { 301 public: 302 // |tester| must remain alive throughout the TestRunner's lifetime. 303 // |tester| will be notified of completion. 304 TestRunner(ConnectionTester* tester, net::NetLog* net_log) 305 : tester_(tester), 306 net_log_(net_log), 307 weak_factory_(this) {} 308 309 // Finish running |experiment| once a ProxyConfigService has been created. 310 // In the case of a FirefoxProxyConfigService, this will be called back 311 // after disk access has completed. 312 void ProxyConfigServiceCreated( 313 const Experiment& experiment, 314 scoped_ptr<net::ProxyConfigService>* proxy_config_service, int status); 315 316 // Starts running |experiment|. Notifies tester->OnExperimentCompleted() when 317 // it is done. 318 void Run(const Experiment& experiment); 319 320 // Overridden from net::URLRequest::Delegate: 321 virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE; 322 virtual void OnReadCompleted(net::URLRequest* request, 323 int bytes_read) OVERRIDE; 324 // TODO(eroman): handle cases requiring authentication. 325 326 private: 327 // The number of bytes to read each response body chunk. 328 static const int kReadBufferSize = 1024; 329 330 // Starts reading the response's body (and keeps reading until an error or 331 // end of stream). 332 void ReadBody(net::URLRequest* request); 333 334 // Called when the request has completed (for both success and failure). 335 void OnResponseCompleted(net::URLRequest* request); 336 void OnExperimentCompletedWithResult(int result); 337 338 ConnectionTester* tester_; 339 scoped_ptr<ExperimentURLRequestContext> request_context_; 340 scoped_ptr<net::URLRequest> request_; 341 net::NetLog* net_log_; 342 343 base::WeakPtrFactory<TestRunner> weak_factory_; 344 345 DISALLOW_COPY_AND_ASSIGN(TestRunner); 346 }; 347 348 void ConnectionTester::TestRunner::OnResponseStarted(net::URLRequest* request) { 349 if (!request->status().is_success()) { 350 OnResponseCompleted(request); 351 return; 352 } 353 354 // Start reading the body. 355 ReadBody(request); 356 } 357 358 void ConnectionTester::TestRunner::OnReadCompleted(net::URLRequest* request, 359 int bytes_read) { 360 if (bytes_read <= 0) { 361 OnResponseCompleted(request); 362 return; 363 } 364 365 // Keep reading until the stream is closed. Throw the data read away. 366 ReadBody(request); 367 } 368 369 void ConnectionTester::TestRunner::ReadBody(net::URLRequest* request) { 370 // Read the response body |kReadBufferSize| bytes at a time. 371 scoped_refptr<net::IOBuffer> unused_buffer( 372 new net::IOBuffer(kReadBufferSize)); 373 int num_bytes; 374 if (request->Read(unused_buffer.get(), kReadBufferSize, &num_bytes)) { 375 OnReadCompleted(request, num_bytes); 376 } else if (!request->status().is_io_pending()) { 377 // Read failed synchronously. 378 OnResponseCompleted(request); 379 } 380 } 381 382 void ConnectionTester::TestRunner::OnResponseCompleted( 383 net::URLRequest* request) { 384 int result = net::OK; 385 if (!request->status().is_success()) { 386 DCHECK_NE(net::ERR_IO_PENDING, request->status().error()); 387 result = request->status().error(); 388 } 389 390 // Post a task to notify the parent rather than handling it right away, 391 // to avoid re-entrancy problems with URLRequest. (Don't want the caller 392 // to end up deleting the URLRequest while in the middle of processing). 393 base::MessageLoop::current()->PostTask( 394 FROM_HERE, 395 base::Bind(&TestRunner::OnExperimentCompletedWithResult, 396 weak_factory_.GetWeakPtr(), result)); 397 } 398 399 void ConnectionTester::TestRunner::OnExperimentCompletedWithResult(int result) { 400 tester_->OnExperimentCompleted(result); 401 } 402 403 void ConnectionTester::TestRunner::ProxyConfigServiceCreated( 404 const Experiment& experiment, 405 scoped_ptr<net::ProxyConfigService>* proxy_config_service, 406 int status) { 407 if (status == net::OK) 408 status = request_context_->Init(experiment, 409 proxy_config_service, 410 net_log_); 411 if (status != net::OK) { 412 tester_->OnExperimentCompleted(status); 413 return; 414 } 415 // Fetch a request using the experimental context. 416 request_ = request_context_->CreateRequest( 417 experiment.url, net::DEFAULT_PRIORITY, this, NULL); 418 request_->Start(); 419 } 420 421 void ConnectionTester::TestRunner::Run(const Experiment& experiment) { 422 // Try to create a net::URLRequestContext for this experiment. 423 request_context_.reset( 424 new ExperimentURLRequestContext(tester_->proxy_request_context_)); 425 scoped_ptr<net::ProxyConfigService>* proxy_config_service = 426 new scoped_ptr<net::ProxyConfigService>(); 427 base::Callback<void(int)> config_service_callback = 428 base::Bind( 429 &TestRunner::ProxyConfigServiceCreated, weak_factory_.GetWeakPtr(), 430 experiment, base::Owned(proxy_config_service)); 431 int rv = request_context_->CreateProxyConfigService( 432 experiment.proxy_settings_experiment, 433 proxy_config_service, config_service_callback); 434 if (rv != net::ERR_IO_PENDING) 435 ProxyConfigServiceCreated(experiment, proxy_config_service, rv); 436 } 437 438 // ConnectionTester ---------------------------------------------------------- 439 440 ConnectionTester::ConnectionTester( 441 Delegate* delegate, 442 net::URLRequestContext* proxy_request_context, 443 net::NetLog* net_log) 444 : delegate_(delegate), 445 proxy_request_context_(proxy_request_context), 446 net_log_(net_log) { 447 DCHECK(delegate); 448 DCHECK(proxy_request_context); 449 } 450 451 ConnectionTester::~ConnectionTester() { 452 // Cancellation happens automatically by deleting test_runner_. 453 } 454 455 void ConnectionTester::RunAllTests(const GURL& url) { 456 // Select all possible experiments to run. (In no particular order). 457 // It is possible that some of these experiments are actually duplicates. 458 GetAllPossibleExperimentCombinations(url, &remaining_experiments_); 459 460 delegate_->OnStartConnectionTestSuite(); 461 StartNextExperiment(); 462 } 463 464 // static 465 base::string16 ConnectionTester::ProxySettingsExperimentDescription( 466 ProxySettingsExperiment experiment) { 467 // TODO(eroman): Use proper string resources. 468 switch (experiment) { 469 case PROXY_EXPERIMENT_USE_DIRECT: 470 return base::ASCIIToUTF16("Don't use any proxy"); 471 case PROXY_EXPERIMENT_USE_SYSTEM_SETTINGS: 472 return base::ASCIIToUTF16("Use system proxy settings"); 473 case PROXY_EXPERIMENT_USE_FIREFOX_SETTINGS: 474 return base::ASCIIToUTF16("Use Firefox's proxy settings"); 475 case PROXY_EXPERIMENT_USE_AUTO_DETECT: 476 return base::ASCIIToUTF16("Auto-detect proxy settings"); 477 default: 478 NOTREACHED(); 479 return base::string16(); 480 } 481 } 482 483 // static 484 base::string16 ConnectionTester::HostResolverExperimentDescription( 485 HostResolverExperiment experiment) { 486 // TODO(eroman): Use proper string resources. 487 switch (experiment) { 488 case HOST_RESOLVER_EXPERIMENT_PLAIN: 489 return base::string16(); 490 case HOST_RESOLVER_EXPERIMENT_DISABLE_IPV6: 491 return base::ASCIIToUTF16("Disable IPv6 host resolving"); 492 case HOST_RESOLVER_EXPERIMENT_IPV6_PROBE: 493 return base::ASCIIToUTF16("Probe for IPv6 host resolving"); 494 default: 495 NOTREACHED(); 496 return base::string16(); 497 } 498 } 499 500 // static 501 void ConnectionTester::GetAllPossibleExperimentCombinations( 502 const GURL& url, 503 ConnectionTester::ExperimentList* list) { 504 list->clear(); 505 for (size_t resolver_experiment = 0; 506 resolver_experiment < HOST_RESOLVER_EXPERIMENT_COUNT; 507 ++resolver_experiment) { 508 for (size_t proxy_experiment = 0; 509 proxy_experiment < PROXY_EXPERIMENT_COUNT; 510 ++proxy_experiment) { 511 Experiment experiment( 512 url, 513 static_cast<ProxySettingsExperiment>(proxy_experiment), 514 static_cast<HostResolverExperiment>(resolver_experiment)); 515 list->push_back(experiment); 516 } 517 } 518 } 519 520 void ConnectionTester::StartNextExperiment() { 521 DCHECK(!remaining_experiments_.empty()); 522 DCHECK(!current_test_runner_.get()); 523 524 delegate_->OnStartConnectionTestExperiment(current_experiment()); 525 526 current_test_runner_.reset(new TestRunner(this, net_log_)); 527 current_test_runner_->Run(current_experiment()); 528 } 529 530 void ConnectionTester::OnExperimentCompleted(int result) { 531 Experiment current = current_experiment(); 532 533 // Advance to the next experiment. 534 remaining_experiments_.erase(remaining_experiments_.begin()); 535 current_test_runner_.reset(); 536 537 // Notify the delegate of completion. 538 delegate_->OnCompletedConnectionTestExperiment(current, result); 539 540 if (remaining_experiments_.empty()) { 541 delegate_->OnCompletedConnectionTestSuite(); 542 } else { 543 StartNextExperiment(); 544 } 545 } 546