1 // Copyright 2015 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_factory_mojo.h"
10 #include "base/logging.h"
11 #include "base/stl_util.h"
12 #include "base/threading/thread_checker.h"
13 #include "base/values.h"
14 #include "mojo/common/common_type_converters.h"
15 #include "mojo/common/url_type_converters.h"
16 #include "net/base/load_states.h"
17 #include "net/base/net_errors.h"
18 #include "net/dns/mojo_host_resolver_impl.h"
19 #include "net/interfaces/host_resolver_service.mojom.h"
20 #include "net/interfaces/proxy_resolver_service.mojom.h"
21 #include "net/proxy/mojo_proxy_resolver_factory.h"
22 #include "net/proxy/mojo_proxy_type_converters.h"
23 #include "net/proxy/proxy_info.h"
24 #include "net/proxy/proxy_resolver.h"
25 #include "net/proxy/proxy_resolver_error_observer.h"
26 #include "net/proxy/proxy_resolver_script_data.h"
27 #include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h"
32 scoped_ptr
<base::Value
> NetLogErrorCallback(
34 const base::string16
* message
,
35 NetLogCaptureMode
/* capture_mode */) {
36 scoped_ptr
<base::DictionaryValue
> dict(new base::DictionaryValue());
37 dict
->SetInteger("line_number", line_number
);
38 dict
->SetString("message", *message
);
42 // A mixin that forwards logging to (Bound)NetLog and ProxyResolverErrorObserver
43 // and DNS requests to a MojoHostResolverImpl, which is implemented in terms of
45 template <typename ClientInterface
>
46 class ClientMixin
: public ClientInterface
{
48 ClientMixin(HostResolver
* host_resolver
,
49 ProxyResolverErrorObserver
* error_observer
,
51 const BoundNetLog
& bound_net_log
)
52 : host_resolver_(host_resolver
, bound_net_log
),
53 error_observer_(error_observer
),
55 bound_net_log_(bound_net_log
) {}
57 // Overridden from ClientInterface:
58 void Alert(const mojo::String
& message
) override
{
59 base::string16 message_str
= message
.To
<base::string16
>();
60 auto callback
= NetLog::StringCallback("message", &message_str
);
61 bound_net_log_
.AddEvent(NetLog::TYPE_PAC_JAVASCRIPT_ALERT
, callback
);
63 net_log_
->AddGlobalEntry(NetLog::TYPE_PAC_JAVASCRIPT_ALERT
, callback
);
66 void OnError(int32_t line_number
, const mojo::String
& message
) override
{
67 base::string16 message_str
= message
.To
<base::string16
>();
68 auto callback
= base::Bind(&NetLogErrorCallback
, line_number
, &message_str
);
69 bound_net_log_
.AddEvent(NetLog::TYPE_PAC_JAVASCRIPT_ERROR
, callback
);
71 net_log_
->AddGlobalEntry(NetLog::TYPE_PAC_JAVASCRIPT_ERROR
, callback
);
73 error_observer_
->OnPACScriptError(line_number
, message_str
);
76 void ResolveDns(interfaces::HostResolverRequestInfoPtr request_info
,
77 interfaces::HostResolverRequestClientPtr client
) override
{
78 host_resolver_
.Resolve(request_info
.Pass(), client
.Pass());
82 bool dns_request_in_progress() {
83 return host_resolver_
.request_in_progress();
87 MojoHostResolverImpl host_resolver_
;
88 ProxyResolverErrorObserver
* const error_observer_
;
89 NetLog
* const net_log_
;
90 const BoundNetLog bound_net_log_
;
93 // Implementation of ProxyResolver that connects to a Mojo service to evaluate
94 // PAC scripts. This implementation only knows about Mojo services, and
95 // therefore that service may live in or out of process.
97 // This implementation reports disconnections from the Mojo service (i.e. if the
98 // service is out-of-process and that process crashes) using the error code
99 // ERR_PAC_SCRIPT_TERMINATED.
100 class ProxyResolverMojo
: public ProxyResolver
{
102 // Constructs a ProxyResolverMojo that connects to a mojo proxy resolver
103 // implementation using |resolver_ptr|. The implementation uses
104 // |host_resolver| as the DNS resolver, using |host_resolver_binding| to
105 // communicate with it. When deleted, the closure contained within
106 // |on_delete_callback_runner| will be run.
108 interfaces::ProxyResolverPtr resolver_ptr
,
109 HostResolver
* host_resolver
,
110 scoped_ptr
<base::ScopedClosureRunner
> on_delete_callback_runner
,
111 scoped_ptr
<ProxyResolverErrorObserver
> error_observer
,
113 ~ProxyResolverMojo() override
;
115 // ProxyResolver implementation:
116 int GetProxyForURL(const GURL
& url
,
118 const net::CompletionCallback
& callback
,
119 RequestHandle
* request
,
120 const BoundNetLog
& net_log
) override
;
121 void CancelRequest(RequestHandle request
) override
;
122 LoadState
GetLoadState(RequestHandle request
) const override
;
127 // Mojo error handler.
128 void OnConnectionError();
130 void RemoveJob(Job
* job
);
132 // Connection to the Mojo proxy resolver.
133 interfaces::ProxyResolverPtr mojo_proxy_resolver_ptr_
;
135 HostResolver
* host_resolver_
;
137 scoped_ptr
<ProxyResolverErrorObserver
> error_observer_
;
141 std::set
<Job
*> pending_jobs_
;
143 base::ThreadChecker thread_checker_
;
145 scoped_ptr
<base::ScopedClosureRunner
> on_delete_callback_runner_
;
147 DISALLOW_COPY_AND_ASSIGN(ProxyResolverMojo
);
150 class ProxyResolverMojo::Job
151 : public ClientMixin
<interfaces::ProxyResolverRequestClient
> {
153 Job(ProxyResolverMojo
* resolver
,
156 const CompletionCallback
& callback
,
157 const BoundNetLog
& net_log
);
160 // Cancels the job and prevents the callback from being run.
163 // Returns the LoadState of this job.
164 LoadState
GetLoadState();
167 // Mojo error handler.
168 void OnConnectionError();
170 // Overridden from interfaces::ProxyResolverRequestClient:
173 mojo::Array
<interfaces::ProxyServerPtr
> proxy_servers
) override
;
175 ProxyResolverMojo
* resolver_
;
178 CompletionCallback callback_
;
180 base::ThreadChecker thread_checker_
;
181 mojo::Binding
<interfaces::ProxyResolverRequestClient
> binding_
;
184 ProxyResolverMojo::Job::Job(ProxyResolverMojo
* resolver
,
187 const CompletionCallback
& callback
,
188 const BoundNetLog
& net_log
)
189 : ClientMixin
<interfaces::ProxyResolverRequestClient
>(
190 resolver
->host_resolver_
,
191 resolver
->error_observer_
.get(),
199 binding_
.set_connection_error_handler(base::Bind(
200 &ProxyResolverMojo::Job::OnConnectionError
, base::Unretained(this)));
202 interfaces::ProxyResolverRequestClientPtr client_ptr
;
203 binding_
.Bind(mojo::GetProxy(&client_ptr
));
204 resolver_
->mojo_proxy_resolver_ptr_
->GetProxyForUrl(mojo::String::From(url_
),
208 ProxyResolverMojo::Job::~Job() {
209 DCHECK(thread_checker_
.CalledOnValidThread());
210 if (!callback_
.is_null())
211 callback_
.Run(ERR_PAC_SCRIPT_TERMINATED
);
214 void ProxyResolverMojo::Job::Cancel() {
215 DCHECK(thread_checker_
.CalledOnValidThread());
216 DCHECK(!callback_
.is_null());
220 LoadState
ProxyResolverMojo::Job::GetLoadState() {
221 return dns_request_in_progress() ? LOAD_STATE_RESOLVING_HOST_IN_PROXY_SCRIPT
222 : LOAD_STATE_RESOLVING_PROXY_FOR_URL
;
225 void ProxyResolverMojo::Job::OnConnectionError() {
226 DCHECK(thread_checker_
.CalledOnValidThread());
227 DVLOG(1) << "ProxyResolverMojo::Job::OnConnectionError";
228 resolver_
->RemoveJob(this);
231 void ProxyResolverMojo::Job::ReportResult(
233 mojo::Array
<interfaces::ProxyServerPtr
> proxy_servers
) {
234 DCHECK(thread_checker_
.CalledOnValidThread());
235 DVLOG(1) << "ProxyResolverMojo::Job::ReportResult: " << error
;
238 *results_
= proxy_servers
.To
<ProxyInfo
>();
239 DVLOG(1) << "Servers: " << results_
->ToPacString();
242 CompletionCallback callback
= callback_
;
244 resolver_
->RemoveJob(this);
248 ProxyResolverMojo::ProxyResolverMojo(
249 interfaces::ProxyResolverPtr resolver_ptr
,
250 HostResolver
* host_resolver
,
251 scoped_ptr
<base::ScopedClosureRunner
> on_delete_callback_runner
,
252 scoped_ptr
<ProxyResolverErrorObserver
> error_observer
,
254 : mojo_proxy_resolver_ptr_(resolver_ptr
.Pass()),
255 host_resolver_(host_resolver
),
256 error_observer_(error_observer
.Pass()),
258 on_delete_callback_runner_(on_delete_callback_runner
.Pass()) {
259 mojo_proxy_resolver_ptr_
.set_connection_error_handler(base::Bind(
260 &ProxyResolverMojo::OnConnectionError
, base::Unretained(this)));
263 ProxyResolverMojo::~ProxyResolverMojo() {
264 DCHECK(thread_checker_
.CalledOnValidThread());
265 // All pending requests should have been cancelled.
266 DCHECK(pending_jobs_
.empty());
269 void ProxyResolverMojo::OnConnectionError() {
270 DCHECK(thread_checker_
.CalledOnValidThread());
271 DVLOG(1) << "ProxyResolverMojo::OnConnectionError";
273 // Disconnect from the Mojo proxy resolver service.
274 mojo_proxy_resolver_ptr_
.reset();
277 void ProxyResolverMojo::RemoveJob(Job
* job
) {
278 DCHECK(thread_checker_
.CalledOnValidThread());
279 size_t num_erased
= pending_jobs_
.erase(job
);
284 int ProxyResolverMojo::GetProxyForURL(const GURL
& url
,
286 const CompletionCallback
& callback
,
287 RequestHandle
* request
,
288 const BoundNetLog
& net_log
) {
289 DCHECK(thread_checker_
.CalledOnValidThread());
291 if (!mojo_proxy_resolver_ptr_
)
292 return ERR_PAC_SCRIPT_TERMINATED
;
294 Job
* job
= new Job(this, url
, results
, callback
, net_log
);
295 bool inserted
= pending_jobs_
.insert(job
).second
;
299 return ERR_IO_PENDING
;
302 void ProxyResolverMojo::CancelRequest(RequestHandle request
) {
303 DCHECK(thread_checker_
.CalledOnValidThread());
304 Job
* job
= static_cast<Job
*>(request
);
310 LoadState
ProxyResolverMojo::GetLoadState(RequestHandle request
) const {
311 Job
* job
= static_cast<Job
*>(request
);
312 CHECK_EQ(1u, pending_jobs_
.count(job
));
313 return job
->GetLoadState();
318 // A Job to create a ProxyResolver instance.
320 // Note: a Job instance is not tied to a particular resolve request, and hence
321 // there is no per-request logging to be done (any netlog events are only sent
322 // globally) so this always uses an empty BoundNetLog.
323 class ProxyResolverFactoryMojo::Job
324 : public ClientMixin
<interfaces::ProxyResolverFactoryRequestClient
>,
325 public ProxyResolverFactory::Request
{
327 Job(ProxyResolverFactoryMojo
* factory
,
328 const scoped_refptr
<ProxyResolverScriptData
>& pac_script
,
329 scoped_ptr
<ProxyResolver
>* resolver
,
330 const CompletionCallback
& callback
,
331 scoped_ptr
<ProxyResolverErrorObserver
> error_observer
)
332 : ClientMixin
<interfaces::ProxyResolverFactoryRequestClient
>(
333 factory
->host_resolver_
,
334 error_observer
.get(),
341 error_observer_(error_observer
.Pass()) {
342 interfaces::ProxyResolverFactoryRequestClientPtr client_ptr
;
343 binding_
.Bind(mojo::GetProxy(&client_ptr
));
344 on_delete_callback_runner_
= factory_
->mojo_proxy_factory_
->CreateResolver(
345 mojo::String::From(pac_script
->utf16()), mojo::GetProxy(&resolver_ptr_
),
347 resolver_ptr_
.set_connection_error_handler(
348 base::Bind(&ProxyResolverFactoryMojo::Job::OnConnectionError
,
349 base::Unretained(this)));
350 binding_
.set_connection_error_handler(
351 base::Bind(&ProxyResolverFactoryMojo::Job::OnConnectionError
,
352 base::Unretained(this)));
355 void OnConnectionError() { ReportResult(ERR_PAC_SCRIPT_TERMINATED
); }
358 void ReportResult(int32_t error
) override
{
359 resolver_ptr_
.set_connection_error_handler(mojo::Closure());
360 binding_
.set_connection_error_handler(mojo::Closure());
363 new ProxyResolverMojo(resolver_ptr_
.Pass(), factory_
->host_resolver_
,
364 on_delete_callback_runner_
.Pass(),
365 error_observer_
.Pass(), factory_
->net_log_
));
367 on_delete_callback_runner_
.reset();
368 callback_
.Run(error
);
371 ProxyResolverFactoryMojo
* const factory_
;
372 scoped_ptr
<ProxyResolver
>* resolver_
;
373 const CompletionCallback callback_
;
374 interfaces::ProxyResolverPtr resolver_ptr_
;
375 mojo::Binding
<interfaces::ProxyResolverFactoryRequestClient
> binding_
;
376 scoped_ptr
<base::ScopedClosureRunner
> on_delete_callback_runner_
;
377 scoped_ptr
<ProxyResolverErrorObserver
> error_observer_
;
380 ProxyResolverFactoryMojo::ProxyResolverFactoryMojo(
381 MojoProxyResolverFactory
* mojo_proxy_factory
,
382 HostResolver
* host_resolver
,
383 const base::Callback
<scoped_ptr
<ProxyResolverErrorObserver
>()>&
384 error_observer_factory
,
386 : ProxyResolverFactory(true),
387 mojo_proxy_factory_(mojo_proxy_factory
),
388 host_resolver_(host_resolver
),
389 error_observer_factory_(error_observer_factory
),
393 ProxyResolverFactoryMojo::~ProxyResolverFactoryMojo() = default;
395 int ProxyResolverFactoryMojo::CreateProxyResolver(
396 const scoped_refptr
<ProxyResolverScriptData
>& pac_script
,
397 scoped_ptr
<ProxyResolver
>* resolver
,
398 const CompletionCallback
& callback
,
399 scoped_ptr
<ProxyResolverFactory::Request
>* request
) {
402 if (pac_script
->type() != ProxyResolverScriptData::TYPE_SCRIPT_CONTENTS
||
403 pac_script
->utf16().empty()) {
404 return ERR_PAC_SCRIPT_FAILED
;
406 request
->reset(new Job(this, pac_script
, resolver
, callback
,
407 error_observer_factory_
.is_null()
409 : error_observer_factory_
.Run()));
410 return ERR_IO_PENDING
;