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"
9 #include "base/files/file_util.h"
10 #include "base/path_service.h"
11 #include "base/run_loop.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/synchronization/waitable_event.h"
14 #include "base/threading/platform_thread.h"
15 #include "base/threading/thread_checker.h"
16 #include "base/values.h"
17 #include "net/base/net_errors.h"
18 #include "net/base/test_completion_callback.h"
19 #include "net/dns/host_cache.h"
20 #include "net/dns/mock_host_resolver.h"
21 #include "net/log/net_log.h"
22 #include "net/proxy/proxy_info.h"
23 #include "net/test/event_waiter.h"
24 #include "testing/gtest/include/gtest/gtest.h"
31 class ProxyResolverV8TracingTest
: public testing::Test
{
33 void TearDown() override
{
34 // Drain any pending messages, which may be left over from cancellation.
35 // This way they get reliably run as part of the current test, rather than
36 // spilling into the next test's execution.
37 base::RunLoop().RunUntilIdle();
41 scoped_refptr
<ProxyResolverScriptData
> LoadScriptData(const char* filename
) {
43 PathService::Get(base::DIR_SOURCE_ROOT
, &path
);
44 path
= path
.AppendASCII("net");
45 path
= path
.AppendASCII("data");
46 path
= path
.AppendASCII("proxy_resolver_v8_tracing_unittest");
47 path
= path
.AppendASCII(filename
);
49 // Try to read the file from disk.
50 std::string file_contents
;
51 bool ok
= base::ReadFileToString(path
, &file_contents
);
53 // If we can't load the file from disk, something is misconfigured.
54 EXPECT_TRUE(ok
) << "Failed to read file: " << path
.value();
56 // Load the PAC script into the ProxyResolver.
57 return ProxyResolverScriptData::FromUTF8(file_contents
);
62 explicit MockBindings(HostResolver
* host_resolver
)
63 : host_resolver_(host_resolver
) {}
65 void Alert(const base::string16
& message
) {
66 alerts_
.push_back(base::UTF16ToASCII(message
));
68 void OnError(int line_number
, const base::string16
& error
) {
69 waiter_
.NotifyEvent(EVENT_ERROR
);
70 errors_
.push_back(std::make_pair(line_number
, base::UTF16ToASCII(error
)));
71 if (!error_callback_
.is_null())
72 error_callback_
.Run();
75 HostResolver
* host_resolver() { return host_resolver_
; }
77 std::vector
<std::string
> GetAlerts() {
81 std::vector
<std::pair
<int, std::string
>> GetErrors() {
85 void RunOnError(const base::Closure
& callback
) {
86 error_callback_
= callback
;
87 waiter_
.WaitForEvent(EVENT_ERROR
);
90 scoped_ptr
<ProxyResolverV8Tracing::Bindings
> CreateBindings() {
91 return make_scoped_ptr(new ForwardingBindings(this));
95 class ForwardingBindings
: public ProxyResolverV8Tracing::Bindings
{
97 explicit ForwardingBindings(MockBindings
* bindings
) : bindings_(bindings
) {}
99 // ProxyResolverV8Tracing::Bindings overrides.
100 void Alert(const base::string16
& message
) override
{
101 DCHECK(thread_checker_
.CalledOnValidThread());
102 bindings_
->Alert(message
);
105 void OnError(int line_number
, const base::string16
& error
) override
{
106 DCHECK(thread_checker_
.CalledOnValidThread());
107 bindings_
->OnError(line_number
, error
);
110 BoundNetLog
GetBoundNetLog() override
{
111 DCHECK(thread_checker_
.CalledOnValidThread());
112 return BoundNetLog();
115 HostResolver
* GetHostResolver() override
{
116 DCHECK(thread_checker_
.CalledOnValidThread());
117 return bindings_
->host_resolver();
121 MockBindings
* bindings_
;
122 base::ThreadChecker thread_checker_
;
129 std::vector
<std::string
> alerts_
;
130 std::vector
<std::pair
<int, std::string
>> errors_
;
131 HostResolver
* const host_resolver_
;
132 base::Closure error_callback_
;
133 EventWaiter
<Event
> waiter_
;
136 scoped_ptr
<ProxyResolverV8Tracing
> CreateResolver(
137 scoped_ptr
<ProxyResolverV8Tracing::Bindings
> bindings
,
138 const char* filename
) {
139 scoped_ptr
<ProxyResolverV8Tracing
> resolver
;
140 scoped_ptr
<ProxyResolverV8TracingFactory
> factory(
141 ProxyResolverV8TracingFactory::Create());
142 TestCompletionCallback callback
;
143 scoped_ptr
<ProxyResolverFactory::Request
> request
;
144 factory
->CreateProxyResolverV8Tracing(LoadScriptData(filename
),
145 bindings
.Pass(), &resolver
,
146 callback
.callback(), &request
);
147 EXPECT_EQ(OK
, callback
.WaitForResult());
148 EXPECT_TRUE(resolver
);
149 return resolver
.Pass();
152 TEST_F(ProxyResolverV8TracingTest
, Simple
) {
153 MockCachingHostResolver host_resolver
;
154 MockBindings
mock_bindings(&host_resolver
);
156 scoped_ptr
<ProxyResolverV8Tracing
> resolver
=
157 CreateResolver(mock_bindings
.CreateBindings(), "simple.js");
159 TestCompletionCallback callback
;
160 ProxyInfo proxy_info
;
162 resolver
->GetProxyForURL(GURL("http://foo/"), &proxy_info
,
163 callback
.callback(), NULL
,
164 mock_bindings
.CreateBindings());
166 EXPECT_EQ(OK
, callback
.WaitForResult());
168 EXPECT_EQ("foo:99", proxy_info
.proxy_server().ToURI());
170 EXPECT_EQ(0u, host_resolver
.num_resolve());
172 // There were no alerts or errors.
173 EXPECT_TRUE(mock_bindings
.GetAlerts().empty());
174 EXPECT_TRUE(mock_bindings
.GetErrors().empty());
177 TEST_F(ProxyResolverV8TracingTest
, JavascriptError
) {
178 MockCachingHostResolver host_resolver
;
179 MockBindings
mock_bindings(&host_resolver
);
181 scoped_ptr
<ProxyResolverV8Tracing
> resolver
=
182 CreateResolver(mock_bindings
.CreateBindings(), "error.js");
184 TestCompletionCallback callback
;
185 ProxyInfo proxy_info
;
187 resolver
->GetProxyForURL(GURL("http://throw-an-error/"), &proxy_info
,
188 callback
.callback(), NULL
,
189 mock_bindings
.CreateBindings());
191 EXPECT_EQ(ERR_PAC_SCRIPT_FAILED
, callback
.WaitForResult());
193 EXPECT_EQ(0u, host_resolver
.num_resolve());
195 // Check the output -- there was 1 alert and 1 javascript error.
196 ASSERT_EQ(1u, mock_bindings
.GetAlerts().size());
197 EXPECT_EQ("Prepare to DIE!", mock_bindings
.GetAlerts()[0]);
198 ASSERT_EQ(1u, mock_bindings
.GetErrors().size());
199 EXPECT_EQ(5, mock_bindings
.GetErrors()[0].first
);
200 EXPECT_EQ("Uncaught TypeError: Cannot read property 'split' of null",
201 mock_bindings
.GetErrors()[0].second
);
204 TEST_F(ProxyResolverV8TracingTest
, TooManyAlerts
) {
205 MockCachingHostResolver host_resolver
;
206 MockBindings
mock_bindings(&host_resolver
);
208 scoped_ptr
<ProxyResolverV8Tracing
> resolver
=
209 CreateResolver(mock_bindings
.CreateBindings(), "too_many_alerts.js");
211 TestCompletionCallback callback
;
212 ProxyInfo proxy_info
;
214 resolver
->GetProxyForURL(GURL("http://foo/"), &proxy_info
,
215 callback
.callback(), NULL
,
216 mock_bindings
.CreateBindings());
218 EXPECT_EQ(OK
, callback
.WaitForResult());
220 // Iteration1 does a DNS resolve
221 // Iteration2 exceeds the alert buffer
222 // Iteration3 runs in blocking mode and completes
223 EXPECT_EQ("foo:3", proxy_info
.proxy_server().ToURI());
225 EXPECT_EQ(1u, host_resolver
.num_resolve());
228 EXPECT_TRUE(mock_bindings
.GetErrors().empty());
230 // Check the alerts -- the script generated 50 alerts.
231 std::vector
<std::string
> alerts
= mock_bindings
.GetAlerts();
232 ASSERT_EQ(50u, alerts
.size());
233 for (size_t i
= 0; i
< alerts
.size(); i
++) {
234 EXPECT_EQ("Gee, all these alerts are silly!", alerts
[i
]);
238 // Verify that buffered alerts cannot grow unboundedly, even when the message is
240 TEST_F(ProxyResolverV8TracingTest
, TooManyEmptyAlerts
) {
241 MockCachingHostResolver host_resolver
;
242 MockBindings
mock_bindings(&host_resolver
);
244 scoped_ptr
<ProxyResolverV8Tracing
> resolver
= CreateResolver(
245 mock_bindings
.CreateBindings(), "too_many_empty_alerts.js");
247 TestCompletionCallback callback
;
248 ProxyInfo proxy_info
;
250 resolver
->GetProxyForURL(GURL("http://foo/"), &proxy_info
,
251 callback
.callback(), NULL
,
252 mock_bindings
.CreateBindings());
254 EXPECT_EQ(OK
, callback
.WaitForResult());
256 EXPECT_EQ("foo:3", proxy_info
.proxy_server().ToURI());
258 EXPECT_EQ(1u, host_resolver
.num_resolve());
261 EXPECT_TRUE(mock_bindings
.GetErrors().empty());
263 // Check the alerts -- the script generated 1000 alerts.
264 std::vector
<std::string
> alerts
= mock_bindings
.GetAlerts();
265 ASSERT_EQ(1000u, alerts
.size());
266 for (size_t i
= 0; i
< alerts
.size(); i
++) {
267 EXPECT_EQ("", alerts
[i
]);
271 // This test runs a PAC script that issues a sequence of DNS resolves. The test
272 // verifies the final result, and that the underlying DNS resolver received
273 // the correct set of queries.
274 TEST_F(ProxyResolverV8TracingTest
, Dns
) {
275 MockCachingHostResolver host_resolver
;
276 MockBindings
mock_bindings(&host_resolver
);
278 host_resolver
.rules()->AddRuleForAddressFamily(
279 "host1", ADDRESS_FAMILY_IPV4
, "166.155.144.44");
280 host_resolver
.rules()
281 ->AddIPLiteralRule("host1", "::1,192.168.1.1", std::string());
282 host_resolver
.rules()->AddSimulatedFailure("host2");
283 host_resolver
.rules()->AddRule("host3", "166.155.144.33");
284 host_resolver
.rules()->AddRule("host5", "166.155.144.55");
285 host_resolver
.rules()->AddSimulatedFailure("host6");
286 host_resolver
.rules()->AddRuleForAddressFamily(
287 "*", ADDRESS_FAMILY_IPV4
, "122.133.144.155");
288 host_resolver
.rules()->AddRule("*", "133.122.100.200");
290 scoped_ptr
<ProxyResolverV8Tracing
> resolver
=
291 CreateResolver(mock_bindings
.CreateBindings(), "dns.js");
293 TestCompletionCallback callback
;
294 ProxyInfo proxy_info
;
296 resolver
->GetProxyForURL(GURL("http://foo/"), &proxy_info
,
297 callback
.callback(), NULL
,
298 mock_bindings
.CreateBindings());
300 EXPECT_EQ(OK
, callback
.WaitForResult());
302 // The test does 13 DNS resolution, however only 7 of them are unique.
303 EXPECT_EQ(7u, host_resolver
.num_resolve());
305 const char* kExpectedResult
=
306 "122.133.144.155-" // myIpAddress()
307 "null-" // dnsResolve('')
308 "__1_192.168.1.1-" // dnsResolveEx('host1')
309 "null-" // dnsResolve('host2')
310 "166.155.144.33-" // dnsResolve('host3')
311 "122.133.144.155-" // myIpAddress()
312 "166.155.144.33-" // dnsResolve('host3')
313 "__1_192.168.1.1-" // dnsResolveEx('host1')
314 "122.133.144.155-" // myIpAddress()
315 "null-" // dnsResolve('host2')
316 "-" // dnsResolveEx('host6')
317 "133.122.100.200-" // myIpAddressEx()
318 "166.155.144.44" // dnsResolve('host1')
321 EXPECT_EQ(kExpectedResult
, proxy_info
.proxy_server().ToURI());
324 EXPECT_TRUE(mock_bindings
.GetErrors().empty());
326 // The script generated 1 alert.
327 ASSERT_EQ(1u, mock_bindings
.GetAlerts().size());
328 EXPECT_EQ("iteration: 7", mock_bindings
.GetAlerts()[0]);
331 // This test runs a PAC script that does "myIpAddress()" followed by
332 // "dnsResolve()". This requires 2 restarts. However once the HostResolver's
333 // cache is warmed, subsequent calls should take 0 restarts.
334 TEST_F(ProxyResolverV8TracingTest
, DnsChecksCache
) {
335 MockCachingHostResolver host_resolver
;
336 MockBindings
mock_bindings(&host_resolver
);
338 host_resolver
.rules()->AddRule("foopy", "166.155.144.11");
339 host_resolver
.rules()->AddRule("*", "122.133.144.155");
341 scoped_ptr
<ProxyResolverV8Tracing
> resolver
=
342 CreateResolver(mock_bindings
.CreateBindings(), "simple_dns.js");
344 TestCompletionCallback callback1
;
345 TestCompletionCallback callback2
;
346 ProxyInfo proxy_info
;
348 resolver
->GetProxyForURL(GURL("http://foopy/req1"), &proxy_info
,
349 callback1
.callback(), NULL
,
350 mock_bindings
.CreateBindings());
352 EXPECT_EQ(OK
, callback1
.WaitForResult());
354 // The test does 2 DNS resolutions.
355 EXPECT_EQ(2u, host_resolver
.num_resolve());
357 // The first request took 2 restarts, hence on g_iteration=3.
358 EXPECT_EQ("166.155.144.11:3", proxy_info
.proxy_server().ToURI());
360 resolver
->GetProxyForURL(GURL("http://foopy/req2"), &proxy_info
,
361 callback2
.callback(), NULL
,
362 mock_bindings
.CreateBindings());
364 EXPECT_EQ(OK
, callback2
.WaitForResult());
366 EXPECT_EQ(4u, host_resolver
.num_resolve());
368 // This time no restarts were required, so g_iteration incremented by 1.
369 EXPECT_EQ("166.155.144.11:4", proxy_info
.proxy_server().ToURI());
371 // There were no alerts or errors.
372 EXPECT_TRUE(mock_bindings
.GetAlerts().empty());
373 EXPECT_TRUE(mock_bindings
.GetErrors().empty());
376 // This test runs a weird PAC script that was designed to defeat the DNS tracing
377 // optimization. The proxy resolver should detect the inconsistency and
378 // fall-back to synchronous mode execution.
379 TEST_F(ProxyResolverV8TracingTest
, FallBackToSynchronous1
) {
380 MockCachingHostResolver host_resolver
;
381 MockBindings
mock_bindings(&host_resolver
);
383 host_resolver
.rules()->AddRule("host1", "166.155.144.11");
384 host_resolver
.rules()->AddRule("crazy4", "133.199.111.4");
385 host_resolver
.rules()->AddRule("*", "122.133.144.155");
387 scoped_ptr
<ProxyResolverV8Tracing
> resolver
=
388 CreateResolver(mock_bindings
.CreateBindings(), "global_sideffects1.js");
390 TestCompletionCallback callback
;
391 ProxyInfo proxy_info
;
393 resolver
->GetProxyForURL(GURL("http://foo/"), &proxy_info
,
394 callback
.callback(), NULL
,
395 mock_bindings
.CreateBindings());
396 EXPECT_EQ(OK
, callback
.WaitForResult());
398 // The script itself only does 2 DNS resolves per execution, however it
399 // constructs the hostname using a global counter which changes on each
401 EXPECT_EQ(3u, host_resolver
.num_resolve());
403 EXPECT_EQ("166.155.144.11-133.199.111.4:100",
404 proxy_info
.proxy_server().ToURI());
407 EXPECT_TRUE(mock_bindings
.GetErrors().empty());
409 ASSERT_EQ(1u, mock_bindings
.GetAlerts().size());
410 EXPECT_EQ("iteration: 4", mock_bindings
.GetAlerts()[0]);
413 // This test runs a weird PAC script that was designed to defeat the DNS tracing
414 // optimization. The proxy resolver should detect the inconsistency and
415 // fall-back to synchronous mode execution.
416 TEST_F(ProxyResolverV8TracingTest
, FallBackToSynchronous2
) {
417 MockCachingHostResolver host_resolver
;
418 MockBindings
mock_bindings(&host_resolver
);
420 host_resolver
.rules()->AddRule("host1", "166.155.144.11");
421 host_resolver
.rules()->AddRule("host2", "166.155.144.22");
422 host_resolver
.rules()->AddRule("host3", "166.155.144.33");
423 host_resolver
.rules()->AddRule("host4", "166.155.144.44");
424 host_resolver
.rules()->AddRule("*", "122.133.144.155");
426 scoped_ptr
<ProxyResolverV8Tracing
> resolver
=
427 CreateResolver(mock_bindings
.CreateBindings(), "global_sideffects2.js");
429 TestCompletionCallback callback
;
430 ProxyInfo proxy_info
;
432 resolver
->GetProxyForURL(GURL("http://foo/"), &proxy_info
,
433 callback
.callback(), NULL
,
434 mock_bindings
.CreateBindings());
435 EXPECT_EQ(OK
, callback
.WaitForResult());
437 EXPECT_EQ(3u, host_resolver
.num_resolve());
439 EXPECT_EQ("166.155.144.44:100", proxy_info
.proxy_server().ToURI());
441 // There were no alerts or errors.
442 EXPECT_TRUE(mock_bindings
.GetAlerts().empty());
443 EXPECT_TRUE(mock_bindings
.GetErrors().empty());
446 // This test runs a weird PAC script that yields a never ending sequence
447 // of DNS resolves when restarting. Running it will hit the maximum
448 // DNS resolves per request limit (20) after which every DNS resolve will
450 TEST_F(ProxyResolverV8TracingTest
, InfiniteDNSSequence
) {
451 MockCachingHostResolver host_resolver
;
452 MockBindings
mock_bindings(&host_resolver
);
454 host_resolver
.rules()->AddRule("host*", "166.155.144.11");
455 host_resolver
.rules()->AddRule("*", "122.133.144.155");
457 scoped_ptr
<ProxyResolverV8Tracing
> resolver
=
458 CreateResolver(mock_bindings
.CreateBindings(), "global_sideffects3.js");
460 TestCompletionCallback callback
;
461 ProxyInfo proxy_info
;
463 resolver
->GetProxyForURL(GURL("http://foo/"), &proxy_info
,
464 callback
.callback(), NULL
,
465 mock_bindings
.CreateBindings());
466 EXPECT_EQ(OK
, callback
.WaitForResult());
468 EXPECT_EQ(20u, host_resolver
.num_resolve());
471 "166.155.144.11-166.155.144.11-166.155.144.11-166.155.144.11-"
472 "166.155.144.11-166.155.144.11-166.155.144.11-166.155.144.11-"
473 "166.155.144.11-166.155.144.11-166.155.144.11-166.155.144.11-"
474 "166.155.144.11-166.155.144.11-166.155.144.11-166.155.144.11-"
475 "166.155.144.11-166.155.144.11-166.155.144.11-166.155.144.11-"
476 "null:21", proxy_info
.proxy_server().ToURI());
479 EXPECT_TRUE(mock_bindings
.GetErrors().empty());
482 EXPECT_EQ(1u, mock_bindings
.GetAlerts().size());
483 EXPECT_EQ("iteration: 21", mock_bindings
.GetAlerts()[0]);
486 // This test runs a weird PAC script that yields a never ending sequence
487 // of DNS resolves when restarting. Running it will hit the maximum
488 // DNS resolves per request limit (20) after which every DNS resolve will
490 TEST_F(ProxyResolverV8TracingTest
, InfiniteDNSSequence2
) {
491 MockCachingHostResolver host_resolver
;
492 MockBindings
mock_bindings(&host_resolver
);
494 host_resolver
.rules()->AddRule("host*", "166.155.144.11");
495 host_resolver
.rules()->AddRule("*", "122.133.144.155");
497 scoped_ptr
<ProxyResolverV8Tracing
> resolver
=
498 CreateResolver(mock_bindings
.CreateBindings(), "global_sideffects4.js");
500 TestCompletionCallback callback
;
501 ProxyInfo proxy_info
;
503 resolver
->GetProxyForURL(GURL("http://foo/"), &proxy_info
,
504 callback
.callback(), NULL
,
505 mock_bindings
.CreateBindings());
506 EXPECT_EQ(OK
, callback
.WaitForResult());
508 EXPECT_EQ(20u, host_resolver
.num_resolve());
510 EXPECT_EQ("null21:34", proxy_info
.proxy_server().ToURI());
513 EXPECT_TRUE(mock_bindings
.GetErrors().empty());
516 EXPECT_EQ(1u, mock_bindings
.GetAlerts().size());
517 EXPECT_EQ("iteration: 21", mock_bindings
.GetAlerts()[0]);
520 void DnsDuringInitHelper(bool synchronous_host_resolver
) {
521 MockCachingHostResolver host_resolver
;
522 MockBindings
mock_bindings(&host_resolver
);
523 host_resolver
.set_synchronous_mode(synchronous_host_resolver
);
525 host_resolver
.rules()->AddRule("host1", "91.13.12.1");
526 host_resolver
.rules()->AddRule("host2", "91.13.12.2");
528 scoped_ptr
<ProxyResolverV8Tracing
> resolver
=
529 CreateResolver(mock_bindings
.CreateBindings(), "dns_during_init.js");
531 // Initialization did 2 dnsResolves.
532 EXPECT_EQ(2u, host_resolver
.num_resolve());
534 host_resolver
.rules()->ClearRules();
535 host_resolver
.GetHostCache()->clear();
537 host_resolver
.rules()->AddRule("host1", "145.88.13.3");
538 host_resolver
.rules()->AddRule("host2", "137.89.8.45");
540 TestCompletionCallback callback
;
541 ProxyInfo proxy_info
;
543 resolver
->GetProxyForURL(GURL("http://foo/"), &proxy_info
,
544 callback
.callback(), NULL
,
545 mock_bindings
.CreateBindings());
546 EXPECT_EQ(OK
, callback
.WaitForResult());
548 // Fetched host1 and host2 again, since the ones done during initialization
549 // should not have been cached.
550 EXPECT_EQ(4u, host_resolver
.num_resolve());
552 EXPECT_EQ("91.13.12.1-91.13.12.2-145.88.13.3-137.89.8.45:99",
553 proxy_info
.proxy_server().ToURI());
556 ASSERT_EQ(2u, mock_bindings
.GetAlerts().size());
557 EXPECT_EQ("Watsup", mock_bindings
.GetAlerts()[0]);
558 EXPECT_EQ("Watsup2", mock_bindings
.GetAlerts()[1]);
561 // Tests a PAC script which does DNS resolves during initialization.
562 TEST_F(ProxyResolverV8TracingTest
, DnsDuringInit
) {
563 // Test with both both a host resolver that always completes asynchronously,
564 // and then again with one that completes synchronously.
565 DnsDuringInitHelper(false);
566 DnsDuringInitHelper(true);
569 void CrashCallback(int) {
570 // Be extra sure that if the callback ever gets invoked, the test will fail.
574 // Start some requests, cancel them all, and then destroy the resolver.
575 // Note the execution order for this test can vary. Since multiple
576 // threads are involved, the cancellation may be received a different
578 TEST_F(ProxyResolverV8TracingTest
, CancelAll
) {
579 MockCachingHostResolver host_resolver
;
580 MockBindings
mock_bindings(&host_resolver
);
582 host_resolver
.rules()->AddSimulatedFailure("*");
584 scoped_ptr
<ProxyResolverV8Tracing
> resolver
=
585 CreateResolver(mock_bindings
.CreateBindings(), "dns.js");
587 const size_t kNumRequests
= 5;
588 ProxyInfo proxy_info
[kNumRequests
];
589 ProxyResolver::RequestHandle request
[kNumRequests
];
591 for (size_t i
= 0; i
< kNumRequests
; ++i
) {
592 resolver
->GetProxyForURL(GURL("http://foo/"), &proxy_info
[i
],
593 base::Bind(&CrashCallback
), &request
[i
],
594 mock_bindings
.CreateBindings());
597 for (size_t i
= 0; i
< kNumRequests
; ++i
) {
598 resolver
->CancelRequest(request
[i
]);
602 // Note the execution order for this test can vary. Since multiple
603 // threads are involved, the cancellation may be received a different
605 TEST_F(ProxyResolverV8TracingTest
, CancelSome
) {
606 MockCachingHostResolver host_resolver
;
607 MockBindings
mock_bindings(&host_resolver
);
609 host_resolver
.rules()->AddSimulatedFailure("*");
611 scoped_ptr
<ProxyResolverV8Tracing
> resolver
=
612 CreateResolver(mock_bindings
.CreateBindings(), "dns.js");
614 ProxyInfo proxy_info1
;
615 ProxyInfo proxy_info2
;
616 ProxyResolver::RequestHandle request1
;
617 ProxyResolver::RequestHandle request2
;
618 TestCompletionCallback callback
;
620 resolver
->GetProxyForURL(GURL("http://foo/"), &proxy_info1
,
621 base::Bind(&CrashCallback
), &request1
,
622 mock_bindings
.CreateBindings());
623 resolver
->GetProxyForURL(GURL("http://foo/"), &proxy_info2
,
624 callback
.callback(), &request2
,
625 mock_bindings
.CreateBindings());
627 resolver
->CancelRequest(request1
);
629 EXPECT_EQ(OK
, callback
.WaitForResult());
632 // Cancel a request after it has finished running on the worker thread, and has
633 // posted a task the completion task back to origin thread.
634 TEST_F(ProxyResolverV8TracingTest
, CancelWhilePendingCompletionTask
) {
635 MockCachingHostResolver host_resolver
;
636 MockBindings
mock_bindings(&host_resolver
);
638 host_resolver
.rules()->AddSimulatedFailure("*");
640 scoped_ptr
<ProxyResolverV8Tracing
> resolver
=
641 CreateResolver(mock_bindings
.CreateBindings(), "error.js");
643 ProxyInfo proxy_info1
;
644 ProxyInfo proxy_info2
;
645 ProxyResolver::RequestHandle request1
;
646 ProxyResolver::RequestHandle request2
;
647 TestCompletionCallback callback
;
649 resolver
->GetProxyForURL(GURL("http://throw-an-error/"), &proxy_info1
,
650 base::Bind(&CrashCallback
), &request1
,
651 mock_bindings
.CreateBindings());
653 // Wait until the first request has finished running on the worker thread.
654 // Cancel the first request, while it is running its completion task on
655 // the origin thread.
656 mock_bindings
.RunOnError(base::Bind(&ProxyResolverV8Tracing::CancelRequest
,
657 base::Unretained(resolver
.get()),
660 // Start another request, to make sure it is able to complete.
661 resolver
->GetProxyForURL(GURL("http://i-have-no-idea-what-im-doing/"),
662 &proxy_info2
, callback
.callback(), &request2
,
663 mock_bindings
.CreateBindings());
665 EXPECT_EQ(OK
, callback
.WaitForResult());
667 EXPECT_EQ("i-approve-this-message:42", proxy_info2
.proxy_server().ToURI());
670 // This implementation of HostResolver allows blocking until a resolve request
671 // has been received. The resolve requests it receives will never be completed.
672 class BlockableHostResolver
: public HostResolver
{
674 BlockableHostResolver()
675 : num_cancelled_requests_(0), waiting_for_resolve_(false) {}
677 int Resolve(const RequestInfo
& info
,
678 RequestPriority priority
,
679 AddressList
* addresses
,
680 const CompletionCallback
& callback
,
681 RequestHandle
* out_req
,
682 const BoundNetLog
& net_log
) override
{
683 EXPECT_FALSE(callback
.is_null());
684 EXPECT_TRUE(out_req
);
686 if (!action_
.is_null())
689 // Indicate to the caller that a request was received.
690 EXPECT_TRUE(waiting_for_resolve_
);
691 base::MessageLoop::current()->Quit();
693 // This line is intentionally after action_.Run(), since one of the
694 // tests does a cancellation inside of Resolve(), and it is more
695 // interesting if *out_req hasn't been written yet at that point.
696 *out_req
= reinterpret_cast<RequestHandle
*>(1); // Magic value.
698 // Return ERR_IO_PENDING as this request will NEVER be completed.
699 // Expectation is for the caller to later cancel the request.
700 return ERR_IO_PENDING
;
703 int ResolveFromCache(const RequestInfo
& info
,
704 AddressList
* addresses
,
705 const BoundNetLog
& net_log
) override
{
707 return ERR_DNS_CACHE_MISS
;
710 void CancelRequest(RequestHandle req
) override
{
711 EXPECT_EQ(reinterpret_cast<RequestHandle
*>(1), req
);
712 num_cancelled_requests_
++;
715 void SetAction(const base::Callback
<void(void)>& action
) {
719 // Waits until Resolve() has been called.
720 void WaitUntilRequestIsReceived() {
721 waiting_for_resolve_
= true;
722 base::MessageLoop::current()->Run();
723 DCHECK(waiting_for_resolve_
);
724 waiting_for_resolve_
= false;
727 int num_cancelled_requests() const {
728 return num_cancelled_requests_
;
732 int num_cancelled_requests_
;
733 bool waiting_for_resolve_
;
734 base::Callback
<void(void)> action_
;
737 // This cancellation test exercises a more predictable cancellation codepath --
738 // when the request has an outstanding DNS request in flight.
739 TEST_F(ProxyResolverV8TracingTest
, CancelWhileOutstandingNonBlockingDns
) {
740 BlockableHostResolver host_resolver
;
741 MockBindings
mock_bindings(&host_resolver
);
743 scoped_ptr
<ProxyResolverV8Tracing
> resolver
=
744 CreateResolver(mock_bindings
.CreateBindings(), "dns.js");
746 ProxyInfo proxy_info1
;
747 ProxyInfo proxy_info2
;
748 ProxyResolver::RequestHandle request1
;
749 ProxyResolver::RequestHandle request2
;
751 resolver
->GetProxyForURL(GURL("http://foo/req1"), &proxy_info1
,
752 base::Bind(&CrashCallback
), &request1
,
753 mock_bindings
.CreateBindings());
755 host_resolver
.WaitUntilRequestIsReceived();
757 resolver
->GetProxyForURL(GURL("http://foo/req2"), &proxy_info2
,
758 base::Bind(&CrashCallback
), &request2
,
759 mock_bindings
.CreateBindings());
761 host_resolver
.WaitUntilRequestIsReceived();
763 resolver
->CancelRequest(request1
);
764 resolver
->CancelRequest(request2
);
766 EXPECT_EQ(2, host_resolver
.num_cancelled_requests());
768 // After leaving this scope, the ProxyResolver is destroyed.
769 // This should not cause any problems, as the outstanding work
770 // should have been cancelled.
773 void CancelRequestAndPause(ProxyResolverV8Tracing
* resolver
,
774 ProxyResolver::RequestHandle request
) {
775 resolver
->CancelRequest(request
);
777 // Sleep for a little bit. This makes it more likely for the worker
778 // thread to have returned from its call, and serves as a regression
779 // test for http://crbug.com/173373.
780 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(30));
783 // In non-blocking mode, the worker thread actually does block for
784 // a short time to see if the result is in the DNS cache. Test
785 // cancellation while the worker thread is waiting on this event.
786 TEST_F(ProxyResolverV8TracingTest
, CancelWhileBlockedInNonBlockingDns
) {
787 BlockableHostResolver host_resolver
;
788 MockBindings
mock_bindings(&host_resolver
);
790 scoped_ptr
<ProxyResolverV8Tracing
> resolver
=
791 CreateResolver(mock_bindings
.CreateBindings(), "dns.js");
793 ProxyInfo proxy_info
;
794 ProxyResolver::RequestHandle request
;
796 resolver
->GetProxyForURL(GURL("http://foo/"), &proxy_info
,
797 base::Bind(&CrashCallback
), &request
,
798 mock_bindings
.CreateBindings());
800 host_resolver
.SetAction(
801 base::Bind(CancelRequestAndPause
, resolver
.get(), request
));
803 host_resolver
.WaitUntilRequestIsReceived();
806 // Cancel the request while there is a pending DNS request, however before
807 // the request is sent to the host resolver.
808 TEST_F(ProxyResolverV8TracingTest
, CancelWhileBlockedInNonBlockingDns2
) {
809 MockCachingHostResolver host_resolver
;
810 MockBindings
mock_bindings(&host_resolver
);
812 scoped_ptr
<ProxyResolverV8Tracing
> resolver
=
813 CreateResolver(mock_bindings
.CreateBindings(), "dns.js");
815 ProxyInfo proxy_info
;
816 ProxyResolver::RequestHandle request
;
818 resolver
->GetProxyForURL(GURL("http://foo/"), &proxy_info
,
819 base::Bind(&CrashCallback
), &request
,
820 mock_bindings
.CreateBindings());
822 // Wait a bit, so the DNS task has hopefully been posted. The test will
823 // work whatever the delay is here, but it is most useful if the delay
824 // is large enough to allow a task to be posted back.
825 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
826 resolver
->CancelRequest(request
);
828 EXPECT_EQ(0u, host_resolver
.num_resolve());
831 TEST_F(ProxyResolverV8TracingTest
,
832 CancelCreateResolverWhileOutstandingBlockingDns
) {
833 BlockableHostResolver host_resolver
;
834 MockBindings
mock_bindings(&host_resolver
);
836 scoped_ptr
<ProxyResolverV8TracingFactory
> factory(
837 ProxyResolverV8TracingFactory::Create());
838 scoped_ptr
<ProxyResolverV8Tracing
> resolver
;
839 scoped_ptr
<ProxyResolverFactory::Request
> request
;
840 factory
->CreateProxyResolverV8Tracing(
841 LoadScriptData("dns_during_init.js"), mock_bindings
.CreateBindings(),
842 &resolver
, base::Bind(&CrashCallback
), &request
);
844 host_resolver
.WaitUntilRequestIsReceived();
847 EXPECT_EQ(1, host_resolver
.num_cancelled_requests());
850 TEST_F(ProxyResolverV8TracingTest
, DeleteFactoryWhileOutstandingBlockingDns
) {
851 BlockableHostResolver host_resolver
;
852 MockBindings
mock_bindings(&host_resolver
);
854 scoped_ptr
<ProxyResolverV8Tracing
> resolver
;
855 scoped_ptr
<ProxyResolverFactory::Request
> request
;
857 scoped_ptr
<ProxyResolverV8TracingFactory
> factory(
858 ProxyResolverV8TracingFactory::Create());
860 factory
->CreateProxyResolverV8Tracing(
861 LoadScriptData("dns_during_init.js"), mock_bindings
.CreateBindings(),
862 &resolver
, base::Bind(&CrashCallback
), &request
);
863 host_resolver
.WaitUntilRequestIsReceived();
865 EXPECT_EQ(1, host_resolver
.num_cancelled_requests());
868 TEST_F(ProxyResolverV8TracingTest
, ErrorLoadingScript
) {
869 BlockableHostResolver host_resolver
;
870 MockBindings
mock_bindings(&host_resolver
);
872 scoped_ptr
<ProxyResolverV8TracingFactory
> factory(
873 ProxyResolverV8TracingFactory::Create());
874 scoped_ptr
<ProxyResolverV8Tracing
> resolver
;
875 scoped_ptr
<ProxyResolverFactory::Request
> request
;
876 TestCompletionCallback callback
;
877 factory
->CreateProxyResolverV8Tracing(
878 LoadScriptData("error_on_load.js"), mock_bindings
.CreateBindings(),
879 &resolver
, callback
.callback(), &request
);
881 EXPECT_EQ(ERR_PAC_SCRIPT_FAILED
, callback
.WaitForResult());
882 EXPECT_FALSE(resolver
);
885 // This tests that the execution of a PAC script is terminated when the DNS
886 // dependencies are missing. If the test fails, then it will hang.
887 TEST_F(ProxyResolverV8TracingTest
, Terminate
) {
888 MockCachingHostResolver host_resolver
;
889 MockBindings
mock_bindings(&host_resolver
);
891 host_resolver
.rules()->AddRule("host1", "182.111.0.222");
892 host_resolver
.rules()->AddRule("host2", "111.33.44.55");
894 scoped_ptr
<ProxyResolverV8Tracing
> resolver
=
895 CreateResolver(mock_bindings
.CreateBindings(), "terminate.js");
897 TestCompletionCallback callback
;
898 ProxyInfo proxy_info
;
900 resolver
->GetProxyForURL(GURL("http://foopy/req1"), &proxy_info
,
901 callback
.callback(), NULL
,
902 mock_bindings
.CreateBindings());
903 EXPECT_EQ(OK
, callback
.WaitForResult());
905 // The test does 2 DNS resolutions.
906 EXPECT_EQ(2u, host_resolver
.num_resolve());
908 EXPECT_EQ("foopy:3", proxy_info
.proxy_server().ToURI());
910 // No errors or alerts.
911 EXPECT_TRUE(mock_bindings
.GetErrors().empty());
912 EXPECT_TRUE(mock_bindings
.GetAlerts().empty());
915 // Tests that multiple instances of ProxyResolverV8Tracing can coexist and run
916 // correctly at the same time. This is relevant because at the moment (time
917 // this test was written) each ProxyResolverV8Tracing creates its own thread to
918 // run V8 on, however each thread is operating on the same v8::Isolate.
919 TEST_F(ProxyResolverV8TracingTest
, MultipleResolvers
) {
920 // ------------------------
922 // ------------------------
923 MockHostResolver host_resolver0
;
924 MockBindings
mock_bindings0(&host_resolver0
);
925 host_resolver0
.rules()->AddRuleForAddressFamily(
926 "host1", ADDRESS_FAMILY_IPV4
, "166.155.144.44");
927 host_resolver0
.rules()
928 ->AddIPLiteralRule("host1", "::1,192.168.1.1", std::string());
929 host_resolver0
.rules()->AddSimulatedFailure("host2");
930 host_resolver0
.rules()->AddRule("host3", "166.155.144.33");
931 host_resolver0
.rules()->AddRule("host5", "166.155.144.55");
932 host_resolver0
.rules()->AddSimulatedFailure("host6");
933 host_resolver0
.rules()->AddRuleForAddressFamily(
934 "*", ADDRESS_FAMILY_IPV4
, "122.133.144.155");
935 host_resolver0
.rules()->AddRule("*", "133.122.100.200");
936 scoped_ptr
<ProxyResolverV8Tracing
> resolver0
=
937 CreateResolver(mock_bindings0
.CreateBindings(), "dns.js");
939 // ------------------------
941 // ------------------------
942 scoped_ptr
<ProxyResolverV8Tracing
> resolver1
=
943 CreateResolver(mock_bindings0
.CreateBindings(), "dns.js");
945 // ------------------------
947 // ------------------------
948 scoped_ptr
<ProxyResolverV8Tracing
> resolver2
=
949 CreateResolver(mock_bindings0
.CreateBindings(), "simple.js");
951 // ------------------------
953 // ------------------------
954 MockHostResolver host_resolver3
;
955 MockBindings
mock_bindings3(&host_resolver3
);
956 host_resolver3
.rules()->AddRule("foo", "166.155.144.33");
957 scoped_ptr
<ProxyResolverV8Tracing
> resolver3
=
958 CreateResolver(mock_bindings3
.CreateBindings(), "simple_dns.js");
960 // ------------------------
961 // Queue up work for each resolver (which will be running in parallel).
962 // ------------------------
964 ProxyResolverV8Tracing
* resolver
[] = {
965 resolver0
.get(), resolver1
.get(), resolver2
.get(), resolver3
.get(),
968 const size_t kNumResolvers
= arraysize(resolver
);
969 const size_t kNumIterations
= 20;
970 const size_t kNumResults
= kNumResolvers
* kNumIterations
;
971 TestCompletionCallback callback
[kNumResults
];
972 ProxyInfo proxy_info
[kNumResults
];
974 for (size_t i
= 0; i
< kNumResults
; ++i
) {
975 size_t resolver_i
= i
% kNumResolvers
;
976 resolver
[resolver_i
]->GetProxyForURL(
977 GURL("http://foo/"), &proxy_info
[i
], callback
[i
].callback(), NULL
,
978 resolver_i
== 3 ? mock_bindings3
.CreateBindings()
979 : mock_bindings0
.CreateBindings());
982 // ------------------------
983 // Verify all of the results.
984 // ------------------------
986 const char* kExpectedForDnsJs
=
987 "122.133.144.155-" // myIpAddress()
988 "null-" // dnsResolve('')
989 "__1_192.168.1.1-" // dnsResolveEx('host1')
990 "null-" // dnsResolve('host2')
991 "166.155.144.33-" // dnsResolve('host3')
992 "122.133.144.155-" // myIpAddress()
993 "166.155.144.33-" // dnsResolve('host3')
994 "__1_192.168.1.1-" // dnsResolveEx('host1')
995 "122.133.144.155-" // myIpAddress()
996 "null-" // dnsResolve('host2')
997 "-" // dnsResolveEx('host6')
998 "133.122.100.200-" // myIpAddressEx()
999 "166.155.144.44" // dnsResolve('host1')
1002 for (size_t i
= 0; i
< kNumResults
; ++i
) {
1003 size_t resolver_i
= i
% kNumResolvers
;
1004 EXPECT_EQ(OK
, callback
[i
].WaitForResult());
1006 std::string proxy_uri
= proxy_info
[i
].proxy_server().ToURI();
1008 if (resolver_i
== 0 || resolver_i
== 1) {
1009 EXPECT_EQ(kExpectedForDnsJs
, proxy_uri
);
1010 } else if (resolver_i
== 2) {
1011 EXPECT_EQ("foo:99", proxy_uri
);
1012 } else if (resolver_i
== 3) {
1013 EXPECT_EQ("166.155.144.33:",
1014 proxy_uri
.substr(0, proxy_uri
.find(':') + 1));