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

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

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