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 <vector>
      6 
      7 #include "base/bind.h"
      8 #include "base/memory/weak_ptr.h"
      9 #include "base/message_loop/message_loop.h"
     10 #include "base/run_loop.h"
     11 #include "base/strings/string_util.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "base/time/time.h"
     14 #include "net/base/net_errors.h"
     15 #include "net/base/net_log.h"
     16 #include "net/base/net_log_unittest.h"
     17 #include "net/base/test_completion_callback.h"
     18 #include "net/dns/mock_host_resolver.h"
     19 #include "net/proxy/dhcp_proxy_script_fetcher.h"
     20 #include "net/proxy/proxy_config.h"
     21 #include "net/proxy/proxy_resolver.h"
     22 #include "net/proxy/proxy_script_decider.h"
     23 #include "net/proxy/proxy_script_fetcher.h"
     24 #include "net/url_request/url_request_context.h"
     25 #include "testing/gtest/include/gtest/gtest.h"
     26 
     27 namespace net {
     28 namespace {
     29 
     30 enum Error {
     31   kFailedDownloading = -100,
     32   kFailedParsing = ERR_PAC_SCRIPT_FAILED,
     33 };
     34 
     35 class Rules {
     36  public:
     37   struct Rule {
     38     Rule(const GURL& url, int fetch_error, bool is_valid_script)
     39         : url(url),
     40           fetch_error(fetch_error),
     41           is_valid_script(is_valid_script) {
     42     }
     43 
     44     base::string16 text() const {
     45       if (is_valid_script)
     46         return UTF8ToUTF16(url.spec() + "!FindProxyForURL");
     47       if (fetch_error == OK)
     48         return UTF8ToUTF16(url.spec() + "!invalid-script");
     49       return base::string16();
     50     }
     51 
     52     GURL url;
     53     int fetch_error;
     54     bool is_valid_script;
     55   };
     56 
     57   Rule AddSuccessRule(const char* url) {
     58     Rule rule(GURL(url), OK /*fetch_error*/, true);
     59     rules_.push_back(rule);
     60     return rule;
     61   }
     62 
     63   void AddFailDownloadRule(const char* url) {
     64     rules_.push_back(Rule(GURL(url), kFailedDownloading /*fetch_error*/,
     65         false));
     66   }
     67 
     68   void AddFailParsingRule(const char* url) {
     69     rules_.push_back(Rule(GURL(url), OK /*fetch_error*/, false));
     70   }
     71 
     72   const Rule& GetRuleByUrl(const GURL& url) const {
     73     for (RuleList::const_iterator it = rules_.begin(); it != rules_.end();
     74          ++it) {
     75       if (it->url == url)
     76         return *it;
     77     }
     78     LOG(FATAL) << "Rule not found for " << url;
     79     return rules_[0];
     80   }
     81 
     82   const Rule& GetRuleByText(const base::string16& text) const {
     83     for (RuleList::const_iterator it = rules_.begin(); it != rules_.end();
     84          ++it) {
     85       if (it->text() == text)
     86         return *it;
     87     }
     88     LOG(FATAL) << "Rule not found for " << text;
     89     return rules_[0];
     90   }
     91 
     92  private:
     93   typedef std::vector<Rule> RuleList;
     94   RuleList rules_;
     95 };
     96 
     97 class RuleBasedProxyScriptFetcher : public ProxyScriptFetcher {
     98  public:
     99   explicit RuleBasedProxyScriptFetcher(const Rules* rules)
    100       : rules_(rules), request_context_(NULL) {}
    101 
    102   virtual void SetRequestContext(URLRequestContext* context) {
    103     request_context_ = context;
    104   }
    105 
    106   // ProxyScriptFetcher implementation.
    107   virtual int Fetch(const GURL& url,
    108                     base::string16* text,
    109                     const CompletionCallback& callback) OVERRIDE {
    110     const Rules::Rule& rule = rules_->GetRuleByUrl(url);
    111     int rv = rule.fetch_error;
    112     EXPECT_NE(ERR_UNEXPECTED, rv);
    113     if (rv == OK)
    114       *text = rule.text();
    115     return rv;
    116   }
    117 
    118   virtual void Cancel() OVERRIDE {}
    119 
    120   virtual URLRequestContext* GetRequestContext() const OVERRIDE {
    121     return request_context_;
    122   }
    123 
    124  private:
    125   const Rules* rules_;
    126   URLRequestContext* request_context_;
    127 };
    128 
    129 // A mock retriever, returns asynchronously when CompleteRequests() is called.
    130 class MockDhcpProxyScriptFetcher : public DhcpProxyScriptFetcher {
    131  public:
    132   MockDhcpProxyScriptFetcher();
    133   virtual ~MockDhcpProxyScriptFetcher();
    134 
    135   virtual int Fetch(base::string16* utf16_text,
    136                     const CompletionCallback& callback) OVERRIDE;
    137   virtual void Cancel() OVERRIDE;
    138   virtual const GURL& GetPacURL() const OVERRIDE;
    139 
    140   virtual void SetPacURL(const GURL& url);
    141 
    142   virtual void CompleteRequests(int result, const base::string16& script);
    143 
    144  private:
    145   CompletionCallback callback_;
    146   base::string16* utf16_text_;
    147   GURL gurl_;
    148   DISALLOW_COPY_AND_ASSIGN(MockDhcpProxyScriptFetcher);
    149 };
    150 
    151 MockDhcpProxyScriptFetcher::MockDhcpProxyScriptFetcher() { }
    152 
    153 MockDhcpProxyScriptFetcher::~MockDhcpProxyScriptFetcher() { }
    154 
    155 int MockDhcpProxyScriptFetcher::Fetch(base::string16* utf16_text,
    156                                       const CompletionCallback& callback) {
    157   utf16_text_ = utf16_text;
    158   callback_ = callback;
    159   return ERR_IO_PENDING;
    160 }
    161 
    162 void MockDhcpProxyScriptFetcher::Cancel() { }
    163 
    164 const GURL& MockDhcpProxyScriptFetcher::GetPacURL() const {
    165   return gurl_;
    166 }
    167 
    168 void MockDhcpProxyScriptFetcher::SetPacURL(const GURL& url) {
    169   gurl_ = url;
    170 }
    171 
    172 void MockDhcpProxyScriptFetcher::CompleteRequests(
    173     int result, const base::string16& script) {
    174   *utf16_text_ = script;
    175   callback_.Run(result);
    176 }
    177 
    178 // Succeed using custom PAC script.
    179 TEST(ProxyScriptDeciderTest, CustomPacSucceeds) {
    180   Rules rules;
    181   RuleBasedProxyScriptFetcher fetcher(&rules);
    182   DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
    183 
    184   ProxyConfig config;
    185   config.set_pac_url(GURL("http://custom/proxy.pac"));
    186 
    187   Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
    188 
    189   TestCompletionCallback callback;
    190   CapturingNetLog log;
    191   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, &log);
    192   EXPECT_EQ(OK, decider.Start(
    193       config, base::TimeDelta(), true, callback.callback()));
    194   EXPECT_EQ(rule.text(), decider.script_data()->utf16());
    195 
    196   // Check the NetLog was filled correctly.
    197   CapturingNetLog::CapturedEntryList entries;
    198   log.GetEntries(&entries);
    199 
    200   EXPECT_EQ(4u, entries.size());
    201   EXPECT_TRUE(LogContainsBeginEvent(
    202       entries, 0, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
    203   EXPECT_TRUE(LogContainsBeginEvent(
    204       entries, 1, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    205   EXPECT_TRUE(LogContainsEndEvent(
    206       entries, 2, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    207   EXPECT_TRUE(LogContainsEndEvent(
    208       entries, 3, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
    209 
    210   EXPECT_TRUE(decider.effective_config().has_pac_url());
    211   EXPECT_EQ(config.pac_url(), decider.effective_config().pac_url());
    212 }
    213 
    214 // Fail downloading the custom PAC script.
    215 TEST(ProxyScriptDeciderTest, CustomPacFails1) {
    216   Rules rules;
    217   RuleBasedProxyScriptFetcher fetcher(&rules);
    218   DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
    219 
    220   ProxyConfig config;
    221   config.set_pac_url(GURL("http://custom/proxy.pac"));
    222 
    223   rules.AddFailDownloadRule("http://custom/proxy.pac");
    224 
    225   TestCompletionCallback callback;
    226   CapturingNetLog log;
    227   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, &log);
    228   EXPECT_EQ(kFailedDownloading,
    229             decider.Start(config, base::TimeDelta(), true,
    230                           callback.callback()));
    231   EXPECT_EQ(NULL, decider.script_data());
    232 
    233   // Check the NetLog was filled correctly.
    234   CapturingNetLog::CapturedEntryList entries;
    235   log.GetEntries(&entries);
    236 
    237   EXPECT_EQ(4u, entries.size());
    238   EXPECT_TRUE(LogContainsBeginEvent(
    239       entries, 0, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
    240   EXPECT_TRUE(LogContainsBeginEvent(
    241       entries, 1, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    242   EXPECT_TRUE(LogContainsEndEvent(
    243       entries, 2, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    244   EXPECT_TRUE(LogContainsEndEvent(
    245       entries, 3, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
    246 
    247   EXPECT_FALSE(decider.effective_config().has_pac_url());
    248 }
    249 
    250 // Fail parsing the custom PAC script.
    251 TEST(ProxyScriptDeciderTest, CustomPacFails2) {
    252   Rules rules;
    253   RuleBasedProxyScriptFetcher fetcher(&rules);
    254   DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
    255 
    256   ProxyConfig config;
    257   config.set_pac_url(GURL("http://custom/proxy.pac"));
    258 
    259   rules.AddFailParsingRule("http://custom/proxy.pac");
    260 
    261   TestCompletionCallback callback;
    262   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, NULL);
    263   EXPECT_EQ(kFailedParsing,
    264             decider.Start(config, base::TimeDelta(), true,
    265                           callback.callback()));
    266   EXPECT_EQ(NULL, decider.script_data());
    267 }
    268 
    269 // Fail downloading the custom PAC script, because the fetcher was NULL.
    270 TEST(ProxyScriptDeciderTest, HasNullProxyScriptFetcher) {
    271   Rules rules;
    272   DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
    273 
    274   ProxyConfig config;
    275   config.set_pac_url(GURL("http://custom/proxy.pac"));
    276 
    277   TestCompletionCallback callback;
    278   ProxyScriptDecider decider(NULL, &dhcp_fetcher, NULL);
    279   EXPECT_EQ(ERR_UNEXPECTED,
    280             decider.Start(config, base::TimeDelta(), true,
    281                           callback.callback()));
    282   EXPECT_EQ(NULL, decider.script_data());
    283 }
    284 
    285 // Succeeds in choosing autodetect (WPAD DNS).
    286 TEST(ProxyScriptDeciderTest, AutodetectSuccess) {
    287   Rules rules;
    288   RuleBasedProxyScriptFetcher fetcher(&rules);
    289   DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
    290 
    291   ProxyConfig config;
    292   config.set_auto_detect(true);
    293 
    294   Rules::Rule rule = rules.AddSuccessRule("http://wpad/wpad.dat");
    295 
    296   TestCompletionCallback callback;
    297   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, NULL);
    298   EXPECT_EQ(OK, decider.Start(
    299       config, base::TimeDelta(), true, callback.callback()));
    300   EXPECT_EQ(rule.text(), decider.script_data()->utf16());
    301 
    302   EXPECT_TRUE(decider.effective_config().has_pac_url());
    303   EXPECT_EQ(rule.url, decider.effective_config().pac_url());
    304 }
    305 
    306 class ProxyScriptDeciderQuickCheckTest : public ::testing::Test {
    307  public:
    308   ProxyScriptDeciderQuickCheckTest()
    309       : rule_(rules_.AddSuccessRule("http://wpad/wpad.dat")),
    310         fetcher_(&rules_) { }
    311 
    312   virtual void SetUp() OVERRIDE {
    313     request_context_.set_host_resolver(&resolver_);
    314     fetcher_.SetRequestContext(&request_context_);
    315     config_.set_auto_detect(true);
    316     decider_.reset(new ProxyScriptDecider(&fetcher_, &dhcp_fetcher_, NULL));
    317   }
    318 
    319   int StartDecider() {
    320     return decider_->Start(config_, base::TimeDelta(), true,
    321                             callback_.callback());
    322   }
    323 
    324  protected:
    325   scoped_ptr<ProxyScriptDecider> decider_;
    326   MockHostResolver resolver_;
    327   Rules rules_;
    328   Rules::Rule rule_;
    329   TestCompletionCallback callback_;
    330   RuleBasedProxyScriptFetcher fetcher_;
    331   ProxyConfig config_;
    332 
    333  private:
    334   URLRequestContext request_context_;
    335 
    336   DoNothingDhcpProxyScriptFetcher dhcp_fetcher_;
    337 };
    338 
    339 // Fails if a synchronous DNS lookup success for wpad causes QuickCheck to fail.
    340 TEST_F(ProxyScriptDeciderQuickCheckTest, SyncSuccess) {
    341   resolver_.set_synchronous_mode(true);
    342   resolver_.rules()->AddRule("wpad", "1.2.3.4");
    343 
    344   EXPECT_EQ(OK, StartDecider());
    345   EXPECT_EQ(rule_.text(), decider_->script_data()->utf16());
    346 
    347   EXPECT_TRUE(decider_->effective_config().has_pac_url());
    348   EXPECT_EQ(rule_.url, decider_->effective_config().pac_url());
    349 }
    350 
    351 // Fails if an asynchronous DNS lookup success for wpad causes QuickCheck to
    352 // fail.
    353 TEST_F(ProxyScriptDeciderQuickCheckTest, AsyncSuccess) {
    354   resolver_.set_ondemand_mode(true);
    355   resolver_.rules()->AddRule("wpad", "1.2.3.4");
    356 
    357   EXPECT_EQ(ERR_IO_PENDING, StartDecider());
    358   ASSERT_TRUE(resolver_.has_pending_requests());
    359   resolver_.ResolveAllPending();
    360   callback_.WaitForResult();
    361   EXPECT_FALSE(resolver_.has_pending_requests());
    362   EXPECT_EQ(rule_.text(), decider_->script_data()->utf16());
    363   EXPECT_TRUE(decider_->effective_config().has_pac_url());
    364   EXPECT_EQ(rule_.url, decider_->effective_config().pac_url());
    365 }
    366 
    367 // Fails if an asynchronous DNS lookup failure (i.e. an NXDOMAIN) still causes
    368 // ProxyScriptDecider to yield a PAC URL.
    369 TEST_F(ProxyScriptDeciderQuickCheckTest, AsyncFail) {
    370   resolver_.set_ondemand_mode(true);
    371   resolver_.rules()->AddSimulatedFailure("wpad");
    372   EXPECT_EQ(ERR_IO_PENDING, StartDecider());
    373   ASSERT_TRUE(resolver_.has_pending_requests());
    374   resolver_.ResolveAllPending();
    375   callback_.WaitForResult();
    376   EXPECT_FALSE(decider_->effective_config().has_pac_url());
    377 }
    378 
    379 // Fails if a DNS lookup timeout either causes ProxyScriptDecider to yield a PAC
    380 // URL or causes ProxyScriptDecider not to cancel its pending resolution.
    381 TEST_F(ProxyScriptDeciderQuickCheckTest, AsyncTimeout) {
    382   resolver_.set_ondemand_mode(true);
    383   EXPECT_EQ(ERR_IO_PENDING, StartDecider());
    384   ASSERT_TRUE(resolver_.has_pending_requests());
    385   callback_.WaitForResult();
    386   EXPECT_FALSE(resolver_.has_pending_requests());
    387   EXPECT_FALSE(decider_->effective_config().has_pac_url());
    388 }
    389 
    390 // Fails if DHCP check doesn't take place before QuickCheck.
    391 TEST_F(ProxyScriptDeciderQuickCheckTest, QuickCheckInhibitsDhcp) {
    392   MockDhcpProxyScriptFetcher dhcp_fetcher;
    393   const char *kPac = "function FindProxyForURL(u,h) { return \"DIRECT\"; }";
    394   base::string16 pac_contents = base::UTF8ToUTF16(kPac);
    395   GURL url("http://foobar/baz");
    396   dhcp_fetcher.SetPacURL(url);
    397   decider_.reset(new ProxyScriptDecider(&fetcher_, &dhcp_fetcher, NULL));
    398   EXPECT_EQ(ERR_IO_PENDING, StartDecider());
    399   dhcp_fetcher.CompleteRequests(OK, pac_contents);
    400   EXPECT_TRUE(decider_->effective_config().has_pac_url());
    401   EXPECT_EQ(decider_->effective_config().pac_url(), url);
    402 }
    403 
    404 TEST_F(ProxyScriptDeciderQuickCheckTest, ExplicitPacUrl) {
    405   const char *kCustomUrl = "http://custom/proxy.pac";
    406   config_.set_pac_url(GURL(kCustomUrl));
    407   Rules::Rule rule = rules_.AddSuccessRule(kCustomUrl);
    408   resolver_.rules()->AddSimulatedFailure("wpad");
    409   resolver_.rules()->AddRule("custom", "1.2.3.4");
    410   EXPECT_EQ(ERR_IO_PENDING, StartDecider());
    411   callback_.WaitForResult();
    412   EXPECT_TRUE(decider_->effective_config().has_pac_url());
    413   EXPECT_EQ(rule.url, decider_->effective_config().pac_url());
    414 }
    415 
    416 // Fails at WPAD (downloading), but succeeds in choosing the custom PAC.
    417 TEST(ProxyScriptDeciderTest, AutodetectFailCustomSuccess1) {
    418   Rules rules;
    419   RuleBasedProxyScriptFetcher fetcher(&rules);
    420   DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
    421 
    422   ProxyConfig config;
    423   config.set_auto_detect(true);
    424   config.set_pac_url(GURL("http://custom/proxy.pac"));
    425 
    426   rules.AddFailDownloadRule("http://wpad/wpad.dat");
    427   Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
    428 
    429   TestCompletionCallback callback;
    430   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, NULL);
    431   EXPECT_EQ(OK, decider.Start(
    432       config, base::TimeDelta(), true, callback.callback()));
    433   EXPECT_EQ(rule.text(), decider.script_data()->utf16());
    434 
    435   EXPECT_TRUE(decider.effective_config().has_pac_url());
    436   EXPECT_EQ(rule.url, decider.effective_config().pac_url());
    437 }
    438 
    439 // Fails at WPAD (no DHCP config, DNS PAC fails parsing), but succeeds in
    440 // choosing the custom PAC.
    441 TEST(ProxyScriptDeciderTest, AutodetectFailCustomSuccess2) {
    442   Rules rules;
    443   RuleBasedProxyScriptFetcher fetcher(&rules);
    444   DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
    445 
    446   ProxyConfig config;
    447   config.set_auto_detect(true);
    448   config.set_pac_url(GURL("http://custom/proxy.pac"));
    449   config.proxy_rules().ParseFromString("unused-manual-proxy:99");
    450 
    451   rules.AddFailParsingRule("http://wpad/wpad.dat");
    452   Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
    453 
    454   TestCompletionCallback callback;
    455   CapturingNetLog log;
    456 
    457   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, &log);
    458   EXPECT_EQ(OK, decider.Start(config, base::TimeDelta(),
    459                           true, callback.callback()));
    460   EXPECT_EQ(rule.text(), decider.script_data()->utf16());
    461 
    462   // Verify that the effective configuration no longer contains auto detect or
    463   // any of the manual settings.
    464   EXPECT_TRUE(decider.effective_config().Equals(
    465       ProxyConfig::CreateFromCustomPacURL(GURL("http://custom/proxy.pac"))));
    466 
    467   // Check the NetLog was filled correctly.
    468   // (Note that various states are repeated since both WPAD and custom
    469   // PAC scripts are tried).
    470   CapturingNetLog::CapturedEntryList entries;
    471   log.GetEntries(&entries);
    472 
    473   EXPECT_EQ(10u, entries.size());
    474   EXPECT_TRUE(LogContainsBeginEvent(
    475       entries, 0, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
    476   // This is the DHCP phase, which fails fetching rather than parsing, so
    477   // there is no pair of SET_PAC_SCRIPT events.
    478   EXPECT_TRUE(LogContainsBeginEvent(
    479       entries, 1, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    480   EXPECT_TRUE(LogContainsEndEvent(
    481       entries, 2, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    482   EXPECT_TRUE(LogContainsEvent(
    483       entries, 3,
    484       NetLog::TYPE_PROXY_SCRIPT_DECIDER_FALLING_BACK_TO_NEXT_PAC_SOURCE,
    485       NetLog::PHASE_NONE));
    486   // This is the DNS phase, which attempts a fetch but fails.
    487   EXPECT_TRUE(LogContainsBeginEvent(
    488       entries, 4, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    489   EXPECT_TRUE(LogContainsEndEvent(
    490       entries, 5, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    491   EXPECT_TRUE(LogContainsEvent(
    492       entries, 6,
    493       NetLog::TYPE_PROXY_SCRIPT_DECIDER_FALLING_BACK_TO_NEXT_PAC_SOURCE,
    494       NetLog::PHASE_NONE));
    495   // Finally, the custom PAC URL phase.
    496   EXPECT_TRUE(LogContainsBeginEvent(
    497       entries, 7, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    498   EXPECT_TRUE(LogContainsEndEvent(
    499       entries, 8, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    500   EXPECT_TRUE(LogContainsEndEvent(
    501       entries, 9, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
    502 }
    503 
    504 // Fails at WPAD (downloading), and fails at custom PAC (downloading).
    505 TEST(ProxyScriptDeciderTest, AutodetectFailCustomFails1) {
    506   Rules rules;
    507   RuleBasedProxyScriptFetcher fetcher(&rules);
    508   DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
    509 
    510   ProxyConfig config;
    511   config.set_auto_detect(true);
    512   config.set_pac_url(GURL("http://custom/proxy.pac"));
    513 
    514   rules.AddFailDownloadRule("http://wpad/wpad.dat");
    515   rules.AddFailDownloadRule("http://custom/proxy.pac");
    516 
    517   TestCompletionCallback callback;
    518   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, NULL);
    519   EXPECT_EQ(kFailedDownloading,
    520             decider.Start(config, base::TimeDelta(), true,
    521                           callback.callback()));
    522   EXPECT_EQ(NULL, decider.script_data());
    523 }
    524 
    525 // Fails at WPAD (downloading), and fails at custom PAC (parsing).
    526 TEST(ProxyScriptDeciderTest, AutodetectFailCustomFails2) {
    527   Rules rules;
    528   RuleBasedProxyScriptFetcher fetcher(&rules);
    529   DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
    530 
    531   ProxyConfig config;
    532   config.set_auto_detect(true);
    533   config.set_pac_url(GURL("http://custom/proxy.pac"));
    534 
    535   rules.AddFailDownloadRule("http://wpad/wpad.dat");
    536   rules.AddFailParsingRule("http://custom/proxy.pac");
    537 
    538   TestCompletionCallback callback;
    539   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, NULL);
    540   EXPECT_EQ(kFailedParsing,
    541             decider.Start(config, base::TimeDelta(), true,
    542                           callback.callback()));
    543   EXPECT_EQ(NULL, decider.script_data());
    544 }
    545 
    546 // This is a copy-paste of CustomPacFails1, with the exception that we give it
    547 // a 1 millisecond delay. This means it will now complete asynchronously.
    548 // Moreover, we test the NetLog to make sure it logged the pause.
    549 TEST(ProxyScriptDeciderTest, CustomPacFails1_WithPositiveDelay) {
    550   Rules rules;
    551   RuleBasedProxyScriptFetcher fetcher(&rules);
    552   DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
    553 
    554   ProxyConfig config;
    555   config.set_pac_url(GURL("http://custom/proxy.pac"));
    556 
    557   rules.AddFailDownloadRule("http://custom/proxy.pac");
    558 
    559   TestCompletionCallback callback;
    560   CapturingNetLog log;
    561   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, &log);
    562   EXPECT_EQ(ERR_IO_PENDING,
    563             decider.Start(config, base::TimeDelta::FromMilliseconds(1),
    564                       true, callback.callback()));
    565 
    566   EXPECT_EQ(kFailedDownloading, callback.WaitForResult());
    567   EXPECT_EQ(NULL, decider.script_data());
    568 
    569   // Check the NetLog was filled correctly.
    570   CapturingNetLog::CapturedEntryList entries;
    571   log.GetEntries(&entries);
    572 
    573   EXPECT_EQ(6u, entries.size());
    574   EXPECT_TRUE(LogContainsBeginEvent(
    575       entries, 0, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
    576   EXPECT_TRUE(LogContainsBeginEvent(
    577       entries, 1, NetLog::TYPE_PROXY_SCRIPT_DECIDER_WAIT));
    578   EXPECT_TRUE(LogContainsEndEvent(
    579       entries, 2, NetLog::TYPE_PROXY_SCRIPT_DECIDER_WAIT));
    580   EXPECT_TRUE(LogContainsBeginEvent(
    581       entries, 3, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    582   EXPECT_TRUE(LogContainsEndEvent(
    583       entries, 4, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    584   EXPECT_TRUE(LogContainsEndEvent(
    585       entries, 5, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
    586 }
    587 
    588 // This is a copy-paste of CustomPacFails1, with the exception that we give it
    589 // a -5 second delay instead of a 0 ms delay. This change should have no effect
    590 // so the rest of the test is unchanged.
    591 TEST(ProxyScriptDeciderTest, CustomPacFails1_WithNegativeDelay) {
    592   Rules rules;
    593   RuleBasedProxyScriptFetcher fetcher(&rules);
    594   DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
    595 
    596   ProxyConfig config;
    597   config.set_pac_url(GURL("http://custom/proxy.pac"));
    598 
    599   rules.AddFailDownloadRule("http://custom/proxy.pac");
    600 
    601   TestCompletionCallback callback;
    602   CapturingNetLog log;
    603   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, &log);
    604   EXPECT_EQ(kFailedDownloading,
    605             decider.Start(config, base::TimeDelta::FromSeconds(-5),
    606                           true, callback.callback()));
    607   EXPECT_EQ(NULL, decider.script_data());
    608 
    609   // Check the NetLog was filled correctly.
    610   CapturingNetLog::CapturedEntryList entries;
    611   log.GetEntries(&entries);
    612 
    613   EXPECT_EQ(4u, entries.size());
    614   EXPECT_TRUE(LogContainsBeginEvent(
    615       entries, 0, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
    616   EXPECT_TRUE(LogContainsBeginEvent(
    617       entries, 1, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    618   EXPECT_TRUE(LogContainsEndEvent(
    619       entries, 2, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    620   EXPECT_TRUE(LogContainsEndEvent(
    621       entries, 3, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
    622 }
    623 
    624 class SynchronousSuccessDhcpFetcher : public DhcpProxyScriptFetcher {
    625  public:
    626   explicit SynchronousSuccessDhcpFetcher(const base::string16& expected_text)
    627       : gurl_("http://dhcppac/"), expected_text_(expected_text) {
    628   }
    629 
    630   virtual int Fetch(base::string16* utf16_text,
    631                     const CompletionCallback& callback) OVERRIDE {
    632     *utf16_text = expected_text_;
    633     return OK;
    634   }
    635 
    636   virtual void Cancel() OVERRIDE {
    637   }
    638 
    639   virtual const GURL& GetPacURL() const OVERRIDE {
    640     return gurl_;
    641   }
    642 
    643   const base::string16& expected_text() const {
    644     return expected_text_;
    645   }
    646 
    647  private:
    648   GURL gurl_;
    649   base::string16 expected_text_;
    650 
    651   DISALLOW_COPY_AND_ASSIGN(SynchronousSuccessDhcpFetcher);
    652 };
    653 
    654 // All of the tests above that use ProxyScriptDecider have tested
    655 // failure to fetch a PAC file via DHCP configuration, so we now test
    656 // success at downloading and parsing, and then success at downloading,
    657 // failure at parsing.
    658 
    659 TEST(ProxyScriptDeciderTest, AutodetectDhcpSuccess) {
    660   Rules rules;
    661   RuleBasedProxyScriptFetcher fetcher(&rules);
    662   SynchronousSuccessDhcpFetcher dhcp_fetcher(
    663       WideToUTF16(L"http://bingo/!FindProxyForURL"));
    664 
    665   ProxyConfig config;
    666   config.set_auto_detect(true);
    667 
    668   rules.AddSuccessRule("http://bingo/");
    669   rules.AddFailDownloadRule("http://wpad/wpad.dat");
    670 
    671   TestCompletionCallback callback;
    672   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, NULL);
    673   EXPECT_EQ(OK, decider.Start(
    674       config, base::TimeDelta(), true, callback.callback()));
    675   EXPECT_EQ(dhcp_fetcher.expected_text(),
    676             decider.script_data()->utf16());
    677 
    678   EXPECT_TRUE(decider.effective_config().has_pac_url());
    679   EXPECT_EQ(GURL("http://dhcppac/"), decider.effective_config().pac_url());
    680 }
    681 
    682 TEST(ProxyScriptDeciderTest, AutodetectDhcpFailParse) {
    683   Rules rules;
    684   RuleBasedProxyScriptFetcher fetcher(&rules);
    685   SynchronousSuccessDhcpFetcher dhcp_fetcher(
    686       WideToUTF16(L"http://bingo/!invalid-script"));
    687 
    688   ProxyConfig config;
    689   config.set_auto_detect(true);
    690 
    691   rules.AddFailParsingRule("http://bingo/");
    692   rules.AddFailDownloadRule("http://wpad/wpad.dat");
    693 
    694   TestCompletionCallback callback;
    695   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, NULL);
    696   // Since there is fallback to DNS-based WPAD, the final error will be that
    697   // it failed downloading, not that it failed parsing.
    698   EXPECT_EQ(kFailedDownloading,
    699       decider.Start(config, base::TimeDelta(), true, callback.callback()));
    700   EXPECT_EQ(NULL, decider.script_data());
    701 
    702   EXPECT_FALSE(decider.effective_config().has_pac_url());
    703 }
    704 
    705 class AsyncFailDhcpFetcher
    706     : public DhcpProxyScriptFetcher,
    707       public base::SupportsWeakPtr<AsyncFailDhcpFetcher> {
    708  public:
    709   AsyncFailDhcpFetcher() {}
    710   virtual ~AsyncFailDhcpFetcher() {}
    711 
    712   virtual int Fetch(base::string16* utf16_text,
    713                     const CompletionCallback& callback) OVERRIDE {
    714     callback_ = callback;
    715     base::MessageLoop::current()->PostTask(
    716         FROM_HERE,
    717         base::Bind(&AsyncFailDhcpFetcher::CallbackWithFailure, AsWeakPtr()));
    718     return ERR_IO_PENDING;
    719   }
    720 
    721   virtual void Cancel() OVERRIDE {
    722     callback_.Reset();
    723   }
    724 
    725   virtual const GURL& GetPacURL() const OVERRIDE {
    726     return dummy_gurl_;
    727   }
    728 
    729   void CallbackWithFailure() {
    730     if (!callback_.is_null())
    731       callback_.Run(ERR_PAC_NOT_IN_DHCP);
    732   }
    733 
    734  private:
    735   GURL dummy_gurl_;
    736   CompletionCallback callback_;
    737 };
    738 
    739 TEST(ProxyScriptDeciderTest, DhcpCancelledByDestructor) {
    740   // This regression test would crash before
    741   // http://codereview.chromium.org/7044058/
    742   // Thus, we don't care much about actual results (hence no EXPECT or ASSERT
    743   // macros below), just that it doesn't crash.
    744   Rules rules;
    745   RuleBasedProxyScriptFetcher fetcher(&rules);
    746 
    747   scoped_ptr<AsyncFailDhcpFetcher> dhcp_fetcher(new AsyncFailDhcpFetcher());
    748 
    749   ProxyConfig config;
    750   config.set_auto_detect(true);
    751   rules.AddFailDownloadRule("http://wpad/wpad.dat");
    752 
    753   TestCompletionCallback callback;
    754 
    755   // Scope so ProxyScriptDecider gets destroyed early.
    756   {
    757     ProxyScriptDecider decider(&fetcher, dhcp_fetcher.get(), NULL);
    758     decider.Start(config, base::TimeDelta(), true, callback.callback());
    759   }
    760 
    761   // Run the message loop to let the DHCP fetch complete and post the results
    762   // back. Before the fix linked to above, this would try to invoke on
    763   // the callback object provided by ProxyScriptDecider after it was
    764   // no longer valid.
    765   base::MessageLoop::current()->RunUntilIdle();
    766 }
    767 
    768 }  // namespace
    769 }  // namespace net
    770