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 76namespace net { 77 78namespace { 79 80// Pseudo-name for the PAC script. 81const char kPacResourceName[] = "proxy-pac-script.js"; 82// Pseudo-name for the PAC utility script. 83const char kPacUtilityResourceName[] = "proxy-pac-utility-script.js"; 84 85// External string wrapper so V8 can access the UTF16 string wrapped by 86// ProxyResolverScriptData. 87class 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. 108class 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. 136const size_t kMaxStringBytesForCopy = 256; 137 138// Converts a V8 String to a UTF8 std::string. 139std::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. 146string16 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. 156v8::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. 163v8::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. 174v8::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. 184bool 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. 199bool 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. 233struct 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. 261bool 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). 307bool 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 331class 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 680ProxyResolverV8::ProxyResolverV8( 681 ProxyResolverJSBindings* custom_js_bindings) 682 : ProxyResolver(true /*expects_pac_bytes*/), 683 js_bindings_(custom_js_bindings) { 684} 685 686ProxyResolverV8::~ProxyResolverV8() {} 687 688int 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 719void ProxyResolverV8::CancelRequest(RequestHandle request) { 720 // This is a synchronous ProxyResolver; no possibility for async requests. 721 NOTREACHED(); 722} 723 724void ProxyResolverV8::CancelSetPacScript() { 725 NOTREACHED(); 726} 727 728void ProxyResolverV8::PurgeMemory() { 729 context_->PurgeMemory(); 730} 731 732void ProxyResolverV8::Shutdown() { 733 js_bindings_->Shutdown(); 734} 735 736int 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