| // 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 <set> |
| |
| #include "base/bind.h" |
| #include "base/logging.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 "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" |
| #include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" |
| |
| namespace net { |
| namespace { |
| |
| scoped_ptr<base::Value> NetLogErrorCallback( |
| int line_number, |
| const base::string16* message, |
| NetLogCaptureMode /* capture_mode */) { |
| scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); |
| dict->SetInteger("line_number", line_number); |
| dict->SetString("message", *message); |
| return dict.Pass(); |
| } |
| |
| // 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 <typename ClientInterface> |
| 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<base::string16>(); |
| 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<base::string16>(); |
| 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(request_info.Pass(), client.Pass()); |
| } |
| |
| 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<base::ScopedClosureRunner> on_delete_callback_runner, |
| scoped_ptr<ProxyResolverErrorObserver> 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<ProxyResolverErrorObserver> error_observer_; |
| |
| NetLog* net_log_; |
| |
| std::set<Job*> pending_jobs_; |
| |
| base::ThreadChecker thread_checker_; |
| |
| scoped_ptr<base::ScopedClosureRunner> on_delete_callback_runner_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ProxyResolverMojo); |
| }; |
| |
| class ProxyResolverMojo::Job |
| : public ClientMixin<interfaces::ProxyResolverRequestClient> { |
| 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<interfaces::ProxyServerPtr> proxy_servers) override; |
| |
| ProxyResolverMojo* resolver_; |
| const GURL url_; |
| ProxyInfo* results_; |
| CompletionCallback callback_; |
| |
| base::ThreadChecker thread_checker_; |
| mojo::Binding<interfaces::ProxyResolverRequestClient> binding_; |
| }; |
| |
| ProxyResolverMojo::Job::Job(ProxyResolverMojo* resolver, |
| const GURL& url, |
| ProxyInfo* results, |
| const CompletionCallback& callback, |
| const BoundNetLog& net_log) |
| : ClientMixin<interfaces::ProxyResolverRequestClient>( |
| resolver->host_resolver_, |
| resolver->error_observer_.get(), |
| resolver->net_log_, |
| net_log), |
| resolver_(resolver), |
| url_(url), |
| results_(results), |
| callback_(callback), |
| binding_(this) { |
| binding_.set_connection_error_handler(base::Bind( |
| &ProxyResolverMojo::Job::OnConnectionError, base::Unretained(this))); |
| |
| interfaces::ProxyResolverRequestClientPtr client_ptr; |
| binding_.Bind(mojo::GetProxy(&client_ptr)); |
| resolver_->mojo_proxy_resolver_ptr_->GetProxyForUrl(mojo::String::From(url_), |
| client_ptr.Pass()); |
| } |
| |
| 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<interfaces::ProxyServerPtr> proxy_servers) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DVLOG(1) << "ProxyResolverMojo::Job::ReportResult: " << error; |
| |
| if (error == OK) { |
| *results_ = proxy_servers.To<ProxyInfo>(); |
| 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<base::ScopedClosureRunner> on_delete_callback_runner, |
| scoped_ptr<ProxyResolverErrorObserver> error_observer, |
| NetLog* net_log) |
| : mojo_proxy_resolver_ptr_(resolver_ptr.Pass()), |
| host_resolver_(host_resolver), |
| error_observer_(error_observer.Pass()), |
| net_log_(net_log), |
| on_delete_callback_runner_(on_delete_callback_runner.Pass()) { |
| 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<Job*>(request); |
| DCHECK(job); |
| job->Cancel(); |
| RemoveJob(job); |
| } |
| |
| LoadState ProxyResolverMojo::GetLoadState(RequestHandle request) const { |
| Job* job = static_cast<Job*>(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<interfaces::ProxyResolverFactoryRequestClient>, |
| public ProxyResolverFactory::Request { |
| public: |
| Job(ProxyResolverFactoryMojo* factory, |
| const scoped_refptr<ProxyResolverScriptData>& pac_script, |
| scoped_ptr<ProxyResolver>* resolver, |
| const CompletionCallback& callback, |
| scoped_ptr<ProxyResolverErrorObserver> error_observer) |
| : ClientMixin<interfaces::ProxyResolverFactoryRequestClient>( |
| factory->host_resolver_, |
| error_observer.get(), |
| factory->net_log_, |
| BoundNetLog()), |
| factory_(factory), |
| resolver_(resolver), |
| callback_(callback), |
| binding_(this), |
| error_observer_(error_observer.Pass()) { |
| interfaces::ProxyResolverFactoryRequestClientPtr client_ptr; |
| binding_.Bind(mojo::GetProxy(&client_ptr)); |
| on_delete_callback_runner_ = factory_->mojo_proxy_factory_->CreateResolver( |
| mojo::String::From(pac_script->utf16()), mojo::GetProxy(&resolver_ptr_), |
| client_ptr.Pass()); |
| 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(resolver_ptr_.Pass(), factory_->host_resolver_, |
| on_delete_callback_runner_.Pass(), |
| error_observer_.Pass(), factory_->net_log_)); |
| } |
| on_delete_callback_runner_.reset(); |
| callback_.Run(error); |
| } |
| |
| ProxyResolverFactoryMojo* const factory_; |
| scoped_ptr<ProxyResolver>* resolver_; |
| const CompletionCallback callback_; |
| interfaces::ProxyResolverPtr resolver_ptr_; |
| mojo::Binding<interfaces::ProxyResolverFactoryRequestClient> binding_; |
| scoped_ptr<base::ScopedClosureRunner> on_delete_callback_runner_; |
| scoped_ptr<ProxyResolverErrorObserver> error_observer_; |
| }; |
| |
| ProxyResolverFactoryMojo::ProxyResolverFactoryMojo( |
| MojoProxyResolverFactory* mojo_proxy_factory, |
| HostResolver* host_resolver, |
| const base::Callback<scoped_ptr<ProxyResolverErrorObserver>()>& |
| 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<ProxyResolverScriptData>& pac_script, |
| scoped_ptr<ProxyResolver>* resolver, |
| const CompletionCallback& callback, |
| scoped_ptr<ProxyResolverFactory::Request>* 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 |