Home | History | Annotate | Download | only in proxy
      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 
     22 namespace net {
     23 
     24 namespace {
     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.
     29 class 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.
    115 class 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|.
    159 class 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|.
    198 class 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.
    213 class 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 
    235 TEST(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.
    330 TEST(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.
    424 TEST(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).
    501 TEST(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().
    564 TEST(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.
    598 TEST(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.
    711 TEST(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