• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
ExperimentURLRequestContext(net::URLRequestContext * proxy_request_context)43   explicit ExperimentURLRequestContext(
44       net::URLRequestContext* proxy_request_context)
45       : proxy_request_context_(proxy_request_context) {}
46 
Init(const ConnectionTester::Experiment & experiment)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:
~ExperimentURLRequestContext()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.
CreateHostResolver(ConnectionTester::HostResolverExperiment experiment,net::HostResolver ** host_resolver)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.
CreateProxyConfigService(ConnectionTester::ProxySettingsExperiment experiment,scoped_ptr<net::ProxyConfigService> * config_service)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.
CreateProxyService(ConnectionTester::ProxySettingsExperiment experiment,scoped_refptr<net::ProxyService> * proxy_service)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.
CreateSystemProxyConfigService(scoped_ptr<net::ProxyConfigService> * config_service)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.
CreateFirefoxProxyConfigService(scoped_ptr<net::ProxyConfigService> * config_service)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.
TestRunner(ConnectionTester * tester)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 
OnResponseStarted(net::URLRequest * request)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 
OnReadCompleted(net::URLRequest * request,int bytes_read)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 
ReadBody(net::URLRequest * request)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 
OnResponseCompleted(net::URLRequest * request)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 
OnExperimentCompletedWithResult(int result)334 void ConnectionTester::TestRunner::OnExperimentCompletedWithResult(int result) {
335   tester_->OnExperimentCompleted(result);
336 }
337 
Run(const Experiment & experiment)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 
ConnectionTester(Delegate * delegate,net::URLRequestContext * proxy_request_context)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 
~ConnectionTester()365 ConnectionTester::~ConnectionTester() {
366   // Cancellation happens automatically by deleting test_runner_.
367 }
368 
RunAllTests(const GURL & url)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
ProxySettingsExperimentDescription(ProxySettingsExperiment experiment)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
HostResolverExperimentDescription(HostResolverExperiment experiment)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
GetAllPossibleExperimentCombinations(const GURL & url,ConnectionTester::ExperimentList * list)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 
StartNextExperiment()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 
OnExperimentCompleted(int result)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