1 // Copyright (c) 2011 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/command_line.h" 8 #include "base/compiler_specific.h" 9 #include "base/logging.h" 10 #include "base/message_loop.h" 11 #include "base/task.h" 12 #include "base/threading/thread_restrictions.h" 13 #include "base/utf_string_conversions.h" 14 #include "chrome/browser/importer/firefox_proxy_settings.h" 15 #include "chrome/common/chrome_switches.h" 16 #include "net/base/cert_verifier.h" 17 #include "net/base/cookie_monster.h" 18 #include "net/base/dnsrr_resolver.h" 19 #include "net/base/host_resolver.h" 20 #include "net/base/host_resolver_impl.h" 21 #include "net/base/io_buffer.h" 22 #include "net/base/net_errors.h" 23 #include "net/base/net_util.h" 24 #include "net/base/ssl_config_service_defaults.h" 25 #include "net/ftp/ftp_network_layer.h" 26 #include "net/http/http_auth_handler_factory.h" 27 #include "net/http/http_cache.h" 28 #include "net/http/http_network_session.h" 29 #include "net/proxy/proxy_config_service_fixed.h" 30 #include "net/proxy/proxy_script_fetcher_impl.h" 31 #include "net/url_request/url_request.h" 32 #include "net/url_request/url_request_context.h" 33 34 namespace { 35 36 // ExperimentURLRequestContext ------------------------------------------------ 37 38 // An instance of ExperimentURLRequestContext is created for each experiment 39 // run by ConnectionTester. The class initializes network dependencies according 40 // to the specified "experiment". 41 class ExperimentURLRequestContext : public net::URLRequestContext { 42 public: 43 explicit ExperimentURLRequestContext( 44 net::URLRequestContext* proxy_request_context) 45 : proxy_request_context_(proxy_request_context) {} 46 47 int Init(const ConnectionTester::Experiment& experiment) { 48 int rv; 49 50 // Create a custom HostResolver for this experiment. 51 net::HostResolver* host_resolver_tmp = NULL; 52 rv = CreateHostResolver(experiment.host_resolver_experiment, 53 &host_resolver_tmp); 54 if (rv != net::OK) 55 return rv; // Failure. 56 set_host_resolver(host_resolver_tmp); 57 58 // Create a custom ProxyService for this this experiment. 59 scoped_refptr<net::ProxyService> proxy_service_tmp = NULL; 60 rv = CreateProxyService(experiment.proxy_settings_experiment, 61 &proxy_service_tmp); 62 if (rv != net::OK) 63 return rv; // Failure. 64 set_proxy_service(proxy_service_tmp); 65 66 // The rest of the dependencies are standard, and don't depend on the 67 // experiment being run. 68 set_cert_verifier(new net::CertVerifier); 69 set_dnsrr_resolver(new net::DnsRRResolver); 70 set_ftp_transaction_factory(new net::FtpNetworkLayer(host_resolver_tmp)); 71 set_ssl_config_service(new net::SSLConfigServiceDefaults); 72 set_http_auth_handler_factory(net::HttpAuthHandlerFactory::CreateDefault( 73 host_resolver_tmp)); 74 75 net::HttpNetworkSession::Params session_params; 76 session_params.host_resolver = host_resolver_tmp; 77 session_params.dnsrr_resolver = dnsrr_resolver(); 78 session_params.cert_verifier = cert_verifier(); 79 session_params.proxy_service = proxy_service_tmp; 80 session_params.http_auth_handler_factory = http_auth_handler_factory(); 81 session_params.ssl_config_service = ssl_config_service(); 82 scoped_refptr<net::HttpNetworkSession> network_session( 83 new net::HttpNetworkSession(session_params)); 84 set_http_transaction_factory(new net::HttpCache( 85 network_session, 86 net::HttpCache::DefaultBackend::InMemory(0))); 87 // In-memory cookie store. 88 set_cookie_store(new net::CookieMonster(NULL, NULL)); 89 90 return net::OK; 91 } 92 93 protected: 94 virtual ~ExperimentURLRequestContext() { 95 delete ftp_transaction_factory(); 96 delete http_transaction_factory(); 97 delete http_auth_handler_factory(); 98 delete dnsrr_resolver(); 99 delete cert_verifier(); 100 delete host_resolver(); 101 } 102 103 private: 104 // Creates a host resolver for |experiment|. On success returns net::OK and 105 // fills |host_resolver| with a new pointer. Otherwise returns a network 106 // error code. 107 int CreateHostResolver( 108 ConnectionTester::HostResolverExperiment experiment, 109 net::HostResolver** host_resolver) { 110 // Create a vanilla HostResolver that disables caching. 111 const size_t kMaxJobs = 50u; 112 net::HostResolverImpl* impl = 113 new net::HostResolverImpl(NULL, NULL, kMaxJobs, NULL); 114 115 *host_resolver = impl; 116 117 // Modify it slightly based on the experiment being run. 118 switch (experiment) { 119 case ConnectionTester::HOST_RESOLVER_EXPERIMENT_PLAIN: 120 return net::OK; 121 case ConnectionTester::HOST_RESOLVER_EXPERIMENT_DISABLE_IPV6: 122 impl->SetDefaultAddressFamily(net::ADDRESS_FAMILY_IPV4); 123 return net::OK; 124 case ConnectionTester::HOST_RESOLVER_EXPERIMENT_IPV6_PROBE: { 125 // Note that we don't use impl->ProbeIPv6Support() since that finishes 126 // asynchronously and may not take effect in time for the test. 127 // So instead we will probe synchronously (might take 100-200 ms). 128 net::AddressFamily family = net::IPv6Supported() ? 129 net::ADDRESS_FAMILY_UNSPECIFIED : net::ADDRESS_FAMILY_IPV4; 130 impl->SetDefaultAddressFamily(family); 131 return net::OK; 132 } 133 default: 134 NOTREACHED(); 135 return net::ERR_UNEXPECTED; 136 } 137 } 138 139 // Creates a proxy config service for |experiment|. On success returns net::OK 140 // and fills |config_service| with a new pointer. Otherwise returns a network 141 // error code. 142 int CreateProxyConfigService( 143 ConnectionTester::ProxySettingsExperiment experiment, 144 scoped_ptr<net::ProxyConfigService>* config_service) { 145 scoped_ptr<base::ThreadRestrictions::ScopedAllowIO> allow_io; 146 switch (experiment) { 147 case ConnectionTester::PROXY_EXPERIMENT_USE_SYSTEM_SETTINGS: 148 return CreateSystemProxyConfigService(config_service); 149 case ConnectionTester::PROXY_EXPERIMENT_USE_FIREFOX_SETTINGS: 150 // http://crbug.com/67664: This call can lead to blocking IO on the IO 151 // thread. This is a bug and should be fixed. 152 allow_io.reset(new base::ThreadRestrictions::ScopedAllowIO); 153 return CreateFirefoxProxyConfigService(config_service); 154 case ConnectionTester::PROXY_EXPERIMENT_USE_AUTO_DETECT: 155 config_service->reset(new net::ProxyConfigServiceFixed( 156 net::ProxyConfig::CreateAutoDetect())); 157 return net::OK; 158 case ConnectionTester::PROXY_EXPERIMENT_USE_DIRECT: 159 config_service->reset(new net::ProxyConfigServiceFixed( 160 net::ProxyConfig::CreateDirect())); 161 return net::OK; 162 default: 163 NOTREACHED(); 164 return net::ERR_UNEXPECTED; 165 } 166 } 167 168 // Creates a proxy service for |experiment|. On success returns net::OK 169 // and fills |config_service| with a new pointer. Otherwise returns a network 170 // error code. 171 int CreateProxyService( 172 ConnectionTester::ProxySettingsExperiment experiment, 173 scoped_refptr<net::ProxyService>* proxy_service) { 174 // Create an appropriate proxy config service. 175 scoped_ptr<net::ProxyConfigService> config_service; 176 int rv = CreateProxyConfigService(experiment, &config_service); 177 if (rv != net::OK) 178 return rv; // Failure. 179 180 if (CommandLine::ForCurrentProcess()->HasSwitch( 181 switches::kSingleProcess)) { 182 // We can't create a standard proxy resolver in single-process mode. 183 // Rather than falling-back to some other implementation, fail. 184 return net::ERR_NOT_IMPLEMENTED; 185 } 186 187 *proxy_service = net::ProxyService::CreateUsingV8ProxyResolver( 188 config_service.release(), 189 0u, 190 new net::ProxyScriptFetcherImpl(proxy_request_context_), 191 host_resolver(), 192 NULL); 193 194 return net::OK; 195 } 196 197 // Creates a proxy config service that pulls from the system proxy settings. 198 // On success returns net::OK and fills |config_service| with a new pointer. 199 // Otherwise returns a network error code. 200 int CreateSystemProxyConfigService( 201 scoped_ptr<net::ProxyConfigService>* config_service) { 202 #if defined(OS_LINUX) 203 // TODO(eroman): This is not supported on Linux yet, because of how 204 // construction needs ot happen on the UI thread. 205 return net::ERR_NOT_IMPLEMENTED; 206 #else 207 config_service->reset( 208 net::ProxyService::CreateSystemProxyConfigService( 209 MessageLoop::current(), NULL)); 210 return net::OK; 211 #endif 212 } 213 214 // Creates a fixed proxy config service that is initialized using Firefox's 215 // current proxy settings. On success returns net::OK and fills 216 // |config_service| with a new pointer. Otherwise returns a network error 217 // code. 218 int CreateFirefoxProxyConfigService( 219 scoped_ptr<net::ProxyConfigService>* config_service) { 220 // Fetch Firefox's proxy settings (can fail if Firefox is not installed). 221 FirefoxProxySettings firefox_settings; 222 if (!FirefoxProxySettings::GetSettings(&firefox_settings)) 223 return net::ERR_FILE_NOT_FOUND; 224 225 if (FirefoxProxySettings::SYSTEM == firefox_settings.config_type()) 226 return CreateSystemProxyConfigService(config_service); 227 228 net::ProxyConfig config; 229 if (firefox_settings.ToProxyConfig(&config)) { 230 config_service->reset(new net::ProxyConfigServiceFixed(config)); 231 return net::OK; 232 } 233 234 return net::ERR_FAILED; 235 } 236 237 const scoped_refptr<net::URLRequestContext> proxy_request_context_; 238 }; 239 240 } // namespace 241 242 // ConnectionTester::TestRunner ---------------------------------------------- 243 244 // TestRunner is a helper class for running an individual experiment. It can 245 // be deleted any time after it is started, and this will abort the request. 246 class ConnectionTester::TestRunner : public net::URLRequest::Delegate { 247 public: 248 // |tester| must remain alive throughout the TestRunner's lifetime. 249 // |tester| will be notified of completion. 250 explicit TestRunner(ConnectionTester* tester) 251 : tester_(tester), 252 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {} 253 254 // Starts running |experiment|. Notifies tester->OnExperimentCompleted() when 255 // it is done. 256 void Run(const Experiment& experiment); 257 258 // Overridden from net::URLRequest::Delegate: 259 virtual void OnResponseStarted(net::URLRequest* request); 260 virtual void OnReadCompleted(net::URLRequest* request, int bytes_read); 261 // TODO(eroman): handle cases requiring authentication. 262 263 private: 264 // The number of bytes to read each response body chunk. 265 static const int kReadBufferSize = 1024; 266 267 // Starts reading the response's body (and keeps reading until an error or 268 // end of stream). 269 void ReadBody(net::URLRequest* request); 270 271 // Called when the request has completed (for both success and failure). 272 void OnResponseCompleted(net::URLRequest* request); 273 void OnExperimentCompletedWithResult(int result); 274 275 ConnectionTester* tester_; 276 scoped_ptr<net::URLRequest> request_; 277 278 ScopedRunnableMethodFactory<TestRunner> method_factory_; 279 280 DISALLOW_COPY_AND_ASSIGN(TestRunner); 281 }; 282 283 void ConnectionTester::TestRunner::OnResponseStarted(net::URLRequest* request) { 284 if (!request->status().is_success()) { 285 OnResponseCompleted(request); 286 return; 287 } 288 289 // Start reading the body. 290 ReadBody(request); 291 } 292 293 void ConnectionTester::TestRunner::OnReadCompleted(net::URLRequest* request, 294 int bytes_read) { 295 if (bytes_read <= 0) { 296 OnResponseCompleted(request); 297 return; 298 } 299 300 // Keep reading until the stream is closed. Throw the data read away. 301 ReadBody(request); 302 } 303 304 void ConnectionTester::TestRunner::ReadBody(net::URLRequest* request) { 305 // Read the response body |kReadBufferSize| bytes at a time. 306 scoped_refptr<net::IOBuffer> unused_buffer( 307 new net::IOBuffer(kReadBufferSize)); 308 int num_bytes; 309 if (request->Read(unused_buffer, kReadBufferSize, &num_bytes)) { 310 OnReadCompleted(request, num_bytes); 311 } else if (!request->status().is_io_pending()) { 312 // Read failed synchronously. 313 OnResponseCompleted(request); 314 } 315 } 316 317 void ConnectionTester::TestRunner::OnResponseCompleted( 318 net::URLRequest* request) { 319 int result = net::OK; 320 if (!request->status().is_success()) { 321 DCHECK_NE(net::ERR_IO_PENDING, request->status().os_error()); 322 result = request->status().os_error(); 323 } 324 325 // Post a task to notify the parent rather than handling it right away, 326 // to avoid re-entrancy problems with URLRequest. (Don't want the caller 327 // to end up deleting the URLRequest while in the middle of processing). 328 MessageLoop::current()->PostTask( 329 FROM_HERE, 330 method_factory_.NewRunnableMethod( 331 &TestRunner::OnExperimentCompletedWithResult, result)); 332 } 333 334 void ConnectionTester::TestRunner::OnExperimentCompletedWithResult(int result) { 335 tester_->OnExperimentCompleted(result); 336 } 337 338 void ConnectionTester::TestRunner::Run(const Experiment& experiment) { 339 // Try to create a net::URLRequestContext for this experiment. 340 scoped_refptr<ExperimentURLRequestContext> context( 341 new ExperimentURLRequestContext(tester_->proxy_request_context_)); 342 int rv = context->Init(experiment); 343 if (rv != net::OK) { 344 // Complete the experiment with a failure. 345 tester_->OnExperimentCompleted(rv); 346 return; 347 } 348 349 // Fetch a request using the experimental context. 350 request_.reset(new net::URLRequest(experiment.url, this)); 351 request_->set_context(context); 352 request_->Start(); 353 } 354 355 // ConnectionTester ---------------------------------------------------------- 356 357 ConnectionTester::ConnectionTester( 358 Delegate* delegate, 359 net::URLRequestContext* proxy_request_context) 360 : delegate_(delegate), proxy_request_context_(proxy_request_context) { 361 DCHECK(delegate); 362 DCHECK(proxy_request_context); 363 } 364 365 ConnectionTester::~ConnectionTester() { 366 // Cancellation happens automatically by deleting test_runner_. 367 } 368 369 void ConnectionTester::RunAllTests(const GURL& url) { 370 // Select all possible experiments to run. (In no particular order). 371 // It is possible that some of these experiments are actually duplicates. 372 GetAllPossibleExperimentCombinations(url, &remaining_experiments_); 373 374 delegate_->OnStartConnectionTestSuite(); 375 StartNextExperiment(); 376 } 377 378 // static 379 string16 ConnectionTester::ProxySettingsExperimentDescription( 380 ProxySettingsExperiment experiment) { 381 // TODO(eroman): Use proper string resources. 382 switch (experiment) { 383 case PROXY_EXPERIMENT_USE_DIRECT: 384 return ASCIIToUTF16("Don't use any proxy"); 385 case PROXY_EXPERIMENT_USE_SYSTEM_SETTINGS: 386 return ASCIIToUTF16("Use system proxy settings"); 387 case PROXY_EXPERIMENT_USE_FIREFOX_SETTINGS: 388 return ASCIIToUTF16("Use Firefox's proxy settings"); 389 case PROXY_EXPERIMENT_USE_AUTO_DETECT: 390 return ASCIIToUTF16("Auto-detect proxy settings"); 391 default: 392 NOTREACHED(); 393 return string16(); 394 } 395 } 396 397 // static 398 string16 ConnectionTester::HostResolverExperimentDescription( 399 HostResolverExperiment experiment) { 400 // TODO(eroman): Use proper string resources. 401 switch (experiment) { 402 case HOST_RESOLVER_EXPERIMENT_PLAIN: 403 return string16(); 404 case HOST_RESOLVER_EXPERIMENT_DISABLE_IPV6: 405 return ASCIIToUTF16("Disable IPv6 host resolving"); 406 case HOST_RESOLVER_EXPERIMENT_IPV6_PROBE: 407 return ASCIIToUTF16("Probe for IPv6 host resolving"); 408 default: 409 NOTREACHED(); 410 return string16(); 411 } 412 } 413 414 // static 415 void ConnectionTester::GetAllPossibleExperimentCombinations( 416 const GURL& url, 417 ConnectionTester::ExperimentList* list) { 418 list->clear(); 419 for (size_t resolver_experiment = 0; 420 resolver_experiment < HOST_RESOLVER_EXPERIMENT_COUNT; 421 ++resolver_experiment) { 422 for (size_t proxy_experiment = 0; 423 proxy_experiment < PROXY_EXPERIMENT_COUNT; 424 ++proxy_experiment) { 425 Experiment experiment( 426 url, 427 static_cast<ProxySettingsExperiment>(proxy_experiment), 428 static_cast<HostResolverExperiment>(resolver_experiment)); 429 list->push_back(experiment); 430 } 431 } 432 } 433 434 void ConnectionTester::StartNextExperiment() { 435 DCHECK(!remaining_experiments_.empty()); 436 DCHECK(!current_test_runner_.get()); 437 438 delegate_->OnStartConnectionTestExperiment(current_experiment()); 439 440 current_test_runner_.reset(new TestRunner(this)); 441 current_test_runner_->Run(current_experiment()); 442 } 443 444 void ConnectionTester::OnExperimentCompleted(int result) { 445 Experiment current = current_experiment(); 446 447 // Advance to the next experiment. 448 remaining_experiments_.erase(remaining_experiments_.begin()); 449 current_test_runner_.reset(); 450 451 // Notify the delegate of completion. 452 delegate_->OnCompletedConnectionTestExperiment(current, result); 453 454 if (remaining_experiments_.empty()) { 455 delegate_->OnCompletedConnectionTestSuite(); 456 } else { 457 StartNextExperiment(); 458 } 459 } 460