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