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