Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / net / proxy / proxy_resolver_v8_tracing.cc
blob01e1ef5839f047c413743b81bfe5ab818e54fff4
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"
7 #include <map>
8 #include <string>
9 #include <vector>
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
42 // known.
43 namespace net {
45 namespace {
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
61 // similar code.
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
69 // or the other.
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
73 // worker thread.
74 class Job : public base::RefCountedThreadSafe<Job>,
75 public ProxyResolverV8::JSBindings {
76 public:
77 struct Params {
78 Params(
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
90 // outlive it.
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,
102 ProxyInfo* results,
103 const CompletionCallback& callback);
105 // Called from origin thread.
106 void Cancel();
108 // Called from origin thread.
109 LoadState GetLoadState() const;
111 private:
112 typedef std::map<std::string, std::string> DnsCache;
113 friend class base::RefCountedThreadSafe<Job>;
115 enum Operation {
116 CREATE_V8_RESOLVER,
117 GET_PROXY_FOR_URL,
120 struct AlertOrError {
121 bool is_alert;
122 int line_number;
123 base::string16 message;
126 ~Job() override;
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,
152 std::string* output,
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,
163 std::string* output,
164 bool* terminate);
166 bool PostDnsOperationAndWait(const std::string& host,
167 ResolveDnsOperation op,
168 bool* completed_synchronously)
169 WARN_UNUSED_RESULT;
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,
181 int net_error,
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.
223 bool blocking_dns_;
225 // Used to block the worker thread on a DNS operation taking place on the
226 // origin thread.
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.
231 DnsCache dns_cache_;
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.
249 GURL url_;
250 ProxyInfo results_;
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.
259 bool abandoned_;
261 // Number of calls made to ResolveDns() by this execution.
262 int num_dns_;
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.
269 int last_num_dns_;
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 {
299 public:
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,
308 ProxyInfo* results,
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;
315 private:
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()),
331 params_(params),
332 bindings_(bindings.Pass()),
333 event_(true, false),
334 last_num_dns_(0),
335 pending_dns_(NULL) {
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,
356 ProxyInfo* results,
357 const CompletionCallback& callback) {
358 CheckIsOnOriginThread();
360 url_ = url;
361 user_results_ = results;
363 Start(GET_PROXY_FOR_URL, false /*non-blocking*/, callback);
366 void Job::Cancel() {
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
377 // execution.
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.
386 cancelled_.Set();
388 ReleaseCallback();
390 if (pending_dns_) {
391 host_resolver()->CancelRequest(pending_dns_);
392 pending_dns_ = NULL;
395 // The worker thread might be blocked waiting for DNS.
396 event_.Signal();
398 owned_self_reference_ = NULL;
401 LoadState Job::GetLoadState() const {
402 CheckIsOnOriginThread();
404 if (pending_dns_)
405 return LOAD_STATE_RESOLVING_HOST_IN_PROXY_SCRIPT;
407 return LOAD_STATE_RESOLVING_PROXY_FOR_URL;
410 Job::~Job() {
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)--;
435 callback_.Reset();
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())
464 return;
466 DCHECK(!callback_.is_null());
467 DCHECK(!pending_dns_);
469 if (operation_ == GET_PROXY_FOR_URL) {
470 *user_results_ = results_;
473 CompletionCallback callback = callback_;
474 ReleaseCallback();
475 callback.Run(result);
477 owned_self_reference_ = NULL;
480 void Job::Start(Operation op,
481 bool blocking_dns,
482 const CompletionCallback& callback) {
483 CheckIsOnOriginThread();
485 operation_ = op;
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())
501 return;
503 NotifyCaller(ExecuteProxyResolver());
506 void Job::ExecuteNonBlocking() {
507 CheckIsOnWorkerThread();
508 DCHECK(!blocking_dns_);
510 if (cancelled_.IsSet())
511 return;
513 // Reset state for the current execution.
514 abandoned_ = false;
515 num_dns_ = 0;
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_);
524 DCHECK(abandoned_);
525 blocking_dns_ = true;
526 ExecuteBlocking();
527 return;
530 if (abandoned_)
531 return;
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);
544 if (result == OK)
545 *resolver_out_ = resolver.Pass();
546 break;
548 case GET_PROXY_FOR_URL: {
549 result = v8_resolver()->GetProxyForURL(
550 url_,
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.
554 &results_, this);
555 break;
559 return result;
562 bool Job::ResolveDns(const std::string& host,
563 ResolveDnsOperation op,
564 std::string* output,
565 bool* terminate) {
566 if (cancelled_.IsSet()) {
567 *terminate = true;
568 return false;
571 if ((op == DNS_RESOLVE || op == DNS_RESOLVE_EX) && host.empty()) {
572 // a DNS resolve with an empty hostname is considered an error.
573 return false;
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.
595 bool rv;
596 if (GetDnsFromLocalCache(host, op, output, &rv)) {
597 // Yay, cache hit!
598 return 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.
605 return false;
608 if (!PostDnsOperationAndWait(host, op, NULL))
609 return false; // Was cancelled.
611 CHECK(GetDnsFromLocalCache(host, op, output, &rv));
612 return rv;
615 bool Job::ResolveDnsNonBlocking(const std::string& host,
616 ResolveDnsOperation op,
617 std::string* output,
618 bool* terminate) {
619 CheckIsOnWorkerThread();
621 if (abandoned_) {
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).
624 return false;
627 num_dns_ += 1;
629 // Check if the DNS result for this host has already been cached.
630 bool rv;
631 if (GetDnsFromLocalCache(host, op, output, &rv)) {
632 // Yay, cache hit!
633 return rv;
636 if (num_dns_ <= last_num_dns_) {
637 // The sequence of DNS operations is different from last time!
638 ScheduleRestartWithBlockingDns();
639 *terminate = true;
640 return false;
643 if (dns_cache_.size() >= kMaxUniqueResolveDnsPerExec) {
644 // Safety net for scripts with unexpectedly many DNS calls.
645 return false;
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));
656 return 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.
662 abandoned_ = true;
663 *terminate = true;
664 last_num_dns_ = num_dns_;
665 return false;
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));
677 event_.Wait();
678 event_.Reset();
680 if (cancelled_.IsSet())
681 return false;
683 if (completed_synchronously)
684 *completed_synchronously = pending_dns_completed_synchronously_;
686 return true;
689 void Job::DoDnsOperation() {
690 CheckIsOnOriginThread();
691 DCHECK(!pending_dns_);
693 if (cancelled_.IsSet())
694 return;
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);
710 return;
713 if (pending_dns_completed_synchronously_) {
714 OnDnsOperationComplete(result);
715 } else {
716 DCHECK(dns_request);
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.
724 event_.Signal();
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_);
736 pending_dns_ = NULL;
738 if (blocking_dns_) {
739 event_.Signal();
740 return;
743 if (!blocking_dns_ && !pending_dns_completed_synchronously_) {
744 // Restart. This time it should make more progress due to having
745 // cached items.
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_);
755 DCHECK(!abandoned_);
756 DCHECK(!blocking_dns_);
758 abandoned_ = true;
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,
766 std::string* output,
767 bool* return_value) {
768 CheckIsOnWorkerThread();
770 DnsCache::const_iterator it = dns_cache_.find(MakeDnsCacheKey(host, op));
771 if (it == dns_cache_.end())
772 return false;
774 *output = it->second;
775 *return_value = !it->second.empty();
776 return true;
779 void Job::SaveDnsToLocalCache(const std::string& host,
780 ResolveDnsOperation op,
781 int net_error,
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
791 // address.
792 cache_value = addresses.front().ToStringWithoutPort();
793 } else {
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())
798 cache_value += ";";
799 cache_value += iter->ToStringWithoutPort();
803 dns_cache_[MakeDnsCacheKey(host, op)] = cache_value;
806 // static
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);
826 return info;
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,
835 int line_number,
836 const base::string16& message) {
837 CheckIsOnWorkerThread();
839 if (cancelled_.IsSet())
840 return;
842 if (blocking_dns_) {
843 // In blocking DNS mode the events can be dispatched immediately.
844 DispatchAlertOrError(is_alert, line_number, message);
845 return;
848 // Otherwise in nonblocking mode, buffer all the messages until
849 // the end.
851 if (abandoned_)
852 return;
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();
861 return;
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_);
871 DCHECK(!abandoned_);
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,
880 int line_number,
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
890 // problems.
891 if (cancelled_.IsSet())
892 return;
894 if (is_alert) {
895 // -------------------
896 // alert
897 // -------------------
898 VLOG(1) << "PAC-alert: " << message;
900 bindings_->Alert(message);
901 } else {
902 // -------------------
903 // error
904 // -------------------
905 if (line_number == -1)
906 VLOG(1) << "PAC-error: " << message;
907 else
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;
931 thread_.reset();
934 void ProxyResolverV8TracingImpl::GetProxyForURL(
935 const GURL& url,
936 ProxyInfo* results,
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());
945 if (request)
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);
954 job->Cancel();
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 {
964 public:
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;
975 private:
976 class CreateJob;
978 void RemoveJob(CreateJob* job);
980 std::set<CreateJob*> jobs_;
982 DISALLOW_COPY_AND_ASSIGN(ProxyResolverV8TracingFactoryImpl);
985 class ProxyResolverV8TracingFactoryImpl::CreateJob
986 : public ProxyResolverFactory::Request {
987 public:
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)
993 : factory_(factory),
994 thread_(new base::Thread("Proxy Resolver")),
995 resolver_out_(resolver_out),
996 callback_(callback),
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));
1002 job_params_.reset(
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_,
1007 base::Bind(
1008 &ProxyResolverV8TracingFactoryImpl::CreateJob::OnV8ResolverCreated,
1009 base::Unretained(this)));
1012 ~CreateJob() override {
1013 if (factory_) {
1014 factory_->RemoveJob(this);
1015 DCHECK(create_resolver_job_);
1016 create_resolver_job_->Cancel();
1017 StopWorkerThread();
1019 DCHECK_EQ(0, num_outstanding_callbacks_);
1022 void FactoryDestroyed() {
1023 factory_ = nullptr;
1024 create_resolver_job_->Cancel();
1025 create_resolver_job_ = nullptr;
1026 StopWorkerThread();
1029 private:
1030 void OnV8ResolverCreated(int error) {
1031 DCHECK(factory_);
1032 if (error == OK) {
1033 job_params_->v8_resolver = v8_resolver_.get();
1034 resolver_out_->reset(new ProxyResolverV8TracingImpl(
1035 thread_.Pass(), v8_resolver_.Pass(), job_params_.Pass()));
1036 } else {
1037 StopWorkerThread();
1040 factory_->RemoveJob(this);
1041 factory_ = nullptr;
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;
1049 thread_.reset();
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);
1091 } // namespace
1093 // static
1094 scoped_ptr<ProxyResolverV8TracingFactory>
1095 ProxyResolverV8TracingFactory::Create() {
1096 return make_scoped_ptr(new ProxyResolverV8TracingFactoryImpl());
1099 } // namespace net