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.
5 #include "chrome/browser/net/connection_tester.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"
40 #if !defined(OS_ANDROID) && !defined(OS_IOS)
41 #include "chrome/browser/net/firefox_proxy_settings.h"
46 // ExperimentURLRequestContext ------------------------------------------------
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
{
53 explicit ExperimentURLRequestContext(
54 net::URLRequestContext
* proxy_request_context
) :
56 proxy_request_context_(proxy_request_context
),
59 weak_factory_(this) {}
61 virtual ~ExperimentURLRequestContext() {}
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
66 int CreateProxyConfigService(
67 ConnectionTester::ProxySettingsExperiment experiment
,
68 scoped_ptr
<net::ProxyConfigService
>* config_service
,
69 base::Callback
<void(int)> callback
) {
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()));
79 case ConnectionTester::PROXY_EXPERIMENT_USE_DIRECT
:
80 config_service
->reset(new net::ProxyConfigServiceFixed(
81 net::ProxyConfig::CreateDirect()));
85 return net::ERR_UNEXPECTED
;
89 int Init(const ConnectionTester::Experiment
& experiment
,
90 scoped_ptr
<net::ProxyConfigService
>* proxy_config_service
,
91 net::NetLog
* net_log
) {
94 // Create a custom HostResolver for this experiment.
95 scoped_ptr
<net::HostResolver
> host_resolver_tmp
;
96 rv
= CreateHostResolver(experiment
.host_resolver_experiment
,
99 return rv
; // Failure.
100 storage_
.set_host_resolver(host_resolver_tmp
.Pass());
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
);
107 return rv
; // Failure.
108 storage_
.set_proxy_service(experiment_proxy_service
.release());
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()));
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(
136 content::CreateCookieStore(content::CookieStoreConfig()));
142 // Creates a host resolver for |experiment|. On success returns net::OK and
143 // fills |host_resolver| with a new pointer. Otherwise returns a network
145 int CreateHostResolver(
146 ConnectionTester::HostResolverExperiment experiment
,
147 scoped_ptr
<net::HostResolver
>* host_resolver
) {
148 // Create a vanilla HostResolver that disables caching.
149 const size_t kMaxJobs
= 50u;
150 const size_t kMaxRetryAttempts
= 4u;
151 net::HostResolver::Options options
;
152 options
.max_concurrent_resolves
= kMaxJobs
;
153 options
.max_retry_attempts
= kMaxRetryAttempts
;
154 options
.enable_caching
= false;
155 scoped_ptr
<net::HostResolver
> resolver(
156 net::HostResolver::CreateSystemResolver(options
, NULL
/* NetLog */));
158 // Modify it slightly based on the experiment being run.
159 switch (experiment
) {
160 case ConnectionTester::HOST_RESOLVER_EXPERIMENT_PLAIN
:
162 case ConnectionTester::HOST_RESOLVER_EXPERIMENT_DISABLE_IPV6
:
163 resolver
->SetDefaultAddressFamily(net::ADDRESS_FAMILY_IPV4
);
165 case ConnectionTester::HOST_RESOLVER_EXPERIMENT_IPV6_PROBE
: {
166 // The system HostResolver will probe by default.
171 return net::ERR_UNEXPECTED
;
173 host_resolver
->swap(resolver
);
177 // Creates a proxy service for |experiment|. On success returns net::OK
178 // and fills |experiment_proxy_service| with a new pointer. Otherwise returns
179 // a network error code.
180 int CreateProxyService(
181 ConnectionTester::ProxySettingsExperiment experiment
,
182 scoped_ptr
<net::ProxyConfigService
>* proxy_config_service
,
183 scoped_ptr
<net::ProxyService
>* experiment_proxy_service
) {
184 if (CommandLine::ForCurrentProcess()->HasSwitch(
185 switches::kSingleProcess
)) {
186 // We can't create a standard proxy resolver in single-process mode.
187 // Rather than falling-back to some other implementation, fail.
188 return net::ERR_NOT_IMPLEMENTED
;
191 net::DhcpProxyScriptFetcherFactory dhcp_factory
;
194 experiment_proxy_service
->reset(
195 net::ProxyService::CreateUsingSystemProxyResolver(
196 proxy_config_service
->release(), 0u, NULL
));
198 experiment_proxy_service
->reset(
199 net::CreateProxyServiceUsingV8ProxyResolver(
200 proxy_config_service
->release(),
201 new net::ProxyScriptFetcherImpl(proxy_request_context_
),
202 dhcp_factory
.Create(proxy_request_context_
),
211 // Creates a proxy config service that pulls from the system proxy settings.
212 // On success returns net::OK and fills |config_service| with a new pointer.
213 // Otherwise returns a network error code.
214 int CreateSystemProxyConfigService(
215 scoped_ptr
<net::ProxyConfigService
>* config_service
) {
216 #if defined(OS_LINUX) || defined(OS_OPENBSD)
217 // TODO(eroman): This is not supported on Linux yet, because of how
218 // construction needs ot happen on the UI thread.
219 return net::ERR_NOT_IMPLEMENTED
;
221 config_service
->reset(net::ProxyService::CreateSystemProxyConfigService(
222 base::ThreadTaskRunnerHandle::Get().get(), NULL
));
227 #if !defined(OS_ANDROID) && !defined(OS_IOS)
228 static int FirefoxProxySettingsTask(
229 FirefoxProxySettings
* firefox_settings
) {
230 if (!FirefoxProxySettings::GetSettings(firefox_settings
))
231 return net::ERR_FILE_NOT_FOUND
;
235 void FirefoxProxySettingsReply(
236 scoped_ptr
<net::ProxyConfigService
>* config_service
,
237 FirefoxProxySettings
* firefox_settings
,
238 base::Callback
<void(int)> callback
,
241 if (FirefoxProxySettings::SYSTEM
== firefox_settings
->config_type()) {
242 rv
= CreateSystemProxyConfigService(config_service
);
244 net::ProxyConfig config
;
245 if (firefox_settings
->ToProxyConfig(&config
))
246 config_service
->reset(new net::ProxyConfigServiceFixed(config
));
248 rv
= net::ERR_FAILED
;
255 // Creates a fixed proxy config service that is initialized using Firefox's
256 // current proxy settings. On success returns net::OK and fills
257 // |config_service| with a new pointer. Otherwise returns a network error
259 int CreateFirefoxProxyConfigService(
260 scoped_ptr
<net::ProxyConfigService
>* config_service
,
261 base::Callback
<void(int)> callback
) {
262 #if defined(OS_ANDROID) || defined(OS_IOS)
263 // Chrome on Android and iOS do not support Firefox settings.
264 return net::ERR_NOT_IMPLEMENTED
;
266 // Fetch Firefox's proxy settings (can fail if Firefox is not installed).
267 FirefoxProxySettings
* ff_settings
= new FirefoxProxySettings();
268 base::Callback
<int(void)> task
= base::Bind(
269 &FirefoxProxySettingsTask
, ff_settings
);
270 base::Callback
<void(int)> reply
= base::Bind(
271 &ExperimentURLRequestContext::FirefoxProxySettingsReply
,
272 weak_factory_
.GetWeakPtr(), config_service
,
273 base::Owned(ff_settings
), callback
);
274 if (!content::BrowserThread::PostTaskAndReplyWithResult
<int>(
275 content::BrowserThread::FILE, FROM_HERE
, task
, reply
))
276 return net::ERR_FAILED
;
277 return net::ERR_IO_PENDING
;
282 net::URLRequestContext
* const proxy_request_context_
;
284 net::URLRequestContextStorage storage_
;
285 base::WeakPtrFactory
<ExperimentURLRequestContext
> weak_factory_
;
290 // ConnectionTester::TestRunner ----------------------------------------------
292 // TestRunner is a helper class for running an individual experiment. It can
293 // be deleted any time after it is started, and this will abort the request.
294 class ConnectionTester::TestRunner
: public net::URLRequest::Delegate
{
296 // |tester| must remain alive throughout the TestRunner's lifetime.
297 // |tester| will be notified of completion.
298 TestRunner(ConnectionTester
* tester
, net::NetLog
* net_log
)
301 weak_factory_(this) {}
303 // Finish running |experiment| once a ProxyConfigService has been created.
304 // In the case of a FirefoxProxyConfigService, this will be called back
305 // after disk access has completed.
306 void ProxyConfigServiceCreated(
307 const Experiment
& experiment
,
308 scoped_ptr
<net::ProxyConfigService
>* proxy_config_service
, int status
);
310 // Starts running |experiment|. Notifies tester->OnExperimentCompleted() when
312 void Run(const Experiment
& experiment
);
314 // Overridden from net::URLRequest::Delegate:
315 virtual void OnResponseStarted(net::URLRequest
* request
) OVERRIDE
;
316 virtual void OnReadCompleted(net::URLRequest
* request
,
317 int bytes_read
) OVERRIDE
;
318 // TODO(eroman): handle cases requiring authentication.
321 // The number of bytes to read each response body chunk.
322 static const int kReadBufferSize
= 1024;
324 // Starts reading the response's body (and keeps reading until an error or
326 void ReadBody(net::URLRequest
* request
);
328 // Called when the request has completed (for both success and failure).
329 void OnResponseCompleted(net::URLRequest
* request
);
330 void OnExperimentCompletedWithResult(int result
);
332 ConnectionTester
* tester_
;
333 scoped_ptr
<ExperimentURLRequestContext
> request_context_
;
334 scoped_ptr
<net::URLRequest
> request_
;
335 net::NetLog
* net_log_
;
337 base::WeakPtrFactory
<TestRunner
> weak_factory_
;
339 DISALLOW_COPY_AND_ASSIGN(TestRunner
);
342 void ConnectionTester::TestRunner::OnResponseStarted(net::URLRequest
* request
) {
343 if (!request
->status().is_success()) {
344 OnResponseCompleted(request
);
348 // Start reading the body.
352 void ConnectionTester::TestRunner::OnReadCompleted(net::URLRequest
* request
,
354 if (bytes_read
<= 0) {
355 OnResponseCompleted(request
);
359 // Keep reading until the stream is closed. Throw the data read away.
363 void ConnectionTester::TestRunner::ReadBody(net::URLRequest
* request
) {
364 // Read the response body |kReadBufferSize| bytes at a time.
365 scoped_refptr
<net::IOBuffer
> unused_buffer(
366 new net::IOBuffer(kReadBufferSize
));
368 if (request
->Read(unused_buffer
.get(), kReadBufferSize
, &num_bytes
)) {
369 OnReadCompleted(request
, num_bytes
);
370 } else if (!request
->status().is_io_pending()) {
371 // Read failed synchronously.
372 OnResponseCompleted(request
);
376 void ConnectionTester::TestRunner::OnResponseCompleted(
377 net::URLRequest
* request
) {
378 int result
= net::OK
;
379 if (!request
->status().is_success()) {
380 DCHECK_NE(net::ERR_IO_PENDING
, request
->status().error());
381 result
= request
->status().error();
384 // Post a task to notify the parent rather than handling it right away,
385 // to avoid re-entrancy problems with URLRequest. (Don't want the caller
386 // to end up deleting the URLRequest while in the middle of processing).
387 base::MessageLoop::current()->PostTask(
389 base::Bind(&TestRunner::OnExperimentCompletedWithResult
,
390 weak_factory_
.GetWeakPtr(), result
));
393 void ConnectionTester::TestRunner::OnExperimentCompletedWithResult(int result
) {
394 tester_
->OnExperimentCompleted(result
);
397 void ConnectionTester::TestRunner::ProxyConfigServiceCreated(
398 const Experiment
& experiment
,
399 scoped_ptr
<net::ProxyConfigService
>* proxy_config_service
,
401 if (status
== net::OK
)
402 status
= request_context_
->Init(experiment
,
403 proxy_config_service
,
405 if (status
!= net::OK
) {
406 tester_
->OnExperimentCompleted(status
);
409 // Fetch a request using the experimental context.
410 request_
= request_context_
->CreateRequest(
411 experiment
.url
, net::DEFAULT_PRIORITY
, this, NULL
);
415 void ConnectionTester::TestRunner::Run(const Experiment
& experiment
) {
416 // Try to create a net::URLRequestContext for this experiment.
417 request_context_
.reset(
418 new ExperimentURLRequestContext(tester_
->proxy_request_context_
));
419 scoped_ptr
<net::ProxyConfigService
>* proxy_config_service
=
420 new scoped_ptr
<net::ProxyConfigService
>();
421 base::Callback
<void(int)> config_service_callback
=
423 &TestRunner::ProxyConfigServiceCreated
, weak_factory_
.GetWeakPtr(),
424 experiment
, base::Owned(proxy_config_service
));
425 int rv
= request_context_
->CreateProxyConfigService(
426 experiment
.proxy_settings_experiment
,
427 proxy_config_service
, config_service_callback
);
428 if (rv
!= net::ERR_IO_PENDING
)
429 ProxyConfigServiceCreated(experiment
, proxy_config_service
, rv
);
432 // ConnectionTester ----------------------------------------------------------
434 ConnectionTester::ConnectionTester(
436 net::URLRequestContext
* proxy_request_context
,
437 net::NetLog
* net_log
)
438 : delegate_(delegate
),
439 proxy_request_context_(proxy_request_context
),
442 DCHECK(proxy_request_context
);
445 ConnectionTester::~ConnectionTester() {
446 // Cancellation happens automatically by deleting test_runner_.
449 void ConnectionTester::RunAllTests(const GURL
& url
) {
450 // Select all possible experiments to run. (In no particular order).
451 // It is possible that some of these experiments are actually duplicates.
452 GetAllPossibleExperimentCombinations(url
, &remaining_experiments_
);
454 delegate_
->OnStartConnectionTestSuite();
455 StartNextExperiment();
459 base::string16
ConnectionTester::ProxySettingsExperimentDescription(
460 ProxySettingsExperiment experiment
) {
461 // TODO(eroman): Use proper string resources.
462 switch (experiment
) {
463 case PROXY_EXPERIMENT_USE_DIRECT
:
464 return base::ASCIIToUTF16("Don't use any proxy");
465 case PROXY_EXPERIMENT_USE_SYSTEM_SETTINGS
:
466 return base::ASCIIToUTF16("Use system proxy settings");
467 case PROXY_EXPERIMENT_USE_FIREFOX_SETTINGS
:
468 return base::ASCIIToUTF16("Use Firefox's proxy settings");
469 case PROXY_EXPERIMENT_USE_AUTO_DETECT
:
470 return base::ASCIIToUTF16("Auto-detect proxy settings");
473 return base::string16();
478 base::string16
ConnectionTester::HostResolverExperimentDescription(
479 HostResolverExperiment experiment
) {
480 // TODO(eroman): Use proper string resources.
481 switch (experiment
) {
482 case HOST_RESOLVER_EXPERIMENT_PLAIN
:
483 return base::string16();
484 case HOST_RESOLVER_EXPERIMENT_DISABLE_IPV6
:
485 return base::ASCIIToUTF16("Disable IPv6 host resolving");
486 case HOST_RESOLVER_EXPERIMENT_IPV6_PROBE
:
487 return base::ASCIIToUTF16("Probe for IPv6 host resolving");
490 return base::string16();
495 void ConnectionTester::GetAllPossibleExperimentCombinations(
497 ConnectionTester::ExperimentList
* list
) {
499 for (size_t resolver_experiment
= 0;
500 resolver_experiment
< HOST_RESOLVER_EXPERIMENT_COUNT
;
501 ++resolver_experiment
) {
502 for (size_t proxy_experiment
= 0;
503 proxy_experiment
< PROXY_EXPERIMENT_COUNT
;
504 ++proxy_experiment
) {
505 Experiment
experiment(
507 static_cast<ProxySettingsExperiment
>(proxy_experiment
),
508 static_cast<HostResolverExperiment
>(resolver_experiment
));
509 list
->push_back(experiment
);
514 void ConnectionTester::StartNextExperiment() {
515 DCHECK(!remaining_experiments_
.empty());
516 DCHECK(!current_test_runner_
.get());
518 delegate_
->OnStartConnectionTestExperiment(current_experiment());
520 current_test_runner_
.reset(new TestRunner(this, net_log_
));
521 current_test_runner_
->Run(current_experiment());
524 void ConnectionTester::OnExperimentCompleted(int result
) {
525 Experiment current
= current_experiment();
527 // Advance to the next experiment.
528 remaining_experiments_
.erase(remaining_experiments_
.begin());
529 current_test_runner_
.reset();
531 // Notify the delegate of completion.
532 delegate_
->OnCompletedConnectionTestExperiment(current
, result
);
534 if (remaining_experiments_
.empty()) {
535 delegate_
->OnCompletedConnectionTestSuite();
537 StartNextExperiment();