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