blob: 80a1937d30f387b0ae23d2bbe39d18176f3713e1 [file] [log] [blame]
[email protected]9e743cd2010-03-16 07:03:531// 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.
[email protected]943c8082009-02-23 19:10:454
[email protected]a37b8bb2010-08-26 23:31:005#include <algorithm>
6#include <cstdio>
7
[email protected]943c8082009-02-23 19:10:458#include "net/proxy/proxy_resolver_v8.h"
9
[email protected]9b9ae9552010-07-01 22:20:5010#include "base/basictypes.h"
[email protected]50d7d7282009-03-02 21:45:1811#include "base/logging.h"
[email protected]a37b8bb2010-08-26 23:31:0012#include "base/string_tokenizer.h"
[email protected]7dc52f22009-03-02 22:37:1813#include "base/string_util.h"
[email protected]0238de42010-06-23 18:04:0114#include "base/utf_string_conversions.h"
[email protected]7dc52f22009-03-02 22:37:1815#include "googleurl/src/gurl.h"
[email protected]0238de42010-06-23 18:04:0116#include "googleurl/src/url_canon.h"
[email protected]59872eb2010-06-22 18:56:1217#include "net/base/host_cache.h"
[email protected]943c8082009-02-23 19:10:4518#include "net/base/net_errors.h"
[email protected]9e743cd2010-03-16 07:03:5319#include "net/base/net_log.h"
[email protected]a37b8bb2010-08-26 23:31:0020#include "net/base/net_util.h"
[email protected]7dc52f22009-03-02 22:37:1821#include "net/proxy/proxy_info.h"
[email protected]90ae1fb2009-08-02 21:07:1222#include "net/proxy/proxy_resolver_js_bindings.h"
[email protected]59872eb2010-06-22 18:56:1223#include "net/proxy/proxy_resolver_request_context.h"
[email protected]943c8082009-02-23 19:10:4524#include "net/proxy/proxy_resolver_script.h"
25#include "v8/include/v8.h"
26
[email protected]21f20c892009-10-26 23:51:2827// 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.
[email protected]a37b8bb2010-08-26 23:31:0034// - myIpAddressEx()
35// - dnsResolveEx()
36// - isResolvableEx()
37// - isInNetEx()
38// - sortIpAddressList()
[email protected]21f20c892009-10-26 23:51:2839//
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//
[email protected]a37b8bb2010-08-26 23:31:0045// --------------------+-------------+-------------------+--------------
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// --------------------+-------------+-------------------+--------------
[email protected]21f20c892009-10-26 23:51:2855//
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//
[email protected]a37b8bb2010-08-26 23:31:0063// --------------------+-------------+-------------------+--------------
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
[email protected]21f20c892009-10-26 23:51:2874// -----------------+-------------+-------------------+--------------
75
[email protected]943c8082009-02-23 19:10:4576namespace net {
77
78namespace {
79
[email protected]50d7d7282009-03-02 21:45:1880// Pseudo-name for the PAC script.
81const char kPacResourceName[] = "proxy-pac-script.js";
[email protected]c945a462009-09-24 02:13:5782// Pseudo-name for the PAC utility script.
83const char kPacUtilityResourceName[] = "proxy-pac-utility-script.js";
[email protected]50d7d7282009-03-02 21:45:1884
[email protected]24476402010-07-20 20:55:1785// External string wrapper so V8 can access the UTF16 string wrapped by
86// ProxyResolverScriptData.
87class V8ExternalStringFromScriptData
88 : public v8::String::ExternalStringResource {
[email protected]9b9ae9552010-07-01 22:20:5089 public:
[email protected]24476402010-07-20 20:55:1790 explicit V8ExternalStringFromScriptData(
91 const scoped_refptr<ProxyResolverScriptData>& script_data)
92 : script_data_(script_data) {}
[email protected]9b9ae9552010-07-01 22:20:5093
94 virtual const uint16_t* data() const {
[email protected]24476402010-07-20 20:55:1795 return reinterpret_cast<const uint16*>(script_data_->utf16().data());
[email protected]9b9ae9552010-07-01 22:20:5096 }
97
98 virtual size_t length() const {
[email protected]24476402010-07-20 20:55:1799 return script_data_->utf16().size();
[email protected]9b9ae9552010-07-01 22:20:50100 }
101
102 private:
[email protected]24476402010-07-20 20:55:17103 const scoped_refptr<ProxyResolverScriptData> script_data_;
104 DISALLOW_COPY_AND_ASSIGN(V8ExternalStringFromScriptData);
[email protected]9b9ae9552010-07-01 22:20:50105};
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
[email protected]9b9ae9552010-07-01 22:20:50131// 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.
[email protected]4a74b0a2010-07-02 03:40:05136const size_t kMaxStringBytesForCopy = 256;
[email protected]9b9ae9552010-07-01 22:20:50137
[email protected]a37b8bb2010-08-26 23:31:00138// 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
[email protected]51e413c2010-06-23 00:15:58145// 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);
[email protected]943c8082009-02-23 19:10:45152 return result;
153}
154
[email protected]9b9ae9552010-07-01 22:20:50155// Converts an ASCII std::string to a V8 string.
156v8::Local<v8::String> ASCIIStringToV8String(const std::string& s) {
157 DCHECK(IsStringASCII(s));
[email protected]24476402010-07-20 20:55:17158 return v8::String::New(s.data(), s.size());
[email protected]9b9ae9552010-07-01 22:20:50159}
160
[email protected]24476402010-07-20 20:55:17161// 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) {
[email protected]9b9ae9552010-07-01 22:20:50166 return v8::String::New(
[email protected]24476402010-07-20 20:55:17167 reinterpret_cast<const uint16_t*>(s->utf16().data()),
168 s->utf16().size());
[email protected]9b9ae9552010-07-01 22:20:50169 }
[email protected]24476402010-07-20 20:55:17170 return v8::String::NewExternal(new V8ExternalStringFromScriptData(s));
[email protected]9b9ae9552010-07-01 22:20:50171}
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));
[email protected]943c8082009-02-23 19:10:45180}
181
[email protected]51e413c2010-06-23 00:15:58182// Stringizes a V8 object by calling its toString() method. Returns true
[email protected]50d7d7282009-03-02 21:45:18183// on success. This may fail if the toString() throws an exception.
[email protected]51e413c2010-06-23 00:15:58184bool V8ObjectToUTF16String(v8::Handle<v8::Value> object,
185 string16* utf16_result) {
[email protected]50d7d7282009-03-02 21:45:18186 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;
[email protected]51e413c2010-06-23 00:15:58193 *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
[email protected]0238de42010-06-23 18:04:01206 // 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)) {
[email protected]51e413c2010-06-23 00:15:58218 return false;
[email protected]0238de42010-06-23 18:04:01219 }
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;
[email protected]50d7d7282009-03-02 21:45:18230}
231
[email protected]a37b8bb2010-08-26 23:31:00232// 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
[email protected]943c8082009-02-23 19:10:45327} // namespace
328
329// ProxyResolverV8::Context ---------------------------------------------------
330
331class ProxyResolverV8::Context {
332 public:
[email protected]620f5712009-08-04 22:43:12333 explicit Context(ProxyResolverJSBindings* js_bindings)
[email protected]5a1d7ca2010-04-28 20:12:27334 : js_bindings_(js_bindings) {
[email protected]50d7d7282009-03-02 21:45:18335 DCHECK(js_bindings != NULL);
[email protected]943c8082009-02-23 19:10:45336 }
337
338 ~Context() {
339 v8::Locker locked;
[email protected]943c8082009-02-23 19:10:45340
[email protected]50d7d7282009-03-02 21:45:18341 v8_this_.Dispose();
[email protected]943c8082009-02-23 19:10:45342 v8_context_.Dispose();
[email protected]4a74b0a2010-07-02 03:40:05343
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();
[email protected]943c8082009-02-23 19:10:45349 }
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
[email protected]620f5712009-08-04 22:43:12357 v8::Local<v8::Value> function;
358 if (!GetFindProxyForURL(&function)) {
[email protected]51e413c2010-06-23 00:15:58359 js_bindings_->OnError(
360 -1, ASCIIToUTF16("FindProxyForURL() is undefined."));
[email protected]943c8082009-02-23 19:10:45361 return ERR_PAC_SCRIPT_FAILED;
[email protected]50d7d7282009-03-02 21:45:18362 }
[email protected]943c8082009-02-23 19:10:45363
364 v8::Handle<v8::Value> argv[] = {
[email protected]9b9ae9552010-07-01 22:20:50365 ASCIIStringToV8String(query_url.spec()),
366 ASCIIStringToV8String(query_url.host()),
[email protected]943c8082009-02-23 19:10:45367 };
368
[email protected]50d7d7282009-03-02 21:45:18369 v8::TryCatch try_catch;
[email protected]943c8082009-02-23 19:10:45370 v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
371 v8_context_->Global(), arraysize(argv), argv);
372
[email protected]50d7d7282009-03-02 21:45:18373 if (try_catch.HasCaught()) {
374 HandleError(try_catch.Message());
[email protected]943c8082009-02-23 19:10:45375 return ERR_PAC_SCRIPT_FAILED;
[email protected]50d7d7282009-03-02 21:45:18376 }
[email protected]943c8082009-02-23 19:10:45377
[email protected]50d7d7282009-03-02 21:45:18378 if (!ret->IsString()) {
[email protected]51e413c2010-06-23 00:15:58379 js_bindings_->OnError(
380 -1, ASCIIToUTF16("FindProxyForURL() did not return a string."));
[email protected]943c8082009-02-23 19:10:45381 return ERR_PAC_SCRIPT_FAILED;
[email protected]50d7d7282009-03-02 21:45:18382 }
[email protected]943c8082009-02-23 19:10:45383
[email protected]51e413c2010-06-23 00:15:58384 string16 ret_str = V8StringToUTF16(ret->ToString());
[email protected]943c8082009-02-23 19:10:45385
[email protected]51e413c2010-06-23 00:15:58386 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 }
[email protected]943c8082009-02-23 19:10:45397
[email protected]51e413c2010-06-23 00:15:58398 results->UsePacString(UTF16ToASCII(ret_str));
[email protected]943c8082009-02-23 19:10:45399 return OK;
400 }
401
[email protected]24476402010-07-20 20:55:17402 int InitV8(const scoped_refptr<ProxyResolverScriptData>& pac_script) {
[email protected]943c8082009-02-23 19:10:45403 v8::Locker locked;
404 v8::HandleScope scope;
405
[email protected]50d7d7282009-03-02 21:45:18406 v8_this_ = v8::Persistent<v8::External>::New(v8::External::New(this));
[email protected]943c8082009-02-23 19:10:45407 v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
408
[email protected]50d7d7282009-03-02 21:45:18409 // Attach the javascript bindings.
410 v8::Local<v8::FunctionTemplate> alert_template =
411 v8::FunctionTemplate::New(&AlertCallback, v8_this_);
[email protected]9b9ae9552010-07-01 22:20:50412 global_template->Set(ASCIILiteralToV8String("alert"), alert_template);
[email protected]50d7d7282009-03-02 21:45:18413
414 v8::Local<v8::FunctionTemplate> my_ip_address_template =
415 v8::FunctionTemplate::New(&MyIpAddressCallback, v8_this_);
[email protected]9b9ae9552010-07-01 22:20:50416 global_template->Set(ASCIILiteralToV8String("myIpAddress"),
[email protected]50d7d7282009-03-02 21:45:18417 my_ip_address_template);
418
419 v8::Local<v8::FunctionTemplate> dns_resolve_template =
420 v8::FunctionTemplate::New(&DnsResolveCallback, v8_this_);
[email protected]9b9ae9552010-07-01 22:20:50421 global_template->Set(ASCIILiteralToV8String("dnsResolve"),
[email protected]50d7d7282009-03-02 21:45:18422 dns_resolve_template);
423
[email protected]a37b8bb2010-08-26 23:31:00424 // Microsoft's PAC extensions:
[email protected]21f20c892009-10-26 23:51:28425
426 v8::Local<v8::FunctionTemplate> dns_resolve_ex_template =
427 v8::FunctionTemplate::New(&DnsResolveExCallback, v8_this_);
[email protected]9b9ae9552010-07-01 22:20:50428 global_template->Set(ASCIILiteralToV8String("dnsResolveEx"),
[email protected]21f20c892009-10-26 23:51:28429 dns_resolve_ex_template);
430
431 v8::Local<v8::FunctionTemplate> my_ip_address_ex_template =
432 v8::FunctionTemplate::New(&MyIpAddressExCallback, v8_this_);
[email protected]9b9ae9552010-07-01 22:20:50433 global_template->Set(ASCIILiteralToV8String("myIpAddressEx"),
[email protected]21f20c892009-10-26 23:51:28434 my_ip_address_ex_template);
435
[email protected]a37b8bb2010-08-26 23:31:00436 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
[email protected]943c8082009-02-23 19:10:45446 v8_context_ = v8::Context::New(NULL, global_template);
[email protected]50d7d7282009-03-02 21:45:18447
[email protected]943c8082009-02-23 19:10:45448 v8::Context::Scope ctx(v8_context_);
449
[email protected]c945a462009-09-24 02:13:57450 // Add the PAC utility functions to the environment.
451 // (This script should never fail, as it is a string literal!)
[email protected]21f20c892009-10-26 23:51:28452 // Note that the two string literals are concatenated.
[email protected]9b9ae9552010-07-01 22:20:50453 int rv = RunScript(
454 ASCIILiteralToV8String(
455 PROXY_RESOLVER_SCRIPT
456 PROXY_RESOLVER_SCRIPT_EX),
457 kPacUtilityResourceName);
[email protected]c945a462009-09-24 02:13:57458 if (rv != OK) {
459 NOTREACHED();
460 return rv;
[email protected]620f5712009-08-04 22:43:12461 }
462
[email protected]c945a462009-09-24 02:13:57463 // Add the user's PAC code to the environment.
[email protected]24476402010-07-20 20:55:17464 rv = RunScript(ScriptDataToV8String(pac_script), kPacResourceName);
[email protected]c945a462009-09-24 02:13:57465 if (rv != OK)
466 return rv;
467
[email protected]620f5712009-08-04 22:43:12468 // 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
[email protected]59872eb2010-06-22 18:56:12477 void SetCurrentRequestContext(ProxyResolverRequestContext* context) {
478 js_bindings_->set_current_request_context(context);
[email protected]52ae80c2009-09-10 21:27:00479 }
480
[email protected]0cf348be2009-10-13 01:39:58481 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
[email protected]620f5712009-08-04 22:43:12491 private:
492 bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
[email protected]9b9ae9552010-07-01 22:20:50493 *function = v8_context_->Global()->Get(
494 ASCIILiteralToV8String("FindProxyForURL"));
[email protected]620f5712009-08-04 22:43:12495 return (*function)->IsFunction();
[email protected]943c8082009-02-23 19:10:45496 }
497
[email protected]50d7d7282009-03-02 21:45:18498 // 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();
[email protected]51e413c2010-06-23 00:15:58505 string16 error_message;
506 V8ObjectToUTF16String(message->Get(), &error_message);
[email protected]50d7d7282009-03-02 21:45:18507 js_bindings_->OnError(line_number, error_message);
508 }
509
[email protected]9b9ae9552010-07-01 22:20:50510 // Compiles and runs |script| in the current V8 context.
[email protected]c945a462009-09-24 02:13:57511 // Returns OK on success, otherwise an error code.
[email protected]9b9ae9552010-07-01 22:20:50512 int RunScript(v8::Handle<v8::String> script, const char* script_name) {
[email protected]c945a462009-09-24 02:13:57513 v8::TryCatch try_catch;
514
515 // Compile the script.
[email protected]9b9ae9552010-07-01 22:20:50516 v8::ScriptOrigin origin =
517 v8::ScriptOrigin(ASCIILiteralToV8String(script_name));
518 v8::Local<v8::Script> code = v8::Script::Compile(script, &origin);
[email protected]c945a462009-09-24 02:13:57519
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
[email protected]50d7d7282009-03-02 21:45:18533 // 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.
[email protected]51e413c2010-06-23 00:15:58540 string16 message;
[email protected]50d7d7282009-03-02 21:45:18541 if (args.Length() == 0) {
[email protected]51e413c2010-06-23 00:15:58542 message = ASCIIToUTF16("undefined");
[email protected]50d7d7282009-03-02 21:45:18543 } else {
[email protected]51e413c2010-06-23 00:15:58544 if (!V8ObjectToUTF16String(args[0], &message))
[email protected]50d7d7282009-03-02 21:45:18545 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
[email protected]3908c022010-05-13 20:13:38557 std::string result;
[email protected]51e413c2010-06-23 00:15:58558 bool success;
[email protected]52ae80c2009-09-10 21:27:00559
[email protected]3908c022010-05-13 20:13:38560 {
561 v8::Unlocker unlocker;
[email protected]52ae80c2009-09-10 21:27:00562
[email protected]3908c022010-05-13 20:13:38563 // We shouldn't be called with any arguments, but will not complain if
564 // we are.
[email protected]51e413c2010-06-23 00:15:58565 success = context->js_bindings_->MyIpAddress(&result);
[email protected]3908c022010-05-13 20:13:38566 }
[email protected]52ae80c2009-09-10 21:27:00567
[email protected]51e413c2010-06-23 00:15:58568 if (!success)
[email protected]9b9ae9552010-07-01 22:20:50569 return ASCIILiteralToV8String("127.0.0.1");
570 return ASCIIStringToV8String(result);
[email protected]50d7d7282009-03-02 21:45:18571 }
572
[email protected]21f20c892009-10-26 23:51:28573 // 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
[email protected]51e413c2010-06-23 00:15:58579 std::string ip_address_list;
580 bool success;
[email protected]21f20c892009-10-26 23:51:28581
[email protected]3908c022010-05-13 20:13:38582 {
583 v8::Unlocker unlocker;
[email protected]21f20c892009-10-26 23:51:28584
[email protected]3908c022010-05-13 20:13:38585 // We shouldn't be called with any arguments, but will not complain if
586 // we are.
[email protected]51e413c2010-06-23 00:15:58587 success = context->js_bindings_->MyIpAddressEx(&ip_address_list);
[email protected]3908c022010-05-13 20:13:38588 }
[email protected]21f20c892009-10-26 23:51:28589
[email protected]51e413c2010-06-23 00:15:58590 if (!success)
591 ip_address_list = std::string();
[email protected]9b9ae9552010-07-01 22:20:50592 return ASCIIStringToV8String(ip_address_list);
[email protected]21f20c892009-10-26 23:51:28593 }
594
[email protected]50d7d7282009-03-02 21:45:18595 // 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
[email protected]d35c3662010-05-05 20:04:34600 // We need at least one string argument.
[email protected]51e413c2010-06-23 00:15:58601 std::string hostname;
602 if (!GetHostnameArgument(args, &hostname))
[email protected]d35c3662010-05-05 20:04:34603 return v8::Null();
[email protected]50d7d7282009-03-02 21:45:18604
[email protected]51e413c2010-06-23 00:15:58605 std::string ip_address;
606 bool success;
[email protected]52ae80c2009-09-10 21:27:00607
[email protected]3908c022010-05-13 20:13:38608 {
609 v8::Unlocker unlocker;
[email protected]51e413c2010-06-23 00:15:58610 success = context->js_bindings_->DnsResolve(hostname, &ip_address);
[email protected]3908c022010-05-13 20:13:38611 }
[email protected]52ae80c2009-09-10 21:27:00612
[email protected]9b9ae9552010-07-01 22:20:50613 return success ? ASCIIStringToV8String(ip_address) : v8::Null();
[email protected]50d7d7282009-03-02 21:45:18614 }
615
[email protected]21f20c892009-10-26 23:51:28616 // 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
[email protected]d35c3662010-05-05 20:04:34621 // We need at least one string argument.
[email protected]51e413c2010-06-23 00:15:58622 std::string hostname;
623 if (!GetHostnameArgument(args, &hostname))
[email protected]d35c3662010-05-05 20:04:34624 return v8::Undefined();
[email protected]21f20c892009-10-26 23:51:28625
[email protected]51e413c2010-06-23 00:15:58626 std::string ip_address_list;
627 bool success;
[email protected]21f20c892009-10-26 23:51:28628
[email protected]3908c022010-05-13 20:13:38629 {
630 v8::Unlocker unlocker;
[email protected]a37b8bb2010-08-26 23:31:00631 success = context->js_bindings_->DnsResolveEx(hostname, &ip_address_list);
[email protected]3908c022010-05-13 20:13:38632 }
[email protected]21f20c892009-10-26 23:51:28633
[email protected]51e413c2010-06-23 00:15:58634 if (!success)
635 ip_address_list = std::string();
636
[email protected]9b9ae9552010-07-01 22:20:50637 return ASCIIStringToV8String(ip_address_list);
[email protected]21f20c892009-10-26 23:51:28638 }
639
[email protected]a37b8bb2010-08-26 23:31:00640 // 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
[email protected]90ae1fb2009-08-02 21:07:12673 ProxyResolverJSBindings* js_bindings_;
[email protected]50d7d7282009-03-02 21:45:18674 v8::Persistent<v8::External> v8_this_;
[email protected]943c8082009-02-23 19:10:45675 v8::Persistent<v8::Context> v8_context_;
676};
677
678// ProxyResolverV8 ------------------------------------------------------------
679
[email protected]50d7d7282009-03-02 21:45:18680ProxyResolverV8::ProxyResolverV8(
[email protected]90ae1fb2009-08-02 21:07:12681 ProxyResolverJSBindings* custom_js_bindings)
[email protected]775fd9e2009-07-26 21:12:20682 : ProxyResolver(true /*expects_pac_bytes*/),
683 js_bindings_(custom_js_bindings) {
[email protected]50d7d7282009-03-02 21:45:18684}
[email protected]943c8082009-02-23 19:10:45685
686ProxyResolverV8::~ProxyResolverV8() {}
687
688int ProxyResolverV8::GetProxyForURL(const GURL& query_url,
[email protected]775fd9e2009-07-26 21:12:20689 ProxyInfo* results,
690 CompletionCallback* /*callback*/,
[email protected]52ae80c2009-09-10 21:27:00691 RequestHandle* /*request*/,
[email protected]9e743cd2010-03-16 07:03:53692 const BoundNetLog& net_log) {
[email protected]775fd9e2009-07-26 21:12:20693 // If the V8 instance has not been initialized (either because
[email protected]620f5712009-08-04 22:43:12694 // SetPacScript() wasn't called yet, or because it failed.
[email protected]943c8082009-02-23 19:10:45695 if (!context_.get())
696 return ERR_FAILED;
697
[email protected]59872eb2010-06-22 18:56:12698 // 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
[email protected]a658ed172010-06-23 17:26:12709 ProxyResolverRequestContext request_context(&net_log, &host_cache);
[email protected]59872eb2010-06-22 18:56:12710
[email protected]943c8082009-02-23 19:10:45711 // Otherwise call into V8.
[email protected]59872eb2010-06-22 18:56:12712 context_->SetCurrentRequestContext(&request_context);
[email protected]52ae80c2009-09-10 21:27:00713 int rv = context_->ResolveProxy(query_url, results);
[email protected]59872eb2010-06-22 18:56:12714 context_->SetCurrentRequestContext(NULL);
[email protected]52ae80c2009-09-10 21:27:00715
716 return rv;
[email protected]943c8082009-02-23 19:10:45717}
718
[email protected]775fd9e2009-07-26 21:12:20719void ProxyResolverV8::CancelRequest(RequestHandle request) {
720 // This is a synchronous ProxyResolver; no possibility for async requests.
721 NOTREACHED();
[email protected]943c8082009-02-23 19:10:45722}
723
[email protected]1e605472010-12-16 21:41:40724void ProxyResolverV8::CancelSetPacScript() {
725 NOTREACHED();
726}
727
[email protected]0cf348be2009-10-13 01:39:58728void ProxyResolverV8::PurgeMemory() {
729 context_->PurgeMemory();
730}
731
[email protected]61b84d52010-07-09 03:32:00732void ProxyResolverV8::Shutdown() {
733 js_bindings_->Shutdown();
734}
735
[email protected]24476402010-07-20 20:55:17736int ProxyResolverV8::SetPacScript(
737 const scoped_refptr<ProxyResolverScriptData>& script_data,
738 CompletionCallback* /*callback*/) {
739 DCHECK(script_data.get());
[email protected]775fd9e2009-07-26 21:12:20740 context_.reset();
[email protected]24476402010-07-20 20:55:17741 if (script_data->utf16().empty())
[email protected]620f5712009-08-04 22:43:12742 return ERR_PAC_SCRIPT_FAILED;
743
744 // Try parsing the PAC script.
745 scoped_ptr<Context> context(new Context(js_bindings_.get()));
[email protected]24476402010-07-20 20:55:17746 int rv = context->InitV8(script_data);
[email protected]620f5712009-08-04 22:43:12747 if (rv == OK)
748 context_.reset(context.release());
749 return rv;
[email protected]775fd9e2009-07-26 21:12:20750}
751
[email protected]943c8082009-02-23 19:10:45752} // namespace net