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 "base/values.h"
20 #include "net/base/address_list.h"
21 #include "net/base/net_errors.h"
22 #include "net/dns/host_resolver.h"
23 #include "net/log/net_log.h"
24 #include "net/proxy/proxy_info.h"
25 #include "net/proxy/proxy_resolver_error_observer.h"
26 #include "net/proxy/proxy_resolver_v8.h"
28 // The intent of this class is explained in the design document:
29 // https://docs.google.com/a/chromium.org/document/d/16Ij5OcVnR3s0MH4Z5XkhI9VTPoMJdaBn9rKreAmGOdE/edit
31 // In a nutshell, PAC scripts are Javascript programs and may depend on
32 // network I/O, by calling functions like dnsResolve().
34 // This is problematic since functions such as dnsResolve() will block the
35 // Javascript execution until the DNS result is availble, thereby stalling the
36 // PAC thread, which hurts the ability to process parallel proxy resolves.
37 // An obvious solution is to simply start more PAC threads, however this scales
38 // poorly, which hurts the ability to process parallel proxy resolves.
40 // The solution in ProxyResolverV8Tracing is to model PAC scripts as being
41 // deterministic, and depending only on the inputted URL. When the script
42 // issues a dnsResolve() for a yet unresolved hostname, the Javascript
43 // execution is "aborted", and then re-started once the DNS result is
49 // Upper bound on how many *unique* DNS resolves a PAC script is allowed
50 // to make. This is a failsafe both for scripts that do a ridiculous
51 // number of DNS resolves, as well as scripts which are misbehaving
52 // under the tracing optimization. It is not expected to hit this normally.
53 const size_t kMaxUniqueResolveDnsPerExec
= 20;
55 // Approximate number of bytes to use for buffering alerts() and errors.
56 // This is a failsafe in case repeated executions of the script causes
57 // too much memory bloat. It is not expected for well behaved scripts to
58 // hit this. (In fact normal scripts should not even have alerts() or errors).
59 const size_t kMaxAlertsAndErrorsBytes
= 2048;
61 // Returns event parameters for a PAC error message (line number + message).
62 scoped_ptr
<base::Value
> NetLogErrorCallback(
64 const base::string16
* message
,
65 NetLogCaptureMode
/* capture_mode */) {
66 scoped_ptr
<base::DictionaryValue
> dict(new base::DictionaryValue());
67 dict
->SetInteger("line_number", line_number
);
68 dict
->SetString("message", *message
);
72 // The Job class is responsible for executing GetProxyForURL() and
73 // creating ProxyResolverV8 instances, since both of these operations share
76 // The DNS for these operations can operate in either blocking or
77 // non-blocking mode. Blocking mode is used as a fallback when the PAC script
78 // seems to be misbehaving under the tracing optimization.
80 // Note that this class runs on both the origin thread and a worker
81 // thread. Most methods are expected to be used exclusively on one thread
84 // The lifetime of Jobs does not exceed that of the ProxyResolverV8Tracing that
85 // spawned it. Destruction might happen on either the origin thread or the
87 class Job
: public base::RefCountedThreadSafe
<Job
>,
88 public ProxyResolverV8::JSBindings
{
92 const scoped_refptr
<base::SingleThreadTaskRunner
>& worker_task_runner
,
93 HostResolver
* host_resolver
,
94 ProxyResolverErrorObserver
* error_observer
,
96 ProxyResolver::LoadStateChangedCallback on_load_state_changed
,
97 int* num_outstanding_callbacks
)
98 : v8_resolver(nullptr),
99 worker_task_runner(worker_task_runner
),
100 host_resolver(host_resolver
),
101 error_observer(error_observer
),
103 on_load_state_changed(on_load_state_changed
),
104 num_outstanding_callbacks(num_outstanding_callbacks
) {}
106 ProxyResolverV8
* v8_resolver
;
107 scoped_refptr
<base::SingleThreadTaskRunner
> worker_task_runner
;
108 HostResolver
* host_resolver
;
109 ProxyResolverErrorObserver
* error_observer
;
111 ProxyResolver::LoadStateChangedCallback on_load_state_changed
;
112 int* num_outstanding_callbacks
;
114 // |params| is non-owned. It contains the parameters for this Job, and must
116 explicit Job(const Params
* params
);
118 // Called from origin thread.
119 void StartCreateV8Resolver(
120 const scoped_refptr
<ProxyResolverScriptData
>& script_data
,
121 scoped_ptr
<ProxyResolverV8
>* resolver
,
122 const CompletionCallback
& callback
);
124 // Called from origin thread.
125 void StartGetProxyForURL(const GURL
& url
,
127 const BoundNetLog
& net_log
,
128 const CompletionCallback
& callback
);
130 // Called from origin thread.
133 // Called from origin thread.
134 LoadState
GetLoadState() const;
137 typedef std::map
<std::string
, std::string
> DnsCache
;
138 friend class base::RefCountedThreadSafe
<Job
>;
145 struct AlertOrError
{
148 base::string16 message
;
153 void CheckIsOnWorkerThread() const;
154 void CheckIsOnOriginThread() const;
156 void SetCallback(const CompletionCallback
& callback
);
157 void ReleaseCallback();
159 ProxyResolverV8
* v8_resolver();
160 const scoped_refptr
<base::SingleThreadTaskRunner
>& worker_task_runner();
161 HostResolver
* host_resolver();
162 ProxyResolverErrorObserver
* error_observer();
165 // Invokes the user's callback.
166 void NotifyCaller(int result
);
167 void NotifyCallerOnOriginLoop(int result
);
169 void Start(Operation op
, bool blocking_dns
,
170 const CompletionCallback
& callback
);
172 void ExecuteBlocking();
173 void ExecuteNonBlocking();
174 int ExecuteProxyResolver();
176 // Implementation of ProxyResolverv8::JSBindings
177 bool ResolveDns(const std::string
& host
,
178 ResolveDnsOperation op
,
180 bool* terminate
) override
;
181 void Alert(const base::string16
& message
) override
;
182 void OnError(int line_number
, const base::string16
& error
) override
;
184 bool ResolveDnsBlocking(const std::string
& host
,
185 ResolveDnsOperation op
,
186 std::string
* output
);
188 bool ResolveDnsNonBlocking(const std::string
& host
,
189 ResolveDnsOperation op
,
193 bool PostDnsOperationAndWait(const std::string
& host
,
194 ResolveDnsOperation op
,
195 bool* completed_synchronously
)
198 void DoDnsOperation();
199 void OnDnsOperationComplete(int result
);
201 void ScheduleRestartWithBlockingDns();
203 bool GetDnsFromLocalCache(const std::string
& host
, ResolveDnsOperation op
,
204 std::string
* output
, bool* return_value
);
206 void SaveDnsToLocalCache(const std::string
& host
,
207 ResolveDnsOperation op
,
209 const AddressList
& addresses
);
211 // Builds a RequestInfo to service the specified PAC DNS operation.
212 static HostResolver::RequestInfo
MakeDnsRequestInfo(const std::string
& host
,
213 ResolveDnsOperation op
);
215 // Makes a key for looking up |host, op| in |dns_cache_|. Strings are used for
216 // convenience, to avoid defining custom comparators.
217 static std::string
MakeDnsCacheKey(const std::string
& host
,
218 ResolveDnsOperation op
);
220 void HandleAlertOrError(bool is_alert
, int line_number
,
221 const base::string16
& message
);
222 void DispatchBufferedAlertsAndErrors();
223 void DispatchAlertOrError(bool is_alert
, int line_number
,
224 const base::string16
& message
);
226 void LogEventToCurrentRequestAndGlobally(
227 NetLog::EventType type
,
228 const NetLog::ParametersCallback
& parameters_callback
);
230 // The thread which called into ProxyResolverV8Tracing, and on which the
231 // completion callback is expected to run.
232 scoped_refptr
<base::SingleThreadTaskRunner
> origin_runner_
;
234 // The Parameters for this Job.
235 // Initialized on origin thread and then accessed from both threads.
236 const Params
* const params_
;
238 // The callback to run (on the origin thread) when the Job finishes.
239 // Should only be accessed from origin thread.
240 CompletionCallback callback_
;
242 // Flag to indicate whether the request has been cancelled.
243 base::CancellationFlag cancelled_
;
245 // The operation that this Job is running.
246 // Initialized on origin thread and then accessed from both threads.
247 Operation operation_
;
249 // The DNS mode for this Job.
250 // Initialized on origin thread, mutated on worker thread, and accessed
251 // by both the origin thread and worker thread.
254 // Used to block the worker thread on a DNS operation taking place on the
256 base::WaitableEvent event_
;
258 // Map of DNS operations completed so far. Written into on the origin thread
259 // and read on the worker thread.
262 // The job holds a reference to itself to ensure that it remains alive until
263 // either completion or cancellation.
264 scoped_refptr
<Job
> owned_self_reference_
;
266 // -------------------------------------------------------
267 // State specific to CREATE_V8_RESOLVER.
268 // -------------------------------------------------------
270 scoped_refptr
<ProxyResolverScriptData
> script_data_
;
271 scoped_ptr
<ProxyResolverV8
>* resolver_out_
;
273 // -------------------------------------------------------
274 // State specific to GET_PROXY_FOR_URL.
275 // -------------------------------------------------------
277 ProxyInfo
* user_results_
; // Owned by caller, lives on origin thread.
280 BoundNetLog bound_net_log_
;
282 // ---------------------------------------------------------------------------
283 // State for ExecuteNonBlocking()
284 // ---------------------------------------------------------------------------
285 // These variables are used exclusively on the worker thread and are only
286 // meaningful when executing inside of ExecuteNonBlocking().
288 // Whether this execution was abandoned due to a missing DNS dependency.
291 // Number of calls made to ResolveDns() by this execution.
294 // Sequence of calls made to Alert() or OnError() by this execution.
295 std::vector
<AlertOrError
> alerts_and_errors_
;
296 size_t alerts_and_errors_byte_cost_
; // Approximate byte cost of the above.
298 // Number of calls made to ResolveDns() by the PREVIOUS execution.
301 // Whether the current execution needs to be restarted in blocking mode.
302 bool should_restart_with_blocking_dns_
;
304 // ---------------------------------------------------------------------------
305 // State for pending DNS request.
306 // ---------------------------------------------------------------------------
308 // Handle to the outstanding request in the HostResolver, or NULL.
309 // This is mutated and used on the origin thread, however it may be read by
310 // the worker thread for some DCHECKS().
311 HostResolver::RequestHandle pending_dns_
;
313 // Indicates if the outstanding DNS request completed synchronously. Written
314 // on the origin thread, and read by the worker thread.
315 bool pending_dns_completed_synchronously_
;
317 // These are the inputs to DoDnsOperation(). Written on the worker thread,
318 // read by the origin thread.
319 std::string pending_dns_host_
;
320 ResolveDnsOperation pending_dns_op_
;
322 // This contains the resolved address list that DoDnsOperation() fills in.
323 // Used exclusively on the origin thread.
324 AddressList pending_dns_addresses_
;
327 class ProxyResolverV8Tracing
: public ProxyResolver
,
328 public base::NonThreadSafe
{
330 // Constructs a ProxyResolver that will issue DNS requests through
331 // |job_params->host_resolver|, forward Javascript errors through
332 // |error_observer|, and log Javascript errors and alerts to
333 // |job_params->net_log|. When the LoadState for a request changes,
334 // |job_params->on_load_state_changed| will be invoked with the RequestHandle
335 // for that request with the new LoadState.
337 // Note that the constructor takes ownership of |error_observer|, whereas
338 // |job_params->host_resolver| and |job_params->net_log| are expected to
340 ProxyResolverV8Tracing(scoped_ptr
<ProxyResolverErrorObserver
> error_observer
,
341 scoped_ptr
<base::Thread
> thread
,
342 scoped_ptr
<ProxyResolverV8
> resolver
,
343 scoped_ptr
<Job::Params
> job_params
);
345 ~ProxyResolverV8Tracing() override
;
347 // ProxyResolver implementation:
348 int GetProxyForURL(const GURL
& url
,
350 const CompletionCallback
& callback
,
351 RequestHandle
* request
,
352 const BoundNetLog
& net_log
) override
;
353 void CancelRequest(RequestHandle request
) override
;
354 LoadState
GetLoadState(RequestHandle request
) const override
;
357 // The worker thread on which the ProxyResolverV8 will be run.
358 scoped_ptr
<base::Thread
> thread_
;
359 scoped_ptr
<ProxyResolverV8
> v8_resolver_
;
361 scoped_ptr
<ProxyResolverErrorObserver
> error_observer_
;
363 scoped_ptr
<Job::Params
> job_params_
;
365 // The number of outstanding (non-cancelled) jobs.
366 int num_outstanding_callbacks_
;
368 DISALLOW_COPY_AND_ASSIGN(ProxyResolverV8Tracing
);
371 Job::Job(const Job::Params
* params
)
372 : origin_runner_(base::ThreadTaskRunnerHandle::Get()),
377 CheckIsOnOriginThread();
380 void Job::StartCreateV8Resolver(
381 const scoped_refptr
<ProxyResolverScriptData
>& script_data
,
382 scoped_ptr
<ProxyResolverV8
>* resolver
,
383 const CompletionCallback
& callback
) {
384 CheckIsOnOriginThread();
386 resolver_out_
= resolver
;
387 script_data_
= script_data
;
389 // Script initialization uses blocking DNS since there isn't any
390 // advantage to using non-blocking mode here. That is because the
391 // parent ProxyService can't submit any ProxyResolve requests until
392 // initialization has completed successfully!
393 Start(CREATE_V8_RESOLVER
, true /*blocking*/, callback
);
396 void Job::StartGetProxyForURL(const GURL
& url
,
398 const BoundNetLog
& net_log
,
399 const CompletionCallback
& callback
) {
400 CheckIsOnOriginThread();
403 user_results_
= results
;
404 bound_net_log_
= net_log
;
406 Start(GET_PROXY_FOR_URL
, false /*non-blocking*/, callback
);
410 CheckIsOnOriginThread();
412 // There are several possibilities to consider for cancellation:
413 // (a) The job has been posted to the worker thread, however script execution
414 // has not yet started.
415 // (b) The script is executing on the worker thread.
416 // (c) The script is executing on the worker thread, however is blocked inside
417 // of dnsResolve() waiting for a response from the origin thread.
418 // (d) Nothing is running on the worker thread, however the host resolver has
419 // a pending DNS request which upon completion will restart the script
421 // (e) The worker thread has a pending task to restart execution, which was
422 // posted after the DNS dependency was resolved and saved to local cache.
423 // (f) The script execution completed entirely, and posted a task to the
424 // origin thread to notify the caller.
426 // |cancelled_| is read on both the origin thread and worker thread. The
427 // code that runs on the worker thread is littered with checks on
428 // |cancelled_| to break out early.
434 host_resolver()->CancelRequest(pending_dns_
);
438 // The worker thread might be blocked waiting for DNS.
441 owned_self_reference_
= NULL
;
444 LoadState
Job::GetLoadState() const {
445 CheckIsOnOriginThread();
448 return LOAD_STATE_RESOLVING_HOST_IN_PROXY_SCRIPT
;
450 return LOAD_STATE_RESOLVING_PROXY_FOR_URL
;
454 DCHECK(!pending_dns_
);
455 DCHECK(callback_
.is_null());
458 void Job::CheckIsOnWorkerThread() const {
459 DCHECK(params_
->worker_task_runner
->BelongsToCurrentThread());
462 void Job::CheckIsOnOriginThread() const {
463 DCHECK(origin_runner_
->BelongsToCurrentThread());
466 void Job::SetCallback(const CompletionCallback
& callback
) {
467 CheckIsOnOriginThread();
468 DCHECK(callback_
.is_null());
469 (*params_
->num_outstanding_callbacks
)++;
470 callback_
= callback
;
473 void Job::ReleaseCallback() {
474 CheckIsOnOriginThread();
475 DCHECK(!callback_
.is_null());
476 CHECK_GT(*params_
->num_outstanding_callbacks
, 0);
477 (*params_
->num_outstanding_callbacks
)--;
480 // For good measure, clear this other user-owned pointer.
481 user_results_
= NULL
;
484 ProxyResolverV8
* Job::v8_resolver() {
485 return params_
->v8_resolver
;
488 const scoped_refptr
<base::SingleThreadTaskRunner
>& Job::worker_task_runner() {
489 return params_
->worker_task_runner
;
492 HostResolver
* Job::host_resolver() {
493 return params_
->host_resolver
;
496 ProxyResolverErrorObserver
* Job::error_observer() {
497 return params_
->error_observer
;
500 NetLog
* Job::net_log() {
501 return params_
->net_log
;
504 void Job::NotifyCaller(int result
) {
505 CheckIsOnWorkerThread();
507 origin_runner_
->PostTask(
508 FROM_HERE
, base::Bind(&Job::NotifyCallerOnOriginLoop
, this, result
));
511 void Job::NotifyCallerOnOriginLoop(int result
) {
512 CheckIsOnOriginThread();
514 if (cancelled_
.IsSet())
517 DCHECK(!callback_
.is_null());
518 DCHECK(!pending_dns_
);
520 if (operation_
== GET_PROXY_FOR_URL
) {
521 *user_results_
= results_
;
524 CompletionCallback callback
= callback_
;
526 callback
.Run(result
);
528 owned_self_reference_
= NULL
;
531 void Job::Start(Operation op
,
533 const CompletionCallback
& callback
) {
534 CheckIsOnOriginThread();
537 blocking_dns_
= blocking_dns
;
538 SetCallback(callback
);
540 owned_self_reference_
= this;
542 worker_task_runner()->PostTask(
543 FROM_HERE
, blocking_dns_
? base::Bind(&Job::ExecuteBlocking
, this)
544 : base::Bind(&Job::ExecuteNonBlocking
, this));
547 void Job::ExecuteBlocking() {
548 CheckIsOnWorkerThread();
549 DCHECK(blocking_dns_
);
551 if (cancelled_
.IsSet())
554 NotifyCaller(ExecuteProxyResolver());
557 void Job::ExecuteNonBlocking() {
558 CheckIsOnWorkerThread();
559 DCHECK(!blocking_dns_
);
561 if (cancelled_
.IsSet())
564 // Reset state for the current execution.
567 alerts_and_errors_
.clear();
568 alerts_and_errors_byte_cost_
= 0;
569 should_restart_with_blocking_dns_
= false;
571 int result
= ExecuteProxyResolver();
573 if (should_restart_with_blocking_dns_
) {
574 DCHECK(!blocking_dns_
);
576 blocking_dns_
= true;
584 DispatchBufferedAlertsAndErrors();
585 NotifyCaller(result
);
588 int Job::ExecuteProxyResolver() {
589 int result
= ERR_UNEXPECTED
; // Initialized to silence warnings.
591 switch (operation_
) {
592 case CREATE_V8_RESOLVER
: {
593 scoped_ptr
<ProxyResolverV8
> resolver
;
594 result
= ProxyResolverV8::Create(script_data_
, this, &resolver
);
596 *resolver_out_
= resolver
.Pass();
599 case GET_PROXY_FOR_URL
: {
600 result
= v8_resolver()->GetProxyForURL(
602 // Important: Do not write directly into |user_results_|, since if the
603 // request were to be cancelled from the origin thread, must guarantee
604 // that |user_results_| is not accessed anymore.
613 bool Job::ResolveDns(const std::string
& host
,
614 ResolveDnsOperation op
,
617 if (cancelled_
.IsSet()) {
622 if ((op
== DNS_RESOLVE
|| op
== DNS_RESOLVE_EX
) && host
.empty()) {
623 // a DNS resolve with an empty hostname is considered an error.
627 return blocking_dns_
?
628 ResolveDnsBlocking(host
, op
, output
) :
629 ResolveDnsNonBlocking(host
, op
, output
, terminate
);
632 void Job::Alert(const base::string16
& message
) {
633 HandleAlertOrError(true, -1, message
);
636 void Job::OnError(int line_number
, const base::string16
& error
) {
637 HandleAlertOrError(false, line_number
, error
);
640 bool Job::ResolveDnsBlocking(const std::string
& host
,
641 ResolveDnsOperation op
,
642 std::string
* output
) {
643 CheckIsOnWorkerThread();
645 // Check if the DNS result for this host has already been cached.
647 if (GetDnsFromLocalCache(host
, op
, output
, &rv
)) {
652 if (dns_cache_
.size() >= kMaxUniqueResolveDnsPerExec
) {
653 // Safety net for scripts with unexpectedly many DNS calls.
654 // We will continue running to completion, but will fail every
655 // subsequent DNS request.
659 if (!PostDnsOperationAndWait(host
, op
, NULL
))
660 return false; // Was cancelled.
662 CHECK(GetDnsFromLocalCache(host
, op
, output
, &rv
));
666 bool Job::ResolveDnsNonBlocking(const std::string
& host
,
667 ResolveDnsOperation op
,
670 CheckIsOnWorkerThread();
673 // If this execution was already abandoned can fail right away. Only 1 DNS
674 // dependency will be traced at a time (for more predictable outcomes).
680 // Check if the DNS result for this host has already been cached.
682 if (GetDnsFromLocalCache(host
, op
, output
, &rv
)) {
687 if (num_dns_
<= last_num_dns_
) {
688 // The sequence of DNS operations is different from last time!
689 ScheduleRestartWithBlockingDns();
694 if (dns_cache_
.size() >= kMaxUniqueResolveDnsPerExec
) {
695 // Safety net for scripts with unexpectedly many DNS calls.
699 DCHECK(!should_restart_with_blocking_dns_
);
701 bool completed_synchronously
;
702 if (!PostDnsOperationAndWait(host
, op
, &completed_synchronously
))
703 return false; // Was cancelled.
705 if (completed_synchronously
) {
706 CHECK(GetDnsFromLocalCache(host
, op
, output
, &rv
));
710 // Otherwise if the result was not in the cache, then a DNS request has
711 // been started. Abandon this invocation of FindProxyForURL(), it will be
712 // restarted once the DNS request completes.
715 last_num_dns_
= num_dns_
;
719 bool Job::PostDnsOperationAndWait(const std::string
& host
,
720 ResolveDnsOperation op
,
721 bool* completed_synchronously
) {
722 // Post the DNS request to the origin thread.
723 DCHECK(!pending_dns_
);
724 pending_dns_host_
= host
;
725 pending_dns_op_
= op
;
726 origin_runner_
->PostTask(FROM_HERE
, base::Bind(&Job::DoDnsOperation
, this));
731 if (cancelled_
.IsSet())
734 if (completed_synchronously
)
735 *completed_synchronously
= pending_dns_completed_synchronously_
;
740 void Job::DoDnsOperation() {
741 CheckIsOnOriginThread();
742 DCHECK(!pending_dns_
);
744 if (cancelled_
.IsSet())
747 HostResolver::RequestHandle dns_request
= NULL
;
748 int result
= host_resolver()->Resolve(
749 MakeDnsRequestInfo(pending_dns_host_
, pending_dns_op_
),
751 &pending_dns_addresses_
,
752 base::Bind(&Job::OnDnsOperationComplete
, this),
756 pending_dns_completed_synchronously_
= result
!= ERR_IO_PENDING
;
758 // Check if the request was cancelled as a side-effect of calling into the
759 // HostResolver. This isn't the ordinary execution flow, however it is
760 // exercised by unit-tests.
761 if (cancelled_
.IsSet()) {
762 if (!pending_dns_completed_synchronously_
)
763 host_resolver()->CancelRequest(dns_request
);
767 if (pending_dns_completed_synchronously_
) {
768 OnDnsOperationComplete(result
);
771 pending_dns_
= dns_request
;
772 if (!params_
->on_load_state_changed
.is_null()) {
773 params_
->on_load_state_changed
.Run(
774 this, LOAD_STATE_RESOLVING_HOST_IN_PROXY_SCRIPT
);
776 // OnDnsOperationComplete() will be called by host resolver on completion.
779 if (!blocking_dns_
) {
780 // The worker thread always blocks waiting to see if the result can be
781 // serviced from cache before restarting.
786 void Job::OnDnsOperationComplete(int result
) {
787 CheckIsOnOriginThread();
789 DCHECK(!cancelled_
.IsSet());
790 DCHECK(pending_dns_completed_synchronously_
== (pending_dns_
== NULL
));
792 SaveDnsToLocalCache(pending_dns_host_
, pending_dns_op_
, result
,
793 pending_dns_addresses_
);
796 if (!params_
->on_load_state_changed
.is_null() &&
797 !pending_dns_completed_synchronously_
&& !cancelled_
.IsSet()) {
798 params_
->on_load_state_changed
.Run(this,
799 LOAD_STATE_RESOLVING_PROXY_FOR_URL
);
807 if (!blocking_dns_
&& !pending_dns_completed_synchronously_
) {
808 // Restart. This time it should make more progress due to having
810 worker_task_runner()->PostTask(FROM_HERE
,
811 base::Bind(&Job::ExecuteNonBlocking
, this));
815 void Job::ScheduleRestartWithBlockingDns() {
816 CheckIsOnWorkerThread();
818 DCHECK(!should_restart_with_blocking_dns_
);
820 DCHECK(!blocking_dns_
);
824 // The restart will happen after ExecuteNonBlocking() finishes.
825 should_restart_with_blocking_dns_
= true;
828 bool Job::GetDnsFromLocalCache(const std::string
& host
,
829 ResolveDnsOperation op
,
831 bool* return_value
) {
832 CheckIsOnWorkerThread();
834 DnsCache::const_iterator it
= dns_cache_
.find(MakeDnsCacheKey(host
, op
));
835 if (it
== dns_cache_
.end())
838 *output
= it
->second
;
839 *return_value
= !it
->second
.empty();
843 void Job::SaveDnsToLocalCache(const std::string
& host
,
844 ResolveDnsOperation op
,
846 const AddressList
& addresses
) {
847 CheckIsOnOriginThread();
849 // Serialize the result into a string to save to the cache.
850 std::string cache_value
;
851 if (net_error
!= OK
) {
852 cache_value
= std::string();
853 } else if (op
== DNS_RESOLVE
|| op
== MY_IP_ADDRESS
) {
854 // dnsResolve() and myIpAddress() are expected to return a single IP
856 cache_value
= addresses
.front().ToStringWithoutPort();
858 // The *Ex versions are expected to return a semi-colon separated list.
859 for (AddressList::const_iterator iter
= addresses
.begin();
860 iter
!= addresses
.end(); ++iter
) {
861 if (!cache_value
.empty())
863 cache_value
+= iter
->ToStringWithoutPort();
867 dns_cache_
[MakeDnsCacheKey(host
, op
)] = cache_value
;
871 HostResolver::RequestInfo
Job::MakeDnsRequestInfo(const std::string
& host
,
872 ResolveDnsOperation op
) {
873 HostPortPair host_port
= HostPortPair(host
, 80);
874 if (op
== MY_IP_ADDRESS
|| op
== MY_IP_ADDRESS_EX
) {
875 host_port
.set_host(GetHostName());
878 HostResolver::RequestInfo
info(host_port
);
879 // Flag myIpAddress requests.
880 if (op
== MY_IP_ADDRESS
|| op
== MY_IP_ADDRESS_EX
) {
881 // TODO: Provide a RequestInfo construction mechanism that does not
882 // require a hostname and sets is_my_ip_address to true instead of this.
883 info
.set_is_my_ip_address(true);
885 // The non-ex flavors are limited to IPv4 results.
886 if (op
== MY_IP_ADDRESS
|| op
== DNS_RESOLVE
) {
887 info
.set_address_family(ADDRESS_FAMILY_IPV4
);
893 std::string
Job::MakeDnsCacheKey(const std::string
& host
,
894 ResolveDnsOperation op
) {
895 return base::StringPrintf("%d:%s", op
, host
.c_str());
898 void Job::HandleAlertOrError(bool is_alert
,
900 const base::string16
& message
) {
901 CheckIsOnWorkerThread();
903 if (cancelled_
.IsSet())
907 // In blocking DNS mode the events can be dispatched immediately.
908 DispatchAlertOrError(is_alert
, line_number
, message
);
912 // Otherwise in nonblocking mode, buffer all the messages until
918 alerts_and_errors_byte_cost_
+= sizeof(AlertOrError
) + message
.size() * 2;
920 // If there have been lots of messages, enqueing could be expensive on
921 // memory. Consider a script which does megabytes worth of alerts().
922 // Avoid this by falling back to blocking mode.
923 if (alerts_and_errors_byte_cost_
> kMaxAlertsAndErrorsBytes
) {
924 ScheduleRestartWithBlockingDns();
928 AlertOrError entry
= {is_alert
, line_number
, message
};
929 alerts_and_errors_
.push_back(entry
);
932 void Job::DispatchBufferedAlertsAndErrors() {
933 CheckIsOnWorkerThread();
934 DCHECK(!blocking_dns_
);
937 for (size_t i
= 0; i
< alerts_and_errors_
.size(); ++i
) {
938 const AlertOrError
& x
= alerts_and_errors_
[i
];
939 DispatchAlertOrError(x
.is_alert
, x
.line_number
, x
.message
);
943 void Job::DispatchAlertOrError(bool is_alert
,
945 const base::string16
& message
) {
946 CheckIsOnWorkerThread();
948 // Note that the handling of cancellation is racy with regard to
949 // alerts/errors. The request might get cancelled shortly after this
950 // check! (There is no lock being held to guarantee otherwise).
952 // If this happens, then some information will get written to the NetLog
953 // needlessly, however the NetLog will still be alive so it shouldn't cause
955 if (cancelled_
.IsSet())
959 // -------------------
961 // -------------------
962 VLOG(1) << "PAC-alert: " << message
;
964 // Send to the NetLog.
965 LogEventToCurrentRequestAndGlobally(
966 NetLog::TYPE_PAC_JAVASCRIPT_ALERT
,
967 NetLog::StringCallback("message", &message
));
969 // -------------------
971 // -------------------
972 if (line_number
== -1)
973 VLOG(1) << "PAC-error: " << message
;
975 VLOG(1) << "PAC-error: " << "line: " << line_number
<< ": " << message
;
977 // Send the error to the NetLog.
978 LogEventToCurrentRequestAndGlobally(
979 NetLog::TYPE_PAC_JAVASCRIPT_ERROR
,
980 base::Bind(&NetLogErrorCallback
, line_number
, &message
));
982 if (error_observer())
983 error_observer()->OnPACScriptError(line_number
, message
);
987 void Job::LogEventToCurrentRequestAndGlobally(
988 NetLog::EventType type
,
989 const NetLog::ParametersCallback
& parameters_callback
) {
990 CheckIsOnWorkerThread();
991 bound_net_log_
.AddEvent(type
, parameters_callback
);
993 // Emit to the global NetLog event stream.
995 net_log()->AddGlobalEntry(type
, parameters_callback
);
998 ProxyResolverV8Tracing::ProxyResolverV8Tracing(
999 scoped_ptr
<ProxyResolverErrorObserver
> error_observer
,
1000 scoped_ptr
<base::Thread
> thread
,
1001 scoped_ptr
<ProxyResolverV8
> resolver
,
1002 scoped_ptr
<Job::Params
> job_params
)
1003 : thread_(thread
.Pass()),
1004 v8_resolver_(resolver
.Pass()),
1005 error_observer_(error_observer
.Pass()),
1006 job_params_(job_params
.Pass()),
1007 num_outstanding_callbacks_(0) {
1008 job_params_
->num_outstanding_callbacks
= &num_outstanding_callbacks_
;
1011 ProxyResolverV8Tracing::~ProxyResolverV8Tracing() {
1012 // Note, all requests should have been cancelled.
1013 CHECK_EQ(0, num_outstanding_callbacks_
);
1015 // Join the worker thread. See http://crbug.com/69710.
1016 base::ThreadRestrictions::ScopedAllowIO allow_io
;
1020 int ProxyResolverV8Tracing::GetProxyForURL(const GURL
& url
,
1022 const CompletionCallback
& callback
,
1023 RequestHandle
* request
,
1024 const BoundNetLog
& net_log
) {
1025 DCHECK(CalledOnValidThread());
1026 DCHECK(!callback
.is_null());
1028 scoped_refptr
<Job
> job
= new Job(job_params_
.get());
1031 *request
= job
.get();
1033 job
->StartGetProxyForURL(url
, results
, net_log
, callback
);
1034 return ERR_IO_PENDING
;
1037 void ProxyResolverV8Tracing::CancelRequest(RequestHandle request
) {
1038 Job
* job
= reinterpret_cast<Job
*>(request
);
1042 LoadState
ProxyResolverV8Tracing::GetLoadState(RequestHandle request
) const {
1043 Job
* job
= reinterpret_cast<Job
*>(request
);
1044 return job
->GetLoadState();
1049 class ProxyResolverFactoryV8Tracing::CreateJob
1050 : public ProxyResolverFactory::Request
{
1052 CreateJob(ProxyResolverFactoryV8Tracing
* factory
,
1053 HostResolver
* host_resolver
,
1054 scoped_ptr
<ProxyResolverErrorObserver
> error_observer
,
1056 const ProxyResolver::LoadStateChangedCallback
&
1057 load_state_changed_callback
,
1058 const scoped_refptr
<ProxyResolverScriptData
>& pac_script
,
1059 scoped_ptr
<ProxyResolver
>* resolver_out
,
1060 const CompletionCallback
& callback
)
1061 : factory_(factory
),
1062 thread_(new base::Thread("Proxy Resolver")),
1063 error_observer_(error_observer
.Pass()),
1064 resolver_out_(resolver_out
),
1065 callback_(callback
),
1066 num_outstanding_callbacks_(0) {
1067 // Start up the thread.
1068 base::Thread::Options options
;
1069 options
.timer_slack
= base::TIMER_SLACK_MAXIMUM
;
1070 CHECK(thread_
->StartWithOptions(options
));
1071 job_params_
.reset(new Job::Params(
1072 thread_
->task_runner(), host_resolver
, error_observer_
.get(), net_log
,
1073 load_state_changed_callback
, &num_outstanding_callbacks_
));
1074 create_resolver_job_
= new Job(job_params_
.get());
1075 create_resolver_job_
->StartCreateV8Resolver(
1076 pac_script
, &v8_resolver_
,
1078 &ProxyResolverFactoryV8Tracing::CreateJob::OnV8ResolverCreated
,
1079 base::Unretained(this)));
1082 ~CreateJob() override
{
1084 factory_
->RemoveJob(this);
1085 DCHECK(create_resolver_job_
);
1086 create_resolver_job_
->Cancel();
1089 DCHECK_EQ(0, num_outstanding_callbacks_
);
1092 void FactoryDestroyed() {
1094 create_resolver_job_
->Cancel();
1095 create_resolver_job_
= nullptr;
1100 void OnV8ResolverCreated(int error
) {
1103 job_params_
->v8_resolver
= v8_resolver_
.get();
1104 resolver_out_
->reset(
1105 new ProxyResolverV8Tracing(error_observer_
.Pass(), thread_
.Pass(),
1106 v8_resolver_
.Pass(), job_params_
.Pass()));
1111 factory_
->RemoveJob(this);
1113 create_resolver_job_
= nullptr;
1114 callback_
.Run(error
);
1117 void StopWorkerThread() {
1118 // Join the worker thread. See http://crbug.com/69710.
1119 base::ThreadRestrictions::ScopedAllowIO allow_io
;
1123 ProxyResolverFactoryV8Tracing
* factory_
;
1124 scoped_ptr
<base::Thread
> thread_
;
1125 scoped_ptr
<ProxyResolverErrorObserver
> error_observer_
;
1126 scoped_ptr
<Job::Params
> job_params_
;
1127 scoped_refptr
<Job
> create_resolver_job_
;
1128 scoped_ptr
<ProxyResolverV8
> v8_resolver_
;
1129 scoped_ptr
<ProxyResolver
>* resolver_out_
;
1130 const CompletionCallback callback_
;
1131 int num_outstanding_callbacks_
;
1133 DISALLOW_COPY_AND_ASSIGN(CreateJob
);
1136 ProxyResolverFactoryV8Tracing::ProxyResolverFactoryV8Tracing(
1137 HostResolver
* host_resolver
,
1139 const ProxyResolver::LoadStateChangedCallback
& callback
,
1140 const base::Callback
<scoped_ptr
<ProxyResolverErrorObserver
>()>&
1141 error_observer_factory
)
1142 : ProxyResolverFactory(true),
1143 host_resolver_(host_resolver
),
1145 load_state_changed_callback_(callback
),
1146 error_observer_factory_(error_observer_factory
) {
1149 ProxyResolverFactoryV8Tracing::~ProxyResolverFactoryV8Tracing() {
1150 for (auto job
: jobs_
) {
1151 job
->FactoryDestroyed();
1155 // ProxyResolverFactory override.
1156 int ProxyResolverFactoryV8Tracing::CreateProxyResolver(
1157 const scoped_refptr
<ProxyResolverScriptData
>& pac_script
,
1158 scoped_ptr
<ProxyResolver
>* resolver
,
1159 const CompletionCallback
& callback
,
1160 scoped_ptr
<Request
>* request
) {
1161 scoped_ptr
<CreateJob
> job(new CreateJob(
1162 this, host_resolver_
,
1163 error_observer_factory_
.is_null() ? nullptr
1164 : error_observer_factory_
.Run(),
1165 net_log_
, load_state_changed_callback_
, pac_script
, resolver
, callback
));
1166 jobs_
.insert(job
.get());
1167 *request
= job
.Pass();
1168 return ERR_IO_PENDING
;
1171 void ProxyResolverFactoryV8Tracing::RemoveJob(
1172 ProxyResolverFactoryV8Tracing::CreateJob
* job
) {
1173 size_t erased
= jobs_
.erase(job
);
1174 DCHECK_EQ(1u, erased
);