Home | History | Annotate | Download | only in proxy
      1 // Copyright (c) 2012 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/dhcp_proxy_script_adapter_fetcher_win.h"
      6 
      7 #include "base/synchronization/waitable_event.h"
      8 #include "base/test/test_timeouts.h"
      9 #include "base/threading/sequenced_worker_pool.h"
     10 #include "base/timer/elapsed_timer.h"
     11 #include "base/timer/timer.h"
     12 #include "net/base/net_errors.h"
     13 #include "net/base/test_completion_callback.h"
     14 #include "net/proxy/mock_proxy_script_fetcher.h"
     15 #include "net/proxy/proxy_script_fetcher_impl.h"
     16 #include "net/test/spawned_test_server/spawned_test_server.h"
     17 #include "net/url_request/url_request_test_util.h"
     18 #include "testing/gtest/include/gtest/gtest.h"
     19 
     20 namespace net {
     21 
     22 namespace {
     23 
     24 const char* const kPacUrl = "http://pacserver/script.pac";
     25 
     26 // In net/proxy/dhcp_proxy_script_fetcher_win_unittest.cc there are a few
     27 // tests that exercise DhcpProxyScriptAdapterFetcher end-to-end along with
     28 // DhcpProxyScriptFetcherWin, i.e. it tests the end-to-end usage of Win32
     29 // APIs and the network.  In this file we test only by stubbing out
     30 // functionality.
     31 
     32 // Version of DhcpProxyScriptAdapterFetcher that mocks out dependencies
     33 // to allow unit testing.
     34 class MockDhcpProxyScriptAdapterFetcher
     35     : public DhcpProxyScriptAdapterFetcher {
     36  public:
     37   explicit MockDhcpProxyScriptAdapterFetcher(
     38       URLRequestContext* context,
     39       scoped_refptr<base::TaskRunner> task_runner)
     40       : DhcpProxyScriptAdapterFetcher(context, task_runner),
     41         dhcp_delay_(base::TimeDelta::FromMilliseconds(1)),
     42         timeout_(TestTimeouts::action_timeout()),
     43         configured_url_(kPacUrl),
     44         fetcher_delay_ms_(1),
     45         fetcher_result_(OK),
     46         pac_script_("bingo") {
     47   }
     48 
     49   void Cancel() {
     50     DhcpProxyScriptAdapterFetcher::Cancel();
     51     fetcher_ = NULL;
     52   }
     53 
     54   virtual ProxyScriptFetcher* ImplCreateScriptFetcher() OVERRIDE {
     55     // We don't maintain ownership of the fetcher, it is transferred to
     56     // the caller.
     57     fetcher_ = new MockProxyScriptFetcher();
     58     if (fetcher_delay_ms_ != -1) {
     59       fetcher_timer_.Start(FROM_HERE,
     60           base::TimeDelta::FromMilliseconds(fetcher_delay_ms_),
     61           this, &MockDhcpProxyScriptAdapterFetcher::OnFetcherTimer);
     62     }
     63     return fetcher_;
     64   }
     65 
     66   class DelayingDhcpQuery : public DhcpQuery {
     67    public:
     68     explicit DelayingDhcpQuery()
     69         : DhcpQuery(),
     70           test_finished_event_(true, false) {
     71     }
     72 
     73     std::string ImplGetPacURLFromDhcp(
     74         const std::string& adapter_name) OVERRIDE {
     75       base::ElapsedTimer timer;
     76       test_finished_event_.TimedWait(dhcp_delay_);
     77       return configured_url_;
     78     }
     79 
     80     base::WaitableEvent test_finished_event_;
     81     base::TimeDelta dhcp_delay_;
     82     std::string configured_url_;
     83   };
     84 
     85   virtual DhcpQuery* ImplCreateDhcpQuery() OVERRIDE {
     86     dhcp_query_ = new DelayingDhcpQuery();
     87     dhcp_query_->dhcp_delay_ = dhcp_delay_;
     88     dhcp_query_->configured_url_ = configured_url_;
     89     return dhcp_query_;
     90   }
     91 
     92   // Use a shorter timeout so tests can finish more quickly.
     93   virtual base::TimeDelta ImplGetTimeout() const OVERRIDE {
     94     return timeout_;
     95   }
     96 
     97   void OnFetcherTimer() {
     98     // Note that there is an assumption by this mock implementation that
     99     // DhcpProxyScriptAdapterFetcher::Fetch will call ImplCreateScriptFetcher
    100     // and call Fetch on the fetcher before the message loop is re-entered.
    101     // This holds true today, but if you hit this DCHECK the problem can
    102     // possibly be resolved by having a separate subclass of
    103     // MockProxyScriptFetcher that adds the delay internally (instead of
    104     // the simple approach currently used in ImplCreateScriptFetcher above).
    105     DCHECK(fetcher_ && fetcher_->has_pending_request());
    106     fetcher_->NotifyFetchCompletion(fetcher_result_, pac_script_);
    107     fetcher_ = NULL;
    108   }
    109 
    110   bool IsWaitingForFetcher() const {
    111     return state() == STATE_WAIT_URL;
    112   }
    113 
    114   bool WasCancelled() const {
    115     return state() == STATE_CANCEL;
    116   }
    117 
    118   void FinishTest() {
    119     DCHECK(dhcp_query_);
    120     dhcp_query_->test_finished_event_.Signal();
    121   }
    122 
    123   base::TimeDelta dhcp_delay_;
    124   base::TimeDelta timeout_;
    125   std::string configured_url_;
    126   int fetcher_delay_ms_;
    127   int fetcher_result_;
    128   std::string pac_script_;
    129   MockProxyScriptFetcher* fetcher_;
    130   base::OneShotTimer<MockDhcpProxyScriptAdapterFetcher> fetcher_timer_;
    131   scoped_refptr<DelayingDhcpQuery> dhcp_query_;
    132 };
    133 
    134 class FetcherClient {
    135  public:
    136   FetcherClient()
    137       : url_request_context_(new TestURLRequestContext()),
    138         worker_pool_(
    139             new base::SequencedWorkerPool(4, "DhcpAdapterFetcherTest")),
    140         fetcher_(new MockDhcpProxyScriptAdapterFetcher(
    141             url_request_context_.get(),
    142             worker_pool_->GetTaskRunnerWithShutdownBehavior(
    143                 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN))) {
    144   }
    145 
    146   ~FetcherClient() {
    147     worker_pool_->Shutdown();
    148   }
    149 
    150   void WaitForResult(int expected_error) {
    151     EXPECT_EQ(expected_error, callback_.WaitForResult());
    152   }
    153 
    154   void RunTest() {
    155     fetcher_->Fetch("adapter name", callback_.callback());
    156   }
    157 
    158   void FinishTestAllowCleanup() {
    159     fetcher_->FinishTest();
    160     base::MessageLoop::current()->RunUntilIdle();
    161   }
    162 
    163   TestCompletionCallback callback_;
    164   scoped_ptr<URLRequestContext> url_request_context_;
    165   scoped_refptr<base::SequencedWorkerPool> worker_pool_;
    166   scoped_ptr<MockDhcpProxyScriptAdapterFetcher> fetcher_;
    167   base::string16 pac_text_;
    168 };
    169 
    170 TEST(DhcpProxyScriptAdapterFetcher, NormalCaseURLNotInDhcp) {
    171   FetcherClient client;
    172   client.fetcher_->configured_url_ = "";
    173   client.RunTest();
    174   client.WaitForResult(ERR_PAC_NOT_IN_DHCP);
    175   ASSERT_TRUE(client.fetcher_->DidFinish());
    176   EXPECT_EQ(ERR_PAC_NOT_IN_DHCP, client.fetcher_->GetResult());
    177   EXPECT_EQ(base::string16(L""), client.fetcher_->GetPacScript());
    178 }
    179 
    180 TEST(DhcpProxyScriptAdapterFetcher, NormalCaseURLInDhcp) {
    181   FetcherClient client;
    182   client.RunTest();
    183   client.WaitForResult(OK);
    184   ASSERT_TRUE(client.fetcher_->DidFinish());
    185   EXPECT_EQ(OK, client.fetcher_->GetResult());
    186   EXPECT_EQ(base::string16(L"bingo"), client.fetcher_->GetPacScript());
    187   EXPECT_EQ(GURL(kPacUrl), client.fetcher_->GetPacURL());
    188 }
    189 
    190 TEST(DhcpProxyScriptAdapterFetcher, TimeoutDuringDhcp) {
    191   // Does a Fetch() with a long enough delay on accessing DHCP that the
    192   // fetcher should time out.  This is to test a case manual testing found,
    193   // where under certain circumstances (e.g. adapter enabled for DHCP and
    194   // needs to retrieve its configuration from DHCP, but no DHCP server
    195   // present on the network) accessing DHCP can take on the order of tens
    196   // of seconds.
    197   FetcherClient client;
    198   client.fetcher_->dhcp_delay_ = TestTimeouts::action_max_timeout();
    199   client.fetcher_->timeout_ = base::TimeDelta::FromMilliseconds(25);
    200 
    201   base::ElapsedTimer timer;
    202   client.RunTest();
    203   // An error different from this would be received if the timeout didn't
    204   // kick in.
    205   client.WaitForResult(ERR_TIMED_OUT);
    206 
    207   ASSERT_TRUE(client.fetcher_->DidFinish());
    208   EXPECT_EQ(ERR_TIMED_OUT, client.fetcher_->GetResult());
    209   EXPECT_EQ(base::string16(L""), client.fetcher_->GetPacScript());
    210   EXPECT_EQ(GURL(), client.fetcher_->GetPacURL());
    211   client.FinishTestAllowCleanup();
    212 }
    213 
    214 TEST(DhcpProxyScriptAdapterFetcher, CancelWhileDhcp) {
    215   FetcherClient client;
    216   client.RunTest();
    217   client.fetcher_->Cancel();
    218   base::MessageLoop::current()->RunUntilIdle();
    219   ASSERT_FALSE(client.fetcher_->DidFinish());
    220   ASSERT_TRUE(client.fetcher_->WasCancelled());
    221   EXPECT_EQ(ERR_ABORTED, client.fetcher_->GetResult());
    222   EXPECT_EQ(base::string16(L""), client.fetcher_->GetPacScript());
    223   EXPECT_EQ(GURL(), client.fetcher_->GetPacURL());
    224   client.FinishTestAllowCleanup();
    225 }
    226 
    227 TEST(DhcpProxyScriptAdapterFetcher, CancelWhileFetcher) {
    228   FetcherClient client;
    229   // This causes the mock fetcher not to pretend the
    230   // fetcher finishes after a timeout.
    231   client.fetcher_->fetcher_delay_ms_ = -1;
    232   client.RunTest();
    233   int max_loops = 4;
    234   while (!client.fetcher_->IsWaitingForFetcher() && max_loops--) {
    235     base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
    236     base::MessageLoop::current()->RunUntilIdle();
    237   }
    238   client.fetcher_->Cancel();
    239   base::MessageLoop::current()->RunUntilIdle();
    240   ASSERT_FALSE(client.fetcher_->DidFinish());
    241   ASSERT_TRUE(client.fetcher_->WasCancelled());
    242   EXPECT_EQ(ERR_ABORTED, client.fetcher_->GetResult());
    243   EXPECT_EQ(base::string16(L""), client.fetcher_->GetPacScript());
    244   // GetPacURL() still returns the URL fetched in this case.
    245   EXPECT_EQ(GURL(kPacUrl), client.fetcher_->GetPacURL());
    246   client.FinishTestAllowCleanup();
    247 }
    248 
    249 TEST(DhcpProxyScriptAdapterFetcher, CancelAtCompletion) {
    250   FetcherClient client;
    251   client.RunTest();
    252   client.WaitForResult(OK);
    253   client.fetcher_->Cancel();
    254   // Canceling after you're done should have no effect, so these
    255   // are identical expectations to the NormalCaseURLInDhcp test.
    256   ASSERT_TRUE(client.fetcher_->DidFinish());
    257   EXPECT_EQ(OK, client.fetcher_->GetResult());
    258   EXPECT_EQ(base::string16(L"bingo"), client.fetcher_->GetPacScript());
    259   EXPECT_EQ(GURL(kPacUrl), client.fetcher_->GetPacURL());
    260   client.FinishTestAllowCleanup();
    261 }
    262 
    263 // Does a real fetch on a mock DHCP configuration.
    264 class MockDhcpRealFetchProxyScriptAdapterFetcher
    265     : public MockDhcpProxyScriptAdapterFetcher {
    266  public:
    267   explicit MockDhcpRealFetchProxyScriptAdapterFetcher(
    268       URLRequestContext* context,
    269       scoped_refptr<base::TaskRunner> task_runner)
    270       : MockDhcpProxyScriptAdapterFetcher(context, task_runner),
    271         url_request_context_(context) {
    272   }
    273 
    274   // Returns a real proxy script fetcher.
    275   ProxyScriptFetcher* ImplCreateScriptFetcher() OVERRIDE {
    276     ProxyScriptFetcher* fetcher =
    277         new ProxyScriptFetcherImpl(url_request_context_);
    278     return fetcher;
    279   }
    280 
    281   URLRequestContext* url_request_context_;
    282 };
    283 
    284 TEST(DhcpProxyScriptAdapterFetcher, MockDhcpRealFetch) {
    285   SpawnedTestServer test_server(
    286       SpawnedTestServer::TYPE_HTTP,
    287       SpawnedTestServer::kLocalhost,
    288       base::FilePath(
    289           FILE_PATH_LITERAL("net/data/proxy_script_fetcher_unittest")));
    290   ASSERT_TRUE(test_server.Start());
    291 
    292   GURL configured_url = test_server.GetURL("files/downloadable.pac");
    293 
    294   FetcherClient client;
    295   TestURLRequestContext url_request_context;
    296   scoped_refptr<base::TaskRunner> runner =
    297       client.worker_pool_->GetTaskRunnerWithShutdownBehavior(
    298           base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
    299   client.fetcher_.reset(
    300       new MockDhcpRealFetchProxyScriptAdapterFetcher(
    301           &url_request_context, runner));
    302   client.fetcher_->configured_url_ = configured_url.spec();
    303   client.RunTest();
    304   client.WaitForResult(OK);
    305   ASSERT_TRUE(client.fetcher_->DidFinish());
    306   EXPECT_EQ(OK, client.fetcher_->GetResult());
    307   EXPECT_EQ(base::string16(L"-downloadable.pac-\n"),
    308             client.fetcher_->GetPacScript());
    309   EXPECT_EQ(configured_url,
    310             client.fetcher_->GetPacURL());
    311 }
    312 
    313 #define BASE_URL "http://corpserver/proxy.pac"
    314 
    315 TEST(DhcpProxyScriptAdapterFetcher, SanitizeDhcpApiString) {
    316   const size_t kBaseUrlLen = strlen(BASE_URL);
    317 
    318   // Default case.
    319   EXPECT_EQ(BASE_URL,
    320             DhcpProxyScriptAdapterFetcher::SanitizeDhcpApiString(
    321                 BASE_URL, kBaseUrlLen));
    322 
    323   // Trailing \n and no null-termination.
    324   EXPECT_EQ(BASE_URL,
    325             DhcpProxyScriptAdapterFetcher::SanitizeDhcpApiString(
    326                 BASE_URL "\nblablabla", kBaseUrlLen + 1));
    327 
    328   // Embedded NULLs.
    329   EXPECT_EQ(BASE_URL,
    330             DhcpProxyScriptAdapterFetcher::SanitizeDhcpApiString(
    331                 BASE_URL "\0foo\0blat", kBaseUrlLen + 9));
    332 }
    333 
    334 #undef BASE_URL
    335 
    336 }  // namespace
    337 
    338 }  // namespace net
    339