1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/proxy/proxy_resolver_js_bindings.h"
8 #include "base/compiler_specific.h"
9 #include "base/logging.h"
10 #include "base/string_util.h"
11 #include "base/values.h"
12 #include "net/base/address_list.h"
13 #include "net/base/host_cache.h"
14 #include "net/base/host_resolver.h"
15 #include "net/base/net_errors.h"
16 #include "net/base/net_log.h"
17 #include "net/base/net_util.h"
18 #include "net/proxy/proxy_resolver_error_observer.h"
19 #include "net/proxy/proxy_resolver_request_context.h"
20 #include "net/proxy/sync_host_resolver.h"
26 // TTL for the per-request DNS cache. Applies to both successful and failed
28 const unsigned kCacheEntryTTLSeconds
= 5 * 60;
30 // Returns event parameters for a PAC error message (line number + message).
31 Value
* NetLogErrorCallback(int line_number
,
32 const string16
* message
,
33 NetLog::LogLevel
/* log_level */) {
34 DictionaryValue
* dict
= new DictionaryValue();
35 dict
->SetInteger("line_number", line_number
);
36 dict
->SetString("message", *message
);
40 // ProxyResolverJSBindings implementation.
41 class DefaultJSBindings
: public ProxyResolverJSBindings
{
43 DefaultJSBindings(SyncHostResolver
* host_resolver
,
45 ProxyResolverErrorObserver
* error_observer
)
46 : host_resolver_(host_resolver
),
48 error_observer_(error_observer
) {
51 // Handler for "alert(message)".
52 virtual void Alert(const string16
& message
) OVERRIDE
{
53 VLOG(1) << "PAC-alert: " << message
;
55 // Send to the NetLog.
56 LogEventToCurrentRequestAndGlobally(
57 NetLog::TYPE_PAC_JAVASCRIPT_ALERT
,
58 NetLog::StringCallback("message", &message
));
61 // Handler for "myIpAddress()".
62 // TODO(eroman): Perhaps enumerate the interfaces directly, using
64 virtual bool MyIpAddress(std::string
* first_ip_address
) OVERRIDE
{
65 LogEventToCurrentRequest(NetLog::PHASE_BEGIN
,
66 NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS
);
68 bool ok
= MyIpAddressImpl(first_ip_address
);
70 LogEventToCurrentRequest(NetLog::PHASE_END
,
71 NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS
);
75 // Handler for "myIpAddressEx()".
76 virtual bool MyIpAddressEx(std::string
* ip_address_list
) OVERRIDE
{
77 LogEventToCurrentRequest(NetLog::PHASE_BEGIN
,
78 NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS_EX
);
80 bool ok
= MyIpAddressExImpl(ip_address_list
);
82 LogEventToCurrentRequest(NetLog::PHASE_END
,
83 NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS_EX
);
87 // Handler for "dnsResolve(host)".
88 virtual bool DnsResolve(const std::string
& host
,
89 std::string
* first_ip_address
) OVERRIDE
{
90 LogEventToCurrentRequest(NetLog::PHASE_BEGIN
,
91 NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE
);
93 bool ok
= DnsResolveImpl(host
, first_ip_address
);
95 LogEventToCurrentRequest(NetLog::PHASE_END
,
96 NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE
);
100 // Handler for "dnsResolveEx(host)".
101 virtual bool DnsResolveEx(const std::string
& host
,
102 std::string
* ip_address_list
) OVERRIDE
{
103 LogEventToCurrentRequest(NetLog::PHASE_BEGIN
,
104 NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE_EX
);
106 bool ok
= DnsResolveExImpl(host
, ip_address_list
);
108 LogEventToCurrentRequest(NetLog::PHASE_END
,
109 NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE_EX
);
113 // Handler for when an error is encountered. |line_number| may be -1.
114 virtual void OnError(int line_number
, const string16
& message
) OVERRIDE
{
115 // Send to the chrome log.
116 if (line_number
== -1)
117 VLOG(1) << "PAC-error: " << message
;
119 VLOG(1) << "PAC-error: " << "line: " << line_number
<< ": " << message
;
121 // Send the error to the NetLog.
122 LogEventToCurrentRequestAndGlobally(
123 NetLog::TYPE_PAC_JAVASCRIPT_ERROR
,
124 base::Bind(&NetLogErrorCallback
, line_number
, &message
));
126 if (error_observer_
.get())
127 error_observer_
->OnPACScriptError(line_number
, message
);
130 virtual void Shutdown() OVERRIDE
{
131 host_resolver_
->Shutdown();
135 bool MyIpAddressImpl(std::string
* first_ip_address
) {
136 std::string my_hostname
= GetHostName();
137 if (my_hostname
.empty())
139 return DnsResolveImpl(my_hostname
, first_ip_address
);
142 bool MyIpAddressExImpl(std::string
* ip_address_list
) {
143 std::string my_hostname
= GetHostName();
144 if (my_hostname
.empty())
146 return DnsResolveExImpl(my_hostname
, ip_address_list
);
149 bool DnsResolveImpl(const std::string
& host
,
150 std::string
* first_ip_address
) {
151 // Do a sync resolve of the hostname (port doesn't matter).
152 // Disable IPv6 results. We do this because the PAC specification isn't
153 // really IPv6 friendly, and Internet Explorer also restricts to IPv4.
154 // Consequently a lot of existing PAC scripts assume they will only get
155 // IPv4 results, and will misbehave if they get an IPv6 result.
156 // See http://crbug.com/24641 for more details.
157 HostResolver::RequestInfo
info(HostPortPair(host
, 80));
158 info
.set_address_family(ADDRESS_FAMILY_IPV4
);
159 AddressList address_list
;
161 int result
= DnsResolveHelper(info
, &address_list
);
165 // There may be multiple results; we will just use the first one.
166 // This returns empty string on failure.
167 *first_ip_address
= address_list
.front().ToStringWithoutPort();
168 if (first_ip_address
->empty())
174 bool DnsResolveExImpl(const std::string
& host
,
175 std::string
* ip_address_list
) {
176 // Do a sync resolve of the hostname (port doesn't matter).
177 HostResolver::RequestInfo
info(HostPortPair(host
, 80));
178 AddressList address_list
;
179 int result
= DnsResolveHelper(info
, &address_list
);
184 // Stringify all of the addresses in the address list, separated
186 std::string address_list_str
;
187 for (AddressList::const_iterator iter
= address_list
.begin();
188 iter
!= address_list
.end(); ++iter
) {
189 if (!address_list_str
.empty())
190 address_list_str
+= ";";
191 const std::string address_string
= iter
->ToStringWithoutPort();
192 if (address_string
.empty())
194 address_list_str
+= address_string
;
197 *ip_address_list
= address_list_str
;
201 // Helper to execute a synchronous DNS resolve, using the per-request
202 // DNS cache if there is one.
203 int DnsResolveHelper(const HostResolver::RequestInfo
& info
,
204 AddressList
* address_list
) {
205 HostCache::Key
cache_key(info
.hostname(),
206 info
.address_family(),
207 info
.host_resolver_flags());
209 HostCache
* host_cache
= current_request_context() ?
210 current_request_context()->host_cache
: NULL
;
212 // First try to service this request from the per-request DNS cache.
213 // (we cache DNS failures much more aggressively within the context
214 // of a FindProxyForURL() request).
216 const HostCache::Entry
* entry
=
217 host_cache
->Lookup(cache_key
, base::TimeTicks::Now());
219 if (entry
->error
== OK
)
220 *address_list
= entry
->addrlist
;
225 // Otherwise ask the host resolver.
226 const BoundNetLog
* net_log
= GetNetLogForCurrentRequest();
227 int result
= host_resolver_
->Resolve(info
,
229 net_log
? *net_log
: BoundNetLog());
231 // Save the result back to the per-request DNS cache.
233 host_cache
->Set(cache_key
, HostCache::Entry(result
, *address_list
),
234 base::TimeTicks::Now(),
235 base::TimeDelta::FromSeconds(kCacheEntryTTLSeconds
));
242 const BoundNetLog
* GetNetLogForCurrentRequest() {
243 if (!current_request_context())
245 return current_request_context()->net_log
;
248 void LogEventToCurrentRequest(
249 NetLog::EventPhase phase
,
250 NetLog::EventType type
) {
251 const BoundNetLog
* net_log
= GetNetLogForCurrentRequest();
253 net_log
->AddEntry(type
, phase
);
256 void LogEventToCurrentRequest(
257 NetLog::EventPhase phase
,
258 NetLog::EventType type
,
259 const NetLog::ParametersCallback
& parameters_callback
) {
260 const BoundNetLog
* net_log
= GetNetLogForCurrentRequest();
262 net_log
->AddEntry(type
, phase
, parameters_callback
);
265 void LogEventToCurrentRequestAndGlobally(
266 NetLog::EventType type
,
267 const NetLog::ParametersCallback
& parameters_callback
) {
268 LogEventToCurrentRequest(NetLog::PHASE_NONE
, type
, parameters_callback
);
270 // Emit to the global NetLog event stream.
272 net_log_
->AddGlobalEntry(type
, parameters_callback
);
275 scoped_ptr
<SyncHostResolver
> host_resolver_
;
277 scoped_ptr
<ProxyResolverErrorObserver
> error_observer_
;
278 DISALLOW_COPY_AND_ASSIGN(DefaultJSBindings
);
284 ProxyResolverJSBindings
* ProxyResolverJSBindings::CreateDefault(
285 SyncHostResolver
* host_resolver
,
287 ProxyResolverErrorObserver
* error_observer
) {
288 return new DefaultJSBindings(host_resolver
, net_log
, error_observer
);