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
45namespace {
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".
52class 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.
300class 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
348void 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
358void 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
369void 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
382void 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
399void ConnectionTester::TestRunner::OnExperimentCompletedWithResult(int result) {
400  tester_->OnExperimentCompleted(result);
401}
402
403void 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
421void 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
440ConnectionTester::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
451ConnectionTester::~ConnectionTester() {
452  // Cancellation happens automatically by deleting test_runner_.
453}
454
455void 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
465base::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
484base::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
501void 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
520void 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
530void 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