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