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"
11 #include "base/bind.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/synchronization/cancellation_flag.h"
15 #include "base/synchronization/waitable_event.h"
16 #include "base/thread_task_runner_handle.h"
17 #include "base/threading/thread.h"
18 #include "base/threading/thread_restrictions.h"
19 #include "net/base/address_list.h"
20 #include "net/base/net_errors.h"
21 #include "net/dns/host_resolver.h"
22 #include "net/proxy/proxy_info.h"
23 #include "net/proxy/proxy_resolver_error_observer.h"
24 #include "net/proxy/proxy_resolver_v8.h"
26 // The intent of this class is explained in the design document:
27 // https://docs.google.com/a/chromium.org/document/d/16Ij5OcVnR3s0MH4Z5XkhI9VTPoMJdaBn9rKreAmGOdE/edit
29 // In a nutshell, PAC scripts are Javascript programs and may depend on
30 // network I/O, by calling functions like dnsResolve().
32 // This is problematic since functions such as dnsResolve() will block the
33 // Javascript execution until the DNS result is availble, thereby stalling the
34 // PAC thread, which hurts the ability to process parallel proxy resolves.
35 // An obvious solution is to simply start more PAC threads, however this scales
36 // poorly, which hurts the ability to process parallel proxy resolves.
38 // The solution in ProxyResolverV8Tracing is to model PAC scripts as being
39 // deterministic, and depending only on the inputted URL. When the script
40 // issues a dnsResolve() for a yet unresolved hostname, the Javascript
41 // execution is "aborted", and then re-started once the DNS result is
47 // Upper bound on how many *unique* DNS resolves a PAC script is allowed
48 // to make. This is a failsafe both for scripts that do a ridiculous
49 // number of DNS resolves, as well as scripts which are misbehaving
50 // under the tracing optimization. It is not expected to hit this normally.
51 const size_t kMaxUniqueResolveDnsPerExec
= 20;
53 // Approximate number of bytes to use for buffering alerts() and errors.
54 // This is a failsafe in case repeated executions of the script causes
55 // too much memory bloat. It is not expected for well behaved scripts to
56 // hit this. (In fact normal scripts should not even have alerts() or errors).
57 const size_t kMaxAlertsAndErrorsBytes
= 2048;
59 // The Job class is responsible for executing GetProxyForURL() and
60 // creating ProxyResolverV8 instances, since both of these operations share
63 // The DNS for these operations can operate in either blocking or
64 // non-blocking mode. Blocking mode is used as a fallback when the PAC script
65 // seems to be misbehaving under the tracing optimization.
67 // Note that this class runs on both the origin thread and a worker
68 // thread. Most methods are expected to be used exclusively on one thread
71 // The lifetime of Jobs does not exceed that of the ProxyResolverV8TracingImpl
72 // that spawned it. Destruction might happen on either the origin thread or the
74 class Job
: public base::RefCountedThreadSafe
<Job
>,
75 public ProxyResolverV8::JSBindings
{
79 const scoped_refptr
<base::SingleThreadTaskRunner
>& worker_task_runner
,
80 int* num_outstanding_callbacks
)
81 : v8_resolver(nullptr),
82 worker_task_runner(worker_task_runner
),
83 num_outstanding_callbacks(num_outstanding_callbacks
) {}
85 ProxyResolverV8
* v8_resolver
;
86 scoped_refptr
<base::SingleThreadTaskRunner
> worker_task_runner
;
87 int* num_outstanding_callbacks
;
89 // |params| is non-owned. It contains the parameters for this Job, and must
91 Job(const Params
* params
,
92 scoped_ptr
<ProxyResolverV8Tracing::Bindings
> bindings
);
94 // Called from origin thread.
95 void StartCreateV8Resolver(
96 const scoped_refptr
<ProxyResolverScriptData
>& script_data
,
97 scoped_ptr
<ProxyResolverV8
>* resolver
,
98 const CompletionCallback
& callback
);
100 // Called from origin thread.
101 void StartGetProxyForURL(const GURL
& url
,
103 const CompletionCallback
& callback
);
105 // Called from origin thread.
108 // Called from origin thread.
109 LoadState
GetLoadState() const;
112 typedef std::map
<std::string
, std::string
> DnsCache
;
113 friend class base::RefCountedThreadSafe
<Job
>;
120 struct AlertOrError
{
123 base::string16 message
;
128 void CheckIsOnWorkerThread() const;
129 void CheckIsOnOriginThread() const;
131 void SetCallback(const CompletionCallback
& callback
);
132 void ReleaseCallback();
134 ProxyResolverV8
* v8_resolver();
135 const scoped_refptr
<base::SingleThreadTaskRunner
>& worker_task_runner();
136 HostResolver
* host_resolver();
138 // Invokes the user's callback.
139 void NotifyCaller(int result
);
140 void NotifyCallerOnOriginLoop(int result
);
142 void Start(Operation op
, bool blocking_dns
,
143 const CompletionCallback
& callback
);
145 void ExecuteBlocking();
146 void ExecuteNonBlocking();
147 int ExecuteProxyResolver();
149 // Implementation of ProxyResolverv8::JSBindings
150 bool ResolveDns(const std::string
& host
,
151 ResolveDnsOperation op
,
153 bool* terminate
) override
;
154 void Alert(const base::string16
& message
) override
;
155 void OnError(int line_number
, const base::string16
& error
) override
;
157 bool ResolveDnsBlocking(const std::string
& host
,
158 ResolveDnsOperation op
,
159 std::string
* output
);
161 bool ResolveDnsNonBlocking(const std::string
& host
,
162 ResolveDnsOperation op
,
166 bool PostDnsOperationAndWait(const std::string
& host
,
167 ResolveDnsOperation op
,
168 bool* completed_synchronously
)
171 void DoDnsOperation();
172 void OnDnsOperationComplete(int result
);
174 void ScheduleRestartWithBlockingDns();
176 bool GetDnsFromLocalCache(const std::string
& host
, ResolveDnsOperation op
,
177 std::string
* output
, bool* return_value
);
179 void SaveDnsToLocalCache(const std::string
& host
,
180 ResolveDnsOperation op
,
182 const AddressList
& addresses
);
184 // Builds a RequestInfo to service the specified PAC DNS operation.
185 static HostResolver::RequestInfo
MakeDnsRequestInfo(const std::string
& host
,
186 ResolveDnsOperation op
);
188 // Makes a key for looking up |host, op| in |dns_cache_|. Strings are used for
189 // convenience, to avoid defining custom comparators.
190 static std::string
MakeDnsCacheKey(const std::string
& host
,
191 ResolveDnsOperation op
);
193 void HandleAlertOrError(bool is_alert
, int line_number
,
194 const base::string16
& message
);
195 void DispatchBufferedAlertsAndErrors();
196 void DispatchAlertOrError(bool is_alert
, int line_number
,
197 const base::string16
& message
);
199 // The thread which called into ProxyResolverV8TracingImpl, and on which the
200 // completion callback is expected to run.
201 scoped_refptr
<base::SingleThreadTaskRunner
> origin_runner_
;
203 // The Parameters for this Job.
204 // Initialized on origin thread and then accessed from both threads.
205 const Params
* const params_
;
207 scoped_ptr
<ProxyResolverV8Tracing::Bindings
> bindings_
;
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 CREATE_V8_RESOLVER.
239 // -------------------------------------------------------
241 scoped_refptr
<ProxyResolverScriptData
> script_data_
;
242 scoped_ptr
<ProxyResolverV8
>* resolver_out_
;
244 // -------------------------------------------------------
245 // State specific to GET_PROXY_FOR_URL.
246 // -------------------------------------------------------
248 ProxyInfo
* user_results_
; // Owned by caller, lives on origin thread.
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 class ProxyResolverV8TracingImpl
: public ProxyResolverV8Tracing
,
298 public base::NonThreadSafe
{
300 ProxyResolverV8TracingImpl(scoped_ptr
<base::Thread
> thread
,
301 scoped_ptr
<ProxyResolverV8
> resolver
,
302 scoped_ptr
<Job::Params
> job_params
);
304 ~ProxyResolverV8TracingImpl() override
;
306 // ProxyResolverV8Tracing overrides.
307 void GetProxyForURL(const GURL
& url
,
309 const CompletionCallback
& callback
,
310 ProxyResolver::RequestHandle
* request
,
311 scoped_ptr
<Bindings
> bindings
) override
;
312 void CancelRequest(ProxyResolver::RequestHandle request
) override
;
313 LoadState
GetLoadState(ProxyResolver::RequestHandle request
) const override
;
316 // The worker thread on which the ProxyResolverV8 will be run.
317 scoped_ptr
<base::Thread
> thread_
;
318 scoped_ptr
<ProxyResolverV8
> v8_resolver_
;
320 scoped_ptr
<Job::Params
> job_params_
;
322 // The number of outstanding (non-cancelled) jobs.
323 int num_outstanding_callbacks_
;
325 DISALLOW_COPY_AND_ASSIGN(ProxyResolverV8TracingImpl
);
328 Job::Job(const Job::Params
* params
,
329 scoped_ptr
<ProxyResolverV8Tracing::Bindings
> bindings
)
330 : origin_runner_(base::ThreadTaskRunnerHandle::Get()),
332 bindings_(bindings
.Pass()),
336 CheckIsOnOriginThread();
339 void Job::StartCreateV8Resolver(
340 const scoped_refptr
<ProxyResolverScriptData
>& script_data
,
341 scoped_ptr
<ProxyResolverV8
>* resolver
,
342 const CompletionCallback
& callback
) {
343 CheckIsOnOriginThread();
345 resolver_out_
= resolver
;
346 script_data_
= script_data
;
348 // Script initialization uses blocking DNS since there isn't any
349 // advantage to using non-blocking mode here. That is because the
350 // parent ProxyService can't submit any ProxyResolve requests until
351 // initialization has completed successfully!
352 Start(CREATE_V8_RESOLVER
, true /*blocking*/, callback
);
355 void Job::StartGetProxyForURL(const GURL
& url
,
357 const CompletionCallback
& callback
) {
358 CheckIsOnOriginThread();
361 user_results_
= results
;
363 Start(GET_PROXY_FOR_URL
, false /*non-blocking*/, callback
);
367 CheckIsOnOriginThread();
369 // There are several possibilities to consider for cancellation:
370 // (a) The job has been posted to the worker thread, however script execution
371 // has not yet started.
372 // (b) The script is executing on the worker thread.
373 // (c) The script is executing on the worker thread, however is blocked inside
374 // of dnsResolve() waiting for a response from the origin thread.
375 // (d) Nothing is running on the worker thread, however the host resolver has
376 // a pending DNS request which upon completion will restart the script
378 // (e) The worker thread has a pending task to restart execution, which was
379 // posted after the DNS dependency was resolved and saved to local cache.
380 // (f) The script execution completed entirely, and posted a task to the
381 // origin thread to notify the caller.
383 // |cancelled_| is read on both the origin thread and worker thread. The
384 // code that runs on the worker thread is littered with checks on
385 // |cancelled_| to break out early.
391 host_resolver()->CancelRequest(pending_dns_
);
395 // The worker thread might be blocked waiting for DNS.
398 owned_self_reference_
= NULL
;
401 LoadState
Job::GetLoadState() const {
402 CheckIsOnOriginThread();
405 return LOAD_STATE_RESOLVING_HOST_IN_PROXY_SCRIPT
;
407 return LOAD_STATE_RESOLVING_PROXY_FOR_URL
;
411 DCHECK(!pending_dns_
);
412 DCHECK(callback_
.is_null());
415 void Job::CheckIsOnWorkerThread() const {
416 DCHECK(params_
->worker_task_runner
->BelongsToCurrentThread());
419 void Job::CheckIsOnOriginThread() const {
420 DCHECK(origin_runner_
->BelongsToCurrentThread());
423 void Job::SetCallback(const CompletionCallback
& callback
) {
424 CheckIsOnOriginThread();
425 DCHECK(callback_
.is_null());
426 (*params_
->num_outstanding_callbacks
)++;
427 callback_
= callback
;
430 void Job::ReleaseCallback() {
431 CheckIsOnOriginThread();
432 DCHECK(!callback_
.is_null());
433 CHECK_GT(*params_
->num_outstanding_callbacks
, 0);
434 (*params_
->num_outstanding_callbacks
)--;
437 // For good measure, clear this other user-owned pointer.
438 user_results_
= NULL
;
441 ProxyResolverV8
* Job::v8_resolver() {
442 return params_
->v8_resolver
;
445 const scoped_refptr
<base::SingleThreadTaskRunner
>& Job::worker_task_runner() {
446 return params_
->worker_task_runner
;
449 HostResolver
* Job::host_resolver() {
450 return bindings_
->GetHostResolver();
453 void Job::NotifyCaller(int result
) {
454 CheckIsOnWorkerThread();
456 origin_runner_
->PostTask(
457 FROM_HERE
, base::Bind(&Job::NotifyCallerOnOriginLoop
, this, result
));
460 void Job::NotifyCallerOnOriginLoop(int result
) {
461 CheckIsOnOriginThread();
463 if (cancelled_
.IsSet())
466 DCHECK(!callback_
.is_null());
467 DCHECK(!pending_dns_
);
469 if (operation_
== GET_PROXY_FOR_URL
) {
470 *user_results_
= results_
;
473 CompletionCallback callback
= callback_
;
475 callback
.Run(result
);
477 owned_self_reference_
= NULL
;
480 void Job::Start(Operation op
,
482 const CompletionCallback
& callback
) {
483 CheckIsOnOriginThread();
486 blocking_dns_
= blocking_dns
;
487 SetCallback(callback
);
489 owned_self_reference_
= this;
491 worker_task_runner()->PostTask(
492 FROM_HERE
, blocking_dns_
? base::Bind(&Job::ExecuteBlocking
, this)
493 : base::Bind(&Job::ExecuteNonBlocking
, this));
496 void Job::ExecuteBlocking() {
497 CheckIsOnWorkerThread();
498 DCHECK(blocking_dns_
);
500 if (cancelled_
.IsSet())
503 NotifyCaller(ExecuteProxyResolver());
506 void Job::ExecuteNonBlocking() {
507 CheckIsOnWorkerThread();
508 DCHECK(!blocking_dns_
);
510 if (cancelled_
.IsSet())
513 // Reset state for the current execution.
516 alerts_and_errors_
.clear();
517 alerts_and_errors_byte_cost_
= 0;
518 should_restart_with_blocking_dns_
= false;
520 int result
= ExecuteProxyResolver();
522 if (should_restart_with_blocking_dns_
) {
523 DCHECK(!blocking_dns_
);
525 blocking_dns_
= true;
533 DispatchBufferedAlertsAndErrors();
534 NotifyCaller(result
);
537 int Job::ExecuteProxyResolver() {
538 int result
= ERR_UNEXPECTED
; // Initialized to silence warnings.
540 switch (operation_
) {
541 case CREATE_V8_RESOLVER
: {
542 scoped_ptr
<ProxyResolverV8
> resolver
;
543 result
= ProxyResolverV8::Create(script_data_
, this, &resolver
);
545 *resolver_out_
= resolver
.Pass();
548 case GET_PROXY_FOR_URL
: {
549 result
= v8_resolver()->GetProxyForURL(
551 // Important: Do not write directly into |user_results_|, since if the
552 // request were to be cancelled from the origin thread, must guarantee
553 // that |user_results_| is not accessed anymore.
562 bool Job::ResolveDns(const std::string
& host
,
563 ResolveDnsOperation op
,
566 if (cancelled_
.IsSet()) {
571 if ((op
== DNS_RESOLVE
|| op
== DNS_RESOLVE_EX
) && host
.empty()) {
572 // a DNS resolve with an empty hostname is considered an error.
576 return blocking_dns_
?
577 ResolveDnsBlocking(host
, op
, output
) :
578 ResolveDnsNonBlocking(host
, op
, output
, terminate
);
581 void Job::Alert(const base::string16
& message
) {
582 HandleAlertOrError(true, -1, message
);
585 void Job::OnError(int line_number
, const base::string16
& error
) {
586 HandleAlertOrError(false, line_number
, error
);
589 bool Job::ResolveDnsBlocking(const std::string
& host
,
590 ResolveDnsOperation op
,
591 std::string
* output
) {
592 CheckIsOnWorkerThread();
594 // Check if the DNS result for this host has already been cached.
596 if (GetDnsFromLocalCache(host
, op
, output
, &rv
)) {
601 if (dns_cache_
.size() >= kMaxUniqueResolveDnsPerExec
) {
602 // Safety net for scripts with unexpectedly many DNS calls.
603 // We will continue running to completion, but will fail every
604 // subsequent DNS request.
608 if (!PostDnsOperationAndWait(host
, op
, NULL
))
609 return false; // Was cancelled.
611 CHECK(GetDnsFromLocalCache(host
, op
, output
, &rv
));
615 bool Job::ResolveDnsNonBlocking(const std::string
& host
,
616 ResolveDnsOperation op
,
619 CheckIsOnWorkerThread();
622 // If this execution was already abandoned can fail right away. Only 1 DNS
623 // dependency will be traced at a time (for more predictable outcomes).
629 // Check if the DNS result for this host has already been cached.
631 if (GetDnsFromLocalCache(host
, op
, output
, &rv
)) {
636 if (num_dns_
<= last_num_dns_
) {
637 // The sequence of DNS operations is different from last time!
638 ScheduleRestartWithBlockingDns();
643 if (dns_cache_
.size() >= kMaxUniqueResolveDnsPerExec
) {
644 // Safety net for scripts with unexpectedly many DNS calls.
648 DCHECK(!should_restart_with_blocking_dns_
);
650 bool completed_synchronously
;
651 if (!PostDnsOperationAndWait(host
, op
, &completed_synchronously
))
652 return false; // Was cancelled.
654 if (completed_synchronously
) {
655 CHECK(GetDnsFromLocalCache(host
, op
, output
, &rv
));
659 // Otherwise if the result was not in the cache, then a DNS request has
660 // been started. Abandon this invocation of FindProxyForURL(), it will be
661 // restarted once the DNS request completes.
664 last_num_dns_
= num_dns_
;
668 bool Job::PostDnsOperationAndWait(const std::string
& host
,
669 ResolveDnsOperation op
,
670 bool* completed_synchronously
) {
671 // Post the DNS request to the origin thread.
672 DCHECK(!pending_dns_
);
673 pending_dns_host_
= host
;
674 pending_dns_op_
= op
;
675 origin_runner_
->PostTask(FROM_HERE
, base::Bind(&Job::DoDnsOperation
, this));
680 if (cancelled_
.IsSet())
683 if (completed_synchronously
)
684 *completed_synchronously
= pending_dns_completed_synchronously_
;
689 void Job::DoDnsOperation() {
690 CheckIsOnOriginThread();
691 DCHECK(!pending_dns_
);
693 if (cancelled_
.IsSet())
696 HostResolver::RequestHandle dns_request
= NULL
;
697 int result
= host_resolver()->Resolve(
698 MakeDnsRequestInfo(pending_dns_host_
, pending_dns_op_
), DEFAULT_PRIORITY
,
699 &pending_dns_addresses_
, base::Bind(&Job::OnDnsOperationComplete
, this),
700 &dns_request
, bindings_
->GetBoundNetLog());
702 pending_dns_completed_synchronously_
= result
!= ERR_IO_PENDING
;
704 // Check if the request was cancelled as a side-effect of calling into the
705 // HostResolver. This isn't the ordinary execution flow, however it is
706 // exercised by unit-tests.
707 if (cancelled_
.IsSet()) {
708 if (!pending_dns_completed_synchronously_
)
709 host_resolver()->CancelRequest(dns_request
);
713 if (pending_dns_completed_synchronously_
) {
714 OnDnsOperationComplete(result
);
717 pending_dns_
= dns_request
;
718 // OnDnsOperationComplete() will be called by host resolver on completion.
721 if (!blocking_dns_
) {
722 // The worker thread always blocks waiting to see if the result can be
723 // serviced from cache before restarting.
728 void Job::OnDnsOperationComplete(int result
) {
729 CheckIsOnOriginThread();
731 DCHECK(!cancelled_
.IsSet());
732 DCHECK(pending_dns_completed_synchronously_
== (pending_dns_
== NULL
));
734 SaveDnsToLocalCache(pending_dns_host_
, pending_dns_op_
, result
,
735 pending_dns_addresses_
);
743 if (!blocking_dns_
&& !pending_dns_completed_synchronously_
) {
744 // Restart. This time it should make more progress due to having
746 worker_task_runner()->PostTask(FROM_HERE
,
747 base::Bind(&Job::ExecuteNonBlocking
, this));
751 void Job::ScheduleRestartWithBlockingDns() {
752 CheckIsOnWorkerThread();
754 DCHECK(!should_restart_with_blocking_dns_
);
756 DCHECK(!blocking_dns_
);
760 // The restart will happen after ExecuteNonBlocking() finishes.
761 should_restart_with_blocking_dns_
= true;
764 bool Job::GetDnsFromLocalCache(const std::string
& host
,
765 ResolveDnsOperation op
,
767 bool* return_value
) {
768 CheckIsOnWorkerThread();
770 DnsCache::const_iterator it
= dns_cache_
.find(MakeDnsCacheKey(host
, op
));
771 if (it
== dns_cache_
.end())
774 *output
= it
->second
;
775 *return_value
= !it
->second
.empty();
779 void Job::SaveDnsToLocalCache(const std::string
& host
,
780 ResolveDnsOperation op
,
782 const AddressList
& addresses
) {
783 CheckIsOnOriginThread();
785 // Serialize the result into a string to save to the cache.
786 std::string cache_value
;
787 if (net_error
!= OK
) {
788 cache_value
= std::string();
789 } else if (op
== DNS_RESOLVE
|| op
== MY_IP_ADDRESS
) {
790 // dnsResolve() and myIpAddress() are expected to return a single IP
792 cache_value
= addresses
.front().ToStringWithoutPort();
794 // The *Ex versions are expected to return a semi-colon separated list.
795 for (AddressList::const_iterator iter
= addresses
.begin();
796 iter
!= addresses
.end(); ++iter
) {
797 if (!cache_value
.empty())
799 cache_value
+= iter
->ToStringWithoutPort();
803 dns_cache_
[MakeDnsCacheKey(host
, op
)] = cache_value
;
807 HostResolver::RequestInfo
Job::MakeDnsRequestInfo(const std::string
& host
,
808 ResolveDnsOperation op
) {
809 HostPortPair host_port
= HostPortPair(host
, 80);
810 if (op
== MY_IP_ADDRESS
|| op
== MY_IP_ADDRESS_EX
) {
811 host_port
.set_host(GetHostName());
814 HostResolver::RequestInfo
info(host_port
);
815 // Flag myIpAddress requests.
816 if (op
== MY_IP_ADDRESS
|| op
== MY_IP_ADDRESS_EX
) {
817 // TODO: Provide a RequestInfo construction mechanism that does not
818 // require a hostname and sets is_my_ip_address to true instead of this.
819 info
.set_is_my_ip_address(true);
821 // The non-ex flavors are limited to IPv4 results.
822 if (op
== MY_IP_ADDRESS
|| op
== DNS_RESOLVE
) {
823 info
.set_address_family(ADDRESS_FAMILY_IPV4
);
829 std::string
Job::MakeDnsCacheKey(const std::string
& host
,
830 ResolveDnsOperation op
) {
831 return base::StringPrintf("%d:%s", op
, host
.c_str());
834 void Job::HandleAlertOrError(bool is_alert
,
836 const base::string16
& message
) {
837 CheckIsOnWorkerThread();
839 if (cancelled_
.IsSet())
843 // In blocking DNS mode the events can be dispatched immediately.
844 DispatchAlertOrError(is_alert
, line_number
, message
);
848 // Otherwise in nonblocking mode, buffer all the messages until
854 alerts_and_errors_byte_cost_
+= sizeof(AlertOrError
) + message
.size() * 2;
856 // If there have been lots of messages, enqueing could be expensive on
857 // memory. Consider a script which does megabytes worth of alerts().
858 // Avoid this by falling back to blocking mode.
859 if (alerts_and_errors_byte_cost_
> kMaxAlertsAndErrorsBytes
) {
860 ScheduleRestartWithBlockingDns();
864 AlertOrError entry
= {is_alert
, line_number
, message
};
865 alerts_and_errors_
.push_back(entry
);
868 void Job::DispatchBufferedAlertsAndErrors() {
869 CheckIsOnWorkerThread();
870 DCHECK(!blocking_dns_
);
873 for (size_t i
= 0; i
< alerts_and_errors_
.size(); ++i
) {
874 const AlertOrError
& x
= alerts_and_errors_
[i
];
875 DispatchAlertOrError(x
.is_alert
, x
.line_number
, x
.message
);
879 void Job::DispatchAlertOrError(bool is_alert
,
881 const base::string16
& message
) {
882 CheckIsOnWorkerThread();
884 // Note that the handling of cancellation is racy with regard to
885 // alerts/errors. The request might get cancelled shortly after this
886 // check! (There is no lock being held to guarantee otherwise).
888 // If this happens, then some information will be logged needlessly, however
889 // the Bindings are responsible for handling this case so it shouldn't cause
891 if (cancelled_
.IsSet())
895 // -------------------
897 // -------------------
898 VLOG(1) << "PAC-alert: " << message
;
900 bindings_
->Alert(message
);
902 // -------------------
904 // -------------------
905 if (line_number
== -1)
906 VLOG(1) << "PAC-error: " << message
;
908 VLOG(1) << "PAC-error: " << "line: " << line_number
<< ": " << message
;
910 bindings_
->OnError(line_number
, message
);
914 ProxyResolverV8TracingImpl::ProxyResolverV8TracingImpl(
915 scoped_ptr
<base::Thread
> thread
,
916 scoped_ptr
<ProxyResolverV8
> resolver
,
917 scoped_ptr
<Job::Params
> job_params
)
918 : thread_(thread
.Pass()),
919 v8_resolver_(resolver
.Pass()),
920 job_params_(job_params
.Pass()),
921 num_outstanding_callbacks_(0) {
922 job_params_
->num_outstanding_callbacks
= &num_outstanding_callbacks_
;
925 ProxyResolverV8TracingImpl::~ProxyResolverV8TracingImpl() {
926 // Note, all requests should have been cancelled.
927 CHECK_EQ(0, num_outstanding_callbacks_
);
929 // Join the worker thread. See http://crbug.com/69710.
930 base::ThreadRestrictions::ScopedAllowIO allow_io
;
934 void ProxyResolverV8TracingImpl::GetProxyForURL(
937 const CompletionCallback
& callback
,
938 ProxyResolver::RequestHandle
* request
,
939 scoped_ptr
<Bindings
> bindings
) {
940 DCHECK(CalledOnValidThread());
941 DCHECK(!callback
.is_null());
943 scoped_refptr
<Job
> job
= new Job(job_params_
.get(), bindings
.Pass());
946 *request
= job
.get();
948 job
->StartGetProxyForURL(url
, results
, callback
);
951 void ProxyResolverV8TracingImpl::CancelRequest(
952 ProxyResolver::RequestHandle request
) {
953 Job
* job
= reinterpret_cast<Job
*>(request
);
957 LoadState
ProxyResolverV8TracingImpl::GetLoadState(
958 ProxyResolver::RequestHandle request
) const {
959 Job
* job
= reinterpret_cast<Job
*>(request
);
960 return job
->GetLoadState();
963 class ProxyResolverV8TracingFactoryImpl
: public ProxyResolverV8TracingFactory
{
965 ProxyResolverV8TracingFactoryImpl();
966 ~ProxyResolverV8TracingFactoryImpl() override
;
968 void CreateProxyResolverV8Tracing(
969 const scoped_refptr
<ProxyResolverScriptData
>& pac_script
,
970 scoped_ptr
<ProxyResolverV8Tracing::Bindings
> bindings
,
971 scoped_ptr
<ProxyResolverV8Tracing
>* resolver
,
972 const CompletionCallback
& callback
,
973 scoped_ptr
<ProxyResolverFactory::Request
>* request
) override
;
978 void RemoveJob(CreateJob
* job
);
980 std::set
<CreateJob
*> jobs_
;
982 DISALLOW_COPY_AND_ASSIGN(ProxyResolverV8TracingFactoryImpl
);
985 class ProxyResolverV8TracingFactoryImpl::CreateJob
986 : public ProxyResolverFactory::Request
{
988 CreateJob(ProxyResolverV8TracingFactoryImpl
* factory
,
989 scoped_ptr
<ProxyResolverV8Tracing::Bindings
> bindings
,
990 const scoped_refptr
<ProxyResolverScriptData
>& pac_script
,
991 scoped_ptr
<ProxyResolverV8Tracing
>* resolver_out
,
992 const CompletionCallback
& callback
)
994 thread_(new base::Thread("Proxy Resolver")),
995 resolver_out_(resolver_out
),
997 num_outstanding_callbacks_(0) {
998 // Start up the thread.
999 base::Thread::Options options
;
1000 options
.timer_slack
= base::TIMER_SLACK_MAXIMUM
;
1001 CHECK(thread_
->StartWithOptions(options
));
1003 new Job::Params(thread_
->task_runner(), &num_outstanding_callbacks_
));
1004 create_resolver_job_
= new Job(job_params_
.get(), bindings
.Pass());
1005 create_resolver_job_
->StartCreateV8Resolver(
1006 pac_script
, &v8_resolver_
,
1008 &ProxyResolverV8TracingFactoryImpl::CreateJob::OnV8ResolverCreated
,
1009 base::Unretained(this)));
1012 ~CreateJob() override
{
1014 factory_
->RemoveJob(this);
1015 DCHECK(create_resolver_job_
);
1016 create_resolver_job_
->Cancel();
1019 DCHECK_EQ(0, num_outstanding_callbacks_
);
1022 void FactoryDestroyed() {
1024 create_resolver_job_
->Cancel();
1025 create_resolver_job_
= nullptr;
1030 void OnV8ResolverCreated(int error
) {
1033 job_params_
->v8_resolver
= v8_resolver_
.get();
1034 resolver_out_
->reset(new ProxyResolverV8TracingImpl(
1035 thread_
.Pass(), v8_resolver_
.Pass(), job_params_
.Pass()));
1040 factory_
->RemoveJob(this);
1042 create_resolver_job_
= nullptr;
1043 callback_
.Run(error
);
1046 void StopWorkerThread() {
1047 // Join the worker thread. See http://crbug.com/69710.
1048 base::ThreadRestrictions::ScopedAllowIO allow_io
;
1052 ProxyResolverV8TracingFactoryImpl
* factory_
;
1053 scoped_ptr
<base::Thread
> thread_
;
1054 scoped_ptr
<Job::Params
> job_params_
;
1055 scoped_refptr
<Job
> create_resolver_job_
;
1056 scoped_ptr
<ProxyResolverV8
> v8_resolver_
;
1057 scoped_ptr
<ProxyResolverV8Tracing
>* resolver_out_
;
1058 const CompletionCallback callback_
;
1059 int num_outstanding_callbacks_
;
1061 DISALLOW_COPY_AND_ASSIGN(CreateJob
);
1064 ProxyResolverV8TracingFactoryImpl::ProxyResolverV8TracingFactoryImpl() {
1067 ProxyResolverV8TracingFactoryImpl::~ProxyResolverV8TracingFactoryImpl() {
1068 for (auto job
: jobs_
) {
1069 job
->FactoryDestroyed();
1073 void ProxyResolverV8TracingFactoryImpl::CreateProxyResolverV8Tracing(
1074 const scoped_refptr
<ProxyResolverScriptData
>& pac_script
,
1075 scoped_ptr
<ProxyResolverV8Tracing::Bindings
> bindings
,
1076 scoped_ptr
<ProxyResolverV8Tracing
>* resolver
,
1077 const CompletionCallback
& callback
,
1078 scoped_ptr
<ProxyResolverFactory::Request
>* request
) {
1079 scoped_ptr
<CreateJob
> job(
1080 new CreateJob(this, bindings
.Pass(), pac_script
, resolver
, callback
));
1081 jobs_
.insert(job
.get());
1082 *request
= job
.Pass();
1085 void ProxyResolverV8TracingFactoryImpl::RemoveJob(
1086 ProxyResolverV8TracingFactoryImpl::CreateJob
* job
) {
1087 size_t erased
= jobs_
.erase(job
);
1088 DCHECK_EQ(1u, erased
);
1094 scoped_ptr
<ProxyResolverV8TracingFactory
>
1095 ProxyResolverV8TracingFactory::Create() {
1096 return make_scoped_ptr(new ProxyResolverV8TracingFactoryImpl());