diff options
-rw-r--r-- | chrome/browser/dom_ui/net_internals_ui.cc | 97 | ||||
-rw-r--r-- | chrome/browser/importer/firefox_proxy_settings.cc | 70 | ||||
-rw-r--r-- | chrome/browser/importer/firefox_proxy_settings.h | 12 | ||||
-rw-r--r-- | chrome/browser/importer/firefox_proxy_settings_unittest.cc | 29 | ||||
-rw-r--r-- | chrome/browser/net/connection_tester.cc | 403 | ||||
-rw-r--r-- | chrome/browser/net/connection_tester.h | 175 | ||||
-rw-r--r-- | chrome/browser/net/connection_tester_unittest.cc | 138 | ||||
-rw-r--r-- | chrome/browser/resources/net_internals/index.html | 13 | ||||
-rw-r--r-- | chrome/browser/resources/net_internals/main.css | 10 | ||||
-rw-r--r-- | chrome/browser/resources/net_internals/main.js | 54 | ||||
-rw-r--r-- | chrome/browser/resources/net_internals/testview.js | 132 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 3 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 1 | ||||
-rw-r--r-- | net/proxy/proxy_config.h | 18 | ||||
-rw-r--r-- | net/proxy/proxy_config_service_fixed.h | 1 | ||||
-rw-r--r-- | net/url_request/url_request_context.h | 5 |
16 files changed, 1144 insertions, 17 deletions
diff --git a/chrome/browser/dom_ui/net_internals_ui.cc b/chrome/browser/dom_ui/net_internals_ui.cc index f7bf423..5f4139a 100644 --- a/chrome/browser/dom_ui/net_internals_ui.cc +++ b/chrome/browser/dom_ui/net_internals_ui.cc @@ -22,6 +22,7 @@ #include "chrome/browser/dom_ui/chrome_url_data_manager.h" #include "chrome/browser/io_thread.h" #include "chrome/browser/net/chrome_net_log.h" +#include "chrome/browser/net/connection_tester.h" #include "chrome/browser/net/passive_log_collector.h" #include "chrome/browser/net/url_request_context_getter.h" #include "chrome/browser/profile.h" @@ -83,6 +84,21 @@ Value* EntryToDictionaryValue(net::NetLog::EventType type, return entry_dict; } +Value* ExperimentToValue(const ConnectionTester::Experiment& experiment) { + DictionaryValue* dict = new DictionaryValue(); + dict->SetString(L"url", experiment.url.spec()); + + dict->SetStringFromUTF16( + L"proxy_settings_experiment", + ConnectionTester::ProxySettingsExperimentDescription( + experiment.proxy_settings_experiment)); + dict->SetStringFromUTF16( + L"host_resolver_experiment", + ConnectionTester::HostResolverExperimentDescription( + experiment.host_resolver_experiment)); + return dict; +} + class NetInternalsHTMLSource : public ChromeURLDataManager::DataSource { public: NetInternalsHTMLSource(); @@ -122,7 +138,7 @@ class NetInternalsMessageHandler // Executes the javascript function |function_name| in the renderer, passing // it the argument |value|. void CallJavascriptFunction(const std::wstring& function_name, - const Value& value); + const Value* value); private: class IOThreadImpl; @@ -140,7 +156,8 @@ class NetInternalsMessageHandler::IOThreadImpl : public base::RefCountedThreadSafe< NetInternalsMessageHandler::IOThreadImpl, ChromeThread::DeleteOnUIThread>, - public ChromeNetLog::Observer { + public ChromeNetLog::Observer, + public ConnectionTester::Delegate { public: // Type for methods that can be used as MessageHandler callbacks. typedef void (IOThreadImpl::*MessageHandler)(const Value*); @@ -182,6 +199,7 @@ class NetInternalsMessageHandler::IOThreadImpl void OnGetHostResolverCache(const Value* value); void OnClearHostResolverCache(const Value* value); void OnGetPassiveLogEntries(const Value* value); + void OnStartConnectionTests(const Value* value); // ChromeNetLog::Observer implementation: virtual void OnAddEntry(net::NetLog::EventType type, @@ -190,6 +208,15 @@ class NetInternalsMessageHandler::IOThreadImpl net::NetLog::EventPhase phase, net::NetLog::EventParameters* extra_parameters); + // ConnectionTester::Delegate implementation: + virtual void OnStartConnectionTestSuite(); + virtual void OnStartConnectionTestExperiment( + const ConnectionTester::Experiment& experiment); + virtual void OnCompletedConnectionTestExperiment( + const ConnectionTester::Experiment& experiment, + int result); + virtual void OnCompletedConnectionTestSuite(); + private: class CallbackHelper; @@ -210,6 +237,9 @@ class NetInternalsMessageHandler::IOThreadImpl scoped_refptr<URLRequestContextGetter> context_getter_; + // Helper that runs the suite of connection tests. + scoped_ptr<ConnectionTester> connection_tester_; + // True if we have attached an observer to the NetLog already. bool is_observing_log_; friend class base::RefCountedThreadSafe<IOThreadImpl>; @@ -347,13 +377,20 @@ void NetInternalsMessageHandler::RegisterMessages() { dom_ui_->RegisterMessageCallback( "getPassiveLogEntries", proxy_->CreateCallback(&IOThreadImpl::OnGetPassiveLogEntries)); + dom_ui_->RegisterMessageCallback( + "startConnectionTests", + proxy_->CreateCallback(&IOThreadImpl::OnStartConnectionTests)); } void NetInternalsMessageHandler::CallJavascriptFunction( const std::wstring& function_name, - const Value& value) { + const Value* value) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); - dom_ui_->CallJavascriptFunction(function_name, value); + if (value) { + dom_ui_->CallJavascriptFunction(function_name, *value); + } else { + dom_ui_->CallJavascriptFunction(function_name); + } } //////////////////////////////////////////////////////////////////////////////// @@ -389,6 +426,9 @@ void NetInternalsMessageHandler::IOThreadImpl::Detach() { // Unregister with network stack to observe events. if (is_observing_log_) io_thread_->globals()->net_log->RemoveObserver(this); + + // Cancel any in-progress connection tests. + connection_tester_.reset(); } void NetInternalsMessageHandler::IOThreadImpl::OnRendererReady( @@ -630,6 +670,21 @@ void NetInternalsMessageHandler::IOThreadImpl::OnGetPassiveLogEntries( CallJavascriptFunction(L"g_browser.receivedPassiveLogEntries", list); } +void NetInternalsMessageHandler::IOThreadImpl::OnStartConnectionTests( + const Value* value) { + // |value| should be: [<URL to test>]. + string16 url_str; + if (value && value->GetType() == Value::TYPE_LIST) { + const ListValue* list = static_cast<const ListValue*>(value); + list->GetStringAsUTF16(0, &url_str); + } + + GURL url(url_str); + + connection_tester_.reset(new ConnectionTester(this)); + connection_tester_->RunAllTests(url); +} + void NetInternalsMessageHandler::IOThreadImpl::OnAddEntry( net::NetLog::EventType type, const base::TimeTicks& time, @@ -643,6 +698,38 @@ void NetInternalsMessageHandler::IOThreadImpl::OnAddEntry( EntryToDictionaryValue(type, time, source, phase, extra_parameters)); } +void NetInternalsMessageHandler::IOThreadImpl::OnStartConnectionTestSuite() { + CallJavascriptFunction(L"g_browser.receivedStartConnectionTestSuite", NULL); +} + +void NetInternalsMessageHandler::IOThreadImpl::OnStartConnectionTestExperiment( + const ConnectionTester::Experiment& experiment) { + CallJavascriptFunction( + L"g_browser.receivedStartConnectionTestExperiment", + ExperimentToValue(experiment)); +} + +void +NetInternalsMessageHandler::IOThreadImpl::OnCompletedConnectionTestExperiment( + const ConnectionTester::Experiment& experiment, + int result) { + DictionaryValue* dict = new DictionaryValue(); + + dict->Set(L"experiment", ExperimentToValue(experiment)); + dict->SetInteger(L"result", result); + + CallJavascriptFunction( + L"g_browser.receivedCompletedConnectionTestExperiment", + dict); +} + +void +NetInternalsMessageHandler::IOThreadImpl::OnCompletedConnectionTestSuite() { + CallJavascriptFunction( + L"g_browser.receivedCompletedConnectionTestSuite", + NULL); +} + void NetInternalsMessageHandler::IOThreadImpl::DispatchToMessageHandler( Value* arg, MessageHandler method) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); @@ -657,7 +744,7 @@ void NetInternalsMessageHandler::IOThreadImpl::CallJavascriptFunction( if (handler_) { // We check |handler_| in case it was deleted on the UI thread earlier // while we were running on the IO thread. - handler_->CallJavascriptFunction(function_name, *arg); + handler_->CallJavascriptFunction(function_name, arg); } delete arg; return; diff --git a/chrome/browser/importer/firefox_proxy_settings.cc b/chrome/browser/importer/firefox_proxy_settings.cc index 493beff..d4d0d88 100644 --- a/chrome/browser/importer/firefox_proxy_settings.cc +++ b/chrome/browser/importer/firefox_proxy_settings.cc @@ -8,6 +8,7 @@ #include "base/string_util.h" #include "base/values.h" #include "chrome/browser/importer/firefox_importer_utils.h" +#include "net/proxy/proxy_config.h" namespace { @@ -82,7 +83,7 @@ void FirefoxProxySettings::Reset() { } // static - bool FirefoxProxySettings::GetSettings(FirefoxProxySettings* settings) { +bool FirefoxProxySettings::GetSettings(FirefoxProxySettings* settings) { DCHECK(settings); settings->Reset(); @@ -93,6 +94,73 @@ void FirefoxProxySettings::Reset() { return GetSettingsFromFile(pref_file, settings); } +bool FirefoxProxySettings::ToProxyConfig(net::ProxyConfig* config) { + switch (config_type()) { + case NO_PROXY: + *config = net::ProxyConfig::CreateDirect(); + return true; + case AUTO_DETECT: + *config = net::ProxyConfig::CreateAutoDetect(); + return true; + case AUTO_FROM_URL: + *config = net::ProxyConfig::CreateFromCustomPacURL( + GURL(autoconfig_url())); + return true; + case SYSTEM: + // Can't convert this directly to a ProxyConfig. + return false; + case MANUAL: + // Handled outside of the switch (since it is a lot of code.) + break; + default: + NOTREACHED(); + return false; + } + + // The rest of this funciton is for handling the MANUAL case. + DCHECK_EQ(MANUAL, config_type()); + + *config = net::ProxyConfig(); + config->proxy_rules().type = + net::ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME; + + if (!http_proxy().empty()) { + config->proxy_rules().proxy_for_http = net::ProxyServer( + net::ProxyServer::SCHEME_HTTP, + http_proxy(), + http_proxy_port()); + } + + if (!ftp_proxy().empty()) { + config->proxy_rules().proxy_for_ftp = net::ProxyServer( + net::ProxyServer::SCHEME_HTTP, + ftp_proxy(), + ftp_proxy_port()); + } + + if (!ssl_proxy().empty()) { + config->proxy_rules().proxy_for_https = net::ProxyServer( + net::ProxyServer::SCHEME_HTTP, + ssl_proxy(), + ssl_proxy_port()); + } + + if (!socks_host().empty()) { + net::ProxyServer::Scheme proxy_scheme = V5 == socks_version() ? + net::ProxyServer::SCHEME_SOCKS5 : net::ProxyServer::SCHEME_SOCKS4; + + config->proxy_rules().socks_proxy = net::ProxyServer( + proxy_scheme, + socks_host(), + socks_port()); + } + + config->proxy_rules().bypass_rules.ParseFromStringUsingSuffixMatching( + JoinString(proxy_bypass_list_, ';')); + + return true; +} + // static bool FirefoxProxySettings::GetSettingsFromFile(const FilePath& pref_file, FirefoxProxySettings* settings) { diff --git a/chrome/browser/importer/firefox_proxy_settings.h b/chrome/browser/importer/firefox_proxy_settings.h index eb624bd..80e1e24 100644 --- a/chrome/browser/importer/firefox_proxy_settings.h +++ b/chrome/browser/importer/firefox_proxy_settings.h @@ -12,6 +12,10 @@ class FilePath; +namespace net { +class ProxyConfig; +} + class FirefoxProxySettings { public: enum ProxyConfig { @@ -62,6 +66,14 @@ class FirefoxProxySettings { return proxy_bypass_list_; } + const std::string autoconfig_url() const { + return autoconfig_url_; + } + + // Converts a FirefoxProxySettings object to a net::ProxyConfig. + // On success returns true and fills |config| with the result. + bool ToProxyConfig(net::ProxyConfig* config); + protected: // Gets the settings from the passed prefs.js file and returns true if // successful. diff --git a/chrome/browser/importer/firefox_proxy_settings_unittest.cc b/chrome/browser/importer/firefox_proxy_settings_unittest.cc index 8c854f4..8926f47 100644 --- a/chrome/browser/importer/firefox_proxy_settings_unittest.cc +++ b/chrome/browser/importer/firefox_proxy_settings_unittest.cc @@ -2,12 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <sstream> + #include "testing/gtest/include/gtest/gtest.h" #include "base/file_path.h" #include "base/path_service.h" #include "chrome/browser/importer/firefox_proxy_settings.h" #include "chrome/common/chrome_paths.h" +#include "net/proxy/proxy_config.h" class FirefoxProxySettingsTest : public testing::Test { }; @@ -47,5 +50,29 @@ TEST_F(FirefoxProxySettingsTest, TestParse) { EXPECT_EQ("localhost", settings.proxy_bypass_list()[0]); EXPECT_EQ("127.0.0.1", settings.proxy_bypass_list()[1]); EXPECT_EQ("noproxy.com", settings.proxy_bypass_list()[2]); -} + // Test that ToProxyConfig() properly translates into a net::ProxyConfig. + net::ProxyConfig config; + EXPECT_TRUE(settings.ToProxyConfig(&config)); + + // Pretty-print |config| to a string (easy way to define the expectations). + std::ostringstream stream; + stream << config; + std::string pretty_printed_config = stream.str(); + + EXPECT_EQ( + "Automatic settings:\n" + " Auto-detect: No\n" + " Custom PAC script: [None]\n" + "Manual settings:\n" + " Proxy server: \n" + " HTTP: http_proxy:1111\n" + " HTTPS: ssl_proxy:2222\n" + " FTP: ftp_proxy:3333\n" + " SOCKS: socks4://socks_host:5555\n" + " Bypass list: \n" + " *localhost\n" + " 127.0.0.1\n" + " *noproxy.com", + pretty_printed_config); +} diff --git a/chrome/browser/net/connection_tester.cc b/chrome/browser/net/connection_tester.cc new file mode 100644 index 0000000..c441f1d --- /dev/null +++ b/chrome/browser/net/connection_tester.cc @@ -0,0 +1,403 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/net/connection_tester.h" + +#include "base/command_line.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "chrome/browser/importer/firefox_proxy_settings.h" +#include "chrome/common/chrome_switches.h" +#include "net/base/cookie_monster.h" +#include "net/base/host_resolver_impl.h" +#include "net/base/net_errors.h" +#include "net/base/net_util.h" +#include "net/base/ssl_config_service_defaults.h" +#include "net/disk_cache/disk_cache.h" +#include "net/ftp/ftp_network_layer.h" +#include "net/http/http_auth_handler_factory.h" +#include "net/http/http_cache.h" +#include "net/http/http_network_layer.h" +#include "net/proxy/proxy_config_service_fixed.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_context.h" + +namespace { + +// ExperimentURLRequestContext ------------------------------------------------ + +// An instance of ExperimentURLRequestContext is created for each experiment +// run by ConnectionTester. The class initializes network dependencies according +// to the specified "experiment". +class ExperimentURLRequestContext : public URLRequestContext { + public: + int Init(const ConnectionTester::Experiment& experiment) { + int rv; + + // Create a custom HostResolver for this experiment. + rv = CreateHostResolver(experiment.host_resolver_experiment, + &host_resolver_); + if (rv != net::OK) + return rv; // Failure. + + // Create a custom ProxyService for this this experiment. + rv = CreateProxyService(experiment.proxy_settings_experiment, + &proxy_service_); + if (rv != net::OK) + return rv; // Failure. + + // The rest of the dependencies are standard, and don't depend on the + // experiment being run. + ftp_transaction_factory_ = new net::FtpNetworkLayer(host_resolver_); + ssl_config_service_ = new net::SSLConfigServiceDefaults; + http_auth_handler_factory_ = net::HttpAuthHandlerFactory::CreateDefault(); + http_transaction_factory_ = + new net::HttpCache( + net::HttpNetworkLayer::CreateFactory(NULL, host_resolver_, + proxy_service_, + ssl_config_service_, + http_auth_handler_factory_), + disk_cache::CreateInMemoryCacheBackend(0)); + // In-memory cookie store. + cookie_store_ = new net::CookieMonster(NULL, NULL); + + return net::OK; + } + + protected: + virtual ~ExperimentURLRequestContext() { + delete ftp_transaction_factory_; + delete http_transaction_factory_; + delete http_auth_handler_factory_; + } + + private: + // Creates a host resolver for |experiment|. On success returns net::OK and + // fills |host_resolver| with a new pointer. Otherwise returns a network + // error code. + int CreateHostResolver( + ConnectionTester::HostResolverExperiment experiment, + scoped_refptr<net::HostResolver>* host_resolver) { + // Create a vanilla HostResolver that disables caching. + const size_t kMaxJobs = 50u; + scoped_refptr<net::HostResolverImpl> impl = new net::HostResolverImpl( + NULL, NULL, NULL, kMaxJobs); + + *host_resolver = impl; + + // Modify it slightly based on the experiment being run. + switch (experiment) { + case ConnectionTester::HOST_RESOLVER_EXPERIMENT_PLAIN: + return net::OK; + case ConnectionTester::HOST_RESOLVER_EXPERIMENT_DISABLE_IPV6: + impl->SetDefaultAddressFamily(net::ADDRESS_FAMILY_IPV4); + return net::OK; + case ConnectionTester::HOST_RESOLVER_EXPERIMENT_IPV6_PROBE: { + // Note that we don't use impl->ProbeIPv6Support() since that finishes + // asynchronously and may not take effect in time for the test. + // So instead we will probe synchronously (might take 100-200 ms). + net::AddressFamily family = net::IPv6Supported() ? + net::ADDRESS_FAMILY_UNSPECIFIED : net::ADDRESS_FAMILY_IPV4; + impl->SetDefaultAddressFamily(family); + return net::OK; + } + default: + NOTREACHED(); + return net::ERR_UNEXPECTED; + } + } + + // Creates a proxy config service for |experiment|. On success returns net::OK + // and fills |config_service| with a new pointer. Otherwise returns a network + // error code. + int CreateProxyConfigService( + ConnectionTester::ProxySettingsExperiment experiment, + scoped_ptr<net::ProxyConfigService>* config_service) { + switch (experiment) { + case ConnectionTester::PROXY_EXPERIMENT_USE_SYSTEM_SETTINGS: + return CreateSystemProxyConfigService(config_service); + case ConnectionTester::PROXY_EXPERIMENT_USE_FIREFOX_SETTINGS: + return CreateFirefoxProxyConfigService(config_service); + case ConnectionTester::PROXY_EXPERIMENT_USE_AUTO_DETECT: + config_service->reset(new net::ProxyConfigServiceFixed( + net::ProxyConfig::CreateAutoDetect())); + return net::OK; + case ConnectionTester::PROXY_EXPERIMENT_USE_DIRECT: + config_service->reset(new net::ProxyConfigServiceFixed( + net::ProxyConfig::CreateDirect())); + return net::OK; + default: + NOTREACHED(); + return net::ERR_UNEXPECTED; + } + } + + // Creates a proxy service for |experiment|. On success returns net::OK + // and fills |config_service| with a new pointer. Otherwise returns a network + // error code. + int CreateProxyService( + ConnectionTester::ProxySettingsExperiment experiment, + scoped_refptr<net::ProxyService>* proxy_service) { + // Create an appropriate proxy config service. + scoped_ptr<net::ProxyConfigService> config_service; + int rv = CreateProxyConfigService(experiment, &config_service); + if (rv != net::OK) + return rv; // Failure. + + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kSingleProcess)) { + // We can't create a standard proxy resolver in single-process mode. + // Rather than falling-back to some other implementation, fail. + return net::ERR_NOT_IMPLEMENTED; + } + + *proxy_service = net::ProxyService::Create( + config_service.release(), + true, this, NULL, NULL, MessageLoop::current()); + + return net::OK; + } + + // Creates a proxy config service that pulls from the system proxy settings. + // On success returns net::OK and fills |config_service| with a new pointer. + // Otherwise returns a network error code. + int CreateSystemProxyConfigService( + scoped_ptr<net::ProxyConfigService>* config_service) { +#if defined(OS_LINUX) + // TODO(eroman): This is not supported on Linux yet, because of how + // construction needs ot happen on the UI thread. + return net::ERR_NOT_IMPLEMENTED; +#else + config_service->reset( + net::ProxyService::CreateSystemProxyConfigService(NULL, NULL)); + return net::OK; +#endif + } + + // Creates a fixed proxy config service that is initialized using Firefox's + // current proxy settings. On success returns net::OK and fills + // |config_service| with a new pointer. Otherwise returns a network error + // code. + int CreateFirefoxProxyConfigService( + scoped_ptr<net::ProxyConfigService>* config_service) { + // Fetch Firefox's proxy settings (can fail if Firefox is not installed). + FirefoxProxySettings firefox_settings; + if (!FirefoxProxySettings::GetSettings(&firefox_settings)) + return net::ERR_FILE_NOT_FOUND; + + if (FirefoxProxySettings::SYSTEM == firefox_settings.config_type()) + return CreateSystemProxyConfigService(config_service); + + net::ProxyConfig config; + if (firefox_settings.ToProxyConfig(&config)) { + config_service->reset(new net::ProxyConfigServiceFixed(config)); + return net::OK; + } + + return net::ERR_FAILED; + } +}; + +} // namespace + +// ConnectionTester::TestRunner ---------------------------------------------- + +// TestRunner is a helper class for running an individual experiment. It can +// be deleted any time after it is started, and this will abort the request. +class ConnectionTester::TestRunner : public URLRequest::Delegate { + public: + // |tester| must remain alive throughout the TestRunner's lifetime. + // |tester| will be notified of completion. + explicit TestRunner(ConnectionTester* tester) : tester_(tester) {} + + // Starts running |experiment|. Notifies tester->OnExperimentCompleted() when + // it is done. + void Run(const Experiment& experiment); + + // URLRequest::Delegate implementation. + virtual void OnResponseStarted(URLRequest* request); + virtual void OnReadCompleted(URLRequest* request, int bytes_read); + // TODO(eroman): handle cases requiring authentication. + + private: + // The number of bytes to read each response body chunk. + static const int kReadBufferSize = 1024; + + // Starts reading the response's body (and keeps reading until an error or + // end of stream). + void ReadBody(URLRequest* request); + + // Called when the request has completed (for both success and failure). + void OnResponseCompleted(URLRequest* request); + + ConnectionTester* tester_; + scoped_ptr<URLRequest> request_; + + DISALLOW_COPY_AND_ASSIGN(TestRunner); +}; + +void ConnectionTester::TestRunner::OnResponseStarted(URLRequest* request) { + if (!request->status().is_success()) { + OnResponseCompleted(request); + return; + } + + // Start reading the body. + ReadBody(request); +} + +void ConnectionTester::TestRunner::OnReadCompleted(URLRequest* request, + int bytes_read) { + if (bytes_read <= 0) { + OnResponseCompleted(request); + return; + } + + // Keep reading until the stream is closed. Throw the data read away. + ReadBody(request); +} + +void ConnectionTester::TestRunner::ReadBody(URLRequest* request) { + // Read the response body |kReadBufferSize| bytes at a time. + scoped_refptr<net::IOBuffer> unused_buffer = + new net::IOBuffer(kReadBufferSize); + int num_bytes; + if (request->Read(unused_buffer, kReadBufferSize, &num_bytes)) { + OnReadCompleted(request, num_bytes); + } else if (!request->status().is_io_pending()) { + // Read failed synchronously. + OnResponseCompleted(request); + } +} + +void ConnectionTester::TestRunner::OnResponseCompleted(URLRequest* request) { + int result = net::OK; + if (!request->status().is_success()) { + DCHECK_NE(net::ERR_IO_PENDING, request->status().os_error()); + result = request->status().os_error(); + } + tester_->OnExperimentCompleted(result); +} + +void ConnectionTester::TestRunner::Run(const Experiment& experiment) { + // Try to create a URLRequestContext for this experiment. + scoped_refptr<ExperimentURLRequestContext> context = + new ExperimentURLRequestContext(); + int rv = context->Init(experiment); + if (rv != net::OK) { + // Complete the experiment with a failure. + tester_->OnExperimentCompleted(rv); + return; + } + + // Fetch a request using the experimental context. + request_.reset(new URLRequest(experiment.url, this)); + request_->set_context(context); + request_->Start(); +} + +// ConnectionTester ---------------------------------------------------------- + +ConnectionTester::ConnectionTester(Delegate* delegate) + : delegate_(delegate) { + DCHECK(delegate); +} + +ConnectionTester::~ConnectionTester() { + // Cancellation happens automatically by deleting test_runner_. +} + +void ConnectionTester::RunAllTests(const GURL& url) { + // Select all possible experiments to run. (In no particular order). + // It is possible that some of these experiments are actually duplicates. + GetAllPossibleExperimentCombinations(url, &remaining_experiments_); + + delegate_->OnStartConnectionTestSuite(); + StartNextExperiment(); +} + +// static +string16 ConnectionTester::ProxySettingsExperimentDescription( + ProxySettingsExperiment experiment) { + // TODO(eroman): Use proper string resources. + switch (experiment) { + case PROXY_EXPERIMENT_USE_DIRECT: + return ASCIIToUTF16("Don't use any proxy"); + case PROXY_EXPERIMENT_USE_SYSTEM_SETTINGS: + return ASCIIToUTF16("Use system proxy settings"); + case PROXY_EXPERIMENT_USE_FIREFOX_SETTINGS: + return ASCIIToUTF16("Use Firefox's proxy settings"); + case PROXY_EXPERIMENT_USE_AUTO_DETECT: + return ASCIIToUTF16("Auto-detect proxy settings"); + default: + NOTREACHED(); + return string16(); + } +} + +// static +string16 ConnectionTester::HostResolverExperimentDescription( + HostResolverExperiment experiment) { + // TODO(eroman): Use proper string resources. + switch (experiment) { + case HOST_RESOLVER_EXPERIMENT_PLAIN: + return string16(); + case HOST_RESOLVER_EXPERIMENT_DISABLE_IPV6: + return ASCIIToUTF16("Disable IPv6 host resolving"); + case HOST_RESOLVER_EXPERIMENT_IPV6_PROBE: + return ASCIIToUTF16("Probe for IPv6 host resolving"); + default: + NOTREACHED(); + return string16(); + } +} + +// static +void ConnectionTester::GetAllPossibleExperimentCombinations( + const GURL& url, + ConnectionTester::ExperimentList* list) { + list->clear(); + for (size_t resolver_experiment = 0; + resolver_experiment < HOST_RESOLVER_EXPERIMENT_COUNT; + ++resolver_experiment) { + for (size_t proxy_experiment = 0; + proxy_experiment < PROXY_EXPERIMENT_COUNT; + ++proxy_experiment) { + Experiment experiment( + url, + static_cast<ProxySettingsExperiment>(proxy_experiment), + static_cast<HostResolverExperiment>(resolver_experiment)); + list->push_back(experiment); + } + } +} + +void ConnectionTester::StartNextExperiment() { + DCHECK(!remaining_experiments_.empty()); + DCHECK(!current_test_runner_.get()); + + delegate_->OnStartConnectionTestExperiment(current_experiment()); + + current_test_runner_.reset(new TestRunner(this)); + current_test_runner_->Run(current_experiment()); +} + +void ConnectionTester::OnExperimentCompleted(int result) { + Experiment current = current_experiment(); + + // Advance to the next experiment. + remaining_experiments_.erase(remaining_experiments_.begin()); + current_test_runner_.reset(); + + // Notify the delegate of completion. + delegate_->OnCompletedConnectionTestExperiment(current, result); + + if (remaining_experiments_.empty()) { + delegate_->OnCompletedConnectionTestSuite(); + } else { + StartNextExperiment(); + } +} + diff --git a/chrome/browser/net/connection_tester.h b/chrome/browser/net/connection_tester.h new file mode 100644 index 0000000..10534f5 --- /dev/null +++ b/chrome/browser/net/connection_tester.h @@ -0,0 +1,175 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_NET_CONNECTION_TESTER_H_ +#define CHROME_BROWSER_NET_CONNECTION_TESTER_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" +#include "googleurl/src/gurl.h" +#include "net/base/completion_callback.h" + +// ConnectionTester runs a suite of tests (also called "experiments"), +// to try and discover why loading a particular URL is failing with an error +// code. +// +// For example, one reason why the URL might have failed, is that the +// network requires the URL to be routed through a proxy, however chrome is +// not configured for that. +// +// The above issue might be detected by running test that fetches the URL using +// auto-detect and seeing if it works this time. Or even by retrieving the +// settings from another installed browser and trying with those. +// +// USAGE: +// +// To run the test suite, create an instance of ConnectionTester and then call +// RunAllTests(). +// +// This starts a sequence of tests, which will complete asynchronously. +// The ConnectionTester object can be deleted at any time, and it will abort +// any of the in-progress tests. +// +// As tests are started or completed, notification will be sent through the +// "Delegate" object. + +class ConnectionTester { + public: + // This enum lists the possible proxy settings configurations. + enum ProxySettingsExperiment { + // Do not use any proxy. + PROXY_EXPERIMENT_USE_DIRECT = 0, + + // Use the system proxy settings. + PROXY_EXPERIMENT_USE_SYSTEM_SETTINGS, + + // Use Firefox's proxy settings if they are available. + PROXY_EXPERIMENT_USE_FIREFOX_SETTINGS, + + // Use proxy auto-detect. + PROXY_EXPERIMENT_USE_AUTO_DETECT, + + PROXY_EXPERIMENT_COUNT, + }; + + // This enum lists the possible host resolving configurations. + enum HostResolverExperiment { + // Use a default host resolver implementation. + HOST_RESOLVER_EXPERIMENT_PLAIN = 0, + + // Disable IPv6 host resolving. + HOST_RESOLVER_EXPERIMENT_DISABLE_IPV6, + + // Probe for IPv6 support. + HOST_RESOLVER_EXPERIMENT_IPV6_PROBE, + + HOST_RESOLVER_EXPERIMENT_COUNT, + }; + + // The "Experiment" structure describes an individual test to run. + struct Experiment { + Experiment(const GURL& url, + ProxySettingsExperiment proxy_settings_experiment, + HostResolverExperiment host_resolver_experiment) + : url(url), + proxy_settings_experiment(proxy_settings_experiment), + host_resolver_experiment(host_resolver_experiment) { + } + + // The URL to try and fetch. + GURL url; + + // The proxy settings to use. + ProxySettingsExperiment proxy_settings_experiment; + + // The host resolver settings to use. + HostResolverExperiment host_resolver_experiment; + }; + + typedef std::vector<Experiment> ExperimentList; + + // "Delegate" is an interface for receiving start and completion notification + // of individual tests that are run by the ConnectionTester. + // + // NOTE: do not delete the ConnectionTester when executing within one of the + // delegate methods. + class Delegate { + public: + virtual ~Delegate() {} + + // Called once the test suite is about to start. + virtual void OnStartConnectionTestSuite() = 0; + + // Called when an individual experiment is about to be started. + virtual void OnStartConnectionTestExperiment( + const Experiment& experiment) = 0; + + // Called when an individual experiment has completed. + // |experiment| - the experiment that has completed. + // |result| - the net error that the experiment completed with + // (or net::OK if it was success). + virtual void OnCompletedConnectionTestExperiment( + const Experiment& experiment, + int result) = 0; + + // Called once ALL tests have completed. + virtual void OnCompletedConnectionTestSuite() = 0; + }; + + // Constructs a ConnectionTester that notifies test progress to |delegate|. + // |delegate| is owned by the caller, and must remain valid for the lifetime + // of ConnectionTester. + explicit ConnectionTester(Delegate* delegate); + + // Note that destruction cancels any in-progress tests. + ~ConnectionTester(); + + // Starts running the test suite on |url|. Notification of progress is sent to + // |delegate_|. + void RunAllTests(const GURL& url); + + // Returns a text string explaining what |experiment| is testing. + static string16 ProxySettingsExperimentDescription( + ProxySettingsExperiment experiment); + static string16 HostResolverExperimentDescription( + HostResolverExperiment experiment); + + private: + // Internally each experiment run by ConnectionTester is handled by a + // "TestRunner" instance. + class TestRunner; + friend class TestRunner; + + // Fills |list| with the set of all possible experiments for |url|. + static void GetAllPossibleExperimentCombinations(const GURL& url, + ExperimentList* list); + + // Starts the next experiment from |remaining_experiments_|. + void StartNextExperiment(); + + // Callback for when |current_test_runner_| finishes. + void OnExperimentCompleted(int result); + + // Returns the experiment at the front of our list. + const Experiment& current_experiment() const { + return remaining_experiments_.front(); + } + + // The object to notify test progress to. + Delegate* delegate_; + + // The current in-progress test, or NULL if there is no active test. + scoped_ptr<TestRunner> current_test_runner_; + + // The ordered list of experiments to try next. The experiment at the front + // of the list is the one currently in progress. + ExperimentList remaining_experiments_; + + DISALLOW_COPY_AND_ASSIGN(ConnectionTester); +}; + +#endif // CHROME_BROWSER_NET_CONNECTION_TESTER_H_ + diff --git a/chrome/browser/net/connection_tester_unittest.cc b/chrome/browser/net/connection_tester_unittest.cc new file mode 100644 index 0000000..b9cf2ca --- /dev/null +++ b/chrome/browser/net/connection_tester_unittest.cc @@ -0,0 +1,138 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/net/connection_tester.h" + +#include "net/base/mock_host_resolver.h" +#include "net/url_request/url_request_unittest.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +namespace { + +// This is a testing delegate which simply counts how many times each of +// the delegate's methods were invoked. +class ConnectionTesterDelegate : public ConnectionTester::Delegate { + public: + ConnectionTesterDelegate() + : start_connection_test_suite_count_(0), + start_connection_test_experiment_count_(0), + completed_connection_test_experiment_count_(0), + completed_connection_test_suite_count_(0) { + } + + virtual void OnStartConnectionTestSuite() { + start_connection_test_suite_count_++; + } + + virtual void OnStartConnectionTestExperiment( + const ConnectionTester::Experiment& experiment) { + start_connection_test_experiment_count_++; + } + + virtual void OnCompletedConnectionTestExperiment( + const ConnectionTester::Experiment& experiment, + int result) { + completed_connection_test_experiment_count_++; + } + + virtual void OnCompletedConnectionTestSuite() { + completed_connection_test_suite_count_++; + MessageLoop::current()->Quit(); + } + + int start_connection_test_suite_count() const { + return start_connection_test_suite_count_; + } + + int start_connection_test_experiment_count() const { + return start_connection_test_experiment_count_; + } + + int completed_connection_test_experiment_count() const { + return completed_connection_test_experiment_count_; + } + + int completed_connection_test_suite_count() const { + return completed_connection_test_suite_count_; + } + + private: + int start_connection_test_suite_count_; + int start_connection_test_experiment_count_; + int completed_connection_test_experiment_count_; + int completed_connection_test_suite_count_; +}; + +// The test fixture is responsible for: +// - Making sure each test has an IO loop running +// - Catching any host resolve requests and mapping them to localhost +// (so the test doesn't use any external network dependencies). +class ConnectionTesterTest : public PlatformTest { + public: + ConnectionTesterTest() : message_loop_(MessageLoop::TYPE_IO) { + scoped_refptr<net::RuleBasedHostResolverProc> catchall_resolver = + new net::RuleBasedHostResolverProc(NULL); + + catchall_resolver->AddRule("*", "127.0.0.1"); + + scoped_host_resolver_proc_.Init(catchall_resolver); + } + + protected: + net::ScopedDefaultHostResolverProc scoped_host_resolver_proc_; + ConnectionTesterDelegate test_delegate_; + MessageLoop message_loop_; +}; + +TEST_F(ConnectionTesterTest, RunAllTests) { + scoped_refptr<HTTPTestServer> server = + HTTPTestServer::CreateForkingServer(L"net/data/url_request_unittest/"); + + ConnectionTester tester(&test_delegate_); + + // Start the test suite on URL "echoall". + // TODO(eroman): Is this URL right? + GURL url = server->TestServerPage("echoall"); + tester.RunAllTests(url); + + // Wait for all the tests to complete. + MessageLoop::current()->Run(); + + const int kNumExperiments = + ConnectionTester::PROXY_EXPERIMENT_COUNT * + ConnectionTester::HOST_RESOLVER_EXPERIMENT_COUNT; + + EXPECT_EQ(1, test_delegate_.start_connection_test_suite_count()); + EXPECT_EQ(kNumExperiments, + test_delegate_.start_connection_test_experiment_count()); + EXPECT_EQ(kNumExperiments, + test_delegate_.completed_connection_test_experiment_count()); + EXPECT_EQ(1, test_delegate_.completed_connection_test_suite_count()); +} + +TEST_F(ConnectionTesterTest, DeleteWhileInProgress) { + scoped_refptr<HTTPTestServer> server = + HTTPTestServer::CreateForkingServer(L"net/data/url_request_unittest/"); + + ConnectionTester tester(&test_delegate_); + + // Start the test suite on URL "echoall". + // TODO(eroman): Is this URL right? + GURL url = server->TestServerPage("echoall"); + tester.RunAllTests(url); + + MessageLoop::current()->RunAllPending(); + + EXPECT_EQ(1, test_delegate_.start_connection_test_suite_count()); + EXPECT_EQ(1, test_delegate_.start_connection_test_experiment_count()); + EXPECT_EQ(0, test_delegate_.completed_connection_test_experiment_count()); + EXPECT_EQ(0, test_delegate_.completed_connection_test_suite_count()); + + // Note here that we don't wait for the tests to complete + // (so |tester| will be deleted upon leaving this scope. +} + +} // namespace + diff --git a/chrome/browser/resources/net_internals/index.html b/chrome/browser/resources/net_internals/index.html index be62e42..648333c 100644 --- a/chrome/browser/resources/net_internals/index.html +++ b/chrome/browser/resources/net_internals/index.html @@ -10,6 +10,7 @@ found in the LICENSE file. <script src="view.js"></script> <script src="tabswitcherview.js"></script> <script src="dataview.js"></script> + <script src="testview.js"></script> <script src="main.js"></script> <script src="dnsview.js"></script> <script src="requestsview.js"></script> @@ -32,6 +33,7 @@ found in the LICENSE file. <li><a href="#sockets" id=socketsTab>Sockets</a></li> <li><a href="#httpCache" id=httpCacheTab>HTTP Cache</a></li> <li><a href="#data" id=dataTab>Data</a></li> + <li><a href="#tests" id=testTab>Tests</a></li> </ul> <div style="clear: both;"></div> </div> @@ -89,12 +91,19 @@ found in the LICENSE file. <div id=httpCacheTabContent>TODO: display http cache information (disk cache navigator)</div> <!-- Import/Export data --> <div id=dataTabContent> - <br/> - <button id=exportToText><b>Click here to generate bug report data</b><br/>(Copy paste the result and attach to bug)</button> + <button id=exportToText><b>Click here to generate bug report data</b><br/>(Copy paste the result and attach to bug)</button> <br/> <br/> <button id=exportToJson>Export data to JSON</button> </div> + <!-- Connection tests --> + <div id=testTabContent> + <p>Input a URL which failed to load, and then click the button to run some + tests for why it failed.</p> + URL: <input type=text id=testUrlInput /> + <input type=button id=testStart value="Start tests" /> + <div id=testSummary></div> + </div> <!-- ================= Requests view =================== --> diff --git a/chrome/browser/resources/net_internals/main.css b/chrome/browser/resources/net_internals/main.css index 76c608d..4da22a0 100644 --- a/chrome/browser/resources/net_internals/main.css +++ b/chrome/browser/resources/net_internals/main.css @@ -181,12 +181,12 @@ body { #detailsLogBox, #detailsTimelineBox, -#proxyTabContent { +#proxyTabContent, +#dataTabContent, +#dnsTabContent, +#testTabContent { overflow: auto; -} - -#proxyTabContent { - padding-left: 20px; + padding: 10px; } #proxyTabContent td, diff --git a/chrome/browser/resources/net_internals/main.js b/chrome/browser/resources/net_internals/main.js index d06e5e9..b05cbdd 100644 --- a/chrome/browser/resources/net_internals/main.js +++ b/chrome/browser/resources/net_internals/main.js @@ -61,6 +61,11 @@ function onLoaded() { // captured data. var dataView = new DataView("dataTabContent", "exportToJson", "exportToText"); + // Create a view which will display the results and controls for connection + // tests. + var testView = new TestView("testTabContent", "testUrlInput", "testStart", + "testSummary"); + // Create a view which lets you tab between the different sub-views. var categoryTabSwitcher = new TabSwitcherView(new DivView('categoryTabHandles')); @@ -74,6 +79,7 @@ function onLoaded() { categoryTabSwitcher.addTab('httpCacheTab', new DivView('httpCacheTabContent'), false); categoryTabSwitcher.addTab('dataTab', dataView, false); + categoryTabSwitcher.addTab('testTab', testView, false); // Build a map from the anchor name of each tab handle to its "tab ID". // We will consider navigations to the #hash as a switch tab request. @@ -114,9 +120,11 @@ function onLoaded() { function BrowserBridge() { // List of observers for various bits of browser state. this.logObservers_ = []; + this.connectionTestsObservers_ = []; this.proxySettings_ = new PollableDataHelper('onProxySettingsChanged'); this.badProxies_ = new PollableDataHelper('onBadProxiesChanged'); - this.hostResolverCache_ = new PollableDataHelper('onHostResolverCacheChanged'); + this.hostResolverCache_ = + new PollableDataHelper('onHostResolverCacheChanged'); // Cache of the data received. // TODO(eroman): the controls to clear data in the "Requests" tab should be @@ -171,6 +179,10 @@ BrowserBridge.prototype.sendClearHostResolverCache = function() { chrome.send('clearHostResolverCache'); }; +BrowserBridge.prototype.sendStartConnectionTests = function(url) { + chrome.send('startConnectionTests', [url]); +}; + //------------------------------------------------------------------------------ // Messages received from the browser //------------------------------------------------------------------------------ @@ -222,6 +234,33 @@ BrowserBridge.prototype.receivedPassiveLogEntries = function(entries) { this.receivedLogEntry(entries[i], true); }; + +BrowserBridge.prototype.receivedStartConnectionTestSuite = function() { + for (var i = 0; i < this.connectionTestsObservers_.length; ++i) + this.connectionTestsObservers_[i].onStartedConnectionTestSuite(); +}; + +BrowserBridge.prototype.receivedStartConnectionTestExperiment = function( + experiment) { + for (var i = 0; i < this.connectionTestsObservers_.length; ++i) { + this.connectionTestsObservers_[i].onStartedConnectionTestExperiment( + experiment); + } +}; + +BrowserBridge.prototype.receivedCompletedConnectionTestExperiment = +function(info) { + for (var i = 0; i < this.connectionTestsObservers_.length; ++i) { + this.connectionTestsObservers_[i].onCompletedConnectionTestExperiment( + info.experiment, info.result); + } +}; + +BrowserBridge.prototype.receivedCompletedConnectionTestSuite = function() { + for (var i = 0; i < this.connectionTestsObservers_.length; ++i) + this.connectionTestsObservers_[i].onCompletedConnectionTestSuite(); +}; + //------------------------------------------------------------------------------ /** @@ -273,6 +312,19 @@ BrowserBridge.prototype.addHostResolverCacheObserver = function(observer) { }; /** + * Adds a listener for the progress of the connection tests. + * The observer will be called back with: + * + * observer.onStartedConnectionTestSuite(); + * observer.onStartedConnectionTestExperiment(experiment); + * observer.onCompletedConnectionTestExperiment(experiment, result); + * observer.onCompletedConnectionTestSuite(); + */ +BrowserBridge.prototype.addConnectionTestsObserver = function(observer) { + this.connectionTestsObservers_.push(observer); +}; + +/** * The browser gives us times in terms of "time ticks" in milliseconds. * This function converts the tick count to a Date() object. * diff --git a/chrome/browser/resources/net_internals/testview.js b/chrome/browser/resources/net_internals/testview.js new file mode 100644 index 0000000..e721606 --- /dev/null +++ b/chrome/browser/resources/net_internals/testview.js @@ -0,0 +1,132 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * This view displays the progress and results from the "connection tester". + * + * - Has an input box to specify the URL. + * - Has a button to start running the tests. + * - Shows the set of experiments that have been run so far, and their + * result. + * + * @constructor + */ +function TestView(mainBoxId, urlInputId, startButtonId, summaryDivId) { + DivView.call(this, mainBoxId); + + this.urlInput_ = document.getElementById(urlInputId); + var startButton = document.getElementById(startButtonId); + this.summaryDiv_ = document.getElementById(summaryDivId); + + startButton.onclick = this.startTests_.bind(this); + + g_browser.addConnectionTestsObserver(this); +} + +inherits(TestView, DivView); + +TestView.prototype.startTests_ = function() { + g_browser.sendStartConnectionTests(this.urlInput_.value); +}; + +/** + * Callback for when the connection tests have begun. + */ +TestView.prototype.onStartedConnectionTestSuite = function() { + this.summaryDiv_.innerHTML = ''; + + var p = addNode(this.summaryDiv_, 'p'); + addTextNode(p, 'Started connection test suite suite on ' + + (new Date()).toLocaleString()); + + // Add a table that will hold the individual test results. + var table = addNode(this.summaryDiv_, 'table'); + table.border = 1; + var thead = addNode(table, 'thead'); + thead.innerHTML = '<tr><th>Result</th><th>Experiment</th>' + + '<th>Error</th><th>Time (ms)</th></tr>'; + + this.tbody_ = addNode(table, 'tbody'); +}; + +/** + * Callback for when an individual test in the suite has begun. + */ +TestView.prototype.onStartedConnectionTestExperiment = function(experiment) { + var tr = addNode(this.tbody_, 'tr'); + + var passFailCell = addNode(tr, 'td'); + + var experimentCell = addNode(tr, 'td'); + + var resultCell = addNode(tr, 'td'); + addTextNode(resultCell, '?'); + + var dtCell = addNode(tr, 'td'); + addTextNode(dtCell, '?'); + + // We will fill in result cells with actual values (to replace the + // placeholder '?') once the test has completed. For now we just + // save references to these cells. + this.currentExperimentRow_ = { + experimentCell: experimentCell, + dtCell: dtCell, + resultCell: resultCell, + passFailCell: passFailCell, + startTime: (new Date()).getTime() + }; + + addTextNode(experimentCell, 'Fetch ' + experiment.url); + + if (experiment.proxy_settings_experiment || + experiment.host_resolver_experiment) { + var ul = addNode(experimentCell, 'ul'); + + if (experiment.proxy_settings_experiment) { + var li = addNode(ul, 'li'); + addTextNode(li, experiment.proxy_settings_experiment); + } + + if (experiment.host_resolver_experiment) { + var li = addNode(ul, 'li'); + addTextNode(li, experiment.host_resolver_experiment); + } + } +}; + +/** + * Callback for when an individual test in the suite has finished. + */ +TestView.prototype.onCompletedConnectionTestExperiment = function( + experiment, result) { + var r = this.currentExperimentRow_; + + var endTime = (new Date()).getTime(); + + r.dtCell.innerHTML = ''; + addTextNode(r.dtCell, (endTime - r.startTime)); + + r.resultCell.innerHTML = ''; + + if (result == 0) { + r.passFailCell.style.color = 'green'; + addTextNode(r.passFailCell, 'PASS'); + } else { + // TODO(eroman): stringize the error code. + addTextNode(r.resultCell, result); + r.passFailCell.style.color = 'red'; + addTextNode(r.passFailCell, 'FAIL'); + } + + this.currentExperimentRow_ = null; +}; + +/** + * Callback for when the last test in the suite has finished. + */ +TestView.prototype.onCompletedConnectionTestSuite = function() { + var p = addNode(this.summaryDiv_, 'p'); + addTextNode(p, 'Completed connection test suite suite'); +}; + diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 91b147e..d8e42d8 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1589,6 +1589,8 @@ 'browser/net/chrome_net_log.h', 'browser/net/chrome_url_request_context.cc', 'browser/net/chrome_url_request_context.h', + 'browser/net/connection_tester.cc', + 'browser/net/connection_tester.h', 'browser/net/url_request_context_getter.cc', 'browser/net/url_request_context_getter.h', 'browser/net/dns_global.cc', @@ -3401,6 +3403,7 @@ 'browser/resources/net_internals/resizableverticalsplitview.js', 'browser/resources/net_internals/sourceentry.js', 'browser/resources/net_internals/tabswitcherview.js', + 'browser/resources/net_internals/testview.js', 'browser/resources/net_internals/timelineviewpainter.js', 'browser/resources/net_internals/topmidbottomview.js', 'browser/resources/net_internals/util.js', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 40b2316..79c783c 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -843,6 +843,7 @@ 'browser/metrics/metrics_log_unittest.cc', 'browser/metrics/metrics_response_unittest.cc', 'browser/metrics/metrics_service_unittest.cc', + 'browser/net/connection_tester_unittest.cc', 'browser/net/chrome_url_request_context_unittest.cc', 'browser/net/dns_host_info_unittest.cc', 'browser/net/dns_master_unittest.cc', diff --git a/net/proxy/proxy_config.h b/net/proxy/proxy_config.h index c641bbf..58e70e3 100644 --- a/net/proxy/proxy_config.h +++ b/net/proxy/proxy_config.h @@ -148,6 +148,24 @@ class ProxyConfig { return auto_detect_; } + // Helpers to construct some common proxy configurations. + + static ProxyConfig CreateDirect() { + return ProxyConfig(); + } + + static ProxyConfig CreateAutoDetect() { + ProxyConfig config; + config.set_auto_detect(true); + return config; + } + + static ProxyConfig CreateFromCustomPacURL(const GURL& pac_url) { + ProxyConfig config; + config.set_pac_url(pac_url); + return config; + } + private: // True if the proxy configuration should be auto-detected. bool auto_detect_; diff --git a/net/proxy/proxy_config_service_fixed.h b/net/proxy/proxy_config_service_fixed.h index 54fd9ac..b677eb4 100644 --- a/net/proxy/proxy_config_service_fixed.h +++ b/net/proxy/proxy_config_service_fixed.h @@ -6,6 +6,7 @@ #define NET_PROXY_PROXY_CONFIG_SERVICE_FIXED_H_ #include "net/base/net_errors.h" +#include "net/proxy/proxy_config.h" #include "net/proxy/proxy_config_service.h" namespace net { diff --git a/net/url_request/url_request_context.h b/net/url_request/url_request_context.h index 070942f..cd1736b 100644 --- a/net/url_request/url_request_context.h +++ b/net/url_request/url_request_context.h @@ -30,13 +30,14 @@ class SocketStream; class URLRequest; // Subclass to provide application-specific context for URLRequest instances. -class URLRequestContext : - public base::RefCountedThreadSafe<URLRequestContext> { +class URLRequestContext + : public base::RefCountedThreadSafe<URLRequestContext> { public: URLRequestContext() : net_log_(NULL), http_transaction_factory_(NULL), ftp_transaction_factory_(NULL), + http_auth_handler_factory_(NULL), cookie_policy_(NULL), transport_security_state_(NULL) { } |