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_mojo.h"
8 #include "base/logging.h"
9 #include "base/stl_util.h"
10 #include "mojo/common/common_type_converters.h"
11 #include "mojo/common/url_type_converters.h"
12 #include "net/base/net_errors.h"
13 #include "net/dns/mojo_host_resolver_impl.h"
14 #include "net/proxy/mojo_proxy_resolver_factory.h"
15 #include "net/proxy/mojo_proxy_type_converters.h"
16 #include "net/proxy/proxy_info.h"
17 #include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h"
18 #include "third_party/mojo/src/mojo/public/cpp/bindings/error_handler.h"
22 class ProxyResolverMojo::Job
: public interfaces::ProxyResolverRequestClient
,
23 public mojo::ErrorHandler
{
25 Job(ProxyResolverMojo
* resolver
,
28 const CompletionCallback
& callback
);
31 // Cancels the job and prevents the callback from being run.
34 // Returns the LoadState of this job.
35 LoadState
load_state() { return load_state_
; }
38 // Overridden from mojo::ErrorHandler:
39 void OnConnectionError() override
;
41 // Overridden from interfaces::ProxyResolverRequestClient:
44 mojo::Array
<interfaces::ProxyServerPtr
> proxy_servers
) override
;
45 void LoadStateChanged(int32_t load_state
) override
;
47 ProxyResolverMojo
* resolver_
;
50 CompletionCallback callback_
;
51 LoadState load_state_
= LOAD_STATE_RESOLVING_PROXY_FOR_URL
;
53 base::ThreadChecker thread_checker_
;
54 mojo::Binding
<interfaces::ProxyResolverRequestClient
> binding_
;
57 ProxyResolverMojo::Job::Job(ProxyResolverMojo
* resolver
,
60 const CompletionCallback
& callback
)
61 : resolver_(resolver
),
66 binding_
.set_error_handler(this);
68 interfaces::ProxyResolverRequestClientPtr client_ptr
;
69 binding_
.Bind(mojo::GetProxy(&client_ptr
));
70 resolver_
->mojo_proxy_resolver_ptr_
->GetProxyForUrl(mojo::String::From(url_
),
74 ProxyResolverMojo::Job::~Job() {
75 DCHECK(thread_checker_
.CalledOnValidThread());
76 if (!callback_
.is_null())
77 callback_
.Run(ERR_PAC_SCRIPT_TERMINATED
);
80 void ProxyResolverMojo::Job::Cancel() {
81 DCHECK(thread_checker_
.CalledOnValidThread());
82 DCHECK(!callback_
.is_null());
86 void ProxyResolverMojo::Job::OnConnectionError() {
87 DCHECK(thread_checker_
.CalledOnValidThread());
88 DVLOG(1) << "ProxyResolverMojo::Job::OnConnectionError";
89 resolver_
->RemoveJob(this);
92 void ProxyResolverMojo::Job::ReportResult(
94 mojo::Array
<interfaces::ProxyServerPtr
> proxy_servers
) {
95 DCHECK(thread_checker_
.CalledOnValidThread());
96 DVLOG(1) << "ProxyResolverMojo::Job::ReportResult: " << error
;
99 *results_
= proxy_servers
.To
<ProxyInfo
>();
100 DVLOG(1) << "Servers: " << results_
->ToPacString();
103 callback_
.Run(error
);
105 resolver_
->RemoveJob(this);
108 void ProxyResolverMojo::Job::LoadStateChanged(int32_t load_state
) {
109 load_state_
= static_cast<LoadState
>(load_state
);
112 ProxyResolverMojo::ProxyResolverMojo(
113 MojoProxyResolverFactory
* mojo_proxy_resolver_factory
,
114 HostResolver
* host_resolver
)
115 : ProxyResolver(true /* |expects_pac_bytes| */),
116 mojo_proxy_resolver_factory_(mojo_proxy_resolver_factory
),
117 host_resolver_(host_resolver
) {
120 ProxyResolverMojo::~ProxyResolverMojo() {
121 DCHECK(thread_checker_
.CalledOnValidThread());
122 // All pending requests should have been cancelled.
123 DCHECK(pending_jobs_
.empty());
124 DCHECK(set_pac_script_callback_
.IsCancelled());
127 void ProxyResolverMojo::CancelSetPacScript() {
128 DCHECK(thread_checker_
.CalledOnValidThread());
129 set_pac_script_callback_
.Cancel();
132 int ProxyResolverMojo::SetPacScript(
133 const scoped_refptr
<ProxyResolverScriptData
>& pac_script
,
134 const CompletionCallback
& callback
) {
135 DCHECK(thread_checker_
.CalledOnValidThread());
136 DCHECK(set_pac_script_callback_
.IsCancelled());
137 DCHECK(!callback
.is_null());
138 if (pac_script
->type() != ProxyResolverScriptData::TYPE_SCRIPT_CONTENTS
||
139 pac_script
->utf16().empty()) {
140 return ERR_PAC_SCRIPT_FAILED
;
143 DVLOG(1) << "ProxyResolverMojo::SetPacScript: " << pac_script
->utf16();
144 set_pac_script_callback_
.Reset(
145 base::Bind(&ProxyResolverMojo::OnSetPacScriptDone
, base::Unretained(this),
146 pac_script
, callback
));
148 if (!mojo_proxy_resolver_ptr_
)
151 mojo_proxy_resolver_ptr_
->SetPacScript(
152 mojo::String::From(pac_script
->utf16()),
153 set_pac_script_callback_
.callback());
155 return ERR_IO_PENDING
;
158 void ProxyResolverMojo::OnSetPacScriptDone(
159 const scoped_refptr
<ProxyResolverScriptData
>& pac_script
,
160 const CompletionCallback
& callback
,
162 DCHECK(thread_checker_
.CalledOnValidThread());
163 DCHECK(!set_pac_script_callback_
.IsCancelled());
164 DVLOG(1) << "ProxyResolverMojo::OnSetPacScriptDone: " << result
;
166 callback
.Run(result
);
167 set_pac_script_callback_
.Cancel();
170 void ProxyResolverMojo::SetUpServices() {
171 DCHECK(thread_checker_
.CalledOnValidThread());
172 // A Mojo service implementation must outlive its binding.
173 mojo_host_resolver_binding_
.reset();
175 interfaces::HostResolverPtr mojo_host_resolver_ptr
;
176 mojo_host_resolver_
.reset(new MojoHostResolverImpl(host_resolver_
));
177 mojo_host_resolver_binding_
.reset(new mojo::Binding
<interfaces::HostResolver
>(
178 mojo_host_resolver_
.get(), mojo::GetProxy(&mojo_host_resolver_ptr
)));
179 mojo_proxy_resolver_ptr_
.reset();
180 mojo_proxy_resolver_factory_
->Create(
181 mojo::GetProxy(&mojo_proxy_resolver_ptr_
), mojo_host_resolver_ptr
.Pass());
182 mojo_proxy_resolver_ptr_
.set_error_handler(this);
185 void ProxyResolverMojo::AbortPendingRequests() {
186 DCHECK(thread_checker_
.CalledOnValidThread());
187 if (!set_pac_script_callback_
.IsCancelled()) {
188 set_pac_script_callback_
.callback().Run(ERR_PAC_SCRIPT_TERMINATED
);
189 set_pac_script_callback_
.Cancel();
192 // Need to use this loop because deleting a Job will cause its callback to be
193 // run with a failure error code, which may cause other Jobs to be deleted.
194 while (!pending_jobs_
.empty()) {
195 auto it
= pending_jobs_
.begin();
197 pending_jobs_
.erase(it
);
199 // Deleting the job will cause its completion callback to be run with an
200 // ERR_PAC_SCRIPT_TERMINATED error.
205 void ProxyResolverMojo::OnConnectionError() {
206 DCHECK(thread_checker_
.CalledOnValidThread());
207 DVLOG(1) << "ProxyResolverMojo::OnConnectionError";
209 // Disconnect from the Mojo proxy resolver service. An attempt to reconnect
210 // will happen on the next |SetPacScript()| request.
211 mojo_proxy_resolver_ptr_
.reset();
213 // Aborting requests will invoke their callbacks, which may call
214 // |SetPacScript()| and re-create the connection. So disconnect from the Mojo
215 // service (above) before aborting the pending requests.
216 AbortPendingRequests();
219 void ProxyResolverMojo::RemoveJob(Job
* job
) {
220 DCHECK(thread_checker_
.CalledOnValidThread());
221 size_t num_erased
= pending_jobs_
.erase(job
);
226 int ProxyResolverMojo::GetProxyForURL(const GURL
& url
,
228 const CompletionCallback
& callback
,
229 RequestHandle
* request
,
230 const BoundNetLog
& net_log
) {
231 DCHECK(thread_checker_
.CalledOnValidThread());
233 // If the Mojo service is not connected, fail. The Mojo service is connected
234 // when the script is set, which must be done after construction and after a
235 // previous request returns ERR_PAC_SCRIPT_TERMINATED due to the Mojo proxy
236 // resolver process crashing.
237 if (!mojo_proxy_resolver_ptr_
) {
238 DVLOG(1) << "ProxyResolverMojo::GetProxyForURL: Mojo not connected";
239 return ERR_PAC_SCRIPT_TERMINATED
;
242 Job
* job
= new Job(this, url
, results
, callback
);
243 bool inserted
= pending_jobs_
.insert(job
).second
;
247 return ERR_IO_PENDING
;
250 void ProxyResolverMojo::CancelRequest(RequestHandle request
) {
251 DCHECK(thread_checker_
.CalledOnValidThread());
252 Job
* job
= static_cast<Job
*>(request
);
258 LoadState
ProxyResolverMojo::GetLoadState(RequestHandle request
) const {
259 Job
* job
= static_cast<Job
*>(request
);
260 CHECK_EQ(1u, pending_jobs_
.count(job
));
261 return job
->load_state();