summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authoreroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-30 22:30:49 +0000
committereroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-30 22:30:49 +0000
commit501e2d4ea3c5d685fec8b34618a79964fe2945a7 (patch)
treea6c1251f903369a770f1adcaf99ebec972cb7b5c /net
parent616b701515db7fea8e86cf01688b8840238ecdad (diff)
downloadchromium_src-501e2d4ea3c5d685fec8b34618a79964fe2945a7.zip
chromium_src-501e2d4ea3c5d685fec8b34618a79964fe2945a7.tar.gz
chromium_src-501e2d4ea3c5d685fec8b34618a79964fe2945a7.tar.bz2
Improve performance of proxy resolver by tracing DNS dependencies.
This replaces the multi-threaded V8 proxy resolver implementation, with a faster single-threaded one. The single-threaded version uses some magic to avoid blocking on DNS dependencies, so it is able to handle more parallel requests than the multi-threaded one. Design document: https://docs.google.com/a/chromium.org/document/d/16Ij5OcVnR3s0MH4Z5XkhI9VTPoMJdaBn9rKreAmGOdE/edit This has the benefit of reducing the number of threads that Chrome uses for PAC evaluation, while at the same time speeding up proxy resolving for PAC scripts that do DNS resolving (due to better parallelism). I ran a benchmark to evaluate the effectiveness of this new approach. The benchmark simulates loading the http://www.newyorktimes.com webpage with slow DNS (where each DNS resolve takes 2 seconds), and a maximum DNS resolver parallelism of 10 requests. This webpage resolves the proxy for 221 URLs, across 40 unique hostnames. - Proxy resolving using the old multithreaded code took 24.076 seconds [*] - Proxy resolving using the new code took 8.011 seconds [*] - Without a PAC script, resolving the DNS took 8.003 seconds The new proxy resolving times (8.011s) are much closer to the theoretical best (8.003s)! [*] The PAC script I used for the test was a fairly complex script 20kb (a version of google's corp PAC script modified to always call dnsResolve(host)). I will be adding histograms in a follow-up CL, to measure how often requests need to be restarted, or fall-back to synchronous mode. BUG=119151 Review URL: https://codereview.chromium.org/11885009 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@179714 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/base/capturing_net_log.cc15
-rw-r--r--net/base/capturing_net_log.h4
-rw-r--r--net/base/mock_host_resolver.cc10
-rw-r--r--net/base/mock_host_resolver.h20
-rw-r--r--net/data/proxy_resolver_v8_tracing_unittest/dns.js31
-rw-r--r--net/data/proxy_resolver_v8_tracing_unittest/dns_during_init.js14
-rw-r--r--net/data/proxy_resolver_v8_tracing_unittest/error.js8
-rw-r--r--net/data/proxy_resolver_v8_tracing_unittest/global_sideffects1.js14
-rw-r--r--net/data/proxy_resolver_v8_tracing_unittest/global_sideffects2.js18
-rw-r--r--net/data/proxy_resolver_v8_tracing_unittest/global_sideffects3.js13
-rw-r--r--net/data/proxy_resolver_v8_tracing_unittest/global_sideffects4.js15
-rw-r--r--net/data/proxy_resolver_v8_tracing_unittest/simple.js3
-rw-r--r--net/data/proxy_resolver_v8_tracing_unittest/simple_dns.js8
-rw-r--r--net/data/proxy_resolver_v8_tracing_unittest/too_many_alerts.js13
-rw-r--r--net/data/proxy_resolver_v8_tracing_unittest/too_many_empty_alerts.js13
-rw-r--r--net/http/http_network_transaction_spdy2_unittest.cc6
-rw-r--r--net/http/http_network_transaction_spdy3_unittest.cc6
-rw-r--r--net/net.gyp12
-rw-r--r--net/net_unittests.isolate11
-rw-r--r--net/proxy/mock_proxy_resolver.cc6
-rw-r--r--net/proxy/mock_proxy_resolver.h2
-rw-r--r--net/proxy/multi_threaded_proxy_resolver.cc15
-rw-r--r--net/proxy/multi_threaded_proxy_resolver.h2
-rw-r--r--net/proxy/multi_threaded_proxy_resolver_unittest.cc12
-rw-r--r--net/proxy/proxy_resolver.h7
-rw-r--r--net/proxy/proxy_resolver_error_observer.h10
-rw-r--r--net/proxy/proxy_resolver_js_bindings.cc291
-rw-r--r--net/proxy/proxy_resolver_js_bindings.h92
-rw-r--r--net/proxy/proxy_resolver_js_bindings_unittest.cc365
-rw-r--r--net/proxy/proxy_resolver_mac.cc5
-rw-r--r--net/proxy/proxy_resolver_mac.h3
-rw-r--r--net/proxy/proxy_resolver_perftest.cc41
-rw-r--r--net/proxy/proxy_resolver_request_context.h31
-rw-r--r--net/proxy/proxy_resolver_v8.cc121
-rw-r--r--net/proxy/proxy_resolver_v8.h47
-rw-r--r--net/proxy/proxy_resolver_v8_tracing.cc984
-rw-r--r--net/proxy/proxy_resolver_v8_tracing.h85
-rw-r--r--net/proxy/proxy_resolver_v8_tracing_unittest.cc928
-rw-r--r--net/proxy/proxy_resolver_v8_unittest.cc64
-rw-r--r--net/proxy/proxy_resolver_winhttp.cc5
-rw-r--r--net/proxy/proxy_resolver_winhttp.h3
-rw-r--r--net/proxy/proxy_service.cc13
-rw-r--r--net/proxy/proxy_service_v8.cc72
-rw-r--r--net/proxy/proxy_service_v8.h16
-rw-r--r--net/proxy/sync_host_resolver.h31
-rw-r--r--net/proxy/sync_host_resolver_bridge.cc177
-rw-r--r--net/proxy/sync_host_resolver_bridge.h45
-rw-r--r--net/proxy/sync_host_resolver_bridge_unittest.cc244
48 files changed, 2341 insertions, 1610 deletions
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