1 // Copyright 2015 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "services/proxy_resolver/proxy_resolver_impl.h"
11 #include "base/bind.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/memory/raw_ptr.h"
14 #include "base/ranges/algorithm.h"
15 #include "base/run_loop.h"
16 #include "base/test/task_environment.h"
17 #include "mojo/public/cpp/bindings/receiver.h"
18 #include "net/base/completion_once_callback.h"
19 #include "net/base/net_errors.h"
20 #include "net/base/proxy_server.h"
21 #include "net/proxy_resolution/mock_proxy_resolver.h"
22 #include "net/proxy_resolution/proxy_info.h"
23 #include "net/proxy_resolution/proxy_resolve_dns_operation.h"
24 #include "net/test/event_waiter.h"
25 #include "net/test/gtest_util.h"
26 #include "services/proxy_resolver/proxy_resolver_v8_tracing.h"
27 #include "services/service_manager/public/cpp/service_context_ref.h"
28 #include "testing/gmock/include/gmock/gmock.h"
29 #include "testing/gtest/include/gtest/gtest.h"
31 #include "url/origin.h"
33 using net::test::IsError;
34 using net::test::IsOk;
36 namespace proxy_resolver {
39 class TestRequestClient : public mojom::ProxyResolverRequestClient {
46 explicit TestRequestClient(
47 mojo::PendingReceiver<mojom::ProxyResolverRequestClient> receiver);
51 net::Error error() { return error_; }
52 const net::ProxyInfo& results() { return results_; }
53 net::EventWaiter<Event>& event_waiter() { return event_waiter_; }
56 // mojom::ProxyResolverRequestClient override.
57 void ReportResult(int32_t error, const net::ProxyInfo& results) override;
58 void Alert(const std::string& message) override;
59 void OnError(int32_t line_number, const std::string& message) override;
61 const std::string& hostname,
62 net::ProxyResolveDnsOperation operation,
63 const net::NetworkAnonymizationKey& network_anonymization_key,
64 mojo::PendingRemote<mojom::HostResolverRequestClient> client) override;
69 net::Error error_ = net::ERR_FAILED;
70 net::ProxyInfo results_;
72 mojo::Receiver<mojom::ProxyResolverRequestClient> receiver_;
74 net::EventWaiter<Event> event_waiter_;
77 TestRequestClient::TestRequestClient(
78 mojo::PendingReceiver<mojom::ProxyResolverRequestClient> receiver)
79 : receiver_(this, std::move(receiver)) {
80 receiver_.set_disconnect_handler(
81 base::BindOnce(&TestRequestClient::OnDisconnect, base::Unretained(this)));
84 void TestRequestClient::WaitForResult() {
88 event_waiter_.WaitForEvent(RESULT_RECEIVED);
92 void TestRequestClient::ReportResult(int32_t error,
93 const net::ProxyInfo& results) {
94 event_waiter_.NotifyEvent(RESULT_RECEIVED);
96 error_ = static_cast<net::Error>(error);
101 void TestRequestClient::Alert(const std::string& message) {}
103 void TestRequestClient::OnError(int32_t line_number,
104 const std::string& message) {}
106 void TestRequestClient::ResolveDns(
107 const std::string& hostname,
108 net::ProxyResolveDnsOperation operation,
109 const net::NetworkAnonymizationKey& network_anonymization_key,
110 mojo::PendingRemote<mojom::HostResolverRequestClient> client) {}
112 void TestRequestClient::OnDisconnect() {
113 event_waiter_.NotifyEvent(CONNECTION_ERROR);
116 class MockProxyResolverV8Tracing : public ProxyResolverV8Tracing {
118 // TODO(mmenke): This struct violates the Google style guide, as structs
119 // aren't allowed to have private members. Fix that.
122 net::NetworkAnonymizationKey network_anonymization_key;
123 raw_ptr<net::ProxyInfo> results;
124 bool cancelled = false;
126 void Complete(int result) {
127 DCHECK(!callback_.is_null());
128 std::move(callback_).Run(result);
131 bool WasCompleted() { return callback_.is_null(); }
133 void SetCallback(net::CompletionOnceCallback callback) {
134 callback_ = std::move(callback);
138 net::CompletionOnceCallback callback_;
141 class RequestImpl : public net::ProxyResolver::Request {
143 RequestImpl(Job* job, MockProxyResolverV8Tracing* resolver)
144 : job_(job), resolver_(resolver) {}
146 ~RequestImpl() override {
147 if (job_->WasCompleted())
149 job_->cancelled = true;
150 if (resolver_->cancel_callback_)
151 std::move(resolver_->cancel_callback_).Run();
154 net::LoadState GetLoadState() override {
155 return net::LOAD_STATE_RESOLVING_PROXY_FOR_URL;
160 raw_ptr<MockProxyResolverV8Tracing> resolver_;
163 MockProxyResolverV8Tracing() {}
165 // ProxyResolverV8Tracing overrides.
168 const net::NetworkAnonymizationKey& network_anonymization_key,
169 net::ProxyInfo* results,
170 net::CompletionOnceCallback callback,
171 std::unique_ptr<net::ProxyResolver::Request>* request,
172 std::unique_ptr<Bindings> bindings) override;
174 void WaitForCancel();
176 const std::vector<std::unique_ptr<Job>>& pending_jobs() {
177 return pending_jobs_;
181 base::OnceClosure cancel_callback_;
182 std::vector<std::unique_ptr<Job>> pending_jobs_;
185 void MockProxyResolverV8Tracing::GetProxyForURL(
187 const net::NetworkAnonymizationKey& network_anonymization_key,
188 net::ProxyInfo* results,
189 net::CompletionOnceCallback callback,
190 std::unique_ptr<net::ProxyResolver::Request>* request,
191 std::unique_ptr<Bindings> bindings) {
192 pending_jobs_.push_back(std::make_unique<Job>());
193 auto* pending_job = pending_jobs_.back().get();
194 pending_job->url = url;
195 pending_job->network_anonymization_key = network_anonymization_key;
196 pending_job->results = results;
197 pending_job->SetCallback(std::move(callback));
198 *request = std::make_unique<RequestImpl>(pending_job, this);
201 void MockProxyResolverV8Tracing::WaitForCancel() {
202 while (base::ranges::any_of(pending_jobs_, &Job::cancelled)) {
203 base::RunLoop run_loop;
204 cancel_callback_ = run_loop.QuitClosure();
211 class ProxyResolverImplTest : public testing::Test {
213 ProxyResolverImplTest() {
214 std::unique_ptr<MockProxyResolverV8Tracing> mock_resolver =
215 std::make_unique<MockProxyResolverV8Tracing>();
216 mock_proxy_resolver_ = mock_resolver.get();
218 std::make_unique<ProxyResolverImpl>(std::move(mock_resolver));
219 resolver_ = resolver_impl_.get();
222 ~ProxyResolverImplTest() override = default;
225 base::test::TaskEnvironment task_environment_;
226 raw_ptr<MockProxyResolverV8Tracing> mock_proxy_resolver_;
228 std::unique_ptr<ProxyResolverImpl> resolver_impl_;
229 raw_ptr<mojom::ProxyResolver> resolver_;
232 TEST_F(ProxyResolverImplTest, GetProxyForUrl) {
233 mojo::PendingRemote<mojom::ProxyResolverRequestClient> remote_client;
234 TestRequestClient client(remote_client.InitWithNewPipeAndPassReceiver());
236 resolver_->GetProxyForUrl(GURL("http://example.com"),
237 net::NetworkAnonymizationKey(),
238 std::move(remote_client));
239 ASSERT_EQ(1u, mock_proxy_resolver_->pending_jobs().size());
240 MockProxyResolverV8Tracing::Job* job =
241 mock_proxy_resolver_->pending_jobs()[0].get();
242 EXPECT_EQ(GURL("http://example.com"), job->url);
244 job->results->UsePacString(
245 "PROXY proxy.example.com:1; "
246 "SOCKS4 socks4.example.com:2; "
247 "SOCKS5 socks5.example.com:3; "
248 "HTTPS https.example.com:4; "
249 "QUIC quic.example.com:65000; "
251 job->Complete(net::OK);
252 client.WaitForResult();
254 EXPECT_THAT(client.error(), IsOk());
255 std::vector<net::ProxyServer> servers =
256 client.results().proxy_list().GetAll();
257 ASSERT_EQ(6u, servers.size());
258 EXPECT_EQ(net::ProxyServer::SCHEME_HTTP, servers[0].scheme());
259 EXPECT_EQ("proxy.example.com", servers[0].host_port_pair().host());
260 EXPECT_EQ(1, servers[0].host_port_pair().port());
262 EXPECT_EQ(net::ProxyServer::SCHEME_SOCKS4, servers[1].scheme());
263 EXPECT_EQ("socks4.example.com", servers[1].host_port_pair().host());
264 EXPECT_EQ(2, servers[1].host_port_pair().port());
266 EXPECT_EQ(net::ProxyServer::SCHEME_SOCKS5, servers[2].scheme());
267 EXPECT_EQ("socks5.example.com", servers[2].host_port_pair().host());
268 EXPECT_EQ(3, servers[2].host_port_pair().port());
270 EXPECT_EQ(net::ProxyServer::SCHEME_HTTPS, servers[3].scheme());
271 EXPECT_EQ("https.example.com", servers[3].host_port_pair().host());
272 EXPECT_EQ(4, servers[3].host_port_pair().port());
274 EXPECT_EQ(net::ProxyServer::SCHEME_QUIC, servers[4].scheme());
275 EXPECT_EQ("quic.example.com", servers[4].host_port_pair().host());
276 EXPECT_EQ(65000, servers[4].host_port_pair().port());
278 EXPECT_EQ(net::ProxyServer::SCHEME_DIRECT, servers[5].scheme());
281 TEST_F(ProxyResolverImplTest, GetProxyForUrlWithNetworkAnonymizationKey) {
282 const net::SchemefulSite kSite(
283 net::SchemefulSite(GURL("https://site.test/")));
284 const net::NetworkAnonymizationKey kNetworkAnonymizationKey(kSite, kSite);
286 mojo::PendingRemote<mojom::ProxyResolverRequestClient> remote_client;
287 TestRequestClient client(remote_client.InitWithNewPipeAndPassReceiver());
289 resolver_->GetProxyForUrl(GURL("http://example.com"),
290 kNetworkAnonymizationKey, std::move(remote_client));
291 ASSERT_EQ(1u, mock_proxy_resolver_->pending_jobs().size());
292 MockProxyResolverV8Tracing::Job* job =
293 mock_proxy_resolver_->pending_jobs()[0].get();
294 EXPECT_EQ(GURL("http://example.com"), job->url);
295 EXPECT_EQ(kNetworkAnonymizationKey, job->network_anonymization_key);
298 TEST_F(ProxyResolverImplTest, GetProxyForUrlFailure) {
299 mojo::PendingRemote<mojom::ProxyResolverRequestClient> remote_client;
300 TestRequestClient client(remote_client.InitWithNewPipeAndPassReceiver());
302 resolver_->GetProxyForUrl(GURL("http://example.com"),
303 net::NetworkAnonymizationKey(),
304 std::move(remote_client));
305 ASSERT_EQ(1u, mock_proxy_resolver_->pending_jobs().size());
306 MockProxyResolverV8Tracing::Job* job =
307 mock_proxy_resolver_->pending_jobs()[0].get();
308 EXPECT_EQ(GURL("http://example.com"), job->url);
309 job->Complete(net::ERR_FAILED);
310 client.WaitForResult();
312 EXPECT_THAT(client.error(), IsError(net::ERR_FAILED));
313 std::vector<net::ProxyServer> proxy_servers =
314 client.results().proxy_list().GetAll();
315 EXPECT_TRUE(proxy_servers.empty());
318 TEST_F(ProxyResolverImplTest, GetProxyForUrlMultiple) {
319 mojo::PendingRemote<mojom::ProxyResolverRequestClient> remote_client1;
320 TestRequestClient client1(remote_client1.InitWithNewPipeAndPassReceiver());
321 mojo::PendingRemote<mojom::ProxyResolverRequestClient> remote_client2;
322 TestRequestClient client2(remote_client2.InitWithNewPipeAndPassReceiver());
324 resolver_->GetProxyForUrl(GURL("http://example.com"),
325 net::NetworkAnonymizationKey(),
326 std::move(remote_client1));
327 resolver_->GetProxyForUrl(GURL("https://example.com"),
328 net::NetworkAnonymizationKey(),
329 std::move(remote_client2));
330 ASSERT_EQ(2u, mock_proxy_resolver_->pending_jobs().size());
331 MockProxyResolverV8Tracing::Job* job1 =
332 mock_proxy_resolver_->pending_jobs()[0].get();
333 EXPECT_EQ(GURL("http://example.com"), job1->url);
334 MockProxyResolverV8Tracing::Job* job2 =
335 mock_proxy_resolver_->pending_jobs()[1].get();
336 EXPECT_EQ(GURL("https://example.com"), job2->url);
337 job1->results->UsePacString("HTTPS proxy.example.com:12345");
338 job1->Complete(net::OK);
339 job2->results->UsePacString("SOCKS5 another-proxy.example.com:6789");
340 job2->Complete(net::OK);
341 client1.WaitForResult();
342 client2.WaitForResult();
344 EXPECT_THAT(client1.error(), IsOk());
345 std::vector<net::ProxyServer> proxy_servers1 =
346 client1.results().proxy_list().GetAll();
347 ASSERT_EQ(1u, proxy_servers1.size());
348 net::ProxyServer& server1 = proxy_servers1[0];
349 EXPECT_EQ(net::ProxyServer::SCHEME_HTTPS, server1.scheme());
350 EXPECT_EQ("proxy.example.com", server1.host_port_pair().host());
351 EXPECT_EQ(12345, server1.host_port_pair().port());
353 EXPECT_THAT(client2.error(), IsOk());
354 std::vector<net::ProxyServer> proxy_servers2 =
355 client2.results().proxy_list().GetAll();
356 ASSERT_EQ(1u, proxy_servers1.size());
357 net::ProxyServer& server2 = proxy_servers2[0];
358 EXPECT_EQ(net::ProxyServer::SCHEME_SOCKS5, server2.scheme());
359 EXPECT_EQ("another-proxy.example.com", server2.host_port_pair().host());
360 EXPECT_EQ(6789, server2.host_port_pair().port());
363 TEST_F(ProxyResolverImplTest, DestroyClient) {
364 mojo::PendingRemote<mojom::ProxyResolverRequestClient> remote_client;
365 auto client = std::make_unique<TestRequestClient>(
366 remote_client.InitWithNewPipeAndPassReceiver());
368 resolver_->GetProxyForUrl(GURL("http://example.com"),
369 net::NetworkAnonymizationKey(),
370 std::move(remote_client));
371 ASSERT_EQ(1u, mock_proxy_resolver_->pending_jobs().size());
372 const MockProxyResolverV8Tracing::Job* job =
373 mock_proxy_resolver_->pending_jobs()[0].get();
374 EXPECT_EQ(GURL("http://example.com"), job->url);
375 job->results->UsePacString("PROXY proxy.example.com:8080");
377 mock_proxy_resolver_->WaitForCancel();
380 TEST_F(ProxyResolverImplTest, DestroyService) {
381 mojo::PendingRemote<mojom::ProxyResolverRequestClient> remote_client;
382 TestRequestClient client(remote_client.InitWithNewPipeAndPassReceiver());
384 resolver_->GetProxyForUrl(GURL("http://example.com"),
385 net::NetworkAnonymizationKey(),
386 std::move(remote_client));
387 ASSERT_EQ(1u, mock_proxy_resolver_->pending_jobs().size());
388 resolver_impl_.reset();
389 client.event_waiter().WaitForEvent(TestRequestClient::CONNECTION_ERROR);
392 } // namespace proxy_resolver