diff options
51 files changed, 2342 insertions, 1612 deletions
diff --git a/build/android/pylib/gtest/test_runner.py b/build/android/pylib/gtest/test_runner.py index 9947ab4..6799f5a 100644 --- a/build/android/pylib/gtest/test_runner.py +++ b/build/android/pylib/gtest/test_runner.py @@ -174,6 +174,7 @@ class SingleTestRunner(BaseTestRunner): 'net/data/cache_tests', 'net/data/filter_unittests', 'net/data/ftp', + 'net/data/proxy_resolver_v8_tracing_unittest', 'net/data/proxy_resolver_v8_unittest', 'net/data/ssl/certificates', 'net/data/url_request_unittest/', diff --git a/chrome/browser/net/connection_tester.cc b/chrome/browser/net/connection_tester.cc index f5e892b..5707a5d 100644 --- a/chrome/browser/net/connection_tester.cc +++ b/chrome/browser/net/connection_tester.cc @@ -206,7 +206,6 @@ class ExperimentURLRequestContext : public net::URLRequestContext { experiment_proxy_service->reset( net::CreateProxyServiceUsingV8ProxyResolver( proxy_config_service->release(), - 0u, new net::ProxyScriptFetcherImpl(proxy_request_context_), dhcp_factory.Create(proxy_request_context_), host_resolver(), diff --git a/chrome/browser/net/proxy_service_factory.cc b/chrome/browser/net/proxy_service_factory.cc index cff40af..0dd312a 100644 --- a/chrome/browser/net/proxy_service_factory.cc +++ b/chrome/browser/net/proxy_service_factory.cc @@ -117,7 +117,6 @@ net::ProxyService* ProxyServiceFactory::CreateProxyService( proxy_service = net::CreateProxyServiceUsingV8ProxyResolver( proxy_config_service, - num_pac_threads, new net::ProxyScriptFetcherImpl(context), dhcp_factory.Create(context), context->host_resolver(), diff --git a/net/base/capturing_net_log.cc b/net/base/capturing_net_log.cc index eb7f9e5..0ffdfb7 100644 --- a/net/base/capturing_net_log.cc +++ b/net/base/capturing_net_log.cc @@ -4,6 +4,7 @@ #include "net/base/capturing_net_log.h" +#include "base/json/json_writer.h" #include "base/logging.h" #include "base/values.h" @@ -34,14 +35,14 @@ CapturingNetLog::CapturedEntry::operator=(const CapturedEntry& entry) { time = entry.time; source = entry.source; phase = entry.phase; - params.reset(entry.params.get() ? entry.params->DeepCopy() : NULL); + params.reset(entry.params ? entry.params->DeepCopy() : NULL); return *this; } bool CapturingNetLog::CapturedEntry::GetStringValue( const std::string& name, std::string* value) const { - if (!params.get()) + if (!params) return false; return params->GetString(name, value); } @@ -49,7 +50,7 @@ bool CapturingNetLog::CapturedEntry::GetStringValue( bool CapturingNetLog::CapturedEntry::GetIntegerValue( const std::string& name, int* value) const { - if (!params.get()) + if (!params) return false; return params->GetInteger(name, value); } @@ -58,6 +59,14 @@ bool CapturingNetLog::CapturedEntry::GetNetErrorCode(int* value) const { return GetIntegerValue("net_error", value); } +std::string CapturingNetLog::CapturedEntry::GetParamsJson() const { + if (!params) + return std::string(); + std::string json; + base::JSONWriter::Write(params.get(), &json); + return json; +} + CapturingNetLog::CapturingNetLog() : last_id_(0), log_level_(LOG_ALL_BUT_BYTES) { diff --git a/net/base/capturing_net_log.h b/net/base/capturing_net_log.h index 413be527..ec78a3f 100644 --- a/net/base/capturing_net_log.h +++ b/net/base/capturing_net_log.h @@ -54,6 +54,10 @@ class CapturingNetLog : public NetLog { // log entry. bool GetNetErrorCode(int* value) const; + // Returns the parameters as a JSON string, or empty string if there are no + // parameters. + std::string GetParamsJson() const; + EventType type; base::TimeTicks time; Source source; diff --git a/net/base/mock_host_resolver.cc b/net/base/mock_host_resolver.cc index a0835b8..1bfd1ba 100644 --- a/net/base/mock_host_resolver.cc +++ b/net/base/mock_host_resolver.cc @@ -71,6 +71,7 @@ int MockHostResolverBase::Resolve(const RequestInfo& info, RequestHandle* handle, const BoundNetLog& net_log) { DCHECK(CalledOnValidThread()); + num_resolve_++; size_t id = next_request_id_++; int rv = ResolveFromIPLiteralOrCache(info, addresses); if (rv != ERR_DNS_CACHE_MISS) { @@ -97,6 +98,7 @@ int MockHostResolverBase::Resolve(const RequestInfo& info, int MockHostResolverBase::ResolveFromCache(const RequestInfo& info, AddressList* addresses, const BoundNetLog& net_log) { + num_resolve_from_cache_++; DCHECK(CalledOnValidThread()); next_request_id_++; int rv = ResolveFromIPLiteralOrCache(info, addresses); @@ -134,7 +136,9 @@ void MockHostResolverBase::ResolveAllPending() { MockHostResolverBase::MockHostResolverBase(bool use_caching) : synchronous_mode_(false), ondemand_mode_(false), - next_request_id_(1) { + next_request_id_(1), + num_resolve_(0), + num_resolve_from_cache_(0) { rules_ = CreateCatchAllHostResolverProc(); if (use_caching) { @@ -305,6 +309,10 @@ void RuleBasedHostResolverProc::AddSimulatedFailure( rules_.push_back(rule); } +void RuleBasedHostResolverProc::ClearRules() { + rules_.clear(); +} + int RuleBasedHostResolverProc::Resolve(const std::string& host, AddressFamily address_family, HostResolverFlags host_resolver_flags, diff --git a/net/base/mock_host_resolver.h b/net/base/mock_host_resolver.h index 5542a64..ee6f1b5 100644 --- a/net/base/mock_host_resolver.h +++ b/net/base/mock_host_resolver.h @@ -94,6 +94,16 @@ class MockHostResolverBase : public HostResolver, // ResolveAllPending(). bool has_pending_requests() const { return !requests_.empty(); } + // The number of times that Resolve() has been called. + size_t num_resolve() const { + return num_resolve_; + } + + // The number of times that ResolveFromCache() has been called. + size_t num_resolve_from_cache() const { + return num_resolve_from_cache_; + } + protected: explicit MockHostResolverBase(bool use_caching); @@ -117,6 +127,9 @@ class MockHostResolverBase : public HostResolver, RequestMap requests_; size_t next_request_id_; + size_t num_resolve_; + size_t num_resolve_from_cache_; + DISALLOW_COPY_AND_ASSIGN(MockHostResolverBase); }; @@ -158,8 +171,10 @@ class RuleBasedHostResolverProc : public HostResolverProc { // Same as AddRule(), but the replacement is expected to be an IPv4 or IPv6 // literal. This can be used in place of AddRule() to bypass the system's // host resolver (the address list will be constructed manually). - // If |canonical-name| is non-empty, it is copied to the resulting AddressList + // If |canonical_name| is non-empty, it is copied to the resulting AddressList // but does not impact DNS resolution. + // |ip_literal| can be a single IP address like "192.168.1.1" or a comma + // separated list of IP addresses, like "::1,192:168.1.2". void AddIPLiteralRule(const std::string& host_pattern, const std::string& ip_literal, const std::string& canonical_name); @@ -175,6 +190,9 @@ class RuleBasedHostResolverProc : public HostResolverProc { // Simulate a lookup failure for |host| (it also can be a pattern). void AddSimulatedFailure(const std::string& host); + // Deletes all the rules that have been added. + void ClearRules(); + // HostResolverProc methods: virtual int Resolve(const std::string& host, AddressFamily address_family, diff --git a/net/data/proxy_resolver_v8_tracing_unittest/dns.js b/net/data/proxy_resolver_v8_tracing_unittest/dns.js new file mode 100644 index 0000000..fbfb74e --- /dev/null +++ b/net/data/proxy_resolver_v8_tracing_unittest/dns.js @@ -0,0 +1,31 @@ +var g_iteration = 0; + +function FindProxyForURL(url, host) { + alert('iteration: ' + g_iteration++); + + var ips = [ + myIpAddress(), + dnsResolve(''), + dnsResolveEx('host1'), + dnsResolve('host2'), + dnsResolve('host3'), + myIpAddress(), + dnsResolve('host3'), + dnsResolveEx('host1'), + myIpAddress(), + dnsResolve('host2'), + dnsResolveEx('host6'), + myIpAddressEx(), + dnsResolve('host1'), + ]; + + for (var i = 0; i < ips.length; ++i) { + // Stringize everything. + ips[i] = '' + ips[i]; + } + + var proxyHost = ips.join('-'); + proxyHost = proxyHost.replace(/[^0-9a-zA-Z.-]/g, '_'); + + return "PROXY " + proxyHost + ":99"; +} diff --git a/net/data/proxy_resolver_v8_tracing_unittest/dns_during_init.js b/net/data/proxy_resolver_v8_tracing_unittest/dns_during_init.js new file mode 100644 index 0000000..55aef52 --- /dev/null +++ b/net/data/proxy_resolver_v8_tracing_unittest/dns_during_init.js @@ -0,0 +1,14 @@ +var g_ips = [ + dnsResolve('host1'), + dnsResolve('host2') +]; + +alert('Watsup'); +alert('Watsup2'); + +function FindProxyForURL(url, host) { + // Note that host1 and host2 should not resolve using the same cache as was + // used for g_ips! + var ips = g_ips.concat([dnsResolve('host1'), dnsResolve('host2')]); + return 'PROXY ' + ips.join('-') + ':99'; +} diff --git a/net/data/proxy_resolver_v8_tracing_unittest/error.js b/net/data/proxy_resolver_v8_tracing_unittest/error.js new file mode 100644 index 0000000..83e534c --- /dev/null +++ b/net/data/proxy_resolver_v8_tracing_unittest/error.js @@ -0,0 +1,8 @@ +function FindProxyForURL(url, host) { + if (host == 'throw-an-error') { + alert('Prepare to DIE!'); + var x = null; + return x.split('-'); // Throws exception. + } + return "PROXY i-approve-this-message:42"; +} diff --git a/net/data/proxy_resolver_v8_tracing_unittest/global_sideffects1.js b/net/data/proxy_resolver_v8_tracing_unittest/global_sideffects1.js new file mode 100644 index 0000000..251af9f --- /dev/null +++ b/net/data/proxy_resolver_v8_tracing_unittest/global_sideffects1.js @@ -0,0 +1,14 @@ +var g_iteration = 0; + +function FindProxyForURL(url, host) { + g_iteration++; + + var ips = [ + dnsResolve('host1'), + dnsResolve('crazy' + g_iteration) + ]; + + alert('iteration: ' + g_iteration); + + return 'PROXY ' + ips.join('-') + ':100'; +} diff --git a/net/data/proxy_resolver_v8_tracing_unittest/global_sideffects2.js b/net/data/proxy_resolver_v8_tracing_unittest/global_sideffects2.js new file mode 100644 index 0000000..f5e5076a --- /dev/null +++ b/net/data/proxy_resolver_v8_tracing_unittest/global_sideffects2.js @@ -0,0 +1,18 @@ +var g_iteration = 0; + +function FindProxyForURL(url, host) { + g_iteration++; + + var ips; + + if (g_iteration < 3) { + ips = [ + dnsResolve('host1'), + dnsResolve('host2') + ]; + } else { + ips = [ dnsResolve('host' + g_iteration) ]; + } + + return 'PROXY ' + ips.join('-') + ':100'; +} diff --git a/net/data/proxy_resolver_v8_tracing_unittest/global_sideffects3.js b/net/data/proxy_resolver_v8_tracing_unittest/global_sideffects3.js new file mode 100644 index 0000000..0421126 --- /dev/null +++ b/net/data/proxy_resolver_v8_tracing_unittest/global_sideffects3.js @@ -0,0 +1,13 @@ +var g_iteration = 0; + +function FindProxyForURL(url, host) { + g_iteration++; + + var results = []; + for (var i = 1; i <= g_iteration; ++i) { + results.push('' + dnsResolve('host' + i)); + } + + alert('iteration: ' + g_iteration); + return 'PROXY ' + results.join('-') + ':' + g_iteration; +} diff --git a/net/data/proxy_resolver_v8_tracing_unittest/global_sideffects4.js b/net/data/proxy_resolver_v8_tracing_unittest/global_sideffects4.js new file mode 100644 index 0000000..1bfbbfd --- /dev/null +++ b/net/data/proxy_resolver_v8_tracing_unittest/global_sideffects4.js @@ -0,0 +1,15 @@ +var g_iteration = 0; + +function FindProxyForURL(url, host) { + g_iteration++; + + for (var i = 0; i < g_iteration; ++i) { + myIpAddress(); + } + + var result = '' + dnsResolve('host' + g_iteration); + result += g_iteration; + + alert('iteration: ' + g_iteration); + return 'PROXY ' + result + ':34'; +} diff --git a/net/data/proxy_resolver_v8_tracing_unittest/simple.js b/net/data/proxy_resolver_v8_tracing_unittest/simple.js new file mode 100644 index 0000000..f742081 --- /dev/null +++ b/net/data/proxy_resolver_v8_tracing_unittest/simple.js @@ -0,0 +1,3 @@ +function FindProxyForURL(url, host) { + return "PROXY " + host + ":99"; +} diff --git a/net/data/proxy_resolver_v8_tracing_unittest/simple_dns.js b/net/data/proxy_resolver_v8_tracing_unittest/simple_dns.js new file mode 100644 index 0000000..24867b0 --- /dev/null +++ b/net/data/proxy_resolver_v8_tracing_unittest/simple_dns.js @@ -0,0 +1,8 @@ +var g_iteration = 0; + +function FindProxyForURL(url, host) { + g_iteration++; + myIpAddress(); + var ip = dnsResolve(host); + return "PROXY " + ip + ':' + g_iteration; +} diff --git a/net/data/proxy_resolver_v8_tracing_unittest/too_many_alerts.js b/net/data/proxy_resolver_v8_tracing_unittest/too_many_alerts.js new file mode 100644 index 0000000..564e30e --- /dev/null +++ b/net/data/proxy_resolver_v8_tracing_unittest/too_many_alerts.js @@ -0,0 +1,13 @@ +var g_iteration = 0; + +function FindProxyForURL(url, host) { + g_iteration++; + + dnsResolve(host); + + for (var i = 0; i < 50; i++) { + alert('Gee, all these alerts are silly!'); + } + + return "PROXY foo:" + g_iteration; +} diff --git a/net/data/proxy_resolver_v8_tracing_unittest/too_many_empty_alerts.js b/net/data/proxy_resolver_v8_tracing_unittest/too_many_empty_alerts.js new file mode 100644 index 0000000..6fe85d4 --- /dev/null +++ b/net/data/proxy_resolver_v8_tracing_unittest/too_many_empty_alerts.js @@ -0,0 +1,13 @@ +var g_iteration = 0; + +function FindProxyForURL(url, host) { + g_iteration++; + + dnsResolve(host); + + for (var i = 0; i < 1000; i++) { + alert(''); + } + + return "PROXY foo:" + g_iteration; +} diff --git a/net/http/http_network_transaction_spdy2_unittest.cc b/net/http/http_network_transaction_spdy2_unittest.cc index dd0683b..09742a4 100644 --- a/net/http/http_network_transaction_spdy2_unittest.cc +++ b/net/http/http_network_transaction_spdy2_unittest.cc @@ -8084,12 +8084,6 @@ class CapturingProxyResolver : public ProxyResolver { return LOAD_STATE_IDLE; } - virtual LoadState GetLoadStateThreadSafe( - RequestHandle request) const OVERRIDE { - NOTREACHED(); - return LOAD_STATE_IDLE; - } - virtual void CancelSetPacScript() { NOTREACHED(); } diff --git a/net/http/http_network_transaction_spdy3_unittest.cc b/net/http/http_network_transaction_spdy3_unittest.cc index 03a2c8e..ff54dad 100644 --- a/net/http/http_network_transaction_spdy3_unittest.cc +++ b/net/http/http_network_transaction_spdy3_unittest.cc @@ -8083,12 +8083,6 @@ class CapturingProxyResolver : public ProxyResolver { return LOAD_STATE_IDLE; } - virtual LoadState GetLoadStateThreadSafe( - RequestHandle request) const OVERRIDE { - NOTREACHED(); - return LOAD_STATE_IDLE; - } - virtual void CancelSetPacScript() { NOTREACHED(); } diff --git a/net/net.gyp b/net/net.gyp index 4c7d4f0..eeaeab5 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -633,11 +633,8 @@ 'proxy/proxy_list.h', 'proxy/proxy_resolver.h', 'proxy/proxy_resolver_error_observer.h', - 'proxy/proxy_resolver_js_bindings.cc', - 'proxy/proxy_resolver_js_bindings.h', 'proxy/proxy_resolver_mac.cc', 'proxy/proxy_resolver_mac.h', - 'proxy/proxy_resolver_request_context.h', 'proxy/proxy_resolver_script.h', 'proxy/proxy_resolver_script_data.cc', 'proxy/proxy_resolver_script_data.h', @@ -654,9 +651,6 @@ 'proxy/proxy_server_mac.cc', 'proxy/proxy_service.cc', 'proxy/proxy_service.h', - 'proxy/sync_host_resolver.h', - 'proxy/sync_host_resolver_bridge.cc', - 'proxy/sync_host_resolver_bridge.h', 'quic/congestion_control/cubic.cc', 'quic/congestion_control/cubic.h', 'quic/congestion_control/fix_rate_receiver.cc', @@ -1474,13 +1468,12 @@ 'proxy/proxy_config_unittest.cc', 'proxy/proxy_info_unittest.cc', 'proxy/proxy_list_unittest.cc', - 'proxy/proxy_resolver_js_bindings_unittest.cc', + 'proxy/proxy_resolver_v8_tracing_unittest.cc', 'proxy/proxy_resolver_v8_unittest.cc', 'proxy/proxy_script_decider_unittest.cc', 'proxy/proxy_script_fetcher_impl_unittest.cc', 'proxy/proxy_server_unittest.cc', 'proxy/proxy_service_unittest.cc', - 'proxy/sync_host_resolver_bridge_unittest.cc', 'quic/congestion_control/cubic_test.cc', 'quic/congestion_control/fix_rate_test.cc', 'quic/congestion_control/hybrid_slow_start_test.cc', @@ -1701,6 +1694,7 @@ }, { # else: !use_v8_in_net 'sources!': [ 'proxy/proxy_resolver_v8_unittest.cc', + 'proxy/proxy_resolver_v8_tracing_unittest.cc', ], }, ], @@ -2070,6 +2064,8 @@ 'sources': [ 'proxy/proxy_resolver_v8.cc', 'proxy/proxy_resolver_v8.h', + 'proxy/proxy_resolver_v8_tracing.cc', + 'proxy/proxy_resolver_v8_tracing.h', 'proxy/proxy_service_v8.cc', 'proxy/proxy_service_v8.h', ], diff --git a/net/net_unittests.isolate b/net/net_unittests.isolate index b3c8d7b..0fc30a2 100644 --- a/net/net_unittests.isolate +++ b/net/net_unittests.isolate @@ -228,6 +228,17 @@ }, { 'variables': { 'isolate_dependency_tracked': [ + 'data/proxy_resolver_v8_tracing_unittest/dns.js', + 'data/proxy_resolver_v8_tracing_unittest/dns_during_init.js', + 'data/proxy_resolver_v8_tracing_unittest/error.js', + 'data/proxy_resolver_v8_tracing_unittest/global_sideffects1.js', + 'data/proxy_resolver_v8_tracing_unittest/global_sideffects2.js', + 'data/proxy_resolver_v8_tracing_unittest/global_sideffects3.js', + 'data/proxy_resolver_v8_tracing_unittest/global_sideffects4.js', + 'data/proxy_resolver_v8_tracing_unittest/simple.js', + 'data/proxy_resolver_v8_tracing_unittest/simple_dns.js', + 'data/proxy_resolver_v8_tracing_unittest/too_many_alerts.js', + 'data/proxy_resolver_v8_tracing_unittest/too_many_empty_alerts.js', 'data/proxy_resolver_v8_unittest/binding_from_global.js', 'data/proxy_resolver_v8_unittest/bindings.js', 'data/proxy_resolver_v8_unittest/direct.js', diff --git a/net/proxy/mock_proxy_resolver.cc b/net/proxy/mock_proxy_resolver.cc index 0d40528..c50f7d4 100644 --- a/net/proxy/mock_proxy_resolver.cc +++ b/net/proxy/mock_proxy_resolver.cc @@ -78,12 +78,6 @@ LoadState MockAsyncProxyResolverBase::GetLoadState( return LOAD_STATE_RESOLVING_PROXY_FOR_URL; } -LoadState MockAsyncProxyResolverBase::GetLoadStateThreadSafe( - RequestHandle request_handle) const { - NOTREACHED(); - return LOAD_STATE_IDLE; -} - int MockAsyncProxyResolverBase::SetPacScript( const scoped_refptr<ProxyResolverScriptData>& script_data, const CompletionCallback& callback) { diff --git a/net/proxy/mock_proxy_resolver.h b/net/proxy/mock_proxy_resolver.h index e68e42b..8f78c91 100644 --- a/net/proxy/mock_proxy_resolver.h +++ b/net/proxy/mock_proxy_resolver.h @@ -76,8 +76,6 @@ class MockAsyncProxyResolverBase : public ProxyResolver { const BoundNetLog& /*net_log*/) OVERRIDE; virtual void CancelRequest(RequestHandle request_handle) OVERRIDE; virtual LoadState GetLoadState(RequestHandle request_handle) const OVERRIDE; - virtual LoadState GetLoadStateThreadSafe( - RequestHandle request_handle) const OVERRIDE; virtual int SetPacScript( const scoped_refptr<ProxyResolverScriptData>& script_data, const net::CompletionCallback& callback) OVERRIDE; diff --git a/net/proxy/multi_threaded_proxy_resolver.cc b/net/proxy/multi_threaded_proxy_resolver.cc index bd9890c..01dfe24 100644 --- a/net/proxy/multi_threaded_proxy_resolver.cc +++ b/net/proxy/multi_threaded_proxy_resolver.cc @@ -359,11 +359,6 @@ void MultiThreadedProxyResolver::Executor::OnJobCompleted(Job* job) { void MultiThreadedProxyResolver::Executor::Destroy() { DCHECK(coordinator_); - // Give the resolver an opportunity to shutdown from THIS THREAD before - // joining on the resolver thread. This allows certain implementations - // to avoid deadlocks. - resolver_->Shutdown(); - { // See http://crbug.com/69710. base::ThreadRestrictions::ScopedAllowIO allow_io; @@ -484,19 +479,9 @@ void MultiThreadedProxyResolver::CancelRequest(RequestHandle req) { LoadState MultiThreadedProxyResolver::GetLoadState(RequestHandle req) const { DCHECK(CalledOnValidThread()); DCHECK(req); - - Job* job = reinterpret_cast<Job*>(req); - if (job->executor()) - return job->executor()->resolver()->GetLoadStateThreadSafe(NULL); return LOAD_STATE_RESOLVING_PROXY_FOR_URL; } -LoadState MultiThreadedProxyResolver::GetLoadStateThreadSafe( - RequestHandle req) const { - NOTIMPLEMENTED(); - return LOAD_STATE_IDLE; -} - void MultiThreadedProxyResolver::CancelSetPacScript() { DCHECK(CalledOnValidThread()); DCHECK_EQ(0u, pending_jobs_.size()); diff --git a/net/proxy/multi_threaded_proxy_resolver.h b/net/proxy/multi_threaded_proxy_resolver.h index 82a06f5..3076c36 100644 --- a/net/proxy/multi_threaded_proxy_resolver.h +++ b/net/proxy/multi_threaded_proxy_resolver.h @@ -98,8 +98,6 @@ class NET_EXPORT_PRIVATE MultiThreadedProxyResolver const BoundNetLog& net_log) OVERRIDE; virtual void CancelRequest(RequestHandle request) OVERRIDE; virtual LoadState GetLoadState(RequestHandle request) const OVERRIDE; - virtual LoadState GetLoadStateThreadSafe( - RequestHandle request) const OVERRIDE; virtual void CancelSetPacScript() OVERRIDE; virtual void PurgeMemory() OVERRIDE; virtual int SetPacScript( diff --git a/net/proxy/multi_threaded_proxy_resolver_unittest.cc b/net/proxy/multi_threaded_proxy_resolver_unittest.cc index dbd2f5d..bb856bd 100644 --- a/net/proxy/multi_threaded_proxy_resolver_unittest.cc +++ b/net/proxy/multi_threaded_proxy_resolver_unittest.cc @@ -66,12 +66,6 @@ class MockProxyResolver : public ProxyResolver { return LOAD_STATE_IDLE; } - virtual LoadState GetLoadStateThreadSafe( - RequestHandle request) const OVERRIDE { - NOTREACHED(); - return LOAD_STATE_IDLE; - } - virtual void CancelSetPacScript() OVERRIDE { NOTREACHED(); } @@ -190,12 +184,6 @@ class ForwardingProxyResolver : public ProxyResolver { return LOAD_STATE_IDLE; } - virtual LoadState GetLoadStateThreadSafe( - RequestHandle request) const OVERRIDE { - NOTREACHED(); - return LOAD_STATE_IDLE; - } - virtual void CancelSetPacScript() OVERRIDE { impl_->CancelSetPacScript(); } diff --git a/net/proxy/proxy_resolver.h b/net/proxy/proxy_resolver.h index 6b63407..9d3387a 100644 --- a/net/proxy/proxy_resolver.h +++ b/net/proxy/proxy_resolver.h @@ -52,9 +52,6 @@ class NET_EXPORT_PRIVATE ProxyResolver { // Gets the LoadState for |request|. virtual LoadState GetLoadState(RequestHandle request) const = 0; - // Gets the LoadState for |request|. May be called from another thread. - virtual LoadState GetLoadStateThreadSafe(RequestHandle request) const = 0; - // The PAC script backend can be specified to the ProxyResolver either via // URL, or via the javascript text itself. If |expects_pac_bytes| is true, // then the ProxyResolverScriptData passed to SetPacScript() should @@ -75,10 +72,6 @@ class NET_EXPORT_PRIVATE ProxyResolver { const scoped_refptr<ProxyResolverScriptData>& pac_script, const net::CompletionCallback& callback) = 0; - // Optional shutdown code to be run before destruction. This is only used - // by the multithreaded runner to signal cleanup from origin thread - virtual void Shutdown() {} - private: const bool expects_pac_bytes_; diff --git a/net/proxy/proxy_resolver_error_observer.h b/net/proxy/proxy_resolver_error_observer.h index 9176c2f..6ee9cbb 100644 --- a/net/proxy/proxy_resolver_error_observer.h +++ b/net/proxy/proxy_resolver_error_observer.h @@ -11,10 +11,7 @@ namespace net { -// Interface for observing JavaScript error messages from PAC scripts. The -// default implementation of the ProxyResolverJSBindings takes a class -// implementing this interface and forwards all JavaScript errors related to -// PAC scripts. +// Interface for observing JavaScript error messages from PAC scripts. class NET_EXPORT_PRIVATE ProxyResolverErrorObserver { public: ProxyResolverErrorObserver() {} @@ -23,6 +20,11 @@ class NET_EXPORT_PRIVATE ProxyResolverErrorObserver { // Handler for when an error is encountered. |line_number| may be -1 // if a line number is not applicable to this error. |error| is a message // describing the error. + // + // Note on threading: This may get called from a worker thread. If the + // backing proxy resolver is ProxyResolverV8Tracing, then it will not + // be called concurrently, however it will be called from a different + // thread than the proxy resolver's origin thread. virtual void OnPACScriptError(int line_number, const string16& error) = 0; private: diff --git a/net/proxy/proxy_resolver_js_bindings.cc b/net/proxy/proxy_resolver_js_bindings.cc deleted file mode 100644 index 84dcafd..0000000 --- a/net/proxy/proxy_resolver_js_bindings.cc +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright (c) 2012 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 "net/proxy/proxy_resolver_js_bindings.h" - -#include "base/bind.h" -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "base/string_util.h" -#include "base/values.h" -#include "net/base/address_list.h" -#include "net/base/host_cache.h" -#include "net/base/host_resolver.h" -#include "net/base/net_errors.h" -#include "net/base/net_log.h" -#include "net/base/net_util.h" -#include "net/proxy/proxy_resolver_error_observer.h" -#include "net/proxy/proxy_resolver_request_context.h" -#include "net/proxy/sync_host_resolver.h" - -namespace net { - -namespace { - -// TTL for the per-request DNS cache. Applies to both successful and failed -// DNS resolutions. -const unsigned kCacheEntryTTLSeconds = 5 * 60; - -// Returns event parameters for a PAC error message (line number + message). -Value* NetLogErrorCallback(int line_number, - const string16* message, - NetLog::LogLevel /* log_level */) { - DictionaryValue* dict = new DictionaryValue(); - dict->SetInteger("line_number", line_number); - dict->SetString("message", *message); - return dict; -} - -// ProxyResolverJSBindings implementation. -class DefaultJSBindings : public ProxyResolverJSBindings { - public: - DefaultJSBindings(SyncHostResolver* host_resolver, - NetLog* net_log, - ProxyResolverErrorObserver* error_observer) - : host_resolver_(host_resolver), - net_log_(net_log), - error_observer_(error_observer) { - } - - // Handler for "alert(message)". - virtual void Alert(const string16& message) OVERRIDE { - VLOG(1) << "PAC-alert: " << message; - - // Send to the NetLog. - LogEventToCurrentRequestAndGlobally( - NetLog::TYPE_PAC_JAVASCRIPT_ALERT, - NetLog::StringCallback("message", &message)); - } - - // Handler for "myIpAddress()". - // TODO(eroman): Perhaps enumerate the interfaces directly, using - // getifaddrs(). - virtual bool MyIpAddress(std::string* first_ip_address) OVERRIDE { - LogEventToCurrentRequest(NetLog::PHASE_BEGIN, - NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS); - - bool ok = MyIpAddressImpl(first_ip_address); - - LogEventToCurrentRequest(NetLog::PHASE_END, - NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS); - return ok; - } - - // Handler for "myIpAddressEx()". - virtual bool MyIpAddressEx(std::string* ip_address_list) OVERRIDE { - LogEventToCurrentRequest(NetLog::PHASE_BEGIN, - NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS_EX); - - bool ok = MyIpAddressExImpl(ip_address_list); - - LogEventToCurrentRequest(NetLog::PHASE_END, - NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS_EX); - return ok; - } - - // Handler for "dnsResolve(host)". - virtual bool DnsResolve(const std::string& host, - std::string* first_ip_address) OVERRIDE { - LogEventToCurrentRequest(NetLog::PHASE_BEGIN, - NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE); - - bool ok = DnsResolveImpl(host, first_ip_address); - - LogEventToCurrentRequest(NetLog::PHASE_END, - NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE); - return ok; - } - - // Handler for "dnsResolveEx(host)". - virtual bool DnsResolveEx(const std::string& host, - std::string* ip_address_list) OVERRIDE { - LogEventToCurrentRequest(NetLog::PHASE_BEGIN, - NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE_EX); - - bool ok = DnsResolveExImpl(host, ip_address_list); - - LogEventToCurrentRequest(NetLog::PHASE_END, - NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE_EX); - return ok; - } - - // Handler for when an error is encountered. |line_number| may be -1. - virtual void OnError(int line_number, const string16& message) OVERRIDE { - // Send to the chrome log. - if (line_number == -1) - VLOG(1) << "PAC-error: " << message; - else - VLOG(1) << "PAC-error: " << "line: " << line_number << ": " << message; - - // Send the error to the NetLog. - LogEventToCurrentRequestAndGlobally( - NetLog::TYPE_PAC_JAVASCRIPT_ERROR, - base::Bind(&NetLogErrorCallback, line_number, &message)); - - if (error_observer_.get()) - error_observer_->OnPACScriptError(line_number, message); - } - - virtual void Shutdown() OVERRIDE { - host_resolver_->Shutdown(); - } - - private: - bool MyIpAddressImpl(std::string* first_ip_address) { - std::string my_hostname = GetHostName(); - if (my_hostname.empty()) - return false; - return DnsResolveImpl(my_hostname, first_ip_address); - } - - bool MyIpAddressExImpl(std::string* ip_address_list) { - std::string my_hostname = GetHostName(); - if (my_hostname.empty()) - return false; - return DnsResolveExImpl(my_hostname, ip_address_list); - } - - bool DnsResolveImpl(const std::string& host, - std::string* first_ip_address) { - // Do a sync resolve of the hostname (port doesn't matter). - // Disable IPv6 results. We do this because the PAC specification isn't - // really IPv6 friendly, and Internet Explorer also restricts to IPv4. - // Consequently a lot of existing PAC scripts assume they will only get - // IPv4 results, and will misbehave if they get an IPv6 result. - // See http://crbug.com/24641 for more details. - HostResolver::RequestInfo info(HostPortPair(host, 80)); - info.set_address_family(ADDRESS_FAMILY_IPV4); - AddressList address_list; - - int result = DnsResolveHelper(info, &address_list); - if (result != OK) - return false; - - // There may be multiple results; we will just use the first one. - // This returns empty string on failure. - *first_ip_address = address_list.front().ToStringWithoutPort(); - if (first_ip_address->empty()) - return false; - - return true; - } - - bool DnsResolveExImpl(const std::string& host, - std::string* ip_address_list) { - // Do a sync resolve of the hostname (port doesn't matter). - HostResolver::RequestInfo info(HostPortPair(host, 80)); - AddressList address_list; - int result = DnsResolveHelper(info, &address_list); - - if (result != OK) - return false; - - // Stringify all of the addresses in the address list, separated - // by semicolons. - std::string address_list_str; - for (AddressList::const_iterator iter = address_list.begin(); - iter != address_list.end(); ++iter) { - if (!address_list_str.empty()) - address_list_str += ";"; - const std::string address_string = iter->ToStringWithoutPort(); - if (address_string.empty()) - return false; - address_list_str += address_string; - } - - *ip_address_list = address_list_str; - return true; - } - - // Helper to execute a synchronous DNS resolve, using the per-request - // DNS cache if there is one. - int DnsResolveHelper(const HostResolver::RequestInfo& info, - AddressList* address_list) { - HostCache::Key cache_key(info.hostname(), - info.address_family(), - info.host_resolver_flags()); - - HostCache* host_cache = current_request_context() ? - current_request_context()->host_cache : NULL; - - // First try to service this request from the per-request DNS cache. - // (we cache DNS failures much more aggressively within the context - // of a FindProxyForURL() request). - if (host_cache) { - const HostCache::Entry* entry = - host_cache->Lookup(cache_key, base::TimeTicks::Now()); - if (entry) { - if (entry->error == OK) - *address_list = entry->addrlist; - return entry->error; - } - } - - // Otherwise ask the host resolver. - const BoundNetLog* net_log = GetNetLogForCurrentRequest(); - int result = host_resolver_->Resolve(info, - address_list, - net_log ? *net_log : BoundNetLog()); - - // Save the result back to the per-request DNS cache. - if (host_cache) { - host_cache->Set(cache_key, HostCache::Entry(result, *address_list), - base::TimeTicks::Now(), - base::TimeDelta::FromSeconds(kCacheEntryTTLSeconds)); - } - - return result; - } - - // May return NULL. - const BoundNetLog* GetNetLogForCurrentRequest() { - if (!current_request_context()) - return NULL; - return current_request_context()->net_log; - } - - void LogEventToCurrentRequest( - NetLog::EventPhase phase, - NetLog::EventType type) { - const BoundNetLog* net_log = GetNetLogForCurrentRequest(); - if (net_log) - net_log->AddEntry(type, phase); - } - - void LogEventToCurrentRequest( - NetLog::EventPhase phase, - NetLog::EventType type, - const NetLog::ParametersCallback& parameters_callback) { - const BoundNetLog* net_log = GetNetLogForCurrentRequest(); - if (net_log) - net_log->AddEntry(type, phase, parameters_callback); - } - - void LogEventToCurrentRequestAndGlobally( - NetLog::EventType type, - const NetLog::ParametersCallback& parameters_callback) { - LogEventToCurrentRequest(NetLog::PHASE_NONE, type, parameters_callback); - - // Emit to the global NetLog event stream. - if (net_log_) - net_log_->AddGlobalEntry(type, parameters_callback); - } - - scoped_ptr<SyncHostResolver> host_resolver_; - NetLog* net_log_; - scoped_ptr<ProxyResolverErrorObserver> error_observer_; - DISALLOW_COPY_AND_ASSIGN(DefaultJSBindings); -}; - -} // namespace - -// static -ProxyResolverJSBindings* ProxyResolverJSBindings::CreateDefault( - SyncHostResolver* host_resolver, - NetLog* net_log, - ProxyResolverErrorObserver* error_observer) { - return new DefaultJSBindings(host_resolver, net_log, error_observer); -} - -} // namespace net diff --git a/net/proxy/proxy_resolver_js_bindings.h b/net/proxy/proxy_resolver_js_bindings.h deleted file mode 100644 index f201707..0000000 --- a/net/proxy/proxy_resolver_js_bindings.h +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2011 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 NET_PROXY_PROXY_RESOLVER_JS_BINDINGS_H_ -#define NET_PROXY_PROXY_RESOLVER_JS_BINDINGS_H_ - -#include <string> - -#include "base/string16.h" -#include "net/base/net_export.h" - -namespace net { - -class HostResolver; -class NetLog; -class ProxyResolverErrorObserver; -struct ProxyResolverRequestContext; -class SyncHostResolver; - -// Interface for the javascript bindings. -class NET_EXPORT_PRIVATE ProxyResolverJSBindings { - public: - ProxyResolverJSBindings() : current_request_context_(NULL) {} - - virtual ~ProxyResolverJSBindings() {} - - // Handler for "alert(message)" - virtual void Alert(const string16& message) = 0; - - // Handler for "myIpAddress()". Returns true on success and fills - // |*first_ip_address| with the result. - virtual bool MyIpAddress(std::string* first_ip_address) = 0; - - // Handler for "myIpAddressEx()". Returns true on success and fills - // |*ip_address_list| with the result. - // - // This is a Microsoft extension to PAC for IPv6, see: - // http://blogs.msdn.com/b/wndp/archive/2006/07/13/ipv6-pac-extensions-v0-9.aspx - - virtual bool MyIpAddressEx(std::string* ip_address_list) = 0; - - // Handler for "dnsResolve(host)". Returns true on success and fills - // |*first_ip_address| with the result. - virtual bool DnsResolve(const std::string& host, - std::string* first_ip_address) = 0; - - // Handler for "dnsResolveEx(host)". Returns true on success and fills - // |*ip_address_list| with the result. - // - // This is a Microsoft extension to PAC for IPv6, see: - // http://blogs.msdn.com/b/wndp/archive/2006/07/13/ipv6-pac-extensions-v0-9.aspx - virtual bool DnsResolveEx(const std::string& host, - std::string* ip_address_list) = 0; - - // Handler for when an error is encountered. |line_number| may be -1 - // if a line number is not applicable to this error. - virtual void OnError(int line_number, const string16& error) = 0; - - // Called before the thread running the proxy resolver is stopped. - virtual void Shutdown() = 0; - - // Creates a default javascript bindings implementation that will: - // - Send script error messages to both VLOG(1) and the NetLog. - // - Send script alert()s to both VLOG(1) and the NetLog. - // - Use the provided host resolver to service dnsResolve(). - // - // Takes ownership of |host_resolver| and |error_observer| (the latter can - // be NULL). - static ProxyResolverJSBindings* CreateDefault( - SyncHostResolver* host_resolver, - NetLog* net_log, - ProxyResolverErrorObserver* error_observer); - - // Sets details about the currently executing FindProxyForURL() request. - void set_current_request_context( - ProxyResolverRequestContext* current_request_context) { - current_request_context_ = current_request_context; - } - - // Retrieves details about the currently executing FindProxyForURL() request. - ProxyResolverRequestContext* current_request_context() { - return current_request_context_; - } - - private: - ProxyResolverRequestContext* current_request_context_; -}; - -} // namespace net - -#endif // NET_PROXY_PROXY_RESOLVER_JS_BINDINGS_H_ diff --git a/net/proxy/proxy_resolver_js_bindings_unittest.cc b/net/proxy/proxy_resolver_js_bindings_unittest.cc deleted file mode 100644 index 97d69bf..0000000 --- a/net/proxy/proxy_resolver_js_bindings_unittest.cc +++ /dev/null @@ -1,365 +0,0 @@ -// Copyright (c) 2012 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 "net/proxy/proxy_resolver_js_bindings.h" - -#include "base/compiler_specific.h" -#include "base/memory/scoped_ptr.h" -#include "base/string_util.h" -#include "net/base/address_list.h" -#include "net/base/host_cache.h" -#include "net/base/mock_host_resolver.h" -#include "net/base/net_errors.h" -#include "net/base/net_log.h" -#include "net/base/net_log_unittest.h" -#include "net/base/net_util.h" -#include "net/base/test_completion_callback.h" -#include "net/proxy/proxy_resolver_request_context.h" -#include "net/proxy/sync_host_resolver.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace net { - -namespace { - -// This is a HostResolver that synchronously resolves all hosts to the -// following address list of length 3: -// 192.168.1.1 -// 172.22.34.1 -// 200.100.1.2 -class MockHostResolverWithMultipleResults : public SyncHostResolver { - public: - // HostResolver methods: - virtual int Resolve(const HostResolver::RequestInfo& info, - AddressList* addresses, - const BoundNetLog& bound_net_log) OVERRIDE { - return ParseAddressList("192.168.1.1,172.22.34.1,200.100.1.2", "", - addresses); - } - - virtual void Shutdown() OVERRIDE {} - - private: - virtual ~MockHostResolverWithMultipleResults() {} -}; - -class MockFailingHostResolver : public SyncHostResolver { - public: - MockFailingHostResolver() : count_(0) {} - - // HostResolver methods: - virtual int Resolve(const HostResolver::RequestInfo& info, - AddressList* addresses, - const BoundNetLog& bound_net_log) OVERRIDE { - count_++; - return ERR_NAME_NOT_RESOLVED; - } - - virtual void Shutdown() OVERRIDE {} - - // Returns the number of times Resolve() has been called. - int count() const { return count_; } - void ResetCount() { count_ = 0; } - - private: - int count_; -}; - -class MockSyncHostResolver : public SyncHostResolver { - public: - MockSyncHostResolver() { - resolver_.set_synchronous_mode(true); - } - - virtual int Resolve(const HostResolver::RequestInfo& info, - AddressList* addresses, - const BoundNetLog& bound_net_log) OVERRIDE { - return resolver_.Resolve(info, addresses, CompletionCallback(), NULL, - bound_net_log); - } - - virtual void Shutdown() OVERRIDE {} - - RuleBasedHostResolverProc* rules() { - return resolver_.rules(); - } - - private: - MockHostResolver resolver_; -}; - -TEST(ProxyResolverJSBindingsTest, DnsResolve) { - MockSyncHostResolver* host_resolver = new MockSyncHostResolver; - - // Get a hold of a DefaultJSBindings* (it is a hidden impl class). - scoped_ptr<ProxyResolverJSBindings> bindings( - ProxyResolverJSBindings::CreateDefault(host_resolver, NULL, NULL)); - - std::string ip_address; - - // Empty string is not considered a valid host (even though on some systems - // requesting this will resolve to localhost). - host_resolver->rules()->AddSimulatedFailure(""); - EXPECT_FALSE(bindings->DnsResolve("", &ip_address)); - - // Should call through to the HostResolver. - host_resolver->rules()->AddRule("google.com", "192.168.1.1"); - EXPECT_TRUE(bindings->DnsResolve("google.com", &ip_address)); - EXPECT_EQ("192.168.1.1", ip_address); - - // Resolve failures should give empty string. - host_resolver->rules()->AddSimulatedFailure("fail"); - EXPECT_FALSE(bindings->DnsResolve("fail", &ip_address)); - - // TODO(eroman): would be nice to have an IPV6 test here too, but that - // won't work on all systems. -} - -TEST(ProxyResolverJSBindingsTest, MyIpAddress) { - MockSyncHostResolver* host_resolver = new MockSyncHostResolver; - - // Get a hold of a DefaultJSBindings* (it is a hidden impl class). - scoped_ptr<ProxyResolverJSBindings> bindings( - ProxyResolverJSBindings::CreateDefault(host_resolver, NULL, NULL)); - - // Our IP address is always going to be 127.0.0.1, since we are using a - // mock host resolver. - std::string my_ip_address; - EXPECT_TRUE(bindings->MyIpAddress(&my_ip_address)); - - EXPECT_EQ("127.0.0.1", my_ip_address); -} - -// Tests that the regular PAC functions restrict results to IPv4, -// but that the Microsoft extensions to PAC do not. We test this -// by seeing whether ADDRESS_FAMILY_IPV4 or ADDRESS_FAMILY_UNSPECIFIED -// was passed into to the host resolver. -// -// Restricted to IPv4 address family: -// myIpAddress() -// dnsResolve() -// -// Unrestricted address family: -// myIpAddressEx() -// dnsResolveEx() -TEST(ProxyResolverJSBindingsTest, RestrictAddressFamily) { - MockSyncHostResolver* host_resolver = new MockSyncHostResolver; - - // Get a hold of a DefaultJSBindings* (it is a hidden impl class). - scoped_ptr<ProxyResolverJSBindings> bindings( - ProxyResolverJSBindings::CreateDefault(host_resolver, NULL, NULL)); - - // Make it so requests resolve to particular address patterns based on family: - // IPV4_ONLY --> 192.168.1.* - // UNSPECIFIED --> 192.168.2.1 - host_resolver->rules()->AddRuleForAddressFamily( - "foo", ADDRESS_FAMILY_IPV4, "192.168.1.1"); - host_resolver->rules()->AddRuleForAddressFamily( - "*", ADDRESS_FAMILY_IPV4, "192.168.1.2"); - host_resolver->rules()->AddRuleForAddressFamily( - "foo", ADDRESS_FAMILY_UNSPECIFIED, "192.168.2.1"); - host_resolver->rules()->AddRuleForAddressFamily( - "*", ADDRESS_FAMILY_UNSPECIFIED, "192.168.2.2"); - - // Verify that our mock setups works as expected, and we get different results - // depending if the address family was IPV4_ONLY or not. - HostResolver::RequestInfo info(HostPortPair("foo", 80)); - AddressList address_list; - EXPECT_EQ(OK, host_resolver->Resolve(info, &address_list, BoundNetLog())); - ASSERT_FALSE(address_list.empty()); - EXPECT_EQ("192.168.2.1", address_list.front().ToStringWithoutPort()); - - info.set_address_family(ADDRESS_FAMILY_IPV4); - EXPECT_EQ(OK, host_resolver->Resolve(info, &address_list, BoundNetLog())); - ASSERT_FALSE(address_list.empty()); - EXPECT_EQ("192.168.1.1", address_list.front().ToStringWithoutPort()); - - std::string ip_address; - // Now the actual test. - EXPECT_TRUE(bindings->MyIpAddress(&ip_address)); - EXPECT_EQ("192.168.1.2", ip_address); // IPv4 restricted. - - EXPECT_TRUE(bindings->DnsResolve("foo", &ip_address)); - EXPECT_EQ("192.168.1.1", ip_address); // IPv4 restricted. - - EXPECT_TRUE(bindings->DnsResolve("foo2", &ip_address)); - EXPECT_EQ("192.168.1.2", ip_address); // IPv4 restricted. - - EXPECT_TRUE(bindings->MyIpAddressEx(&ip_address)); - EXPECT_EQ("192.168.2.2", ip_address); // Unrestricted. - - EXPECT_TRUE(bindings->DnsResolveEx("foo", &ip_address)); - EXPECT_EQ("192.168.2.1", ip_address); // Unrestricted. - - EXPECT_TRUE(bindings->DnsResolveEx("foo2", &ip_address)); - EXPECT_EQ("192.168.2.2", ip_address); // Unrestricted. -} - -// Test that myIpAddressEx() and dnsResolveEx() both return a semi-colon -// separated list of addresses (as opposed to the non-Ex versions which -// just return the first result). -TEST(ProxyResolverJSBindingsTest, ExFunctionsReturnList) { - SyncHostResolver* host_resolver = - new MockHostResolverWithMultipleResults; - - // Get a hold of a DefaultJSBindings* (it is a hidden impl class). - scoped_ptr<ProxyResolverJSBindings> bindings( - ProxyResolverJSBindings::CreateDefault(host_resolver, NULL, NULL)); - - std::string ip_addresses; - - EXPECT_TRUE(bindings->MyIpAddressEx(&ip_addresses)); - EXPECT_EQ("192.168.1.1;172.22.34.1;200.100.1.2", ip_addresses); - - EXPECT_TRUE(bindings->DnsResolveEx("FOO", &ip_addresses)); - EXPECT_EQ("192.168.1.1;172.22.34.1;200.100.1.2", ip_addresses); -} - -TEST(ProxyResolverJSBindingsTest, PerRequestDNSCache) { - MockFailingHostResolver* host_resolver = new MockFailingHostResolver; - - // Get a hold of a DefaultJSBindings* (it is a hidden impl class). - scoped_ptr<ProxyResolverJSBindings> bindings( - ProxyResolverJSBindings::CreateDefault(host_resolver, NULL, NULL)); - - std::string ip_address; - - // Call DnsResolve() 4 times for the same hostname -- this should issue - // 4 separate calls to the underlying host resolver, since there is no - // current request context. - EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address)); - EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address)); - EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address)); - EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address)); - EXPECT_EQ(4, host_resolver->count()); - - host_resolver->ResetCount(); - - // Now setup a per-request context, and try the same experiment -- we - // expect the underlying host resolver to receive only 1 request this time, - // since it will service the others from the per-request DNS cache. - const unsigned kMaxCacheEntries = 50; - HostCache cache(kMaxCacheEntries); - ProxyResolverRequestContext context(NULL, &cache); - bindings->set_current_request_context(&context); - - EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address)); - EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address)); - EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address)); - EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address)); - EXPECT_EQ(1, host_resolver->count()); - - host_resolver->ResetCount(); - - // The "Ex" version shares this same cache, however since the flags - // are different it won't reuse this particular entry. - EXPECT_FALSE(bindings->DnsResolveEx("foo", &ip_address)); - EXPECT_EQ(1, host_resolver->count()); - EXPECT_FALSE(bindings->DnsResolveEx("foo", &ip_address)); - EXPECT_FALSE(bindings->DnsResolveEx("foo", &ip_address)); - EXPECT_EQ(1, host_resolver->count()); - - bindings->set_current_request_context(NULL); -} - -// Test that when a binding is called, it logs to the per-request NetLog. -TEST(ProxyResolverJSBindingsTest, NetLog) { - MockFailingHostResolver* host_resolver = new MockFailingHostResolver; - - CapturingNetLog global_log; - - // Get a hold of a DefaultJSBindings* (it is a hidden impl class). - scoped_ptr<ProxyResolverJSBindings> bindings( - ProxyResolverJSBindings::CreateDefault( - host_resolver, &global_log, NULL)); - - // Attach a capturing NetLog as the current request's log stream. - CapturingNetLog log; - BoundNetLog bound_log(BoundNetLog::Make(&log, NetLog::SOURCE_NONE)); - ProxyResolverRequestContext context(&bound_log, NULL); - bindings->set_current_request_context(&context); - - std::string ip_address; - net::CapturingNetLog::CapturedEntryList entries; - log.GetEntries(&entries); - ASSERT_EQ(0u, entries.size()); - - // Call all the bindings. Each call should be logging something to - // our NetLog. - - bindings->MyIpAddress(&ip_address); - - log.GetEntries(&entries); - EXPECT_EQ(2u, entries.size()); - EXPECT_TRUE(LogContainsBeginEvent( - entries, 0, NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS)); - EXPECT_TRUE(LogContainsEndEvent( - entries, 1, NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS)); - - bindings->MyIpAddressEx(&ip_address); - - log.GetEntries(&entries); - EXPECT_EQ(4u, entries.size()); - EXPECT_TRUE(LogContainsBeginEvent( - entries, 2, NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS_EX)); - EXPECT_TRUE(LogContainsEndEvent( - entries, 3, NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS_EX)); - - bindings->DnsResolve("foo", &ip_address); - - log.GetEntries(&entries); - EXPECT_EQ(6u, entries.size()); - EXPECT_TRUE(LogContainsBeginEvent( - entries, 4, NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE)); - EXPECT_TRUE(LogContainsEndEvent( - entries, 5, NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE)); - - bindings->DnsResolveEx("foo", &ip_address); - - log.GetEntries(&entries); - EXPECT_EQ(8u, entries.size()); - EXPECT_TRUE(LogContainsBeginEvent( - entries, 6, NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE_EX)); - EXPECT_TRUE(LogContainsEndEvent( - entries, 7, NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE_EX)); - - // Nothing has been emitted globally yet. - net::CapturingNetLog::CapturedEntryList global_log_entries; - global_log.GetEntries(&global_log_entries); - EXPECT_EQ(0u, global_log_entries.size()); - - bindings->OnError(30, string16()); - - log.GetEntries(&entries); - EXPECT_EQ(9u, entries.size()); - EXPECT_TRUE(LogContainsEvent( - entries, 8, NetLog::TYPE_PAC_JAVASCRIPT_ERROR, - NetLog::PHASE_NONE)); - - // We also emit errors to the top-level log stream. - global_log.GetEntries(&global_log_entries); - EXPECT_EQ(1u, global_log_entries.size()); - EXPECT_TRUE(LogContainsEvent( - global_log_entries, 0, NetLog::TYPE_PAC_JAVASCRIPT_ERROR, - NetLog::PHASE_NONE)); - - bindings->Alert(string16()); - - log.GetEntries(&entries); - EXPECT_EQ(10u, entries.size()); - EXPECT_TRUE(LogContainsEvent( - entries, 9, NetLog::TYPE_PAC_JAVASCRIPT_ALERT, - NetLog::PHASE_NONE)); - - // We also emit javascript alerts to the top-level log stream. - global_log.GetEntries(&global_log_entries); - EXPECT_EQ(2u, global_log_entries.size()); - EXPECT_TRUE(LogContainsEvent( - global_log_entries, 1, NetLog::TYPE_PAC_JAVASCRIPT_ALERT, - NetLog::PHASE_NONE)); -} - -} // namespace - -} // namespace net diff --git a/net/proxy/proxy_resolver_mac.cc b/net/proxy/proxy_resolver_mac.cc index a5a05af..d452ef8 100644 --- a/net/proxy/proxy_resolver_mac.cc +++ b/net/proxy/proxy_resolver_mac.cc @@ -196,11 +196,6 @@ LoadState ProxyResolverMac::GetLoadState(RequestHandle request) const { return LOAD_STATE_IDLE; } -LoadState ProxyResolverMac::GetLoadStateThreadSafe( - RequestHandle request) const { - return LOAD_STATE_IDLE; -} - void ProxyResolverMac::CancelSetPacScript() { NOTREACHED(); } diff --git a/net/proxy/proxy_resolver_mac.h b/net/proxy/proxy_resolver_mac.h index 2aafce2..d791a7d 100644 --- a/net/proxy/proxy_resolver_mac.h +++ b/net/proxy/proxy_resolver_mac.h @@ -31,9 +31,6 @@ class NET_EXPORT ProxyResolverMac : public ProxyResolver { virtual LoadState GetLoadState(RequestHandle request) const OVERRIDE; - virtual LoadState GetLoadStateThreadSafe( - RequestHandle request) const OVERRIDE; - virtual void CancelSetPacScript() OVERRIDE; virtual int SetPacScript( diff --git a/net/proxy/proxy_resolver_perftest.cc b/net/proxy/proxy_resolver_perftest.cc index f5994ca..4eae4c6 100644 --- a/net/proxy/proxy_resolver_perftest.cc +++ b/net/proxy/proxy_resolver_perftest.cc @@ -11,9 +11,7 @@ #include "net/base/mock_host_resolver.h" #include "net/base/net_errors.h" #include "net/proxy/proxy_info.h" -#include "net/proxy/proxy_resolver_js_bindings.h" #include "net/proxy/proxy_resolver_v8.h" -#include "net/proxy/sync_host_resolver.h" #include "net/test/test_server.h" #include "testing/gtest/include/gtest/gtest.h" @@ -23,17 +21,6 @@ #include "net/proxy/proxy_resolver_mac.h" #endif -class MockSyncHostResolver : public net::SyncHostResolver { - public: - virtual int Resolve(const net::HostResolver::RequestInfo& info, - net::AddressList* addresses, - const net::BoundNetLog& net_log) OVERRIDE { - return net::ERR_NAME_NOT_RESOLVED; - } - - virtual void Shutdown() OVERRIDE {} -}; - // This class holds the URL to use for resolving, and the expected result. // We track the expected result in order to make sure the performance // test is actually resolving URLs properly, otherwise the perf numbers @@ -207,12 +194,30 @@ TEST(ProxyResolverPerfTest, ProxyResolverMac) { } #endif -TEST(ProxyResolverPerfTest, ProxyResolverV8) { - net::ProxyResolverJSBindings* js_bindings = - net::ProxyResolverJSBindings::CreateDefault( - new MockSyncHostResolver, NULL, NULL); +class MockJSBindings : public net::ProxyResolverV8::JSBindings { + public: + MockJSBindings() {} - net::ProxyResolverV8 resolver(js_bindings); + virtual void Alert(const string16& message) OVERRIDE { + CHECK(false); + } + + virtual bool ResolveDns(const std::string& host, + ResolveDnsOperation op, + std::string* output) OVERRIDE { + CHECK(false); + return false; + } + + virtual void OnError(int line_number, const string16& message) OVERRIDE { + CHECK(false); + } +}; + +TEST(ProxyResolverPerfTest, ProxyResolverV8) { + MockJSBindings js_bindings; + net::ProxyResolverV8 resolver; + resolver.set_js_bindings(&js_bindings); PacPerfSuiteRunner runner(&resolver, "ProxyResolverV8"); runner.RunAllTests(); } diff --git a/net/proxy/proxy_resolver_request_context.h b/net/proxy/proxy_resolver_request_context.h deleted file mode 100644 index fdcced1..0000000 --- a/net/proxy/proxy_resolver_request_context.h +++ /dev/null @@ -1,31 +0,0 @@ -// 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 NET_PROXY_PROXY_RESOLVER_REQUEST_CONTEXT_H_ -#define NET_PROXY_PROXY_RESOLVER_REQUEST_CONTEXT_H_ - -namespace net { - -class HostCache; -class BoundNetLog; - -// This data structure holds state related to an invocation of -// "FindProxyForURL()". It is used to associate per-request -// data that can be retrieved by the bindings. -struct ProxyResolverRequestContext { - // All of these pointers are expected to remain valid for duration of - // this instance's lifetime. - ProxyResolverRequestContext(const BoundNetLog* net_log, - HostCache* host_cache) - : net_log(net_log), - host_cache(host_cache) { - } - - const BoundNetLog* net_log; - HostCache* host_cache; -}; - -} // namespace net - -#endif // NET_PROXY_PROXY_RESOLVER_REQUEST_CONTEXT_H_ diff --git a/net/proxy/proxy_resolver_v8.cc b/net/proxy/proxy_resolver_v8.cc index 6956dae..8f71af9 100644 --- a/net/proxy/proxy_resolver_v8.cc +++ b/net/proxy/proxy_resolver_v8.cc @@ -18,13 +18,10 @@ #include "base/utf_string_conversions.h" #include "googleurl/src/gurl.h" #include "googleurl/src/url_canon.h" -#include "net/base/host_cache.h" #include "net/base/net_errors.h" #include "net/base/net_log.h" #include "net/base/net_util.h" #include "net/proxy/proxy_info.h" -#include "net/proxy/proxy_resolver_js_bindings.h" -#include "net/proxy/proxy_resolver_request_context.h" #include "net/proxy/proxy_resolver_script.h" #include "v8/include/v8.h" @@ -337,10 +334,8 @@ bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) { class ProxyResolverV8::Context { public: - explicit Context(ProxyResolverJSBindings* js_bindings) - : is_resolving_host_(false), - js_bindings_(js_bindings) { - DCHECK(js_bindings != NULL); + explicit Context(ProxyResolverV8* parent) + : parent_(parent) { } ~Context() { @@ -356,6 +351,10 @@ class ProxyResolverV8::Context { PurgeMemory(); } + JSBindings* js_bindings() { + return parent_->js_bindings_; + } + int ResolveProxy(const GURL& query_url, ProxyInfo* results) { v8::Locker locked; v8::HandleScope scope; @@ -364,7 +363,7 @@ class ProxyResolverV8::Context { v8::Local<v8::Value> function; if (!GetFindProxyForURL(&function)) { - js_bindings_->OnError( + js_bindings()->OnError( -1, ASCIIToUTF16("FindProxyForURL() is undefined.")); return ERR_PAC_SCRIPT_FAILED; } @@ -384,7 +383,7 @@ class ProxyResolverV8::Context { } if (!ret->IsString()) { - js_bindings_->OnError( + js_bindings()->OnError( -1, ASCIIToUTF16("FindProxyForURL() did not return a string.")); return ERR_PAC_SCRIPT_FAILED; } @@ -399,7 +398,7 @@ class ProxyResolverV8::Context { string16 error_message = ASCIIToUTF16("FindProxyForURL() returned a non-ASCII string " "(crbug.com/47234): ") + ret_str; - js_bindings_->OnError(-1, error_message); + js_bindings()->OnError(-1, error_message); return ERR_PAC_SCRIPT_FAILED; } @@ -477,7 +476,7 @@ class ProxyResolverV8::Context { // to be a legitimiate PAC script. v8::Local<v8::Value> function; if (!GetFindProxyForURL(&function)) { - js_bindings_->OnError( + js_bindings()->OnError( -1, ASCIIToUTF16("FindProxyForURL() is undefined.")); return ERR_PAC_SCRIPT_FAILED; } @@ -485,49 +484,12 @@ class ProxyResolverV8::Context { return OK; } - void SetCurrentRequestContext(ProxyResolverRequestContext* context) { - js_bindings_->set_current_request_context(context); - } - void PurgeMemory() { v8::Locker locked; v8::V8::LowMemoryNotification(); } - bool is_resolving_host() const { - base::AutoLock auto_lock(lock_); - return is_resolving_host_; - } - private: - class ScopedHostResolve { - public: - explicit ScopedHostResolve(Context* context) - : context_(context) { - context_->BeginHostResolve(); - } - - ~ScopedHostResolve() { - context_->EndHostResolve(); - } - - private: - Context* const context_; - DISALLOW_COPY_AND_ASSIGN(ScopedHostResolve); - }; - - void BeginHostResolve() { - base::AutoLock auto_lock(lock_); - DCHECK(!is_resolving_host_); - is_resolving_host_ = true; - } - - void EndHostResolve() { - base::AutoLock auto_lock(lock_); - DCHECK(is_resolving_host_); - is_resolving_host_ = false; - } - bool GetFindProxyForURL(v8::Local<v8::Value>* function) { *function = v8_context_->Global()->Get( ASCIILiteralToV8String("FindProxyForURL")); @@ -543,7 +505,7 @@ class ProxyResolverV8::Context { int line_number = message->GetLineNumber(); string16 error_message; V8ObjectToUTF16String(message->Get(), &error_message); - js_bindings_->OnError(line_number, error_message); + js_bindings()->OnError(line_number, error_message); } // Compiles and runs |script| in the current V8 context. @@ -584,7 +546,7 @@ class ProxyResolverV8::Context { return v8::Undefined(); // toString() threw an exception. } - context->js_bindings_->Alert(message); + context->js_bindings()->Alert(message); return v8::Undefined(); } @@ -598,11 +560,10 @@ class ProxyResolverV8::Context { { v8::Unlocker unlocker(args.GetIsolate()); - ScopedHostResolve scoped_host_resolve(context); - // We shouldn't be called with any arguments, but will not complain if // we are. - success = context->js_bindings_->MyIpAddress(&result); + success = context->js_bindings()->ResolveDns( + "", JSBindings::MY_IP_ADDRESS, &result); } if (!success) @@ -621,11 +582,10 @@ class ProxyResolverV8::Context { { v8::Unlocker unlocker(args.GetIsolate()); - ScopedHostResolve scoped_host_resolve(context); - // We shouldn't be called with any arguments, but will not complain if // we are. - success = context->js_bindings_->MyIpAddressEx(&ip_address_list); + success = context->js_bindings()->ResolveDns( + "", JSBindings::MY_IP_ADDRESS_EX, &ip_address_list); } if (!success) @@ -648,8 +608,8 @@ class ProxyResolverV8::Context { { v8::Unlocker unlocker(args.GetIsolate()); - ScopedHostResolve scoped_host_resolve(context); - success = context->js_bindings_->DnsResolve(hostname, &ip_address); + success = context->js_bindings()->ResolveDns( + hostname, JSBindings::DNS_RESOLVE, &ip_address); } return success ? ASCIIStringToV8String(ip_address) : v8::Null(); @@ -670,8 +630,8 @@ class ProxyResolverV8::Context { { v8::Unlocker unlocker(args.GetIsolate()); - ScopedHostResolve scoped_host_resolve(context); - success = context->js_bindings_->DnsResolveEx(hostname, &ip_address_list); + success = context->js_bindings()->ResolveDns( + hostname, JSBindings::DNS_RESOLVE_EX, &ip_address_list); } if (!success) @@ -714,18 +674,16 @@ class ProxyResolverV8::Context { } mutable base::Lock lock_; - bool is_resolving_host_; - ProxyResolverJSBindings* js_bindings_; + ProxyResolverV8* parent_; v8::Persistent<v8::External> v8_this_; v8::Persistent<v8::Context> v8_context_; }; // ProxyResolverV8 ------------------------------------------------------------ -ProxyResolverV8::ProxyResolverV8( - ProxyResolverJSBindings* custom_js_bindings) +ProxyResolverV8::ProxyResolverV8() : ProxyResolver(true /*expects_pac_bytes*/), - js_bindings_(custom_js_bindings) { + js_bindings_(NULL) { } ProxyResolverV8::~ProxyResolverV8() {} @@ -735,26 +693,15 @@ int ProxyResolverV8::GetProxyForURL( const CompletionCallback& /*callback*/, RequestHandle* /*request*/, const BoundNetLog& net_log) { + DCHECK(js_bindings_); + // If the V8 instance has not been initialized (either because // SetPacScript() wasn't called yet, or because it failed. - if (!context_.get()) + if (!context_) return ERR_FAILED; - // Associate some short-lived context with this request. This context will be - // available to any of the javascript "bindings" that are subsequently invoked - // from the javascript. - // - // In particular, we create a HostCache to aggressively cache failed DNS - // resolves. - const unsigned kMaxCacheEntries = 50; - HostCache host_cache(kMaxCacheEntries); - - ProxyResolverRequestContext request_context(&net_log, &host_cache); - // Otherwise call into V8. - context_->SetCurrentRequestContext(&request_context); int rv = context_->ResolveProxy(query_url, results); - context_->SetCurrentRequestContext(NULL); return rv; } @@ -769,12 +716,6 @@ LoadState ProxyResolverV8::GetLoadState(RequestHandle request) const { return LOAD_STATE_IDLE; } -LoadState ProxyResolverV8::GetLoadStateThreadSafe(RequestHandle request) const { - if (context_->is_resolving_host()) - return LOAD_STATE_RESOLVING_HOST_IN_PROXY_SCRIPT; - return LOAD_STATE_RESOLVING_PROXY_FOR_URL; -} - void ProxyResolverV8::CancelSetPacScript() { NOTREACHED(); } @@ -783,20 +724,18 @@ void ProxyResolverV8::PurgeMemory() { context_->PurgeMemory(); } -void ProxyResolverV8::Shutdown() { - js_bindings_->Shutdown(); -} - int ProxyResolverV8::SetPacScript( const scoped_refptr<ProxyResolverScriptData>& script_data, const CompletionCallback& /*callback*/) { - DCHECK(script_data.get()); + DCHECK(script_data); + DCHECK(js_bindings_); + context_.reset(); if (script_data->utf16().empty()) return ERR_PAC_SCRIPT_FAILED; // Try parsing the PAC script. - scoped_ptr<Context> context(new Context(js_bindings_.get())); + scoped_ptr<Context> context(new Context(this)); int rv = context->InitV8(script_data); if (rv == OK) context_.reset(context.release()); diff --git a/net/proxy/proxy_resolver_v8.h b/net/proxy/proxy_resolver_v8.h index c00bb8a..a310460 100644 --- a/net/proxy/proxy_resolver_v8.h +++ b/net/proxy/proxy_resolver_v8.h @@ -12,8 +12,6 @@ namespace net { -class ProxyResolverJSBindings; - // Implementation of ProxyResolver that uses V8 to evaluate PAC scripts. // // ---------------------------------------------------------------------------- @@ -34,14 +32,43 @@ class ProxyResolverJSBindings; // and does not use locking since it expects to be alone. class NET_EXPORT_PRIVATE ProxyResolverV8 : public ProxyResolver { public: - // Constructs a ProxyResolverV8 with custom bindings. ProxyResolverV8 takes - // ownership of |custom_js_bindings| and deletes it when ProxyResolverV8 - // is destroyed. - explicit ProxyResolverV8(ProxyResolverJSBindings* custom_js_bindings); + // Interface for the javascript bindings. + class NET_EXPORT_PRIVATE JSBindings { + public: + enum ResolveDnsOperation { + DNS_RESOLVE, + DNS_RESOLVE_EX, + MY_IP_ADDRESS, + MY_IP_ADDRESS_EX, + NUM_DNS_OPERATIONS, + }; + + JSBindings() {} + + virtual ~JSBindings() {} + + // Handler for "dnsResolve()", "dnsResolveEx()", "myIpAddress()", + // "myIpAddressEx()". Returns true on success and fills |*output| with the + // result. + virtual bool ResolveDns(const std::string& host, + ResolveDnsOperation op, + std::string* output) = 0; + + // Handler for "alert(message)" + virtual void Alert(const string16& message) = 0; + + // Handler for when an error is encountered. |line_number| may be -1 + // if a line number is not applicable to this error. + virtual void OnError(int line_number, const string16& error) = 0; + }; + + // Constructs a ProxyResolverV8. + ProxyResolverV8(); virtual ~ProxyResolverV8(); - ProxyResolverJSBindings* js_bindings() const { return js_bindings_.get(); } + JSBindings* js_bindings() const { return js_bindings_; } + void set_js_bindings(JSBindings* js_bindings) { js_bindings_ = js_bindings; } // ProxyResolver implementation: virtual int GetProxyForURL(const GURL& url, @@ -51,11 +78,8 @@ class NET_EXPORT_PRIVATE ProxyResolverV8 : public ProxyResolver { const BoundNetLog& net_log) OVERRIDE; virtual void CancelRequest(RequestHandle request) OVERRIDE; virtual LoadState GetLoadState(RequestHandle request) const OVERRIDE; - virtual LoadState GetLoadStateThreadSafe( - RequestHandle request) const OVERRIDE; virtual void CancelSetPacScript() OVERRIDE; virtual void PurgeMemory() OVERRIDE; - virtual void Shutdown() OVERRIDE; virtual int SetPacScript( const scoped_refptr<ProxyResolverScriptData>& script_data, const net::CompletionCallback& /*callback*/) OVERRIDE; @@ -65,9 +89,10 @@ class NET_EXPORT_PRIVATE ProxyResolverV8 : public ProxyResolver { // script. It corresponds with the data from the last call to // SetPacScript(). class Context; + scoped_ptr<Context> context_; - scoped_ptr<ProxyResolverJSBindings> js_bindings_; + JSBindings* js_bindings_; DISALLOW_COPY_AND_ASSIGN(ProxyResolverV8); }; diff --git a/net/proxy/proxy_resolver_v8_tracing.cc b/net/proxy/proxy_resolver_v8_tracing.cc new file mode 100644 index 0000000..ea32577 --- /dev/null +++ b/net/proxy/proxy_resolver_v8_tracing.cc @@ -0,0 +1,984 @@ +// Copyright (c) 2013 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 "net/proxy/proxy_resolver_v8_tracing.h" + +#include "base/bind.h" +#include "base/message_loop_proxy.h" +#include "base/stringprintf.h" +#include "base/synchronization/cancellation_flag.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "base/threading/thread_restrictions.h" +#include "base/values.h" +#include "net/base/address_list.h" +#include "net/base/host_resolver.h" +#include "net/base/net_errors.h" +#include "net/base/net_log.h" +#include "net/proxy/proxy_info.h" +#include "net/proxy/proxy_resolver_error_observer.h" +#include "net/proxy/proxy_resolver_v8.h" + +// The intent of this class is explained in the design document: +// https://docs.google.com/a/chromium.org/document/d/16Ij5OcVnR3s0MH4Z5XkhI9VTPoMJdaBn9rKreAmGOdE/edit +// +// In a nutshell, PAC scripts are Javascript programs and may depend on +// network I/O, by calling functions like dnsResolve(). +// +// This is problematic since functions such as dnsResolve() will block the +// Javascript execution until the DNS result is availble, thereby stalling the +// PAC thread, which hurts the ability to process parallel proxy resolves. +// An obvious solution is to simply start more PAC threads, however this scales +// poorly, which hurts the ability to process parallel proxy resolves. +// +// The solution in ProxyResolverV8Tracing is to model PAC scripts as being +// deterministic, and depending only on the inputted URL. When the script +// issues a dnsResolve() for a yet unresolved hostname, the Javascript +// execution is "aborted", and then re-started once the DNS result is +// known. +namespace net { + +namespace { + +// Upper bound on how many *unique* DNS resolves a PAC script is allowed +// to make. This is a failsafe both for scripts that do a ridiculous +// number of DNS resolves, as well as scripts which are misbehaving +// under the tracing optimization. It is not expected to hit this normally. +const size_t kMaxUniqueResolveDnsPerExec = 20; + +// Approximate number of bytes to use for buffering alerts() and errors. +// This is a failsafe in case repeated executions of the script causes +// too much memory bloat. It is not expected for well behaved scripts to +// hit this. (In fact normal scripts should not even have alerts() or errors). +const size_t kMaxAlertsAndErrorsBytes = 2048; + +// Returns event parameters for a PAC error message (line number + message). +base::Value* NetLogErrorCallback(int line_number, + const string16* message, + NetLog::LogLevel /* log_level */) { + base::DictionaryValue* dict = new base::DictionaryValue(); + dict->SetInteger("line_number", line_number); + dict->SetString("message", *message); + return dict; +} + +} // namespace + +// The Job class is responsible for executing GetProxyForURL() and +// SetPacScript(), since both of these operations share similar code. +// +// The DNS for these operations can operate in either blocking or +// non-blocking mode. Blocking mode is used as a fallback when the PAC script +// seems to be misbehaving under the tracing optimization. +// +// Note that this class runs on both the origin thread and a worker +// thread. Most methods are expected to be used exclusively on one thread +// or the other. +// +// The lifetime of Jobs does not exceed that of the ProxyResolverV8Tracing that +// spawned it. Destruction might happen on either the origin thread or the +// worker thread. +class ProxyResolverV8Tracing::Job + : public base::RefCountedThreadSafe<ProxyResolverV8Tracing::Job>, + public ProxyResolverV8::JSBindings { + public: + // |parent| is non-owned. It is the ProxyResolverV8Tracing that spawned this + // Job, and must oulive it. + explicit Job(ProxyResolverV8Tracing* parent); + + // Called from origin thread. + void StartSetPacScript( + const scoped_refptr<ProxyResolverScriptData>& script_data, + const CompletionCallback& callback); + + // Called from origin thread. + void StartGetProxyForURL(const GURL& url, + ProxyInfo* results, + const BoundNetLog& net_log, + const CompletionCallback& callback); + + // Called from origin thread. + void Cancel(); + + // Called from origin thread. + LoadState GetLoadState() const; + + private: + typedef std::map<std::string, std::string> DnsCache; + friend class base::RefCountedThreadSafe<ProxyResolverV8Tracing::Job>; + + enum Operation { + SET_PAC_SCRIPT, + GET_PROXY_FOR_URL, + }; + + struct AlertOrError { + bool is_alert; + int line_number; + string16 message; + }; + + ~Job(); + + void CheckIsOnWorkerThread() const; + void CheckIsOnOriginThread() const; + + void SetCallback(const CompletionCallback& callback); + void ReleaseCallback(); + + ProxyResolverV8* v8_resolver(); + MessageLoop* worker_loop(); + HostResolver* host_resolver(); + ProxyResolverErrorObserver* error_observer(); + NetLog* net_log(); + + // Invokes the user's callback. + void NotifyCaller(int result); + void NotifyCallerOnOriginLoop(int result); + + void Start(Operation op, bool blocking_dns, + const CompletionCallback& callback); + + void ExecuteBlocking(); + void ExecuteNonBlocking(); + int ExecuteProxyResolver(); + + // Implementation of ProxyResolverv8::JSBindings + virtual bool ResolveDns(const std::string& host, + ResolveDnsOperation op, + std::string* output) OVERRIDE; + virtual void Alert(const string16& message) OVERRIDE; + virtual void OnError(int line_number, const string16& error) OVERRIDE; + + bool ResolveDnsBlocking(const std::string& host, + ResolveDnsOperation op, + std::string* output); + + bool ResolveDnsNonBlocking(const std::string& host, + ResolveDnsOperation op, + std::string* output); + + void DoDnsOperation(const std::string& host, ResolveDnsOperation op, + bool* out_cache_hit); + void OnDnsOperationComplete(int result); + + void ScheduleRestartWithBlockingDns(); + + bool GetDnsFromLocalCache(const std::string& host, ResolveDnsOperation op, + std::string* output, bool* return_value); + + void SaveDnsToLocalCache(const std::string& host, ResolveDnsOperation op, + int net_error, const net::AddressList& addresses); + + // Builds a RequestInfo to service the specified PAC DNS operation. + static HostResolver::RequestInfo MakeDnsRequestInfo(const std::string& host, + ResolveDnsOperation op); + + // Makes a key for looking up |host, op| in |dns_cache_|. Strings are used for + // convenience, to avoid defining custom comparators. + static std::string MakeDnsCacheKey(const std::string& host, + ResolveDnsOperation op); + + void HandleAlertOrError(bool is_alert, int line_number, + const string16& message); + void DispatchBufferedAlertsAndErrors(); + void DispatchAlertOrError(bool is_alert, int line_number, + const string16& message); + + void LogEventToCurrentRequestAndGlobally( + NetLog::EventType type, + const NetLog::ParametersCallback& parameters_callback); + + // The thread which called into ProxyResolverV8Tracing, and on which the + // completion callback is expected to run. + scoped_refptr<base::MessageLoopProxy> origin_loop_; + + // The ProxyResolverV8Tracing which spawned this Job. + // Initialized on origin thread and then accessed from both threads. + ProxyResolverV8Tracing* parent_; + + // The callback to run (on the origin thread) when the Job finishes. + // Should only be accessed from origin thread. + CompletionCallback callback_; + + // Flag to indicate whether the request has been cancelled. + base::CancellationFlag cancelled_; + + // The operation that this Job is running. + // Initialized on origin thread and then accessed from both threads. + Operation operation_; + + // The DNS mode for this Job. + // Initialized on origin thread, mutated on worker thread, and accessed + // by both the origin thread and worker thread. + bool blocking_dns_; + + // Used to block the worker thread on a DNS operation taking place on the + // origin thread. + base::WaitableEvent event_; + + // Map of DNS operations completed so far. Written into on the origin thread + // and read on the worker thread. + DnsCache dns_cache_; + + // The job holds a reference to itself to ensure that it remains alive until + // either completion or cancellation. + scoped_refptr<Job> owned_self_reference_; + + // ------------------------------------------------------- + // State specific to SET_PAC_SCRIPT. + // ------------------------------------------------------- + + scoped_refptr<ProxyResolverScriptData> script_data_; + + // ------------------------------------------------------- + // State specific to GET_PROXY_FOR_URL. + // ------------------------------------------------------- + + ProxyInfo* user_results_; // Owned by caller, lives on origin thread. + GURL url_; + ProxyInfo results_; + BoundNetLog bound_net_log_; + + // --------------------------------------------------------------------------- + // State for ExecuteNonBlocking() + // --------------------------------------------------------------------------- + // These variables are used exclusively on the worker thread and are only + // meaningful when executing inside of ExecuteNonBlocking(). + + // Whether this execution was abandoned due to a missing DNS dependency. + bool abandoned_; + + // Number of calls made to ResolveDns() by this execution. + int num_dns_; + + // Sequence of calls made to Alert() or OnError() by this execution. + std::vector<AlertOrError> alerts_and_errors_; + size_t alerts_and_errors_byte_cost_; // Approximate byte cost of the above. + + // Number of calls made to ResolveDns() by the PREVIOUS execution. + int last_num_dns_; + + // Whether the current execution needs to be restarted in blocking mode. + bool should_restart_with_blocking_dns_; + + // --------------------------------------------------------------------------- + // State for pending DNS request. + // --------------------------------------------------------------------------- + // These variables are used exclusively on the origin thread. + + HostResolver::RequestHandle pending_dns_; + // Only meaningful when |pending_dns_|: + std::string pending_dns_host_; + ResolveDnsOperation pending_dns_op_; + AddressList pending_dns_addresses_; +}; + +ProxyResolverV8Tracing::Job::Job(ProxyResolverV8Tracing* parent) + : origin_loop_(base::MessageLoopProxy::current()), + parent_(parent), + event_(true, false), + last_num_dns_(0), + pending_dns_(NULL) { + CheckIsOnOriginThread(); +} + +void ProxyResolverV8Tracing::Job::StartSetPacScript( + const scoped_refptr<ProxyResolverScriptData>& script_data, + const CompletionCallback& callback) { + CheckIsOnOriginThread(); + + script_data_ = script_data; + + // Script initialization uses blocking DNS since there isn't any + // advantage to using non-blocking mode here. That is because the + // parent ProxyService can't submit any ProxyResolve requests until + // initialization has completed successfully! + Start(SET_PAC_SCRIPT, true /*blocking*/, callback); +} + +void ProxyResolverV8Tracing::Job::StartGetProxyForURL( + const GURL& url, + ProxyInfo* results, + const BoundNetLog& net_log, + const CompletionCallback& callback) { + CheckIsOnOriginThread(); + + url_ = url; + user_results_ = results; + bound_net_log_ = net_log; + + Start(GET_PROXY_FOR_URL, false /*non-blocking*/, callback); +} + +void ProxyResolverV8Tracing::Job::Cancel() { + CheckIsOnOriginThread(); + + // There are several possibilities to consider for cancellation: + // (a) The job has been posted to the worker thread, however script execution + // has not yet started. + // (b) The script is executing on the worker thread. + // (c) The script is executing on the worker thread, however is blocked inside + // of dnsResolve() waiting for a response from the origin thread. + // (d) Nothing is running on the worker thread, however the host resolver has + // a pending DNS request which upon completion will restart the script + // execution. + // (e) The worker thread has a pending task to restart execution, which was + // posted after the DNS dependency was resolved and saved to local cache. + // (f) The script execution completed entirely, and posted a task to the + // origin thread to notify the caller. + // + // |cancelled_| is read on both the origin thread and worker thread. The + // code that runs on the worker thread is littered with checks on + // |cancelled_| to break out early. + cancelled_.Set(); + + ReleaseCallback(); + + if (pending_dns_) { + host_resolver()->CancelRequest(pending_dns_); + pending_dns_ = NULL; + } + + // The worker thread might be blocked waiting for DNS. + event_.Signal(); + + owned_self_reference_ = NULL; +} + +LoadState ProxyResolverV8Tracing::Job::GetLoadState() const { + CheckIsOnOriginThread(); + + if (pending_dns_) + return LOAD_STATE_RESOLVING_HOST_IN_PROXY_SCRIPT; + + return LOAD_STATE_RESOLVING_PROXY_FOR_URL; +} + +ProxyResolverV8Tracing::Job::~Job() { + DCHECK(!pending_dns_); + DCHECK(callback_.is_null()); +} + +void ProxyResolverV8Tracing::Job::CheckIsOnWorkerThread() const { + DCHECK_EQ(MessageLoop::current(), parent_->thread_->message_loop()); +} + +void ProxyResolverV8Tracing::Job::CheckIsOnOriginThread() const { + DCHECK(origin_loop_->BelongsToCurrentThread()); +} + +void ProxyResolverV8Tracing::Job::SetCallback( + const CompletionCallback& callback) { + CheckIsOnOriginThread(); + DCHECK(callback_.is_null()); + parent_->num_outstanding_callbacks_++; + callback_ = callback; +} + +void ProxyResolverV8Tracing::Job::ReleaseCallback() { + CheckIsOnOriginThread(); + DCHECK(!callback_.is_null()); + CHECK_GT(parent_->num_outstanding_callbacks_, 0); + parent_->num_outstanding_callbacks_--; + callback_.Reset(); + + // For good measure, clear this other user-owned pointer. + user_results_ = NULL; +} + +ProxyResolverV8* ProxyResolverV8Tracing::Job::v8_resolver() { + return parent_->v8_resolver_.get(); +} + +MessageLoop* ProxyResolverV8Tracing::Job::worker_loop() { + return parent_->thread_->message_loop(); +} + +HostResolver* ProxyResolverV8Tracing::Job::host_resolver() { + return parent_->host_resolver_; +} + +ProxyResolverErrorObserver* ProxyResolverV8Tracing::Job::error_observer() { + return parent_->error_observer_.get(); +} + +NetLog* ProxyResolverV8Tracing::Job::net_log() { + return parent_->net_log_; +} + +void ProxyResolverV8Tracing::Job::NotifyCaller(int result) { + CheckIsOnWorkerThread(); + + origin_loop_->PostTask( + FROM_HERE, + base::Bind(&Job::NotifyCallerOnOriginLoop, this, result)); +} + +void ProxyResolverV8Tracing::Job::NotifyCallerOnOriginLoop(int result) { + CheckIsOnOriginThread(); + + if (cancelled_.IsSet()) + return; + + DCHECK(!callback_.is_null()); + DCHECK(!pending_dns_); + + if (operation_ == GET_PROXY_FOR_URL) + *user_results_ = results_; + + // There is only ever 1 outstanding SET_PAC_SCRIPT job. It needs to be + // tracked to support cancellation. + if (operation_ == SET_PAC_SCRIPT) { + DCHECK_EQ(parent_->set_pac_script_job_.get(), this); + parent_->set_pac_script_job_ = NULL; + } + + CompletionCallback callback = callback_; + ReleaseCallback(); + callback.Run(result); + + owned_self_reference_ = NULL; +} + +void ProxyResolverV8Tracing::Job::Start(Operation op, bool blocking_dns, + const CompletionCallback& callback) { + CheckIsOnOriginThread(); + + operation_ = op; + blocking_dns_ = blocking_dns; + SetCallback(callback); + + owned_self_reference_ = this; + + worker_loop()->PostTask(FROM_HERE, + blocking_dns_ ? base::Bind(&Job::ExecuteBlocking, this) : + base::Bind(&Job::ExecuteNonBlocking, this)); +} + +void ProxyResolverV8Tracing::Job::ExecuteBlocking() { + CheckIsOnWorkerThread(); + DCHECK(blocking_dns_); + + if (cancelled_.IsSet()) + return; + + NotifyCaller(ExecuteProxyResolver()); +} + +void ProxyResolverV8Tracing::Job::ExecuteNonBlocking() { + CheckIsOnWorkerThread(); + DCHECK(!blocking_dns_); + + if (cancelled_.IsSet()) + return; + + // Reset state for the current execution. + abandoned_ = false; + num_dns_ = 0; + alerts_and_errors_.clear(); + alerts_and_errors_byte_cost_ = 0; + should_restart_with_blocking_dns_ = false; + + int result = ExecuteProxyResolver(); + + if (should_restart_with_blocking_dns_) { + DCHECK(!blocking_dns_); + DCHECK(abandoned_); + blocking_dns_ = true; + ExecuteBlocking(); + return; + } + + if (abandoned_) + return; + + DispatchBufferedAlertsAndErrors(); + NotifyCaller(result); +} + +int ProxyResolverV8Tracing::Job::ExecuteProxyResolver() { + JSBindings* prev_bindings = v8_resolver()->js_bindings(); + v8_resolver()->set_js_bindings(this); + + int result = ERR_UNEXPECTED; // Initialized to silence warnings. + + switch (operation_) { + case SET_PAC_SCRIPT: + result = v8_resolver()->SetPacScript( + script_data_, CompletionCallback()); + break; + case GET_PROXY_FOR_URL: + result = v8_resolver()->GetProxyForURL( + url_, + // Important: Do not write directly into |user_results_|, since if the + // request were to be cancelled from the origin thread, must guarantee + // that |user_results_| is not accessed anymore. + &results_, + CompletionCallback(), + NULL, + bound_net_log_); + break; + } + + v8_resolver()->set_js_bindings(prev_bindings); + return result; +} + +bool ProxyResolverV8Tracing::Job::ResolveDns(const std::string& host, + ResolveDnsOperation op, + std::string* output) { + if (cancelled_.IsSet()) + return false; + + if ((op == DNS_RESOLVE || op == DNS_RESOLVE_EX) && host.empty()) { + // a DNS resolve with an empty hostname is considered an error. + return false; + } + + return blocking_dns_ ? + ResolveDnsBlocking(host, op, output) : + ResolveDnsNonBlocking(host, op, output); +} + +void ProxyResolverV8Tracing::Job::Alert(const string16& message) { + HandleAlertOrError(true, -1, message); +} + +void ProxyResolverV8Tracing::Job::OnError(int line_number, + const string16& error) { + HandleAlertOrError(false, line_number, error); +} + +bool ProxyResolverV8Tracing::Job::ResolveDnsBlocking(const std::string& host, + ResolveDnsOperation op, + std::string* output) { + CheckIsOnWorkerThread(); + + // Check if the DNS result for this host has already been cached. + bool rv; + if (GetDnsFromLocalCache(host, op, output, &rv)) { + // Yay, cache hit! + return rv; + } + + // If the host was not in the local cache, this is a new hostname. + + if (dns_cache_.size() >= kMaxUniqueResolveDnsPerExec) { + // Safety net for scripts with unexpectedly many DNS calls. + // We will continue running to completion, but will fail every + // subsequent DNS request. + return false; + } + + bool unused; + origin_loop_->PostTask( + FROM_HERE, base::Bind(&Job::DoDnsOperation, this, host, op, &unused)); + + // Wait for the DNS operation to be completed by the host resolver on the + // origin thread. After waking up, either the request was cancelled, or + // the DNS result is now available in the cache. + event_.Wait(); + event_.Reset(); + + if (cancelled_.IsSet()) + return false; + + CHECK(GetDnsFromLocalCache(host, op, output, &rv)); + return rv; +} + +bool ProxyResolverV8Tracing::Job::ResolveDnsNonBlocking(const std::string& host, + ResolveDnsOperation op, + std::string* output) { + CheckIsOnWorkerThread(); + + if (abandoned_) { + // If this execution was already abandoned can fail right away. Only 1 DNS + // dependency will be traced at a time (for more predictable outcomes). + return false; + } + + num_dns_ += 1; + + // Check if the DNS result for this host has already been cached. + bool rv; + if (GetDnsFromLocalCache(host, op, output, &rv)) { + // Yay, cache hit! + return rv; + } + + // If the host was not in the local cache, then this is a new hostname. + + if (num_dns_ <= last_num_dns_) { + // The sequence of DNS operations is different from last time! + ScheduleRestartWithBlockingDns(); + return false; + } + + if (dns_cache_.size() >= kMaxUniqueResolveDnsPerExec) { + // Safety net for scripts with unexpectedly many DNS calls. + return false; + } + + DCHECK(!should_restart_with_blocking_dns_); + + // Post the DNS request to the origin thread. + bool resolver_cache_hit = false; + origin_loop_->PostTask( + FROM_HERE, base::Bind(&Job::DoDnsOperation, this, host, op, + &resolver_cache_hit)); + + // As an optimization to avoid restarting too often, wait until the + // resolver's cache has been inspected on the origin thread. + event_.Wait(); + event_.Reset(); + + if (cancelled_.IsSet()) + return false; + + if (resolver_cache_hit) { + CHECK(GetDnsFromLocalCache(host, op, output, &rv)); + return rv; + } + + // Otherwise if the result was not in the cache, then a DNS request has + // been started. Abandon this invocation of FindProxyForURL(), it will be + // restarted once the DNS request completes. + abandoned_ = true; + last_num_dns_ = num_dns_; + return false; +} + +void ProxyResolverV8Tracing::Job::DoDnsOperation( + const std::string& host, ResolveDnsOperation op, bool* out_cache_hit) { + CheckIsOnOriginThread(); + DCHECK(!pending_dns_); + + if (cancelled_.IsSet()) + return; + + int result = host_resolver()->Resolve( + MakeDnsRequestInfo(host, op), + &pending_dns_addresses_, + base::Bind(&Job::OnDnsOperationComplete, this), + &pending_dns_, + bound_net_log_); + + bool completed_synchronously = result != ERR_IO_PENDING; + + if (!blocking_dns_) { + // Check if the DNS result can be serviced directly from the cache. + // (The worker thread is blocked waiting for this information). + if (completed_synchronously) { + SaveDnsToLocalCache(host, op, result, pending_dns_addresses_); + pending_dns_ = NULL; + } + + // Important: Do not read/write |out_cache_hit| after signalling, since + // the memory may no longer be valid. + *out_cache_hit = completed_synchronously; + event_.Signal(); + + if (completed_synchronously) + return; + } + + pending_dns_host_ = host; + pending_dns_op_ = op; + + if (completed_synchronously) + OnDnsOperationComplete(result); +} + +void ProxyResolverV8Tracing::Job::OnDnsOperationComplete(int result) { + CheckIsOnOriginThread(); + + DCHECK(pending_dns_); + DCHECK(!cancelled_.IsSet()); + + SaveDnsToLocalCache(pending_dns_host_, pending_dns_op_, result, + pending_dns_addresses_); + pending_dns_ = NULL; + + if (!blocking_dns_) { + // Restart. This time it should make more progress due to having + // cached items. + worker_loop()->PostTask(FROM_HERE, + base::Bind(&Job::ExecuteNonBlocking, this)); + } else { + // Otherwise wakeup the blocked worker thread. + event_.Signal(); + } +} + +void ProxyResolverV8Tracing::Job::ScheduleRestartWithBlockingDns() { + CheckIsOnWorkerThread(); + + DCHECK(!should_restart_with_blocking_dns_); + DCHECK(!abandoned_); + DCHECK(!blocking_dns_); + + abandoned_ = true; + + // The restart will happen after ExecuteNonBlocking() finishes. + should_restart_with_blocking_dns_ = true; +} + +bool ProxyResolverV8Tracing::Job::GetDnsFromLocalCache( + const std::string& host, + ResolveDnsOperation op, + std::string* output, + bool* return_value) { + CheckIsOnWorkerThread(); + + DnsCache::const_iterator it = dns_cache_.find(MakeDnsCacheKey(host, op)); + if (it == dns_cache_.end()) + return false; + + *output = it->second; + *return_value = !it->second.empty(); + return true; +} + +void ProxyResolverV8Tracing::Job::SaveDnsToLocalCache( + const std::string& host, + ResolveDnsOperation op, + int net_error, + const net::AddressList& addresses) { + CheckIsOnOriginThread(); + + // Serialize the result into a string to save to the cache. + std::string cache_value; + if (net_error != OK) { + cache_value = std::string(); + } else if (op == DNS_RESOLVE || op == MY_IP_ADDRESS) { + // dnsResolve() and myIpAddress() are expected to return a single IP + // address. + cache_value = addresses.front().ToStringWithoutPort(); + } else { + // The *Ex versions are expected to return a semi-colon separated list. + for (AddressList::const_iterator iter = addresses.begin(); + iter != addresses.end(); ++iter) { + if (!cache_value.empty()) + cache_value += ";"; + cache_value += iter->ToStringWithoutPort(); + } + } + + dns_cache_[MakeDnsCacheKey(host, op)] = cache_value; +} + +// static +HostResolver::RequestInfo ProxyResolverV8Tracing::Job::MakeDnsRequestInfo( + const std::string& host, ResolveDnsOperation op) { + HostPortPair host_port = HostPortPair(host, 80); + if (op == MY_IP_ADDRESS || op == MY_IP_ADDRESS_EX) { + host_port.set_host(GetHostName()); + } + + HostResolver::RequestInfo info(host_port); + + // The non-ex flavors are limited to IPv4 results. + if (op == MY_IP_ADDRESS || op == DNS_RESOLVE) { + info.set_address_family(ADDRESS_FAMILY_IPV4); + } + + return info; +} + +std::string ProxyResolverV8Tracing::Job::MakeDnsCacheKey( + const std::string& host, ResolveDnsOperation op) { + return StringPrintf("%d:%s", op, host.c_str()); +} + +void ProxyResolverV8Tracing::Job::HandleAlertOrError(bool is_alert, + int line_number, + const string16& message) { + CheckIsOnWorkerThread(); + + if (cancelled_.IsSet()) + return; + + if (blocking_dns_) { + // In blocking DNS mode the events can be dispatched immediately. + DispatchAlertOrError(is_alert, line_number, message); + return; + } + + // Otherwise in nonblocking mode, buffer all the messages until + // the end. + + if (abandoned_) + return; + + alerts_and_errors_byte_cost_ += sizeof(AlertOrError) + message.size() * 2; + + // If there have been lots of messages, enqueing could be expensive on + // memory. Consider a script which does megabytes worth of alerts(). + // Avoid this by falling back to blocking mode. + if (alerts_and_errors_byte_cost_ > kMaxAlertsAndErrorsBytes) { + ScheduleRestartWithBlockingDns(); + return; + } + + AlertOrError entry = {is_alert, line_number, message}; + alerts_and_errors_.push_back(entry); +} + +void ProxyResolverV8Tracing::Job::DispatchBufferedAlertsAndErrors() { + CheckIsOnWorkerThread(); + DCHECK(!blocking_dns_); + DCHECK(!abandoned_); + + for (size_t i = 0; i < alerts_and_errors_.size(); ++i) { + const AlertOrError& x = alerts_and_errors_[i]; + DispatchAlertOrError(x.is_alert, x.line_number, x.message); + } +} + +void ProxyResolverV8Tracing::Job::DispatchAlertOrError( + bool is_alert, int line_number, const string16& message) { + CheckIsOnWorkerThread(); + + // Note that the handling of cancellation is racy with regard to + // alerts/errors. The request might get cancelled shortly after this + // check! (There is no lock being held to guarantee otherwise). + // + // If this happens, then some information will get written to the NetLog + // needlessly, however the NetLog will still be alive so it shouldn't cause + // problems. + if (cancelled_.IsSet()) + return; + + if (is_alert) { + // ------------------- + // alert + // ------------------- + VLOG(1) << "PAC-alert: " << message; + + // Send to the NetLog. + LogEventToCurrentRequestAndGlobally( + NetLog::TYPE_PAC_JAVASCRIPT_ALERT, + NetLog::StringCallback("message", &message)); + } else { + // ------------------- + // error + // ------------------- + if (line_number == -1) + VLOG(1) << "PAC-error: " << message; + else + VLOG(1) << "PAC-error: " << "line: " << line_number << ": " << message; + + // Send the error to the NetLog. + LogEventToCurrentRequestAndGlobally( + NetLog::TYPE_PAC_JAVASCRIPT_ERROR, + base::Bind(&NetLogErrorCallback, line_number, &message)); + + if (error_observer()) + error_observer()->OnPACScriptError(line_number, message); + } +} + +void ProxyResolverV8Tracing::Job::LogEventToCurrentRequestAndGlobally( + NetLog::EventType type, + const NetLog::ParametersCallback& parameters_callback) { + CheckIsOnWorkerThread(); + bound_net_log_.AddEvent(type, parameters_callback); + + // Emit to the global NetLog event stream. + if (net_log()) + net_log()->AddGlobalEntry(type, parameters_callback); +} + +ProxyResolverV8Tracing::ProxyResolverV8Tracing( + HostResolver* host_resolver, + ProxyResolverErrorObserver* error_observer, + NetLog* net_log) + : ProxyResolver(true /*expects_pac_bytes*/), + host_resolver_(host_resolver), + error_observer_(error_observer), + net_log_(net_log), + num_outstanding_callbacks_(0) { + DCHECK(host_resolver); + // Start up the thread. + thread_.reset(new base::Thread("Proxy resolver")); + CHECK(thread_->Start()); + + v8_resolver_.reset(new ProxyResolverV8); +} + +ProxyResolverV8Tracing::~ProxyResolverV8Tracing() { + // Note, all requests should have been cancelled. + CHECK(!set_pac_script_job_); + CHECK_EQ(0, num_outstanding_callbacks_); + + // Join the worker thread. + // See http://crbug.com/69710. + base::ThreadRestrictions::ScopedAllowIO allow_io; + thread_.reset(); +} + +int ProxyResolverV8Tracing::GetProxyForURL(const GURL& url, + ProxyInfo* results, + const CompletionCallback& callback, + RequestHandle* request, + const BoundNetLog& net_log) { + DCHECK(CalledOnValidThread()); + DCHECK(!callback.is_null()); + DCHECK(!set_pac_script_job_); + + scoped_refptr<Job> job = new Job(this); + + if (request) + *request = job.get(); + + job->StartGetProxyForURL(url, results, net_log, callback); + return ERR_IO_PENDING; +} + +void ProxyResolverV8Tracing::CancelRequest(RequestHandle request) { + Job* job = reinterpret_cast<Job*>(request); + job->Cancel(); +} + +LoadState ProxyResolverV8Tracing::GetLoadState(RequestHandle request) const { + Job* job = reinterpret_cast<Job*>(request); + return job->GetLoadState(); +} + +void ProxyResolverV8Tracing::CancelSetPacScript() { + DCHECK(set_pac_script_job_); + set_pac_script_job_->Cancel(); + set_pac_script_job_ = NULL; +} + +void ProxyResolverV8Tracing::PurgeMemory() { + thread_->message_loop()->PostTask( + FROM_HERE, + base::Bind(&ProxyResolverV8::PurgeMemory, + // The use of unretained is safe, since the worker thread + // cannot outlive |this|. + base::Unretained(v8_resolver_.get()))); +} + +int ProxyResolverV8Tracing::SetPacScript( + const scoped_refptr<ProxyResolverScriptData>& script_data, + const CompletionCallback& callback) { + DCHECK(CalledOnValidThread()); + DCHECK(!callback.is_null()); + + // Note that there should not be any outstanding (non-cancelled) Jobs when + // setting the PAC script (ProxyService should guarantee this). If there are, + // then they might complete in strange ways after the new script is set. + DCHECK(!set_pac_script_job_); + CHECK_EQ(0, num_outstanding_callbacks_); + + set_pac_script_job_ = new Job(this); + set_pac_script_job_->StartSetPacScript(script_data, callback); + + return ERR_IO_PENDING; +} + +} // namespace net diff --git a/net/proxy/proxy_resolver_v8_tracing.h b/net/proxy/proxy_resolver_v8_tracing.h new file mode 100644 index 0000000..5877aa2 --- /dev/null +++ b/net/proxy/proxy_resolver_v8_tracing.h @@ -0,0 +1,85 @@ +// Copyright (c) 2013 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 NET_PROXY_PROXY_RESOLVER_V8_TRACING_H_ +#define NET_PROXY_PROXY_RESOLVER_V8_TRACING_H_ + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/threading/non_thread_safe.h" +#include "net/base/net_export.h" +#include "net/proxy/proxy_resolver.h" + +namespace base { +class Thread; +class MessageLoopProxy; +} // namespace base + +namespace net { + +class HostResolver; +class NetLog; +class ProxyResolverErrorObserver; +class ProxyResolverV8; + +// ProxyResolverV8Tracing is a non-blocking ProxyResolver. It executes +// ProxyResolverV8 on a single helper thread, and does some magic to avoid +// blocking in DNS. For more details see the design document: +// https://docs.google.com/a/google.com/document/d/16Ij5OcVnR3s0MH4Z5XkhI9VTPoMJdaBn9rKreAmGOdE/edit?pli=1 +class NET_EXPORT_PRIVATE ProxyResolverV8Tracing + : public ProxyResolver, + NON_EXPORTED_BASE(public base::NonThreadSafe) { + public: + // Constructs a ProxyResolver that will issue DNS requests through + // |host_resolver|, forward Javascript errors through |error_observer|, and + // log Javascript errors and alerts to |net_log|. + // + // Note that the constructor takes ownership of |error_observer|, whereas + // |host_resolver| and |net_log| are expected to outlive |this|. + ProxyResolverV8Tracing(HostResolver* host_resolver, + ProxyResolverErrorObserver* error_observer, + NetLog* net_log); + + virtual ~ProxyResolverV8Tracing(); + + // ProxyResolver implementation: + virtual int GetProxyForURL(const GURL& url, + ProxyInfo* results, + const CompletionCallback& callback, + RequestHandle* request, + const BoundNetLog& net_log) OVERRIDE; + virtual void CancelRequest(RequestHandle request) OVERRIDE; + virtual LoadState GetLoadState(RequestHandle request) const OVERRIDE; + virtual void CancelSetPacScript() OVERRIDE; + virtual void PurgeMemory() OVERRIDE; + virtual int SetPacScript( + const scoped_refptr<ProxyResolverScriptData>& script_data, + const CompletionCallback& callback) OVERRIDE; + + private: + class Job; + + // The worker thread on which the ProxyResolverV8 will be run. + scoped_ptr<base::Thread> thread_; + scoped_ptr<ProxyResolverV8> v8_resolver_; + + // Non-owned host resolver, which is to be operated on the origin thread. + HostResolver* host_resolver_; + + scoped_ptr<ProxyResolverErrorObserver> error_observer_; + NetLog* net_log_; + + // The outstanding SetPacScript operation, or NULL. + scoped_refptr<Job> set_pac_script_job_; + + // The number of outstanding (non-cancelled) jobs. + int num_outstanding_callbacks_; + + DISALLOW_COPY_AND_ASSIGN(ProxyResolverV8Tracing); +}; + +} // namespace net + +#endif // NET_PROXY_PROXY_RESOLVER_V8_TRACING_H_ diff --git a/net/proxy/proxy_resolver_v8_tracing_unittest.cc b/net/proxy/proxy_resolver_v8_tracing_unittest.cc new file mode 100644 index 0000000..36e6087 --- /dev/null +++ b/net/proxy/proxy_resolver_v8_tracing_unittest.cc @@ -0,0 +1,928 @@ +// Copyright (c) 2013 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 "net/proxy/proxy_resolver_v8_tracing.h" + +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/path_service.h" +#include "base/stl_util.h" +#include "base/string_util.h" +#include "base/stringprintf.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/platform_thread.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "googleurl/src/gurl.h" +#include "net/base/host_cache.h" +#include "net/base/mock_host_resolver.h" +#include "net/base/net_errors.h" +#include "net/base/net_log.h" +#include "net/base/net_log_unittest.h" +#include "net/base/test_completion_callback.h" +#include "net/proxy/proxy_info.h" +#include "net/proxy/proxy_resolver_error_observer.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +namespace { + +class ProxyResolverV8TracingTest : public testing::Test { + public: + virtual void TearDown() OVERRIDE { + // Drain any pending messages, which may be left over from cancellation. + // This way they get reliably run as part of the current test, rather than + // spilling into the next test's execution. + MessageLoop::current()->RunUntilIdle(); + } +}; + +scoped_refptr<ProxyResolverScriptData> LoadScriptData(const char* filename) { + FilePath path; + PathService::Get(base::DIR_SOURCE_ROOT, &path); + path = path.AppendASCII("net"); + path = path.AppendASCII("data"); + path = path.AppendASCII("proxy_resolver_v8_tracing_unittest"); + path = path.AppendASCII(filename); + + // Try to read the file from disk. + std::string file_contents; + bool ok = file_util::ReadFileToString(path, &file_contents); + + // If we can't load the file from disk, something is misconfigured. + EXPECT_TRUE(ok) << "Failed to read file: " << path.value(); + + // Load the PAC script into the ProxyResolver. + return ProxyResolverScriptData::FromUTF8(file_contents); +} + +void InitResolver(ProxyResolverV8Tracing* resolver, const char* filename) { + TestCompletionCallback callback; + int rv = + resolver->SetPacScript(LoadScriptData(filename), callback.callback()); + EXPECT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(OK, callback.WaitForResult()); +} + +class MockErrorObserver : public ProxyResolverErrorObserver { + public: + MockErrorObserver() : event_(true, false) {} + + virtual void OnPACScriptError(int line_number, + const string16& error) OVERRIDE { + { + base::AutoLock l(lock_); + output += StringPrintf("Error: line %d: %s\n", line_number, + UTF16ToASCII(error).c_str()); + } + event_.Signal(); + } + + std::string GetOutput() { + base::AutoLock l(lock_); + return output; + } + + void WaitForOutput() { + event_.Wait(); + } + + private: + base::Lock lock_; + std::string output; + + base::WaitableEvent event_; +}; + +TEST_F(ProxyResolverV8TracingTest, Simple) { + CapturingNetLog log; + CapturingBoundNetLog request_log; + MockCachingHostResolver host_resolver; + MockErrorObserver* error_observer = new MockErrorObserver; + ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); + + InitResolver(&resolver, "simple.js"); + + TestCompletionCallback callback; + ProxyInfo proxy_info; + + int rv = resolver.GetProxyForURL( + GURL("http://foo/"), &proxy_info, callback.callback(), + NULL, request_log.bound()); + + EXPECT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(OK, callback.WaitForResult()); + + EXPECT_EQ("foo:99", proxy_info.proxy_server().ToURI()); + + EXPECT_EQ(0u, host_resolver.num_resolve()); + + // There were no errors. + EXPECT_EQ("", error_observer->GetOutput()); + + // Check the NetLogs -- nothing was logged. + EXPECT_EQ(0u, log.GetSize()); + EXPECT_EQ(0u, request_log.GetSize()); +} + +TEST_F(ProxyResolverV8TracingTest, JavascriptError) { + CapturingNetLog log; + CapturingBoundNetLog request_log; + MockCachingHostResolver host_resolver; + MockErrorObserver* error_observer = new MockErrorObserver; + ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); + + InitResolver(&resolver, "error.js"); + + TestCompletionCallback callback; + ProxyInfo proxy_info; + + int rv = resolver.GetProxyForURL( + GURL("http://throw-an-error/"), &proxy_info, callback.callback(), NULL, + request_log.bound()); + + EXPECT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, callback.WaitForResult()); + + EXPECT_EQ(0u, host_resolver.num_resolve()); + + EXPECT_EQ("Error: line 5: Uncaught TypeError: Cannot call method 'split' " + "of null\n", error_observer->GetOutput()); + + // Check the NetLogs -- there was 1 alert and 1 javascript error, and they + // were output to both the global log, and per-request log. + CapturingNetLog::CapturedEntryList entries_list[2]; + log.GetEntries(&entries_list[0]); + request_log.GetEntries(&entries_list[1]); + + for (size_t list_i = 0; list_i < arraysize(entries_list); list_i++) { + const CapturingNetLog::CapturedEntryList& entries = entries_list[list_i]; + EXPECT_EQ(2u, entries.size()); + EXPECT_TRUE( + LogContainsEvent(entries, 0, NetLog::TYPE_PAC_JAVASCRIPT_ALERT, + NetLog::PHASE_NONE)); + EXPECT_TRUE( + LogContainsEvent(entries, 1, NetLog::TYPE_PAC_JAVASCRIPT_ERROR, + NetLog::PHASE_NONE)); + + EXPECT_EQ("{\"message\":\"Prepare to DIE!\"}", entries[0].GetParamsJson()); + EXPECT_EQ("{\"line_number\":5,\"message\":\"Uncaught TypeError: Cannot " + "call method 'split' of null\"}", entries[1].GetParamsJson()); + } +} + +TEST_F(ProxyResolverV8TracingTest, TooManyAlerts) { + CapturingNetLog log; + CapturingBoundNetLog request_log; + MockCachingHostResolver host_resolver; + MockErrorObserver* error_observer = new MockErrorObserver; + ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); + + InitResolver(&resolver, "too_many_alerts.js"); + + TestCompletionCallback callback; + ProxyInfo proxy_info; + + int rv = resolver.GetProxyForURL( + GURL("http://foo/"), + &proxy_info, + callback.callback(), + NULL, + request_log.bound()); + + EXPECT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(OK, callback.WaitForResult()); + + // Iteration1 does a DNS resolve + // Iteration2 exceeds the alert buffer + // Iteration3 runs in blocking mode and completes + EXPECT_EQ("foo:3", proxy_info.proxy_server().ToURI()); + + EXPECT_EQ(1u, host_resolver.num_resolve()); + + // No errors. + EXPECT_EQ("", error_observer->GetOutput()); + + // Check the NetLogs -- the script generated 50 alerts, which were mirrored + // to both the global and per-request logs. + CapturingNetLog::CapturedEntryList entries_list[2]; + log.GetEntries(&entries_list[0]); + request_log.GetEntries(&entries_list[1]); + + for (size_t list_i = 0; list_i < arraysize(entries_list); list_i++) { + const CapturingNetLog::CapturedEntryList& entries = entries_list[list_i]; + EXPECT_EQ(50u, entries.size()); + for (size_t i = 0; i < entries.size(); ++i) { + ASSERT_TRUE( + LogContainsEvent(entries, i, NetLog::TYPE_PAC_JAVASCRIPT_ALERT, + NetLog::PHASE_NONE)); + } + } +} + +// Verify that buffered alerts cannot grow unboundedly, even when the message is +// empty string. +TEST_F(ProxyResolverV8TracingTest, TooManyEmptyAlerts) { + CapturingNetLog log; + CapturingBoundNetLog request_log; + MockCachingHostResolver host_resolver; + MockErrorObserver* error_observer = new MockErrorObserver; + ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); + + InitResolver(&resolver, "too_many_empty_alerts.js"); + + TestCompletionCallback callback; + ProxyInfo proxy_info; + + int rv = resolver.GetProxyForURL( + GURL("http://foo/"), + &proxy_info, + callback.callback(), + NULL, + request_log.bound()); + + EXPECT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(OK, callback.WaitForResult()); + + EXPECT_EQ("foo:3", proxy_info.proxy_server().ToURI()); + + EXPECT_EQ(1u, host_resolver.num_resolve()); + + // No errors. + EXPECT_EQ("", error_observer->GetOutput()); + + // Check the NetLogs -- the script generated 50 alerts, which were mirrored + // to both the global and per-request logs. + CapturingNetLog::CapturedEntryList entries_list[2]; + log.GetEntries(&entries_list[0]); + request_log.GetEntries(&entries_list[1]); + + for (size_t list_i = 0; list_i < arraysize(entries_list); list_i++) { + const CapturingNetLog::CapturedEntryList& entries = entries_list[list_i]; + EXPECT_EQ(1000u, entries.size()); + for (size_t i = 0; i < entries.size(); ++i) { + ASSERT_TRUE( + LogContainsEvent(entries, i, NetLog::TYPE_PAC_JAVASCRIPT_ALERT, + NetLog::PHASE_NONE)); + } + } +} + +// This test runs a PAC script that issues a sequence of DNS resolves. The test +// verifies the final result, and that the underlying DNS resolver received +// the correct set of queries. +TEST_F(ProxyResolverV8TracingTest, Dns) { + CapturingNetLog log; + CapturingBoundNetLog request_log; + MockCachingHostResolver host_resolver; + MockErrorObserver* error_observer = new MockErrorObserver; + ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); + + host_resolver.rules()->AddRuleForAddressFamily( + "host1", ADDRESS_FAMILY_IPV4, "166.155.144.44"); + host_resolver.rules()->AddIPLiteralRule("host1", "::1,192.168.1.1", ""); + host_resolver.rules()->AddSimulatedFailure("host2"); + host_resolver.rules()->AddRule("host3", "166.155.144.33"); + host_resolver.rules()->AddRule("host5", "166.155.144.55"); + host_resolver.rules()->AddSimulatedFailure("host6"); + host_resolver.rules()->AddRuleForAddressFamily( + "*", ADDRESS_FAMILY_IPV4, "122.133.144.155"); + host_resolver.rules()->AddRule("*", "133.122.100.200"); + + InitResolver(&resolver, "dns.js"); + + TestCompletionCallback callback; + ProxyInfo proxy_info; + + int rv = resolver.GetProxyForURL( + GURL("http://foo/"), + &proxy_info, + callback.callback(), + NULL, + request_log.bound()); + + EXPECT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(OK, callback.WaitForResult()); + + // The test does 13 DNS resolution, however only 7 of them are unique. + EXPECT_EQ(7u, host_resolver.num_resolve()); + + const char* kExpectedResult = + "122.133.144.155-" // myIpAddress() + "null-" // dnsResolve('') + "__1_192.168.1.1-" // dnsResolveEx('host1') + "null-" // dnsResolve('host2') + "166.155.144.33-" // dnsResolve('host3') + "122.133.144.155-" // myIpAddress() + "166.155.144.33-" // dnsResolve('host3') + "__1_192.168.1.1-" // dnsResolveEx('host1') + "122.133.144.155-" // myIpAddress() + "null-" // dnsResolve('host2') + "-" // dnsResolveEx('host6') + "133.122.100.200-" // myIpAddressEx() + "166.155.144.44" // dnsResolve('host1') + ":99"; + + EXPECT_EQ(kExpectedResult, proxy_info.proxy_server().ToURI()); + + // No errors. + EXPECT_EQ("", error_observer->GetOutput()); + + // Check the NetLogs -- the script generated 1 alert, mirrored to both + // the per-request and global logs. + CapturingNetLog::CapturedEntryList entries_list[2]; + log.GetEntries(&entries_list[0]); + request_log.GetEntries(&entries_list[1]); + + for (size_t list_i = 0; list_i < arraysize(entries_list); list_i++) { + const CapturingNetLog::CapturedEntryList& entries = entries_list[list_i]; + EXPECT_EQ(1u, entries.size()); + EXPECT_TRUE( + LogContainsEvent(entries, 0, NetLog::TYPE_PAC_JAVASCRIPT_ALERT, + NetLog::PHASE_NONE)); + EXPECT_EQ("{\"message\":\"iteration: 7\"}", entries[0].GetParamsJson()); + } +} + +// This test runs a PAC script that does "myIpAddress()" followed by +// "dnsResolve()". This requires 2 restarts. However once the HostResolver's +// cache is warmed, subsequent calls should take 0 restarts. +TEST_F(ProxyResolverV8TracingTest, DnsChecksCache) { + CapturingNetLog log; + CapturingBoundNetLog request_log; + MockCachingHostResolver host_resolver; + MockErrorObserver* error_observer = new MockErrorObserver; + ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); + + host_resolver.rules()->AddRule("foopy", "166.155.144.11"); + host_resolver.rules()->AddRule("*", "122.133.144.155"); + + InitResolver(&resolver, "simple_dns.js"); + + TestCompletionCallback callback1; + TestCompletionCallback callback2; + ProxyInfo proxy_info; + + int rv = resolver.GetProxyForURL( + GURL("http://foopy/req1"), + &proxy_info, + callback1.callback(), + NULL, + request_log.bound()); + + EXPECT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(OK, callback1.WaitForResult()); + + // The test does 2 DNS resolutions. + EXPECT_EQ(2u, host_resolver.num_resolve()); + + // The first request took 2 restarts, hence on g_iteration=3. + EXPECT_EQ("166.155.144.11:3", proxy_info.proxy_server().ToURI()); + + rv = resolver.GetProxyForURL( + GURL("http://foopy/req2"), + &proxy_info, + callback2.callback(), + NULL, + request_log.bound()); + + EXPECT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(OK, callback2.WaitForResult()); + + EXPECT_EQ(4u, host_resolver.num_resolve()); + + // This time no restarts were required, so g_iteration incremented by 1. + EXPECT_EQ("166.155.144.11:4", proxy_info.proxy_server().ToURI()); + + // No errors. + EXPECT_EQ("", error_observer->GetOutput()); + + EXPECT_EQ(0u, log.GetSize()); + EXPECT_EQ(0u, request_log.GetSize()); +} + +// This test runs a weird PAC script that was designed to defeat the DNS tracing +// optimization. The proxy resolver should detect the inconsistency and +// fall-back to synchronous mode execution. +TEST_F(ProxyResolverV8TracingTest, FallBackToSynchronous1) { + CapturingNetLog log; + CapturingBoundNetLog request_log; + MockCachingHostResolver host_resolver; + MockErrorObserver* error_observer = new MockErrorObserver; + ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); + + host_resolver.rules()->AddRule("host1", "166.155.144.11"); + host_resolver.rules()->AddRule("crazy4", "133.199.111.4"); + host_resolver.rules()->AddRule("*", "122.133.144.155"); + + InitResolver(&resolver, "global_sideffects1.js"); + + TestCompletionCallback callback; + ProxyInfo proxy_info; + + int rv = resolver.GetProxyForURL( + GURL("http://foo/"), &proxy_info, callback.callback(), NULL, + request_log.bound()); + EXPECT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(OK, callback.WaitForResult()); + + // The script itself only does 2 DNS resolves per execution, however it + // constructs the hostname using a global counter which changes on each + // invocation. + EXPECT_EQ(3u, host_resolver.num_resolve()); + + EXPECT_EQ("166.155.144.11-133.199.111.4:100", + proxy_info.proxy_server().ToURI()); + + // No errors. + EXPECT_EQ("", error_observer->GetOutput()); + + // Check the NetLogs -- the script generated 1 alert, mirrored to both + // the per-request and global logs. + CapturingNetLog::CapturedEntryList entries_list[2]; + log.GetEntries(&entries_list[0]); + request_log.GetEntries(&entries_list[1]); + + for (size_t list_i = 0; list_i < arraysize(entries_list); list_i++) { + const CapturingNetLog::CapturedEntryList& entries = entries_list[list_i]; + EXPECT_EQ(1u, entries.size()); + EXPECT_TRUE( + LogContainsEvent(entries, 0, NetLog::TYPE_PAC_JAVASCRIPT_ALERT, + NetLog::PHASE_NONE)); + EXPECT_EQ("{\"message\":\"iteration: 4\"}", entries[0].GetParamsJson()); + } +} + +// This test runs a weird PAC script that was designed to defeat the DNS tracing +// optimization. The proxy resolver should detect the inconsistency and +// fall-back to synchronous mode execution. +TEST_F(ProxyResolverV8TracingTest, FallBackToSynchronous2) { + CapturingNetLog log; + CapturingBoundNetLog request_log; + MockCachingHostResolver host_resolver; + MockErrorObserver* error_observer = new MockErrorObserver; + ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); + + host_resolver.rules()->AddRule("host1", "166.155.144.11"); + host_resolver.rules()->AddRule("host2", "166.155.144.22"); + host_resolver.rules()->AddRule("host3", "166.155.144.33"); + host_resolver.rules()->AddRule("host4", "166.155.144.44"); + host_resolver.rules()->AddRule("*", "122.133.144.155"); + + InitResolver(&resolver, "global_sideffects2.js"); + + TestCompletionCallback callback; + ProxyInfo proxy_info; + + int rv = resolver.GetProxyForURL( + GURL("http://foo/"), &proxy_info, callback.callback(), NULL, + request_log.bound()); + EXPECT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(OK, callback.WaitForResult()); + + EXPECT_EQ(3u, host_resolver.num_resolve()); + + EXPECT_EQ("166.155.144.44:100", proxy_info.proxy_server().ToURI()); + + // No errors. + EXPECT_EQ("", error_observer->GetOutput()); + + // Check the NetLogs -- nothing was logged. + EXPECT_EQ(0u, log.GetSize()); + EXPECT_EQ(0u, request_log.GetSize()); +} + +// This test runs a weird PAC script that yields a never ending sequence +// of DNS resolves when restarting. Running it will hit the maximum +// DNS resolves per request limit (20) after which every DNS resolve will +// fail. +TEST_F(ProxyResolverV8TracingTest, InfiniteDNSSequence) { + CapturingNetLog log; + CapturingBoundNetLog request_log; + MockCachingHostResolver host_resolver; + MockErrorObserver* error_observer = new MockErrorObserver; + ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); + + host_resolver.rules()->AddRule("host*", "166.155.144.11"); + host_resolver.rules()->AddRule("*", "122.133.144.155"); + + InitResolver(&resolver, "global_sideffects3.js"); + + TestCompletionCallback callback; + ProxyInfo proxy_info; + + int rv = resolver.GetProxyForURL( + GURL("http://foo/"), &proxy_info, callback.callback(), NULL, + request_log.bound()); + EXPECT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(OK, callback.WaitForResult()); + + EXPECT_EQ(20u, host_resolver.num_resolve()); + + EXPECT_EQ( + "166.155.144.11-166.155.144.11-166.155.144.11-166.155.144.11-" + "166.155.144.11-166.155.144.11-166.155.144.11-166.155.144.11-" + "166.155.144.11-166.155.144.11-166.155.144.11-166.155.144.11-" + "166.155.144.11-166.155.144.11-166.155.144.11-166.155.144.11-" + "166.155.144.11-166.155.144.11-166.155.144.11-166.155.144.11-" + "null:21", proxy_info.proxy_server().ToURI()); + + // No errors. + EXPECT_EQ("", error_observer->GetOutput()); + + // Check the NetLogs -- 1 alert was logged. + EXPECT_EQ(1u, log.GetSize()); + EXPECT_EQ(1u, request_log.GetSize()); +} + +// This test runs a weird PAC script that yields a never ending sequence +// of DNS resolves when restarting. Running it will hit the maximum +// DNS resolves per request limit (20) after which every DNS resolve will +// fail. +TEST_F(ProxyResolverV8TracingTest, InfiniteDNSSequence2) { + CapturingNetLog log; + CapturingBoundNetLog request_log; + MockCachingHostResolver host_resolver; + MockErrorObserver* error_observer = new MockErrorObserver; + ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); + + host_resolver.rules()->AddRule("host*", "166.155.144.11"); + host_resolver.rules()->AddRule("*", "122.133.144.155"); + + InitResolver(&resolver, "global_sideffects4.js"); + + TestCompletionCallback callback; + ProxyInfo proxy_info; + + int rv = resolver.GetProxyForURL( + GURL("http://foo/"), &proxy_info, callback.callback(), NULL, + request_log.bound()); + EXPECT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(OK, callback.WaitForResult()); + + EXPECT_EQ(20u, host_resolver.num_resolve()); + + EXPECT_EQ("null21:34", proxy_info.proxy_server().ToURI()); + + // No errors. + EXPECT_EQ("", error_observer->GetOutput()); + + // Check the NetLogs -- 1 alert was logged. + EXPECT_EQ(1u, log.GetSize()); + EXPECT_EQ(1u, request_log.GetSize()); +} + +// Tests a PAC script which does DNS resolves during initialization. +TEST_F(ProxyResolverV8TracingTest, DnsDuringInit) { + CapturingNetLog log; + CapturingBoundNetLog request_log; + MockCachingHostResolver host_resolver; + MockErrorObserver* error_observer = new MockErrorObserver; + ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); + + host_resolver.rules()->AddRule("host1", "91.13.12.1"); + host_resolver.rules()->AddRule("host2", "91.13.12.2"); + + InitResolver(&resolver, "dns_during_init.js"); + + // Initialization did 2 dnsResolves. + EXPECT_EQ(2u, host_resolver.num_resolve()); + + host_resolver.rules()->ClearRules(); + host_resolver.GetHostCache()->clear(); + + host_resolver.rules()->AddRule("host1", "145.88.13.3"); + host_resolver.rules()->AddRule("host2", "137.89.8.45"); + + TestCompletionCallback callback; + ProxyInfo proxy_info; + + int rv = resolver.GetProxyForURL( + GURL("http://foo/"), &proxy_info, callback.callback(), NULL, + request_log.bound()); + EXPECT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(OK, callback.WaitForResult()); + + // Fetched host1 and host2 again, since the ones done during initialization + // should not have been cached. + EXPECT_EQ(4u, host_resolver.num_resolve()); + + EXPECT_EQ("91.13.12.1-91.13.12.2-145.88.13.3-137.89.8.45:99", + proxy_info.proxy_server().ToURI()); + + // Check the NetLogs -- the script generated 2 alerts during initialization. + EXPECT_EQ(0u, request_log.GetSize()); + CapturingNetLog::CapturedEntryList entries; + log.GetEntries(&entries); + + ASSERT_EQ(2u, entries.size()); + EXPECT_TRUE( + LogContainsEvent(entries, 0, NetLog::TYPE_PAC_JAVASCRIPT_ALERT, + NetLog::PHASE_NONE)); + EXPECT_TRUE( + LogContainsEvent(entries, 1, NetLog::TYPE_PAC_JAVASCRIPT_ALERT, + NetLog::PHASE_NONE)); + + EXPECT_EQ("{\"message\":\"Watsup\"}", entries[0].GetParamsJson()); + EXPECT_EQ("{\"message\":\"Watsup2\"}", entries[1].GetParamsJson()); +} + +void CrashCallback(int) { + // Be extra sure that if the callback ever gets invoked, the test will fail. + CHECK(false); +} + +// Start some requests, cancel them all, and then destroy the resolver. +// Note the execution order for this test can vary. Since multiple +// threads are involved, the cancellation may be received a different +// times. +TEST_F(ProxyResolverV8TracingTest, CancelAll) { + MockCachingHostResolver host_resolver; + MockErrorObserver* error_observer = new MockErrorObserver; + ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL); + + host_resolver.rules()->AddSimulatedFailure("*"); + + InitResolver(&resolver, "dns.js"); + + const size_t kNumRequests = 5; + ProxyInfo proxy_info[kNumRequests]; + ProxyResolver::RequestHandle request[kNumRequests]; + + for (size_t i = 0; i < kNumRequests; ++i) { + int rv = resolver.GetProxyForURL( + GURL("http://foo/"), &proxy_info[i], + base::Bind(&CrashCallback), &request[i], BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + } + + for (size_t i = 0; i < kNumRequests; ++i) { + resolver.CancelRequest(request[i]); + } +} + +// Note the execution order for this test can vary. Since multiple +// threads are involved, the cancellation may be received a different +// times. +TEST_F(ProxyResolverV8TracingTest, CancelSome) { + MockCachingHostResolver host_resolver; + MockErrorObserver* error_observer = new MockErrorObserver; + ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL); + + host_resolver.rules()->AddSimulatedFailure("*"); + + InitResolver(&resolver, "dns.js"); + + ProxyInfo proxy_info1; + ProxyInfo proxy_info2; + ProxyResolver::RequestHandle request1; + ProxyResolver::RequestHandle request2; + TestCompletionCallback callback; + + int rv = resolver.GetProxyForURL( + GURL("http://foo/"), &proxy_info1, + base::Bind(&CrashCallback), &request1, BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = resolver.GetProxyForURL( + GURL("http://foo/"), &proxy_info2, + callback.callback(), &request2, BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + resolver.CancelRequest(request1); + + EXPECT_EQ(OK, callback.WaitForResult()); +} + +// Cancel a request after it has finished running on the worker thread, and has +// posted a task the completion task back to origin thread. +TEST_F(ProxyResolverV8TracingTest, CancelWhilePendingCompletionTask) { + MockCachingHostResolver host_resolver; + MockErrorObserver* error_observer = new MockErrorObserver; + ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL); + + host_resolver.rules()->AddSimulatedFailure("*"); + + InitResolver(&resolver, "error.js"); + + ProxyInfo proxy_info1; + ProxyInfo proxy_info2; + ProxyInfo proxy_info3; + ProxyResolver::RequestHandle request1; + ProxyResolver::RequestHandle request2; + ProxyResolver::RequestHandle request3; + TestCompletionCallback callback; + + int rv = resolver.GetProxyForURL( + GURL("http://foo/"), &proxy_info1, + base::Bind(&CrashCallback), &request1, BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = resolver.GetProxyForURL( + GURL("http://throw-an-error/"), &proxy_info2, + callback.callback(), &request2, BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + // Wait until the first request has finished running on the worker thread. + // (The second request will output an error). + error_observer->WaitForOutput(); + + // Cancel the first request, while it has a pending completion task on + // the origin thread. + resolver.CancelRequest(request1); + + EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, callback.WaitForResult()); + + // Start another request, to make sure it is able to complete. + rv = resolver.GetProxyForURL( + GURL("http://i-have-no-idea-what-im-doing/"), &proxy_info3, + callback.callback(), &request3, BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + EXPECT_EQ(OK, callback.WaitForResult()); + + EXPECT_EQ("i-approve-this-message:42", + proxy_info3.proxy_server().ToURI()); +} + +// This implementation of HostResolver allows blocking until a resolve request +// has been received. The resolve requests it receives will never be completed. +class BlockableHostResolver : public HostResolver { + public: + BlockableHostResolver() + : num_cancelled_requests_(0), waiting_for_resolve_(false) {} + + virtual int Resolve(const RequestInfo& info, + AddressList* addresses, + const CompletionCallback& callback, + RequestHandle* out_req, + const BoundNetLog& net_log) OVERRIDE { + EXPECT_FALSE(callback.is_null()); + EXPECT_TRUE(out_req); + *out_req = reinterpret_cast<RequestHandle*>(1); // Magic value. + + if (!action_.is_null()) + action_.Run(); + + // Indicate to the caller that a request was received. + EXPECT_TRUE(waiting_for_resolve_); + MessageLoop::current()->Quit(); + + // Return ERR_IO_PENDING as this request will NEVER be completed. + // Expectation is for the caller to later cancel the request. + return ERR_IO_PENDING; + } + + virtual int ResolveFromCache(const RequestInfo& info, + AddressList* addresses, + const BoundNetLog& net_log) OVERRIDE { + NOTREACHED(); + return ERR_DNS_CACHE_MISS; + } + + virtual void CancelRequest(RequestHandle req) OVERRIDE { + EXPECT_EQ(reinterpret_cast<RequestHandle*>(1), req); + num_cancelled_requests_++; + } + + void SetAction(const base::Callback<void(void)>& action) { + action_ = action; + } + + // Waits until Resolve() has been called. + void WaitUntilRequestIsReceived() { + waiting_for_resolve_ = true; + MessageLoop::current()->Run(); + DCHECK(waiting_for_resolve_); + waiting_for_resolve_ = false; + } + + int num_cancelled_requests() const { + return num_cancelled_requests_; + } + + private: + int num_cancelled_requests_; + bool waiting_for_resolve_; + base::Callback<void(void)> action_; +}; + +// This cancellation test exercises a more predictable cancellation codepath -- +// when the request has an outstanding DNS request in flight. +TEST_F(ProxyResolverV8TracingTest, CancelWhileOutstandingNonBlockingDns) { + BlockableHostResolver host_resolver; + MockErrorObserver* error_observer = new MockErrorObserver; + ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL); + + InitResolver(&resolver, "dns.js"); + + ProxyInfo proxy_info1; + ProxyInfo proxy_info2; + ProxyResolver::RequestHandle request1; + ProxyResolver::RequestHandle request2; + + int rv = resolver.GetProxyForURL( + GURL("http://foo/req1"), &proxy_info1, + base::Bind(&CrashCallback), &request1, BoundNetLog()); + + EXPECT_EQ(ERR_IO_PENDING, rv); + + host_resolver.WaitUntilRequestIsReceived(); + + rv = resolver.GetProxyForURL( + GURL("http://foo/req2"), &proxy_info2, + base::Bind(&CrashCallback), &request2, BoundNetLog()); + + EXPECT_EQ(ERR_IO_PENDING, rv); + + host_resolver.WaitUntilRequestIsReceived(); + + resolver.CancelRequest(request1); + resolver.CancelRequest(request2); + + EXPECT_EQ(2, host_resolver.num_cancelled_requests()); + + // After leaving this scope, the ProxyResolver is destroyed. + // This should not cause any problems, as the outstanding work + // should have been cancelled. +} + +// In non-blocking mode, the worker thread actually does block for +// a short time to see if the result is in the DNS cache. Test +// cancellation while the worker thread is waiting on this event. +TEST_F(ProxyResolverV8TracingTest, CancelWhileBlockedInNonBlockingDns) { + BlockableHostResolver host_resolver; + MockErrorObserver* error_observer = new MockErrorObserver; + ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL); + + InitResolver(&resolver, "dns.js"); + + ProxyInfo proxy_info; + ProxyResolver::RequestHandle request; + + int rv = resolver.GetProxyForURL( + GURL("http://foo/"), &proxy_info, + base::Bind(&CrashCallback), &request, BoundNetLog()); + + EXPECT_EQ(ERR_IO_PENDING, rv); + + host_resolver.SetAction(base::Bind(&ProxyResolverV8Tracing::CancelRequest, + base::Unretained(&resolver), request)); + + host_resolver.WaitUntilRequestIsReceived(); + + // At this point the host resolver ran Resolve(), and should have cancelled + // the request. + + EXPECT_EQ(1, host_resolver.num_cancelled_requests()); +} + +// Cancel the request while there is a pending DNS request, however before +// the request is sent to the host resolver. +TEST_F(ProxyResolverV8TracingTest, CancelWhileBlockedInNonBlockingDns2) { + MockCachingHostResolver host_resolver; + MockErrorObserver* error_observer = new MockErrorObserver; + ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL); + + InitResolver(&resolver, "dns.js"); + + ProxyInfo proxy_info; + ProxyResolver::RequestHandle request; + + int rv = resolver.GetProxyForURL( + GURL("http://foo/"), &proxy_info, + base::Bind(&CrashCallback), &request, BoundNetLog()); + + EXPECT_EQ(ERR_IO_PENDING, rv); + + // Wait a bit, so the DNS task has hopefully been posted. The test will + // work whatever the delay is here, but it is most useful if the delay + // is large enough to allow a task to be posted back. + base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10)); + resolver.CancelRequest(request); + + EXPECT_EQ(0u, host_resolver.num_resolve()); +} + +TEST_F(ProxyResolverV8TracingTest, CancelSetPacWhileOutstandingBlockingDns) { + BlockableHostResolver host_resolver; + MockErrorObserver* error_observer = new MockErrorObserver; + + ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL); + + int rv = + resolver.SetPacScript(LoadScriptData("dns_during_init.js"), + base::Bind(&CrashCallback)); + EXPECT_EQ(ERR_IO_PENDING, rv); + + host_resolver.WaitUntilRequestIsReceived(); + + resolver.CancelSetPacScript(); + EXPECT_EQ(1, host_resolver.num_cancelled_requests()); +} + +} // namespace + +} // namespace net diff --git a/net/proxy/proxy_resolver_v8_unittest.cc b/net/proxy/proxy_resolver_v8_unittest.cc index b93d4f3..41683a2 100644 --- a/net/proxy/proxy_resolver_v8_unittest.cc +++ b/net/proxy/proxy_resolver_v8_unittest.cc @@ -12,7 +12,6 @@ #include "net/base/net_errors.h" #include "net/base/net_log_unittest.h" #include "net/proxy/proxy_info.h" -#include "net/proxy/proxy_resolver_js_bindings.h" #include "net/proxy/proxy_resolver_v8.h" #include "testing/gtest/include/gtest/gtest.h" @@ -22,7 +21,7 @@ namespace { // Javascript bindings for ProxyResolverV8, which returns mock values. // Each time one of the bindings is called into, we push the input into a // list, for later verification. -class MockJSBindings : public ProxyResolverJSBindings { +class MockJSBindings : public ProxyResolverV8::JSBindings { public: MockJSBindings() : my_ip_address_count(0), my_ip_address_ex_count(0) {} @@ -31,30 +30,35 @@ class MockJSBindings : public ProxyResolverJSBindings { alerts.push_back(UTF16ToUTF8(message)); } - virtual bool MyIpAddress(std::string* ip_address) OVERRIDE { - my_ip_address_count++; - *ip_address = my_ip_address_result; - return !my_ip_address_result.empty(); - } + virtual bool ResolveDns(const std::string& host, + ResolveDnsOperation op, + std::string* output) OVERRIDE { + if (op == MY_IP_ADDRESS) { + my_ip_address_count++; + *output = my_ip_address_result; + return !my_ip_address_result.empty(); + } - virtual bool MyIpAddressEx(std::string* ip_address_list) OVERRIDE { - my_ip_address_ex_count++; - *ip_address_list = my_ip_address_ex_result; - return !my_ip_address_ex_result.empty(); - } + if (op == MY_IP_ADDRESS_EX) { + my_ip_address_ex_count++; + *output = my_ip_address_ex_result; + return !my_ip_address_ex_result.empty(); + } - virtual bool DnsResolve(const std::string& host, std::string* ip_address) - OVERRIDE { - dns_resolves.push_back(host); - *ip_address = dns_resolve_result; - return !dns_resolve_result.empty(); - } + if (op == DNS_RESOLVE) { + dns_resolves.push_back(host); + *output = dns_resolve_result; + return !dns_resolve_result.empty(); + } - virtual bool DnsResolveEx(const std::string& host, - std::string* ip_address_list) OVERRIDE { - dns_resolves_ex.push_back(host); - *ip_address_list = dns_resolve_ex_result; - return !dns_resolve_ex_result.empty(); + if (op == DNS_RESOLVE_EX) { + dns_resolves_ex.push_back(host); + *output = dns_resolve_ex_result; + return !dns_resolve_ex_result.empty(); + } + + CHECK(false); + return false; } virtual void OnError(int line_number, const string16& message) OVERRIDE { @@ -65,8 +69,6 @@ class MockJSBindings : public ProxyResolverJSBindings { errors_line_number.push_back(line_number); } - virtual void Shutdown() OVERRIDE {} - // Mock values to return. std::string my_ip_address_result; std::string my_ip_address_ex_result; @@ -88,10 +90,12 @@ class MockJSBindings : public ProxyResolverJSBindings { // disk. class ProxyResolverV8WithMockBindings : public ProxyResolverV8 { public: - ProxyResolverV8WithMockBindings() : ProxyResolverV8(new MockJSBindings()) {} + ProxyResolverV8WithMockBindings() { + set_js_bindings(&mock_js_bindings_); + } - MockJSBindings* mock_js_bindings() const { - return reinterpret_cast<MockJSBindings*>(js_bindings()); + MockJSBindings* mock_js_bindings() { + return &mock_js_bindings_; } // Initialize with the PAC script data at |filename|. @@ -117,13 +121,15 @@ class ProxyResolverV8WithMockBindings : public ProxyResolverV8 { return SetPacScript(ProxyResolverScriptData::FromUTF8(file_contents), CompletionCallback()); } + + private: + MockJSBindings mock_js_bindings_; }; // Doesn't really matter what these values are for many of the tests. const GURL kQueryUrl("http://www.google.com"); const GURL kPacUrl; - TEST(ProxyResolverV8Test, Direct) { ProxyResolverV8WithMockBindings resolver; int result = resolver.SetPacScriptFromDisk("direct.js"); diff --git a/net/proxy/proxy_resolver_winhttp.cc b/net/proxy/proxy_resolver_winhttp.cc index 1e14fbb..1ddc8ca 100644 --- a/net/proxy/proxy_resolver_winhttp.cc +++ b/net/proxy/proxy_resolver_winhttp.cc @@ -128,11 +128,6 @@ LoadState ProxyResolverWinHttp::GetLoadState(RequestHandle request) const { return LOAD_STATE_IDLE; } -LoadState ProxyResolverWinHttp::GetLoadStateThreadSafe( - RequestHandle request) const { - return LOAD_STATE_IDLE; -} - void ProxyResolverWinHttp::CancelSetPacScript() { NOTREACHED(); } diff --git a/net/proxy/proxy_resolver_winhttp.h b/net/proxy/proxy_resolver_winhttp.h index e92827a..047fbf5 100644 --- a/net/proxy/proxy_resolver_winhttp.h +++ b/net/proxy/proxy_resolver_winhttp.h @@ -30,9 +30,6 @@ class NET_EXPORT_PRIVATE ProxyResolverWinHttp : public ProxyResolver { virtual LoadState GetLoadState(RequestHandle request) const OVERRIDE; - virtual LoadState GetLoadStateThreadSafe( - RequestHandle request) const OVERRIDE; - virtual void CancelSetPacScript() OVERRIDE; virtual int SetPacScript( diff --git a/net/proxy/proxy_service.cc b/net/proxy/proxy_service.cc index 181441d..8cfa1f3 100644 --- a/net/proxy/proxy_service.cc +++ b/net/proxy/proxy_service.cc @@ -28,7 +28,6 @@ #include "net/proxy/proxy_resolver.h" #include "net/proxy/proxy_script_decider.h" #include "net/proxy/proxy_script_fetcher.h" -#include "net/proxy/sync_host_resolver_bridge.h" #include "net/url_request/url_request_context.h" #if defined(OS_WIN) @@ -195,12 +194,6 @@ class ProxyResolverNull : public ProxyResolver { return LOAD_STATE_IDLE; } - virtual LoadState GetLoadStateThreadSafe( - RequestHandle request) const OVERRIDE { - NOTREACHED(); - return LOAD_STATE_IDLE; - } - virtual void CancelSetPacScript() OVERRIDE { NOTREACHED(); } @@ -238,12 +231,6 @@ class ProxyResolverFromPacString : public ProxyResolver { return LOAD_STATE_IDLE; } - virtual LoadState GetLoadStateThreadSafe( - RequestHandle request) const OVERRIDE { - NOTREACHED(); - return LOAD_STATE_IDLE; - } - virtual void CancelSetPacScript() OVERRIDE { NOTREACHED(); } diff --git a/net/proxy/proxy_service_v8.cc b/net/proxy/proxy_service_v8.cc index 44ae82c..d5a3dd7 100644 --- a/net/proxy/proxy_service_v8.cc +++ b/net/proxy/proxy_service_v8.cc @@ -5,71 +5,16 @@ #include "net/proxy/proxy_service_v8.h" #include "base/logging.h" -#include "net/proxy/multi_threaded_proxy_resolver.h" #include "net/proxy/network_delegate_error_observer.h" #include "net/proxy/proxy_resolver.h" -#include "net/proxy/proxy_resolver_js_bindings.h" -#include "net/proxy/proxy_resolver_v8.h" +#include "net/proxy/proxy_resolver_v8_tracing.h" #include "net/proxy/proxy_service.h" -#include "net/proxy/sync_host_resolver_bridge.h" namespace net { -namespace { - -// This factory creates V8ProxyResolvers with appropriate javascript bindings. -class ProxyResolverFactoryForV8 : public ProxyResolverFactory { - public: - // |async_host_resolver|, |io_loop| and |net_log| must remain - // valid for the duration of our lifetime. - // |async_host_resolver| will only be operated on |io_loop|. - // TODO(willchan): remove io_loop and replace it with origin_loop. - ProxyResolverFactoryForV8(HostResolver* async_host_resolver, - MessageLoop* io_loop, - base::MessageLoopProxy* origin_loop, - NetLog* net_log, - NetworkDelegate* network_delegate) - : ProxyResolverFactory(true /*expects_pac_bytes*/), - async_host_resolver_(async_host_resolver), - io_loop_(io_loop), - origin_loop_(origin_loop), - net_log_(net_log), - network_delegate_(network_delegate) { - } - - virtual ProxyResolver* CreateProxyResolver() OVERRIDE { - // Create a synchronous host resolver wrapper that operates - // |async_host_resolver_| on |io_loop_|. - SyncHostResolverBridge* sync_host_resolver = - new SyncHostResolverBridge(async_host_resolver_, io_loop_); - - NetworkDelegateErrorObserver* error_observer = - new NetworkDelegateErrorObserver( - network_delegate_, origin_loop_.get()); - - // ProxyResolverJSBindings takes ownership of |error_observer| and - // |sync_host_resolver|. - ProxyResolverJSBindings* js_bindings = - ProxyResolverJSBindings::CreateDefault( - sync_host_resolver, net_log_, error_observer); - - // ProxyResolverV8 takes ownership of |js_bindings|. - return new ProxyResolverV8(js_bindings); - } - - private: - HostResolver* const async_host_resolver_; - MessageLoop* io_loop_; - scoped_refptr<base::MessageLoopProxy> origin_loop_; - NetLog* net_log_; - NetworkDelegate* network_delegate_; -}; - -} // namespace // static ProxyService* CreateProxyServiceUsingV8ProxyResolver( ProxyConfigService* proxy_config_service, - size_t num_pac_threads, ProxyScriptFetcher* proxy_script_fetcher, DhcpProxyScriptFetcher* dhcp_proxy_script_fetcher, HostResolver* host_resolver, @@ -80,19 +25,12 @@ ProxyService* CreateProxyServiceUsingV8ProxyResolver( DCHECK(dhcp_proxy_script_fetcher); DCHECK(host_resolver); - if (num_pac_threads == 0) - num_pac_threads = ProxyService::kDefaultNumPacThreads; - - ProxyResolverFactory* sync_resolver_factory = - new ProxyResolverFactoryForV8( - host_resolver, - MessageLoop::current(), - base::MessageLoopProxy::current(), - net_log, - network_delegate); + ProxyResolverErrorObserver* error_observer = + new NetworkDelegateErrorObserver( + network_delegate, base::MessageLoopProxy::current()); ProxyResolver* proxy_resolver = - new MultiThreadedProxyResolver(sync_resolver_factory, num_pac_threads); + new ProxyResolverV8Tracing(host_resolver, error_observer, net_log); ProxyService* proxy_service = new ProxyService(proxy_config_service, proxy_resolver, net_log); diff --git a/net/proxy/proxy_service_v8.h b/net/proxy/proxy_service_v8.h index 0ff574d..0e339eb 100644 --- a/net/proxy/proxy_service_v8.h +++ b/net/proxy/proxy_service_v8.h @@ -21,21 +21,6 @@ class ProxyService; // Creates a proxy service that polls |proxy_config_service| to notice when // the proxy settings change. We take ownership of |proxy_config_service|. // -// |num_pac_threads| specifies the maximum number of threads to use for -// executing PAC scripts. Threads are created lazily on demand. -// If |0| is specified, then a default number of threads will be selected. -// -// Having more threads avoids stalling proxy resolve requests when the -// PAC script takes a while to run. This is particularly a problem when PAC -// scripts do synchronous DNS resolutions, since that can take on the order -// of seconds. -// -// However, the disadvantages of using more than 1 thread are: -// (a) can cause compatibility issues for scripts that rely on side effects -// between runs (such scripts should not be common though). -// (b) increases the memory used by proxy resolving, as each thread will -// duplicate its own script context. - // |proxy_script_fetcher| specifies the dependency to use for downloading // any PAC scripts. The resulting ProxyService will take ownership of it. // @@ -54,7 +39,6 @@ class ProxyService; // ########################################################################## NET_EXPORT ProxyService* CreateProxyServiceUsingV8ProxyResolver( ProxyConfigService* proxy_config_service, - size_t num_pac_threads, ProxyScriptFetcher* proxy_script_fetcher, DhcpProxyScriptFetcher* dhcp_proxy_script_fetcher, HostResolver* host_resolver, diff --git a/net/proxy/sync_host_resolver.h b/net/proxy/sync_host_resolver.h deleted file mode 100644 index 153dfad..0000000 --- a/net/proxy/sync_host_resolver.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2012 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 NET_PROXY_SYNC_HOST_RESOLVER_H_ -#define NET_PROXY_SYNC_HOST_RESOLVER_H_ - -#include "net/base/host_resolver.h" -#include "net/base/net_export.h" - -namespace net { - -class BoundNetLog; - -// Interface used by ProxyResolverJSBindings to abstract a synchronous host -// resolver module (which includes a Shutdown method). -class NET_EXPORT_PRIVATE SyncHostResolver { - public: - virtual ~SyncHostResolver() {} - - virtual int Resolve(const HostResolver::RequestInfo& info, - AddressList* addresses, - const BoundNetLog& net_log) = 0; - - // Optionally aborts any blocking resolves that are in progress. - virtual void Shutdown() = 0; -}; - -} // namespace net - -#endif // NET_PROXY_SYNC_HOST_RESOLVER_H_ diff --git a/net/proxy/sync_host_resolver_bridge.cc b/net/proxy/sync_host_resolver_bridge.cc deleted file mode 100644 index 9f55524..0000000 --- a/net/proxy/sync_host_resolver_bridge.cc +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright (c) 2012 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 "net/proxy/sync_host_resolver_bridge.h" - -#include "base/bind.h" -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "base/message_loop.h" -#include "base/synchronization/lock.h" -#include "base/synchronization/waitable_event.h" -#include "net/base/net_errors.h" -#include "net/base/net_log.h" - -namespace net { - -// SyncHostResolverBridge::Core ---------------------------------------------- - -class SyncHostResolverBridge::Core - : public base::RefCountedThreadSafe<SyncHostResolverBridge::Core> { - public: - Core(HostResolver* resolver, MessageLoop* host_resolver_loop); - - int ResolveSynchronously(const HostResolver::RequestInfo& info, - AddressList* addresses, - const BoundNetLog& net_log); - - // Returns true if Shutdown() has been called. - bool HasShutdown() const { - base::AutoLock l(lock_); - return HasShutdownLocked(); - } - - // Called on |host_resolver_loop_|. - void Shutdown(); - - private: - friend class base::RefCountedThreadSafe<SyncHostResolverBridge::Core>; - ~Core() {} - - bool HasShutdownLocked() const { - return has_shutdown_; - } - - // Called on |host_resolver_loop_|. - void StartResolve(const HostResolver::RequestInfo& info, - AddressList* addresses, - const BoundNetLog& net_log); - - // Called on |host_resolver_loop_|. - void OnResolveCompletion(int result); - - // Not called on |host_resolver_loop_|. - int WaitForResolveCompletion(); - - HostResolver* const host_resolver_; - MessageLoop* const host_resolver_loop_; - // The result from the current request (set on |host_resolver_loop_|). - int err_; - // The currently outstanding request to |host_resolver_|, or NULL. - HostResolver::RequestHandle outstanding_request_; - - // Event to notify completion of resolve request. We always Signal() on - // |host_resolver_loop_| and Wait() on a different thread. - base::WaitableEvent event_; - - // True if Shutdown() has been called. Must hold |lock_| to access it. - bool has_shutdown_; - - // Mutex to guard accesses to |has_shutdown_|. - mutable base::Lock lock_; - - DISALLOW_COPY_AND_ASSIGN(Core); -}; - -SyncHostResolverBridge::Core::Core(HostResolver* host_resolver, - MessageLoop* host_resolver_loop) - : host_resolver_(host_resolver), - host_resolver_loop_(host_resolver_loop), - err_(0), - outstanding_request_(NULL), - event_(true, false), - has_shutdown_(false) {} - -int SyncHostResolverBridge::Core::ResolveSynchronously( - const HostResolver::RequestInfo& info, - net::AddressList* addresses, - const BoundNetLog& net_log) { - // Otherwise start an async resolve on the resolver's thread. - host_resolver_loop_->PostTask( - FROM_HERE, - base::Bind(&Core::StartResolve, this, info, addresses, net_log)); - - return WaitForResolveCompletion(); -} - -void SyncHostResolverBridge::Core::StartResolve( - const HostResolver::RequestInfo& info, - net::AddressList* addresses, - const BoundNetLog& net_log) { - DCHECK_EQ(MessageLoop::current(), host_resolver_loop_); - DCHECK(!outstanding_request_); - - if (HasShutdown()) - return; - - int error = host_resolver_->Resolve( - info, addresses, base::Bind(&Core::OnResolveCompletion, this), - &outstanding_request_, net_log); - if (error != ERR_IO_PENDING) - OnResolveCompletion(error); // Completed synchronously. -} - -void SyncHostResolverBridge::Core::OnResolveCompletion(int result) { - DCHECK_EQ(MessageLoop::current(), host_resolver_loop_); - err_ = result; - outstanding_request_ = NULL; - event_.Signal(); -} - -int SyncHostResolverBridge::Core::WaitForResolveCompletion() { - DCHECK_NE(MessageLoop::current(), host_resolver_loop_); - event_.Wait(); - - { - base::AutoLock l(lock_); - if (HasShutdownLocked()) - return ERR_ABORTED; - event_.Reset(); - } - - return err_; -} - -void SyncHostResolverBridge::Core::Shutdown() { - DCHECK_EQ(MessageLoop::current(), host_resolver_loop_); - - if (outstanding_request_) { - host_resolver_->CancelRequest(outstanding_request_); - outstanding_request_ = NULL; - } - - { - base::AutoLock l(lock_); - has_shutdown_ = true; - } - - // Wake up the PAC thread in case it was waiting for resolve completion. - event_.Signal(); -} - -// SyncHostResolverBridge ----------------------------------------------------- - -SyncHostResolverBridge::SyncHostResolverBridge(HostResolver* host_resolver, - MessageLoop* host_resolver_loop) - : host_resolver_loop_(host_resolver_loop), - core_(new Core(host_resolver, host_resolver_loop)) { - DCHECK(host_resolver_loop_); -} - -SyncHostResolverBridge::~SyncHostResolverBridge() { - DCHECK(core_->HasShutdown()); -} - -int SyncHostResolverBridge::Resolve(const HostResolver::RequestInfo& info, - AddressList* addresses, - const BoundNetLog& net_log) { - return core_->ResolveSynchronously(info, addresses, net_log); -} - -void SyncHostResolverBridge::Shutdown() { - DCHECK_EQ(MessageLoop::current(), host_resolver_loop_); - core_->Shutdown(); -} - -} // namespace net diff --git a/net/proxy/sync_host_resolver_bridge.h b/net/proxy/sync_host_resolver_bridge.h deleted file mode 100644 index 1de67b5..0000000 --- a/net/proxy/sync_host_resolver_bridge.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2012 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 NET_PROXY_SYNC_HOST_RESOLVER_BRIDGE_H_ -#define NET_PROXY_SYNC_HOST_RESOLVER_BRIDGE_H_ - -#include "base/compiler_specific.h" -#include "base/memory/ref_counted.h" -#include "net/proxy/sync_host_resolver.h" - -class MessageLoop; - -namespace net { - -// Wrapper around HostResolver to give a sync API while running the resolver -// in async mode on |host_resolver_loop|. -class NET_EXPORT_PRIVATE SyncHostResolverBridge : public SyncHostResolver { - public: - SyncHostResolverBridge(HostResolver* host_resolver, - MessageLoop* host_resolver_loop); - - virtual ~SyncHostResolverBridge(); - - // SyncHostResolver methods: - virtual int Resolve(const HostResolver::RequestInfo& info, - AddressList* addresses, - const BoundNetLog& net_log) OVERRIDE; - - // The Shutdown() method should be called prior to destruction, from - // |host_resolver_loop_|. It aborts any in progress synchronous resolves, to - // prevent deadlocks from happening. - virtual void Shutdown() OVERRIDE; - - private: - class Core; - - MessageLoop* const host_resolver_loop_; - scoped_refptr<Core> core_; - DISALLOW_COPY_AND_ASSIGN(SyncHostResolverBridge); -}; - -} // namespace net - -#endif // NET_PROXY_SYNC_HOST_RESOLVER_BRIDGE_H_ diff --git a/net/proxy/sync_host_resolver_bridge_unittest.cc b/net/proxy/sync_host_resolver_bridge_unittest.cc deleted file mode 100644 index 81ec33e..0000000 --- a/net/proxy/sync_host_resolver_bridge_unittest.cc +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright (c) 2012 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 "net/proxy/sync_host_resolver_bridge.h" - -#include "base/threading/thread.h" -#include "base/synchronization/waitable_event.h" -#include "net/base/address_list.h" -#include "net/base/net_errors.h" -#include "net/base/net_log.h" -#include "net/proxy/multi_threaded_proxy_resolver.h" -#include "net/base/test_completion_callback.h" -#include "net/proxy/proxy_info.h" -#include "testing/gtest/include/gtest/gtest.h" - -// TODO(eroman): This test should be moved into -// multi_threaded_proxy_resolver_unittest.cc. - -namespace net { - -namespace { - -// This implementation of HostResolver allows blocking until a resolve request -// has been received. The resolve requests it receives will never be completed. -class BlockableHostResolver : public HostResolver { - public: - BlockableHostResolver() - : event_(true, false), - was_request_cancelled_(false) { - } - - virtual int Resolve(const RequestInfo& info, - AddressList* addresses, - const CompletionCallback& callback, - RequestHandle* out_req, - const BoundNetLog& net_log) OVERRIDE { - EXPECT_FALSE(callback.is_null()); - EXPECT_TRUE(out_req); - *out_req = reinterpret_cast<RequestHandle*>(1); // Magic value. - - // Indicate to the caller that a request was received. - event_.Signal(); - - // We return ERR_IO_PENDING, as this request will NEVER be completed. - // Expectation is for the caller to later cancel the request. - return ERR_IO_PENDING; - } - - virtual int ResolveFromCache(const RequestInfo& info, - AddressList* addresses, - const BoundNetLog& net_log) OVERRIDE { - NOTIMPLEMENTED(); - return ERR_UNEXPECTED; - } - - virtual void CancelRequest(RequestHandle req) OVERRIDE { - EXPECT_EQ(reinterpret_cast<RequestHandle*>(1), req); - was_request_cancelled_ = true; - } - - // Waits until Resolve() has been called. - void WaitUntilRequestIsReceived() { - event_.Wait(); - } - - bool was_request_cancelled() const { - return was_request_cancelled_; - } - - private: - // Event to notify when a resolve request was received. - base::WaitableEvent event_; - bool was_request_cancelled_; -}; - -// This implementation of ProxyResolver simply does a synchronous resolve -// on |host_resolver| in response to GetProxyForURL(). -class SyncProxyResolver : public ProxyResolver { - public: - explicit SyncProxyResolver(SyncHostResolverBridge* host_resolver) - : ProxyResolver(false), host_resolver_(host_resolver) {} - - virtual int GetProxyForURL(const GURL& url, - ProxyInfo* results, - const CompletionCallback& callback, - RequestHandle* request, - const BoundNetLog& net_log) { - EXPECT_FALSE(!callback.is_null()); - EXPECT_FALSE(request); - - // Do a synchronous host resolve. - HostResolver::RequestInfo info(HostPortPair::FromURL(url)); - AddressList addresses; - int rv = host_resolver_->Resolve(info, &addresses, net_log); - - EXPECT_EQ(ERR_ABORTED, rv); - - return rv; - } - - virtual void CancelRequest(RequestHandle request) OVERRIDE { - NOTREACHED(); - } - - virtual LoadState GetLoadState(RequestHandle request) const OVERRIDE { - NOTREACHED(); - return LOAD_STATE_IDLE; - } - - virtual LoadState GetLoadStateThreadSafe( - RequestHandle request) const OVERRIDE { - NOTREACHED(); - return LOAD_STATE_IDLE; - } - - virtual void Shutdown() OVERRIDE { - host_resolver_->Shutdown(); - } - - virtual void CancelSetPacScript() OVERRIDE { - NOTREACHED(); - } - - virtual int SetPacScript( - const scoped_refptr<ProxyResolverScriptData>& script_data, - const CompletionCallback& callback) OVERRIDE { - return OK; - } - - private: - SyncHostResolverBridge* const host_resolver_; -}; - -class SyncProxyResolverFactory : public ProxyResolverFactory { - public: - // Takes ownership of |sync_host_resolver|. - explicit SyncProxyResolverFactory(SyncHostResolverBridge* sync_host_resolver) - : ProxyResolverFactory(false), - sync_host_resolver_(sync_host_resolver) { - } - - virtual ProxyResolver* CreateProxyResolver() OVERRIDE { - return new SyncProxyResolver(sync_host_resolver_.get()); - } - - private: - const scoped_ptr<SyncHostResolverBridge> sync_host_resolver_; -}; - -// This helper thread is used to create the circumstances for the deadlock. -// It is analagous to the "IO thread" which would be main thread running the -// network stack. -class IOThread : public base::Thread { - public: - IOThread() : base::Thread("IO-thread") {} - - virtual ~IOThread() { - Stop(); - } - - BlockableHostResolver* async_resolver() { - return async_resolver_.get(); - } - - protected: - virtual void Init() OVERRIDE { - async_resolver_.reset(new BlockableHostResolver()); - - // Create a synchronous host resolver that operates the async host - // resolver on THIS thread. - SyncHostResolverBridge* sync_resolver = - new SyncHostResolverBridge(async_resolver_.get(), message_loop()); - - proxy_resolver_.reset( - new MultiThreadedProxyResolver( - new SyncProxyResolverFactory(sync_resolver), - 1u)); - - // Initialize the resolver. - TestCompletionCallback callback; - proxy_resolver_->SetPacScript(ProxyResolverScriptData::FromURL(GURL()), - callback.callback()); - EXPECT_EQ(OK, callback.WaitForResult()); - - // Start an asynchronous request to the proxy resolver - // (note that it will never complete). - proxy_resolver_->GetProxyForURL( - GURL("http://test/"), &results_, callback_.callback(), &request_, - BoundNetLog()); - } - - virtual void CleanUp() OVERRIDE { - // Cancel the outstanding request (note however that this will not - // unblock the PAC thread though). - proxy_resolver_->CancelRequest(request_); - - // Delete the single threaded proxy resolver. - proxy_resolver_.reset(); - - // (There may have been a completion posted back to origin thread, avoid - // leaking it by running). - MessageLoop::current()->RunUntilIdle(); - - // During the teardown sequence of the single threaded proxy resolver, - // the outstanding host resolve should have been cancelled. - EXPECT_TRUE(async_resolver_->was_request_cancelled()); - } - - private: - // This (async) host resolver will outlive the thread that is operating it - // synchronously. - scoped_ptr<BlockableHostResolver> async_resolver_; - - scoped_ptr<ProxyResolver> proxy_resolver_; - - // Data for the outstanding request to the single threaded proxy resolver. - TestCompletionCallback callback_; - ProxyInfo results_; - ProxyResolver::RequestHandle request_; -}; - -// Test that a deadlock does not happen during shutdown when a host resolve -// is outstanding on the SyncHostResolverBridge. -// This is a regression test for http://crbug.com/41244. -TEST(MultiThreadedProxyResolverTest, ShutdownIsCalledBeforeThreadJoin) { - IOThread io_thread; - base::Thread::Options options; - options.message_loop_type = MessageLoop::TYPE_IO; - ASSERT_TRUE(io_thread.StartWithOptions(options)); - - io_thread.async_resolver()->WaitUntilRequestIsReceived(); - - // Now upon exitting this scope, the IOThread is destroyed -- this will - // stop the IOThread, which will in turn delete the - // SingleThreadedProxyResolver, which in turn will stop its internal - // PAC thread (which is currently blocked waiting on the host resolve which - // is running on IOThread). The IOThread::Cleanup() will verify that after - // the PAC thread is stopped, it cancels the request on the HostResolver. -} - -} // namespace - -} // namespace net |