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 = base::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 base::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 read property '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 "read property '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 RequestPriority priority, 768 AddressList* addresses, 769 const CompletionCallback& callback, 770 RequestHandle* out_req, 771 const BoundNetLog& net_log) OVERRIDE { 772 EXPECT_FALSE(callback.is_null()); 773 EXPECT_TRUE(out_req); 774 775 if (!action_.is_null()) 776 action_.Run(); 777 778 // Indicate to the caller that a request was received. 779 EXPECT_TRUE(waiting_for_resolve_); 780 base::MessageLoop::current()->Quit(); 781 782 // This line is intentionally after action_.Run(), since one of the 783 // tests does a cancellation inside of Resolve(), and it is more 784 // interesting if *out_req hasn't been written yet at that point. 785 *out_req = reinterpret_cast<RequestHandle*>(1); // Magic value. 786 787 // Return ERR_IO_PENDING as this request will NEVER be completed. 788 // Expectation is for the caller to later cancel the request. 789 return ERR_IO_PENDING; 790 } 791 792 virtual int ResolveFromCache(const RequestInfo& info, 793 AddressList* addresses, 794 const BoundNetLog& net_log) OVERRIDE { 795 NOTREACHED(); 796 return ERR_DNS_CACHE_MISS; 797 } 798 799 virtual void CancelRequest(RequestHandle req) OVERRIDE { 800 EXPECT_EQ(reinterpret_cast<RequestHandle*>(1), req); 801 num_cancelled_requests_++; 802 } 803 804 void SetAction(const base::Callback<void(void)>& action) { 805 action_ = action; 806 } 807 808 // Waits until Resolve() has been called. 809 void WaitUntilRequestIsReceived() { 810 waiting_for_resolve_ = true; 811 base::MessageLoop::current()->Run(); 812 DCHECK(waiting_for_resolve_); 813 waiting_for_resolve_ = false; 814 } 815 816 int num_cancelled_requests() const { 817 return num_cancelled_requests_; 818 } 819 820 private: 821 int num_cancelled_requests_; 822 bool waiting_for_resolve_; 823 base::Callback<void(void)> action_; 824 }; 825 826 // This cancellation test exercises a more predictable cancellation codepath -- 827 // when the request has an outstanding DNS request in flight. 828 TEST_F(ProxyResolverV8TracingTest, CancelWhileOutstandingNonBlockingDns) { 829 BlockableHostResolver host_resolver; 830 MockErrorObserver* error_observer = new MockErrorObserver; 831 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL); 832 833 InitResolver(&resolver, "dns.js"); 834 835 ProxyInfo proxy_info1; 836 ProxyInfo proxy_info2; 837 ProxyResolver::RequestHandle request1; 838 ProxyResolver::RequestHandle request2; 839 840 int rv = resolver.GetProxyForURL( 841 GURL("http://foo/req1"), &proxy_info1, 842 base::Bind(&CrashCallback), &request1, BoundNetLog()); 843 844 EXPECT_EQ(ERR_IO_PENDING, rv); 845 846 host_resolver.WaitUntilRequestIsReceived(); 847 848 rv = resolver.GetProxyForURL( 849 GURL("http://foo/req2"), &proxy_info2, 850 base::Bind(&CrashCallback), &request2, BoundNetLog()); 851 852 EXPECT_EQ(ERR_IO_PENDING, rv); 853 854 host_resolver.WaitUntilRequestIsReceived(); 855 856 resolver.CancelRequest(request1); 857 resolver.CancelRequest(request2); 858 859 EXPECT_EQ(2, host_resolver.num_cancelled_requests()); 860 861 // After leaving this scope, the ProxyResolver is destroyed. 862 // This should not cause any problems, as the outstanding work 863 // should have been cancelled. 864 } 865 866 void CancelRequestAndPause(ProxyResolverV8Tracing* resolver, 867 ProxyResolver::RequestHandle request) { 868 resolver->CancelRequest(request); 869 870 // Sleep for a little bit. This makes it more likely for the worker 871 // thread to have returned from its call, and serves as a regression 872 // test for http://crbug.com/173373. 873 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(30)); 874 } 875 876 // In non-blocking mode, the worker thread actually does block for 877 // a short time to see if the result is in the DNS cache. Test 878 // cancellation while the worker thread is waiting on this event. 879 TEST_F(ProxyResolverV8TracingTest, CancelWhileBlockedInNonBlockingDns) { 880 BlockableHostResolver host_resolver; 881 MockErrorObserver* error_observer = new MockErrorObserver; 882 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL); 883 884 InitResolver(&resolver, "dns.js"); 885 886 ProxyInfo proxy_info; 887 ProxyResolver::RequestHandle request; 888 889 int rv = resolver.GetProxyForURL( 890 GURL("http://foo/"), &proxy_info, 891 base::Bind(&CrashCallback), &request, BoundNetLog()); 892 893 EXPECT_EQ(ERR_IO_PENDING, rv); 894 895 host_resolver.SetAction( 896 base::Bind(CancelRequestAndPause, &resolver, request)); 897 898 host_resolver.WaitUntilRequestIsReceived(); 899 900 // At this point the host resolver ran Resolve(), and should have cancelled 901 // the request. 902 903 EXPECT_EQ(1, host_resolver.num_cancelled_requests()); 904 } 905 906 // Cancel the request while there is a pending DNS request, however before 907 // the request is sent to the host resolver. 908 TEST_F(ProxyResolverV8TracingTest, CancelWhileBlockedInNonBlockingDns2) { 909 MockCachingHostResolver host_resolver; 910 MockErrorObserver* error_observer = new MockErrorObserver; 911 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL); 912 913 InitResolver(&resolver, "dns.js"); 914 915 ProxyInfo proxy_info; 916 ProxyResolver::RequestHandle request; 917 918 int rv = resolver.GetProxyForURL( 919 GURL("http://foo/"), &proxy_info, 920 base::Bind(&CrashCallback), &request, BoundNetLog()); 921 922 EXPECT_EQ(ERR_IO_PENDING, rv); 923 924 // Wait a bit, so the DNS task has hopefully been posted. The test will 925 // work whatever the delay is here, but it is most useful if the delay 926 // is large enough to allow a task to be posted back. 927 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10)); 928 resolver.CancelRequest(request); 929 930 EXPECT_EQ(0u, host_resolver.num_resolve()); 931 } 932 933 TEST_F(ProxyResolverV8TracingTest, CancelSetPacWhileOutstandingBlockingDns) { 934 BlockableHostResolver host_resolver; 935 MockErrorObserver* error_observer = new MockErrorObserver; 936 937 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL); 938 939 int rv = 940 resolver.SetPacScript(LoadScriptData("dns_during_init.js"), 941 base::Bind(&CrashCallback)); 942 EXPECT_EQ(ERR_IO_PENDING, rv); 943 944 host_resolver.WaitUntilRequestIsReceived(); 945 946 resolver.CancelSetPacScript(); 947 EXPECT_EQ(1, host_resolver.num_cancelled_requests()); 948 } 949 950 // This tests that the execution of a PAC script is terminated when the DNS 951 // dependencies are missing. If the test fails, then it will hang. 952 TEST_F(ProxyResolverV8TracingTest, Terminate) { 953 CapturingNetLog log; 954 CapturingBoundNetLog request_log; 955 MockCachingHostResolver host_resolver; 956 MockErrorObserver* error_observer = new MockErrorObserver; 957 ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log); 958 959 host_resolver.rules()->AddRule("host1", "182.111.0.222"); 960 host_resolver.rules()->AddRule("host2", "111.33.44.55"); 961 962 InitResolver(&resolver, "terminate.js"); 963 964 TestCompletionCallback callback; 965 ProxyInfo proxy_info; 966 967 int rv = resolver.GetProxyForURL( 968 GURL("http://foopy/req1"), 969 &proxy_info, 970 callback.callback(), 971 NULL, 972 request_log.bound()); 973 974 EXPECT_EQ(ERR_IO_PENDING, rv); 975 EXPECT_EQ(OK, callback.WaitForResult()); 976 977 // The test does 2 DNS resolutions. 978 EXPECT_EQ(2u, host_resolver.num_resolve()); 979 980 EXPECT_EQ("foopy:3", proxy_info.proxy_server().ToURI()); 981 982 // No errors. 983 EXPECT_EQ("", error_observer->GetOutput()); 984 985 EXPECT_EQ(0u, log.GetSize()); 986 EXPECT_EQ(0u, request_log.GetSize()); 987 } 988 989 // Tests that multiple instances of ProxyResolverV8Tracing can coexist and run 990 // correctly at the same time. This is relevant because at the moment (time 991 // this test was written) each ProxyResolverV8Tracing creates its own thread to 992 // run V8 on, however each thread is operating on the same v8::Isolate. 993 TEST_F(ProxyResolverV8TracingTest, MultipleResolvers) { 994 // ------------------------ 995 // Setup resolver0 996 // ------------------------ 997 MockHostResolver host_resolver0; 998 host_resolver0.rules()->AddRuleForAddressFamily( 999 "host1", ADDRESS_FAMILY_IPV4, "166.155.144.44"); 1000 host_resolver0.rules() 1001 ->AddIPLiteralRule("host1", "::1,192.168.1.1", std::string()); 1002 host_resolver0.rules()->AddSimulatedFailure("host2"); 1003 host_resolver0.rules()->AddRule("host3", "166.155.144.33"); 1004 host_resolver0.rules()->AddRule("host5", "166.155.144.55"); 1005 host_resolver0.rules()->AddSimulatedFailure("host6"); 1006 host_resolver0.rules()->AddRuleForAddressFamily( 1007 "*", ADDRESS_FAMILY_IPV4, "122.133.144.155"); 1008 host_resolver0.rules()->AddRule("*", "133.122.100.200"); 1009 ProxyResolverV8Tracing resolver0( 1010 &host_resolver0, new MockErrorObserver, NULL); 1011 InitResolver(&resolver0, "dns.js"); 1012 1013 // ------------------------ 1014 // Setup resolver1 1015 // ------------------------ 1016 ProxyResolverV8Tracing resolver1( 1017 &host_resolver0, new MockErrorObserver, NULL); 1018 InitResolver(&resolver1, "dns.js"); 1019 1020 // ------------------------ 1021 // Setup resolver2 1022 // ------------------------ 1023 ProxyResolverV8Tracing resolver2( 1024 &host_resolver0, new MockErrorObserver, NULL); 1025 InitResolver(&resolver2, "simple.js"); 1026 1027 // ------------------------ 1028 // Setup resolver3 1029 // ------------------------ 1030 MockHostResolver host_resolver3; 1031 host_resolver3.rules()->AddRule("foo", "166.155.144.33"); 1032 ProxyResolverV8Tracing resolver3( 1033 &host_resolver3, new MockErrorObserver, NULL); 1034 InitResolver(&resolver3, "simple_dns.js"); 1035 1036 // ------------------------ 1037 // Queue up work for each resolver (which will be running in parallel). 1038 // ------------------------ 1039 1040 ProxyResolverV8Tracing* resolver[] = { 1041 &resolver0, &resolver1, &resolver2, &resolver3, 1042 }; 1043 1044 const size_t kNumResolvers = arraysize(resolver); 1045 const size_t kNumIterations = 20; 1046 const size_t kNumResults = kNumResolvers * kNumIterations; 1047 TestCompletionCallback callback[kNumResults]; 1048 ProxyInfo proxy_info[kNumResults]; 1049 1050 for (size_t i = 0; i < kNumResults; ++i) { 1051 size_t resolver_i = i % kNumResolvers; 1052 int rv = resolver[resolver_i]->GetProxyForURL( 1053 GURL("http://foo/"), &proxy_info[i], callback[i].callback(), NULL, 1054 BoundNetLog()); 1055 EXPECT_EQ(ERR_IO_PENDING, rv); 1056 } 1057 1058 // ------------------------ 1059 // Verify all of the results. 1060 // ------------------------ 1061 1062 const char* kExpectedForDnsJs = 1063 "122.133.144.155-" // myIpAddress() 1064 "null-" // dnsResolve('') 1065 "__1_192.168.1.1-" // dnsResolveEx('host1') 1066 "null-" // dnsResolve('host2') 1067 "166.155.144.33-" // dnsResolve('host3') 1068 "122.133.144.155-" // myIpAddress() 1069 "166.155.144.33-" // dnsResolve('host3') 1070 "__1_192.168.1.1-" // dnsResolveEx('host1') 1071 "122.133.144.155-" // myIpAddress() 1072 "null-" // dnsResolve('host2') 1073 "-" // dnsResolveEx('host6') 1074 "133.122.100.200-" // myIpAddressEx() 1075 "166.155.144.44" // dnsResolve('host1') 1076 ":99"; 1077 1078 for (size_t i = 0; i < kNumResults; ++i) { 1079 size_t resolver_i = i % kNumResolvers; 1080 EXPECT_EQ(OK, callback[i].WaitForResult()); 1081 1082 std::string proxy_uri = proxy_info[i].proxy_server().ToURI(); 1083 1084 if (resolver_i == 0 || resolver_i == 1) { 1085 EXPECT_EQ(kExpectedForDnsJs, proxy_uri); 1086 } else if (resolver_i == 2) { 1087 EXPECT_EQ("foo:99", proxy_uri); 1088 } else if (resolver_i == 3) { 1089 EXPECT_EQ("166.155.144.33:", 1090 proxy_uri.substr(0, proxy_uri.find(':') + 1)); 1091 } else { 1092 NOTREACHED(); 1093 } 1094 } 1095 } 1096 1097 } // namespace 1098 1099 } // namespace net 1100