xref: /aosp_15_r20/external/cronet/net/proxy_resolution/multi_threaded_proxy_resolver_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 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.
4 
5 #include "net/proxy_resolution/multi_threaded_proxy_resolver.h"
6 
7 #include <memory>
8 #include <utility>
9 #include <vector>
10 
11 #include "base/functional/bind.h"
12 #include "base/memory/raw_ptr.h"
13 #include "base/run_loop.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/synchronization/condition_variable.h"
18 #include "base/synchronization/lock.h"
19 #include "base/threading/platform_thread.h"
20 #include "base/threading/thread_checker_impl.h"
21 #include "base/time/time.h"
22 #include "net/base/net_errors.h"
23 #include "net/base/network_anonymization_key.h"
24 #include "net/base/schemeful_site.h"
25 #include "net/base/test_completion_callback.h"
26 #include "net/log/net_log_event_type.h"
27 #include "net/log/net_log_with_source.h"
28 #include "net/log/test_net_log.h"
29 #include "net/log/test_net_log_util.h"
30 #include "net/proxy_resolution/mock_proxy_resolver.h"
31 #include "net/proxy_resolution/proxy_info.h"
32 #include "net/proxy_resolution/proxy_resolver_factory.h"
33 #include "net/test/gtest_util.h"
34 #include "net/test/test_with_task_environment.h"
35 #include "testing/gmock/include/gmock/gmock.h"
36 #include "testing/gtest/include/gtest/gtest.h"
37 #include "url/gurl.h"
38 
39 using net::test::IsError;
40 using net::test::IsOk;
41 
42 using base::ASCIIToUTF16;
43 
44 namespace net {
45 
46 namespace {
47 
48 // A synchronous mock ProxyResolver implementation, which can be used in
49 // conjunction with MultiThreadedProxyResolver.
50 //       - returns a single-item proxy list with the query's host.
51 class MockProxyResolver : public ProxyResolver {
52  public:
53   MockProxyResolver() = default;
54 
55   // ProxyResolver implementation.
GetProxyForURL(const GURL & query_url,const NetworkAnonymizationKey & network_anonymization_key,ProxyInfo * results,CompletionOnceCallback callback,std::unique_ptr<Request> * request,const NetLogWithSource & net_log)56   int GetProxyForURL(const GURL& query_url,
57                      const NetworkAnonymizationKey& network_anonymization_key,
58                      ProxyInfo* results,
59                      CompletionOnceCallback callback,
60                      std::unique_ptr<Request>* request,
61                      const NetLogWithSource& net_log) override {
62     last_query_url_ = query_url;
63     last_network_anonymization_key_ = network_anonymization_key;
64 
65     if (!resolve_latency_.is_zero())
66       base::PlatformThread::Sleep(resolve_latency_);
67 
68     EXPECT_TRUE(worker_thread_checker_.CalledOnValidThread());
69 
70     EXPECT_TRUE(callback.is_null());
71     EXPECT_TRUE(request == nullptr);
72 
73     // Write something into |net_log| (doesn't really have any meaning.)
74     net_log.BeginEvent(NetLogEventType::PAC_JAVASCRIPT_ALERT);
75 
76     results->UseNamedProxy(query_url.host());
77 
78     // Return a success code which represents the request's order.
79     return request_count_++;
80   }
81 
request_count() const82   int request_count() const { return request_count_; }
83 
SetResolveLatency(base::TimeDelta latency)84   void SetResolveLatency(base::TimeDelta latency) {
85     resolve_latency_ = latency;
86   }
87 
88   // Return the most recent values passed to GetProxyForURL(), if any.
last_query_url() const89   const GURL& last_query_url() const { return last_query_url_; }
last_network_anonymization_key() const90   const NetworkAnonymizationKey& last_network_anonymization_key() const {
91     return last_network_anonymization_key_;
92   }
93 
94  private:
95   base::ThreadCheckerImpl worker_thread_checker_;
96   int request_count_ = 0;
97   base::TimeDelta resolve_latency_;
98 
99   GURL last_query_url_;
100   NetworkAnonymizationKey last_network_anonymization_key_;
101 };
102 
103 
104 // A mock synchronous ProxyResolver which can be set to block upon reaching
105 // GetProxyForURL().
106 class BlockableProxyResolver : public MockProxyResolver {
107  public:
108   enum class State {
109     NONE,
110     BLOCKED,
111     WILL_BLOCK,
112   };
113 
BlockableProxyResolver()114   BlockableProxyResolver() : condition_(&lock_) {}
115 
116   BlockableProxyResolver(const BlockableProxyResolver&) = delete;
117   BlockableProxyResolver& operator=(const BlockableProxyResolver&) = delete;
118 
~BlockableProxyResolver()119   ~BlockableProxyResolver() override {
120     base::AutoLock lock(lock_);
121     EXPECT_NE(State::BLOCKED, state_);
122   }
123 
124   // Causes the next call into GetProxyForURL() to block. Must be followed by
125   // a call to Unblock().
Block()126   void Block() {
127     base::AutoLock lock(lock_);
128     EXPECT_EQ(State::NONE, state_);
129     state_ = State::WILL_BLOCK;
130     condition_.Broadcast();
131   }
132 
133   // Unblocks the ProxyResolver. The ProxyResolver must already be in a
134   // blocked state prior to calling.
Unblock()135   void Unblock() {
136     base::AutoLock lock(lock_);
137     EXPECT_EQ(State::BLOCKED, state_);
138     state_ = State::NONE;
139     condition_.Broadcast();
140   }
141 
142   // Waits until the proxy resolver is blocked within GetProxyForURL().
WaitUntilBlocked()143   void WaitUntilBlocked() {
144     base::AutoLock lock(lock_);
145     while (state_ != State::BLOCKED)
146       condition_.Wait();
147   }
148 
GetProxyForURL(const GURL & query_url,const NetworkAnonymizationKey & network_anonymization_key,ProxyInfo * results,CompletionOnceCallback callback,std::unique_ptr<Request> * request,const NetLogWithSource & net_log)149   int GetProxyForURL(const GURL& query_url,
150                      const NetworkAnonymizationKey& network_anonymization_key,
151                      ProxyInfo* results,
152                      CompletionOnceCallback callback,
153                      std::unique_ptr<Request>* request,
154                      const NetLogWithSource& net_log) override {
155     {
156       base::AutoLock lock(lock_);
157 
158       EXPECT_NE(State::BLOCKED, state_);
159 
160       if (state_ == State::WILL_BLOCK) {
161         state_ = State::BLOCKED;
162         condition_.Broadcast();
163 
164         while (state_ == State::BLOCKED)
165           condition_.Wait();
166       }
167     }
168 
169     return MockProxyResolver::GetProxyForURL(
170         query_url, network_anonymization_key, results, std::move(callback),
171         request, net_log);
172   }
173 
174  private:
175   State state_ = State::NONE;
176   base::Lock lock_;
177   base::ConditionVariable condition_;
178 };
179 
180 // This factory returns new instances of BlockableProxyResolver.
181 class BlockableProxyResolverFactory : public ProxyResolverFactory {
182  public:
BlockableProxyResolverFactory()183   BlockableProxyResolverFactory() : ProxyResolverFactory(false) {}
184 
185   ~BlockableProxyResolverFactory() override = default;
186 
CreateProxyResolver(const scoped_refptr<PacFileData> & script_data,std::unique_ptr<ProxyResolver> * result,CompletionOnceCallback callback,std::unique_ptr<Request> * request)187   int CreateProxyResolver(const scoped_refptr<PacFileData>& script_data,
188                           std::unique_ptr<ProxyResolver>* result,
189                           CompletionOnceCallback callback,
190                           std::unique_ptr<Request>* request) override {
191     auto resolver = std::make_unique<BlockableProxyResolver>();
192     BlockableProxyResolver* resolver_ptr = resolver.get();
193     *result = std::move(resolver);
194     base::AutoLock lock(lock_);
195     resolvers_.push_back(resolver_ptr);
196     script_data_.push_back(script_data);
197     return OK;
198   }
199 
resolvers()200   std::vector<raw_ptr<BlockableProxyResolver, VectorExperimental>> resolvers() {
201     base::AutoLock lock(lock_);
202     return resolvers_;
203   }
204 
script_data()205   const std::vector<scoped_refptr<PacFileData>> script_data() {
206     base::AutoLock lock(lock_);
207     return script_data_;
208   }
209 
210  private:
211   std::vector<raw_ptr<BlockableProxyResolver, VectorExperimental>> resolvers_;
212   std::vector<scoped_refptr<PacFileData>> script_data_;
213   base::Lock lock_;
214 };
215 
216 class SingleShotMultiThreadedProxyResolverFactory
217     : public MultiThreadedProxyResolverFactory {
218  public:
SingleShotMultiThreadedProxyResolverFactory(size_t max_num_threads,std::unique_ptr<ProxyResolverFactory> factory)219   SingleShotMultiThreadedProxyResolverFactory(
220       size_t max_num_threads,
221       std::unique_ptr<ProxyResolverFactory> factory)
222       : MultiThreadedProxyResolverFactory(max_num_threads, false),
223         factory_(std::move(factory)) {}
224 
CreateProxyResolverFactory()225   std::unique_ptr<ProxyResolverFactory> CreateProxyResolverFactory() override {
226     DCHECK(factory_);
227     return std::move(factory_);
228   }
229 
230  private:
231   std::unique_ptr<ProxyResolverFactory> factory_;
232 };
233 
234 class MultiThreadedProxyResolverTest : public TestWithTaskEnvironment {
235  public:
Init(size_t num_threads)236   void Init(size_t num_threads) {
237     auto factory_owner = std::make_unique<BlockableProxyResolverFactory>();
238     factory_ = factory_owner.get();
239     resolver_factory_ =
240         std::make_unique<SingleShotMultiThreadedProxyResolverFactory>(
241             num_threads, std::move(factory_owner));
242     TestCompletionCallback ready_callback;
243     std::unique_ptr<ProxyResolverFactory::Request> request;
244     resolver_factory_->CreateProxyResolver(
245         PacFileData::FromUTF8("pac script bytes"), &resolver_,
246         ready_callback.callback(), &request);
247     EXPECT_TRUE(request);
248     ASSERT_THAT(ready_callback.WaitForResult(), IsOk());
249 
250     // Verify that the script data reaches the synchronous resolver factory.
251     ASSERT_EQ(1u, factory_->script_data().size());
252     EXPECT_EQ(u"pac script bytes", factory_->script_data()[0]->utf16());
253   }
254 
ClearResolver()255   void ClearResolver() { resolver_.reset(); }
256 
factory()257   BlockableProxyResolverFactory& factory() {
258     DCHECK(factory_);
259     return *factory_;
260   }
resolver()261   ProxyResolver& resolver() {
262     DCHECK(resolver_);
263     return *resolver_;
264   }
265 
266  private:
267   raw_ptr<BlockableProxyResolverFactory, DanglingUntriaged> factory_ = nullptr;
268   std::unique_ptr<ProxyResolverFactory> factory_owner_;
269   std::unique_ptr<MultiThreadedProxyResolverFactory> resolver_factory_;
270   std::unique_ptr<ProxyResolver> resolver_;
271 };
272 
TEST_F(MultiThreadedProxyResolverTest,SingleThread_Basic)273 TEST_F(MultiThreadedProxyResolverTest, SingleThread_Basic) {
274   const size_t kNumThreads = 1u;
275   ASSERT_NO_FATAL_FAILURE(Init(kNumThreads));
276 
277   // Start request 0.
278   int rv;
279   TestCompletionCallback callback0;
280   RecordingNetLogObserver net_log_observer;
281   ProxyInfo results0;
282   rv = resolver().GetProxyForURL(
283       GURL("http://request0"), NetworkAnonymizationKey(), &results0,
284       callback0.callback(), nullptr,
285       NetLogWithSource::Make(NetLogSourceType::NONE));
286   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
287 
288   // Wait for request 0 to finish.
289   rv = callback0.WaitForResult();
290   EXPECT_EQ(0, rv);
291   EXPECT_EQ("PROXY request0:80", results0.ToDebugString());
292 
293   // The mock proxy resolver should have written 1 log entry. And
294   // on completion, this should have been copied into |log0|.
295   // We also have 1 log entry that was emitted by the
296   // MultiThreadedProxyResolver.
297   auto entries0 = net_log_observer.GetEntries();
298 
299   ASSERT_EQ(2u, entries0.size());
300   EXPECT_EQ(NetLogEventType::SUBMITTED_TO_RESOLVER_THREAD, entries0[0].type);
301 
302   // Start 3 more requests (request1 to request3).
303 
304   TestCompletionCallback callback1;
305   ProxyInfo results1;
306   rv = resolver().GetProxyForURL(
307       GURL("http://request1"), NetworkAnonymizationKey(), &results1,
308       callback1.callback(), nullptr, NetLogWithSource());
309   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
310 
311   TestCompletionCallback callback2;
312   ProxyInfo results2;
313   rv = resolver().GetProxyForURL(
314       GURL("http://request2"), NetworkAnonymizationKey(), &results2,
315       callback2.callback(), nullptr, NetLogWithSource());
316   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
317 
318   TestCompletionCallback callback3;
319   ProxyInfo results3;
320   rv = resolver().GetProxyForURL(
321       GURL("http://request3"), NetworkAnonymizationKey(), &results3,
322       callback3.callback(), nullptr, NetLogWithSource());
323   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
324 
325   // Wait for the requests to finish (they must finish in the order they were
326   // started, which is what we check for from their magic return value)
327 
328   rv = callback1.WaitForResult();
329   EXPECT_EQ(1, rv);
330   EXPECT_EQ("PROXY request1:80", results1.ToDebugString());
331 
332   rv = callback2.WaitForResult();
333   EXPECT_EQ(2, rv);
334   EXPECT_EQ("PROXY request2:80", results2.ToDebugString());
335 
336   rv = callback3.WaitForResult();
337   EXPECT_EQ(3, rv);
338   EXPECT_EQ("PROXY request3:80", results3.ToDebugString());
339 }
340 
341 // Tests that the NetLog is updated to include the time the request was waiting
342 // to be scheduled to a thread.
TEST_F(MultiThreadedProxyResolverTest,SingleThread_UpdatesNetLogWithThreadWait)343 TEST_F(MultiThreadedProxyResolverTest,
344        SingleThread_UpdatesNetLogWithThreadWait) {
345   const size_t kNumThreads = 1u;
346   ASSERT_NO_FATAL_FAILURE(Init(kNumThreads));
347 
348   int rv;
349 
350   // Block the proxy resolver, so no request can complete.
351   factory().resolvers()[0]->Block();
352 
353   // Start request 0.
354   std::unique_ptr<ProxyResolver::Request> request0;
355   TestCompletionCallback callback0;
356   ProxyInfo results0;
357   RecordingNetLogObserver net_log_observer;
358   NetLogWithSource log_with_source0 =
359       NetLogWithSource::Make(NetLogSourceType::NONE);
360   rv = resolver().GetProxyForURL(
361       GURL("http://request0"), NetworkAnonymizationKey(), &results0,
362       callback0.callback(), &request0, log_with_source0);
363   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
364 
365   // Start 2 more requests (request1 and request2).
366 
367   TestCompletionCallback callback1;
368   ProxyInfo results1;
369   NetLogWithSource log_with_source1 =
370       NetLogWithSource::Make(NetLogSourceType::NONE);
371   rv = resolver().GetProxyForURL(
372       GURL("http://request1"), NetworkAnonymizationKey(), &results1,
373       callback1.callback(), nullptr, log_with_source1);
374   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
375 
376   std::unique_ptr<ProxyResolver::Request> request2;
377   TestCompletionCallback callback2;
378   ProxyInfo results2;
379   NetLogWithSource log_with_source2 =
380       NetLogWithSource::Make(NetLogSourceType::NONE);
381   rv = resolver().GetProxyForURL(
382       GURL("http://request2"), NetworkAnonymizationKey(), &results2,
383       callback2.callback(), &request2, log_with_source2);
384   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
385 
386   // Unblock the worker thread so the requests can continue running.
387   factory().resolvers()[0]->WaitUntilBlocked();
388   factory().resolvers()[0]->Unblock();
389 
390   // Check that request 0 completed as expected.
391   // The NetLog has 1 entry that came from the MultiThreadedProxyResolver, and
392   // 1 entry from the mock proxy resolver.
393   EXPECT_EQ(0, callback0.WaitForResult());
394   EXPECT_EQ("PROXY request0:80", results0.ToDebugString());
395 
396   auto entries0 =
397       net_log_observer.GetEntriesForSource(log_with_source0.source());
398 
399   ASSERT_EQ(2u, entries0.size());
400   EXPECT_EQ(NetLogEventType::SUBMITTED_TO_RESOLVER_THREAD, entries0[0].type);
401 
402   // Check that request 1 completed as expected.
403   EXPECT_EQ(1, callback1.WaitForResult());
404   EXPECT_EQ("PROXY request1:80", results1.ToDebugString());
405 
406   auto entries1 =
407       net_log_observer.GetEntriesForSource(log_with_source1.source());
408 
409   ASSERT_EQ(4u, entries1.size());
410   EXPECT_TRUE(LogContainsBeginEvent(
411       entries1, 0, NetLogEventType::WAITING_FOR_PROXY_RESOLVER_THREAD));
412   EXPECT_TRUE(LogContainsEndEvent(
413       entries1, 1, NetLogEventType::WAITING_FOR_PROXY_RESOLVER_THREAD));
414 
415   // Check that request 2 completed as expected.
416   EXPECT_EQ(2, callback2.WaitForResult());
417   EXPECT_EQ("PROXY request2:80", results2.ToDebugString());
418 
419   auto entries2 =
420       net_log_observer.GetEntriesForSource(log_with_source2.source());
421 
422   ASSERT_EQ(4u, entries2.size());
423   EXPECT_TRUE(LogContainsBeginEvent(
424       entries2, 0, NetLogEventType::WAITING_FOR_PROXY_RESOLVER_THREAD));
425   EXPECT_TRUE(LogContainsEndEvent(
426       entries2, 1, NetLogEventType::WAITING_FOR_PROXY_RESOLVER_THREAD));
427 }
428 
429 // Cancel a request which is in progress, and then cancel a request which
430 // is pending.
TEST_F(MultiThreadedProxyResolverTest,SingleThread_CancelRequest)431 TEST_F(MultiThreadedProxyResolverTest, SingleThread_CancelRequest) {
432   const size_t kNumThreads = 1u;
433   ASSERT_NO_FATAL_FAILURE(Init(kNumThreads));
434 
435   int rv;
436 
437   // Block the proxy resolver, so no request can complete.
438   factory().resolvers()[0]->Block();
439 
440   // Start request 0.
441   std::unique_ptr<ProxyResolver::Request> request0;
442   TestCompletionCallback callback0;
443   ProxyInfo results0;
444   rv = resolver().GetProxyForURL(
445       GURL("http://request0"), NetworkAnonymizationKey(), &results0,
446       callback0.callback(), &request0, NetLogWithSource());
447   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
448 
449   // Wait until requests 0 reaches the worker thread.
450   factory().resolvers()[0]->WaitUntilBlocked();
451 
452   // Start 3 more requests (request1 : request3).
453 
454   TestCompletionCallback callback1;
455   ProxyInfo results1;
456   rv = resolver().GetProxyForURL(
457       GURL("http://request1"), NetworkAnonymizationKey(), &results1,
458       callback1.callback(), nullptr, NetLogWithSource());
459   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
460 
461   std::unique_ptr<ProxyResolver::Request> request2;
462   TestCompletionCallback callback2;
463   ProxyInfo results2;
464   rv = resolver().GetProxyForURL(
465       GURL("http://request2"), NetworkAnonymizationKey(), &results2,
466       callback2.callback(), &request2, NetLogWithSource());
467   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
468 
469   TestCompletionCallback callback3;
470   ProxyInfo results3;
471   rv = resolver().GetProxyForURL(
472       GURL("http://request3"), NetworkAnonymizationKey(), &results3,
473       callback3.callback(), nullptr, NetLogWithSource());
474   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
475 
476   // Cancel request0 (inprogress) and request2 (pending).
477   request0.reset();
478   request2.reset();
479 
480   // Unblock the worker thread so the requests can continue running.
481   factory().resolvers()[0]->Unblock();
482 
483   // Wait for requests 1 and 3 to finish.
484 
485   rv = callback1.WaitForResult();
486   EXPECT_EQ(1, rv);
487   EXPECT_EQ("PROXY request1:80", results1.ToDebugString());
488 
489   rv = callback3.WaitForResult();
490   // Note that since request2 was cancelled before reaching the resolver,
491   // the request count is 2 and not 3 here.
492   EXPECT_EQ(2, rv);
493   EXPECT_EQ("PROXY request3:80", results3.ToDebugString());
494 
495   // Requests 0 and 2 which were cancelled, hence their completion callbacks
496   // were never summoned.
497   EXPECT_FALSE(callback0.have_result());
498   EXPECT_FALSE(callback2.have_result());
499 }
500 
501 // Make sure the NetworkAnonymizationKey makes it to the resolver.
TEST_F(MultiThreadedProxyResolverTest,SingleThread_WithNetworkAnonymizationKey)502 TEST_F(MultiThreadedProxyResolverTest,
503        SingleThread_WithNetworkAnonymizationKey) {
504   const SchemefulSite kSite(GURL("https://origin.test/"));
505   const auto kNetworkAnonymizationKey =
506       NetworkAnonymizationKey::CreateSameSite(kSite);
507   const GURL kUrl("https://url.test/");
508 
509   const size_t kNumThreads = 1u;
510   ASSERT_NO_FATAL_FAILURE(Init(kNumThreads));
511 
512   int rv;
513 
514   // Block the proxy resolver, so no request can complete.
515   factory().resolvers()[0]->Block();
516 
517   // Start request.
518   std::unique_ptr<ProxyResolver::Request> request;
519   TestCompletionCallback callback;
520   ProxyInfo results;
521   rv = resolver().GetProxyForURL(kUrl, kNetworkAnonymizationKey, &results,
522                                  callback.callback(), &request,
523                                  NetLogWithSource());
524   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
525 
526   // Wait until request reaches the worker thread.
527   factory().resolvers()[0]->WaitUntilBlocked();
528 
529   factory().resolvers()[0]->Unblock();
530   EXPECT_EQ(0, callback.WaitForResult());
531 
532   EXPECT_EQ(kUrl, factory().resolvers()[0]->last_query_url());
533   EXPECT_EQ(kNetworkAnonymizationKey,
534             factory().resolvers()[0]->last_network_anonymization_key());
535 }
536 
537 // Test that deleting MultiThreadedProxyResolver while requests are
538 // outstanding cancels them (and doesn't leak anything).
TEST_F(MultiThreadedProxyResolverTest,SingleThread_CancelRequestByDeleting)539 TEST_F(MultiThreadedProxyResolverTest, SingleThread_CancelRequestByDeleting) {
540   const size_t kNumThreads = 1u;
541   ASSERT_NO_FATAL_FAILURE(Init(kNumThreads));
542 
543   ASSERT_EQ(1u, factory().resolvers().size());
544 
545   // Block the proxy resolver, so no request can complete.
546   factory().resolvers()[0]->Block();
547 
548   int rv;
549   // Start 3 requests.
550 
551   TestCompletionCallback callback0;
552   ProxyInfo results0;
553   rv = resolver().GetProxyForURL(
554       GURL("http://request0"), NetworkAnonymizationKey(), &results0,
555       callback0.callback(), nullptr, NetLogWithSource());
556   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
557 
558   TestCompletionCallback callback1;
559   ProxyInfo results1;
560   rv = resolver().GetProxyForURL(
561       GURL("http://request1"), NetworkAnonymizationKey(), &results1,
562       callback1.callback(), nullptr, NetLogWithSource());
563   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
564 
565   TestCompletionCallback callback2;
566   ProxyInfo results2;
567   rv = resolver().GetProxyForURL(
568       GURL("http://request2"), NetworkAnonymizationKey(), &results2,
569       callback2.callback(), nullptr, NetLogWithSource());
570   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
571 
572   // Wait until request 0 reaches the worker thread.
573   factory().resolvers()[0]->WaitUntilBlocked();
574 
575   // Add some latency, to improve the chance that when
576   // MultiThreadedProxyResolver is deleted below we are still running inside
577   // of the worker thread. The test will pass regardless, so this race doesn't
578   // cause flakiness. However the destruction during execution is a more
579   // interesting case to test.
580   factory().resolvers()[0]->SetResolveLatency(base::Milliseconds(100));
581 
582   // Unblock the worker thread and delete the underlying
583   // MultiThreadedProxyResolver immediately.
584   factory().resolvers()[0]->Unblock();
585   ClearResolver();
586 
587   // Give any posted tasks a chance to run (in case there is badness).
588   base::RunLoop().RunUntilIdle();
589 
590   // Check that none of the outstanding requests were completed.
591   EXPECT_FALSE(callback0.have_result());
592   EXPECT_FALSE(callback1.have_result());
593   EXPECT_FALSE(callback2.have_result());
594 }
595 
596 // Tests setting the PAC script once, lazily creating new threads, and
597 // cancelling requests.
TEST_F(MultiThreadedProxyResolverTest,ThreeThreads_Basic)598 TEST_F(MultiThreadedProxyResolverTest, ThreeThreads_Basic) {
599   const size_t kNumThreads = 3u;
600   ASSERT_NO_FATAL_FAILURE(Init(kNumThreads));
601 
602   // Verify that it reaches the synchronous resolver.
603   // One thread has been provisioned (i.e. one ProxyResolver was created).
604   ASSERT_EQ(1u, factory().resolvers().size());
605 
606   const int kNumRequests = 8;
607   int rv;
608   TestCompletionCallback callback[kNumRequests];
609   ProxyInfo results[kNumRequests];
610   std::unique_ptr<ProxyResolver::Request> request[kNumRequests];
611 
612   // Start request 0 -- this should run on thread 0 as there is nothing else
613   // going on right now.
614   rv = resolver().GetProxyForURL(
615       GURL("http://request0"), NetworkAnonymizationKey(), &results[0],
616       callback[0].callback(), &request[0], NetLogWithSource());
617   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
618 
619   // Wait for request 0 to finish.
620   rv = callback[0].WaitForResult();
621   EXPECT_EQ(0, rv);
622   EXPECT_EQ("PROXY request0:80", results[0].ToDebugString());
623   ASSERT_EQ(1u, factory().resolvers().size());
624   EXPECT_EQ(1, factory().resolvers()[0]->request_count());
625 
626   base::RunLoop().RunUntilIdle();
627 
628   // We now block the first resolver to ensure a request is sent to the second
629   // thread.
630   factory().resolvers()[0]->Block();
631   rv = resolver().GetProxyForURL(
632       GURL("http://request1"), NetworkAnonymizationKey(), &results[1],
633       callback[1].callback(), &request[1], NetLogWithSource());
634   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
635   factory().resolvers()[0]->WaitUntilBlocked();
636   rv = resolver().GetProxyForURL(
637       GURL("http://request2"), NetworkAnonymizationKey(), &results[2],
638       callback[2].callback(), &request[2], NetLogWithSource());
639   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
640   EXPECT_EQ(0, callback[2].WaitForResult());
641   ASSERT_EQ(2u, factory().resolvers().size());
642 
643   // We now block the second resolver as well to ensure a request is sent to the
644   // third thread.
645   factory().resolvers()[1]->Block();
646   rv = resolver().GetProxyForURL(
647       GURL("http://request3"), NetworkAnonymizationKey(), &results[3],
648       callback[3].callback(), &request[3], NetLogWithSource());
649   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
650   factory().resolvers()[1]->WaitUntilBlocked();
651   rv = resolver().GetProxyForURL(
652       GURL("http://request4"), NetworkAnonymizationKey(), &results[4],
653       callback[4].callback(), &request[4], NetLogWithSource());
654   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
655   EXPECT_EQ(0, callback[4].WaitForResult());
656 
657   // We should now have a total of 3 threads, each with its own ProxyResolver
658   // that will get initialized with the same data.
659   ASSERT_EQ(3u, factory().resolvers().size());
660 
661   ASSERT_EQ(3u, factory().script_data().size());
662   for (int i = 0; i < 3; ++i) {
663     EXPECT_EQ(u"pac script bytes", factory().script_data()[i]->utf16())
664         << "i=" << i;
665   }
666 
667   // Start and cancel two requests. Since the first two threads are still
668   // blocked, they'll both be serviced by the third thread. The first request
669   // will reach the resolver, but the second will still be queued when canceled.
670   // Start a third request so we can be sure the resolver has completed running
671   // the first request.
672   rv = resolver().GetProxyForURL(
673       GURL("http://request5"), NetworkAnonymizationKey(), &results[5],
674       callback[5].callback(), &request[5], NetLogWithSource());
675   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
676   rv = resolver().GetProxyForURL(
677       GURL("http://request6"), NetworkAnonymizationKey(), &results[6],
678       callback[6].callback(), &request[6], NetLogWithSource());
679   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
680   rv = resolver().GetProxyForURL(
681       GURL("http://request7"), NetworkAnonymizationKey(), &results[7],
682       callback[7].callback(), &request[7], NetLogWithSource());
683   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
684   request[5].reset();
685   request[6].reset();
686 
687   EXPECT_EQ(2, callback[7].WaitForResult());
688 
689   // Check that the cancelled requests never invoked their callback.
690   EXPECT_FALSE(callback[5].have_result());
691   EXPECT_FALSE(callback[6].have_result());
692 
693   // Unblock the first two threads and wait for their requests to complete.
694   factory().resolvers()[0]->Unblock();
695   factory().resolvers()[1]->Unblock();
696   EXPECT_EQ(1, callback[1].WaitForResult());
697   EXPECT_EQ(1, callback[3].WaitForResult());
698 
699   EXPECT_EQ(2, factory().resolvers()[0]->request_count());
700   EXPECT_EQ(2, factory().resolvers()[1]->request_count());
701   EXPECT_EQ(3, factory().resolvers()[2]->request_count());
702 }
703 
704 // Tests using two threads. The first request hangs the first thread. Checks
705 // that other requests are able to complete while this first request remains
706 // stalled.
TEST_F(MultiThreadedProxyResolverTest,OneThreadBlocked)707 TEST_F(MultiThreadedProxyResolverTest, OneThreadBlocked) {
708   const size_t kNumThreads = 2u;
709   ASSERT_NO_FATAL_FAILURE(Init(kNumThreads));
710 
711   int rv;
712 
713   // One thread has been provisioned (i.e. one ProxyResolver was created).
714   ASSERT_EQ(1u, factory().resolvers().size());
715   EXPECT_EQ(u"pac script bytes", factory().script_data()[0]->utf16());
716 
717   const int kNumRequests = 4;
718   TestCompletionCallback callback[kNumRequests];
719   ProxyInfo results[kNumRequests];
720   std::unique_ptr<ProxyResolver::Request> request[kNumRequests];
721 
722   // Start a request that will block the first thread.
723 
724   factory().resolvers()[0]->Block();
725 
726   rv = resolver().GetProxyForURL(
727       GURL("http://request0"), NetworkAnonymizationKey(), &results[0],
728       callback[0].callback(), &request[0], NetLogWithSource());
729 
730   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
731   factory().resolvers()[0]->WaitUntilBlocked();
732 
733   // Start 3 more requests -- they should all be serviced by thread #2
734   // since thread #1 is blocked.
735 
736   for (int i = 1; i < kNumRequests; ++i) {
737     rv = resolver().GetProxyForURL(
738         GURL(base::StringPrintf("http://request%d", i)),
739         NetworkAnonymizationKey(), &results[i], callback[i].callback(),
740         &request[i], NetLogWithSource());
741     EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
742   }
743 
744   // Wait for the three requests to complete (they should complete in FIFO
745   // order).
746   for (int i = 1; i < kNumRequests; ++i) {
747     EXPECT_EQ(i - 1, callback[i].WaitForResult());
748   }
749 
750   // Unblock the first thread.
751   factory().resolvers()[0]->Unblock();
752   EXPECT_EQ(0, callback[0].WaitForResult());
753 
754   // All in all, the first thread should have seen just 1 request. And the
755   // second thread 3 requests.
756   ASSERT_EQ(2u, factory().resolvers().size());
757   EXPECT_EQ(1, factory().resolvers()[0]->request_count());
758   EXPECT_EQ(3, factory().resolvers()[1]->request_count());
759 }
760 
761 class FailingProxyResolverFactory : public ProxyResolverFactory {
762  public:
FailingProxyResolverFactory()763   FailingProxyResolverFactory() : ProxyResolverFactory(false) {}
764 
765   // ProxyResolverFactory override.
CreateProxyResolver(const scoped_refptr<PacFileData> & script_data,std::unique_ptr<ProxyResolver> * result,CompletionOnceCallback callback,std::unique_ptr<Request> * request)766   int CreateProxyResolver(const scoped_refptr<PacFileData>& script_data,
767                           std::unique_ptr<ProxyResolver>* result,
768                           CompletionOnceCallback callback,
769                           std::unique_ptr<Request>* request) override {
770     return ERR_PAC_SCRIPT_FAILED;
771   }
772 };
773 
774 // Test that an error when creating the synchronous resolver causes the
775 // MultiThreadedProxyResolverFactory create request to fail with that error.
TEST_F(MultiThreadedProxyResolverTest,ProxyResolverFactoryError)776 TEST_F(MultiThreadedProxyResolverTest, ProxyResolverFactoryError) {
777   const size_t kNumThreads = 1u;
778   SingleShotMultiThreadedProxyResolverFactory resolver_factory(
779       kNumThreads, std::make_unique<FailingProxyResolverFactory>());
780   TestCompletionCallback ready_callback;
781   std::unique_ptr<ProxyResolverFactory::Request> request;
782   std::unique_ptr<ProxyResolver> resolver;
783   EXPECT_EQ(ERR_IO_PENDING,
784             resolver_factory.CreateProxyResolver(
785                 PacFileData::FromUTF8("pac script bytes"), &resolver,
786                 ready_callback.callback(), &request));
787   EXPECT_TRUE(request);
788   EXPECT_THAT(ready_callback.WaitForResult(), IsError(ERR_PAC_SCRIPT_FAILED));
789   EXPECT_FALSE(resolver);
790 }
791 
Fail(int error)792 void Fail(int error) {
793   FAIL() << "Unexpected callback with error " << error;
794 }
795 
796 // Test that cancelling an in-progress create request works correctly.
TEST_F(MultiThreadedProxyResolverTest,CancelCreate)797 TEST_F(MultiThreadedProxyResolverTest, CancelCreate) {
798   const size_t kNumThreads = 1u;
799   {
800     SingleShotMultiThreadedProxyResolverFactory resolver_factory(
801         kNumThreads, std::make_unique<BlockableProxyResolverFactory>());
802     std::unique_ptr<ProxyResolverFactory::Request> request;
803     std::unique_ptr<ProxyResolver> resolver;
804     EXPECT_EQ(ERR_IO_PENDING, resolver_factory.CreateProxyResolver(
805                                   PacFileData::FromUTF8("pac script bytes"),
806                                   &resolver, base::BindOnce(&Fail), &request));
807     EXPECT_TRUE(request);
808     request.reset();
809   }
810   // The factory destructor will block until the worker thread stops, but it may
811   // post tasks to the origin message loop which are still pending. Run them
812   // now to ensure it works as expected.
813   base::RunLoop().RunUntilIdle();
814 }
815 
DeleteRequest(CompletionOnceCallback callback,std::unique_ptr<ProxyResolverFactory::Request> * request,int result)816 void DeleteRequest(CompletionOnceCallback callback,
817                    std::unique_ptr<ProxyResolverFactory::Request>* request,
818                    int result) {
819   std::move(callback).Run(result);
820   request->reset();
821 }
822 
823 // Test that delete the Request during the factory callback works correctly.
TEST_F(MultiThreadedProxyResolverTest,DeleteRequestInFactoryCallback)824 TEST_F(MultiThreadedProxyResolverTest, DeleteRequestInFactoryCallback) {
825   const size_t kNumThreads = 1u;
826   SingleShotMultiThreadedProxyResolverFactory resolver_factory(
827       kNumThreads, std::make_unique<BlockableProxyResolverFactory>());
828   std::unique_ptr<ProxyResolverFactory::Request> request;
829   std::unique_ptr<ProxyResolver> resolver;
830   TestCompletionCallback callback;
831   EXPECT_EQ(ERR_IO_PENDING,
832             resolver_factory.CreateProxyResolver(
833                 PacFileData::FromUTF8("pac script bytes"), &resolver,
834                 base::BindOnce(&DeleteRequest, callback.callback(),
835                                base::Unretained(&request)),
836                 &request));
837   EXPECT_TRUE(request);
838   EXPECT_THAT(callback.WaitForResult(), IsOk());
839 }
840 
841 // Test that deleting the factory with a request in-progress works correctly.
TEST_F(MultiThreadedProxyResolverTest,DestroyFactoryWithRequestsInProgress)842 TEST_F(MultiThreadedProxyResolverTest, DestroyFactoryWithRequestsInProgress) {
843   const size_t kNumThreads = 1u;
844   std::unique_ptr<ProxyResolverFactory::Request> request;
845   std::unique_ptr<ProxyResolver> resolver;
846   {
847     SingleShotMultiThreadedProxyResolverFactory resolver_factory(
848         kNumThreads, std::make_unique<BlockableProxyResolverFactory>());
849     EXPECT_EQ(ERR_IO_PENDING, resolver_factory.CreateProxyResolver(
850                                   PacFileData::FromUTF8("pac script bytes"),
851                                   &resolver, base::BindOnce(&Fail), &request));
852     EXPECT_TRUE(request);
853   }
854   // The factory destructor will block until the worker thread stops, but it may
855   // post tasks to the origin message loop which are still pending. Run them
856   // now to ensure it works as expected.
857   base::RunLoop().RunUntilIdle();
858 }
859 
860 }  // namespace
861 
862 }  // namespace net
863