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