Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / net / proxy / proxy_resolver_v8_tracing.cc
blob27cf5f7b78807242209c143d50ea2f00c99eb17b
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/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
43 // known.
44 namespace net {
46 namespace {
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
62 // similar code.
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
70 // or the other.
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
74 // worker thread.
75 class Job : public base::RefCountedThreadSafe<Job>,
76 public ProxyResolverV8::JSBindings {
77 public:
78 struct Params {
79 Params(
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
91 // outlive it.
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,
103 ProxyInfo* results,
104 const CompletionCallback& callback);
106 // Called from origin thread.
107 void Cancel();
109 // Called from origin thread.
110 LoadState GetLoadState() const;
112 private:
113 typedef std::map<std::string, std::string> DnsCache;
114 friend class base::RefCountedThreadSafe<Job>;
116 enum Operation {
117 CREATE_V8_RESOLVER,
118 GET_PROXY_FOR_URL,
121 struct AlertOrError {
122 bool is_alert;
123 int line_number;
124 base::string16 message;
127 ~Job() override;
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,
153 std::string* output,
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,
164 std::string* output,
165 bool* terminate);
167 bool PostDnsOperationAndWait(const std::string& host,
168 ResolveDnsOperation op,
169 bool* completed_synchronously)
170 WARN_UNUSED_RESULT;
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,
182 int net_error,
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,
198 int line_number,
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.
225 bool blocking_dns_;
227 // Used to block the worker thread on a DNS operation taking place on the
228 // origin thread.
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.
233 DnsCache dns_cache_;
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.
251 GURL url_;
252 ProxyInfo results_;
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.
261 bool abandoned_;
263 // Number of calls made to ResolveDns() by this execution.
264 int num_dns_;
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.
271 int last_num_dns_;
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 {
301 public:
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,
310 ProxyInfo* results,
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;
317 private:
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()),
333 params_(params),
334 bindings_(bindings.Pass()),
335 event_(true, false),
336 last_num_dns_(0),
337 pending_dns_(NULL) {
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,
358 ProxyInfo* results,
359 const CompletionCallback& callback) {
360 CheckIsOnOriginThread();
362 url_ = url;
363 user_results_ = results;
365 Start(GET_PROXY_FOR_URL, false /*non-blocking*/, callback);
368 void Job::Cancel() {
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
379 // execution.
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.
388 cancelled_.Set();
390 ReleaseCallback();
392 if (pending_dns_) {
393 host_resolver()->CancelRequest(pending_dns_);
394 pending_dns_ = NULL;
397 // The worker thread might be blocked waiting for DNS.
398 event_.Signal();
400 bindings_.reset();
401 owned_self_reference_ = NULL;
404 LoadState Job::GetLoadState() const {
405 CheckIsOnOriginThread();
407 if (pending_dns_)
408 return LOAD_STATE_RESOLVING_HOST_IN_PROXY_SCRIPT;
410 return LOAD_STATE_RESOLVING_PROXY_FOR_URL;
413 Job::~Job() {
414 DCHECK(!pending_dns_);
415 DCHECK(callback_.is_null());
416 DCHECK(!bindings_);
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)--;
439 callback_.Reset();
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())
468 return;
470 DispatchBufferedAlertsAndErrors();
472 // This isn't the ordinary execution flow, however it is exercised by
473 // unit-tests.
474 if (cancelled_.IsSet())
475 return;
477 DCHECK(!callback_.is_null());
478 DCHECK(!pending_dns_);
480 if (operation_ == GET_PROXY_FOR_URL) {
481 *user_results_ = results_;
484 CompletionCallback callback = callback_;
485 ReleaseCallback();
486 callback.Run(result);
488 bindings_.reset();
489 owned_self_reference_ = NULL;
492 void Job::Start(Operation op,
493 bool blocking_dns,
494 const CompletionCallback& callback) {
495 CheckIsOnOriginThread();
497 operation_ = op;
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())
513 return;
515 NotifyCaller(ExecuteProxyResolver());
518 void Job::ExecuteNonBlocking() {
519 CheckIsOnWorkerThread();
520 DCHECK(!blocking_dns_);
522 if (cancelled_.IsSet())
523 return;
525 // Reset state for the current execution.
526 abandoned_ = false;
527 num_dns_ = 0;
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_);
536 DCHECK(abandoned_);
537 blocking_dns_ = true;
538 ExecuteBlocking();
539 return;
542 if (abandoned_)
543 return;
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);
555 if (result == OK)
556 *resolver_out_ = resolver.Pass();
557 break;
559 case GET_PROXY_FOR_URL: {
560 result = v8_resolver()->GetProxyForURL(
561 url_,
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.
565 &results_, this);
566 break;
570 return result;
573 bool Job::ResolveDns(const std::string& host,
574 ResolveDnsOperation op,
575 std::string* output,
576 bool* terminate) {
577 if (cancelled_.IsSet()) {
578 *terminate = true;
579 return false;
582 if ((op == DNS_RESOLVE || op == DNS_RESOLVE_EX) && host.empty()) {
583 // a DNS resolve with an empty hostname is considered an error.
584 return false;
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.
606 bool rv;
607 if (GetDnsFromLocalCache(host, op, output, &rv)) {
608 // Yay, cache hit!
609 return 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.
616 return false;
619 if (!PostDnsOperationAndWait(host, op, NULL))
620 return false; // Was cancelled.
622 CHECK(GetDnsFromLocalCache(host, op, output, &rv));
623 return rv;
626 bool Job::ResolveDnsNonBlocking(const std::string& host,
627 ResolveDnsOperation op,
628 std::string* output,
629 bool* terminate) {
630 CheckIsOnWorkerThread();
632 if (abandoned_) {
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).
635 return false;
638 num_dns_ += 1;
640 // Check if the DNS result for this host has already been cached.
641 bool rv;
642 if (GetDnsFromLocalCache(host, op, output, &rv)) {
643 // Yay, cache hit!
644 return rv;
647 if (num_dns_ <= last_num_dns_) {
648 // The sequence of DNS operations is different from last time!
649 ScheduleRestartWithBlockingDns();
650 *terminate = true;
651 return false;
654 if (dns_cache_.size() >= kMaxUniqueResolveDnsPerExec) {
655 // Safety net for scripts with unexpectedly many DNS calls.
656 return false;
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));
667 return 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.
673 abandoned_ = true;
674 *terminate = true;
675 last_num_dns_ = num_dns_;
676 return false;
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));
688 event_.Wait();
689 event_.Reset();
691 if (cancelled_.IsSet())
692 return false;
694 if (completed_synchronously)
695 *completed_synchronously = pending_dns_completed_synchronously_;
697 return true;
700 void Job::DoDnsOperation() {
701 CheckIsOnOriginThread();
702 DCHECK(!pending_dns_);
704 if (cancelled_.IsSet())
705 return;
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())
719 return;
721 if (pending_dns_completed_synchronously_) {
722 OnDnsOperationComplete(result);
723 } else {
724 DCHECK(dns_request);
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.
732 event_.Signal();
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_);
744 pending_dns_ = NULL;
746 if (blocking_dns_) {
747 event_.Signal();
748 return;
751 if (!blocking_dns_ && !pending_dns_completed_synchronously_) {
752 // Restart. This time it should make more progress due to having
753 // cached items.
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_);
763 DCHECK(!abandoned_);
764 DCHECK(!blocking_dns_);
766 abandoned_ = true;
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,
774 std::string* output,
775 bool* return_value) {
776 CheckIsOnWorkerThread();
778 DnsCache::const_iterator it = dns_cache_.find(MakeDnsCacheKey(host, op));
779 if (it == dns_cache_.end())
780 return false;
782 *output = it->second;
783 *return_value = !it->second.empty();
784 return true;
787 void Job::SaveDnsToLocalCache(const std::string& host,
788 ResolveDnsOperation op,
789 int net_error,
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
799 // address.
800 cache_value = addresses.front().ToStringWithoutPort();
801 } else {
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())
806 cache_value += ";";
807 cache_value += iter->ToStringWithoutPort();
811 dns_cache_[MakeDnsCacheKey(host, op)] = cache_value;
814 // static
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);
834 return info;
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,
843 int line_number,
844 const base::string16& message) {
845 CheckIsOnWorkerThread();
847 if (cancelled_.IsSet())
848 return;
850 if (blocking_dns_) {
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));
855 return;
858 // Otherwise in nonblocking mode, buffer all the messages until
859 // the end.
861 if (abandoned_)
862 return;
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();
872 return;
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,
888 int line_number,
889 const base::string16& message) {
890 CheckIsOnOriginThread();
892 if (cancelled_.IsSet())
893 return;
895 if (is_alert) {
896 // -------------------
897 // alert
898 // -------------------
899 VLOG(1) << "PAC-alert: " << message;
901 bindings_->Alert(message);
902 } else {
903 // -------------------
904 // error
905 // -------------------
906 if (line_number == -1)
907 VLOG(1) << "PAC-error: " << message;
908 else
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;
932 thread_.reset();
935 void ProxyResolverV8TracingImpl::GetProxyForURL(
936 const GURL& url,
937 ProxyInfo* results,
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());
946 if (request)
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);
955 job->Cancel();
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 {
965 public:
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;
976 private:
977 class CreateJob;
979 void RemoveJob(CreateJob* job);
981 std::set<CreateJob*> jobs_;
983 DISALLOW_COPY_AND_ASSIGN(ProxyResolverV8TracingFactoryImpl);
986 class ProxyResolverV8TracingFactoryImpl::CreateJob
987 : public ProxyResolverFactory::Request {
988 public:
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)
994 : factory_(factory),
995 thread_(new base::Thread("Proxy Resolver")),
996 resolver_out_(resolver_out),
997 callback_(callback),
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));
1003 job_params_.reset(
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_,
1008 base::Bind(
1009 &ProxyResolverV8TracingFactoryImpl::CreateJob::OnV8ResolverCreated,
1010 base::Unretained(this)));
1013 ~CreateJob() override {
1014 if (factory_) {
1015 factory_->RemoveJob(this);
1016 DCHECK(create_resolver_job_);
1017 create_resolver_job_->Cancel();
1018 StopWorkerThread();
1020 DCHECK_EQ(0, num_outstanding_callbacks_);
1023 void FactoryDestroyed() {
1024 factory_ = nullptr;
1025 create_resolver_job_->Cancel();
1026 create_resolver_job_ = nullptr;
1027 StopWorkerThread();
1030 private:
1031 void OnV8ResolverCreated(int error) {
1032 DCHECK(factory_);
1033 if (error == OK) {
1034 job_params_->v8_resolver = v8_resolver_.get();
1035 resolver_out_->reset(new ProxyResolverV8TracingImpl(
1036 thread_.Pass(), v8_resolver_.Pass(), job_params_.Pass()));
1037 } else {
1038 StopWorkerThread();
1041 factory_->RemoveJob(this);
1042 factory_ = nullptr;
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;
1050 thread_.reset();
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);
1092 } // namespace
1094 // static
1095 scoped_ptr<ProxyResolverV8TracingFactory>
1096 ProxyResolverV8TracingFactory::Create() {
1097 return make_scoped_ptr(new ProxyResolverV8TracingFactoryImpl());
1100 } // namespace net