Home | History | Annotate | Download | only in net
      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