1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
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/multi_threaded_proxy_resolver.h"
6
7#include "base/message_loop.h"
8#include "base/stl_util-inl.h"
9#include "base/string_util.h"
10#include "base/stringprintf.h"
11#include "base/threading/platform_thread.h"
12#include "base/utf_string_conversions.h"
13#include "base/synchronization/waitable_event.h"
14#include "googleurl/src/gurl.h"
15#include "net/base/net_errors.h"
16#include "net/base/net_log.h"
17#include "net/base/net_log_unittest.h"
18#include "net/base/test_completion_callback.h"
19#include "net/proxy/proxy_info.h"
20#include "testing/gtest/include/gtest/gtest.h"
21
22namespace net {
23
24namespace {
25
26// A synchronous mock ProxyResolver implementation, which can be used in
27// conjunction with MultiThreadedProxyResolver.
28//       - returns a single-item proxy list with the query's host.
29class MockProxyResolver : public ProxyResolver {
30 public:
31  MockProxyResolver()
32      : ProxyResolver(true /*expects_pac_bytes*/),
33        wrong_loop_(MessageLoop::current()),
34        request_count_(0),
35        purge_count_(0),
36        resolve_latency_ms_(0) {}
37
38  // ProxyResolver implementation:
39  virtual int GetProxyForURL(const GURL& query_url,
40                             ProxyInfo* results,
41                             CompletionCallback* callback,
42                             RequestHandle* request,
43                             const BoundNetLog& net_log) {
44    if (resolve_latency_ms_)
45      base::PlatformThread::Sleep(resolve_latency_ms_);
46
47    CheckIsOnWorkerThread();
48
49    EXPECT_TRUE(callback == NULL);
50    EXPECT_TRUE(request == NULL);
51
52    // Write something into |net_log| (doesn't really have any meaning.)
53    net_log.BeginEvent(NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE, NULL);
54
55    results->UseNamedProxy(query_url.host());
56
57    // Return a success code which represents the request's order.
58    return request_count_++;
59  }
60
61  virtual void CancelRequest(RequestHandle request) {
62    NOTREACHED();
63  }
64
65  virtual void CancelSetPacScript() {
66    NOTREACHED();
67  }
68
69  virtual int SetPacScript(
70      const scoped_refptr<ProxyResolverScriptData>& script_data,
71      CompletionCallback* callback) {
72    CheckIsOnWorkerThread();
73    last_script_data_ = script_data;
74    return OK;
75  }
76
77  virtual void PurgeMemory() {
78    CheckIsOnWorkerThread();
79    ++purge_count_;
80  }
81
82  int purge_count() const { return purge_count_; }
83  int request_count() const { return request_count_; }
84
85  const ProxyResolverScriptData* last_script_data() const {
86    return last_script_data_;
87  }
88
89  void SetResolveLatency(int latency_ms) {
90    resolve_latency_ms_ = latency_ms;
91  }
92
93 private:
94  void CheckIsOnWorkerThread() {
95    // We should be running on the worker thread -- while we don't know the
96    // message loop of MultiThreadedProxyResolver's worker thread, we do
97    // know that it is going to be distinct from the loop running the
98    // test, so at least make sure it isn't the main loop.
99    EXPECT_NE(MessageLoop::current(), wrong_loop_);
100  }
101
102  MessageLoop* wrong_loop_;
103  int request_count_;
104  int purge_count_;
105  scoped_refptr<ProxyResolverScriptData> last_script_data_;
106  int resolve_latency_ms_;
107};
108
109
110// A mock synchronous ProxyResolver which can be set to block upon reaching
111// GetProxyForURL().
112// TODO(eroman): WaitUntilBlocked() *must* be called before calling Unblock(),
113//               otherwise there will be a race on |should_block_| since it is
114//               read without any synchronization.
115class BlockableProxyResolver : public MockProxyResolver {
116 public:
117  BlockableProxyResolver()
118      : should_block_(false),
119        unblocked_(true, true),
120        blocked_(true, false) {
121  }
122
123  void Block() {
124    should_block_ = true;
125    unblocked_.Reset();
126  }
127
128  void Unblock() {
129    should_block_ = false;
130    blocked_.Reset();
131    unblocked_.Signal();
132  }
133
134  void WaitUntilBlocked() {
135    blocked_.Wait();
136  }
137
138  virtual int GetProxyForURL(const GURL& query_url,
139                             ProxyInfo* results,
140                             CompletionCallback* callback,
141                             RequestHandle* request,
142                             const BoundNetLog& net_log) {
143    if (should_block_) {
144      blocked_.Signal();
145      unblocked_.Wait();
146    }
147
148    return MockProxyResolver::GetProxyForURL(
149        query_url, results, callback, request, net_log);
150  }
151
152 private:
153  bool should_block_;
154  base::WaitableEvent unblocked_;
155  base::WaitableEvent blocked_;
156};
157
158// ForwardingProxyResolver forwards all requests to |impl|.
159class ForwardingProxyResolver : public ProxyResolver {
160 public:
161  explicit ForwardingProxyResolver(ProxyResolver* impl)
162      : ProxyResolver(impl->expects_pac_bytes()),
163        impl_(impl) {}
164
165  virtual int GetProxyForURL(const GURL& query_url,
166                             ProxyInfo* results,
167                             CompletionCallback* callback,
168                             RequestHandle* request,
169                             const BoundNetLog& net_log) {
170    return impl_->GetProxyForURL(
171        query_url, results, callback, request, net_log);
172  }
173
174  virtual void CancelRequest(RequestHandle request) {
175    impl_->CancelRequest(request);
176  }
177
178  virtual void CancelSetPacScript() {
179    impl_->CancelSetPacScript();
180  }
181
182  virtual int SetPacScript(
183      const scoped_refptr<ProxyResolverScriptData>& script_data,
184      CompletionCallback* callback) {
185    return impl_->SetPacScript(script_data, callback);
186  }
187
188  virtual void PurgeMemory() {
189    impl_->PurgeMemory();
190  }
191
192 private:
193  ProxyResolver* impl_;
194};
195
196// This factory returns ProxyResolvers that forward all requests to
197// |resolver|.
198class ForwardingProxyResolverFactory : public ProxyResolverFactory {
199 public:
200  explicit ForwardingProxyResolverFactory(ProxyResolver* resolver)
201      : ProxyResolverFactory(resolver->expects_pac_bytes()),
202        resolver_(resolver) {}
203
204  virtual ProxyResolver* CreateProxyResolver() {
205    return new ForwardingProxyResolver(resolver_);
206  }
207
208 private:
209  ProxyResolver* resolver_;
210};
211
212// This factory returns new instances of BlockableProxyResolver.
213class BlockableProxyResolverFactory : public ProxyResolverFactory {
214 public:
215  BlockableProxyResolverFactory() : ProxyResolverFactory(true) {}
216
217  ~BlockableProxyResolverFactory() {
218    STLDeleteElements(&resolvers_);
219  }
220
221  virtual ProxyResolver* CreateProxyResolver() {
222    BlockableProxyResolver* resolver = new BlockableProxyResolver;
223    resolvers_.push_back(resolver);
224    return new ForwardingProxyResolver(resolver);
225  }
226
227  std::vector<BlockableProxyResolver*> resolvers() {
228    return resolvers_;
229  }
230
231 private:
232  std::vector<BlockableProxyResolver*> resolvers_;
233};
234
235TEST(MultiThreadedProxyResolverTest, SingleThread_Basic) {
236  const size_t kNumThreads = 1u;
237  scoped_ptr<MockProxyResolver> mock(new MockProxyResolver);
238  MultiThreadedProxyResolver resolver(
239      new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
240
241  int rv;
242
243  EXPECT_TRUE(resolver.expects_pac_bytes());
244
245  // Call SetPacScriptByData() -- verify that it reaches the synchronous
246  // resolver.
247  TestCompletionCallback set_script_callback;
248  rv = resolver.SetPacScript(
249      ProxyResolverScriptData::FromUTF8("pac script bytes"),
250      &set_script_callback);
251  EXPECT_EQ(ERR_IO_PENDING, rv);
252  EXPECT_EQ(OK, set_script_callback.WaitForResult());
253  EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
254            mock->last_script_data()->utf16());
255
256  // Start request 0.
257  TestCompletionCallback callback0;
258  CapturingBoundNetLog log0(CapturingNetLog::kUnbounded);
259  ProxyInfo results0;
260  rv = resolver.GetProxyForURL(
261      GURL("http://request0"), &results0, &callback0, NULL, log0.bound());
262  EXPECT_EQ(ERR_IO_PENDING, rv);
263
264  // Wait for request 0 to finish.
265  rv = callback0.WaitForResult();
266  EXPECT_EQ(0, rv);
267  EXPECT_EQ("PROXY request0:80", results0.ToPacString());
268
269  // The mock proxy resolver should have written 1 log entry. And
270  // on completion, this should have been copied into |log0|.
271  // We also have 1 log entry that was emitted by the
272  // MultiThreadedProxyResolver.
273  net::CapturingNetLog::EntryList entries0;
274  log0.GetEntries(&entries0);
275
276  ASSERT_EQ(2u, entries0.size());
277  EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD, entries0[0].type);
278
279  // Start 3 more requests (request1 to request3).
280
281  TestCompletionCallback callback1;
282  ProxyInfo results1;
283  rv = resolver.GetProxyForURL(
284      GURL("http://request1"), &results1, &callback1, NULL, BoundNetLog());
285  EXPECT_EQ(ERR_IO_PENDING, rv);
286
287  TestCompletionCallback callback2;
288  ProxyInfo results2;
289  rv = resolver.GetProxyForURL(
290      GURL("http://request2"), &results2, &callback2, NULL, BoundNetLog());
291  EXPECT_EQ(ERR_IO_PENDING, rv);
292
293  TestCompletionCallback callback3;
294  ProxyInfo results3;
295  rv = resolver.GetProxyForURL(
296      GURL("http://request3"), &results3, &callback3, NULL, BoundNetLog());
297  EXPECT_EQ(ERR_IO_PENDING, rv);
298
299  // Wait for the requests to finish (they must finish in the order they were
300  // started, which is what we check for from their magic return value)
301
302  rv = callback1.WaitForResult();
303  EXPECT_EQ(1, rv);
304  EXPECT_EQ("PROXY request1:80", results1.ToPacString());
305
306  rv = callback2.WaitForResult();
307  EXPECT_EQ(2, rv);
308  EXPECT_EQ("PROXY request2:80", results2.ToPacString());
309
310  rv = callback3.WaitForResult();
311  EXPECT_EQ(3, rv);
312  EXPECT_EQ("PROXY request3:80", results3.ToPacString());
313
314  // Ensure that PurgeMemory() reaches the wrapped resolver and happens on the
315  // right thread.
316  EXPECT_EQ(0, mock->purge_count());
317  resolver.PurgeMemory();
318  // There is no way to get a callback directly when PurgeMemory() completes, so
319  // we queue up a dummy request after the PurgeMemory() call and wait until it
320  // finishes to ensure PurgeMemory() has had a chance to run.
321  TestCompletionCallback dummy_callback;
322  rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("dummy"),
323                             &dummy_callback);
324  EXPECT_EQ(OK, dummy_callback.WaitForResult());
325  EXPECT_EQ(1, mock->purge_count());
326}
327
328// Tests that the NetLog is updated to include the time the request was waiting
329// to be scheduled to a thread.
330TEST(MultiThreadedProxyResolverTest,
331     SingleThread_UpdatesNetLogWithThreadWait) {
332  const size_t kNumThreads = 1u;
333  scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
334  MultiThreadedProxyResolver resolver(
335      new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
336
337  int rv;
338
339  // Initialize the resolver.
340  TestCompletionCallback init_callback;
341  rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
342                             &init_callback);
343  EXPECT_EQ(OK, init_callback.WaitForResult());
344
345  // Block the proxy resolver, so no request can complete.
346  mock->Block();
347
348  // Start request 0.
349  ProxyResolver::RequestHandle request0;
350  TestCompletionCallback callback0;
351  ProxyInfo results0;
352  CapturingBoundNetLog log0(CapturingNetLog::kUnbounded);
353  rv = resolver.GetProxyForURL(
354      GURL("http://request0"), &results0, &callback0, &request0, log0.bound());
355  EXPECT_EQ(ERR_IO_PENDING, rv);
356
357  // Start 2 more requests (request1 and request2).
358
359  TestCompletionCallback callback1;
360  ProxyInfo results1;
361  CapturingBoundNetLog log1(CapturingNetLog::kUnbounded);
362  rv = resolver.GetProxyForURL(
363      GURL("http://request1"), &results1, &callback1, NULL, log1.bound());
364  EXPECT_EQ(ERR_IO_PENDING, rv);
365
366  ProxyResolver::RequestHandle request2;
367  TestCompletionCallback callback2;
368  ProxyInfo results2;
369  CapturingBoundNetLog log2(CapturingNetLog::kUnbounded);
370  rv = resolver.GetProxyForURL(
371      GURL("http://request2"), &results2, &callback2, &request2, log2.bound());
372  EXPECT_EQ(ERR_IO_PENDING, rv);
373
374  // Unblock the worker thread so the requests can continue running.
375  mock->WaitUntilBlocked();
376  mock->Unblock();
377
378  // Check that request 0 completed as expected.
379  // The NetLog has 1 entry that came from the MultiThreadedProxyResolver, and
380  // 1 entry from the mock proxy resolver.
381  EXPECT_EQ(0, callback0.WaitForResult());
382  EXPECT_EQ("PROXY request0:80", results0.ToPacString());
383
384  net::CapturingNetLog::EntryList entries0;
385  log0.GetEntries(&entries0);
386
387  ASSERT_EQ(2u, entries0.size());
388  EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD,
389            entries0[0].type);
390
391  // Check that request 1 completed as expected.
392  EXPECT_EQ(1, callback1.WaitForResult());
393  EXPECT_EQ("PROXY request1:80", results1.ToPacString());
394
395  net::CapturingNetLog::EntryList entries1;
396  log1.GetEntries(&entries1);
397
398  ASSERT_EQ(4u, entries1.size());
399  EXPECT_TRUE(LogContainsBeginEvent(
400      entries1, 0,
401      NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
402  EXPECT_TRUE(LogContainsEndEvent(
403      entries1, 1,
404      NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
405
406  // Check that request 2 completed as expected.
407  EXPECT_EQ(2, callback2.WaitForResult());
408  EXPECT_EQ("PROXY request2:80", results2.ToPacString());
409
410  net::CapturingNetLog::EntryList entries2;
411  log2.GetEntries(&entries2);
412
413  ASSERT_EQ(4u, entries2.size());
414  EXPECT_TRUE(LogContainsBeginEvent(
415      entries2, 0,
416      NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
417  EXPECT_TRUE(LogContainsEndEvent(
418      entries2, 1,
419      NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
420}
421
422// Cancel a request which is in progress, and then cancel a request which
423// is pending.
424TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequest) {
425  const size_t kNumThreads = 1u;
426  scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
427  MultiThreadedProxyResolver resolver(
428      new ForwardingProxyResolverFactory(mock.get()),
429                                      kNumThreads);
430
431  int rv;
432
433  // Initialize the resolver.
434  TestCompletionCallback init_callback;
435  rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
436                             &init_callback);
437  EXPECT_EQ(OK, init_callback.WaitForResult());
438
439  // Block the proxy resolver, so no request can complete.
440  mock->Block();
441
442  // Start request 0.
443  ProxyResolver::RequestHandle request0;
444  TestCompletionCallback callback0;
445  ProxyInfo results0;
446  rv = resolver.GetProxyForURL(
447      GURL("http://request0"), &results0, &callback0, &request0, BoundNetLog());
448  EXPECT_EQ(ERR_IO_PENDING, rv);
449
450  // Wait until requests 0 reaches the worker thread.
451  mock->WaitUntilBlocked();
452
453  // Start 3 more requests (request1 : request3).
454
455  TestCompletionCallback callback1;
456  ProxyInfo results1;
457  rv = resolver.GetProxyForURL(
458      GURL("http://request1"), &results1, &callback1, NULL, BoundNetLog());
459  EXPECT_EQ(ERR_IO_PENDING, rv);
460
461  ProxyResolver::RequestHandle request2;
462  TestCompletionCallback callback2;
463  ProxyInfo results2;
464  rv = resolver.GetProxyForURL(
465      GURL("http://request2"), &results2, &callback2, &request2, BoundNetLog());
466  EXPECT_EQ(ERR_IO_PENDING, rv);
467
468  TestCompletionCallback callback3;
469  ProxyInfo results3;
470  rv = resolver.GetProxyForURL(
471      GURL("http://request3"), &results3, &callback3, NULL, BoundNetLog());
472  EXPECT_EQ(ERR_IO_PENDING, rv);
473
474  // Cancel request0 (inprogress) and request2 (pending).
475  resolver.CancelRequest(request0);
476  resolver.CancelRequest(request2);
477
478  // Unblock the worker thread so the requests can continue running.
479  mock->Unblock();
480
481  // Wait for requests 1 and 3 to finish.
482
483  rv = callback1.WaitForResult();
484  EXPECT_EQ(1, rv);
485  EXPECT_EQ("PROXY request1:80", results1.ToPacString());
486
487  rv = callback3.WaitForResult();
488  // Note that since request2 was cancelled before reaching the resolver,
489  // the request count is 2 and not 3 here.
490  EXPECT_EQ(2, rv);
491  EXPECT_EQ("PROXY request3:80", results3.ToPacString());
492
493  // Requests 0 and 2 which were cancelled, hence their completion callbacks
494  // were never summoned.
495  EXPECT_FALSE(callback0.have_result());
496  EXPECT_FALSE(callback2.have_result());
497}
498
499// Test that deleting MultiThreadedProxyResolver while requests are
500// outstanding cancels them (and doesn't leak anything).
501TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequestByDeleting) {
502  const size_t kNumThreads = 1u;
503  scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
504  scoped_ptr<MultiThreadedProxyResolver> resolver(
505      new MultiThreadedProxyResolver(
506          new ForwardingProxyResolverFactory(mock.get()), kNumThreads));
507
508  int rv;
509
510  // Initialize the resolver.
511  TestCompletionCallback init_callback;
512  rv = resolver->SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
513                              &init_callback);
514  EXPECT_EQ(OK, init_callback.WaitForResult());
515
516  // Block the proxy resolver, so no request can complete.
517  mock->Block();
518
519  // Start 3 requests.
520
521  TestCompletionCallback callback0;
522  ProxyInfo results0;
523  rv = resolver->GetProxyForURL(
524      GURL("http://request0"), &results0, &callback0, NULL, BoundNetLog());
525  EXPECT_EQ(ERR_IO_PENDING, rv);
526
527  TestCompletionCallback callback1;
528  ProxyInfo results1;
529  rv = resolver->GetProxyForURL(
530      GURL("http://request1"), &results1, &callback1, NULL, BoundNetLog());
531  EXPECT_EQ(ERR_IO_PENDING, rv);
532
533  TestCompletionCallback callback2;
534  ProxyInfo results2;
535  rv = resolver->GetProxyForURL(
536      GURL("http://request2"), &results2, &callback2, NULL, BoundNetLog());
537  EXPECT_EQ(ERR_IO_PENDING, rv);
538
539  // Wait until request 0 reaches the worker thread.
540  mock->WaitUntilBlocked();
541
542  // Add some latency, to improve the chance that when
543  // MultiThreadedProxyResolver is deleted below we are still running inside
544  // of the worker thread. The test will pass regardless, so this race doesn't
545  // cause flakiness. However the destruction during execution is a more
546  // interesting case to test.
547  mock->SetResolveLatency(100);
548
549  // Unblock the worker thread and delete the underlying
550  // MultiThreadedProxyResolver immediately.
551  mock->Unblock();
552  resolver.reset();
553
554  // Give any posted tasks a chance to run (in case there is badness).
555  MessageLoop::current()->RunAllPending();
556
557  // Check that none of the outstanding requests were completed.
558  EXPECT_FALSE(callback0.have_result());
559  EXPECT_FALSE(callback1.have_result());
560  EXPECT_FALSE(callback2.have_result());
561}
562
563// Cancel an outstanding call to SetPacScriptByData().
564TEST(MultiThreadedProxyResolverTest, SingleThread_CancelSetPacScript) {
565  const size_t kNumThreads = 1u;
566  scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
567  MultiThreadedProxyResolver resolver(
568      new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
569
570  int rv;
571
572  TestCompletionCallback set_pac_script_callback;
573  rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("data"),
574                             &set_pac_script_callback);
575  EXPECT_EQ(ERR_IO_PENDING, rv);
576
577  // Cancel the SetPacScriptByData request.
578  resolver.CancelSetPacScript();
579
580  // Start another SetPacScript request
581  TestCompletionCallback set_pac_script_callback2;
582  rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("data2"),
583                             &set_pac_script_callback2);
584  EXPECT_EQ(ERR_IO_PENDING, rv);
585
586  // Wait for the initialization to complete.
587
588  rv = set_pac_script_callback2.WaitForResult();
589  EXPECT_EQ(0, rv);
590  EXPECT_EQ(ASCIIToUTF16("data2"), mock->last_script_data()->utf16());
591
592  // The first SetPacScript callback should never have been completed.
593  EXPECT_FALSE(set_pac_script_callback.have_result());
594}
595
596// Tests setting the PAC script once, lazily creating new threads, and
597// cancelling requests.
598TEST(MultiThreadedProxyResolverTest, ThreeThreads_Basic) {
599  const size_t kNumThreads = 3u;
600  BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory;
601  MultiThreadedProxyResolver resolver(factory, kNumThreads);
602
603  int rv;
604
605  EXPECT_TRUE(resolver.expects_pac_bytes());
606
607  // Call SetPacScriptByData() -- verify that it reaches the synchronous
608  // resolver.
609  TestCompletionCallback set_script_callback;
610  rv = resolver.SetPacScript(
611      ProxyResolverScriptData::FromUTF8("pac script bytes"),
612      &set_script_callback);
613  EXPECT_EQ(ERR_IO_PENDING, rv);
614  EXPECT_EQ(OK, set_script_callback.WaitForResult());
615  // One thread has been provisioned (i.e. one ProxyResolver was created).
616  ASSERT_EQ(1u, factory->resolvers().size());
617  EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
618            factory->resolvers()[0]->last_script_data()->utf16());
619
620  const int kNumRequests = 9;
621  TestCompletionCallback callback[kNumRequests];
622  ProxyInfo results[kNumRequests];
623  ProxyResolver::RequestHandle request[kNumRequests];
624
625  // Start request 0 -- this should run on thread 0 as there is nothing else
626  // going on right now.
627  rv = resolver.GetProxyForURL(
628      GURL("http://request0"), &results[0], &callback[0], &request[0],
629      BoundNetLog());
630  EXPECT_EQ(ERR_IO_PENDING, rv);
631
632  // Wait for request 0 to finish.
633  rv = callback[0].WaitForResult();
634  EXPECT_EQ(0, rv);
635  EXPECT_EQ("PROXY request0:80", results[0].ToPacString());
636  ASSERT_EQ(1u, factory->resolvers().size());
637  EXPECT_EQ(1, factory->resolvers()[0]->request_count());
638
639  MessageLoop::current()->RunAllPending();
640
641  // We now start 8 requests in parallel -- this will cause the maximum of
642  // three threads to be provisioned (an additional two from what we already
643  // have).
644
645  for (int i = 1; i < kNumRequests; ++i) {
646    rv = resolver.GetProxyForURL(
647        GURL(base::StringPrintf("http://request%d", i)), &results[i],
648        &callback[i], &request[i], BoundNetLog());
649    EXPECT_EQ(ERR_IO_PENDING, rv);
650  }
651
652  // We should now have a total of 3 threads, each with its own ProxyResolver
653  // that will get initialized with the same data. (We check this later since
654  // the assignment happens on the worker threads and may not have occurred
655  // yet.)
656  ASSERT_EQ(3u, factory->resolvers().size());
657
658  // Cancel 3 of the 8 oustanding requests.
659  resolver.CancelRequest(request[1]);
660  resolver.CancelRequest(request[3]);
661  resolver.CancelRequest(request[6]);
662
663  // Wait for the remaining requests to complete.
664  int kNonCancelledRequests[] = {2, 4, 5, 7, 8};
665  for (size_t i = 0; i < arraysize(kNonCancelledRequests); ++i) {
666    int request_index = kNonCancelledRequests[i];
667    EXPECT_GE(callback[request_index].WaitForResult(), 0);
668  }
669
670  // Check that the cancelled requests never invoked their callback.
671  EXPECT_FALSE(callback[1].have_result());
672  EXPECT_FALSE(callback[3].have_result());
673  EXPECT_FALSE(callback[6].have_result());
674
675  // We call SetPacScript again, solely to stop the current worker threads.
676  // (That way we can test to see the values observed by the synchronous
677  // resolvers in a non-racy manner).
678  TestCompletionCallback set_script_callback2;
679  rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("xyz"),
680                             &set_script_callback2);
681  EXPECT_EQ(ERR_IO_PENDING, rv);
682  EXPECT_EQ(OK, set_script_callback2.WaitForResult());
683  ASSERT_EQ(4u, factory->resolvers().size());
684
685  for (int i = 0; i < 3; ++i) {
686    EXPECT_EQ(
687        ASCIIToUTF16("pac script bytes"),
688        factory->resolvers()[i]->last_script_data()->utf16()) << "i=" << i;
689  }
690
691  EXPECT_EQ(ASCIIToUTF16("xyz"),
692            factory->resolvers()[3]->last_script_data()->utf16());
693
694  // We don't know the exact ordering that requests ran on threads with,
695  // but we do know the total count that should have reached the threads.
696  // 8 total were submitted, and three were cancelled. Of the three that
697  // were cancelled, one of them (request 1) was cancelled after it had
698  // already been posted to the worker thread. So the resolvers will
699  // have seen 6 total (and 1 from the run prior).
700  ASSERT_EQ(4u, factory->resolvers().size());
701  int total_count = 0;
702  for (int i = 0; i < 3; ++i) {
703    total_count += factory->resolvers()[i]->request_count();
704  }
705  EXPECT_EQ(7, total_count);
706}
707
708// Tests using two threads. The first request hangs the first thread. Checks
709// that other requests are able to complete while this first request remains
710// stalled.
711TEST(MultiThreadedProxyResolverTest, OneThreadBlocked) {
712  const size_t kNumThreads = 2u;
713  BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory;
714  MultiThreadedProxyResolver resolver(factory, kNumThreads);
715
716  int rv;
717
718  EXPECT_TRUE(resolver.expects_pac_bytes());
719
720  // Initialize the resolver.
721  TestCompletionCallback set_script_callback;
722  rv = resolver.SetPacScript(
723      ProxyResolverScriptData::FromUTF8("pac script bytes"),
724      &set_script_callback);
725  EXPECT_EQ(ERR_IO_PENDING, rv);
726  EXPECT_EQ(OK, set_script_callback.WaitForResult());
727  // One thread has been provisioned (i.e. one ProxyResolver was created).
728  ASSERT_EQ(1u, factory->resolvers().size());
729  EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
730            factory->resolvers()[0]->last_script_data()->utf16());
731
732  const int kNumRequests = 4;
733  TestCompletionCallback callback[kNumRequests];
734  ProxyInfo results[kNumRequests];
735  ProxyResolver::RequestHandle request[kNumRequests];
736
737  // Start a request that will block the first thread.
738
739  factory->resolvers()[0]->Block();
740
741  rv = resolver.GetProxyForURL(
742      GURL("http://request0"), &results[0], &callback[0], &request[0],
743      BoundNetLog());
744
745  EXPECT_EQ(ERR_IO_PENDING, rv);
746  factory->resolvers()[0]->WaitUntilBlocked();
747
748  // Start 3 more requests -- they should all be serviced by thread #2
749  // since thread #1 is blocked.
750
751  for (int i = 1; i < kNumRequests; ++i) {
752    rv = resolver.GetProxyForURL(
753        GURL(base::StringPrintf("http://request%d", i)),
754        &results[i], &callback[i], &request[i], BoundNetLog());
755    EXPECT_EQ(ERR_IO_PENDING, rv);
756  }
757
758  // Wait for the three requests to complete (they should complete in FIFO
759  // order).
760  for (int i = 1; i < kNumRequests; ++i) {
761    EXPECT_EQ(i - 1, callback[i].WaitForResult());
762  }
763
764  // Unblock the first thread.
765  factory->resolvers()[0]->Unblock();
766  EXPECT_EQ(0, callback[0].WaitForResult());
767
768  // All in all, the first thread should have seen just 1 request. And the
769  // second thread 3 requests.
770  ASSERT_EQ(2u, factory->resolvers().size());
771  EXPECT_EQ(1, factory->resolvers()[0]->request_count());
772  EXPECT_EQ(3, factory->resolvers()[1]->request_count());
773}
774
775}  // namespace
776
777}  // namespace net
778