This source file includes following definitions.
- data
- length
- length_
- data
- length
- V8StringToUTF8
- V8StringToUTF16
- ASCIIStringToV8String
- ScriptDataToV8String
- ASCIILiteralToV8String
- V8ObjectToUTF16String
- GetHostnameArgument
- SortIpAddressList
- IsInNetEx
- isolate_
- Context
- js_bindings
- ResolveProxy
- InitV8
- GetFindProxyForURL
- HandleError
- RunScript
- AlertCallback
- MyIpAddressCallback
- MyIpAddressExCallback
- DnsResolveCallback
- DnsResolveExCallback
- DnsResolveCallbackHelper
- SortIpAddressListCallback
- IsInNetExCallback
- js_bindings_
- GetProxyForURL
- CancelRequest
- GetLoadState
- CancelSetPacScript
- SetPacScript
- RememberDefaultIsolate
- CreateIsolate
- GetDefaultIsolate
- GetTotalHeapSize
- GetUsedHeapSize
#include "net/proxy/proxy_resolver_v8.h"
#include <algorithm>
#include <cstdio>
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
#include "net/base/net_util.h"
#include "net/proxy/proxy_info.h"
#include "net/proxy/proxy_resolver_script.h"
#include "url/gurl.h"
#include "url/url_canon.h"
#include "v8/include/v8.h"
namespace net {
namespace {
const char kPacResourceName[] = "proxy-pac-script.js";
const char kPacUtilityResourceName[] = "proxy-pac-utility-script.js";
class V8ExternalStringFromScriptData
: public v8::String::ExternalStringResource {
public:
explicit V8ExternalStringFromScriptData(
const scoped_refptr<ProxyResolverScriptData>& script_data)
: script_data_(script_data) {}
virtual const uint16_t* data() const OVERRIDE {
return reinterpret_cast<const uint16*>(script_data_->utf16().data());
}
virtual size_t length() const OVERRIDE {
return script_data_->utf16().size();
}
private:
const scoped_refptr<ProxyResolverScriptData> script_data_;
DISALLOW_COPY_AND_ASSIGN(V8ExternalStringFromScriptData);
};
class V8ExternalASCIILiteral : public v8::String::ExternalAsciiStringResource {
public:
V8ExternalASCIILiteral(const char* ascii, size_t length)
: ascii_(ascii), length_(length) {
DCHECK(IsStringASCII(ascii));
}
virtual const char* data() const OVERRIDE {
return ascii_;
}
virtual size_t length() const OVERRIDE {
return length_;
}
private:
const char* ascii_;
size_t length_;
DISALLOW_COPY_AND_ASSIGN(V8ExternalASCIILiteral);
};
const size_t kMaxStringBytesForCopy = 256;
std::string V8StringToUTF8(v8::Handle<v8::String> s) {
int len = s->Length();
std::string result;
if (len > 0)
s->WriteUtf8(WriteInto(&result, len + 1));
return result;
}
base::string16 V8StringToUTF16(v8::Handle<v8::String> s) {
int len = s->Length();
base::string16 result;
if (len > 0)
s->Write(reinterpret_cast<uint16_t*>(WriteInto(&result, len + 1)), 0, len);
return result;
}
v8::Local<v8::String> ASCIIStringToV8String(v8::Isolate* isolate,
const std::string& s) {
DCHECK(IsStringASCII(s));
return v8::String::NewFromUtf8(isolate, s.data(), v8::String::kNormalString,
s.size());
}
v8::Local<v8::String> ScriptDataToV8String(
v8::Isolate* isolate, const scoped_refptr<ProxyResolverScriptData>& s) {
if (s->utf16().size() * 2 <= kMaxStringBytesForCopy) {
return v8::String::NewFromTwoByte(
isolate,
reinterpret_cast<const uint16_t*>(s->utf16().data()),
v8::String::kNormalString,
s->utf16().size());
}
return v8::String::NewExternal(isolate,
new V8ExternalStringFromScriptData(s));
}
v8::Local<v8::String> ASCIILiteralToV8String(v8::Isolate* isolate,
const char* ascii) {
DCHECK(IsStringASCII(ascii));
size_t length = strlen(ascii);
if (length <= kMaxStringBytesForCopy)
return v8::String::NewFromUtf8(isolate, ascii, v8::String::kNormalString,
length);
return v8::String::NewExternal(isolate,
new V8ExternalASCIILiteral(ascii, length));
}
bool V8ObjectToUTF16String(v8::Handle<v8::Value> object,
base::string16* utf16_result,
v8::Isolate* isolate) {
if (object.IsEmpty())
return false;
v8::HandleScope scope(isolate);
v8::Local<v8::String> str_object = object->ToString();
if (str_object.IsEmpty())
return false;
*utf16_result = V8StringToUTF16(str_object);
return true;
}
bool GetHostnameArgument(const v8::FunctionCallbackInfo<v8::Value>& args,
std::string* hostname) {
if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
return false;
const base::string16 hostname_utf16 = V8StringToUTF16(args[0]->ToString());
if (IsStringASCII(hostname_utf16)) {
*hostname = base::UTF16ToASCII(hostname_utf16);
return true;
}
const int kInitialBufferSize = 256;
url_canon::RawCanonOutputT<base::char16, kInitialBufferSize> punycode_output;
if (!url_canon::IDNToASCII(hostname_utf16.data(),
hostname_utf16.length(),
&punycode_output)) {
return false;
}
bool success = base::UTF16ToUTF8(punycode_output.data(),
punycode_output.length(),
hostname);
DCHECK(success);
DCHECK(IsStringASCII(*hostname));
return success;
}
struct IPAddress {
IPAddress(const std::string& ip_string, const IPAddressNumber& ip_number)
: string_value(ip_string),
ip_address_number(ip_number) {
}
bool operator<(const IPAddress& rhs) const {
const IPAddressNumber& ip1 = this->ip_address_number;
const IPAddressNumber& ip2 = rhs.ip_address_number;
if (ip1.size() != ip2.size())
return ip1.size() > ip2.size();
DCHECK(ip1.size() == ip2.size());
return memcmp(&ip1[0], &ip2[0], ip1.size()) < 0;
}
std::string string_value;
IPAddressNumber ip_address_number;
};
bool SortIpAddressList(const std::string& ip_address_list,
std::string* sorted_ip_address_list) {
sorted_ip_address_list->clear();
std::string cleaned_ip_address_list;
base::RemoveChars(ip_address_list, " \t", &cleaned_ip_address_list);
if (cleaned_ip_address_list.empty())
return false;
std::vector<IPAddress> ip_vector;
IPAddressNumber ip_num;
base::StringTokenizer str_tok(cleaned_ip_address_list, ";");
while (str_tok.GetNext()) {
if (!ParseIPLiteralToNumber(str_tok.token(), &ip_num))
return false;
ip_vector.push_back(IPAddress(str_tok.token(), ip_num));
}
if (ip_vector.empty())
return false;
DCHECK(!ip_vector.empty());
if (ip_vector.size() > 1)
std::stable_sort(ip_vector.begin(), ip_vector.end());
for (size_t i = 0; i < ip_vector.size(); ++i) {
if (i > 0)
*sorted_ip_address_list += ";";
*sorted_ip_address_list += ip_vector[i].string_value;
}
return true;
}
bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) {
IPAddressNumber address;
if (!ParseIPLiteralToNumber(ip_address, &address))
return false;
IPAddressNumber prefix;
size_t prefix_length_in_bits;
if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits))
return false;
if (address.size() != prefix.size())
return false;
DCHECK((address.size() == 4 && prefix.size() == 4) ||
(address.size() == 16 && prefix.size() == 16));
return IPNumberMatchesPrefix(address, prefix, prefix_length_in_bits);
}
}
class ProxyResolverV8::Context {
public:
Context(ProxyResolverV8* parent, v8::Isolate* isolate)
: parent_(parent),
isolate_(isolate) {
DCHECK(isolate);
}
~Context() {
v8::Locker locked(isolate_);
v8::Isolate::Scope isolate_scope(isolate_);
v8_this_.Reset();
v8_context_.Reset();
}
JSBindings* js_bindings() {
return parent_->js_bindings_;
}
int ResolveProxy(const GURL& query_url, ProxyInfo* results) {
v8::Locker locked(isolate_);
v8::Isolate::Scope isolate_scope(isolate_);
v8::HandleScope scope(isolate_);
v8::Local<v8::Context> context =
v8::Local<v8::Context>::New(isolate_, v8_context_);
v8::Context::Scope function_scope(context);
v8::Local<v8::Value> function;
if (!GetFindProxyForURL(&function)) {
js_bindings()->OnError(
-1, base::ASCIIToUTF16("FindProxyForURL() is undefined."));
return ERR_PAC_SCRIPT_FAILED;
}
v8::Handle<v8::Value> argv[] = {
ASCIIStringToV8String(isolate_, query_url.spec()),
ASCIIStringToV8String(isolate_, query_url.HostNoBrackets()),
};
v8::TryCatch try_catch;
v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
context->Global(), arraysize(argv), argv);
if (try_catch.HasCaught()) {
HandleError(try_catch.Message());
return ERR_PAC_SCRIPT_FAILED;
}
if (!ret->IsString()) {
js_bindings()->OnError(
-1, base::ASCIIToUTF16("FindProxyForURL() did not return a string."));
return ERR_PAC_SCRIPT_FAILED;
}
base::string16 ret_str = V8StringToUTF16(ret->ToString());
if (!IsStringASCII(ret_str)) {
base::string16 error_message =
base::ASCIIToUTF16("FindProxyForURL() returned a non-ASCII string "
"(crbug.com/47234): ") + ret_str;
js_bindings()->OnError(-1, error_message);
return ERR_PAC_SCRIPT_FAILED;
}
results->UsePacString(base::UTF16ToASCII(ret_str));
return OK;
}
int InitV8(const scoped_refptr<ProxyResolverScriptData>& pac_script) {
v8::Locker locked(isolate_);
v8::Isolate::Scope isolate_scope(isolate_);
v8::HandleScope scope(isolate_);
v8_this_.Reset(isolate_, v8::External::New(isolate_, this));
v8::Local<v8::External> v8_this =
v8::Local<v8::External>::New(isolate_, v8_this_);
v8::Local<v8::ObjectTemplate> global_template =
v8::ObjectTemplate::New(isolate_);
v8::Local<v8::FunctionTemplate> alert_template =
v8::FunctionTemplate::New(isolate_, &AlertCallback, v8_this);
global_template->Set(ASCIILiteralToV8String(isolate_, "alert"),
alert_template);
v8::Local<v8::FunctionTemplate> my_ip_address_template =
v8::FunctionTemplate::New(isolate_, &MyIpAddressCallback, v8_this);
global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddress"),
my_ip_address_template);
v8::Local<v8::FunctionTemplate> dns_resolve_template =
v8::FunctionTemplate::New(isolate_, &DnsResolveCallback, v8_this);
global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolve"),
dns_resolve_template);
v8::Local<v8::FunctionTemplate> dns_resolve_ex_template =
v8::FunctionTemplate::New(isolate_, &DnsResolveExCallback, v8_this);
global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolveEx"),
dns_resolve_ex_template);
v8::Local<v8::FunctionTemplate> my_ip_address_ex_template =
v8::FunctionTemplate::New(isolate_, &MyIpAddressExCallback, v8_this);
global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddressEx"),
my_ip_address_ex_template);
v8::Local<v8::FunctionTemplate> sort_ip_address_list_template =
v8::FunctionTemplate::New(isolate_,
&SortIpAddressListCallback,
v8_this);
global_template->Set(ASCIILiteralToV8String(isolate_, "sortIpAddressList"),
sort_ip_address_list_template);
v8::Local<v8::FunctionTemplate> is_in_net_ex_template =
v8::FunctionTemplate::New(isolate_, &IsInNetExCallback, v8_this);
global_template->Set(ASCIILiteralToV8String(isolate_, "isInNetEx"),
is_in_net_ex_template);
v8_context_.Reset(
isolate_, v8::Context::New(isolate_, NULL, global_template));
v8::Local<v8::Context> context =
v8::Local<v8::Context>::New(isolate_, v8_context_);
v8::Context::Scope ctx(context);
int rv = RunScript(
ASCIILiteralToV8String(
isolate_,
PROXY_RESOLVER_SCRIPT
PROXY_RESOLVER_SCRIPT_EX),
kPacUtilityResourceName);
if (rv != OK) {
NOTREACHED();
return rv;
}
rv =
RunScript(ScriptDataToV8String(isolate_, pac_script), kPacResourceName);
if (rv != OK)
return rv;
v8::Local<v8::Value> function;
if (!GetFindProxyForURL(&function)) {
js_bindings()->OnError(
-1, base::ASCIIToUTF16("FindProxyForURL() is undefined."));
return ERR_PAC_SCRIPT_FAILED;
}
return OK;
}
private:
bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
v8::Local<v8::Context> context =
v8::Local<v8::Context>::New(isolate_, v8_context_);
*function =
context->Global()->Get(
ASCIILiteralToV8String(isolate_, "FindProxyForURL"));
return (*function)->IsFunction();
}
void HandleError(v8::Handle<v8::Message> message) {
base::string16 error_message;
int line_number = -1;
if (!message.IsEmpty()) {
line_number = message->GetLineNumber();
V8ObjectToUTF16String(message->Get(), &error_message, isolate_);
}
js_bindings()->OnError(line_number, error_message);
}
int RunScript(v8::Handle<v8::String> script, const char* script_name) {
v8::TryCatch try_catch;
v8::ScriptOrigin origin =
v8::ScriptOrigin(ASCIILiteralToV8String(isolate_, script_name));
v8::Local<v8::Script> code = v8::Script::Compile(script, &origin);
if (!code.IsEmpty())
code->Run();
if (try_catch.HasCaught()) {
HandleError(try_catch.Message());
return ERR_PAC_SCRIPT_FAILED;
}
return OK;
}
static void AlertCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
Context* context =
static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
base::string16 message;
if (args.Length() == 0) {
message = base::ASCIIToUTF16("undefined");
} else {
if (!V8ObjectToUTF16String(args[0], &message, args.GetIsolate()))
return;
}
context->js_bindings()->Alert(message);
}
static void MyIpAddressCallback(
const v8::FunctionCallbackInfo<v8::Value>& args) {
DnsResolveCallbackHelper(args, JSBindings::MY_IP_ADDRESS);
}
static void MyIpAddressExCallback(
const v8::FunctionCallbackInfo<v8::Value>& args) {
DnsResolveCallbackHelper(args, JSBindings::MY_IP_ADDRESS_EX);
}
static void DnsResolveCallback(
const v8::FunctionCallbackInfo<v8::Value>& args) {
DnsResolveCallbackHelper(args, JSBindings::DNS_RESOLVE);
}
static void DnsResolveExCallback(
const v8::FunctionCallbackInfo<v8::Value>& args) {
DnsResolveCallbackHelper(args, JSBindings::DNS_RESOLVE_EX);
}
static void DnsResolveCallbackHelper(
const v8::FunctionCallbackInfo<v8::Value>& args,
JSBindings::ResolveDnsOperation op) {
Context* context =
static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
std::string hostname;
if (op == JSBindings::DNS_RESOLVE || op == JSBindings::DNS_RESOLVE_EX) {
if (!GetHostnameArgument(args, &hostname)) {
if (op == JSBindings::DNS_RESOLVE)
args.GetReturnValue().SetNull();
return;
}
}
std::string result;
bool success;
bool terminate = false;
{
v8::Unlocker unlocker(args.GetIsolate());
success = context->js_bindings()->ResolveDns(
hostname, op, &result, &terminate);
}
if (terminate)
v8::V8::TerminateExecution(args.GetIsolate());
if (success) {
args.GetReturnValue().Set(
ASCIIStringToV8String(args.GetIsolate(), result));
return;
}
switch (op) {
case JSBindings::DNS_RESOLVE:
args.GetReturnValue().SetNull();
return;
case JSBindings::DNS_RESOLVE_EX:
args.GetReturnValue().SetEmptyString();
return;
case JSBindings::MY_IP_ADDRESS:
args.GetReturnValue().Set(
ASCIILiteralToV8String(args.GetIsolate(), "127.0.0.1"));
return;
case JSBindings::MY_IP_ADDRESS_EX:
args.GetReturnValue().SetEmptyString();
return;
}
NOTREACHED();
}
static void SortIpAddressListCallback(
const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString()) {
args.GetReturnValue().SetNull();
return;
}
std::string ip_address_list = V8StringToUTF8(args[0]->ToString());
if (!IsStringASCII(ip_address_list)) {
args.GetReturnValue().SetNull();
return;
}
std::string sorted_ip_address_list;
bool success = SortIpAddressList(ip_address_list, &sorted_ip_address_list);
if (!success) {
args.GetReturnValue().Set(false);
return;
}
args.GetReturnValue().Set(
ASCIIStringToV8String(args.GetIsolate(), sorted_ip_address_list));
}
static void IsInNetExCallback(
const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() ||
args[1].IsEmpty() || !args[1]->IsString()) {
args.GetReturnValue().SetNull();
return;
}
std::string ip_address = V8StringToUTF8(args[0]->ToString());
if (!IsStringASCII(ip_address)) {
args.GetReturnValue().Set(false);
return;
}
std::string ip_prefix = V8StringToUTF8(args[1]->ToString());
if (!IsStringASCII(ip_prefix)) {
args.GetReturnValue().Set(false);
return;
}
args.GetReturnValue().Set(IsInNetEx(ip_address, ip_prefix));
}
mutable base::Lock lock_;
ProxyResolverV8* parent_;
v8::Isolate* isolate_;
v8::Persistent<v8::External> v8_this_;
v8::Persistent<v8::Context> v8_context_;
};
ProxyResolverV8::ProxyResolverV8()
: ProxyResolver(true ),
js_bindings_(NULL) {
}
ProxyResolverV8::~ProxyResolverV8() {}
int ProxyResolverV8::GetProxyForURL(
const GURL& query_url, ProxyInfo* results,
const CompletionCallback& ,
RequestHandle* ,
const BoundNetLog& net_log) {
DCHECK(js_bindings_);
if (!context_)
return ERR_FAILED;
int rv = context_->ResolveProxy(query_url, results);
return rv;
}
void ProxyResolverV8::CancelRequest(RequestHandle request) {
NOTREACHED();
}
LoadState ProxyResolverV8::GetLoadState(RequestHandle request) const {
NOTREACHED();
return LOAD_STATE_IDLE;
}
void ProxyResolverV8::CancelSetPacScript() {
NOTREACHED();
}
int ProxyResolverV8::SetPacScript(
const scoped_refptr<ProxyResolverScriptData>& script_data,
const CompletionCallback& ) {
DCHECK(script_data.get());
DCHECK(js_bindings_);
context_.reset();
if (script_data->utf16().empty())
return ERR_PAC_SCRIPT_FAILED;
scoped_ptr<Context> context(new Context(this, GetDefaultIsolate()));
int rv = context->InitV8(script_data);
if (rv == OK)
context_.reset(context.release());
return rv;
}
void ProxyResolverV8::RememberDefaultIsolate() {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
DCHECK(isolate)
<< "ProxyResolverV8::RememberDefaultIsolate called on wrong thread";
DCHECK(g_default_isolate_ == NULL || g_default_isolate_ == isolate)
<< "Default Isolate can not be changed";
g_default_isolate_ = isolate;
}
#if defined(OS_WIN)
void ProxyResolverV8::CreateIsolate() {
v8::Isolate* isolate = v8::Isolate::New();
DCHECK(isolate);
DCHECK(g_default_isolate_ == NULL) << "Default Isolate can not be set twice";
isolate->Enter();
v8::V8::Initialize();
g_default_isolate_ = isolate;
}
#endif
v8::Isolate* ProxyResolverV8::GetDefaultIsolate() {
DCHECK(g_default_isolate_)
<< "Must call ProxyResolverV8::RememberDefaultIsolate() first";
return g_default_isolate_;
}
v8::Isolate* ProxyResolverV8::g_default_isolate_ = NULL;
size_t ProxyResolverV8::GetTotalHeapSize() {
if (!g_default_isolate_)
return 0;
v8::Locker locked(g_default_isolate_);
v8::Isolate::Scope isolate_scope(g_default_isolate_);
v8::HeapStatistics heap_statistics;
g_default_isolate_->GetHeapStatistics(&heap_statistics);
return heap_statistics.total_heap_size();
}
size_t ProxyResolverV8::GetUsedHeapSize() {
if (!g_default_isolate_)
return 0;
v8::Locker locked(g_default_isolate_);
v8::Isolate::Scope isolate_scope(g_default_isolate_);
v8::HeapStatistics heap_statistics;
g_default_isolate_->GetHeapStatistics(&heap_statistics);
return heap_statistics.used_heap_size();
}
}