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