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