1 // Copyright (c) 2012 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 "base/compiler_specific.h" 6 #include "base/file_util.h" 7 #include "base/path_service.h" 8 #include "base/strings/string_util.h" 9 #include "base/strings/stringprintf.h" 10 #include "base/strings/utf_string_conversions.h" 11 #include "net/base/net_errors.h" 12 #include "net/base/net_log_unittest.h" 13 #include "net/proxy/proxy_info.h" 14 #include "net/proxy/proxy_resolver_v8.h" 15 #include "testing/gtest/include/gtest/gtest.h" 16 #include "url/gurl.h" 17 18 namespace net { 19 namespace { 20 21 // Javascript bindings for ProxyResolverV8, which returns mock values. 22 // Each time one of the bindings is called into, we push the input into a 23 // list, for later verification. 24 class MockJSBindings : public ProxyResolverV8::JSBindings { 25 public: 26 MockJSBindings() : my_ip_address_count(0), my_ip_address_ex_count(0), 27 should_terminate(false) {} 28 29 virtual void Alert(const base::string16& message) OVERRIDE { 30 VLOG(1) << "PAC-alert: " << message; // Helpful when debugging. 31 alerts.push_back(UTF16ToUTF8(message)); 32 } 33 34 virtual bool ResolveDns(const std::string& host, 35 ResolveDnsOperation op, 36 std::string* output, 37 bool* terminate) OVERRIDE { 38 *terminate = should_terminate; 39 40 if (op == MY_IP_ADDRESS) { 41 my_ip_address_count++; 42 *output = my_ip_address_result; 43 return !my_ip_address_result.empty(); 44 } 45 46 if (op == MY_IP_ADDRESS_EX) { 47 my_ip_address_ex_count++; 48 *output = my_ip_address_ex_result; 49 return !my_ip_address_ex_result.empty(); 50 } 51 52 if (op == DNS_RESOLVE) { 53 dns_resolves.push_back(host); 54 *output = dns_resolve_result; 55 return !dns_resolve_result.empty(); 56 } 57 58 if (op == DNS_RESOLVE_EX) { 59 dns_resolves_ex.push_back(host); 60 *output = dns_resolve_ex_result; 61 return !dns_resolve_ex_result.empty(); 62 } 63 64 CHECK(false); 65 return false; 66 } 67 68 virtual void OnError(int line_number, 69 const base::string16& message) OVERRIDE { 70 // Helpful when debugging. 71 VLOG(1) << "PAC-error: [" << line_number << "] " << message; 72 73 errors.push_back(UTF16ToUTF8(message)); 74 errors_line_number.push_back(line_number); 75 } 76 77 // Mock values to return. 78 std::string my_ip_address_result; 79 std::string my_ip_address_ex_result; 80 std::string dns_resolve_result; 81 std::string dns_resolve_ex_result; 82 83 // Inputs we got called with. 84 std::vector<std::string> alerts; 85 std::vector<std::string> errors; 86 std::vector<int> errors_line_number; 87 std::vector<std::string> dns_resolves; 88 std::vector<std::string> dns_resolves_ex; 89 int my_ip_address_count; 90 int my_ip_address_ex_count; 91 92 // Whether ResolveDns() should terminate script execution. 93 bool should_terminate; 94 }; 95 96 // This is the same as ProxyResolverV8, but it uses mock bindings in place of 97 // the default bindings, and has a helper function to load PAC scripts from 98 // disk. 99 class ProxyResolverV8WithMockBindings : public ProxyResolverV8 { 100 public: 101 ProxyResolverV8WithMockBindings() { 102 set_js_bindings(&mock_js_bindings_); 103 } 104 105 MockJSBindings* mock_js_bindings() { 106 return &mock_js_bindings_; 107 } 108 109 // Initialize with the PAC script data at |filename|. 110 int SetPacScriptFromDisk(const char* filename) { 111 base::FilePath path; 112 PathService::Get(base::DIR_SOURCE_ROOT, &path); 113 path = path.AppendASCII("net"); 114 path = path.AppendASCII("data"); 115 path = path.AppendASCII("proxy_resolver_v8_unittest"); 116 path = path.AppendASCII(filename); 117 118 // Try to read the file from disk. 119 std::string file_contents; 120 bool ok = file_util::ReadFileToString(path, &file_contents); 121 122 // If we can't load the file from disk, something is misconfigured. 123 if (!ok) { 124 LOG(ERROR) << "Failed to read file: " << path.value(); 125 return ERR_UNEXPECTED; 126 } 127 128 // Load the PAC script into the ProxyResolver. 129 return SetPacScript(ProxyResolverScriptData::FromUTF8(file_contents), 130 CompletionCallback()); 131 } 132 133 private: 134 MockJSBindings mock_js_bindings_; 135 }; 136 137 // Doesn't really matter what these values are for many of the tests. 138 const GURL kQueryUrl("http://www.google.com"); 139 const GURL kPacUrl; 140 141 TEST(ProxyResolverV8Test, Direct) { 142 ProxyResolverV8WithMockBindings resolver; 143 int result = resolver.SetPacScriptFromDisk("direct.js"); 144 EXPECT_EQ(OK, result); 145 146 ProxyInfo proxy_info; 147 CapturingBoundNetLog log; 148 result = resolver.GetProxyForURL( 149 kQueryUrl, &proxy_info, CompletionCallback(), NULL, log.bound()); 150 151 EXPECT_EQ(OK, result); 152 EXPECT_TRUE(proxy_info.is_direct()); 153 154 EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size()); 155 EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size()); 156 157 net::CapturingNetLog::CapturedEntryList entries; 158 log.GetEntries(&entries); 159 // No bindings were called, so no log entries. 160 EXPECT_EQ(0u, entries.size()); 161 } 162 163 TEST(ProxyResolverV8Test, ReturnEmptyString) { 164 ProxyResolverV8WithMockBindings resolver; 165 int result = resolver.SetPacScriptFromDisk("return_empty_string.js"); 166 EXPECT_EQ(OK, result); 167 168 ProxyInfo proxy_info; 169 result = resolver.GetProxyForURL( 170 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog()); 171 172 EXPECT_EQ(OK, result); 173 EXPECT_TRUE(proxy_info.is_direct()); 174 175 EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size()); 176 EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size()); 177 } 178 179 TEST(ProxyResolverV8Test, Basic) { 180 ProxyResolverV8WithMockBindings resolver; 181 int result = resolver.SetPacScriptFromDisk("passthrough.js"); 182 EXPECT_EQ(OK, result); 183 184 // The "FindProxyForURL" of this PAC script simply concatenates all of the 185 // arguments into a pseudo-host. The purpose of this test is to verify that 186 // the correct arguments are being passed to FindProxyForURL(). 187 { 188 ProxyInfo proxy_info; 189 result = resolver.GetProxyForURL(GURL("http://query.com/path"), &proxy_info, 190 CompletionCallback(), NULL, BoundNetLog()); 191 EXPECT_EQ(OK, result); 192 EXPECT_EQ("http.query.com.path.query.com:80", 193 proxy_info.proxy_server().ToURI()); 194 } 195 { 196 ProxyInfo proxy_info; 197 int result = resolver.GetProxyForURL( 198 GURL("ftp://query.com:90/path"), &proxy_info, CompletionCallback(), 199 NULL, BoundNetLog()); 200 EXPECT_EQ(OK, result); 201 // Note that FindProxyForURL(url, host) does not expect |host| to contain 202 // the port number. 203 EXPECT_EQ("ftp.query.com.90.path.query.com:80", 204 proxy_info.proxy_server().ToURI()); 205 206 EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size()); 207 EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size()); 208 } 209 210 // We call this so we'll have code coverage of the function and valgrind will 211 // make sure nothing bad happens. 212 // 213 // NOTE: This is here instead of in its own test so that we'll be calling it 214 // after having done something, in hopes it won't be a no-op. 215 resolver.PurgeMemory(); 216 } 217 218 TEST(ProxyResolverV8Test, BadReturnType) { 219 // These are the filenames of PAC scripts which each return a non-string 220 // types for FindProxyForURL(). They should all fail with 221 // ERR_PAC_SCRIPT_FAILED. 222 static const char* const filenames[] = { 223 "return_undefined.js", 224 "return_integer.js", 225 "return_function.js", 226 "return_object.js", 227 // TODO(eroman): Should 'null' be considered equivalent to "DIRECT" ? 228 "return_null.js" 229 }; 230 231 for (size_t i = 0; i < arraysize(filenames); ++i) { 232 ProxyResolverV8WithMockBindings resolver; 233 int result = resolver.SetPacScriptFromDisk(filenames[i]); 234 EXPECT_EQ(OK, result); 235 236 ProxyInfo proxy_info; 237 result = resolver.GetProxyForURL( 238 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog()); 239 240 EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result); 241 242 MockJSBindings* bindings = resolver.mock_js_bindings(); 243 EXPECT_EQ(0U, bindings->alerts.size()); 244 ASSERT_EQ(1U, bindings->errors.size()); 245 EXPECT_EQ("FindProxyForURL() did not return a string.", 246 bindings->errors[0]); 247 EXPECT_EQ(-1, bindings->errors_line_number[0]); 248 } 249 } 250 251 // Try using a PAC script which defines no "FindProxyForURL" function. 252 TEST(ProxyResolverV8Test, NoEntryPoint) { 253 ProxyResolverV8WithMockBindings resolver; 254 int result = resolver.SetPacScriptFromDisk("no_entrypoint.js"); 255 EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result); 256 257 ProxyInfo proxy_info; 258 result = resolver.GetProxyForURL( 259 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog()); 260 261 EXPECT_EQ(ERR_FAILED, result); 262 } 263 264 // Try loading a malformed PAC script. 265 TEST(ProxyResolverV8Test, ParseError) { 266 ProxyResolverV8WithMockBindings resolver; 267 int result = resolver.SetPacScriptFromDisk("missing_close_brace.js"); 268 EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result); 269 270 ProxyInfo proxy_info; 271 result = resolver.GetProxyForURL( 272 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog()); 273 274 EXPECT_EQ(ERR_FAILED, result); 275 276 MockJSBindings* bindings = resolver.mock_js_bindings(); 277 EXPECT_EQ(0U, bindings->alerts.size()); 278 279 // We get one error during compilation. 280 ASSERT_EQ(1U, bindings->errors.size()); 281 282 EXPECT_EQ("Uncaught SyntaxError: Unexpected end of input", 283 bindings->errors[0]); 284 EXPECT_EQ(0, bindings->errors_line_number[0]); 285 } 286 287 // Run a PAC script several times, which has side-effects. 288 TEST(ProxyResolverV8Test, SideEffects) { 289 ProxyResolverV8WithMockBindings resolver; 290 int result = resolver.SetPacScriptFromDisk("side_effects.js"); 291 292 // The PAC script increments a counter each time we invoke it. 293 for (int i = 0; i < 3; ++i) { 294 ProxyInfo proxy_info; 295 result = resolver.GetProxyForURL( 296 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog()); 297 EXPECT_EQ(OK, result); 298 EXPECT_EQ(base::StringPrintf("sideffect_%d:80", i), 299 proxy_info.proxy_server().ToURI()); 300 } 301 302 // Reload the script -- the javascript environment should be reset, hence 303 // the counter starts over. 304 result = resolver.SetPacScriptFromDisk("side_effects.js"); 305 EXPECT_EQ(OK, result); 306 307 for (int i = 0; i < 3; ++i) { 308 ProxyInfo proxy_info; 309 result = resolver.GetProxyForURL( 310 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog()); 311 EXPECT_EQ(OK, result); 312 EXPECT_EQ(base::StringPrintf("sideffect_%d:80", i), 313 proxy_info.proxy_server().ToURI()); 314 } 315 } 316 317 // Execute a PAC script which throws an exception in FindProxyForURL. 318 TEST(ProxyResolverV8Test, UnhandledException) { 319 ProxyResolverV8WithMockBindings resolver; 320 int result = resolver.SetPacScriptFromDisk("unhandled_exception.js"); 321 EXPECT_EQ(OK, result); 322 323 ProxyInfo proxy_info; 324 result = resolver.GetProxyForURL( 325 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog()); 326 327 EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result); 328 329 MockJSBindings* bindings = resolver.mock_js_bindings(); 330 EXPECT_EQ(0U, bindings->alerts.size()); 331 ASSERT_EQ(1U, bindings->errors.size()); 332 EXPECT_EQ("Uncaught ReferenceError: undefined_variable is not defined", 333 bindings->errors[0]); 334 EXPECT_EQ(3, bindings->errors_line_number[0]); 335 } 336 337 TEST(ProxyResolverV8Test, ReturnUnicode) { 338 ProxyResolverV8WithMockBindings resolver; 339 int result = resolver.SetPacScriptFromDisk("return_unicode.js"); 340 EXPECT_EQ(OK, result); 341 342 ProxyInfo proxy_info; 343 result = resolver.GetProxyForURL( 344 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog()); 345 346 // The result from this resolve was unparseable, because it 347 // wasn't ASCII. 348 EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result); 349 } 350 351 // Test the PAC library functions that we expose in the JS environment. 352 TEST(ProxyResolverV8Test, JavascriptLibrary) { 353 ProxyResolverV8WithMockBindings resolver; 354 int result = resolver.SetPacScriptFromDisk("pac_library_unittest.js"); 355 EXPECT_EQ(OK, result); 356 357 ProxyInfo proxy_info; 358 result = resolver.GetProxyForURL( 359 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog()); 360 361 // If the javascript side of this unit-test fails, it will throw a javascript 362 // exception. Otherwise it will return "PROXY success:80". 363 EXPECT_EQ(OK, result); 364 EXPECT_EQ("success:80", proxy_info.proxy_server().ToURI()); 365 366 EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size()); 367 EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size()); 368 } 369 370 // Try resolving when SetPacScriptByData() has not been called. 371 TEST(ProxyResolverV8Test, NoSetPacScript) { 372 ProxyResolverV8WithMockBindings resolver; 373 374 ProxyInfo proxy_info; 375 376 // Resolve should fail, as we are not yet initialized with a script. 377 int result = resolver.GetProxyForURL( 378 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog()); 379 EXPECT_EQ(ERR_FAILED, result); 380 381 // Initialize it. 382 result = resolver.SetPacScriptFromDisk("direct.js"); 383 EXPECT_EQ(OK, result); 384 385 // Resolve should now succeed. 386 result = resolver.GetProxyForURL( 387 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog()); 388 EXPECT_EQ(OK, result); 389 390 // Clear it, by initializing with an empty string. 391 resolver.SetPacScript( 392 ProxyResolverScriptData::FromUTF16(base::string16()), 393 CompletionCallback()); 394 395 // Resolve should fail again now. 396 result = resolver.GetProxyForURL( 397 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog()); 398 EXPECT_EQ(ERR_FAILED, result); 399 400 // Load a good script once more. 401 result = resolver.SetPacScriptFromDisk("direct.js"); 402 EXPECT_EQ(OK, result); 403 result = resolver.GetProxyForURL( 404 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog()); 405 EXPECT_EQ(OK, result); 406 407 EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size()); 408 EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size()); 409 } 410 411 // Test marshalling/un-marshalling of values between C++/V8. 412 TEST(ProxyResolverV8Test, V8Bindings) { 413 ProxyResolverV8WithMockBindings resolver; 414 MockJSBindings* bindings = resolver.mock_js_bindings(); 415 bindings->dns_resolve_result = "127.0.0.1"; 416 int result = resolver.SetPacScriptFromDisk("bindings.js"); 417 EXPECT_EQ(OK, result); 418 419 ProxyInfo proxy_info; 420 result = resolver.GetProxyForURL( 421 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog()); 422 423 EXPECT_EQ(OK, result); 424 EXPECT_TRUE(proxy_info.is_direct()); 425 426 EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size()); 427 428 // Alert was called 5 times. 429 ASSERT_EQ(5U, bindings->alerts.size()); 430 EXPECT_EQ("undefined", bindings->alerts[0]); 431 EXPECT_EQ("null", bindings->alerts[1]); 432 EXPECT_EQ("undefined", bindings->alerts[2]); 433 EXPECT_EQ("[object Object]", bindings->alerts[3]); 434 EXPECT_EQ("exception from calling toString()", bindings->alerts[4]); 435 436 // DnsResolve was called 8 times, however only 2 of those were string 437 // parameters. (so 6 of them failed immediately). 438 ASSERT_EQ(2U, bindings->dns_resolves.size()); 439 EXPECT_EQ("", bindings->dns_resolves[0]); 440 EXPECT_EQ("arg1", bindings->dns_resolves[1]); 441 442 // MyIpAddress was called two times. 443 EXPECT_EQ(2, bindings->my_ip_address_count); 444 445 // MyIpAddressEx was called once. 446 EXPECT_EQ(1, bindings->my_ip_address_ex_count); 447 448 // DnsResolveEx was called 2 times. 449 ASSERT_EQ(2U, bindings->dns_resolves_ex.size()); 450 EXPECT_EQ("is_resolvable", bindings->dns_resolves_ex[0]); 451 EXPECT_EQ("foobar", bindings->dns_resolves_ex[1]); 452 } 453 454 // Test calling a binding (myIpAddress()) from the script's global scope. 455 // http://crbug.com/40026 456 TEST(ProxyResolverV8Test, BindingCalledDuringInitialization) { 457 ProxyResolverV8WithMockBindings resolver; 458 459 int result = resolver.SetPacScriptFromDisk("binding_from_global.js"); 460 EXPECT_EQ(OK, result); 461 462 MockJSBindings* bindings = resolver.mock_js_bindings(); 463 464 // myIpAddress() got called during initialization of the script. 465 EXPECT_EQ(1, bindings->my_ip_address_count); 466 467 ProxyInfo proxy_info; 468 result = resolver.GetProxyForURL( 469 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog()); 470 471 EXPECT_EQ(OK, result); 472 EXPECT_FALSE(proxy_info.is_direct()); 473 EXPECT_EQ("127.0.0.1:80", proxy_info.proxy_server().ToURI()); 474 475 // Check that no other bindings were called. 476 EXPECT_EQ(0U, bindings->errors.size()); 477 ASSERT_EQ(0U, bindings->alerts.size()); 478 ASSERT_EQ(0U, bindings->dns_resolves.size()); 479 EXPECT_EQ(0, bindings->my_ip_address_ex_count); 480 ASSERT_EQ(0U, bindings->dns_resolves_ex.size()); 481 } 482 483 // Try loading a PAC script that ends with a comment and has no terminal 484 // newline. This should not cause problems with the PAC utility functions 485 // that we add to the script's environment. 486 // http://crbug.com/22864 487 TEST(ProxyResolverV8Test, EndsWithCommentNoNewline) { 488 ProxyResolverV8WithMockBindings resolver; 489 int result = resolver.SetPacScriptFromDisk("ends_with_comment.js"); 490 EXPECT_EQ(OK, result); 491 492 ProxyInfo proxy_info; 493 result = resolver.GetProxyForURL( 494 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog()); 495 496 EXPECT_EQ(OK, result); 497 EXPECT_FALSE(proxy_info.is_direct()); 498 EXPECT_EQ("success:80", proxy_info.proxy_server().ToURI()); 499 } 500 501 // Try loading a PAC script that ends with a statement and has no terminal 502 // newline. This should not cause problems with the PAC utility functions 503 // that we add to the script's environment. 504 // http://crbug.com/22864 505 TEST(ProxyResolverV8Test, EndsWithStatementNoNewline) { 506 ProxyResolverV8WithMockBindings resolver; 507 int result = resolver.SetPacScriptFromDisk( 508 "ends_with_statement_no_semicolon.js"); 509 EXPECT_EQ(OK, result); 510 511 ProxyInfo proxy_info; 512 result = resolver.GetProxyForURL( 513 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog()); 514 515 EXPECT_EQ(OK, result); 516 EXPECT_FALSE(proxy_info.is_direct()); 517 EXPECT_EQ("success:3", proxy_info.proxy_server().ToURI()); 518 } 519 520 // Test the return values from myIpAddress(), myIpAddressEx(), dnsResolve(), 521 // dnsResolveEx(), isResolvable(), isResolvableEx(), when the the binding 522 // returns empty string (failure). This simulates the return values from 523 // those functions when the underlying DNS resolution fails. 524 TEST(ProxyResolverV8Test, DNSResolutionFailure) { 525 ProxyResolverV8WithMockBindings resolver; 526 int result = resolver.SetPacScriptFromDisk("dns_fail.js"); 527 EXPECT_EQ(OK, result); 528 529 ProxyInfo proxy_info; 530 result = resolver.GetProxyForURL( 531 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog()); 532 533 EXPECT_EQ(OK, result); 534 EXPECT_FALSE(proxy_info.is_direct()); 535 EXPECT_EQ("success:80", proxy_info.proxy_server().ToURI()); 536 } 537 538 TEST(ProxyResolverV8Test, DNSResolutionOfInternationDomainName) { 539 ProxyResolverV8WithMockBindings resolver; 540 int result = resolver.SetPacScriptFromDisk("international_domain_names.js"); 541 EXPECT_EQ(OK, result); 542 543 // Execute FindProxyForURL(). 544 ProxyInfo proxy_info; 545 result = resolver.GetProxyForURL( 546 kQueryUrl, &proxy_info, CompletionCallback(), NULL, BoundNetLog()); 547 548 EXPECT_EQ(OK, result); 549 EXPECT_TRUE(proxy_info.is_direct()); 550 551 // Check that the international domain name was converted to punycode 552 // before passing it onto the bindings layer. 553 MockJSBindings* bindings = resolver.mock_js_bindings(); 554 555 ASSERT_EQ(1u, bindings->dns_resolves.size()); 556 EXPECT_EQ("xn--bcher-kva.ch", bindings->dns_resolves[0]); 557 558 ASSERT_EQ(1u, bindings->dns_resolves_ex.size()); 559 EXPECT_EQ("xn--bcher-kva.ch", bindings->dns_resolves_ex[0]); 560 } 561 562 // Test that when resolving a URL which contains an IPv6 string literal, the 563 // brackets are removed from the host before passing it down to the PAC script. 564 // If we don't do this, then subsequent calls to dnsResolveEx(host) will be 565 // doomed to fail since it won't correspond with a valid name. 566 TEST(ProxyResolverV8Test, IPv6HostnamesNotBracketed) { 567 ProxyResolverV8WithMockBindings resolver; 568 int result = resolver.SetPacScriptFromDisk("resolve_host.js"); 569 EXPECT_EQ(OK, result); 570 571 ProxyInfo proxy_info; 572 result = resolver.GetProxyForURL( 573 GURL("http://[abcd::efff]:99/watsupdawg"), &proxy_info, 574 CompletionCallback(), NULL, BoundNetLog()); 575 576 EXPECT_EQ(OK, result); 577 EXPECT_TRUE(proxy_info.is_direct()); 578 579 // We called dnsResolveEx() exactly once, by passing through the "host" 580 // argument to FindProxyForURL(). The brackets should have been stripped. 581 ASSERT_EQ(1U, resolver.mock_js_bindings()->dns_resolves_ex.size()); 582 EXPECT_EQ("abcd::efff", resolver.mock_js_bindings()->dns_resolves_ex[0]); 583 } 584 585 // Test that terminating a script within DnsResolve() leads to eventual 586 // termination of the script. Also test that repeatedly calling terminate is 587 // safe, and running the script again after termination still works. 588 TEST(ProxyResolverV8Test, Terminate) { 589 ProxyResolverV8WithMockBindings resolver; 590 int result = resolver.SetPacScriptFromDisk("terminate.js"); 591 EXPECT_EQ(OK, result); 592 593 MockJSBindings* bindings = resolver.mock_js_bindings(); 594 595 // Terminate script execution upon reaching dnsResolve(). Note that 596 // termination may not take effect right away (so the subsequent dnsResolve() 597 // and alert() may be run). 598 bindings->should_terminate = true; 599 600 ProxyInfo proxy_info; 601 result = resolver.GetProxyForURL( 602 GURL("http://hang/"), &proxy_info, 603 CompletionCallback(), NULL, BoundNetLog()); 604 605 // The script execution was terminated. 606 EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result); 607 608 EXPECT_EQ(1U, resolver.mock_js_bindings()->dns_resolves.size()); 609 EXPECT_GE(2U, resolver.mock_js_bindings()->dns_resolves_ex.size()); 610 EXPECT_GE(1U, bindings->alerts.size()); 611 612 EXPECT_EQ(1U, bindings->errors.size()); 613 614 // Termination shows up as an uncaught exception without any message. 615 EXPECT_EQ("", bindings->errors[0]); 616 617 bindings->errors.clear(); 618 619 // Try running the script again, this time with a different input which won't 620 // cause a termination+hang. 621 result = resolver.GetProxyForURL( 622 GURL("http://kittens/"), &proxy_info, 623 CompletionCallback(), NULL, BoundNetLog()); 624 625 EXPECT_EQ(OK, result); 626 EXPECT_EQ(0u, bindings->errors.size()); 627 EXPECT_EQ("kittens:88", proxy_info.proxy_server().ToURI()); 628 } 629 630 } // namespace 631 } // namespace net 632