1 // Copyright (c) 2013 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_v8_tracing.h"
8 #include "base/message_loop/message_loop_proxy.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/synchronization/cancellation_flag.h"
11 #include "base/synchronization/waitable_event.h"
12 #include "base/threading/thread.h"
13 #include "base/threading/thread_restrictions.h"
14 #include "base/values.h"
15 #include "net/base/address_list.h"
16 #include "net/base/net_errors.h"
17 #include "net/dns/host_resolver.h"
18 #include "net/log/net_log.h"
19 #include "net/proxy/proxy_info.h"
20 #include "net/proxy/proxy_resolver_error_observer.h"
21 #include "net/proxy/proxy_resolver_v8.h"
23 // The intent of this class is explained in the design document:
24 // https://docs.google.com/a/chromium.org/document/d/16Ij5OcVnR3s0MH4Z5XkhI9VTPoMJdaBn9rKreAmGOdE/edit
26 // In a nutshell, PAC scripts are Javascript programs and may depend on
27 // network I/O, by calling functions like dnsResolve().
29 // This is problematic since functions such as dnsResolve() will block the
30 // Javascript execution until the DNS result is availble, thereby stalling the
31 // PAC thread, which hurts the ability to process parallel proxy resolves.
32 // An obvious solution is to simply start more PAC threads, however this scales
33 // poorly, which hurts the ability to process parallel proxy resolves.
35 // The solution in ProxyResolverV8Tracing is to model PAC scripts as being
36 // deterministic, and depending only on the inputted URL. When the script
37 // issues a dnsResolve() for a yet unresolved hostname, the Javascript
38 // execution is "aborted", and then re-started once the DNS result is
44 // Upper bound on how many *unique* DNS resolves a PAC script is allowed
45 // to make. This is a failsafe both for scripts that do a ridiculous
46 // number of DNS resolves, as well as scripts which are misbehaving
47 // under the tracing optimization. It is not expected to hit this normally.
48 const size_t kMaxUniqueResolveDnsPerExec
= 20;
50 // Approximate number of bytes to use for buffering alerts() and errors.
51 // This is a failsafe in case repeated executions of the script causes
52 // too much memory bloat. It is not expected for well behaved scripts to
53 // hit this. (In fact normal scripts should not even have alerts() or errors).
54 const size_t kMaxAlertsAndErrorsBytes
= 2048;
56 // Returns event parameters for a PAC error message (line number + message).
57 base::Value
* NetLogErrorCallback(int line_number
,
58 const base::string16
* message
,
59 NetLogCaptureMode
/* capture_mode */) {
60 base::DictionaryValue
* dict
= new base::DictionaryValue();
61 dict
->SetInteger("line_number", line_number
);
62 dict
->SetString("message", *message
);
68 // The Job class is responsible for executing GetProxyForURL() and
69 // SetPacScript(), since both of these operations share similar code.
71 // The DNS for these operations can operate in either blocking or
72 // non-blocking mode. Blocking mode is used as a fallback when the PAC script
73 // seems to be misbehaving under the tracing optimization.
75 // Note that this class runs on both the origin thread and a worker
76 // thread. Most methods are expected to be used exclusively on one thread
79 // The lifetime of Jobs does not exceed that of the ProxyResolverV8Tracing that
80 // spawned it. Destruction might happen on either the origin thread or the
82 class ProxyResolverV8Tracing::Job
83 : public base::RefCountedThreadSafe
<ProxyResolverV8Tracing::Job
>,
84 public ProxyResolverV8::JSBindings
{
86 // |parent| is non-owned. It is the ProxyResolverV8Tracing that spawned this
87 // Job, and must oulive it.
88 explicit Job(ProxyResolverV8Tracing
* parent
);
90 // Called from origin thread.
91 void StartSetPacScript(
92 const scoped_refptr
<ProxyResolverScriptData
>& script_data
,
93 const CompletionCallback
& callback
);
95 // Called from origin thread.
96 void StartGetProxyForURL(const GURL
& url
,
98 const BoundNetLog
& net_log
,
99 const CompletionCallback
& callback
);
101 // Called from origin thread.
104 // Called from origin thread.
105 LoadState
GetLoadState() const;
108 typedef std::map
<std::string
, std::string
> DnsCache
;
109 friend class base::RefCountedThreadSafe
<ProxyResolverV8Tracing::Job
>;
116 struct AlertOrError
{
119 base::string16 message
;
124 void CheckIsOnWorkerThread() const;
125 void CheckIsOnOriginThread() const;
127 void SetCallback(const CompletionCallback
& callback
);
128 void ReleaseCallback();
130 ProxyResolverV8
* v8_resolver();
131 base::MessageLoop
* worker_loop();
132 HostResolver
* host_resolver();
133 ProxyResolverErrorObserver
* error_observer();
136 // Invokes the user's callback.
137 void NotifyCaller(int result
);
138 void NotifyCallerOnOriginLoop(int result
);
140 void Start(Operation op
, bool blocking_dns
,
141 const CompletionCallback
& callback
);
143 void ExecuteBlocking();
144 void ExecuteNonBlocking();
145 int ExecuteProxyResolver();
147 // Implementation of ProxyResolverv8::JSBindings
148 bool ResolveDns(const std::string
& host
,
149 ResolveDnsOperation op
,
151 bool* terminate
) override
;
152 void Alert(const base::string16
& message
) override
;
153 void OnError(int line_number
, const base::string16
& error
) override
;
155 bool ResolveDnsBlocking(const std::string
& host
,
156 ResolveDnsOperation op
,
157 std::string
* output
);
159 bool ResolveDnsNonBlocking(const std::string
& host
,
160 ResolveDnsOperation op
,
164 bool PostDnsOperationAndWait(const std::string
& host
,
165 ResolveDnsOperation op
,
166 bool* completed_synchronously
)
169 void DoDnsOperation();
170 void OnDnsOperationComplete(int result
);
172 void ScheduleRestartWithBlockingDns();
174 bool GetDnsFromLocalCache(const std::string
& host
, ResolveDnsOperation op
,
175 std::string
* output
, bool* return_value
);
177 void SaveDnsToLocalCache(const std::string
& host
,
178 ResolveDnsOperation op
,
180 const AddressList
& addresses
);
182 // Builds a RequestInfo to service the specified PAC DNS operation.
183 static HostResolver::RequestInfo
MakeDnsRequestInfo(const std::string
& host
,
184 ResolveDnsOperation op
);
186 // Makes a key for looking up |host, op| in |dns_cache_|. Strings are used for
187 // convenience, to avoid defining custom comparators.
188 static std::string
MakeDnsCacheKey(const std::string
& host
,
189 ResolveDnsOperation op
);
191 void HandleAlertOrError(bool is_alert
, int line_number
,
192 const base::string16
& message
);
193 void DispatchBufferedAlertsAndErrors();
194 void DispatchAlertOrError(bool is_alert
, int line_number
,
195 const base::string16
& message
);
197 void LogEventToCurrentRequestAndGlobally(
198 NetLog::EventType type
,
199 const NetLog::ParametersCallback
& parameters_callback
);
201 // The thread which called into ProxyResolverV8Tracing, and on which the
202 // completion callback is expected to run.
203 scoped_refptr
<base::MessageLoopProxy
> origin_loop_
;
205 // The ProxyResolverV8Tracing which spawned this Job.
206 // Initialized on origin thread and then accessed from both threads.
207 ProxyResolverV8Tracing
* parent_
;
209 // The callback to run (on the origin thread) when the Job finishes.
210 // Should only be accessed from origin thread.
211 CompletionCallback callback_
;
213 // Flag to indicate whether the request has been cancelled.
214 base::CancellationFlag cancelled_
;
216 // The operation that this Job is running.
217 // Initialized on origin thread and then accessed from both threads.
218 Operation operation_
;
220 // The DNS mode for this Job.
221 // Initialized on origin thread, mutated on worker thread, and accessed
222 // by both the origin thread and worker thread.
225 // Used to block the worker thread on a DNS operation taking place on the
227 base::WaitableEvent event_
;
229 // Map of DNS operations completed so far. Written into on the origin thread
230 // and read on the worker thread.
233 // The job holds a reference to itself to ensure that it remains alive until
234 // either completion or cancellation.
235 scoped_refptr
<Job
> owned_self_reference_
;
237 // -------------------------------------------------------
238 // State specific to SET_PAC_SCRIPT.
239 // -------------------------------------------------------
241 scoped_refptr
<ProxyResolverScriptData
> script_data_
;
243 // -------------------------------------------------------
244 // State specific to GET_PROXY_FOR_URL.
245 // -------------------------------------------------------
247 ProxyInfo
* user_results_
; // Owned by caller, lives on origin thread.
250 BoundNetLog bound_net_log_
;
252 // ---------------------------------------------------------------------------
253 // State for ExecuteNonBlocking()
254 // ---------------------------------------------------------------------------
255 // These variables are used exclusively on the worker thread and are only
256 // meaningful when executing inside of ExecuteNonBlocking().
258 // Whether this execution was abandoned due to a missing DNS dependency.
261 // Number of calls made to ResolveDns() by this execution.
264 // Sequence of calls made to Alert() or OnError() by this execution.
265 std::vector
<AlertOrError
> alerts_and_errors_
;
266 size_t alerts_and_errors_byte_cost_
; // Approximate byte cost of the above.
268 // Number of calls made to ResolveDns() by the PREVIOUS execution.
271 // Whether the current execution needs to be restarted in blocking mode.
272 bool should_restart_with_blocking_dns_
;
274 // ---------------------------------------------------------------------------
275 // State for pending DNS request.
276 // ---------------------------------------------------------------------------
278 // Handle to the outstanding request in the HostResolver, or NULL.
279 // This is mutated and used on the origin thread, however it may be read by
280 // the worker thread for some DCHECKS().
281 HostResolver::RequestHandle pending_dns_
;
283 // Indicates if the outstanding DNS request completed synchronously. Written
284 // on the origin thread, and read by the worker thread.
285 bool pending_dns_completed_synchronously_
;
287 // These are the inputs to DoDnsOperation(). Written on the worker thread,
288 // read by the origin thread.
289 std::string pending_dns_host_
;
290 ResolveDnsOperation pending_dns_op_
;
292 // This contains the resolved address list that DoDnsOperation() fills in.
293 // Used exclusively on the origin thread.
294 AddressList pending_dns_addresses_
;
297 ProxyResolverV8Tracing::Job::Job(ProxyResolverV8Tracing
* parent
)
298 : origin_loop_(base::MessageLoopProxy::current()),
303 CheckIsOnOriginThread();
306 void ProxyResolverV8Tracing::Job::StartSetPacScript(
307 const scoped_refptr
<ProxyResolverScriptData
>& script_data
,
308 const CompletionCallback
& callback
) {
309 CheckIsOnOriginThread();
311 script_data_
= script_data
;
313 // Script initialization uses blocking DNS since there isn't any
314 // advantage to using non-blocking mode here. That is because the
315 // parent ProxyService can't submit any ProxyResolve requests until
316 // initialization has completed successfully!
317 Start(SET_PAC_SCRIPT
, true /*blocking*/, callback
);
320 void ProxyResolverV8Tracing::Job::StartGetProxyForURL(
323 const BoundNetLog
& net_log
,
324 const CompletionCallback
& callback
) {
325 CheckIsOnOriginThread();
328 user_results_
= results
;
329 bound_net_log_
= net_log
;
331 Start(GET_PROXY_FOR_URL
, false /*non-blocking*/, callback
);
334 void ProxyResolverV8Tracing::Job::Cancel() {
335 CheckIsOnOriginThread();
337 // There are several possibilities to consider for cancellation:
338 // (a) The job has been posted to the worker thread, however script execution
339 // has not yet started.
340 // (b) The script is executing on the worker thread.
341 // (c) The script is executing on the worker thread, however is blocked inside
342 // of dnsResolve() waiting for a response from the origin thread.
343 // (d) Nothing is running on the worker thread, however the host resolver has
344 // a pending DNS request which upon completion will restart the script
346 // (e) The worker thread has a pending task to restart execution, which was
347 // posted after the DNS dependency was resolved and saved to local cache.
348 // (f) The script execution completed entirely, and posted a task to the
349 // origin thread to notify the caller.
351 // |cancelled_| is read on both the origin thread and worker thread. The
352 // code that runs on the worker thread is littered with checks on
353 // |cancelled_| to break out early.
359 host_resolver()->CancelRequest(pending_dns_
);
363 // The worker thread might be blocked waiting for DNS.
366 owned_self_reference_
= NULL
;
369 LoadState
ProxyResolverV8Tracing::Job::GetLoadState() const {
370 CheckIsOnOriginThread();
373 return LOAD_STATE_RESOLVING_HOST_IN_PROXY_SCRIPT
;
375 return LOAD_STATE_RESOLVING_PROXY_FOR_URL
;
378 ProxyResolverV8Tracing::Job::~Job() {
379 DCHECK(!pending_dns_
);
380 DCHECK(callback_
.is_null());
383 void ProxyResolverV8Tracing::Job::CheckIsOnWorkerThread() const {
384 DCHECK_EQ(base::MessageLoop::current(), parent_
->thread_
->message_loop());
387 void ProxyResolverV8Tracing::Job::CheckIsOnOriginThread() const {
388 DCHECK(origin_loop_
->BelongsToCurrentThread());
391 void ProxyResolverV8Tracing::Job::SetCallback(
392 const CompletionCallback
& callback
) {
393 CheckIsOnOriginThread();
394 DCHECK(callback_
.is_null());
395 parent_
->num_outstanding_callbacks_
++;
396 callback_
= callback
;
399 void ProxyResolverV8Tracing::Job::ReleaseCallback() {
400 CheckIsOnOriginThread();
401 DCHECK(!callback_
.is_null());
402 CHECK_GT(parent_
->num_outstanding_callbacks_
, 0);
403 parent_
->num_outstanding_callbacks_
--;
406 // For good measure, clear this other user-owned pointer.
407 user_results_
= NULL
;
410 ProxyResolverV8
* ProxyResolverV8Tracing::Job::v8_resolver() {
411 return parent_
->v8_resolver_
.get();
414 base::MessageLoop
* ProxyResolverV8Tracing::Job::worker_loop() {
415 return parent_
->thread_
->message_loop();
418 HostResolver
* ProxyResolverV8Tracing::Job::host_resolver() {
419 return parent_
->host_resolver_
;
422 ProxyResolverErrorObserver
* ProxyResolverV8Tracing::Job::error_observer() {
423 return parent_
->error_observer_
.get();
426 NetLog
* ProxyResolverV8Tracing::Job::net_log() {
427 return parent_
->net_log_
;
430 void ProxyResolverV8Tracing::Job::NotifyCaller(int result
) {
431 CheckIsOnWorkerThread();
433 origin_loop_
->PostTask(
435 base::Bind(&Job::NotifyCallerOnOriginLoop
, this, result
));
438 void ProxyResolverV8Tracing::Job::NotifyCallerOnOriginLoop(int result
) {
439 CheckIsOnOriginThread();
441 if (cancelled_
.IsSet())
444 DCHECK(!callback_
.is_null());
445 DCHECK(!pending_dns_
);
447 if (operation_
== GET_PROXY_FOR_URL
) {
448 *user_results_
= results_
;
451 // There is only ever 1 outstanding SET_PAC_SCRIPT job. It needs to be
452 // tracked to support cancellation.
453 if (operation_
== SET_PAC_SCRIPT
) {
454 DCHECK_EQ(parent_
->set_pac_script_job_
.get(), this);
455 parent_
->set_pac_script_job_
= NULL
;
458 CompletionCallback callback
= callback_
;
460 callback
.Run(result
);
462 owned_self_reference_
= NULL
;
465 void ProxyResolverV8Tracing::Job::Start(Operation op
, bool blocking_dns
,
466 const CompletionCallback
& callback
) {
467 CheckIsOnOriginThread();
470 blocking_dns_
= blocking_dns
;
471 SetCallback(callback
);
473 owned_self_reference_
= this;
475 worker_loop()->PostTask(FROM_HERE
,
476 blocking_dns_
? base::Bind(&Job::ExecuteBlocking
, this) :
477 base::Bind(&Job::ExecuteNonBlocking
, this));
480 void ProxyResolverV8Tracing::Job::ExecuteBlocking() {
481 CheckIsOnWorkerThread();
482 DCHECK(blocking_dns_
);
484 if (cancelled_
.IsSet())
487 NotifyCaller(ExecuteProxyResolver());
490 void ProxyResolverV8Tracing::Job::ExecuteNonBlocking() {
491 CheckIsOnWorkerThread();
492 DCHECK(!blocking_dns_
);
494 if (cancelled_
.IsSet())
497 // Reset state for the current execution.
500 alerts_and_errors_
.clear();
501 alerts_and_errors_byte_cost_
= 0;
502 should_restart_with_blocking_dns_
= false;
504 int result
= ExecuteProxyResolver();
506 if (should_restart_with_blocking_dns_
) {
507 DCHECK(!blocking_dns_
);
509 blocking_dns_
= true;
517 DispatchBufferedAlertsAndErrors();
518 NotifyCaller(result
);
521 int ProxyResolverV8Tracing::Job::ExecuteProxyResolver() {
522 JSBindings
* prev_bindings
= v8_resolver()->js_bindings();
523 v8_resolver()->set_js_bindings(this);
525 int result
= ERR_UNEXPECTED
; // Initialized to silence warnings.
527 switch (operation_
) {
529 result
= v8_resolver()->SetPacScript(
530 script_data_
, CompletionCallback());
532 case GET_PROXY_FOR_URL
:
533 result
= v8_resolver()->GetProxyForURL(
535 // Important: Do not write directly into |user_results_|, since if the
536 // request were to be cancelled from the origin thread, must guarantee
537 // that |user_results_| is not accessed anymore.
539 CompletionCallback(),
545 v8_resolver()->set_js_bindings(prev_bindings
);
550 bool ProxyResolverV8Tracing::Job::ResolveDns(const std::string
& host
,
551 ResolveDnsOperation op
,
554 if (cancelled_
.IsSet()) {
559 if ((op
== DNS_RESOLVE
|| op
== DNS_RESOLVE_EX
) && host
.empty()) {
560 // a DNS resolve with an empty hostname is considered an error.
564 return blocking_dns_
?
565 ResolveDnsBlocking(host
, op
, output
) :
566 ResolveDnsNonBlocking(host
, op
, output
, terminate
);
569 void ProxyResolverV8Tracing::Job::Alert(const base::string16
& message
) {
570 HandleAlertOrError(true, -1, message
);
573 void ProxyResolverV8Tracing::Job::OnError(int line_number
,
574 const base::string16
& error
) {
575 HandleAlertOrError(false, line_number
, error
);
578 bool ProxyResolverV8Tracing::Job::ResolveDnsBlocking(const std::string
& host
,
579 ResolveDnsOperation op
,
580 std::string
* output
) {
581 CheckIsOnWorkerThread();
583 // Check if the DNS result for this host has already been cached.
585 if (GetDnsFromLocalCache(host
, op
, output
, &rv
)) {
590 if (dns_cache_
.size() >= kMaxUniqueResolveDnsPerExec
) {
591 // Safety net for scripts with unexpectedly many DNS calls.
592 // We will continue running to completion, but will fail every
593 // subsequent DNS request.
597 if (!PostDnsOperationAndWait(host
, op
, NULL
))
598 return false; // Was cancelled.
600 CHECK(GetDnsFromLocalCache(host
, op
, output
, &rv
));
604 bool ProxyResolverV8Tracing::Job::ResolveDnsNonBlocking(const std::string
& host
,
605 ResolveDnsOperation op
,
608 CheckIsOnWorkerThread();
611 // If this execution was already abandoned can fail right away. Only 1 DNS
612 // dependency will be traced at a time (for more predictable outcomes).
618 // Check if the DNS result for this host has already been cached.
620 if (GetDnsFromLocalCache(host
, op
, output
, &rv
)) {
625 if (num_dns_
<= last_num_dns_
) {
626 // The sequence of DNS operations is different from last time!
627 ScheduleRestartWithBlockingDns();
632 if (dns_cache_
.size() >= kMaxUniqueResolveDnsPerExec
) {
633 // Safety net for scripts with unexpectedly many DNS calls.
637 DCHECK(!should_restart_with_blocking_dns_
);
639 bool completed_synchronously
;
640 if (!PostDnsOperationAndWait(host
, op
, &completed_synchronously
))
641 return false; // Was cancelled.
643 if (completed_synchronously
) {
644 CHECK(GetDnsFromLocalCache(host
, op
, output
, &rv
));
648 // Otherwise if the result was not in the cache, then a DNS request has
649 // been started. Abandon this invocation of FindProxyForURL(), it will be
650 // restarted once the DNS request completes.
653 last_num_dns_
= num_dns_
;
657 bool ProxyResolverV8Tracing::Job::PostDnsOperationAndWait(
658 const std::string
& host
, ResolveDnsOperation op
,
659 bool* completed_synchronously
) {
661 // Post the DNS request to the origin thread.
662 DCHECK(!pending_dns_
);
663 pending_dns_host_
= host
;
664 pending_dns_op_
= op
;
665 origin_loop_
->PostTask(FROM_HERE
, base::Bind(&Job::DoDnsOperation
, this));
670 if (cancelled_
.IsSet())
673 if (completed_synchronously
)
674 *completed_synchronously
= pending_dns_completed_synchronously_
;
679 void ProxyResolverV8Tracing::Job::DoDnsOperation() {
680 CheckIsOnOriginThread();
681 DCHECK(!pending_dns_
);
683 if (cancelled_
.IsSet())
686 HostResolver::RequestHandle dns_request
= NULL
;
687 int result
= host_resolver()->Resolve(
688 MakeDnsRequestInfo(pending_dns_host_
, pending_dns_op_
),
690 &pending_dns_addresses_
,
691 base::Bind(&Job::OnDnsOperationComplete
, this),
695 pending_dns_completed_synchronously_
= result
!= ERR_IO_PENDING
;
697 // Check if the request was cancelled as a side-effect of calling into the
698 // HostResolver. This isn't the ordinary execution flow, however it is
699 // exercised by unit-tests.
700 if (cancelled_
.IsSet()) {
701 if (!pending_dns_completed_synchronously_
)
702 host_resolver()->CancelRequest(dns_request
);
706 if (pending_dns_completed_synchronously_
) {
707 OnDnsOperationComplete(result
);
710 pending_dns_
= dns_request
;
711 if (!parent_
->on_load_state_changed_
.is_null()) {
712 parent_
->on_load_state_changed_
.Run(
713 this, LOAD_STATE_RESOLVING_HOST_IN_PROXY_SCRIPT
);
715 // OnDnsOperationComplete() will be called by host resolver on completion.
718 if (!blocking_dns_
) {
719 // The worker thread always blocks waiting to see if the result can be
720 // serviced from cache before restarting.
725 void ProxyResolverV8Tracing::Job::OnDnsOperationComplete(int result
) {
726 CheckIsOnOriginThread();
728 DCHECK(!cancelled_
.IsSet());
729 DCHECK(pending_dns_completed_synchronously_
== (pending_dns_
== NULL
));
731 SaveDnsToLocalCache(pending_dns_host_
, pending_dns_op_
, result
,
732 pending_dns_addresses_
);
735 if (!parent_
->on_load_state_changed_
.is_null() &&
736 !pending_dns_completed_synchronously_
&& !cancelled_
.IsSet()) {
737 parent_
->on_load_state_changed_
.Run(this,
738 LOAD_STATE_RESOLVING_PROXY_FOR_URL
);
746 if (!blocking_dns_
&& !pending_dns_completed_synchronously_
) {
747 // Restart. This time it should make more progress due to having
749 worker_loop()->PostTask(FROM_HERE
,
750 base::Bind(&Job::ExecuteNonBlocking
, this));
754 void ProxyResolverV8Tracing::Job::ScheduleRestartWithBlockingDns() {
755 CheckIsOnWorkerThread();
757 DCHECK(!should_restart_with_blocking_dns_
);
759 DCHECK(!blocking_dns_
);
763 // The restart will happen after ExecuteNonBlocking() finishes.
764 should_restart_with_blocking_dns_
= true;
767 bool ProxyResolverV8Tracing::Job::GetDnsFromLocalCache(
768 const std::string
& host
,
769 ResolveDnsOperation op
,
771 bool* return_value
) {
772 CheckIsOnWorkerThread();
774 DnsCache::const_iterator it
= dns_cache_
.find(MakeDnsCacheKey(host
, op
));
775 if (it
== dns_cache_
.end())
778 *output
= it
->second
;
779 *return_value
= !it
->second
.empty();
783 void ProxyResolverV8Tracing::Job::SaveDnsToLocalCache(
784 const std::string
& host
,
785 ResolveDnsOperation op
,
787 const AddressList
& addresses
) {
788 CheckIsOnOriginThread();
790 // Serialize the result into a string to save to the cache.
791 std::string cache_value
;
792 if (net_error
!= OK
) {
793 cache_value
= std::string();
794 } else if (op
== DNS_RESOLVE
|| op
== MY_IP_ADDRESS
) {
795 // dnsResolve() and myIpAddress() are expected to return a single IP
797 cache_value
= addresses
.front().ToStringWithoutPort();
799 // The *Ex versions are expected to return a semi-colon separated list.
800 for (AddressList::const_iterator iter
= addresses
.begin();
801 iter
!= addresses
.end(); ++iter
) {
802 if (!cache_value
.empty())
804 cache_value
+= iter
->ToStringWithoutPort();
808 dns_cache_
[MakeDnsCacheKey(host
, op
)] = cache_value
;
812 HostResolver::RequestInfo
ProxyResolverV8Tracing::Job::MakeDnsRequestInfo(
813 const std::string
& host
, ResolveDnsOperation op
) {
814 HostPortPair host_port
= HostPortPair(host
, 80);
815 if (op
== MY_IP_ADDRESS
|| op
== MY_IP_ADDRESS_EX
) {
816 host_port
.set_host(GetHostName());
819 HostResolver::RequestInfo
info(host_port
);
820 // Flag myIpAddress requests.
821 if (op
== MY_IP_ADDRESS
|| op
== MY_IP_ADDRESS_EX
) {
822 // TODO: Provide a RequestInfo construction mechanism that does not
823 // require a hostname and sets is_my_ip_address to true instead of this.
824 info
.set_is_my_ip_address(true);
826 // The non-ex flavors are limited to IPv4 results.
827 if (op
== MY_IP_ADDRESS
|| op
== DNS_RESOLVE
) {
828 info
.set_address_family(ADDRESS_FAMILY_IPV4
);
834 std::string
ProxyResolverV8Tracing::Job::MakeDnsCacheKey(
835 const std::string
& host
, ResolveDnsOperation op
) {
836 return base::StringPrintf("%d:%s", op
, host
.c_str());
839 void ProxyResolverV8Tracing::Job::HandleAlertOrError(
842 const base::string16
& message
) {
843 CheckIsOnWorkerThread();
845 if (cancelled_
.IsSet())
849 // In blocking DNS mode the events can be dispatched immediately.
850 DispatchAlertOrError(is_alert
, line_number
, message
);
854 // Otherwise in nonblocking mode, buffer all the messages until
860 alerts_and_errors_byte_cost_
+= sizeof(AlertOrError
) + message
.size() * 2;
862 // If there have been lots of messages, enqueing could be expensive on
863 // memory. Consider a script which does megabytes worth of alerts().
864 // Avoid this by falling back to blocking mode.
865 if (alerts_and_errors_byte_cost_
> kMaxAlertsAndErrorsBytes
) {
866 ScheduleRestartWithBlockingDns();
870 AlertOrError entry
= {is_alert
, line_number
, message
};
871 alerts_and_errors_
.push_back(entry
);
874 void ProxyResolverV8Tracing::Job::DispatchBufferedAlertsAndErrors() {
875 CheckIsOnWorkerThread();
876 DCHECK(!blocking_dns_
);
879 for (size_t i
= 0; i
< alerts_and_errors_
.size(); ++i
) {
880 const AlertOrError
& x
= alerts_and_errors_
[i
];
881 DispatchAlertOrError(x
.is_alert
, x
.line_number
, x
.message
);
885 void ProxyResolverV8Tracing::Job::DispatchAlertOrError(
886 bool is_alert
, int line_number
, const base::string16
& message
) {
887 CheckIsOnWorkerThread();
889 // Note that the handling of cancellation is racy with regard to
890 // alerts/errors. The request might get cancelled shortly after this
891 // check! (There is no lock being held to guarantee otherwise).
893 // If this happens, then some information will get written to the NetLog
894 // needlessly, however the NetLog will still be alive so it shouldn't cause
896 if (cancelled_
.IsSet())
900 // -------------------
902 // -------------------
903 VLOG(1) << "PAC-alert: " << message
;
905 // Send to the NetLog.
906 LogEventToCurrentRequestAndGlobally(
907 NetLog::TYPE_PAC_JAVASCRIPT_ALERT
,
908 NetLog::StringCallback("message", &message
));
910 // -------------------
912 // -------------------
913 if (line_number
== -1)
914 VLOG(1) << "PAC-error: " << message
;
916 VLOG(1) << "PAC-error: " << "line: " << line_number
<< ": " << message
;
918 // Send the error to the NetLog.
919 LogEventToCurrentRequestAndGlobally(
920 NetLog::TYPE_PAC_JAVASCRIPT_ERROR
,
921 base::Bind(&NetLogErrorCallback
, line_number
, &message
));
923 if (error_observer())
924 error_observer()->OnPACScriptError(line_number
, message
);
928 void ProxyResolverV8Tracing::Job::LogEventToCurrentRequestAndGlobally(
929 NetLog::EventType type
,
930 const NetLog::ParametersCallback
& parameters_callback
) {
931 CheckIsOnWorkerThread();
932 bound_net_log_
.AddEvent(type
, parameters_callback
);
934 // Emit to the global NetLog event stream.
936 net_log()->AddGlobalEntry(type
, parameters_callback
);
939 ProxyResolverV8Tracing::ProxyResolverV8Tracing(
940 HostResolver
* host_resolver
,
941 ProxyResolverErrorObserver
* error_observer
,
943 : ProxyResolverV8Tracing(host_resolver
,
946 LoadStateChangedCallback()) {
949 ProxyResolverV8Tracing::ProxyResolverV8Tracing(
950 HostResolver
* host_resolver
,
951 ProxyResolverErrorObserver
* error_observer
,
953 const LoadStateChangedCallback
& on_load_state_changed
)
954 : ProxyResolver(true /*expects_pac_bytes*/),
955 host_resolver_(host_resolver
),
956 error_observer_(error_observer
),
958 num_outstanding_callbacks_(0),
959 on_load_state_changed_(on_load_state_changed
) {
960 DCHECK(host_resolver
);
961 // Start up the thread.
962 thread_
.reset(new base::Thread("Proxy resolver"));
963 base::Thread::Options options
;
964 options
.timer_slack
= base::TIMER_SLACK_MAXIMUM
;
965 CHECK(thread_
->StartWithOptions(options
));
967 v8_resolver_
.reset(new ProxyResolverV8
);
970 ProxyResolverV8Tracing::~ProxyResolverV8Tracing() {
971 // Note, all requests should have been cancelled.
972 CHECK(!set_pac_script_job_
.get());
973 CHECK_EQ(0, num_outstanding_callbacks_
);
975 // Join the worker thread. See http://crbug.com/69710. Note that we call
976 // Stop() here instead of simply clearing thread_ since there may be pending
977 // callbacks on the worker thread which want to dereference thread_.
978 base::ThreadRestrictions::ScopedAllowIO allow_io
;
982 int ProxyResolverV8Tracing::GetProxyForURL(const GURL
& url
,
984 const CompletionCallback
& callback
,
985 RequestHandle
* request
,
986 const BoundNetLog
& net_log
) {
987 DCHECK(CalledOnValidThread());
988 DCHECK(!callback
.is_null());
989 DCHECK(!set_pac_script_job_
.get());
991 scoped_refptr
<Job
> job
= new Job(this);
994 *request
= job
.get();
996 job
->StartGetProxyForURL(url
, results
, net_log
, callback
);
997 return ERR_IO_PENDING
;
1000 void ProxyResolverV8Tracing::CancelRequest(RequestHandle request
) {
1001 Job
* job
= reinterpret_cast<Job
*>(request
);
1005 LoadState
ProxyResolverV8Tracing::GetLoadState(RequestHandle request
) const {
1006 Job
* job
= reinterpret_cast<Job
*>(request
);
1007 return job
->GetLoadState();
1010 void ProxyResolverV8Tracing::CancelSetPacScript() {
1011 DCHECK(set_pac_script_job_
.get());
1012 set_pac_script_job_
->Cancel();
1013 set_pac_script_job_
= NULL
;
1016 int ProxyResolverV8Tracing::SetPacScript(
1017 const scoped_refptr
<ProxyResolverScriptData
>& script_data
,
1018 const CompletionCallback
& callback
) {
1019 DCHECK(CalledOnValidThread());
1020 DCHECK(!callback
.is_null());
1022 // Note that there should not be any outstanding (non-cancelled) Jobs when
1023 // setting the PAC script (ProxyService should guarantee this). If there are,
1024 // then they might complete in strange ways after the new script is set.
1025 DCHECK(!set_pac_script_job_
.get());
1026 CHECK_EQ(0, num_outstanding_callbacks_
);
1028 set_pac_script_job_
= new Job(this);
1029 set_pac_script_job_
->StartSetPacScript(script_data
, callback
);
1031 return ERR_IO_PENDING
;