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