Home | History | Annotate | Download | only in proxy
      1 // Copyright (c) 2010 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 <algorithm>
      6 #include <cstdio>
      7 
      8 #include "net/proxy/proxy_resolver_v8.h"
      9 
     10 #include "base/basictypes.h"
     11 #include "base/logging.h"
     12 #include "base/string_tokenizer.h"
     13 #include "base/string_util.h"
     14 #include "base/utf_string_conversions.h"
     15 #include "googleurl/src/gurl.h"
     16 #include "googleurl/src/url_canon.h"
     17 #include "net/base/host_cache.h"
     18 #include "net/base/net_errors.h"
     19 #include "net/base/net_log.h"
     20 #include "net/base/net_util.h"
     21 #include "net/proxy/proxy_info.h"
     22 #include "net/proxy/proxy_resolver_js_bindings.h"
     23 #include "net/proxy/proxy_resolver_request_context.h"
     24 #include "net/proxy/proxy_resolver_script.h"
     25 #include "v8/include/v8.h"
     26 
     27 // Notes on the javascript environment:
     28 //
     29 // For the majority of the PAC utility functions, we use the same code
     30 // as Firefox. See the javascript library that proxy_resolver_scipt.h
     31 // pulls in.
     32 //
     33 // In addition, we implement a subset of Microsoft's extensions to PAC.
     34 // - myIpAddressEx()
     35 // - dnsResolveEx()
     36 // - isResolvableEx()
     37 // - isInNetEx()
     38 // - sortIpAddressList()
     39 //
     40 // It is worth noting that the original PAC specification does not describe
     41 // the return values on failure. Consequently, there are compatibility
     42 // differences between browsers on what to return on failure, which are
     43 // illustrated below:
     44 //
     45 // --------------------+-------------+-------------------+--------------
     46 //                     | Firefox3    | InternetExplorer8 |  --> Us <---
     47 // --------------------+-------------+-------------------+--------------
     48 // myIpAddress()       | "127.0.0.1" |  ???              |  "127.0.0.1"
     49 // dnsResolve()        | null        |  false            |  null
     50 // myIpAddressEx()     | N/A         |  ""               |  ""
     51 // sortIpAddressList() | N/A         |  false            |  false
     52 // dnsResolveEx()      | N/A         |  ""               |  ""
     53 // isInNetEx()         | N/A         |  false            |  false
     54 // --------------------+-------------+-------------------+--------------
     55 //
     56 // TODO(eroman): The cell above reading ??? means I didn't test it.
     57 //
     58 // Another difference is in how dnsResolve() and myIpAddress() are
     59 // implemented -- whether they should restrict to IPv4 results, or
     60 // include both IPv4 and IPv6. The following table illustrates the
     61 // differences:
     62 //
     63 // --------------------+-------------+-------------------+--------------
     64 //                     | Firefox3    | InternetExplorer8 |  --> Us <---
     65 // --------------------+-------------+-------------------+--------------
     66 // myIpAddress()       | IPv4/IPv6   |  IPv4             |  IPv4
     67 // dnsResolve()        | IPv4/IPv6   |  IPv4             |  IPv4
     68 // isResolvable()      | IPv4/IPv6   |  IPv4             |  IPv4
     69 // myIpAddressEx()     | N/A         |  IPv4/IPv6        |  IPv4/IPv6
     70 // dnsResolveEx()      | N/A         |  IPv4/IPv6        |  IPv4/IPv6
     71 // sortIpAddressList() | N/A         |  IPv4/IPv6        |  IPv4/IPv6
     72 // isResolvableEx()    | N/A         |  IPv4/IPv6        |  IPv4/IPv6
     73 // isInNetEx()         | N/A         |  IPv4/IPv6        |  IPv4/IPv6
     74 // -----------------+-------------+-------------------+--------------
     75 
     76 namespace net {
     77 
     78 namespace {
     79 
     80 // Pseudo-name for the PAC script.
     81 const char kPacResourceName[] = "proxy-pac-script.js";
     82 // Pseudo-name for the PAC utility script.
     83 const char kPacUtilityResourceName[] = "proxy-pac-utility-script.js";
     84 
     85 // External string wrapper so V8 can access the UTF16 string wrapped by
     86 // ProxyResolverScriptData.
     87 class V8ExternalStringFromScriptData
     88     : public v8::String::ExternalStringResource {
     89  public:
     90   explicit V8ExternalStringFromScriptData(
     91       const scoped_refptr<ProxyResolverScriptData>& script_data)
     92       : script_data_(script_data) {}
     93 
     94   virtual const uint16_t* data() const {
     95     return reinterpret_cast<const uint16*>(script_data_->utf16().data());
     96   }
     97 
     98   virtual size_t length() const {
     99     return script_data_->utf16().size();
    100   }
    101 
    102  private:
    103   const scoped_refptr<ProxyResolverScriptData> script_data_;
    104   DISALLOW_COPY_AND_ASSIGN(V8ExternalStringFromScriptData);
    105 };
    106 
    107 // External string wrapper so V8 can access a string literal.
    108 class V8ExternalASCIILiteral : public v8::String::ExternalAsciiStringResource {
    109  public:
    110   // |ascii| must be a NULL-terminated C string, and must remain valid
    111   // throughout this object's lifetime.
    112   V8ExternalASCIILiteral(const char* ascii, size_t length)
    113       : ascii_(ascii), length_(length) {
    114     DCHECK(IsStringASCII(ascii));
    115   }
    116 
    117   virtual const char* data() const {
    118     return ascii_;
    119   }
    120 
    121   virtual size_t length() const {
    122     return length_;
    123   }
    124 
    125  private:
    126   const char* ascii_;
    127   size_t length_;
    128   DISALLOW_COPY_AND_ASSIGN(V8ExternalASCIILiteral);
    129 };
    130 
    131 // When creating a v8::String from a C++ string we have two choices: create
    132 // a copy, or create a wrapper that shares the same underlying storage.
    133 // For small strings it is better to just make a copy, whereas for large
    134 // strings there are savings by sharing the storage. This number identifies
    135 // the cutoff length for when to start wrapping rather than creating copies.
    136 const size_t kMaxStringBytesForCopy = 256;
    137 
    138 // Converts a V8 String to a UTF8 std::string.
    139 std::string V8StringToUTF8(v8::Handle<v8::String> s) {
    140   std::string result;
    141   s->WriteUtf8(WriteInto(&result, s->Length() + 1));
    142   return result;
    143 }
    144 
    145 // Converts a V8 String to a UTF16 string16.
    146 string16 V8StringToUTF16(v8::Handle<v8::String> s) {
    147   int len = s->Length();
    148   string16 result;
    149   // Note that the reinterpret cast is because on Windows string16 is an alias
    150   // to wstring, and hence has character type wchar_t not uint16_t.
    151   s->Write(reinterpret_cast<uint16_t*>(WriteInto(&result, len + 1)), 0, len);
    152   return result;
    153 }
    154 
    155 // Converts an ASCII std::string to a V8 string.
    156 v8::Local<v8::String> ASCIIStringToV8String(const std::string& s) {
    157   DCHECK(IsStringASCII(s));
    158   return v8::String::New(s.data(), s.size());
    159 }
    160 
    161 // Converts a UTF16 string16 (warpped by a ProxyResolverScriptData) to a
    162 // V8 string.
    163 v8::Local<v8::String> ScriptDataToV8String(
    164     const scoped_refptr<ProxyResolverScriptData>& s) {
    165   if (s->utf16().size() * 2 <= kMaxStringBytesForCopy) {
    166     return v8::String::New(
    167         reinterpret_cast<const uint16_t*>(s->utf16().data()),
    168         s->utf16().size());
    169   }
    170   return v8::String::NewExternal(new V8ExternalStringFromScriptData(s));
    171 }
    172 
    173 // Converts an ASCII string literal to a V8 string.
    174 v8::Local<v8::String> ASCIILiteralToV8String(const char* ascii) {
    175   DCHECK(IsStringASCII(ascii));
    176   size_t length = strlen(ascii);
    177   if (length <= kMaxStringBytesForCopy)
    178     return v8::String::New(ascii, length);
    179   return v8::String::NewExternal(new V8ExternalASCIILiteral(ascii, length));
    180 }
    181 
    182 // Stringizes a V8 object by calling its toString() method. Returns true
    183 // on success. This may fail if the toString() throws an exception.
    184 bool V8ObjectToUTF16String(v8::Handle<v8::Value> object,
    185                            string16* utf16_result) {
    186   if (object.IsEmpty())
    187     return false;
    188 
    189   v8::HandleScope scope;
    190   v8::Local<v8::String> str_object = object->ToString();
    191   if (str_object.IsEmpty())
    192     return false;
    193   *utf16_result = V8StringToUTF16(str_object);
    194   return true;
    195 }
    196 
    197 // Extracts an hostname argument from |args|. On success returns true
    198 // and fills |*hostname| with the result.
    199 bool GetHostnameArgument(const v8::Arguments& args, std::string* hostname) {
    200   // The first argument should be a string.
    201   if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
    202     return false;
    203 
    204   const string16 hostname_utf16 = V8StringToUTF16(args[0]->ToString());
    205 
    206   // If the hostname is already in ASCII, simply return it as is.
    207   if (IsStringASCII(hostname_utf16)) {
    208     *hostname = UTF16ToASCII(hostname_utf16);
    209     return true;
    210   }
    211 
    212   // Otherwise try to convert it from IDN to punycode.
    213   const int kInitialBufferSize = 256;
    214   url_canon::RawCanonOutputT<char16, kInitialBufferSize> punycode_output;
    215   if (!url_canon::IDNToASCII(hostname_utf16.data(),
    216                              hostname_utf16.length(),
    217                              &punycode_output)) {
    218     return false;
    219   }
    220 
    221   // |punycode_output| should now be ASCII; convert it to a std::string.
    222   // (We could use UTF16ToASCII() instead, but that requires an extra string
    223   // copy. Since ASCII is a subset of UTF8 the following is equivalent).
    224   bool success = UTF16ToUTF8(punycode_output.data(),
    225                              punycode_output.length(),
    226                              hostname);
    227   DCHECK(success);
    228   DCHECK(IsStringASCII(*hostname));
    229   return success;
    230 }
    231 
    232 // Wrapper for passing around IP address strings and IPAddressNumber objects.
    233 struct IPAddress {
    234   IPAddress(const std::string& ip_string, const IPAddressNumber& ip_number)
    235       : string_value(ip_string),
    236         ip_address_number(ip_number) {
    237   }
    238 
    239   // Used for sorting IP addresses in ascending order in SortIpAddressList().
    240   // IP6 addresses are placed ahead of IPv4 addresses.
    241   bool operator<(const IPAddress& rhs) const {
    242     const IPAddressNumber& ip1 = this->ip_address_number;
    243     const IPAddressNumber& ip2 = rhs.ip_address_number;
    244     if (ip1.size() != ip2.size())
    245       return ip1.size() > ip2.size();  // IPv6 before IPv4.
    246     DCHECK(ip1.size() == ip2.size());
    247     return memcmp(&ip1[0], &ip2[0], ip1.size()) < 0;  // Ascending order.
    248   }
    249 
    250   std::string string_value;
    251   IPAddressNumber ip_address_number;
    252 };
    253 
    254 // Handler for "sortIpAddressList(IpAddressList)". |ip_address_list| is a
    255 // semi-colon delimited string containing IP addresses.
    256 // |sorted_ip_address_list| is the resulting list of sorted semi-colon delimited
    257 // IP addresses or an empty string if unable to sort the IP address list.
    258 // Returns 'true' if the sorting was successful, and 'false' if the input was an
    259 // empty string, a string of separators (";" in this case), or if any of the IP
    260 // addresses in the input list failed to parse.
    261 bool SortIpAddressList(const std::string& ip_address_list,
    262                        std::string* sorted_ip_address_list) {
    263   sorted_ip_address_list->clear();
    264 
    265   // Strip all whitespace (mimics IE behavior).
    266   std::string cleaned_ip_address_list;
    267   RemoveChars(ip_address_list, " \t", &cleaned_ip_address_list);
    268   if (cleaned_ip_address_list.empty())
    269     return false;
    270 
    271   // Split-up IP addresses and store them in a vector.
    272   std::vector<IPAddress> ip_vector;
    273   IPAddressNumber ip_num;
    274   StringTokenizer str_tok(cleaned_ip_address_list, ";");
    275   while (str_tok.GetNext()) {
    276     if (!ParseIPLiteralToNumber(str_tok.token(), &ip_num))
    277       return false;
    278     ip_vector.push_back(IPAddress(str_tok.token(), ip_num));
    279   }
    280 
    281   if (ip_vector.empty())  // Can happen if we have something like
    282     return false;         // sortIpAddressList(";") or sortIpAddressList("; ;")
    283 
    284   DCHECK(!ip_vector.empty());
    285 
    286   // Sort lists according to ascending numeric value.
    287   if (ip_vector.size() > 1)
    288     std::stable_sort(ip_vector.begin(), ip_vector.end());
    289 
    290   // Return a semi-colon delimited list of sorted addresses (IPv6 followed by
    291   // IPv4).
    292   for (size_t i = 0; i < ip_vector.size(); ++i) {
    293     if (i > 0)
    294       *sorted_ip_address_list += ";";
    295     *sorted_ip_address_list += ip_vector[i].string_value;
    296   }
    297   return true;
    298 }
    299 
    300 // Handler for "isInNetEx(ip_address, ip_prefix)". |ip_address| is a string
    301 // containing an IPv4/IPv6 address, and |ip_prefix| is a string containg a
    302 // slash-delimited IP prefix with the top 'n' bits specified in the bit
    303 // field. This returns 'true' if the address is in the same subnet, and
    304 // 'false' otherwise. Also returns 'false' if the prefix is in an incorrect
    305 // format, or if an address and prefix of different types are used (e.g. IPv6
    306 // address and IPv4 prefix).
    307 bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) {
    308   IPAddressNumber address;
    309   if (!ParseIPLiteralToNumber(ip_address, &address))
    310     return false;
    311 
    312   IPAddressNumber prefix;
    313   size_t prefix_length_in_bits;
    314   if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits))
    315     return false;
    316 
    317   // Both |address| and |prefix| must be of the same type (IPv4 or IPv6).
    318   if (address.size() != prefix.size())
    319     return false;
    320 
    321   DCHECK((address.size() == 4 && prefix.size() == 4) ||
    322          (address.size() == 16 && prefix.size() == 16));
    323 
    324   return IPNumberMatchesPrefix(address, prefix, prefix_length_in_bits);
    325 }
    326 
    327 }  // namespace
    328 
    329 // ProxyResolverV8::Context ---------------------------------------------------
    330 
    331 class ProxyResolverV8::Context {
    332  public:
    333   explicit Context(ProxyResolverJSBindings* js_bindings)
    334       : js_bindings_(js_bindings) {
    335     DCHECK(js_bindings != NULL);
    336   }
    337 
    338   ~Context() {
    339     v8::Locker locked;
    340 
    341     v8_this_.Dispose();
    342     v8_context_.Dispose();
    343 
    344     // Run the V8 garbage collector. We do this to be sure the
    345     // ExternalStringResource objects we allocated get properly disposed.
    346     // Otherwise when running the unit-tests they may get leaked.
    347     // See crbug.com/48145.
    348     PurgeMemory();
    349   }
    350 
    351   int ResolveProxy(const GURL& query_url, ProxyInfo* results) {
    352     v8::Locker locked;
    353     v8::HandleScope scope;
    354 
    355     v8::Context::Scope function_scope(v8_context_);
    356 
    357     v8::Local<v8::Value> function;
    358     if (!GetFindProxyForURL(&function)) {
    359       js_bindings_->OnError(
    360           -1, ASCIIToUTF16("FindProxyForURL() is undefined."));
    361       return ERR_PAC_SCRIPT_FAILED;
    362     }
    363 
    364     v8::Handle<v8::Value> argv[] = {
    365       ASCIIStringToV8String(query_url.spec()),
    366       ASCIIStringToV8String(query_url.host()),
    367     };
    368 
    369     v8::TryCatch try_catch;
    370     v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
    371         v8_context_->Global(), arraysize(argv), argv);
    372 
    373     if (try_catch.HasCaught()) {
    374       HandleError(try_catch.Message());
    375       return ERR_PAC_SCRIPT_FAILED;
    376     }
    377 
    378     if (!ret->IsString()) {
    379       js_bindings_->OnError(
    380           -1, ASCIIToUTF16("FindProxyForURL() did not return a string."));
    381       return ERR_PAC_SCRIPT_FAILED;
    382     }
    383 
    384     string16 ret_str = V8StringToUTF16(ret->ToString());
    385 
    386     if (!IsStringASCII(ret_str)) {
    387       // TODO(eroman): Rather than failing when a wide string is returned, we
    388       //               could extend the parsing to handle IDNA hostnames by
    389       //               converting them to ASCII punycode.
    390       //               crbug.com/47234
    391       string16 error_message =
    392           ASCIIToUTF16("FindProxyForURL() returned a non-ASCII string "
    393                        "(crbug.com/47234): ") + ret_str;
    394       js_bindings_->OnError(-1, error_message);
    395       return ERR_PAC_SCRIPT_FAILED;
    396     }
    397 
    398     results->UsePacString(UTF16ToASCII(ret_str));
    399     return OK;
    400   }
    401 
    402   int InitV8(const scoped_refptr<ProxyResolverScriptData>& pac_script) {
    403     v8::Locker locked;
    404     v8::HandleScope scope;
    405 
    406     v8_this_ = v8::Persistent<v8::External>::New(v8::External::New(this));
    407     v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
    408 
    409     // Attach the javascript bindings.
    410     v8::Local<v8::FunctionTemplate> alert_template =
    411         v8::FunctionTemplate::New(&AlertCallback, v8_this_);
    412     global_template->Set(ASCIILiteralToV8String("alert"), alert_template);
    413 
    414     v8::Local<v8::FunctionTemplate> my_ip_address_template =
    415         v8::FunctionTemplate::New(&MyIpAddressCallback, v8_this_);
    416     global_template->Set(ASCIILiteralToV8String("myIpAddress"),
    417         my_ip_address_template);
    418 
    419     v8::Local<v8::FunctionTemplate> dns_resolve_template =
    420         v8::FunctionTemplate::New(&DnsResolveCallback, v8_this_);
    421     global_template->Set(ASCIILiteralToV8String("dnsResolve"),
    422         dns_resolve_template);
    423 
    424     // Microsoft's PAC extensions:
    425 
    426     v8::Local<v8::FunctionTemplate> dns_resolve_ex_template =
    427         v8::FunctionTemplate::New(&DnsResolveExCallback, v8_this_);
    428     global_template->Set(ASCIILiteralToV8String("dnsResolveEx"),
    429                          dns_resolve_ex_template);
    430 
    431     v8::Local<v8::FunctionTemplate> my_ip_address_ex_template =
    432         v8::FunctionTemplate::New(&MyIpAddressExCallback, v8_this_);
    433     global_template->Set(ASCIILiteralToV8String("myIpAddressEx"),
    434                          my_ip_address_ex_template);
    435 
    436     v8::Local<v8::FunctionTemplate> sort_ip_address_list_template =
    437         v8::FunctionTemplate::New(&SortIpAddressListCallback, v8_this_);
    438     global_template->Set(ASCIILiteralToV8String("sortIpAddressList"),
    439                          sort_ip_address_list_template);
    440 
    441     v8::Local<v8::FunctionTemplate> is_in_net_ex_template =
    442         v8::FunctionTemplate::New(&IsInNetExCallback, v8_this_);
    443     global_template->Set(ASCIILiteralToV8String("isInNetEx"),
    444                          is_in_net_ex_template);
    445 
    446     v8_context_ = v8::Context::New(NULL, global_template);
    447 
    448     v8::Context::Scope ctx(v8_context_);
    449 
    450     // Add the PAC utility functions to the environment.
    451     // (This script should never fail, as it is a string literal!)
    452     // Note that the two string literals are concatenated.
    453     int rv = RunScript(
    454         ASCIILiteralToV8String(
    455             PROXY_RESOLVER_SCRIPT
    456             PROXY_RESOLVER_SCRIPT_EX),
    457         kPacUtilityResourceName);
    458     if (rv != OK) {
    459       NOTREACHED();
    460       return rv;
    461     }
    462 
    463     // Add the user's PAC code to the environment.
    464     rv = RunScript(ScriptDataToV8String(pac_script), kPacResourceName);
    465     if (rv != OK)
    466       return rv;
    467 
    468     // At a minimum, the FindProxyForURL() function must be defined for this
    469     // to be a legitimiate PAC script.
    470     v8::Local<v8::Value> function;
    471     if (!GetFindProxyForURL(&function))
    472       return ERR_PAC_SCRIPT_FAILED;
    473 
    474     return OK;
    475   }
    476 
    477   void SetCurrentRequestContext(ProxyResolverRequestContext* context) {
    478     js_bindings_->set_current_request_context(context);
    479   }
    480 
    481   void PurgeMemory() {
    482     v8::Locker locked;
    483     // Repeatedly call the V8 idle notification until it returns true ("nothing
    484     // more to free").  Note that it makes more sense to do this than to
    485     // implement a new "delete everything" pass because object references make
    486     // it difficult to free everything possible in just one pass.
    487     while (!v8::V8::IdleNotification())
    488       ;
    489   }
    490 
    491  private:
    492   bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
    493     *function = v8_context_->Global()->Get(
    494         ASCIILiteralToV8String("FindProxyForURL"));
    495     return (*function)->IsFunction();
    496   }
    497 
    498   // Handle an exception thrown by V8.
    499   void HandleError(v8::Handle<v8::Message> message) {
    500     if (message.IsEmpty())
    501       return;
    502 
    503     // Otherwise dispatch to the bindings.
    504     int line_number = message->GetLineNumber();
    505     string16 error_message;
    506     V8ObjectToUTF16String(message->Get(), &error_message);
    507     js_bindings_->OnError(line_number, error_message);
    508   }
    509 
    510   // Compiles and runs |script| in the current V8 context.
    511   // Returns OK on success, otherwise an error code.
    512   int RunScript(v8::Handle<v8::String> script, const char* script_name) {
    513     v8::TryCatch try_catch;
    514 
    515     // Compile the script.
    516     v8::ScriptOrigin origin =
    517         v8::ScriptOrigin(ASCIILiteralToV8String(script_name));
    518     v8::Local<v8::Script> code = v8::Script::Compile(script, &origin);
    519 
    520     // Execute.
    521     if (!code.IsEmpty())
    522       code->Run();
    523 
    524     // Check for errors.
    525     if (try_catch.HasCaught()) {
    526       HandleError(try_catch.Message());
    527       return ERR_PAC_SCRIPT_FAILED;
    528     }
    529 
    530     return OK;
    531   }
    532 
    533   // V8 callback for when "alert()" is invoked by the PAC script.
    534   static v8::Handle<v8::Value> AlertCallback(const v8::Arguments& args) {
    535     Context* context =
    536         static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
    537 
    538     // Like firefox we assume "undefined" if no argument was specified, and
    539     // disregard any arguments beyond the first.
    540     string16 message;
    541     if (args.Length() == 0) {
    542       message = ASCIIToUTF16("undefined");
    543     } else {
    544       if (!V8ObjectToUTF16String(args[0], &message))
    545         return v8::Undefined();  // toString() threw an exception.
    546     }
    547 
    548     context->js_bindings_->Alert(message);
    549     return v8::Undefined();
    550   }
    551 
    552   // V8 callback for when "myIpAddress()" is invoked by the PAC script.
    553   static v8::Handle<v8::Value> MyIpAddressCallback(const v8::Arguments& args) {
    554     Context* context =
    555         static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
    556 
    557     std::string result;
    558     bool success;
    559 
    560     {
    561       v8::Unlocker unlocker;
    562 
    563       // We shouldn't be called with any arguments, but will not complain if
    564       // we are.
    565       success = context->js_bindings_->MyIpAddress(&result);
    566     }
    567 
    568     if (!success)
    569       return ASCIILiteralToV8String("127.0.0.1");
    570     return ASCIIStringToV8String(result);
    571   }
    572 
    573   // V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
    574   static v8::Handle<v8::Value> MyIpAddressExCallback(
    575       const v8::Arguments& args) {
    576     Context* context =
    577         static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
    578 
    579     std::string ip_address_list;
    580     bool success;
    581 
    582     {
    583       v8::Unlocker unlocker;
    584 
    585       // We shouldn't be called with any arguments, but will not complain if
    586       // we are.
    587       success = context->js_bindings_->MyIpAddressEx(&ip_address_list);
    588     }
    589 
    590     if (!success)
    591       ip_address_list = std::string();
    592     return ASCIIStringToV8String(ip_address_list);
    593   }
    594 
    595   // V8 callback for when "dnsResolve()" is invoked by the PAC script.
    596   static v8::Handle<v8::Value> DnsResolveCallback(const v8::Arguments& args) {
    597     Context* context =
    598         static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
    599 
    600     // We need at least one string argument.
    601     std::string hostname;
    602     if (!GetHostnameArgument(args, &hostname))
    603       return v8::Null();
    604 
    605     std::string ip_address;
    606     bool success;
    607 
    608     {
    609       v8::Unlocker unlocker;
    610       success = context->js_bindings_->DnsResolve(hostname, &ip_address);
    611     }
    612 
    613     return success ? ASCIIStringToV8String(ip_address) : v8::Null();
    614   }
    615 
    616   // V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
    617   static v8::Handle<v8::Value> DnsResolveExCallback(const v8::Arguments& args) {
    618     Context* context =
    619         static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
    620 
    621     // We need at least one string argument.
    622     std::string hostname;
    623     if (!GetHostnameArgument(args, &hostname))
    624       return v8::Undefined();
    625 
    626     std::string ip_address_list;
    627     bool success;
    628 
    629     {
    630       v8::Unlocker unlocker;
    631       success = context->js_bindings_->DnsResolveEx(hostname, &ip_address_list);
    632     }
    633 
    634     if (!success)
    635       ip_address_list = std::string();
    636 
    637     return ASCIIStringToV8String(ip_address_list);
    638   }
    639 
    640   // V8 callback for when "sortIpAddressList()" is invoked by the PAC script.
    641   static v8::Handle<v8::Value> SortIpAddressListCallback(
    642       const v8::Arguments& args) {
    643     // We need at least one string argument.
    644     if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
    645       return v8::Null();
    646 
    647     std::string ip_address_list = V8StringToUTF8(args[0]->ToString());
    648     if (!IsStringASCII(ip_address_list))
    649       return v8::Null();
    650     std::string sorted_ip_address_list;
    651     bool success = SortIpAddressList(ip_address_list, &sorted_ip_address_list);
    652     if (!success)
    653       return v8::False();
    654     return ASCIIStringToV8String(sorted_ip_address_list);
    655   }
    656 
    657   // V8 callback for when "isInNetEx()" is invoked by the PAC script.
    658   static v8::Handle<v8::Value> IsInNetExCallback(const v8::Arguments& args) {
    659     // We need at least 2 string arguments.
    660     if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() ||
    661         args[1].IsEmpty() || !args[1]->IsString())
    662       return v8::Null();
    663 
    664     std::string ip_address = V8StringToUTF8(args[0]->ToString());
    665     if (!IsStringASCII(ip_address))
    666       return v8::False();
    667     std::string ip_prefix = V8StringToUTF8(args[1]->ToString());
    668     if (!IsStringASCII(ip_prefix))
    669       return v8::False();
    670     return IsInNetEx(ip_address, ip_prefix) ? v8::True() : v8::False();
    671   }
    672 
    673   ProxyResolverJSBindings* js_bindings_;
    674   v8::Persistent<v8::External> v8_this_;
    675   v8::Persistent<v8::Context> v8_context_;
    676 };
    677 
    678 // ProxyResolverV8 ------------------------------------------------------------
    679 
    680 ProxyResolverV8::ProxyResolverV8(
    681     ProxyResolverJSBindings* custom_js_bindings)
    682     : ProxyResolver(true /*expects_pac_bytes*/),
    683       js_bindings_(custom_js_bindings) {
    684 }
    685 
    686 ProxyResolverV8::~ProxyResolverV8() {}
    687 
    688 int ProxyResolverV8::GetProxyForURL(const GURL& query_url,
    689                                     ProxyInfo* results,
    690                                     CompletionCallback* /*callback*/,
    691                                     RequestHandle* /*request*/,
    692                                     const BoundNetLog& net_log) {
    693   // If the V8 instance has not been initialized (either because
    694   // SetPacScript() wasn't called yet, or because it failed.
    695   if (!context_.get())
    696     return ERR_FAILED;
    697 
    698   // Associate some short-lived context with this request. This context will be
    699   // available to any of the javascript "bindings" that are subsequently invoked
    700   // from the javascript.
    701   //
    702   // In particular, we create a HostCache that is aggressive about caching
    703   // failed DNS resolves.
    704   HostCache host_cache(
    705       50,
    706       base::TimeDelta::FromMinutes(5),
    707       base::TimeDelta::FromMinutes(5));
    708 
    709   ProxyResolverRequestContext request_context(&net_log, &host_cache);
    710 
    711   // Otherwise call into V8.
    712   context_->SetCurrentRequestContext(&request_context);
    713   int rv = context_->ResolveProxy(query_url, results);
    714   context_->SetCurrentRequestContext(NULL);
    715 
    716   return rv;
    717 }
    718 
    719 void ProxyResolverV8::CancelRequest(RequestHandle request) {
    720   // This is a synchronous ProxyResolver; no possibility for async requests.
    721   NOTREACHED();
    722 }
    723 
    724 void ProxyResolverV8::CancelSetPacScript() {
    725   NOTREACHED();
    726 }
    727 
    728 void ProxyResolverV8::PurgeMemory() {
    729   context_->PurgeMemory();
    730 }
    731 
    732 void ProxyResolverV8::Shutdown() {
    733   js_bindings_->Shutdown();
    734 }
    735 
    736 int ProxyResolverV8::SetPacScript(
    737     const scoped_refptr<ProxyResolverScriptData>& script_data,
    738     CompletionCallback* /*callback*/) {
    739   DCHECK(script_data.get());
    740   context_.reset();
    741   if (script_data->utf16().empty())
    742     return ERR_PAC_SCRIPT_FAILED;
    743 
    744   // Try parsing the PAC script.
    745   scoped_ptr<Context> context(new Context(js_bindings_.get()));
    746   int rv = context->InitV8(script_data);
    747   if (rv == OK)
    748     context_.reset(context.release());
    749   return rv;
    750 }
    751 
    752 }  // namespace net
    753