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 "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