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/base/net_util.h"
22 #include "net/dns/host_resolver.h"
23 #include "net/proxy/proxy_info.h"
24 #include "net/proxy/proxy_resolver_error_observer.h"
25 #include "net/proxy/proxy_resolver_v8.h"
27 // The intent of this class is explained in the design document:
28 // https://docs.google.com/a/chromium.org/document/d/16Ij5OcVnR3s0MH4Z5XkhI9VTPoMJdaBn9rKreAmGOdE/edit
30 // In a nutshell, PAC scripts are Javascript programs and may depend on
31 // network I/O, by calling functions like dnsResolve().
33 // This is problematic since functions such as dnsResolve() will block the
34 // Javascript execution until the DNS result is availble, thereby stalling the
35 // PAC thread, which hurts the ability to process parallel proxy resolves.
36 // An obvious solution is to simply start more PAC threads, however this scales
37 // poorly, which hurts the ability to process parallel proxy resolves.
39 // The solution in ProxyResolverV8Tracing is to model PAC scripts as being
40 // deterministic, and depending only on the inputted URL. When the script
41 // issues a dnsResolve() for a yet unresolved hostname, the Javascript
42 // execution is "aborted", and then re-started once the DNS result is
48 // Upper bound on how many *unique* DNS resolves a PAC script is allowed
49 // to make. This is a failsafe both for scripts that do a ridiculous
50 // number of DNS resolves, as well as scripts which are misbehaving
51 // under the tracing optimization. It is not expected to hit this normally.
52 const size_t kMaxUniqueResolveDnsPerExec
= 20;
54 // Approximate number of bytes to use for buffering alerts() and errors.
55 // This is a failsafe in case repeated executions of the script causes
56 // too much memory bloat. It is not expected for well behaved scripts to
57 // hit this. (In fact normal scripts should not even have alerts() or errors).
58 const size_t kMaxAlertsAndErrorsBytes
= 2048;
60 // The Job class is responsible for executing GetProxyForURL() and
61 // creating ProxyResolverV8 instances, since both of these operations share
64 // The DNS for these operations can operate in either blocking or
65 // non-blocking mode. Blocking mode is used as a fallback when the PAC script
66 // seems to be misbehaving under the tracing optimization.
68 // Note that this class runs on both the origin thread and a worker
69 // thread. Most methods are expected to be used exclusively on one thread
72 // The lifetime of Jobs does not exceed that of the ProxyResolverV8TracingImpl
73 // that spawned it. Destruction might happen on either the origin thread or the
75 class Job
: public base::RefCountedThreadSafe
<Job
>,
76 public ProxyResolverV8::JSBindings
{
80 const scoped_refptr
<base::SingleThreadTaskRunner
>& worker_task_runner
,
81 int* num_outstanding_callbacks
)
82 : v8_resolver(nullptr),
83 worker_task_runner(worker_task_runner
),
84 num_outstanding_callbacks(num_outstanding_callbacks
) {}
86 ProxyResolverV8
* v8_resolver
;
87 scoped_refptr
<base::SingleThreadTaskRunner
> worker_task_runner
;
88 int* num_outstanding_callbacks
;
90 // |params| is non-owned. It contains the parameters for this Job, and must
92 Job(const Params
* params
,
93 scoped_ptr
<ProxyResolverV8Tracing::Bindings
> bindings
);
95 // Called from origin thread.
96 void StartCreateV8Resolver(
97 const scoped_refptr
<ProxyResolverScriptData
>& script_data
,
98 scoped_ptr
<ProxyResolverV8
>* resolver
,
99 const CompletionCallback
& callback
);
101 // Called from origin thread.
102 void StartGetProxyForURL(const GURL
& url
,
104 const CompletionCallback
& callback
);
106 // Called from origin thread.
109 // Called from origin thread.
110 LoadState
GetLoadState() const;
113 typedef std::map
<std::string
, std::string
> DnsCache
;
114 friend class base::RefCountedThreadSafe
<Job
>;
121 struct AlertOrError
{
124 base::string16 message
;
129 void CheckIsOnWorkerThread() const;
130 void CheckIsOnOriginThread() const;
132 void SetCallback(const CompletionCallback
& callback
);
133 void ReleaseCallback();
135 ProxyResolverV8
* v8_resolver();
136 const scoped_refptr
<base::SingleThreadTaskRunner
>& worker_task_runner();
137 HostResolver
* host_resolver();
139 // Invokes the user's callback.
140 void NotifyCaller(int result
);
141 void NotifyCallerOnOriginLoop(int result
);
143 void Start(Operation op
, bool blocking_dns
,
144 const CompletionCallback
& callback
);
146 void ExecuteBlocking();
147 void ExecuteNonBlocking();
148 int ExecuteProxyResolver();
150 // Implementation of ProxyResolverv8::JSBindings
151 bool ResolveDns(const std::string
& host
,
152 ResolveDnsOperation op
,
154 bool* terminate
) override
;
155 void Alert(const base::string16
& message
) override
;
156 void OnError(int line_number
, const base::string16
& error
) override
;
158 bool ResolveDnsBlocking(const std::string
& host
,
159 ResolveDnsOperation op
,
160 std::string
* output
);
162 bool ResolveDnsNonBlocking(const std::string
& host
,
163 ResolveDnsOperation op
,
167 bool PostDnsOperationAndWait(const std::string
& host
,
168 ResolveDnsOperation op
,
169 bool* completed_synchronously
)
172 void DoDnsOperation();
173 void OnDnsOperationComplete(int result
);
175 void ScheduleRestartWithBlockingDns();
177 bool GetDnsFromLocalCache(const std::string
& host
, ResolveDnsOperation op
,
178 std::string
* output
, bool* return_value
);
180 void SaveDnsToLocalCache(const std::string
& host
,
181 ResolveDnsOperation op
,
183 const AddressList
& addresses
);
185 // Builds a RequestInfo to service the specified PAC DNS operation.
186 static HostResolver::RequestInfo
MakeDnsRequestInfo(const std::string
& host
,
187 ResolveDnsOperation op
);
189 // Makes a key for looking up |host, op| in |dns_cache_|. Strings are used for
190 // convenience, to avoid defining custom comparators.
191 static std::string
MakeDnsCacheKey(const std::string
& host
,
192 ResolveDnsOperation op
);
194 void HandleAlertOrError(bool is_alert
, int line_number
,
195 const base::string16
& message
);
196 void DispatchBufferedAlertsAndErrors();
197 void DispatchAlertOrErrorOnOriginThread(bool is_alert
,
199 const base::string16
& message
);
201 // The thread which called into ProxyResolverV8TracingImpl, and on which the
202 // completion callback is expected to run.
203 scoped_refptr
<base::SingleThreadTaskRunner
> origin_runner_
;
205 // The Parameters for this Job.
206 // Initialized on origin thread and then accessed from both threads.
207 const Params
* const params_
;
209 scoped_ptr
<ProxyResolverV8Tracing::Bindings
> bindings_
;
211 // The callback to run (on the origin thread) when the Job finishes.
212 // Should only be accessed from origin thread.
213 CompletionCallback callback_
;
215 // Flag to indicate whether the request has been cancelled.
216 base::CancellationFlag cancelled_
;
218 // The operation that this Job is running.
219 // Initialized on origin thread and then accessed from both threads.
220 Operation operation_
;
222 // The DNS mode for this Job.
223 // Initialized on origin thread, mutated on worker thread, and accessed
224 // by both the origin thread and worker thread.
227 // Used to block the worker thread on a DNS operation taking place on the
229 base::WaitableEvent event_
;
231 // Map of DNS operations completed so far. Written into on the origin thread
232 // and read on the worker thread.
235 // The job holds a reference to itself to ensure that it remains alive until
236 // either completion or cancellation.
237 scoped_refptr
<Job
> owned_self_reference_
;
239 // -------------------------------------------------------
240 // State specific to CREATE_V8_RESOLVER.
241 // -------------------------------------------------------
243 scoped_refptr
<ProxyResolverScriptData
> script_data_
;
244 scoped_ptr
<ProxyResolverV8
>* resolver_out_
;
246 // -------------------------------------------------------
247 // State specific to GET_PROXY_FOR_URL.
248 // -------------------------------------------------------
250 ProxyInfo
* user_results_
; // Owned by caller, lives on origin thread.
254 // ---------------------------------------------------------------------------
255 // State for ExecuteNonBlocking()
256 // ---------------------------------------------------------------------------
257 // These variables are used exclusively on the worker thread and are only
258 // meaningful when executing inside of ExecuteNonBlocking().
260 // Whether this execution was abandoned due to a missing DNS dependency.
263 // Number of calls made to ResolveDns() by this execution.
266 // Sequence of calls made to Alert() or OnError() by this execution.
267 std::vector
<AlertOrError
> alerts_and_errors_
;
268 size_t alerts_and_errors_byte_cost_
; // Approximate byte cost of the above.
270 // Number of calls made to ResolveDns() by the PREVIOUS execution.
273 // Whether the current execution needs to be restarted in blocking mode.
274 bool should_restart_with_blocking_dns_
;
276 // ---------------------------------------------------------------------------
277 // State for pending DNS request.
278 // ---------------------------------------------------------------------------
280 // Handle to the outstanding request in the HostResolver, or NULL.
281 // This is mutated and used on the origin thread, however it may be read by
282 // the worker thread for some DCHECKS().
283 HostResolver::RequestHandle pending_dns_
;
285 // Indicates if the outstanding DNS request completed synchronously. Written
286 // on the origin thread, and read by the worker thread.
287 bool pending_dns_completed_synchronously_
;
289 // These are the inputs to DoDnsOperation(). Written on the worker thread,
290 // read by the origin thread.
291 std::string pending_dns_host_
;
292 ResolveDnsOperation pending_dns_op_
;
294 // This contains the resolved address list that DoDnsOperation() fills in.
295 // Used exclusively on the origin thread.
296 AddressList pending_dns_addresses_
;
299 class ProxyResolverV8TracingImpl
: public ProxyResolverV8Tracing
,
300 public base::NonThreadSafe
{
302 ProxyResolverV8TracingImpl(scoped_ptr
<base::Thread
> thread
,
303 scoped_ptr
<ProxyResolverV8
> resolver
,
304 scoped_ptr
<Job::Params
> job_params
);
306 ~ProxyResolverV8TracingImpl() override
;
308 // ProxyResolverV8Tracing overrides.
309 void GetProxyForURL(const GURL
& url
,
311 const CompletionCallback
& callback
,
312 ProxyResolver::RequestHandle
* request
,
313 scoped_ptr
<Bindings
> bindings
) override
;
314 void CancelRequest(ProxyResolver::RequestHandle request
) override
;
315 LoadState
GetLoadState(ProxyResolver::RequestHandle request
) const override
;
318 // The worker thread on which the ProxyResolverV8 will be run.
319 scoped_ptr
<base::Thread
> thread_
;
320 scoped_ptr
<ProxyResolverV8
> v8_resolver_
;
322 scoped_ptr
<Job::Params
> job_params_
;
324 // The number of outstanding (non-cancelled) jobs.
325 int num_outstanding_callbacks_
;
327 DISALLOW_COPY_AND_ASSIGN(ProxyResolverV8TracingImpl
);
330 Job::Job(const Job::Params
* params
,
331 scoped_ptr
<ProxyResolverV8Tracing::Bindings
> bindings
)
332 : origin_runner_(base::ThreadTaskRunnerHandle::Get()),
334 bindings_(bindings
.Pass()),
338 CheckIsOnOriginThread();
341 void Job::StartCreateV8Resolver(
342 const scoped_refptr
<ProxyResolverScriptData
>& script_data
,
343 scoped_ptr
<ProxyResolverV8
>* resolver
,
344 const CompletionCallback
& callback
) {
345 CheckIsOnOriginThread();
347 resolver_out_
= resolver
;
348 script_data_
= script_data
;
350 // Script initialization uses blocking DNS since there isn't any
351 // advantage to using non-blocking mode here. That is because the
352 // parent ProxyService can't submit any ProxyResolve requests until
353 // initialization has completed successfully!
354 Start(CREATE_V8_RESOLVER
, true /*blocking*/, callback
);
357 void Job::StartGetProxyForURL(const GURL
& url
,
359 const CompletionCallback
& callback
) {
360 CheckIsOnOriginThread();
363 user_results_
= results
;
365 Start(GET_PROXY_FOR_URL
, false /*non-blocking*/, callback
);
369 CheckIsOnOriginThread();
371 // There are several possibilities to consider for cancellation:
372 // (a) The job has been posted to the worker thread, however script execution
373 // has not yet started.
374 // (b) The script is executing on the worker thread.
375 // (c) The script is executing on the worker thread, however is blocked inside
376 // of dnsResolve() waiting for a response from the origin thread.
377 // (d) Nothing is running on the worker thread, however the host resolver has
378 // a pending DNS request which upon completion will restart the script
380 // (e) The worker thread has a pending task to restart execution, which was
381 // posted after the DNS dependency was resolved and saved to local cache.
382 // (f) The script execution completed entirely, and posted a task to the
383 // origin thread to notify the caller.
385 // |cancelled_| is read on both the origin thread and worker thread. The
386 // code that runs on the worker thread is littered with checks on
387 // |cancelled_| to break out early.
393 host_resolver()->CancelRequest(pending_dns_
);
397 // The worker thread might be blocked waiting for DNS.
401 owned_self_reference_
= NULL
;
404 LoadState
Job::GetLoadState() const {
405 CheckIsOnOriginThread();
408 return LOAD_STATE_RESOLVING_HOST_IN_PROXY_SCRIPT
;
410 return LOAD_STATE_RESOLVING_PROXY_FOR_URL
;
414 DCHECK(!pending_dns_
);
415 DCHECK(callback_
.is_null());
419 void Job::CheckIsOnWorkerThread() const {
420 DCHECK(params_
->worker_task_runner
->BelongsToCurrentThread());
423 void Job::CheckIsOnOriginThread() const {
424 DCHECK(origin_runner_
->BelongsToCurrentThread());
427 void Job::SetCallback(const CompletionCallback
& callback
) {
428 CheckIsOnOriginThread();
429 DCHECK(callback_
.is_null());
430 (*params_
->num_outstanding_callbacks
)++;
431 callback_
= callback
;
434 void Job::ReleaseCallback() {
435 CheckIsOnOriginThread();
436 DCHECK(!callback_
.is_null());
437 CHECK_GT(*params_
->num_outstanding_callbacks
, 0);
438 (*params_
->num_outstanding_callbacks
)--;
441 // For good measure, clear this other user-owned pointer.
442 user_results_
= NULL
;
445 ProxyResolverV8
* Job::v8_resolver() {
446 return params_
->v8_resolver
;
449 const scoped_refptr
<base::SingleThreadTaskRunner
>& Job::worker_task_runner() {
450 return params_
->worker_task_runner
;
453 HostResolver
* Job::host_resolver() {
454 return bindings_
->GetHostResolver();
457 void Job::NotifyCaller(int result
) {
458 CheckIsOnWorkerThread();
460 origin_runner_
->PostTask(
461 FROM_HERE
, base::Bind(&Job::NotifyCallerOnOriginLoop
, this, result
));
464 void Job::NotifyCallerOnOriginLoop(int result
) {
465 CheckIsOnOriginThread();
467 if (cancelled_
.IsSet())
470 DispatchBufferedAlertsAndErrors();
472 // This isn't the ordinary execution flow, however it is exercised by
474 if (cancelled_
.IsSet())
477 DCHECK(!callback_
.is_null());
478 DCHECK(!pending_dns_
);
480 if (operation_
== GET_PROXY_FOR_URL
) {
481 *user_results_
= results_
;
484 CompletionCallback callback
= callback_
;
486 callback
.Run(result
);
489 owned_self_reference_
= NULL
;
492 void Job::Start(Operation op
,
494 const CompletionCallback
& callback
) {
495 CheckIsOnOriginThread();
498 blocking_dns_
= blocking_dns
;
499 SetCallback(callback
);
501 owned_self_reference_
= this;
503 worker_task_runner()->PostTask(
504 FROM_HERE
, blocking_dns_
? base::Bind(&Job::ExecuteBlocking
, this)
505 : base::Bind(&Job::ExecuteNonBlocking
, this));
508 void Job::ExecuteBlocking() {
509 CheckIsOnWorkerThread();
510 DCHECK(blocking_dns_
);
512 if (cancelled_
.IsSet())
515 NotifyCaller(ExecuteProxyResolver());
518 void Job::ExecuteNonBlocking() {
519 CheckIsOnWorkerThread();
520 DCHECK(!blocking_dns_
);
522 if (cancelled_
.IsSet())
525 // Reset state for the current execution.
528 alerts_and_errors_
.clear();
529 alerts_and_errors_byte_cost_
= 0;
530 should_restart_with_blocking_dns_
= false;
532 int result
= ExecuteProxyResolver();
534 if (should_restart_with_blocking_dns_
) {
535 DCHECK(!blocking_dns_
);
537 blocking_dns_
= true;
545 NotifyCaller(result
);
548 int Job::ExecuteProxyResolver() {
549 int result
= ERR_UNEXPECTED
; // Initialized to silence warnings.
551 switch (operation_
) {
552 case CREATE_V8_RESOLVER
: {
553 scoped_ptr
<ProxyResolverV8
> resolver
;
554 result
= ProxyResolverV8::Create(script_data_
, this, &resolver
);
556 *resolver_out_
= resolver
.Pass();
559 case GET_PROXY_FOR_URL
: {
560 result
= v8_resolver()->GetProxyForURL(
562 // Important: Do not write directly into |user_results_|, since if the
563 // request were to be cancelled from the origin thread, must guarantee
564 // that |user_results_| is not accessed anymore.
573 bool Job::ResolveDns(const std::string
& host
,
574 ResolveDnsOperation op
,
577 if (cancelled_
.IsSet()) {
582 if ((op
== DNS_RESOLVE
|| op
== DNS_RESOLVE_EX
) && host
.empty()) {
583 // a DNS resolve with an empty hostname is considered an error.
587 return blocking_dns_
?
588 ResolveDnsBlocking(host
, op
, output
) :
589 ResolveDnsNonBlocking(host
, op
, output
, terminate
);
592 void Job::Alert(const base::string16
& message
) {
593 HandleAlertOrError(true, -1, message
);
596 void Job::OnError(int line_number
, const base::string16
& error
) {
597 HandleAlertOrError(false, line_number
, error
);
600 bool Job::ResolveDnsBlocking(const std::string
& host
,
601 ResolveDnsOperation op
,
602 std::string
* output
) {
603 CheckIsOnWorkerThread();
605 // Check if the DNS result for this host has already been cached.
607 if (GetDnsFromLocalCache(host
, op
, output
, &rv
)) {
612 if (dns_cache_
.size() >= kMaxUniqueResolveDnsPerExec
) {
613 // Safety net for scripts with unexpectedly many DNS calls.
614 // We will continue running to completion, but will fail every
615 // subsequent DNS request.
619 if (!PostDnsOperationAndWait(host
, op
, NULL
))
620 return false; // Was cancelled.
622 CHECK(GetDnsFromLocalCache(host
, op
, output
, &rv
));
626 bool Job::ResolveDnsNonBlocking(const std::string
& host
,
627 ResolveDnsOperation op
,
630 CheckIsOnWorkerThread();
633 // If this execution was already abandoned can fail right away. Only 1 DNS
634 // dependency will be traced at a time (for more predictable outcomes).
640 // Check if the DNS result for this host has already been cached.
642 if (GetDnsFromLocalCache(host
, op
, output
, &rv
)) {
647 if (num_dns_
<= last_num_dns_
) {
648 // The sequence of DNS operations is different from last time!
649 ScheduleRestartWithBlockingDns();
654 if (dns_cache_
.size() >= kMaxUniqueResolveDnsPerExec
) {
655 // Safety net for scripts with unexpectedly many DNS calls.
659 DCHECK(!should_restart_with_blocking_dns_
);
661 bool completed_synchronously
;
662 if (!PostDnsOperationAndWait(host
, op
, &completed_synchronously
))
663 return false; // Was cancelled.
665 if (completed_synchronously
) {
666 CHECK(GetDnsFromLocalCache(host
, op
, output
, &rv
));
670 // Otherwise if the result was not in the cache, then a DNS request has
671 // been started. Abandon this invocation of FindProxyForURL(), it will be
672 // restarted once the DNS request completes.
675 last_num_dns_
= num_dns_
;
679 bool Job::PostDnsOperationAndWait(const std::string
& host
,
680 ResolveDnsOperation op
,
681 bool* completed_synchronously
) {
682 // Post the DNS request to the origin thread.
683 DCHECK(!pending_dns_
);
684 pending_dns_host_
= host
;
685 pending_dns_op_
= op
;
686 origin_runner_
->PostTask(FROM_HERE
, base::Bind(&Job::DoDnsOperation
, this));
691 if (cancelled_
.IsSet())
694 if (completed_synchronously
)
695 *completed_synchronously
= pending_dns_completed_synchronously_
;
700 void Job::DoDnsOperation() {
701 CheckIsOnOriginThread();
702 DCHECK(!pending_dns_
);
704 if (cancelled_
.IsSet())
707 HostResolver::RequestHandle dns_request
= NULL
;
708 int result
= host_resolver()->Resolve(
709 MakeDnsRequestInfo(pending_dns_host_
, pending_dns_op_
), DEFAULT_PRIORITY
,
710 &pending_dns_addresses_
, base::Bind(&Job::OnDnsOperationComplete
, this),
711 &dns_request
, bindings_
->GetBoundNetLog());
713 pending_dns_completed_synchronously_
= result
!= ERR_IO_PENDING
;
715 // Check if the request was cancelled as a side-effect of calling into the
716 // HostResolver. This isn't the ordinary execution flow, however it is
717 // exercised by unit-tests.
718 if (cancelled_
.IsSet())
721 if (pending_dns_completed_synchronously_
) {
722 OnDnsOperationComplete(result
);
725 pending_dns_
= dns_request
;
726 // OnDnsOperationComplete() will be called by host resolver on completion.
729 if (!blocking_dns_
) {
730 // The worker thread always blocks waiting to see if the result can be
731 // serviced from cache before restarting.
736 void Job::OnDnsOperationComplete(int result
) {
737 CheckIsOnOriginThread();
739 DCHECK(!cancelled_
.IsSet());
740 DCHECK(pending_dns_completed_synchronously_
== (pending_dns_
== NULL
));
742 SaveDnsToLocalCache(pending_dns_host_
, pending_dns_op_
, result
,
743 pending_dns_addresses_
);
751 if (!blocking_dns_
&& !pending_dns_completed_synchronously_
) {
752 // Restart. This time it should make more progress due to having
754 worker_task_runner()->PostTask(FROM_HERE
,
755 base::Bind(&Job::ExecuteNonBlocking
, this));
759 void Job::ScheduleRestartWithBlockingDns() {
760 CheckIsOnWorkerThread();
762 DCHECK(!should_restart_with_blocking_dns_
);
764 DCHECK(!blocking_dns_
);
768 // The restart will happen after ExecuteNonBlocking() finishes.
769 should_restart_with_blocking_dns_
= true;
772 bool Job::GetDnsFromLocalCache(const std::string
& host
,
773 ResolveDnsOperation op
,
775 bool* return_value
) {
776 CheckIsOnWorkerThread();
778 DnsCache::const_iterator it
= dns_cache_
.find(MakeDnsCacheKey(host
, op
));
779 if (it
== dns_cache_
.end())
782 *output
= it
->second
;
783 *return_value
= !it
->second
.empty();
787 void Job::SaveDnsToLocalCache(const std::string
& host
,
788 ResolveDnsOperation op
,
790 const AddressList
& addresses
) {
791 CheckIsOnOriginThread();
793 // Serialize the result into a string to save to the cache.
794 std::string cache_value
;
795 if (net_error
!= OK
) {
796 cache_value
= std::string();
797 } else if (op
== DNS_RESOLVE
|| op
== MY_IP_ADDRESS
) {
798 // dnsResolve() and myIpAddress() are expected to return a single IP
800 cache_value
= addresses
.front().ToStringWithoutPort();
802 // The *Ex versions are expected to return a semi-colon separated list.
803 for (AddressList::const_iterator iter
= addresses
.begin();
804 iter
!= addresses
.end(); ++iter
) {
805 if (!cache_value
.empty())
807 cache_value
+= iter
->ToStringWithoutPort();
811 dns_cache_
[MakeDnsCacheKey(host
, op
)] = cache_value
;
815 HostResolver::RequestInfo
Job::MakeDnsRequestInfo(const std::string
& host
,
816 ResolveDnsOperation op
) {
817 HostPortPair host_port
= HostPortPair(host
, 80);
818 if (op
== MY_IP_ADDRESS
|| op
== MY_IP_ADDRESS_EX
) {
819 host_port
.set_host(GetHostName());
822 HostResolver::RequestInfo
info(host_port
);
823 // Flag myIpAddress requests.
824 if (op
== MY_IP_ADDRESS
|| op
== MY_IP_ADDRESS_EX
) {
825 // TODO: Provide a RequestInfo construction mechanism that does not
826 // require a hostname and sets is_my_ip_address to true instead of this.
827 info
.set_is_my_ip_address(true);
829 // The non-ex flavors are limited to IPv4 results.
830 if (op
== MY_IP_ADDRESS
|| op
== DNS_RESOLVE
) {
831 info
.set_address_family(ADDRESS_FAMILY_IPV4
);
837 std::string
Job::MakeDnsCacheKey(const std::string
& host
,
838 ResolveDnsOperation op
) {
839 return base::StringPrintf("%d:%s", op
, host
.c_str());
842 void Job::HandleAlertOrError(bool is_alert
,
844 const base::string16
& message
) {
845 CheckIsOnWorkerThread();
847 if (cancelled_
.IsSet())
851 // In blocking DNS mode the events can be dispatched immediately.
852 origin_runner_
->PostTask(
853 FROM_HERE
, base::Bind(&Job::DispatchAlertOrErrorOnOriginThread
, this,
854 is_alert
, line_number
, message
));
858 // Otherwise in nonblocking mode, buffer all the messages until
864 alerts_and_errors_byte_cost_
+= sizeof(AlertOrError
) + message
.size() * 2;
866 // If there have been lots of messages, enqueing could be expensive on
867 // memory. Consider a script which does megabytes worth of alerts().
868 // Avoid this by falling back to blocking mode.
869 if (alerts_and_errors_byte_cost_
> kMaxAlertsAndErrorsBytes
) {
870 alerts_and_errors_
.clear();
871 ScheduleRestartWithBlockingDns();
875 AlertOrError entry
= {is_alert
, line_number
, message
};
876 alerts_and_errors_
.push_back(entry
);
879 void Job::DispatchBufferedAlertsAndErrors() {
880 CheckIsOnOriginThread();
881 for (size_t i
= 0; i
< alerts_and_errors_
.size(); ++i
) {
882 const AlertOrError
& x
= alerts_and_errors_
[i
];
883 DispatchAlertOrErrorOnOriginThread(x
.is_alert
, x
.line_number
, x
.message
);
887 void Job::DispatchAlertOrErrorOnOriginThread(bool is_alert
,
889 const base::string16
& message
) {
890 CheckIsOnOriginThread();
892 if (cancelled_
.IsSet())
896 // -------------------
898 // -------------------
899 VLOG(1) << "PAC-alert: " << message
;
901 bindings_
->Alert(message
);
903 // -------------------
905 // -------------------
906 if (line_number
== -1)
907 VLOG(1) << "PAC-error: " << message
;
909 VLOG(1) << "PAC-error: " << "line: " << line_number
<< ": " << message
;
911 bindings_
->OnError(line_number
, message
);
915 ProxyResolverV8TracingImpl::ProxyResolverV8TracingImpl(
916 scoped_ptr
<base::Thread
> thread
,
917 scoped_ptr
<ProxyResolverV8
> resolver
,
918 scoped_ptr
<Job::Params
> job_params
)
919 : thread_(thread
.Pass()),
920 v8_resolver_(resolver
.Pass()),
921 job_params_(job_params
.Pass()),
922 num_outstanding_callbacks_(0) {
923 job_params_
->num_outstanding_callbacks
= &num_outstanding_callbacks_
;
926 ProxyResolverV8TracingImpl::~ProxyResolverV8TracingImpl() {
927 // Note, all requests should have been cancelled.
928 CHECK_EQ(0, num_outstanding_callbacks_
);
930 // Join the worker thread. See http://crbug.com/69710.
931 base::ThreadRestrictions::ScopedAllowIO allow_io
;
935 void ProxyResolverV8TracingImpl::GetProxyForURL(
938 const CompletionCallback
& callback
,
939 ProxyResolver::RequestHandle
* request
,
940 scoped_ptr
<Bindings
> bindings
) {
941 DCHECK(CalledOnValidThread());
942 DCHECK(!callback
.is_null());
944 scoped_refptr
<Job
> job
= new Job(job_params_
.get(), bindings
.Pass());
947 *request
= job
.get();
949 job
->StartGetProxyForURL(url
, results
, callback
);
952 void ProxyResolverV8TracingImpl::CancelRequest(
953 ProxyResolver::RequestHandle request
) {
954 Job
* job
= reinterpret_cast<Job
*>(request
);
958 LoadState
ProxyResolverV8TracingImpl::GetLoadState(
959 ProxyResolver::RequestHandle request
) const {
960 Job
* job
= reinterpret_cast<Job
*>(request
);
961 return job
->GetLoadState();
964 class ProxyResolverV8TracingFactoryImpl
: public ProxyResolverV8TracingFactory
{
966 ProxyResolverV8TracingFactoryImpl();
967 ~ProxyResolverV8TracingFactoryImpl() override
;
969 void CreateProxyResolverV8Tracing(
970 const scoped_refptr
<ProxyResolverScriptData
>& pac_script
,
971 scoped_ptr
<ProxyResolverV8Tracing::Bindings
> bindings
,
972 scoped_ptr
<ProxyResolverV8Tracing
>* resolver
,
973 const CompletionCallback
& callback
,
974 scoped_ptr
<ProxyResolverFactory::Request
>* request
) override
;
979 void RemoveJob(CreateJob
* job
);
981 std::set
<CreateJob
*> jobs_
;
983 DISALLOW_COPY_AND_ASSIGN(ProxyResolverV8TracingFactoryImpl
);
986 class ProxyResolverV8TracingFactoryImpl::CreateJob
987 : public ProxyResolverFactory::Request
{
989 CreateJob(ProxyResolverV8TracingFactoryImpl
* factory
,
990 scoped_ptr
<ProxyResolverV8Tracing::Bindings
> bindings
,
991 const scoped_refptr
<ProxyResolverScriptData
>& pac_script
,
992 scoped_ptr
<ProxyResolverV8Tracing
>* resolver_out
,
993 const CompletionCallback
& callback
)
995 thread_(new base::Thread("Proxy Resolver")),
996 resolver_out_(resolver_out
),
998 num_outstanding_callbacks_(0) {
999 // Start up the thread.
1000 base::Thread::Options options
;
1001 options
.timer_slack
= base::TIMER_SLACK_MAXIMUM
;
1002 CHECK(thread_
->StartWithOptions(options
));
1004 new Job::Params(thread_
->task_runner(), &num_outstanding_callbacks_
));
1005 create_resolver_job_
= new Job(job_params_
.get(), bindings
.Pass());
1006 create_resolver_job_
->StartCreateV8Resolver(
1007 pac_script
, &v8_resolver_
,
1009 &ProxyResolverV8TracingFactoryImpl::CreateJob::OnV8ResolverCreated
,
1010 base::Unretained(this)));
1013 ~CreateJob() override
{
1015 factory_
->RemoveJob(this);
1016 DCHECK(create_resolver_job_
);
1017 create_resolver_job_
->Cancel();
1020 DCHECK_EQ(0, num_outstanding_callbacks_
);
1023 void FactoryDestroyed() {
1025 create_resolver_job_
->Cancel();
1026 create_resolver_job_
= nullptr;
1031 void OnV8ResolverCreated(int error
) {
1034 job_params_
->v8_resolver
= v8_resolver_
.get();
1035 resolver_out_
->reset(new ProxyResolverV8TracingImpl(
1036 thread_
.Pass(), v8_resolver_
.Pass(), job_params_
.Pass()));
1041 factory_
->RemoveJob(this);
1043 create_resolver_job_
= nullptr;
1044 callback_
.Run(error
);
1047 void StopWorkerThread() {
1048 // Join the worker thread. See http://crbug.com/69710.
1049 base::ThreadRestrictions::ScopedAllowIO allow_io
;
1053 ProxyResolverV8TracingFactoryImpl
* factory_
;
1054 scoped_ptr
<base::Thread
> thread_
;
1055 scoped_ptr
<Job::Params
> job_params_
;
1056 scoped_refptr
<Job
> create_resolver_job_
;
1057 scoped_ptr
<ProxyResolverV8
> v8_resolver_
;
1058 scoped_ptr
<ProxyResolverV8Tracing
>* resolver_out_
;
1059 const CompletionCallback callback_
;
1060 int num_outstanding_callbacks_
;
1062 DISALLOW_COPY_AND_ASSIGN(CreateJob
);
1065 ProxyResolverV8TracingFactoryImpl::ProxyResolverV8TracingFactoryImpl() {
1068 ProxyResolverV8TracingFactoryImpl::~ProxyResolverV8TracingFactoryImpl() {
1069 for (auto job
: jobs_
) {
1070 job
->FactoryDestroyed();
1074 void ProxyResolverV8TracingFactoryImpl::CreateProxyResolverV8Tracing(
1075 const scoped_refptr
<ProxyResolverScriptData
>& pac_script
,
1076 scoped_ptr
<ProxyResolverV8Tracing::Bindings
> bindings
,
1077 scoped_ptr
<ProxyResolverV8Tracing
>* resolver
,
1078 const CompletionCallback
& callback
,
1079 scoped_ptr
<ProxyResolverFactory::Request
>* request
) {
1080 scoped_ptr
<CreateJob
> job(
1081 new CreateJob(this, bindings
.Pass(), pac_script
, resolver
, callback
));
1082 jobs_
.insert(job
.get());
1083 *request
= job
.Pass();
1086 void ProxyResolverV8TracingFactoryImpl::RemoveJob(
1087 ProxyResolverV8TracingFactoryImpl::CreateJob
* job
) {
1088 size_t erased
= jobs_
.erase(job
);
1089 DCHECK_EQ(1u, erased
);
1095 scoped_ptr
<ProxyResolverV8TracingFactory
>
1096 ProxyResolverV8TracingFactory::Create() {
1097 return make_scoped_ptr(new ProxyResolverV8TracingFactoryImpl());