Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(187)

Side by Side Diff: net/proxy/proxy_resolver_v8_tracing.cc

Issue 11885009: Improve performance of proxy resolver by tracing DNS dependencies. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: re-upload due to failure last time Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/proxy/proxy_resolver_v8_tracing.h ('k') | net/proxy/proxy_resolver_v8_tracing_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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.
4
5 #include "net/proxy/proxy_resolver_v8_tracing.h"
6
7 #include "base/bind.h"
8 #include "base/message_loop_proxy.h"
9 #include "base/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/host_resolver.h"
17 #include "net/base/net_errors.h"
18 #include "net/base/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"
22
23 // The intent of this class is explained in the design document:
24 // https://docs.google.com/a/chromium.org/document/d/16Ij5OcVnR3s0MH4Z5XkhI9VTPo MJdaBn9rKreAmGOdE/edit
25 //
26 // In a nutshell, PAC scripts are Javascript programs and may depend on
27 // network I/O, by calling functions like dnsResolve().
28 //
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.
34 //
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 {
41
42 namespace {
43
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;
49
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;
55
56 // Returns event parameters for a PAC error message (line number + message).
57 base::Value* NetLogErrorCallback(int line_number,
58 const 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;
64 }
65
66 } // namespace
67
68 // The Job class is responsible for executing GetProxyForURL() and
69 // SetPacScript(), since both of these operations share similar code.
70 //
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.
74 //
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.
78 //
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);
89
90 // Called from origin thread.
91 void StartSetPacScript(
92 const scoped_refptr<ProxyResolverScriptData>& script_data,
93 const CompletionCallback& callback);
94
95 // Called from origin thread.
96 void StartGetProxyForURL(const GURL& url,
97 ProxyInfo* results,
98 const BoundNetLog& net_log,
99 const CompletionCallback& callback);
100
101 // Called from origin thread.
102 void Cancel();
103
104 // Called from origin thread.
105 LoadState GetLoadState() const;
106
107 private:
108 typedef std::map<std::string, std::string> DnsCache;
109 friend class base::RefCountedThreadSafe<ProxyResolverV8Tracing::Job>;
110
111 enum Operation {
112 SET_PAC_SCRIPT,
113 GET_PROXY_FOR_URL,
114 };
115
116 struct AlertOrError {
117 bool is_alert;
118 int line_number;
119 string16 message;
120 };
121
122 ~Job();
123
124 void CheckIsOnWorkerThread() const;
125 void CheckIsOnOriginThread() const;
126
127 void SetCallback(const CompletionCallback& callback);
128 void ReleaseCallback();
129
130 ProxyResolverV8* v8_resolver();
131 MessageLoop* worker_loop();
132 HostResolver* host_resolver();
133 ProxyResolverErrorObserver* error_observer();
134 NetLog* net_log();
135
136 // Invokes the user's callback.
137 void NotifyCaller(int result);
138 void NotifyCallerOnOriginLoop(int result);
139
140 void Start(Operation op, bool blocking_dns,
141 const CompletionCallback& callback);
142
143 void ExecuteBlocking();
144 void ExecuteNonBlocking();
145 int ExecuteProxyResolver();
146
147 // Implementation of ProxyResolverv8::JSBindings
148 virtual bool ResolveDns(const std::string& host,
149 ResolveDnsOperation op,
150 std::string* output) OVERRIDE;
151 virtual void Alert(const string16& message) OVERRIDE;
152 virtual void OnError(int line_number, const string16& error) OVERRIDE;
153
154 bool ResolveDnsBlocking(const std::string& host,
155 ResolveDnsOperation op,
156 std::string* output);
157
158 bool ResolveDnsNonBlocking(const std::string& host,
159 ResolveDnsOperation op,
160 std::string* output);
161
162 void DoDnsOperation(const std::string& host, ResolveDnsOperation op,
163 bool* out_cache_hit);
164 void OnDnsOperationComplete(int result);
165
166 void ScheduleRestartWithBlockingDns();
167
168 bool GetDnsFromLocalCache(const std::string& host, ResolveDnsOperation op,
169 std::string* output, bool* return_value);
170
171 void SaveDnsToLocalCache(const std::string& host, ResolveDnsOperation op,
172 int net_error, const net::AddressList& addresses);
173
174 // Builds a RequestInfo to service the specified PAC DNS operation.
175 static HostResolver::RequestInfo MakeDnsRequestInfo(const std::string& host,
176 ResolveDnsOperation op);
177
178 // Makes a key for looking up |host, op| in |dns_cache_|. Strings are used for
179 // convenience, to avoid defining custom comparators.
180 static std::string MakeDnsCacheKey(const std::string& host,
181 ResolveDnsOperation op);
182
183 void HandleAlertOrError(bool is_alert, int line_number,
184 const string16& message);
185 void DispatchBufferedAlertsAndErrors();
186 void DispatchAlertOrError(bool is_alert, int line_number,
187 const string16& message);
188
189 void LogEventToCurrentRequestAndGlobally(
190 NetLog::EventType type,
191 const NetLog::ParametersCallback& parameters_callback);
192
193 // The thread which called into ProxyResolverV8Tracing, and on which the
194 // completion callback is expected to run.
195 scoped_refptr<base::MessageLoopProxy> origin_loop_;
196
197 // The ProxyResolverV8Tracing which spawned this Job.
198 // Initialized on origin thread and then accessed from both threads.
199 ProxyResolverV8Tracing* parent_;
200
201 // The callback to run (on the origin thread) when the Job finishes.
202 // Should only be accessed from origin thread.
203 CompletionCallback callback_;
204
205 // Flag to indicate whether the request has been cancelled.
206 base::CancellationFlag cancelled_;
207
208 // The operation that this Job is running.
209 // Initialized on origin thread and then accessed from both threads.
210 Operation operation_;
211
212 // The DNS mode for this Job.
213 // Initialized on origin thread, mutated on worker thread, and accessed
214 // by both the origin thread and worker thread.
215 bool blocking_dns_;
216
217 // Used to block the worker thread on a DNS operation taking place on the
218 // origin thread.
219 base::WaitableEvent event_;
220
221 // Map of DNS operations completed so far. Written into on the origin thread
222 // and read on the worker thread.
223 DnsCache dns_cache_;
224
225 // The job holds a reference to itself to ensure that it remains alive until
226 // either completion or cancellation.
227 scoped_refptr<Job> owned_self_reference_;
228
229 // -------------------------------------------------------
230 // State specific to SET_PAC_SCRIPT.
231 // -------------------------------------------------------
232
233 scoped_refptr<ProxyResolverScriptData> script_data_;
234
235 // -------------------------------------------------------
236 // State specific to GET_PROXY_FOR_URL.
237 // -------------------------------------------------------
238
239 ProxyInfo* user_results_; // Owned by caller, lives on origin thread.
240 GURL url_;
241 ProxyInfo results_;
242 BoundNetLog bound_net_log_;
243
244 // ---------------------------------------------------------------------------
245 // State for ExecuteNonBlocking()
246 // ---------------------------------------------------------------------------
247 // These variables are used exclusively on the worker thread and are only
248 // meaningful when executing inside of ExecuteNonBlocking().
249
250 // Whether this execution was abandoned due to a missing DNS dependency.
251 bool abandoned_;
252
253 // Number of calls made to ResolveDns() by this execution.
254 int num_dns_;
255
256 // Sequence of calls made to Alert() or OnError() by this execution.
257 std::vector<AlertOrError> alerts_and_errors_;
258 size_t alerts_and_errors_byte_cost_; // Approximate byte cost of the above.
259
260 // Number of calls made to ResolveDns() by the PREVIOUS execution.
261 int last_num_dns_;
262
263 // Whether the current execution needs to be restarted in blocking mode.
264 bool should_restart_with_blocking_dns_;
265
266 // ---------------------------------------------------------------------------
267 // State for pending DNS request.
268 // ---------------------------------------------------------------------------
269 // These variables are used exclusively on the origin thread.
270
271 HostResolver::RequestHandle pending_dns_;
272 // Only meaningful when |pending_dns_|:
273 std::string pending_dns_host_;
274 ResolveDnsOperation pending_dns_op_;
275 AddressList pending_dns_addresses_;
276 };
277
278 ProxyResolverV8Tracing::Job::Job(ProxyResolverV8Tracing* parent)
279 : origin_loop_(base::MessageLoopProxy::current()),
280 parent_(parent),
281 event_(true, false),
282 last_num_dns_(0),
283 pending_dns_(NULL) {
284 CheckIsOnOriginThread();
285 }
286
287 void ProxyResolverV8Tracing::Job::StartSetPacScript(
288 const scoped_refptr<ProxyResolverScriptData>& script_data,
289 const CompletionCallback& callback) {
290 CheckIsOnOriginThread();
291
292 script_data_ = script_data;
293
294 // Script initialization uses blocking DNS since there isn't any
295 // advantage to using non-blocking mode here. That is because the
296 // parent ProxyService can't submit any ProxyResolve requests until
297 // initialization has completed successfully!
298 Start(SET_PAC_SCRIPT, true /*blocking*/, callback);
299 }
300
301 void ProxyResolverV8Tracing::Job::StartGetProxyForURL(
302 const GURL& url,
303 ProxyInfo* results,
304 const BoundNetLog& net_log,
305 const CompletionCallback& callback) {
306 CheckIsOnOriginThread();
307
308 url_ = url;
309 user_results_ = results;
310 bound_net_log_ = net_log;
311
312 Start(GET_PROXY_FOR_URL, false /*non-blocking*/, callback);
313 }
314
315 void ProxyResolverV8Tracing::Job::Cancel() {
316 CheckIsOnOriginThread();
317
318 // There are several possibilities to consider for cancellation:
319 // (a) The job has been posted to the worker thread, however script execution
320 // has not yet started.
321 // (b) The script is executing on the worker thread.
322 // (c) The script is executing on the worker thread, however is blocked inside
323 // of dnsResolve() waiting for a response from the origin thread.
324 // (d) Nothing is running on the worker thread, however the host resolver has
325 // a pending DNS request which upon completion will restart the script
326 // execution.
327 // (e) The worker thread has a pending task to restart execution, which was
328 // posted after the DNS dependency was resolved and saved to local cache.
329 // (f) The script execution completed entirely, and posted a task to the
330 // origin thread to notify the caller.
331 //
332 // |cancelled_| is read on both the origin thread and worker thread. The
333 // code that runs on the worker thread is littered with checks on
334 // |cancelled_| to break out early.
335 cancelled_.Set();
336
337 ReleaseCallback();
338
339 if (pending_dns_) {
340 host_resolver()->CancelRequest(pending_dns_);
341 pending_dns_ = NULL;
342 }
343
344 // The worker thread might be blocked waiting for DNS.
345 event_.Signal();
346
347 owned_self_reference_ = NULL;
348 }
349
350 LoadState ProxyResolverV8Tracing::Job::GetLoadState() const {
351 CheckIsOnOriginThread();
352
353 if (pending_dns_)
354 return LOAD_STATE_RESOLVING_HOST_IN_PROXY_SCRIPT;
355
356 return LOAD_STATE_RESOLVING_PROXY_FOR_URL;
357 }
358
359 ProxyResolverV8Tracing::Job::~Job() {
360 DCHECK(!pending_dns_);
361 DCHECK(callback_.is_null());
362 }
363
364 void ProxyResolverV8Tracing::Job::CheckIsOnWorkerThread() const {
365 DCHECK_EQ(MessageLoop::current(), parent_->thread_->message_loop());
366 }
367
368 void ProxyResolverV8Tracing::Job::CheckIsOnOriginThread() const {
369 DCHECK(origin_loop_->BelongsToCurrentThread());
370 }
371
372 void ProxyResolverV8Tracing::Job::SetCallback(
373 const CompletionCallback& callback) {
374 CheckIsOnOriginThread();
375 DCHECK(callback_.is_null());
376 parent_->num_outstanding_callbacks_++;
377 callback_ = callback;
378 }
379
380 void ProxyResolverV8Tracing::Job::ReleaseCallback() {
381 CheckIsOnOriginThread();
382 DCHECK(!callback_.is_null());
383 CHECK_GT(parent_->num_outstanding_callbacks_, 0);
384 parent_->num_outstanding_callbacks_--;
385 callback_.Reset();
386
387 // For good measure, clear this other user-owned pointer.
388 user_results_ = NULL;
389 }
390
391 ProxyResolverV8* ProxyResolverV8Tracing::Job::v8_resolver() {
392 return parent_->v8_resolver_.get();
393 }
394
395 MessageLoop* ProxyResolverV8Tracing::Job::worker_loop() {
396 return parent_->thread_->message_loop();
397 }
398
399 HostResolver* ProxyResolverV8Tracing::Job::host_resolver() {
400 return parent_->host_resolver_;
401 }
402
403 ProxyResolverErrorObserver* ProxyResolverV8Tracing::Job::error_observer() {
404 return parent_->error_observer_.get();
405 }
406
407 NetLog* ProxyResolverV8Tracing::Job::net_log() {
408 return parent_->net_log_;
409 }
410
411 void ProxyResolverV8Tracing::Job::NotifyCaller(int result) {
412 CheckIsOnWorkerThread();
413
414 origin_loop_->PostTask(
415 FROM_HERE,
416 base::Bind(&Job::NotifyCallerOnOriginLoop, this, result));
417 }
418
419 void ProxyResolverV8Tracing::Job::NotifyCallerOnOriginLoop(int result) {
420 CheckIsOnOriginThread();
421
422 if (cancelled_.IsSet())
423 return;
424
425 DCHECK(!callback_.is_null());
426 DCHECK(!pending_dns_);
427
428 if (operation_ == GET_PROXY_FOR_URL)
429 *user_results_ = results_;
430
431 // There is only ever 1 outstanding SET_PAC_SCRIPT job. It needs to be
432 // tracked to support cancellation.
433 if (operation_ == SET_PAC_SCRIPT) {
434 DCHECK_EQ(parent_->set_pac_script_job_.get(), this);
435 parent_->set_pac_script_job_ = NULL;
436 }
437
438 CompletionCallback callback = callback_;
439 ReleaseCallback();
440 callback.Run(result);
441
442 owned_self_reference_ = NULL;
443 }
444
445 void ProxyResolverV8Tracing::Job::Start(Operation op, bool blocking_dns,
446 const CompletionCallback& callback) {
447 CheckIsOnOriginThread();
448
449 operation_ = op;
450 blocking_dns_ = blocking_dns;
451 SetCallback(callback);
452
453 owned_self_reference_ = this;
454
455 worker_loop()->PostTask(FROM_HERE,
456 blocking_dns_ ? base::Bind(&Job::ExecuteBlocking, this) :
457 base::Bind(&Job::ExecuteNonBlocking, this));
458 }
459
460 void ProxyResolverV8Tracing::Job::ExecuteBlocking() {
461 CheckIsOnWorkerThread();
462 DCHECK(blocking_dns_);
463
464 if (cancelled_.IsSet())
465 return;
466
467 NotifyCaller(ExecuteProxyResolver());
468 }
469
470 void ProxyResolverV8Tracing::Job::ExecuteNonBlocking() {
471 CheckIsOnWorkerThread();
472 DCHECK(!blocking_dns_);
473
474 if (cancelled_.IsSet())
475 return;
476
477 // Reset state for the current execution.
478 abandoned_ = false;
479 num_dns_ = 0;
480 alerts_and_errors_.clear();
481 alerts_and_errors_byte_cost_ = 0;
482 should_restart_with_blocking_dns_ = false;
483
484 int result = ExecuteProxyResolver();
485
486 if (should_restart_with_blocking_dns_) {
487 DCHECK(!blocking_dns_);
488 DCHECK(abandoned_);
489 blocking_dns_ = true;
490 ExecuteBlocking();
491 return;
492 }
493
494 if (abandoned_)
495 return;
496
497 DispatchBufferedAlertsAndErrors();
498 NotifyCaller(result);
499 }
500
501 int ProxyResolverV8Tracing::Job::ExecuteProxyResolver() {
502 JSBindings* prev_bindings = v8_resolver()->js_bindings();
503 v8_resolver()->set_js_bindings(this);
504
505 int result = ERR_UNEXPECTED; // Initialized to silence warnings.
506
507 switch (operation_) {
508 case SET_PAC_SCRIPT:
509 result = v8_resolver()->SetPacScript(
510 script_data_, CompletionCallback());
511 break;
512 case GET_PROXY_FOR_URL:
513 result = v8_resolver()->GetProxyForURL(
514 url_,
515 // Important: Do not write directly into |user_results_|, since if the
516 // request were to be cancelled from the origin thread, must guarantee
517 // that |user_results_| is not accessed anymore.
518 &results_,
519 CompletionCallback(),
520 NULL,
521 bound_net_log_);
522 break;
523 }
524
525 v8_resolver()->set_js_bindings(prev_bindings);
526 return result;
527 }
528
529 bool ProxyResolverV8Tracing::Job::ResolveDns(const std::string& host,
530 ResolveDnsOperation op,
531 std::string* output) {
532 if (cancelled_.IsSet())
533 return false;
534
535 if ((op == DNS_RESOLVE || op == DNS_RESOLVE_EX) && host.empty()) {
536 // a DNS resolve with an empty hostname is considered an error.
537 return false;
538 }
539
540 return blocking_dns_ ?
541 ResolveDnsBlocking(host, op, output) :
542 ResolveDnsNonBlocking(host, op, output);
543 }
544
545 void ProxyResolverV8Tracing::Job::Alert(const string16& message) {
546 HandleAlertOrError(true, -1, message);
547 }
548
549 void ProxyResolverV8Tracing::Job::OnError(int line_number,
550 const string16& error) {
551 HandleAlertOrError(false, line_number, error);
552 }
553
554 bool ProxyResolverV8Tracing::Job::ResolveDnsBlocking(const std::string& host,
555 ResolveDnsOperation op,
556 std::string* output) {
557 CheckIsOnWorkerThread();
558
559 // Check if the DNS result for this host has already been cached.
560 bool rv;
561 if (GetDnsFromLocalCache(host, op, output, &rv)) {
562 // Yay, cache hit!
563 return rv;
564 }
565
566 // If the host was not in the local cache, this is a new hostname.
567
568 if (dns_cache_.size() >= kMaxUniqueResolveDnsPerExec) {
569 // Safety net for scripts with unexpectedly many DNS calls.
570 // We will continue running to completion, but will fail every
571 // subsequent DNS request.
572 return false;
573 }
574
575 bool unused;
576 origin_loop_->PostTask(
577 FROM_HERE, base::Bind(&Job::DoDnsOperation, this, host, op, &unused));
578
579 // Wait for the DNS operation to be completed by the host resolver on the
580 // origin thread. After waking up, either the request was cancelled, or
581 // the DNS result is now available in the cache.
582 event_.Wait();
583 event_.Reset();
584
585 if (cancelled_.IsSet())
586 return false;
587
588 CHECK(GetDnsFromLocalCache(host, op, output, &rv));
589 return rv;
590 }
591
592 bool ProxyResolverV8Tracing::Job::ResolveDnsNonBlocking(const std::string& host,
593 ResolveDnsOperation op,
594 std::string* output) {
595 CheckIsOnWorkerThread();
596
597 if (abandoned_) {
598 // If this execution was already abandoned can fail right away. Only 1 DNS
599 // dependency will be traced at a time (for more predictable outcomes).
600 return false;
601 }
602
603 num_dns_ += 1;
604
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;
610 }
611
612 // If the host was not in the local cache, then this is a new hostname.
613
614 if (num_dns_ <= last_num_dns_) {
615 // The sequence of DNS operations is different from last time!
616 ScheduleRestartWithBlockingDns();
617 return false;
618 }
619
620 if (dns_cache_.size() >= kMaxUniqueResolveDnsPerExec) {
621 // Safety net for scripts with unexpectedly many DNS calls.
622 return false;
623 }
624
625 DCHECK(!should_restart_with_blocking_dns_);
626
627 // Post the DNS request to the origin thread.
628 bool resolver_cache_hit = false;
629 origin_loop_->PostTask(
630 FROM_HERE, base::Bind(&Job::DoDnsOperation, this, host, op,
631 &resolver_cache_hit));
632
633 // As an optimization to avoid restarting too often, wait until the
634 // resolver's cache has been inspected on the origin thread.
635 event_.Wait();
636 event_.Reset();
637
638 if (cancelled_.IsSet())
639 return false;
640
641 if (resolver_cache_hit) {
642 CHECK(GetDnsFromLocalCache(host, op, output, &rv));
643 return rv;
644 }
645
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 last_num_dns_ = num_dns_;
651 return false;
652 }
653
654 void ProxyResolverV8Tracing::Job::DoDnsOperation(
655 const std::string& host, ResolveDnsOperation op, bool* out_cache_hit) {
656 CheckIsOnOriginThread();
657 DCHECK(!pending_dns_);
658
659 if (cancelled_.IsSet())
660 return;
661
662 int result = host_resolver()->Resolve(
663 MakeDnsRequestInfo(host, op),
664 &pending_dns_addresses_,
665 base::Bind(&Job::OnDnsOperationComplete, this),
666 &pending_dns_,
667 bound_net_log_);
668
669 bool completed_synchronously = result != ERR_IO_PENDING;
670
671 if (!blocking_dns_) {
672 // Check if the DNS result can be serviced directly from the cache.
673 // (The worker thread is blocked waiting for this information).
674 if (completed_synchronously) {
675 SaveDnsToLocalCache(host, op, result, pending_dns_addresses_);
676 pending_dns_ = NULL;
677 }
678
679 // Important: Do not read/write |out_cache_hit| after signalling, since
680 // the memory may no longer be valid.
681 *out_cache_hit = completed_synchronously;
682 event_.Signal();
683
684 if (completed_synchronously)
685 return;
686 }
687
688 pending_dns_host_ = host;
689 pending_dns_op_ = op;
690
691 if (completed_synchronously)
692 OnDnsOperationComplete(result);
693 }
694
695 void ProxyResolverV8Tracing::Job::OnDnsOperationComplete(int result) {
696 CheckIsOnOriginThread();
697
698 DCHECK(pending_dns_);
699 DCHECK(!cancelled_.IsSet());
700
701 SaveDnsToLocalCache(pending_dns_host_, pending_dns_op_, result,
702 pending_dns_addresses_);
703 pending_dns_ = NULL;
704
705 if (!blocking_dns_) {
706 // Restart. This time it should make more progress due to having
707 // cached items.
708 worker_loop()->PostTask(FROM_HERE,
709 base::Bind(&Job::ExecuteNonBlocking, this));
710 } else {
711 // Otherwise wakeup the blocked worker thread.
712 event_.Signal();
713 }
714 }
715
716 void ProxyResolverV8Tracing::Job::ScheduleRestartWithBlockingDns() {
717 CheckIsOnWorkerThread();
718
719 DCHECK(!should_restart_with_blocking_dns_);
720 DCHECK(!abandoned_);
721 DCHECK(!blocking_dns_);
722
723 abandoned_ = true;
724
725 // The restart will happen after ExecuteNonBlocking() finishes.
726 should_restart_with_blocking_dns_ = true;
727 }
728
729 bool ProxyResolverV8Tracing::Job::GetDnsFromLocalCache(
730 const std::string& host,
731 ResolveDnsOperation op,
732 std::string* output,
733 bool* return_value) {
734 CheckIsOnWorkerThread();
735
736 DnsCache::const_iterator it = dns_cache_.find(MakeDnsCacheKey(host, op));
737 if (it == dns_cache_.end())
738 return false;
739
740 *output = it->second;
741 *return_value = !it->second.empty();
742 return true;
743 }
744
745 void ProxyResolverV8Tracing::Job::SaveDnsToLocalCache(
746 const std::string& host,
747 ResolveDnsOperation op,
748 int net_error,
749 const net::AddressList& addresses) {
750 CheckIsOnOriginThread();
751
752 // Serialize the result into a string to save to the cache.
753 std::string cache_value;
754 if (net_error != OK) {
755 cache_value = std::string();
756 } else if (op == DNS_RESOLVE || op == MY_IP_ADDRESS) {
757 // dnsResolve() and myIpAddress() are expected to return a single IP
758 // address.
759 cache_value = addresses.front().ToStringWithoutPort();
760 } else {
761 // The *Ex versions are expected to return a semi-colon separated list.
762 for (AddressList::const_iterator iter = addresses.begin();
763 iter != addresses.end(); ++iter) {
764 if (!cache_value.empty())
765 cache_value += ";";
766 cache_value += iter->ToStringWithoutPort();
767 }
768 }
769
770 dns_cache_[MakeDnsCacheKey(host, op)] = cache_value;
771 }
772
773 // static
774 HostResolver::RequestInfo ProxyResolverV8Tracing::Job::MakeDnsRequestInfo(
775 const std::string& host, ResolveDnsOperation op) {
776 HostPortPair host_port = HostPortPair(host, 80);
777 if (op == MY_IP_ADDRESS || op == MY_IP_ADDRESS_EX) {
778 host_port.set_host(GetHostName());
779 }
780
781 HostResolver::RequestInfo info(host_port);
782
783 // The non-ex flavors are limited to IPv4 results.
784 if (op == MY_IP_ADDRESS || op == DNS_RESOLVE) {
785 info.set_address_family(ADDRESS_FAMILY_IPV4);
786 }
787
788 return info;
789 }
790
791 std::string ProxyResolverV8Tracing::Job::MakeDnsCacheKey(
792 const std::string& host, ResolveDnsOperation op) {
793 return StringPrintf("%d:%s", op, host.c_str());
794 }
795
796 void ProxyResolverV8Tracing::Job::HandleAlertOrError(bool is_alert,
797 int line_number,
798 const string16& message) {
799 CheckIsOnWorkerThread();
800
801 if (cancelled_.IsSet())
802 return;
803
804 if (blocking_dns_) {
805 // In blocking DNS mode the events can be dispatched immediately.
806 DispatchAlertOrError(is_alert, line_number, message);
807 return;
808 }
809
810 // Otherwise in nonblocking mode, buffer all the messages until
811 // the end.
812
813 if (abandoned_)
814 return;
815
816 alerts_and_errors_byte_cost_ += sizeof(AlertOrError) + message.size() * 2;
817
818 // If there have been lots of messages, enqueing could be expensive on
819 // memory. Consider a script which does megabytes worth of alerts().
820 // Avoid this by falling back to blocking mode.
821 if (alerts_and_errors_byte_cost_ > kMaxAlertsAndErrorsBytes) {
822 ScheduleRestartWithBlockingDns();
823 return;
824 }
825
826 AlertOrError entry = {is_alert, line_number, message};
827 alerts_and_errors_.push_back(entry);
828 }
829
830 void ProxyResolverV8Tracing::Job::DispatchBufferedAlertsAndErrors() {
831 CheckIsOnWorkerThread();
832 DCHECK(!blocking_dns_);
833 DCHECK(!abandoned_);
834
835 for (size_t i = 0; i < alerts_and_errors_.size(); ++i) {
836 const AlertOrError& x = alerts_and_errors_[i];
837 DispatchAlertOrError(x.is_alert, x.line_number, x.message);
838 }
839 }
840
841 void ProxyResolverV8Tracing::Job::DispatchAlertOrError(
842 bool is_alert, int line_number, const string16& message) {
843 CheckIsOnWorkerThread();
844
845 // Note that the handling of cancellation is racy with regard to
846 // alerts/errors. The request might get cancelled shortly after this
847 // check! (There is no lock being held to guarantee otherwise).
848 //
849 // If this happens, then some information will get written to the NetLog
850 // needlessly, however the NetLog will still be alive so it shouldn't cause
851 // problems.
852 if (cancelled_.IsSet())
853 return;
854
855 if (is_alert) {
856 // -------------------
857 // alert
858 // -------------------
859 VLOG(1) << "PAC-alert: " << message;
860
861 // Send to the NetLog.
862 LogEventToCurrentRequestAndGlobally(
863 NetLog::TYPE_PAC_JAVASCRIPT_ALERT,
864 NetLog::StringCallback("message", &message));
865 } else {
866 // -------------------
867 // error
868 // -------------------
869 if (line_number == -1)
870 VLOG(1) << "PAC-error: " << message;
871 else
872 VLOG(1) << "PAC-error: " << "line: " << line_number << ": " << message;
873
874 // Send the error to the NetLog.
875 LogEventToCurrentRequestAndGlobally(
876 NetLog::TYPE_PAC_JAVASCRIPT_ERROR,
877 base::Bind(&NetLogErrorCallback, line_number, &message));
878
879 if (error_observer())
880 error_observer()->OnPACScriptError(line_number, message);
881 }
882 }
883
884 void ProxyResolverV8Tracing::Job::LogEventToCurrentRequestAndGlobally(
885 NetLog::EventType type,
886 const NetLog::ParametersCallback& parameters_callback) {
887 CheckIsOnWorkerThread();
888 bound_net_log_.AddEvent(type, parameters_callback);
889
890 // Emit to the global NetLog event stream.
891 if (net_log())
892 net_log()->AddGlobalEntry(type, parameters_callback);
893 }
894
895 ProxyResolverV8Tracing::ProxyResolverV8Tracing(
896 HostResolver* host_resolver,
897 ProxyResolverErrorObserver* error_observer,
898 NetLog* net_log)
899 : ProxyResolver(true /*expects_pac_bytes*/),
900 host_resolver_(host_resolver),
901 error_observer_(error_observer),
902 net_log_(net_log),
903 num_outstanding_callbacks_(0) {
904 DCHECK(host_resolver);
905 // Start up the thread.
906 thread_.reset(new base::Thread("Proxy resolver"));
907 CHECK(thread_->Start());
908
909 v8_resolver_.reset(new ProxyResolverV8);
910 }
911
912 ProxyResolverV8Tracing::~ProxyResolverV8Tracing() {
913 // Note, all requests should have been cancelled.
914 CHECK(!set_pac_script_job_);
915 CHECK_EQ(0, num_outstanding_callbacks_);
916
917 // Join the worker thread.
918 // See http://crbug.com/69710.
919 base::ThreadRestrictions::ScopedAllowIO allow_io;
920 thread_.reset();
921 }
922
923 int ProxyResolverV8Tracing::GetProxyForURL(const GURL& url,
924 ProxyInfo* results,
925 const CompletionCallback& callback,
926 RequestHandle* request,
927 const BoundNetLog& net_log) {
928 DCHECK(CalledOnValidThread());
929 DCHECK(!callback.is_null());
930 DCHECK(!set_pac_script_job_);
931
932 scoped_refptr<Job> job = new Job(this);
933
934 if (request)
935 *request = job.get();
936
937 job->StartGetProxyForURL(url, results, net_log, callback);
938 return ERR_IO_PENDING;
939 }
940
941 void ProxyResolverV8Tracing::CancelRequest(RequestHandle request) {
942 Job* job = reinterpret_cast<Job*>(request);
943 job->Cancel();
944 }
945
946 LoadState ProxyResolverV8Tracing::GetLoadState(RequestHandle request) const {
947 Job* job = reinterpret_cast<Job*>(request);
948 return job->GetLoadState();
949 }
950
951 void ProxyResolverV8Tracing::CancelSetPacScript() {
952 DCHECK(set_pac_script_job_);
953 set_pac_script_job_->Cancel();
954 set_pac_script_job_ = NULL;
955 }
956
957 void ProxyResolverV8Tracing::PurgeMemory() {
958 thread_->message_loop()->PostTask(
959 FROM_HERE,
960 base::Bind(&ProxyResolverV8::PurgeMemory,
961 // The use of unretained is safe, since the worker thread
962 // cannot outlive |this|.
963 base::Unretained(v8_resolver_.get())));
964 }
965
966 int ProxyResolverV8Tracing::SetPacScript(
967 const scoped_refptr<ProxyResolverScriptData>& script_data,
968 const CompletionCallback& callback) {
969 DCHECK(CalledOnValidThread());
970 DCHECK(!callback.is_null());
971
972 // Note that there should not be any outstanding (non-cancelled) Jobs when
973 // setting the PAC script (ProxyService should guarantee this). If there are,
974 // then they might complete in strange ways after the new script is set.
975 DCHECK(!set_pac_script_job_);
976 CHECK_EQ(0, num_outstanding_callbacks_);
977
978 set_pac_script_job_ = new Job(this);
979 set_pac_script_job_->StartSetPacScript(script_data, callback);
980
981 return ERR_IO_PENDING;
982 }
983
984 } // namespace net
OLDNEW
« no previous file with comments | « net/proxy/proxy_resolver_v8_tracing.h ('k') | net/proxy/proxy_resolver_v8_tracing_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698