Revert of suppress unaddressable error in ManifestBrowserTest.CORSManifest (patchset...
[chromium-blink-merge.git] / net / proxy / proxy_resolver_v8_tracing.cc
blob230b7cafb26a9bf1de8e4f476f023e0f2eb4eff0
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 "base/bind.h"
8 #include "base/message_loop/message_loop_proxy.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/synchronization/cancellation_flag.h"
11 #include "base/synchronization/waitable_event.h"
12 #include "base/threading/thread.h"
13 #include "base/threading/thread_restrictions.h"
14 #include "base/values.h"
15 #include "net/base/address_list.h"
16 #include "net/base/net_errors.h"
17 #include "net/dns/host_resolver.h"
18 #include "net/log/net_log.h"
19 #include "net/proxy/proxy_info.h"
20 #include "net/proxy/proxy_resolver_error_observer.h"
21 #include "net/proxy/proxy_resolver_v8.h"
23 // The intent of this class is explained in the design document:
24 // https://docs.google.com/a/chromium.org/document/d/16Ij5OcVnR3s0MH4Z5XkhI9VTPoMJdaBn9rKreAmGOdE/edit
26 // In a nutshell, PAC scripts are Javascript programs and may depend on
27 // network I/O, by calling functions like dnsResolve().
29 // This is problematic since functions such as dnsResolve() will block the
30 // Javascript execution until the DNS result is availble, thereby stalling the
31 // PAC thread, which hurts the ability to process parallel proxy resolves.
32 // An obvious solution is to simply start more PAC threads, however this scales
33 // poorly, which hurts the ability to process parallel proxy resolves.
35 // The solution in ProxyResolverV8Tracing is to model PAC scripts as being
36 // deterministic, and depending only on the inputted URL. When the script
37 // issues a dnsResolve() for a yet unresolved hostname, the Javascript
38 // execution is "aborted", and then re-started once the DNS result is
39 // known.
40 namespace net {
42 namespace {
44 // Upper bound on how many *unique* DNS resolves a PAC script is allowed
45 // to make. This is a failsafe both for scripts that do a ridiculous
46 // number of DNS resolves, as well as scripts which are misbehaving
47 // under the tracing optimization. It is not expected to hit this normally.
48 const size_t kMaxUniqueResolveDnsPerExec = 20;
50 // Approximate number of bytes to use for buffering alerts() and errors.
51 // This is a failsafe in case repeated executions of the script causes
52 // too much memory bloat. It is not expected for well behaved scripts to
53 // hit this. (In fact normal scripts should not even have alerts() or errors).
54 const size_t kMaxAlertsAndErrorsBytes = 2048;
56 // Returns event parameters for a PAC error message (line number + message).
57 base::Value* NetLogErrorCallback(int line_number,
58 const base::string16* message,
59 NetLog::LogLevel /* log_level */) {
60 base::DictionaryValue* dict = new base::DictionaryValue();
61 dict->SetInteger("line_number", line_number);
62 dict->SetString("message", *message);
63 return dict;
66 } // namespace
68 // The Job class is responsible for executing GetProxyForURL() and
69 // SetPacScript(), since both of these operations share similar code.
71 // The DNS for these operations can operate in either blocking or
72 // non-blocking mode. Blocking mode is used as a fallback when the PAC script
73 // seems to be misbehaving under the tracing optimization.
75 // Note that this class runs on both the origin thread and a worker
76 // thread. Most methods are expected to be used exclusively on one thread
77 // or the other.
79 // The lifetime of Jobs does not exceed that of the ProxyResolverV8Tracing that
80 // spawned it. Destruction might happen on either the origin thread or the
81 // worker thread.
82 class ProxyResolverV8Tracing::Job
83 : public base::RefCountedThreadSafe<ProxyResolverV8Tracing::Job>,
84 public ProxyResolverV8::JSBindings {
85 public:
86 // |parent| is non-owned. It is the ProxyResolverV8Tracing that spawned this
87 // Job, and must oulive it.
88 explicit Job(ProxyResolverV8Tracing* parent);
90 // Called from origin thread.
91 void StartSetPacScript(
92 const scoped_refptr<ProxyResolverScriptData>& script_data,
93 const CompletionCallback& callback);
95 // Called from origin thread.
96 void StartGetProxyForURL(const GURL& url,
97 ProxyInfo* results,
98 const BoundNetLog& net_log,
99 const CompletionCallback& callback);
101 // Called from origin thread.
102 void Cancel();
104 // Called from origin thread.
105 LoadState GetLoadState() const;
107 private:
108 typedef std::map<std::string, std::string> DnsCache;
109 friend class base::RefCountedThreadSafe<ProxyResolverV8Tracing::Job>;
111 enum Operation {
112 SET_PAC_SCRIPT,
113 GET_PROXY_FOR_URL,
116 struct AlertOrError {
117 bool is_alert;
118 int line_number;
119 base::string16 message;
122 ~Job() override;
124 void CheckIsOnWorkerThread() const;
125 void CheckIsOnOriginThread() const;
127 void SetCallback(const CompletionCallback& callback);
128 void ReleaseCallback();
130 ProxyResolverV8* v8_resolver();
131 base::MessageLoop* worker_loop();
132 HostResolver* host_resolver();
133 ProxyResolverErrorObserver* error_observer();
134 NetLog* net_log();
136 // Invokes the user's callback.
137 void NotifyCaller(int result);
138 void NotifyCallerOnOriginLoop(int result);
140 void Start(Operation op, bool blocking_dns,
141 const CompletionCallback& callback);
143 void ExecuteBlocking();
144 void ExecuteNonBlocking();
145 int ExecuteProxyResolver();
147 // Implementation of ProxyResolverv8::JSBindings
148 bool ResolveDns(const std::string& host,
149 ResolveDnsOperation op,
150 std::string* output,
151 bool* terminate) override;
152 void Alert(const base::string16& message) override;
153 void OnError(int line_number, const base::string16& error) override;
155 bool ResolveDnsBlocking(const std::string& host,
156 ResolveDnsOperation op,
157 std::string* output);
159 bool ResolveDnsNonBlocking(const std::string& host,
160 ResolveDnsOperation op,
161 std::string* output,
162 bool* terminate);
164 bool PostDnsOperationAndWait(const std::string& host,
165 ResolveDnsOperation op,
166 bool* completed_synchronously)
167 WARN_UNUSED_RESULT;
169 void DoDnsOperation();
170 void OnDnsOperationComplete(int result);
172 void ScheduleRestartWithBlockingDns();
174 bool GetDnsFromLocalCache(const std::string& host, ResolveDnsOperation op,
175 std::string* output, bool* return_value);
177 void SaveDnsToLocalCache(const std::string& host, ResolveDnsOperation op,
178 int net_error, const net::AddressList& addresses);
180 // Builds a RequestInfo to service the specified PAC DNS operation.
181 static HostResolver::RequestInfo MakeDnsRequestInfo(const std::string& host,
182 ResolveDnsOperation op);
184 // Makes a key for looking up |host, op| in |dns_cache_|. Strings are used for
185 // convenience, to avoid defining custom comparators.
186 static std::string MakeDnsCacheKey(const std::string& host,
187 ResolveDnsOperation op);
189 void HandleAlertOrError(bool is_alert, int line_number,
190 const base::string16& message);
191 void DispatchBufferedAlertsAndErrors();
192 void DispatchAlertOrError(bool is_alert, int line_number,
193 const base::string16& message);
195 void LogEventToCurrentRequestAndGlobally(
196 NetLog::EventType type,
197 const NetLog::ParametersCallback& parameters_callback);
199 // The thread which called into ProxyResolverV8Tracing, and on which the
200 // completion callback is expected to run.
201 scoped_refptr<base::MessageLoopProxy> origin_loop_;
203 // The ProxyResolverV8Tracing which spawned this Job.
204 // Initialized on origin thread and then accessed from both threads.
205 ProxyResolverV8Tracing* parent_;
207 // The callback to run (on the origin thread) when the Job finishes.
208 // Should only be accessed from origin thread.
209 CompletionCallback callback_;
211 // Flag to indicate whether the request has been cancelled.
212 base::CancellationFlag cancelled_;
214 // The operation that this Job is running.
215 // Initialized on origin thread and then accessed from both threads.
216 Operation operation_;
218 // The DNS mode for this Job.
219 // Initialized on origin thread, mutated on worker thread, and accessed
220 // by both the origin thread and worker thread.
221 bool blocking_dns_;
223 // Used to block the worker thread on a DNS operation taking place on the
224 // origin thread.
225 base::WaitableEvent event_;
227 // Map of DNS operations completed so far. Written into on the origin thread
228 // and read on the worker thread.
229 DnsCache dns_cache_;
231 // The job holds a reference to itself to ensure that it remains alive until
232 // either completion or cancellation.
233 scoped_refptr<Job> owned_self_reference_;
235 // -------------------------------------------------------
236 // State specific to SET_PAC_SCRIPT.
237 // -------------------------------------------------------
239 scoped_refptr<ProxyResolverScriptData> script_data_;
241 // -------------------------------------------------------
242 // State specific to GET_PROXY_FOR_URL.
243 // -------------------------------------------------------
245 ProxyInfo* user_results_; // Owned by caller, lives on origin thread.
246 GURL url_;
247 ProxyInfo results_;
248 BoundNetLog bound_net_log_;
250 // ---------------------------------------------------------------------------
251 // State for ExecuteNonBlocking()
252 // ---------------------------------------------------------------------------
253 // These variables are used exclusively on the worker thread and are only
254 // meaningful when executing inside of ExecuteNonBlocking().
256 // Whether this execution was abandoned due to a missing DNS dependency.
257 bool abandoned_;
259 // Number of calls made to ResolveDns() by this execution.
260 int num_dns_;
262 // Sequence of calls made to Alert() or OnError() by this execution.
263 std::vector<AlertOrError> alerts_and_errors_;
264 size_t alerts_and_errors_byte_cost_; // Approximate byte cost of the above.
266 // Number of calls made to ResolveDns() by the PREVIOUS execution.
267 int last_num_dns_;
269 // Whether the current execution needs to be restarted in blocking mode.
270 bool should_restart_with_blocking_dns_;
272 // ---------------------------------------------------------------------------
273 // State for pending DNS request.
274 // ---------------------------------------------------------------------------
276 // Handle to the outstanding request in the HostResolver, or NULL.
277 // This is mutated and used on the origin thread, however it may be read by
278 // the worker thread for some DCHECKS().
279 HostResolver::RequestHandle pending_dns_;
281 // Indicates if the outstanding DNS request completed synchronously. Written
282 // on the origin thread, and read by the worker thread.
283 bool pending_dns_completed_synchronously_;
285 // These are the inputs to DoDnsOperation(). Written on the worker thread,
286 // read by the origin thread.
287 std::string pending_dns_host_;
288 ResolveDnsOperation pending_dns_op_;
290 // This contains the resolved address list that DoDnsOperation() fills in.
291 // Used exclusively on the origin thread.
292 AddressList pending_dns_addresses_;
295 ProxyResolverV8Tracing::Job::Job(ProxyResolverV8Tracing* parent)
296 : origin_loop_(base::MessageLoopProxy::current()),
297 parent_(parent),
298 event_(true, false),
299 last_num_dns_(0),
300 pending_dns_(NULL) {
301 CheckIsOnOriginThread();
304 void ProxyResolverV8Tracing::Job::StartSetPacScript(
305 const scoped_refptr<ProxyResolverScriptData>& script_data,
306 const CompletionCallback& callback) {
307 CheckIsOnOriginThread();
309 script_data_ = script_data;
311 // Script initialization uses blocking DNS since there isn't any
312 // advantage to using non-blocking mode here. That is because the
313 // parent ProxyService can't submit any ProxyResolve requests until
314 // initialization has completed successfully!
315 Start(SET_PAC_SCRIPT, true /*blocking*/, callback);
318 void ProxyResolverV8Tracing::Job::StartGetProxyForURL(
319 const GURL& url,
320 ProxyInfo* results,
321 const BoundNetLog& net_log,
322 const CompletionCallback& callback) {
323 CheckIsOnOriginThread();
325 url_ = url;
326 user_results_ = results;
327 bound_net_log_ = net_log;
329 Start(GET_PROXY_FOR_URL, false /*non-blocking*/, callback);
332 void ProxyResolverV8Tracing::Job::Cancel() {
333 CheckIsOnOriginThread();
335 // There are several possibilities to consider for cancellation:
336 // (a) The job has been posted to the worker thread, however script execution
337 // has not yet started.
338 // (b) The script is executing on the worker thread.
339 // (c) The script is executing on the worker thread, however is blocked inside
340 // of dnsResolve() waiting for a response from the origin thread.
341 // (d) Nothing is running on the worker thread, however the host resolver has
342 // a pending DNS request which upon completion will restart the script
343 // execution.
344 // (e) The worker thread has a pending task to restart execution, which was
345 // posted after the DNS dependency was resolved and saved to local cache.
346 // (f) The script execution completed entirely, and posted a task to the
347 // origin thread to notify the caller.
349 // |cancelled_| is read on both the origin thread and worker thread. The
350 // code that runs on the worker thread is littered with checks on
351 // |cancelled_| to break out early.
352 cancelled_.Set();
354 ReleaseCallback();
356 if (pending_dns_) {
357 host_resolver()->CancelRequest(pending_dns_);
358 pending_dns_ = NULL;
361 // The worker thread might be blocked waiting for DNS.
362 event_.Signal();
364 owned_self_reference_ = NULL;
367 LoadState ProxyResolverV8Tracing::Job::GetLoadState() const {
368 CheckIsOnOriginThread();
370 if (pending_dns_)
371 return LOAD_STATE_RESOLVING_HOST_IN_PROXY_SCRIPT;
373 return LOAD_STATE_RESOLVING_PROXY_FOR_URL;
376 ProxyResolverV8Tracing::Job::~Job() {
377 DCHECK(!pending_dns_);
378 DCHECK(callback_.is_null());
381 void ProxyResolverV8Tracing::Job::CheckIsOnWorkerThread() const {
382 DCHECK_EQ(base::MessageLoop::current(), parent_->thread_->message_loop());
385 void ProxyResolverV8Tracing::Job::CheckIsOnOriginThread() const {
386 DCHECK(origin_loop_->BelongsToCurrentThread());
389 void ProxyResolverV8Tracing::Job::SetCallback(
390 const CompletionCallback& callback) {
391 CheckIsOnOriginThread();
392 DCHECK(callback_.is_null());
393 parent_->num_outstanding_callbacks_++;
394 callback_ = callback;
397 void ProxyResolverV8Tracing::Job::ReleaseCallback() {
398 CheckIsOnOriginThread();
399 DCHECK(!callback_.is_null());
400 CHECK_GT(parent_->num_outstanding_callbacks_, 0);
401 parent_->num_outstanding_callbacks_--;
402 callback_.Reset();
404 // For good measure, clear this other user-owned pointer.
405 user_results_ = NULL;
408 ProxyResolverV8* ProxyResolverV8Tracing::Job::v8_resolver() {
409 return parent_->v8_resolver_.get();
412 base::MessageLoop* ProxyResolverV8Tracing::Job::worker_loop() {
413 return parent_->thread_->message_loop();
416 HostResolver* ProxyResolverV8Tracing::Job::host_resolver() {
417 return parent_->host_resolver_;
420 ProxyResolverErrorObserver* ProxyResolverV8Tracing::Job::error_observer() {
421 return parent_->error_observer_.get();
424 NetLog* ProxyResolverV8Tracing::Job::net_log() {
425 return parent_->net_log_;
428 void ProxyResolverV8Tracing::Job::NotifyCaller(int result) {
429 CheckIsOnWorkerThread();
431 origin_loop_->PostTask(
432 FROM_HERE,
433 base::Bind(&Job::NotifyCallerOnOriginLoop, this, result));
436 void ProxyResolverV8Tracing::Job::NotifyCallerOnOriginLoop(int result) {
437 CheckIsOnOriginThread();
439 if (cancelled_.IsSet())
440 return;
442 DCHECK(!callback_.is_null());
443 DCHECK(!pending_dns_);
445 if (operation_ == GET_PROXY_FOR_URL) {
446 *user_results_ = results_;
449 // There is only ever 1 outstanding SET_PAC_SCRIPT job. It needs to be
450 // tracked to support cancellation.
451 if (operation_ == SET_PAC_SCRIPT) {
452 DCHECK_EQ(parent_->set_pac_script_job_.get(), this);
453 parent_->set_pac_script_job_ = NULL;
456 CompletionCallback callback = callback_;
457 ReleaseCallback();
458 callback.Run(result);
460 owned_self_reference_ = NULL;
463 void ProxyResolverV8Tracing::Job::Start(Operation op, bool blocking_dns,
464 const CompletionCallback& callback) {
465 CheckIsOnOriginThread();
467 operation_ = op;
468 blocking_dns_ = blocking_dns;
469 SetCallback(callback);
471 owned_self_reference_ = this;
473 worker_loop()->PostTask(FROM_HERE,
474 blocking_dns_ ? base::Bind(&Job::ExecuteBlocking, this) :
475 base::Bind(&Job::ExecuteNonBlocking, this));
478 void ProxyResolverV8Tracing::Job::ExecuteBlocking() {
479 CheckIsOnWorkerThread();
480 DCHECK(blocking_dns_);
482 if (cancelled_.IsSet())
483 return;
485 NotifyCaller(ExecuteProxyResolver());
488 void ProxyResolverV8Tracing::Job::ExecuteNonBlocking() {
489 CheckIsOnWorkerThread();
490 DCHECK(!blocking_dns_);
492 if (cancelled_.IsSet())
493 return;
495 // Reset state for the current execution.
496 abandoned_ = false;
497 num_dns_ = 0;
498 alerts_and_errors_.clear();
499 alerts_and_errors_byte_cost_ = 0;
500 should_restart_with_blocking_dns_ = false;
502 int result = ExecuteProxyResolver();
504 if (should_restart_with_blocking_dns_) {
505 DCHECK(!blocking_dns_);
506 DCHECK(abandoned_);
507 blocking_dns_ = true;
508 ExecuteBlocking();
509 return;
512 if (abandoned_)
513 return;
515 DispatchBufferedAlertsAndErrors();
516 NotifyCaller(result);
519 int ProxyResolverV8Tracing::Job::ExecuteProxyResolver() {
520 JSBindings* prev_bindings = v8_resolver()->js_bindings();
521 v8_resolver()->set_js_bindings(this);
523 int result = ERR_UNEXPECTED; // Initialized to silence warnings.
525 switch (operation_) {
526 case SET_PAC_SCRIPT:
527 result = v8_resolver()->SetPacScript(
528 script_data_, CompletionCallback());
529 break;
530 case GET_PROXY_FOR_URL:
531 result = v8_resolver()->GetProxyForURL(
532 url_,
533 // Important: Do not write directly into |user_results_|, since if the
534 // request were to be cancelled from the origin thread, must guarantee
535 // that |user_results_| is not accessed anymore.
536 &results_,
537 CompletionCallback(),
538 NULL,
539 bound_net_log_);
540 break;
543 v8_resolver()->set_js_bindings(prev_bindings);
545 return result;
548 bool ProxyResolverV8Tracing::Job::ResolveDns(const std::string& host,
549 ResolveDnsOperation op,
550 std::string* output,
551 bool* terminate) {
552 if (cancelled_.IsSet()) {
553 *terminate = true;
554 return false;
557 if ((op == DNS_RESOLVE || op == DNS_RESOLVE_EX) && host.empty()) {
558 // a DNS resolve with an empty hostname is considered an error.
559 return false;
562 return blocking_dns_ ?
563 ResolveDnsBlocking(host, op, output) :
564 ResolveDnsNonBlocking(host, op, output, terminate);
567 void ProxyResolverV8Tracing::Job::Alert(const base::string16& message) {
568 HandleAlertOrError(true, -1, message);
571 void ProxyResolverV8Tracing::Job::OnError(int line_number,
572 const base::string16& error) {
573 HandleAlertOrError(false, line_number, error);
576 bool ProxyResolverV8Tracing::Job::ResolveDnsBlocking(const std::string& host,
577 ResolveDnsOperation op,
578 std::string* output) {
579 CheckIsOnWorkerThread();
581 // Check if the DNS result for this host has already been cached.
582 bool rv;
583 if (GetDnsFromLocalCache(host, op, output, &rv)) {
584 // Yay, cache hit!
585 return rv;
588 if (dns_cache_.size() >= kMaxUniqueResolveDnsPerExec) {
589 // Safety net for scripts with unexpectedly many DNS calls.
590 // We will continue running to completion, but will fail every
591 // subsequent DNS request.
592 return false;
595 if (!PostDnsOperationAndWait(host, op, NULL))
596 return false; // Was cancelled.
598 CHECK(GetDnsFromLocalCache(host, op, output, &rv));
599 return rv;
602 bool ProxyResolverV8Tracing::Job::ResolveDnsNonBlocking(const std::string& host,
603 ResolveDnsOperation op,
604 std::string* output,
605 bool* terminate) {
606 CheckIsOnWorkerThread();
608 if (abandoned_) {
609 // If this execution was already abandoned can fail right away. Only 1 DNS
610 // dependency will be traced at a time (for more predictable outcomes).
611 return false;
614 num_dns_ += 1;
616 // Check if the DNS result for this host has already been cached.
617 bool rv;
618 if (GetDnsFromLocalCache(host, op, output, &rv)) {
619 // Yay, cache hit!
620 return rv;
623 if (num_dns_ <= last_num_dns_) {
624 // The sequence of DNS operations is different from last time!
625 ScheduleRestartWithBlockingDns();
626 *terminate = true;
627 return false;
630 if (dns_cache_.size() >= kMaxUniqueResolveDnsPerExec) {
631 // Safety net for scripts with unexpectedly many DNS calls.
632 return false;
635 DCHECK(!should_restart_with_blocking_dns_);
637 bool completed_synchronously;
638 if (!PostDnsOperationAndWait(host, op, &completed_synchronously))
639 return false; // Was cancelled.
641 if (completed_synchronously) {
642 CHECK(GetDnsFromLocalCache(host, op, output, &rv));
643 return rv;
646 // Otherwise if the result was not in the cache, then a DNS request has
647 // been started. Abandon this invocation of FindProxyForURL(), it will be
648 // restarted once the DNS request completes.
649 abandoned_ = true;
650 *terminate = true;
651 last_num_dns_ = num_dns_;
652 return false;
655 bool ProxyResolverV8Tracing::Job::PostDnsOperationAndWait(
656 const std::string& host, ResolveDnsOperation op,
657 bool* completed_synchronously) {
659 // Post the DNS request to the origin thread.
660 DCHECK(!pending_dns_);
661 pending_dns_host_ = host;
662 pending_dns_op_ = op;
663 origin_loop_->PostTask(FROM_HERE, base::Bind(&Job::DoDnsOperation, this));
665 event_.Wait();
666 event_.Reset();
668 if (cancelled_.IsSet())
669 return false;
671 if (completed_synchronously)
672 *completed_synchronously = pending_dns_completed_synchronously_;
674 return true;
677 void ProxyResolverV8Tracing::Job::DoDnsOperation() {
678 CheckIsOnOriginThread();
679 DCHECK(!pending_dns_);
681 if (cancelled_.IsSet())
682 return;
684 HostResolver::RequestHandle dns_request = NULL;
685 int result = host_resolver()->Resolve(
686 MakeDnsRequestInfo(pending_dns_host_, pending_dns_op_),
687 DEFAULT_PRIORITY,
688 &pending_dns_addresses_,
689 base::Bind(&Job::OnDnsOperationComplete, this),
690 &dns_request,
691 bound_net_log_);
693 pending_dns_completed_synchronously_ = result != ERR_IO_PENDING;
695 // Check if the request was cancelled as a side-effect of calling into the
696 // HostResolver. This isn't the ordinary execution flow, however it is
697 // exercised by unit-tests.
698 if (cancelled_.IsSet()) {
699 if (!pending_dns_completed_synchronously_)
700 host_resolver()->CancelRequest(dns_request);
701 return;
704 if (pending_dns_completed_synchronously_) {
705 OnDnsOperationComplete(result);
706 } else {
707 DCHECK(dns_request);
708 pending_dns_ = dns_request;
709 if (!parent_->on_load_state_changed_.is_null()) {
710 parent_->on_load_state_changed_.Run(
711 this, LOAD_STATE_RESOLVING_HOST_IN_PROXY_SCRIPT);
713 // OnDnsOperationComplete() will be called by host resolver on completion.
716 if (!blocking_dns_) {
717 // The worker thread always blocks waiting to see if the result can be
718 // serviced from cache before restarting.
719 event_.Signal();
723 void ProxyResolverV8Tracing::Job::OnDnsOperationComplete(int result) {
724 CheckIsOnOriginThread();
726 DCHECK(!cancelled_.IsSet());
727 DCHECK(pending_dns_completed_synchronously_ == (pending_dns_ == NULL));
729 SaveDnsToLocalCache(pending_dns_host_, pending_dns_op_, result,
730 pending_dns_addresses_);
731 pending_dns_ = NULL;
733 if (!parent_->on_load_state_changed_.is_null() &&
734 !pending_dns_completed_synchronously_ && !cancelled_.IsSet()) {
735 parent_->on_load_state_changed_.Run(this,
736 LOAD_STATE_RESOLVING_PROXY_FOR_URL);
739 if (blocking_dns_) {
740 event_.Signal();
741 return;
744 if (!blocking_dns_ && !pending_dns_completed_synchronously_) {
745 // Restart. This time it should make more progress due to having
746 // cached items.
747 worker_loop()->PostTask(FROM_HERE,
748 base::Bind(&Job::ExecuteNonBlocking, this));
752 void ProxyResolverV8Tracing::Job::ScheduleRestartWithBlockingDns() {
753 CheckIsOnWorkerThread();
755 DCHECK(!should_restart_with_blocking_dns_);
756 DCHECK(!abandoned_);
757 DCHECK(!blocking_dns_);
759 abandoned_ = true;
761 // The restart will happen after ExecuteNonBlocking() finishes.
762 should_restart_with_blocking_dns_ = true;
765 bool ProxyResolverV8Tracing::Job::GetDnsFromLocalCache(
766 const std::string& host,
767 ResolveDnsOperation op,
768 std::string* output,
769 bool* return_value) {
770 CheckIsOnWorkerThread();
772 DnsCache::const_iterator it = dns_cache_.find(MakeDnsCacheKey(host, op));
773 if (it == dns_cache_.end())
774 return false;
776 *output = it->second;
777 *return_value = !it->second.empty();
778 return true;
781 void ProxyResolverV8Tracing::Job::SaveDnsToLocalCache(
782 const std::string& host,
783 ResolveDnsOperation op,
784 int net_error,
785 const net::AddressList& addresses) {
786 CheckIsOnOriginThread();
788 // Serialize the result into a string to save to the cache.
789 std::string cache_value;
790 if (net_error != OK) {
791 cache_value = std::string();
792 } else if (op == DNS_RESOLVE || op == MY_IP_ADDRESS) {
793 // dnsResolve() and myIpAddress() are expected to return a single IP
794 // address.
795 cache_value = addresses.front().ToStringWithoutPort();
796 } else {
797 // The *Ex versions are expected to return a semi-colon separated list.
798 for (AddressList::const_iterator iter = addresses.begin();
799 iter != addresses.end(); ++iter) {
800 if (!cache_value.empty())
801 cache_value += ";";
802 cache_value += iter->ToStringWithoutPort();
806 dns_cache_[MakeDnsCacheKey(host, op)] = cache_value;
809 // static
810 HostResolver::RequestInfo ProxyResolverV8Tracing::Job::MakeDnsRequestInfo(
811 const std::string& host, ResolveDnsOperation op) {
812 HostPortPair host_port = HostPortPair(host, 80);
813 if (op == MY_IP_ADDRESS || op == MY_IP_ADDRESS_EX) {
814 host_port.set_host(GetHostName());
817 HostResolver::RequestInfo info(host_port);
818 // Flag myIpAddress requests.
819 if (op == MY_IP_ADDRESS || op == MY_IP_ADDRESS_EX) {
820 // TODO: Provide a RequestInfo construction mechanism that does not
821 // require a hostname and sets is_my_ip_address to true instead of this.
822 info.set_is_my_ip_address(true);
824 // The non-ex flavors are limited to IPv4 results.
825 if (op == MY_IP_ADDRESS || op == DNS_RESOLVE) {
826 info.set_address_family(ADDRESS_FAMILY_IPV4);
829 return info;
832 std::string ProxyResolverV8Tracing::Job::MakeDnsCacheKey(
833 const std::string& host, ResolveDnsOperation op) {
834 return base::StringPrintf("%d:%s", op, host.c_str());
837 void ProxyResolverV8Tracing::Job::HandleAlertOrError(
838 bool is_alert,
839 int line_number,
840 const base::string16& message) {
841 CheckIsOnWorkerThread();
843 if (cancelled_.IsSet())
844 return;
846 if (blocking_dns_) {
847 // In blocking DNS mode the events can be dispatched immediately.
848 DispatchAlertOrError(is_alert, line_number, message);
849 return;
852 // Otherwise in nonblocking mode, buffer all the messages until
853 // the end.
855 if (abandoned_)
856 return;
858 alerts_and_errors_byte_cost_ += sizeof(AlertOrError) + message.size() * 2;
860 // If there have been lots of messages, enqueing could be expensive on
861 // memory. Consider a script which does megabytes worth of alerts().
862 // Avoid this by falling back to blocking mode.
863 if (alerts_and_errors_byte_cost_ > kMaxAlertsAndErrorsBytes) {
864 ScheduleRestartWithBlockingDns();
865 return;
868 AlertOrError entry = {is_alert, line_number, message};
869 alerts_and_errors_.push_back(entry);
872 void ProxyResolverV8Tracing::Job::DispatchBufferedAlertsAndErrors() {
873 CheckIsOnWorkerThread();
874 DCHECK(!blocking_dns_);
875 DCHECK(!abandoned_);
877 for (size_t i = 0; i < alerts_and_errors_.size(); ++i) {
878 const AlertOrError& x = alerts_and_errors_[i];
879 DispatchAlertOrError(x.is_alert, x.line_number, x.message);
883 void ProxyResolverV8Tracing::Job::DispatchAlertOrError(
884 bool is_alert, int line_number, const base::string16& message) {
885 CheckIsOnWorkerThread();
887 // Note that the handling of cancellation is racy with regard to
888 // alerts/errors. The request might get cancelled shortly after this
889 // check! (There is no lock being held to guarantee otherwise).
891 // If this happens, then some information will get written to the NetLog
892 // needlessly, however the NetLog will still be alive so it shouldn't cause
893 // problems.
894 if (cancelled_.IsSet())
895 return;
897 if (is_alert) {
898 // -------------------
899 // alert
900 // -------------------
901 VLOG(1) << "PAC-alert: " << message;
903 // Send to the NetLog.
904 LogEventToCurrentRequestAndGlobally(
905 NetLog::TYPE_PAC_JAVASCRIPT_ALERT,
906 NetLog::StringCallback("message", &message));
907 } else {
908 // -------------------
909 // error
910 // -------------------
911 if (line_number == -1)
912 VLOG(1) << "PAC-error: " << message;
913 else
914 VLOG(1) << "PAC-error: " << "line: " << line_number << ": " << message;
916 // Send the error to the NetLog.
917 LogEventToCurrentRequestAndGlobally(
918 NetLog::TYPE_PAC_JAVASCRIPT_ERROR,
919 base::Bind(&NetLogErrorCallback, line_number, &message));
921 if (error_observer())
922 error_observer()->OnPACScriptError(line_number, message);
926 void ProxyResolverV8Tracing::Job::LogEventToCurrentRequestAndGlobally(
927 NetLog::EventType type,
928 const NetLog::ParametersCallback& parameters_callback) {
929 CheckIsOnWorkerThread();
930 bound_net_log_.AddEvent(type, parameters_callback);
932 // Emit to the global NetLog event stream.
933 if (net_log())
934 net_log()->AddGlobalEntry(type, parameters_callback);
937 ProxyResolverV8Tracing::ProxyResolverV8Tracing(
938 HostResolver* host_resolver,
939 ProxyResolverErrorObserver* error_observer,
940 NetLog* net_log)
941 : ProxyResolverV8Tracing(host_resolver,
942 error_observer,
943 net_log,
944 LoadStateChangedCallback()) {
947 ProxyResolverV8Tracing::ProxyResolverV8Tracing(
948 HostResolver* host_resolver,
949 ProxyResolverErrorObserver* error_observer,
950 NetLog* net_log,
951 const LoadStateChangedCallback& on_load_state_changed)
952 : ProxyResolver(true /*expects_pac_bytes*/),
953 host_resolver_(host_resolver),
954 error_observer_(error_observer),
955 net_log_(net_log),
956 num_outstanding_callbacks_(0),
957 on_load_state_changed_(on_load_state_changed) {
958 DCHECK(host_resolver);
959 // Start up the thread.
960 thread_.reset(new base::Thread("Proxy resolver"));
961 base::Thread::Options options;
962 options.timer_slack = base::TIMER_SLACK_MAXIMUM;
963 CHECK(thread_->StartWithOptions(options));
965 v8_resolver_.reset(new ProxyResolverV8);
968 ProxyResolverV8Tracing::~ProxyResolverV8Tracing() {
969 // Note, all requests should have been cancelled.
970 CHECK(!set_pac_script_job_.get());
971 CHECK_EQ(0, num_outstanding_callbacks_);
973 // Join the worker thread. See http://crbug.com/69710. Note that we call
974 // Stop() here instead of simply clearing thread_ since there may be pending
975 // callbacks on the worker thread which want to dereference thread_.
976 base::ThreadRestrictions::ScopedAllowIO allow_io;
977 thread_->Stop();
980 int ProxyResolverV8Tracing::GetProxyForURL(const GURL& url,
981 ProxyInfo* results,
982 const CompletionCallback& callback,
983 RequestHandle* request,
984 const BoundNetLog& net_log) {
985 DCHECK(CalledOnValidThread());
986 DCHECK(!callback.is_null());
987 DCHECK(!set_pac_script_job_.get());
989 scoped_refptr<Job> job = new Job(this);
991 if (request)
992 *request = job.get();
994 job->StartGetProxyForURL(url, results, net_log, callback);
995 return ERR_IO_PENDING;
998 void ProxyResolverV8Tracing::CancelRequest(RequestHandle request) {
999 Job* job = reinterpret_cast<Job*>(request);
1000 job->Cancel();
1003 LoadState ProxyResolverV8Tracing::GetLoadState(RequestHandle request) const {
1004 Job* job = reinterpret_cast<Job*>(request);
1005 return job->GetLoadState();
1008 void ProxyResolverV8Tracing::CancelSetPacScript() {
1009 DCHECK(set_pac_script_job_.get());
1010 set_pac_script_job_->Cancel();
1011 set_pac_script_job_ = NULL;
1014 int ProxyResolverV8Tracing::SetPacScript(
1015 const scoped_refptr<ProxyResolverScriptData>& script_data,
1016 const CompletionCallback& callback) {
1017 DCHECK(CalledOnValidThread());
1018 DCHECK(!callback.is_null());
1020 // Note that there should not be any outstanding (non-cancelled) Jobs when
1021 // setting the PAC script (ProxyService should guarantee this). If there are,
1022 // then they might complete in strange ways after the new script is set.
1023 DCHECK(!set_pac_script_job_.get());
1024 CHECK_EQ(0, num_outstanding_callbacks_);
1026 set_pac_script_job_ = new Job(this);
1027 set_pac_script_job_->StartSetPacScript(script_data, callback);
1029 return ERR_IO_PENDING;
1032 } // namespace net