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/sync_host_resolver_bridge.h"
      6 
      7 #include "base/threading/thread.h"
      8 #include "base/synchronization/waitable_event.h"
      9 #include "net/base/address_list.h"
     10 #include "net/base/net_errors.h"
     11 #include "net/base/net_log.h"
     12 #include "net/proxy/multi_threaded_proxy_resolver.h"
     13 #include "net/base/test_completion_callback.h"
     14 #include "net/proxy/proxy_info.h"
     15 #include "testing/gtest/include/gtest/gtest.h"
     16 
     17 // TODO(eroman): This test should be moved into
     18 //               multi_threaded_proxy_resolver_unittest.cc.
     19 
     20 namespace net {
     21 
     22 namespace {
     23 
     24 // This implementation of HostResolver allows blocking until a resolve request
     25 // has been received. The resolve requests it receives will never be completed.
     26 class BlockableHostResolver : public HostResolver {
     27  public:
     28   BlockableHostResolver()
     29       : event_(true, false),
     30         was_request_cancelled_(false) {
     31   }
     32 
     33   virtual int Resolve(const RequestInfo& info,
     34                       AddressList* addresses,
     35                       CompletionCallback* callback,
     36                       RequestHandle* out_req,
     37                       const BoundNetLog& net_log) {
     38     EXPECT_TRUE(callback);
     39     EXPECT_TRUE(out_req);
     40     *out_req = reinterpret_cast<RequestHandle*>(1);  // Magic value.
     41 
     42     // Indicate to the caller that a request was received.
     43     event_.Signal();
     44 
     45     // We return ERR_IO_PENDING, as this request will NEVER be completed.
     46     // Expectation is for the caller to later cancel the request.
     47     return ERR_IO_PENDING;
     48   }
     49 
     50   virtual void CancelRequest(RequestHandle req) {
     51     EXPECT_EQ(reinterpret_cast<RequestHandle*>(1), req);
     52     was_request_cancelled_ = true;
     53   }
     54 
     55   virtual void AddObserver(Observer* observer) {
     56     NOTREACHED();
     57   }
     58 
     59   virtual void RemoveObserver(Observer* observer) {
     60     NOTREACHED();
     61   }
     62 
     63   // Waits until Resolve() has been called.
     64   void WaitUntilRequestIsReceived() {
     65     event_.Wait();
     66   }
     67 
     68   bool was_request_cancelled() const {
     69     return was_request_cancelled_;
     70   }
     71 
     72  private:
     73   // Event to notify when a resolve request was received.
     74   base::WaitableEvent event_;
     75   bool was_request_cancelled_;
     76 };
     77 
     78 // This implementation of ProxyResolver simply does a synchronous resolve
     79 // on |host_resolver| in response to GetProxyForURL().
     80 class SyncProxyResolver : public ProxyResolver {
     81  public:
     82   explicit SyncProxyResolver(SyncHostResolverBridge* host_resolver)
     83       : ProxyResolver(false), host_resolver_(host_resolver) {}
     84 
     85   virtual int GetProxyForURL(const GURL& url,
     86                              ProxyInfo* results,
     87                              CompletionCallback* callback,
     88                              RequestHandle* request,
     89                              const BoundNetLog& net_log) {
     90     EXPECT_FALSE(callback);
     91     EXPECT_FALSE(request);
     92 
     93     // Do a synchronous host resolve.
     94     HostResolver::RequestInfo info(HostPortPair::FromURL(url));
     95     AddressList addresses;
     96     int rv =
     97         host_resolver_->Resolve(info, &addresses, NULL, NULL, BoundNetLog());
     98 
     99     EXPECT_EQ(ERR_ABORTED, rv);
    100 
    101     return rv;
    102   }
    103 
    104   virtual void CancelRequest(RequestHandle request) {
    105     NOTREACHED();
    106   }
    107 
    108   virtual void Shutdown() {
    109     host_resolver_->Shutdown();
    110   }
    111 
    112   virtual void CancelSetPacScript() {
    113     NOTREACHED();
    114   }
    115 
    116   virtual int SetPacScript(
    117       const scoped_refptr<ProxyResolverScriptData>& script_data,
    118       CompletionCallback* callback) {
    119     return OK;
    120   }
    121 
    122  private:
    123   SyncHostResolverBridge* const host_resolver_;
    124 };
    125 
    126 class SyncProxyResolverFactory : public ProxyResolverFactory {
    127  public:
    128   // Takes ownership of |sync_host_resolver|.
    129   explicit SyncProxyResolverFactory(SyncHostResolverBridge* sync_host_resolver)
    130       : ProxyResolverFactory(false),
    131         sync_host_resolver_(sync_host_resolver) {
    132   }
    133 
    134   virtual ProxyResolver* CreateProxyResolver() {
    135     return new SyncProxyResolver(sync_host_resolver_.get());
    136   }
    137 
    138  private:
    139   const scoped_ptr<SyncHostResolverBridge> sync_host_resolver_;
    140 };
    141 
    142 // This helper thread is used to create the circumstances for the deadlock.
    143 // It is analagous to the "IO thread" which would be main thread running the
    144 // network stack.
    145 class IOThread : public base::Thread {
    146  public:
    147   IOThread() : base::Thread("IO-thread") {}
    148 
    149   virtual ~IOThread() {
    150     Stop();
    151   }
    152 
    153   BlockableHostResolver* async_resolver() {
    154     return async_resolver_.get();
    155   }
    156 
    157  protected:
    158   virtual void Init() {
    159     async_resolver_.reset(new BlockableHostResolver());
    160 
    161     // Create a synchronous host resolver that operates the async host
    162     // resolver on THIS thread.
    163     SyncHostResolverBridge* sync_resolver =
    164         new SyncHostResolverBridge(async_resolver_.get(), message_loop());
    165 
    166     proxy_resolver_.reset(
    167         new MultiThreadedProxyResolver(
    168             new SyncProxyResolverFactory(sync_resolver),
    169             1u));
    170 
    171     // Initialize the resolver.
    172     TestCompletionCallback callback;
    173     proxy_resolver_->SetPacScript(ProxyResolverScriptData::FromURL(GURL()),
    174                                   &callback);
    175     EXPECT_EQ(OK, callback.WaitForResult());
    176 
    177     // Start an asynchronous request to the proxy resolver
    178     // (note that it will never complete).
    179     proxy_resolver_->GetProxyForURL(GURL("http://test/"), &results_,
    180                                     &callback_, &request_, BoundNetLog());
    181   }
    182 
    183   virtual void CleanUp() {
    184     // Cancel the outstanding request (note however that this will not
    185     // unblock the PAC thread though).
    186     proxy_resolver_->CancelRequest(request_);
    187 
    188     // Delete the single threaded proxy resolver.
    189     proxy_resolver_.reset();
    190 
    191     // (There may have been a completion posted back to origin thread, avoid
    192     // leaking it by running).
    193     MessageLoop::current()->RunAllPending();
    194 
    195     // During the teardown sequence of the single threaded proxy resolver,
    196     // the outstanding host resolve should have been cancelled.
    197     EXPECT_TRUE(async_resolver_->was_request_cancelled());
    198   }
    199 
    200  private:
    201   // This (async) host resolver will outlive the thread that is operating it
    202   // synchronously.
    203   scoped_ptr<BlockableHostResolver> async_resolver_;
    204 
    205   scoped_ptr<ProxyResolver> proxy_resolver_;
    206 
    207   // Data for the outstanding request to the single threaded proxy resolver.
    208   TestCompletionCallback callback_;
    209   ProxyInfo results_;
    210   ProxyResolver::RequestHandle request_;
    211 };
    212 
    213 // Test that a deadlock does not happen during shutdown when a host resolve
    214 // is outstanding on the SyncHostResolverBridge.
    215 // This is a regression test for http://crbug.com/41244.
    216 TEST(MultiThreadedProxyResolverTest, ShutdownIsCalledBeforeThreadJoin) {
    217   IOThread io_thread;
    218   base::Thread::Options options;
    219   options.message_loop_type = MessageLoop::TYPE_IO;
    220   ASSERT_TRUE(io_thread.StartWithOptions(options));
    221 
    222   io_thread.async_resolver()->WaitUntilRequestIsReceived();
    223 
    224   // Now upon exitting this scope, the IOThread is destroyed -- this will
    225   // stop the IOThread, which will in turn delete the
    226   // SingleThreadedProxyResolver, which in turn will stop its internal
    227   // PAC thread (which is currently blocked waiting on the host resolve which
    228   // is running on IOThread).  The IOThread::Cleanup() will verify that after
    229   // the PAC thread is stopped, it cancels the request on the HostResolver.
    230 }
    231 
    232 }  // namespace
    233 
    234 }  // namespace net
    235