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/file_util.h" 8 #include "base/message_loop/message_loop.h" 9 #include "base/path_service.h" 10 #include "base/stl_util.h" 11 #include "base/strings/string_util.h" 12 #include "base/strings/stringprintf.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "base/synchronization/waitable_event.h" 15 #include "base/threading/platform_thread.h" 16 #include "base/values.h" 17 #include "net/base/net_errors.h" 18 #include "net/base/net_log.h" 19 #include "net/base/net_log_unittest.h" 20 #include "net/base/test_completion_callback.h" 21 #include "net/dns/host_cache.h" 22 #include "net/dns/mock_host_resolver.h" 23 #include "net/proxy/proxy_info.h" 24 #include "net/proxy/proxy_resolver_error_observer.h" 25 #include "testing/gtest/include/gtest/gtest.h" 26 #include "url/gurl.h" 27 28 namespace net { 29 30 namespace { 31 32 class ProxyResolverV8TracingTest : public testing::Test { 33 public: 34 virtual void TearDown() OVERRIDE { 35 // Drain any pending messages, which may be left over from cancellation. 36 // This way they get reliably run as part of the current test, rather than 37 // spilling into the next test's execution. 38 base::MessageLoop::current()->RunUntilIdle(); 39 } 40 }; 41 42 scoped_refptr<ProxyResolverScriptData> LoadScriptData(const char* filename) { 43 base::FilePath path; 44 PathService::Get(base::DIR_SOURCE_ROOT, &path); 45 path = path.AppendASCII("net"); 46 path = path.AppendASCII("data"); 47 path = path.AppendASCII("proxy_resolver_v8_tracing_unittest"); 48 path = path.AppendASCII(filename); 49 50 // Try to read the file from disk. 51 std::string file_contents; 52 bool ok = file_util::ReadFileToString(path, &file_contents); 53 54 // If we can't load the file from disk, something is misconfigured. 55 EXPECT_TRUE(ok) << "Failed to read file: " << path.value(); 56 57 // Load the PAC script into the ProxyResolver. 58 return ProxyResolverScriptData::FromUTF8(file_contents); 59 } 60 61 void InitResolver(ProxyResolverV8Tracing* resolver, const char* filename) { 62 TestCompletionCallback callback; 63 int rv = 64 resolver->SetPacScript(LoadScriptData(filename), callback.callback()); 65 EXPECT_EQ(ERR_IO_PENDING, rv); 66 EXPECT_EQ(OK, callback.WaitForResult()); 67 } 68 69 class MockErrorObserver : public ProxyResolverErrorObserver { 70 public: 71 MockErrorObserver() : event_(true, false) {} 72 73 virtual void OnPACScriptError(int line_number, 74 const base::string16& error) OVERRIDE { 75 { 76 base::AutoLock l(lock_); 77 output += base::StringPrintf("Error: line %d: %s\n", line_number, 78 UTF16ToASCII(error).c_str()); 79 } 80 event_.Signal(); 81 } 82 83 std::string GetOutput() { 84 base::AutoLock l(lock_); 85 return output; 86 } 87 88 void WaitForOutput() { 89 event_.Wait(); 90 } 91 92 private: 93 base::Lock lock_; 94 std::string output; 95 96 base::WaitableEvent event_; 97 }; 98 99 TEST_F(ProxyResolverV8TracingTest, Simple) { 100 CapturingNetLog log; 101 CapturingBoundNetLog request_log; 102 MockCachingHostResolver host_resolver; 103 MockErrorObserver* error_observer = new MockErrorObserver; 104 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); 105 106 InitResolver(&resolver, "simple.js"); 107 108 TestCompletionCallback callback; 109 ProxyInfo proxy_info; 110 111 int rv = resolver.GetProxyForURL( 112 GURL("http://foo/"), &proxy_info, callback.callback(), 113 NULL, request_log.bound()); 114 115 EXPECT_EQ(ERR_IO_PENDING, rv); 116 EXPECT_EQ(OK, callback.WaitForResult()); 117 118 EXPECT_EQ("foo:99", proxy_info.proxy_server().ToURI()); 119 120 EXPECT_EQ(0u, host_resolver.num_resolve()); 121 122 // There were no errors. 123 EXPECT_EQ("", error_observer->GetOutput()); 124 125 // Check the NetLogs -- nothing was logged. 126 EXPECT_EQ(0u, log.GetSize()); 127 EXPECT_EQ(0u, request_log.GetSize()); 128 } 129 130 TEST_F(ProxyResolverV8TracingTest, JavascriptError) { 131 CapturingNetLog log; 132 CapturingBoundNetLog request_log; 133 MockCachingHostResolver host_resolver; 134 MockErrorObserver* error_observer = new MockErrorObserver; 135 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); 136 137 InitResolver(&resolver, "error.js"); 138 139 TestCompletionCallback callback; 140 ProxyInfo proxy_info; 141 142 int rv = resolver.GetProxyForURL( 143 GURL("http://throw-an-error/"), &proxy_info, callback.callback(), NULL, 144 request_log.bound()); 145 146 EXPECT_EQ(ERR_IO_PENDING, rv); 147 EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, callback.WaitForResult()); 148 149 EXPECT_EQ(0u, host_resolver.num_resolve()); 150 151 EXPECT_EQ("Error: line 5: Uncaught TypeError: Cannot call method 'split' " 152 "of null\n", error_observer->GetOutput()); 153 154 // Check the NetLogs -- there was 1 alert and 1 javascript error, and they 155 // were output to both the global log, and per-request log. 156 CapturingNetLog::CapturedEntryList entries_list[2]; 157 log.GetEntries(&entries_list[0]); 158 request_log.GetEntries(&entries_list[1]); 159 160 for (size_t list_i = 0; list_i < arraysize(entries_list); list_i++) { 161 const CapturingNetLog::CapturedEntryList& entries = entries_list[list_i]; 162 EXPECT_EQ(2u, entries.size()); 163 EXPECT_TRUE( 164 LogContainsEvent(entries, 0, NetLog::TYPE_PAC_JAVASCRIPT_ALERT, 165 NetLog::PHASE_NONE)); 166 EXPECT_TRUE( 167 LogContainsEvent(entries, 1, NetLog::TYPE_PAC_JAVASCRIPT_ERROR, 168 NetLog::PHASE_NONE)); 169 170 EXPECT_EQ("{\"message\":\"Prepare to DIE!\"}", entries[0].GetParamsJson()); 171 EXPECT_EQ("{\"line_number\":5,\"message\":\"Uncaught TypeError: Cannot " 172 "call method 'split' of null\"}", entries[1].GetParamsJson()); 173 } 174 } 175 176 TEST_F(ProxyResolverV8TracingTest, TooManyAlerts) { 177 CapturingNetLog log; 178 CapturingBoundNetLog request_log; 179 MockCachingHostResolver host_resolver; 180 MockErrorObserver* error_observer = new MockErrorObserver; 181 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); 182 183 InitResolver(&resolver, "too_many_alerts.js"); 184 185 TestCompletionCallback callback; 186 ProxyInfo proxy_info; 187 188 int rv = resolver.GetProxyForURL( 189 GURL("http://foo/"), 190 &proxy_info, 191 callback.callback(), 192 NULL, 193 request_log.bound()); 194 195 EXPECT_EQ(ERR_IO_PENDING, rv); 196 EXPECT_EQ(OK, callback.WaitForResult()); 197 198 // Iteration1 does a DNS resolve 199 // Iteration2 exceeds the alert buffer 200 // Iteration3 runs in blocking mode and completes 201 EXPECT_EQ("foo:3", proxy_info.proxy_server().ToURI()); 202 203 EXPECT_EQ(1u, host_resolver.num_resolve()); 204 205 // No errors. 206 EXPECT_EQ("", error_observer->GetOutput()); 207 208 // Check the NetLogs -- the script generated 50 alerts, which were mirrored 209 // to both the global and per-request logs. 210 CapturingNetLog::CapturedEntryList entries_list[2]; 211 log.GetEntries(&entries_list[0]); 212 request_log.GetEntries(&entries_list[1]); 213 214 for (size_t list_i = 0; list_i < arraysize(entries_list); list_i++) { 215 const CapturingNetLog::CapturedEntryList& entries = entries_list[list_i]; 216 EXPECT_EQ(50u, entries.size()); 217 for (size_t i = 0; i < entries.size(); ++i) { 218 ASSERT_TRUE( 219 LogContainsEvent(entries, i, NetLog::TYPE_PAC_JAVASCRIPT_ALERT, 220 NetLog::PHASE_NONE)); 221 } 222 } 223 } 224 225 // Verify that buffered alerts cannot grow unboundedly, even when the message is 226 // empty string. 227 TEST_F(ProxyResolverV8TracingTest, TooManyEmptyAlerts) { 228 CapturingNetLog log; 229 CapturingBoundNetLog request_log; 230 MockCachingHostResolver host_resolver; 231 MockErrorObserver* error_observer = new MockErrorObserver; 232 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); 233 234 InitResolver(&resolver, "too_many_empty_alerts.js"); 235 236 TestCompletionCallback callback; 237 ProxyInfo proxy_info; 238 239 int rv = resolver.GetProxyForURL( 240 GURL("http://foo/"), 241 &proxy_info, 242 callback.callback(), 243 NULL, 244 request_log.bound()); 245 246 EXPECT_EQ(ERR_IO_PENDING, rv); 247 EXPECT_EQ(OK, callback.WaitForResult()); 248 249 EXPECT_EQ("foo:3", proxy_info.proxy_server().ToURI()); 250 251 EXPECT_EQ(1u, host_resolver.num_resolve()); 252 253 // No errors. 254 EXPECT_EQ("", error_observer->GetOutput()); 255 256 // Check the NetLogs -- the script generated 50 alerts, which were mirrored 257 // to both the global and per-request logs. 258 CapturingNetLog::CapturedEntryList entries_list[2]; 259 log.GetEntries(&entries_list[0]); 260 request_log.GetEntries(&entries_list[1]); 261 262 for (size_t list_i = 0; list_i < arraysize(entries_list); list_i++) { 263 const CapturingNetLog::CapturedEntryList& entries = entries_list[list_i]; 264 EXPECT_EQ(1000u, entries.size()); 265 for (size_t i = 0; i < entries.size(); ++i) { 266 ASSERT_TRUE( 267 LogContainsEvent(entries, i, NetLog::TYPE_PAC_JAVASCRIPT_ALERT, 268 NetLog::PHASE_NONE)); 269 } 270 } 271 } 272 273 // This test runs a PAC script that issues a sequence of DNS resolves. The test 274 // verifies the final result, and that the underlying DNS resolver received 275 // the correct set of queries. 276 TEST_F(ProxyResolverV8TracingTest, Dns) { 277 CapturingNetLog log; 278 CapturingBoundNetLog request_log; 279 MockCachingHostResolver host_resolver; 280 MockErrorObserver* error_observer = new MockErrorObserver; 281 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); 282 283 host_resolver.rules()->AddRuleForAddressFamily( 284 "host1", ADDRESS_FAMILY_IPV4, "166.155.144.44"); 285 host_resolver.rules() 286 ->AddIPLiteralRule("host1", "::1,192.168.1.1", std::string()); 287 host_resolver.rules()->AddSimulatedFailure("host2"); 288 host_resolver.rules()->AddRule("host3", "166.155.144.33"); 289 host_resolver.rules()->AddRule("host5", "166.155.144.55"); 290 host_resolver.rules()->AddSimulatedFailure("host6"); 291 host_resolver.rules()->AddRuleForAddressFamily( 292 "*", ADDRESS_FAMILY_IPV4, "122.133.144.155"); 293 host_resolver.rules()->AddRule("*", "133.122.100.200"); 294 295 InitResolver(&resolver, "dns.js"); 296 297 TestCompletionCallback callback; 298 ProxyInfo proxy_info; 299 300 int rv = resolver.GetProxyForURL( 301 GURL("http://foo/"), 302 &proxy_info, 303 callback.callback(), 304 NULL, 305 request_log.bound()); 306 307 EXPECT_EQ(ERR_IO_PENDING, rv); 308 EXPECT_EQ(OK, callback.WaitForResult()); 309 310 // The test does 13 DNS resolution, however only 7 of them are unique. 311 EXPECT_EQ(7u, host_resolver.num_resolve()); 312 313 const char* kExpectedResult = 314 "122.133.144.155-" // myIpAddress() 315 "null-" // dnsResolve('') 316 "__1_192.168.1.1-" // dnsResolveEx('host1') 317 "null-" // dnsResolve('host2') 318 "166.155.144.33-" // dnsResolve('host3') 319 "122.133.144.155-" // myIpAddress() 320 "166.155.144.33-" // dnsResolve('host3') 321 "__1_192.168.1.1-" // dnsResolveEx('host1') 322 "122.133.144.155-" // myIpAddress() 323 "null-" // dnsResolve('host2') 324 "-" // dnsResolveEx('host6') 325 "133.122.100.200-" // myIpAddressEx() 326 "166.155.144.44" // dnsResolve('host1') 327 ":99"; 328 329 EXPECT_EQ(kExpectedResult, proxy_info.proxy_server().ToURI()); 330 331 // No errors. 332 EXPECT_EQ("", error_observer->GetOutput()); 333 334 // Check the NetLogs -- the script generated 1 alert, mirrored to both 335 // the per-request and global logs. 336 CapturingNetLog::CapturedEntryList entries_list[2]; 337 log.GetEntries(&entries_list[0]); 338 request_log.GetEntries(&entries_list[1]); 339 340 for (size_t list_i = 0; list_i < arraysize(entries_list); list_i++) { 341 const CapturingNetLog::CapturedEntryList& entries = entries_list[list_i]; 342 EXPECT_EQ(1u, entries.size()); 343 EXPECT_TRUE( 344 LogContainsEvent(entries, 0, NetLog::TYPE_PAC_JAVASCRIPT_ALERT, 345 NetLog::PHASE_NONE)); 346 EXPECT_EQ("{\"message\":\"iteration: 7\"}", entries[0].GetParamsJson()); 347 } 348 } 349 350 // This test runs a PAC script that does "myIpAddress()" followed by 351 // "dnsResolve()". This requires 2 restarts. However once the HostResolver's 352 // cache is warmed, subsequent calls should take 0 restarts. 353 TEST_F(ProxyResolverV8TracingTest, DnsChecksCache) { 354 CapturingNetLog log; 355 CapturingBoundNetLog request_log; 356 MockCachingHostResolver host_resolver; 357 MockErrorObserver* error_observer = new MockErrorObserver; 358 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); 359 360 host_resolver.rules()->AddRule("foopy", "166.155.144.11"); 361 host_resolver.rules()->AddRule("*", "122.133.144.155"); 362 363 InitResolver(&resolver, "simple_dns.js"); 364 365 TestCompletionCallback callback1; 366 TestCompletionCallback callback2; 367 ProxyInfo proxy_info; 368 369 int rv = resolver.GetProxyForURL( 370 GURL("http://foopy/req1"), 371 &proxy_info, 372 callback1.callback(), 373 NULL, 374 request_log.bound()); 375 376 EXPECT_EQ(ERR_IO_PENDING, rv); 377 EXPECT_EQ(OK, callback1.WaitForResult()); 378 379 // The test does 2 DNS resolutions. 380 EXPECT_EQ(2u, host_resolver.num_resolve()); 381 382 // The first request took 2 restarts, hence on g_iteration=3. 383 EXPECT_EQ("166.155.144.11:3", proxy_info.proxy_server().ToURI()); 384 385 rv = resolver.GetProxyForURL( 386 GURL("http://foopy/req2"), 387 &proxy_info, 388 callback2.callback(), 389 NULL, 390 request_log.bound()); 391 392 EXPECT_EQ(ERR_IO_PENDING, rv); 393 EXPECT_EQ(OK, callback2.WaitForResult()); 394 395 EXPECT_EQ(4u, host_resolver.num_resolve()); 396 397 // This time no restarts were required, so g_iteration incremented by 1. 398 EXPECT_EQ("166.155.144.11:4", proxy_info.proxy_server().ToURI()); 399 400 // No errors. 401 EXPECT_EQ("", error_observer->GetOutput()); 402 403 EXPECT_EQ(0u, log.GetSize()); 404 EXPECT_EQ(0u, request_log.GetSize()); 405 } 406 407 // This test runs a weird PAC script that was designed to defeat the DNS tracing 408 // optimization. The proxy resolver should detect the inconsistency and 409 // fall-back to synchronous mode execution. 410 TEST_F(ProxyResolverV8TracingTest, FallBackToSynchronous1) { 411 CapturingNetLog log; 412 CapturingBoundNetLog request_log; 413 MockCachingHostResolver host_resolver; 414 MockErrorObserver* error_observer = new MockErrorObserver; 415 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); 416 417 host_resolver.rules()->AddRule("host1", "166.155.144.11"); 418 host_resolver.rules()->AddRule("crazy4", "133.199.111.4"); 419 host_resolver.rules()->AddRule("*", "122.133.144.155"); 420 421 InitResolver(&resolver, "global_sideffects1.js"); 422 423 TestCompletionCallback callback; 424 ProxyInfo proxy_info; 425 426 int rv = resolver.GetProxyForURL( 427 GURL("http://foo/"), &proxy_info, callback.callback(), NULL, 428 request_log.bound()); 429 EXPECT_EQ(ERR_IO_PENDING, rv); 430 EXPECT_EQ(OK, callback.WaitForResult()); 431 432 // The script itself only does 2 DNS resolves per execution, however it 433 // constructs the hostname using a global counter which changes on each 434 // invocation. 435 EXPECT_EQ(3u, host_resolver.num_resolve()); 436 437 EXPECT_EQ("166.155.144.11-133.199.111.4:100", 438 proxy_info.proxy_server().ToURI()); 439 440 // No errors. 441 EXPECT_EQ("", error_observer->GetOutput()); 442 443 // Check the NetLogs -- the script generated 1 alert, mirrored to both 444 // the per-request and global logs. 445 CapturingNetLog::CapturedEntryList entries_list[2]; 446 log.GetEntries(&entries_list[0]); 447 request_log.GetEntries(&entries_list[1]); 448 449 for (size_t list_i = 0; list_i < arraysize(entries_list); list_i++) { 450 const CapturingNetLog::CapturedEntryList& entries = entries_list[list_i]; 451 EXPECT_EQ(1u, entries.size()); 452 EXPECT_TRUE( 453 LogContainsEvent(entries, 0, NetLog::TYPE_PAC_JAVASCRIPT_ALERT, 454 NetLog::PHASE_NONE)); 455 EXPECT_EQ("{\"message\":\"iteration: 4\"}", entries[0].GetParamsJson()); 456 } 457 } 458 459 // This test runs a weird PAC script that was designed to defeat the DNS tracing 460 // optimization. The proxy resolver should detect the inconsistency and 461 // fall-back to synchronous mode execution. 462 TEST_F(ProxyResolverV8TracingTest, FallBackToSynchronous2) { 463 CapturingNetLog log; 464 CapturingBoundNetLog request_log; 465 MockCachingHostResolver host_resolver; 466 MockErrorObserver* error_observer = new MockErrorObserver; 467 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); 468 469 host_resolver.rules()->AddRule("host1", "166.155.144.11"); 470 host_resolver.rules()->AddRule("host2", "166.155.144.22"); 471 host_resolver.rules()->AddRule("host3", "166.155.144.33"); 472 host_resolver.rules()->AddRule("host4", "166.155.144.44"); 473 host_resolver.rules()->AddRule("*", "122.133.144.155"); 474 475 InitResolver(&resolver, "global_sideffects2.js"); 476 477 TestCompletionCallback callback; 478 ProxyInfo proxy_info; 479 480 int rv = resolver.GetProxyForURL( 481 GURL("http://foo/"), &proxy_info, callback.callback(), NULL, 482 request_log.bound()); 483 EXPECT_EQ(ERR_IO_PENDING, rv); 484 EXPECT_EQ(OK, callback.WaitForResult()); 485 486 EXPECT_EQ(3u, host_resolver.num_resolve()); 487 488 EXPECT_EQ("166.155.144.44:100", proxy_info.proxy_server().ToURI()); 489 490 // No errors. 491 EXPECT_EQ("", error_observer->GetOutput()); 492 493 // Check the NetLogs -- nothing was logged. 494 EXPECT_EQ(0u, log.GetSize()); 495 EXPECT_EQ(0u, request_log.GetSize()); 496 } 497 498 // This test runs a weird PAC script that yields a never ending sequence 499 // of DNS resolves when restarting. Running it will hit the maximum 500 // DNS resolves per request limit (20) after which every DNS resolve will 501 // fail. 502 TEST_F(ProxyResolverV8TracingTest, InfiniteDNSSequence) { 503 CapturingNetLog log; 504 CapturingBoundNetLog request_log; 505 MockCachingHostResolver host_resolver; 506 MockErrorObserver* error_observer = new MockErrorObserver; 507 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); 508 509 host_resolver.rules()->AddRule("host*", "166.155.144.11"); 510 host_resolver.rules()->AddRule("*", "122.133.144.155"); 511 512 InitResolver(&resolver, "global_sideffects3.js"); 513 514 TestCompletionCallback callback; 515 ProxyInfo proxy_info; 516 517 int rv = resolver.GetProxyForURL( 518 GURL("http://foo/"), &proxy_info, callback.callback(), NULL, 519 request_log.bound()); 520 EXPECT_EQ(ERR_IO_PENDING, rv); 521 EXPECT_EQ(OK, callback.WaitForResult()); 522 523 EXPECT_EQ(20u, host_resolver.num_resolve()); 524 525 EXPECT_EQ( 526 "166.155.144.11-166.155.144.11-166.155.144.11-166.155.144.11-" 527 "166.155.144.11-166.155.144.11-166.155.144.11-166.155.144.11-" 528 "166.155.144.11-166.155.144.11-166.155.144.11-166.155.144.11-" 529 "166.155.144.11-166.155.144.11-166.155.144.11-166.155.144.11-" 530 "166.155.144.11-166.155.144.11-166.155.144.11-166.155.144.11-" 531 "null:21", proxy_info.proxy_server().ToURI()); 532 533 // No errors. 534 EXPECT_EQ("", error_observer->GetOutput()); 535 536 // Check the NetLogs -- 1 alert was logged. 537 EXPECT_EQ(1u, log.GetSize()); 538 EXPECT_EQ(1u, request_log.GetSize()); 539 } 540 541 // This test runs a weird PAC script that yields a never ending sequence 542 // of DNS resolves when restarting. Running it will hit the maximum 543 // DNS resolves per request limit (20) after which every DNS resolve will 544 // fail. 545 TEST_F(ProxyResolverV8TracingTest, InfiniteDNSSequence2) { 546 CapturingNetLog log; 547 CapturingBoundNetLog request_log; 548 MockCachingHostResolver host_resolver; 549 MockErrorObserver* error_observer = new MockErrorObserver; 550 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); 551 552 host_resolver.rules()->AddRule("host*", "166.155.144.11"); 553 host_resolver.rules()->AddRule("*", "122.133.144.155"); 554 555 InitResolver(&resolver, "global_sideffects4.js"); 556 557 TestCompletionCallback callback; 558 ProxyInfo proxy_info; 559 560 int rv = resolver.GetProxyForURL( 561 GURL("http://foo/"), &proxy_info, callback.callback(), NULL, 562 request_log.bound()); 563 EXPECT_EQ(ERR_IO_PENDING, rv); 564 EXPECT_EQ(OK, callback.WaitForResult()); 565 566 EXPECT_EQ(20u, host_resolver.num_resolve()); 567 568 EXPECT_EQ("null21:34", proxy_info.proxy_server().ToURI()); 569 570 // No errors. 571 EXPECT_EQ("", error_observer->GetOutput()); 572 573 // Check the NetLogs -- 1 alert was logged. 574 EXPECT_EQ(1u, log.GetSize()); 575 EXPECT_EQ(1u, request_log.GetSize()); 576 } 577 578 void DnsDuringInitHelper(bool synchronous_host_resolver) { 579 CapturingNetLog log; 580 CapturingBoundNetLog request_log; 581 MockCachingHostResolver host_resolver; 582 host_resolver.set_synchronous_mode(synchronous_host_resolver); 583 MockErrorObserver* error_observer = new MockErrorObserver; 584 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); 585 586 host_resolver.rules()->AddRule("host1", "91.13.12.1"); 587 host_resolver.rules()->AddRule("host2", "91.13.12.2"); 588 589 InitResolver(&resolver, "dns_during_init.js"); 590 591 // Initialization did 2 dnsResolves. 592 EXPECT_EQ(2u, host_resolver.num_resolve()); 593 594 host_resolver.rules()->ClearRules(); 595 host_resolver.GetHostCache()->clear(); 596 597 host_resolver.rules()->AddRule("host1", "145.88.13.3"); 598 host_resolver.rules()->AddRule("host2", "137.89.8.45"); 599 600 TestCompletionCallback callback; 601 ProxyInfo proxy_info; 602 603 int rv = resolver.GetProxyForURL( 604 GURL("http://foo/"), &proxy_info, callback.callback(), NULL, 605 request_log.bound()); 606 EXPECT_EQ(ERR_IO_PENDING, rv); 607 EXPECT_EQ(OK, callback.WaitForResult()); 608 609 // Fetched host1 and host2 again, since the ones done during initialization 610 // should not have been cached. 611 EXPECT_EQ(4u, host_resolver.num_resolve()); 612 613 EXPECT_EQ("91.13.12.1-91.13.12.2-145.88.13.3-137.89.8.45:99", 614 proxy_info.proxy_server().ToURI()); 615 616 // Check the NetLogs -- the script generated 2 alerts during initialization. 617 EXPECT_EQ(0u, request_log.GetSize()); 618 CapturingNetLog::CapturedEntryList entries; 619 log.GetEntries(&entries); 620 621 ASSERT_EQ(2u, entries.size()); 622 EXPECT_TRUE( 623 LogContainsEvent(entries, 0, NetLog::TYPE_PAC_JAVASCRIPT_ALERT, 624 NetLog::PHASE_NONE)); 625 EXPECT_TRUE( 626 LogContainsEvent(entries, 1, NetLog::TYPE_PAC_JAVASCRIPT_ALERT, 627 NetLog::PHASE_NONE)); 628 629 EXPECT_EQ("{\"message\":\"Watsup\"}", entries[0].GetParamsJson()); 630 EXPECT_EQ("{\"message\":\"Watsup2\"}", entries[1].GetParamsJson()); 631 } 632 633 // Tests a PAC script which does DNS resolves during initialization. 634 TEST_F(ProxyResolverV8TracingTest, DnsDuringInit) { 635 // Test with both both a host resolver that always completes asynchronously, 636 // and then again with one that completes synchronously. 637 DnsDuringInitHelper(false); 638 DnsDuringInitHelper(true); 639 } 640 641 void CrashCallback(int) { 642 // Be extra sure that if the callback ever gets invoked, the test will fail. 643 CHECK(false); 644 } 645 646 // Start some requests, cancel them all, and then destroy the resolver. 647 // Note the execution order for this test can vary. Since multiple 648 // threads are involved, the cancellation may be received a different 649 // times. 650 TEST_F(ProxyResolverV8TracingTest, CancelAll) { 651 MockCachingHostResolver host_resolver; 652 MockErrorObserver* error_observer = new MockErrorObserver; 653 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL); 654 655 host_resolver.rules()->AddSimulatedFailure("*"); 656 657 InitResolver(&resolver, "dns.js"); 658 659 const size_t kNumRequests = 5; 660 ProxyInfo proxy_info[kNumRequests]; 661 ProxyResolver::RequestHandle request[kNumRequests]; 662 663 for (size_t i = 0; i < kNumRequests; ++i) { 664 int rv = resolver.GetProxyForURL( 665 GURL("http://foo/"), &proxy_info[i], 666 base::Bind(&CrashCallback), &request[i], BoundNetLog()); 667 EXPECT_EQ(ERR_IO_PENDING, rv); 668 } 669 670 for (size_t i = 0; i < kNumRequests; ++i) { 671 resolver.CancelRequest(request[i]); 672 } 673 } 674 675 // Note the execution order for this test can vary. Since multiple 676 // threads are involved, the cancellation may be received a different 677 // times. 678 TEST_F(ProxyResolverV8TracingTest, CancelSome) { 679 MockCachingHostResolver host_resolver; 680 MockErrorObserver* error_observer = new MockErrorObserver; 681 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL); 682 683 host_resolver.rules()->AddSimulatedFailure("*"); 684 685 InitResolver(&resolver, "dns.js"); 686 687 ProxyInfo proxy_info1; 688 ProxyInfo proxy_info2; 689 ProxyResolver::RequestHandle request1; 690 ProxyResolver::RequestHandle request2; 691 TestCompletionCallback callback; 692 693 int rv = resolver.GetProxyForURL( 694 GURL("http://foo/"), &proxy_info1, 695 base::Bind(&CrashCallback), &request1, BoundNetLog()); 696 EXPECT_EQ(ERR_IO_PENDING, rv); 697 698 rv = resolver.GetProxyForURL( 699 GURL("http://foo/"), &proxy_info2, 700 callback.callback(), &request2, BoundNetLog()); 701 EXPECT_EQ(ERR_IO_PENDING, rv); 702 703 resolver.CancelRequest(request1); 704 705 EXPECT_EQ(OK, callback.WaitForResult()); 706 } 707 708 // Cancel a request after it has finished running on the worker thread, and has 709 // posted a task the completion task back to origin thread. 710 TEST_F(ProxyResolverV8TracingTest, CancelWhilePendingCompletionTask) { 711 MockCachingHostResolver host_resolver; 712 MockErrorObserver* error_observer = new MockErrorObserver; 713 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL); 714 715 host_resolver.rules()->AddSimulatedFailure("*"); 716 717 InitResolver(&resolver, "error.js"); 718 719 ProxyInfo proxy_info1; 720 ProxyInfo proxy_info2; 721 ProxyInfo proxy_info3; 722 ProxyResolver::RequestHandle request1; 723 ProxyResolver::RequestHandle request2; 724 ProxyResolver::RequestHandle request3; 725 TestCompletionCallback callback; 726 727 int rv = resolver.GetProxyForURL( 728 GURL("http://foo/"), &proxy_info1, 729 base::Bind(&CrashCallback), &request1, BoundNetLog()); 730 EXPECT_EQ(ERR_IO_PENDING, rv); 731 732 rv = resolver.GetProxyForURL( 733 GURL("http://throw-an-error/"), &proxy_info2, 734 callback.callback(), &request2, BoundNetLog()); 735 EXPECT_EQ(ERR_IO_PENDING, rv); 736 737 // Wait until the first request has finished running on the worker thread. 738 // (The second request will output an error). 739 error_observer->WaitForOutput(); 740 741 // Cancel the first request, while it has a pending completion task on 742 // the origin thread. 743 resolver.CancelRequest(request1); 744 745 EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, callback.WaitForResult()); 746 747 // Start another request, to make sure it is able to complete. 748 rv = resolver.GetProxyForURL( 749 GURL("http://i-have-no-idea-what-im-doing/"), &proxy_info3, 750 callback.callback(), &request3, BoundNetLog()); 751 EXPECT_EQ(ERR_IO_PENDING, rv); 752 753 EXPECT_EQ(OK, callback.WaitForResult()); 754 755 EXPECT_EQ("i-approve-this-message:42", 756 proxy_info3.proxy_server().ToURI()); 757 } 758 759 // This implementation of HostResolver allows blocking until a resolve request 760 // has been received. The resolve requests it receives will never be completed. 761 class BlockableHostResolver : public HostResolver { 762 public: 763 BlockableHostResolver() 764 : num_cancelled_requests_(0), waiting_for_resolve_(false) {} 765 766 virtual int Resolve(const RequestInfo& info, 767 AddressList* addresses, 768 const CompletionCallback& callback, 769 RequestHandle* out_req, 770 const BoundNetLog& net_log) OVERRIDE { 771 EXPECT_FALSE(callback.is_null()); 772 EXPECT_TRUE(out_req); 773 774 if (!action_.is_null()) 775 action_.Run(); 776 777 // Indicate to the caller that a request was received. 778 EXPECT_TRUE(waiting_for_resolve_); 779 base::MessageLoop::current()->Quit(); 780 781 // This line is intentionally after action_.Run(), since one of the 782 // tests does a cancellation inside of Resolve(), and it is more 783 // interesting if *out_req hasn't been written yet at that point. 784 *out_req = reinterpret_cast<RequestHandle*>(1); // Magic value. 785 786 // Return ERR_IO_PENDING as this request will NEVER be completed. 787 // Expectation is for the caller to later cancel the request. 788 return ERR_IO_PENDING; 789 } 790 791 virtual int ResolveFromCache(const RequestInfo& info, 792 AddressList* addresses, 793 const BoundNetLog& net_log) OVERRIDE { 794 NOTREACHED(); 795 return ERR_DNS_CACHE_MISS; 796 } 797 798 virtual void CancelRequest(RequestHandle req) OVERRIDE { 799 EXPECT_EQ(reinterpret_cast<RequestHandle*>(1), req); 800 num_cancelled_requests_++; 801 } 802 803 void SetAction(const base::Callback<void(void)>& action) { 804 action_ = action; 805 } 806 807 // Waits until Resolve() has been called. 808 void WaitUntilRequestIsReceived() { 809 waiting_for_resolve_ = true; 810 base::MessageLoop::current()->Run(); 811 DCHECK(waiting_for_resolve_); 812 waiting_for_resolve_ = false; 813 } 814 815 int num_cancelled_requests() const { 816 return num_cancelled_requests_; 817 } 818 819 private: 820 int num_cancelled_requests_; 821 bool waiting_for_resolve_; 822 base::Callback<void(void)> action_; 823 }; 824 825 // This cancellation test exercises a more predictable cancellation codepath -- 826 // when the request has an outstanding DNS request in flight. 827 TEST_F(ProxyResolverV8TracingTest, CancelWhileOutstandingNonBlockingDns) { 828 BlockableHostResolver host_resolver; 829 MockErrorObserver* error_observer = new MockErrorObserver; 830 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL); 831 832 InitResolver(&resolver, "dns.js"); 833 834 ProxyInfo proxy_info1; 835 ProxyInfo proxy_info2; 836 ProxyResolver::RequestHandle request1; 837 ProxyResolver::RequestHandle request2; 838 839 int rv = resolver.GetProxyForURL( 840 GURL("http://foo/req1"), &proxy_info1, 841 base::Bind(&CrashCallback), &request1, BoundNetLog()); 842 843 EXPECT_EQ(ERR_IO_PENDING, rv); 844 845 host_resolver.WaitUntilRequestIsReceived(); 846 847 rv = resolver.GetProxyForURL( 848 GURL("http://foo/req2"), &proxy_info2, 849 base::Bind(&CrashCallback), &request2, BoundNetLog()); 850 851 EXPECT_EQ(ERR_IO_PENDING, rv); 852 853 host_resolver.WaitUntilRequestIsReceived(); 854 855 resolver.CancelRequest(request1); 856 resolver.CancelRequest(request2); 857 858 EXPECT_EQ(2, host_resolver.num_cancelled_requests()); 859 860 // After leaving this scope, the ProxyResolver is destroyed. 861 // This should not cause any problems, as the outstanding work 862 // should have been cancelled. 863 } 864 865 void CancelRequestAndPause(ProxyResolverV8Tracing* resolver, 866 ProxyResolver::RequestHandle request) { 867 resolver->CancelRequest(request); 868 869 // Sleep for a little bit. This makes it more likely for the worker 870 // thread to have returned from its call, and serves as a regression 871 // test for http://crbug.com/173373. 872 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(30)); 873 } 874 875 // In non-blocking mode, the worker thread actually does block for 876 // a short time to see if the result is in the DNS cache. Test 877 // cancellation while the worker thread is waiting on this event. 878 TEST_F(ProxyResolverV8TracingTest, CancelWhileBlockedInNonBlockingDns) { 879 BlockableHostResolver host_resolver; 880 MockErrorObserver* error_observer = new MockErrorObserver; 881 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL); 882 883 InitResolver(&resolver, "dns.js"); 884 885 ProxyInfo proxy_info; 886 ProxyResolver::RequestHandle request; 887 888 int rv = resolver.GetProxyForURL( 889 GURL("http://foo/"), &proxy_info, 890 base::Bind(&CrashCallback), &request, BoundNetLog()); 891 892 EXPECT_EQ(ERR_IO_PENDING, rv); 893 894 host_resolver.SetAction( 895 base::Bind(CancelRequestAndPause, &resolver, request)); 896 897 host_resolver.WaitUntilRequestIsReceived(); 898 899 // At this point the host resolver ran Resolve(), and should have cancelled 900 // the request. 901 902 EXPECT_EQ(1, host_resolver.num_cancelled_requests()); 903 } 904 905 // Cancel the request while there is a pending DNS request, however before 906 // the request is sent to the host resolver. 907 TEST_F(ProxyResolverV8TracingTest, CancelWhileBlockedInNonBlockingDns2) { 908 MockCachingHostResolver host_resolver; 909 MockErrorObserver* error_observer = new MockErrorObserver; 910 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL); 911 912 InitResolver(&resolver, "dns.js"); 913 914 ProxyInfo proxy_info; 915 ProxyResolver::RequestHandle request; 916 917 int rv = resolver.GetProxyForURL( 918 GURL("http://foo/"), &proxy_info, 919 base::Bind(&CrashCallback), &request, BoundNetLog()); 920 921 EXPECT_EQ(ERR_IO_PENDING, rv); 922 923 // Wait a bit, so the DNS task has hopefully been posted. The test will 924 // work whatever the delay is here, but it is most useful if the delay 925 // is large enough to allow a task to be posted back. 926 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10)); 927 resolver.CancelRequest(request); 928 929 EXPECT_EQ(0u, host_resolver.num_resolve()); 930 } 931 932 TEST_F(ProxyResolverV8TracingTest, CancelSetPacWhileOutstandingBlockingDns) { 933 BlockableHostResolver host_resolver; 934 MockErrorObserver* error_observer = new MockErrorObserver; 935 936 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL); 937 938 int rv = 939 resolver.SetPacScript(LoadScriptData("dns_during_init.js"), 940 base::Bind(&CrashCallback)); 941 EXPECT_EQ(ERR_IO_PENDING, rv); 942 943 host_resolver.WaitUntilRequestIsReceived(); 944 945 resolver.CancelSetPacScript(); 946 EXPECT_EQ(1, host_resolver.num_cancelled_requests()); 947 } 948 949 // This tests that the execution of a PAC script is terminated when the DNS 950 // dependencies are missing. If the test fails, then it will hang. 951 TEST_F(ProxyResolverV8TracingTest, Terminate) { 952 CapturingNetLog log; 953 CapturingBoundNetLog request_log; 954 MockCachingHostResolver host_resolver; 955 MockErrorObserver* error_observer = new MockErrorObserver; 956 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); 957 958 host_resolver.rules()->AddRule("host1", "182.111.0.222"); 959 host_resolver.rules()->AddRule("host2", "111.33.44.55"); 960 961 InitResolver(&resolver, "terminate.js"); 962 963 TestCompletionCallback callback; 964 ProxyInfo proxy_info; 965 966 int rv = resolver.GetProxyForURL( 967 GURL("http://foopy/req1"), 968 &proxy_info, 969 callback.callback(), 970 NULL, 971 request_log.bound()); 972 973 EXPECT_EQ(ERR_IO_PENDING, rv); 974 EXPECT_EQ(OK, callback.WaitForResult()); 975 976 // The test does 2 DNS resolutions. 977 EXPECT_EQ(2u, host_resolver.num_resolve()); 978 979 EXPECT_EQ("foopy:3", proxy_info.proxy_server().ToURI()); 980 981 // No errors. 982 EXPECT_EQ("", error_observer->GetOutput()); 983 984 EXPECT_EQ(0u, log.GetSize()); 985 EXPECT_EQ(0u, request_log.GetSize()); 986 } 987 988 // Tests that multiple instances of ProxyResolverV8Tracing can coexist and run 989 // correctly at the same time. This is relevant because at the moment (time 990 // this test was written) each ProxyResolverV8Tracing creates its own thread to 991 // run V8 on, however each thread is operating on the same v8::Isolate. 992 TEST_F(ProxyResolverV8TracingTest, MultipleResolvers) { 993 // ------------------------ 994 // Setup resolver0 995 // ------------------------ 996 MockHostResolver host_resolver0; 997 host_resolver0.rules()->AddRuleForAddressFamily( 998 "host1", ADDRESS_FAMILY_IPV4, "166.155.144.44"); 999 host_resolver0.rules() 1000 ->AddIPLiteralRule("host1", "::1,192.168.1.1", std::string()); 1001 host_resolver0.rules()->AddSimulatedFailure("host2"); 1002 host_resolver0.rules()->AddRule("host3", "166.155.144.33"); 1003 host_resolver0.rules()->AddRule("host5", "166.155.144.55"); 1004 host_resolver0.rules()->AddSimulatedFailure("host6"); 1005 host_resolver0.rules()->AddRuleForAddressFamily( 1006 "*", ADDRESS_FAMILY_IPV4, "122.133.144.155"); 1007 host_resolver0.rules()->AddRule("*", "133.122.100.200"); 1008 ProxyResolverV8Tracing resolver0( 1009 &host_resolver0, new MockErrorObserver, NULL); 1010 InitResolver(&resolver0, "dns.js"); 1011 1012 // ------------------------ 1013 // Setup resolver1 1014 // ------------------------ 1015 ProxyResolverV8Tracing resolver1( 1016 &host_resolver0, new MockErrorObserver, NULL); 1017 InitResolver(&resolver1, "dns.js"); 1018 1019 // ------------------------ 1020 // Setup resolver2 1021 // ------------------------ 1022 ProxyResolverV8Tracing resolver2( 1023 &host_resolver0, new MockErrorObserver, NULL); 1024 InitResolver(&resolver2, "simple.js"); 1025 1026 // ------------------------ 1027 // Setup resolver3 1028 // ------------------------ 1029 MockHostResolver host_resolver3; 1030 host_resolver3.rules()->AddRule("foo", "166.155.144.33"); 1031 ProxyResolverV8Tracing resolver3( 1032 &host_resolver3, new MockErrorObserver, NULL); 1033 InitResolver(&resolver3, "simple_dns.js"); 1034 1035 // ------------------------ 1036 // Queue up work for each resolver (which will be running in parallel). 1037 // ------------------------ 1038 1039 ProxyResolverV8Tracing* resolver[] = { 1040 &resolver0, &resolver1, &resolver2, &resolver3, 1041 }; 1042 1043 const size_t kNumResolvers = arraysize(resolver); 1044 const size_t kNumIterations = 20; 1045 const size_t kNumResults = kNumResolvers * kNumIterations; 1046 TestCompletionCallback callback[kNumResults]; 1047 ProxyInfo proxy_info[kNumResults]; 1048 1049 for (size_t i = 0; i < kNumResults; ++i) { 1050 size_t resolver_i = i % kNumResolvers; 1051 int rv = resolver[resolver_i]->GetProxyForURL( 1052 GURL("http://foo/"), &proxy_info[i], callback[i].callback(), NULL, 1053 BoundNetLog()); 1054 EXPECT_EQ(ERR_IO_PENDING, rv); 1055 } 1056 1057 // ------------------------ 1058 // Verify all of the results. 1059 // ------------------------ 1060 1061 const char* kExpectedForDnsJs = 1062 "122.133.144.155-" // myIpAddress() 1063 "null-" // dnsResolve('') 1064 "__1_192.168.1.1-" // dnsResolveEx('host1') 1065 "null-" // dnsResolve('host2') 1066 "166.155.144.33-" // dnsResolve('host3') 1067 "122.133.144.155-" // myIpAddress() 1068 "166.155.144.33-" // dnsResolve('host3') 1069 "__1_192.168.1.1-" // dnsResolveEx('host1') 1070 "122.133.144.155-" // myIpAddress() 1071 "null-" // dnsResolve('host2') 1072 "-" // dnsResolveEx('host6') 1073 "133.122.100.200-" // myIpAddressEx() 1074 "166.155.144.44" // dnsResolve('host1') 1075 ":99"; 1076 1077 for (size_t i = 0; i < kNumResults; ++i) { 1078 size_t resolver_i = i % kNumResolvers; 1079 EXPECT_EQ(OK, callback[i].WaitForResult()); 1080 1081 std::string proxy_uri = proxy_info[i].proxy_server().ToURI(); 1082 1083 if (resolver_i == 0 || resolver_i == 1) { 1084 EXPECT_EQ(kExpectedForDnsJs, proxy_uri); 1085 } else if (resolver_i == 2) { 1086 EXPECT_EQ("foo:99", proxy_uri); 1087 } else if (resolver_i == 3) { 1088 EXPECT_EQ("166.155.144.33:", 1089 proxy_uri.substr(0, proxy_uri.find(':') + 1)); 1090 } else { 1091 NOTREACHED(); 1092 } 1093 } 1094 } 1095 1096 } // namespace 1097 1098 } // namespace net 1099