Home | History | Annotate | Download | only in proxy
      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