// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/proxy/proxy_resolver_factory_mojo.h" #include #include #include "base/bind.h" #include "base/logging.h" #include "base/macros.h" #include "base/stl_util.h" #include "base/threading/thread_checker.h" #include "base/values.h" #include "mojo/common/common_type_converters.h" #include "mojo/common/url_type_converters.h" #include "mojo/public/cpp/bindings/binding.h" #include "net/base/load_states.h" #include "net/base/net_errors.h" #include "net/dns/mojo_host_resolver_impl.h" #include "net/interfaces/host_resolver_service.mojom.h" #include "net/interfaces/proxy_resolver_service.mojom.h" #include "net/proxy/mojo_proxy_resolver_factory.h" #include "net/proxy/mojo_proxy_type_converters.h" #include "net/proxy/proxy_info.h" #include "net/proxy/proxy_resolver.h" #include "net/proxy/proxy_resolver_error_observer.h" #include "net/proxy/proxy_resolver_script_data.h" namespace net { namespace { scoped_ptr NetLogErrorCallback( int line_number, const base::string16* message, NetLogCaptureMode /* capture_mode */) { scoped_ptr dict(new base::DictionaryValue()); dict->SetInteger("line_number", line_number); dict->SetString("message", *message); return std::move(dict); } // A mixin that forwards logging to (Bound)NetLog and ProxyResolverErrorObserver // and DNS requests to a MojoHostResolverImpl, which is implemented in terms of // a HostResolver. template class ClientMixin : public ClientInterface { public: ClientMixin(HostResolver* host_resolver, ProxyResolverErrorObserver* error_observer, NetLog* net_log, const BoundNetLog& bound_net_log) : host_resolver_(host_resolver, bound_net_log), error_observer_(error_observer), net_log_(net_log), bound_net_log_(bound_net_log) {} // Overridden from ClientInterface: void Alert(const mojo::String& message) override { base::string16 message_str = message.To(); auto callback = NetLog::StringCallback("message", &message_str); bound_net_log_.AddEvent(NetLog::TYPE_PAC_JAVASCRIPT_ALERT, callback); if (net_log_) net_log_->AddGlobalEntry(NetLog::TYPE_PAC_JAVASCRIPT_ALERT, callback); } void OnError(int32_t line_number, const mojo::String& message) override { base::string16 message_str = message.To(); auto callback = base::Bind(&NetLogErrorCallback, line_number, &message_str); bound_net_log_.AddEvent(NetLog::TYPE_PAC_JAVASCRIPT_ERROR, callback); if (net_log_) net_log_->AddGlobalEntry(NetLog::TYPE_PAC_JAVASCRIPT_ERROR, callback); if (error_observer_) error_observer_->OnPACScriptError(line_number, message_str); } void ResolveDns(interfaces::HostResolverRequestInfoPtr request_info, interfaces::HostResolverRequestClientPtr client) override { host_resolver_.Resolve(std::move(request_info), std::move(client)); } protected: bool dns_request_in_progress() { return host_resolver_.request_in_progress(); } private: MojoHostResolverImpl host_resolver_; ProxyResolverErrorObserver* const error_observer_; NetLog* const net_log_; const BoundNetLog bound_net_log_; }; // Implementation of ProxyResolver that connects to a Mojo service to evaluate // PAC scripts. This implementation only knows about Mojo services, and // therefore that service may live in or out of process. // // This implementation reports disconnections from the Mojo service (i.e. if the // service is out-of-process and that process crashes) using the error code // ERR_PAC_SCRIPT_TERMINATED. class ProxyResolverMojo : public ProxyResolver { public: // Constructs a ProxyResolverMojo that connects to a mojo proxy resolver // implementation using |resolver_ptr|. The implementation uses // |host_resolver| as the DNS resolver, using |host_resolver_binding| to // communicate with it. When deleted, the closure contained within // |on_delete_callback_runner| will be run. ProxyResolverMojo( interfaces::ProxyResolverPtr resolver_ptr, HostResolver* host_resolver, scoped_ptr on_delete_callback_runner, scoped_ptr error_observer, NetLog* net_log); ~ProxyResolverMojo() override; // ProxyResolver implementation: int GetProxyForURL(const GURL& url, ProxyInfo* results, const net::CompletionCallback& callback, RequestHandle* request, const BoundNetLog& net_log) override; void CancelRequest(RequestHandle request) override; LoadState GetLoadState(RequestHandle request) const override; private: class Job; // Mojo error handler. void OnConnectionError(); void RemoveJob(Job* job); // Connection to the Mojo proxy resolver. interfaces::ProxyResolverPtr mojo_proxy_resolver_ptr_; HostResolver* host_resolver_; scoped_ptr error_observer_; NetLog* net_log_; std::set pending_jobs_; base::ThreadChecker thread_checker_; scoped_ptr on_delete_callback_runner_; DISALLOW_COPY_AND_ASSIGN(ProxyResolverMojo); }; class ProxyResolverMojo::Job : public ClientMixin { public: Job(ProxyResolverMojo* resolver, const GURL& url, ProxyInfo* results, const CompletionCallback& callback, const BoundNetLog& net_log); ~Job() override; // Cancels the job and prevents the callback from being run. void Cancel(); // Returns the LoadState of this job. LoadState GetLoadState(); private: // Mojo error handler. void OnConnectionError(); // Overridden from interfaces::ProxyResolverRequestClient: void ReportResult( int32_t error, mojo::Array proxy_servers) override; ProxyResolverMojo* resolver_; const GURL url_; ProxyInfo* results_; CompletionCallback callback_; base::ThreadChecker thread_checker_; mojo::Binding binding_; }; ProxyResolverMojo::Job::Job(ProxyResolverMojo* resolver, const GURL& url, ProxyInfo* results, const CompletionCallback& callback, const BoundNetLog& net_log) : ClientMixin( resolver->host_resolver_, resolver->error_observer_.get(), resolver->net_log_, net_log), resolver_(resolver), url_(url), results_(results), callback_(callback), binding_(this) { resolver_->mojo_proxy_resolver_ptr_->GetProxyForUrl( mojo::String::From(url_), binding_.CreateInterfacePtrAndBind()); binding_.set_connection_error_handler(base::Bind( &ProxyResolverMojo::Job::OnConnectionError, base::Unretained(this))); } ProxyResolverMojo::Job::~Job() { DCHECK(thread_checker_.CalledOnValidThread()); if (!callback_.is_null()) callback_.Run(ERR_PAC_SCRIPT_TERMINATED); } void ProxyResolverMojo::Job::Cancel() { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(!callback_.is_null()); callback_.Reset(); } LoadState ProxyResolverMojo::Job::GetLoadState() { return dns_request_in_progress() ? LOAD_STATE_RESOLVING_HOST_IN_PROXY_SCRIPT : LOAD_STATE_RESOLVING_PROXY_FOR_URL; } void ProxyResolverMojo::Job::OnConnectionError() { DCHECK(thread_checker_.CalledOnValidThread()); DVLOG(1) << "ProxyResolverMojo::Job::OnConnectionError"; resolver_->RemoveJob(this); } void ProxyResolverMojo::Job::ReportResult( int32_t error, mojo::Array proxy_servers) { DCHECK(thread_checker_.CalledOnValidThread()); DVLOG(1) << "ProxyResolverMojo::Job::ReportResult: " << error; if (error == OK) { *results_ = proxy_servers.To(); DVLOG(1) << "Servers: " << results_->ToPacString(); } CompletionCallback callback = callback_; callback_.Reset(); resolver_->RemoveJob(this); callback.Run(error); } ProxyResolverMojo::ProxyResolverMojo( interfaces::ProxyResolverPtr resolver_ptr, HostResolver* host_resolver, scoped_ptr on_delete_callback_runner, scoped_ptr error_observer, NetLog* net_log) : mojo_proxy_resolver_ptr_(std::move(resolver_ptr)), host_resolver_(host_resolver), error_observer_(std::move(error_observer)), net_log_(net_log), on_delete_callback_runner_(std::move(on_delete_callback_runner)) { mojo_proxy_resolver_ptr_.set_connection_error_handler(base::Bind( &ProxyResolverMojo::OnConnectionError, base::Unretained(this))); } ProxyResolverMojo::~ProxyResolverMojo() { DCHECK(thread_checker_.CalledOnValidThread()); // All pending requests should have been cancelled. DCHECK(pending_jobs_.empty()); } void ProxyResolverMojo::OnConnectionError() { DCHECK(thread_checker_.CalledOnValidThread()); DVLOG(1) << "ProxyResolverMojo::OnConnectionError"; // Disconnect from the Mojo proxy resolver service. mojo_proxy_resolver_ptr_.reset(); } void ProxyResolverMojo::RemoveJob(Job* job) { DCHECK(thread_checker_.CalledOnValidThread()); size_t num_erased = pending_jobs_.erase(job); DCHECK(num_erased); delete job; } int ProxyResolverMojo::GetProxyForURL(const GURL& url, ProxyInfo* results, const CompletionCallback& callback, RequestHandle* request, const BoundNetLog& net_log) { DCHECK(thread_checker_.CalledOnValidThread()); if (!mojo_proxy_resolver_ptr_) return ERR_PAC_SCRIPT_TERMINATED; Job* job = new Job(this, url, results, callback, net_log); bool inserted = pending_jobs_.insert(job).second; DCHECK(inserted); *request = job; return ERR_IO_PENDING; } void ProxyResolverMojo::CancelRequest(RequestHandle request) { DCHECK(thread_checker_.CalledOnValidThread()); Job* job = static_cast(request); DCHECK(job); job->Cancel(); RemoveJob(job); } LoadState ProxyResolverMojo::GetLoadState(RequestHandle request) const { Job* job = static_cast(request); CHECK_EQ(1u, pending_jobs_.count(job)); return job->GetLoadState(); } } // namespace // A Job to create a ProxyResolver instance. // // Note: a Job instance is not tied to a particular resolve request, and hence // there is no per-request logging to be done (any netlog events are only sent // globally) so this always uses an empty BoundNetLog. class ProxyResolverFactoryMojo::Job : public ClientMixin, public ProxyResolverFactory::Request { public: Job(ProxyResolverFactoryMojo* factory, const scoped_refptr& pac_script, scoped_ptr* resolver, const CompletionCallback& callback, scoped_ptr error_observer) : ClientMixin( factory->host_resolver_, error_observer.get(), factory->net_log_, BoundNetLog()), factory_(factory), resolver_(resolver), callback_(callback), binding_(this), error_observer_(std::move(error_observer)) { on_delete_callback_runner_ = factory_->mojo_proxy_factory_->CreateResolver( mojo::String::From(pac_script->utf16()), mojo::GetProxy(&resolver_ptr_), binding_.CreateInterfacePtrAndBind()); resolver_ptr_.set_connection_error_handler( base::Bind(&ProxyResolverFactoryMojo::Job::OnConnectionError, base::Unretained(this))); binding_.set_connection_error_handler( base::Bind(&ProxyResolverFactoryMojo::Job::OnConnectionError, base::Unretained(this))); } void OnConnectionError() { ReportResult(ERR_PAC_SCRIPT_TERMINATED); } private: void ReportResult(int32_t error) override { resolver_ptr_.set_connection_error_handler(mojo::Closure()); binding_.set_connection_error_handler(mojo::Closure()); if (error == OK) { resolver_->reset(new ProxyResolverMojo( std::move(resolver_ptr_), factory_->host_resolver_, std::move(on_delete_callback_runner_), std::move(error_observer_), factory_->net_log_)); } on_delete_callback_runner_.reset(); callback_.Run(error); } ProxyResolverFactoryMojo* const factory_; scoped_ptr* resolver_; const CompletionCallback callback_; interfaces::ProxyResolverPtr resolver_ptr_; mojo::Binding binding_; scoped_ptr on_delete_callback_runner_; scoped_ptr error_observer_; }; ProxyResolverFactoryMojo::ProxyResolverFactoryMojo( MojoProxyResolverFactory* mojo_proxy_factory, HostResolver* host_resolver, const base::Callback()>& error_observer_factory, NetLog* net_log) : ProxyResolverFactory(true), mojo_proxy_factory_(mojo_proxy_factory), host_resolver_(host_resolver), error_observer_factory_(error_observer_factory), net_log_(net_log) { } ProxyResolverFactoryMojo::~ProxyResolverFactoryMojo() = default; int ProxyResolverFactoryMojo::CreateProxyResolver( const scoped_refptr& pac_script, scoped_ptr* resolver, const CompletionCallback& callback, scoped_ptr* request) { DCHECK(resolver); DCHECK(request); if (pac_script->type() != ProxyResolverScriptData::TYPE_SCRIPT_CONTENTS || pac_script->utf16().empty()) { return ERR_PAC_SCRIPT_FAILED; } request->reset(new Job(this, pac_script, resolver, callback, error_observer_factory_.is_null() ? nullptr : error_observer_factory_.Run())); return ERR_IO_PENDING; } } // namespace net