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/strings/string_util.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "base/time/time.h"
     13 #include "net/base/net_errors.h"
     14 #include "net/base/net_log.h"
     15 #include "net/base/net_log_unittest.h"
     16 #include "net/base/test_completion_callback.h"
     17 #include "net/proxy/dhcp_proxy_script_fetcher.h"
     18 #include "net/proxy/proxy_config.h"
     19 #include "net/proxy/proxy_resolver.h"
     20 #include "net/proxy/proxy_script_decider.h"
     21 #include "net/proxy/proxy_script_fetcher.h"
     22 #include "testing/gtest/include/gtest/gtest.h"
     23 
     24 namespace net {
     25 namespace {
     26 
     27 enum Error {
     28   kFailedDownloading = -100,
     29   kFailedParsing = ERR_PAC_SCRIPT_FAILED,
     30 };
     31 
     32 class Rules {
     33  public:
     34   struct Rule {
     35     Rule(const GURL& url, int fetch_error, bool is_valid_script)
     36         : url(url),
     37           fetch_error(fetch_error),
     38           is_valid_script(is_valid_script) {
     39     }
     40 
     41     base::string16 text() const {
     42       if (is_valid_script)
     43         return UTF8ToUTF16(url.spec() + "!FindProxyForURL");
     44       if (fetch_error == OK)
     45         return UTF8ToUTF16(url.spec() + "!invalid-script");
     46       return base::string16();
     47     }
     48 
     49     GURL url;
     50     int fetch_error;
     51     bool is_valid_script;
     52   };
     53 
     54   Rule AddSuccessRule(const char* url) {
     55     Rule rule(GURL(url), OK /*fetch_error*/, true);
     56     rules_.push_back(rule);
     57     return rule;
     58   }
     59 
     60   void AddFailDownloadRule(const char* url) {
     61     rules_.push_back(Rule(GURL(url), kFailedDownloading /*fetch_error*/,
     62         false));
     63   }
     64 
     65   void AddFailParsingRule(const char* url) {
     66     rules_.push_back(Rule(GURL(url), OK /*fetch_error*/, false));
     67   }
     68 
     69   const Rule& GetRuleByUrl(const GURL& url) const {
     70     for (RuleList::const_iterator it = rules_.begin(); it != rules_.end();
     71          ++it) {
     72       if (it->url == url)
     73         return *it;
     74     }
     75     LOG(FATAL) << "Rule not found for " << url;
     76     return rules_[0];
     77   }
     78 
     79   const Rule& GetRuleByText(const base::string16& text) const {
     80     for (RuleList::const_iterator it = rules_.begin(); it != rules_.end();
     81          ++it) {
     82       if (it->text() == text)
     83         return *it;
     84     }
     85     LOG(FATAL) << "Rule not found for " << text;
     86     return rules_[0];
     87   }
     88 
     89  private:
     90   typedef std::vector<Rule> RuleList;
     91   RuleList rules_;
     92 };
     93 
     94 class RuleBasedProxyScriptFetcher : public ProxyScriptFetcher {
     95  public:
     96   explicit RuleBasedProxyScriptFetcher(const Rules* rules) : rules_(rules) {}
     97 
     98   // ProxyScriptFetcher implementation.
     99   virtual int Fetch(const GURL& url,
    100                     base::string16* text,
    101                     const CompletionCallback& callback) OVERRIDE {
    102     const Rules::Rule& rule = rules_->GetRuleByUrl(url);
    103     int rv = rule.fetch_error;
    104     EXPECT_NE(ERR_UNEXPECTED, rv);
    105     if (rv == OK)
    106       *text = rule.text();
    107     return rv;
    108   }
    109 
    110   virtual void Cancel() OVERRIDE {}
    111 
    112   virtual URLRequestContext* GetRequestContext() const OVERRIDE { return NULL; }
    113 
    114  private:
    115   const Rules* rules_;
    116 };
    117 
    118 // Succeed using custom PAC script.
    119 TEST(ProxyScriptDeciderTest, CustomPacSucceeds) {
    120   Rules rules;
    121   RuleBasedProxyScriptFetcher fetcher(&rules);
    122   DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
    123 
    124   ProxyConfig config;
    125   config.set_pac_url(GURL("http://custom/proxy.pac"));
    126 
    127   Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
    128 
    129   TestCompletionCallback callback;
    130   CapturingNetLog log;
    131   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, &log);
    132   EXPECT_EQ(OK, decider.Start(
    133       config, base::TimeDelta(), true, callback.callback()));
    134   EXPECT_EQ(rule.text(), decider.script_data()->utf16());
    135 
    136   // Check the NetLog was filled correctly.
    137   CapturingNetLog::CapturedEntryList entries;
    138   log.GetEntries(&entries);
    139 
    140   EXPECT_EQ(4u, entries.size());
    141   EXPECT_TRUE(LogContainsBeginEvent(
    142       entries, 0, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
    143   EXPECT_TRUE(LogContainsBeginEvent(
    144       entries, 1, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    145   EXPECT_TRUE(LogContainsEndEvent(
    146       entries, 2, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    147   EXPECT_TRUE(LogContainsEndEvent(
    148       entries, 3, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
    149 
    150   EXPECT_TRUE(decider.effective_config().has_pac_url());
    151   EXPECT_EQ(config.pac_url(), decider.effective_config().pac_url());
    152 }
    153 
    154 // Fail downloading the custom PAC script.
    155 TEST(ProxyScriptDeciderTest, CustomPacFails1) {
    156   Rules rules;
    157   RuleBasedProxyScriptFetcher fetcher(&rules);
    158   DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
    159 
    160   ProxyConfig config;
    161   config.set_pac_url(GURL("http://custom/proxy.pac"));
    162 
    163   rules.AddFailDownloadRule("http://custom/proxy.pac");
    164 
    165   TestCompletionCallback callback;
    166   CapturingNetLog log;
    167   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, &log);
    168   EXPECT_EQ(kFailedDownloading,
    169             decider.Start(config, base::TimeDelta(), true,
    170                           callback.callback()));
    171   EXPECT_EQ(NULL, decider.script_data());
    172 
    173   // Check the NetLog was filled correctly.
    174   CapturingNetLog::CapturedEntryList entries;
    175   log.GetEntries(&entries);
    176 
    177   EXPECT_EQ(4u, entries.size());
    178   EXPECT_TRUE(LogContainsBeginEvent(
    179       entries, 0, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
    180   EXPECT_TRUE(LogContainsBeginEvent(
    181       entries, 1, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    182   EXPECT_TRUE(LogContainsEndEvent(
    183       entries, 2, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    184   EXPECT_TRUE(LogContainsEndEvent(
    185       entries, 3, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
    186 
    187   EXPECT_FALSE(decider.effective_config().has_pac_url());
    188 }
    189 
    190 // Fail parsing the custom PAC script.
    191 TEST(ProxyScriptDeciderTest, CustomPacFails2) {
    192   Rules rules;
    193   RuleBasedProxyScriptFetcher fetcher(&rules);
    194   DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
    195 
    196   ProxyConfig config;
    197   config.set_pac_url(GURL("http://custom/proxy.pac"));
    198 
    199   rules.AddFailParsingRule("http://custom/proxy.pac");
    200 
    201   TestCompletionCallback callback;
    202   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, NULL);
    203   EXPECT_EQ(kFailedParsing,
    204             decider.Start(config, base::TimeDelta(), true,
    205                           callback.callback()));
    206   EXPECT_EQ(NULL, decider.script_data());
    207 }
    208 
    209 // Fail downloading the custom PAC script, because the fetcher was NULL.
    210 TEST(ProxyScriptDeciderTest, HasNullProxyScriptFetcher) {
    211   Rules rules;
    212   DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
    213 
    214   ProxyConfig config;
    215   config.set_pac_url(GURL("http://custom/proxy.pac"));
    216 
    217   TestCompletionCallback callback;
    218   ProxyScriptDecider decider(NULL, &dhcp_fetcher, NULL);
    219   EXPECT_EQ(ERR_UNEXPECTED,
    220             decider.Start(config, base::TimeDelta(), true,
    221                           callback.callback()));
    222   EXPECT_EQ(NULL, decider.script_data());
    223 }
    224 
    225 // Succeeds in choosing autodetect (WPAD DNS).
    226 TEST(ProxyScriptDeciderTest, AutodetectSuccess) {
    227   Rules rules;
    228   RuleBasedProxyScriptFetcher fetcher(&rules);
    229   DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
    230 
    231   ProxyConfig config;
    232   config.set_auto_detect(true);
    233 
    234   Rules::Rule rule = rules.AddSuccessRule("http://wpad/wpad.dat");
    235 
    236   TestCompletionCallback callback;
    237   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, NULL);
    238   EXPECT_EQ(OK, decider.Start(
    239       config, base::TimeDelta(), true, callback.callback()));
    240   EXPECT_EQ(rule.text(), decider.script_data()->utf16());
    241 
    242   EXPECT_TRUE(decider.effective_config().has_pac_url());
    243   EXPECT_EQ(rule.url, decider.effective_config().pac_url());
    244 }
    245 
    246 // Fails at WPAD (downloading), but succeeds in choosing the custom PAC.
    247 TEST(ProxyScriptDeciderTest, AutodetectFailCustomSuccess1) {
    248   Rules rules;
    249   RuleBasedProxyScriptFetcher fetcher(&rules);
    250   DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
    251 
    252   ProxyConfig config;
    253   config.set_auto_detect(true);
    254   config.set_pac_url(GURL("http://custom/proxy.pac"));
    255 
    256   rules.AddFailDownloadRule("http://wpad/wpad.dat");
    257   Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
    258 
    259   TestCompletionCallback callback;
    260   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, NULL);
    261   EXPECT_EQ(OK, decider.Start(
    262       config, base::TimeDelta(), true, callback.callback()));
    263   EXPECT_EQ(rule.text(), decider.script_data()->utf16());
    264 
    265   EXPECT_TRUE(decider.effective_config().has_pac_url());
    266   EXPECT_EQ(rule.url, decider.effective_config().pac_url());
    267 }
    268 
    269 // Fails at WPAD (no DHCP config, DNS PAC fails parsing), but succeeds in
    270 // choosing the custom PAC.
    271 TEST(ProxyScriptDeciderTest, AutodetectFailCustomSuccess2) {
    272   Rules rules;
    273   RuleBasedProxyScriptFetcher fetcher(&rules);
    274   DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
    275 
    276   ProxyConfig config;
    277   config.set_auto_detect(true);
    278   config.set_pac_url(GURL("http://custom/proxy.pac"));
    279   config.proxy_rules().ParseFromString("unused-manual-proxy:99");
    280 
    281   rules.AddFailParsingRule("http://wpad/wpad.dat");
    282   Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
    283 
    284   TestCompletionCallback callback;
    285   CapturingNetLog log;
    286 
    287   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, &log);
    288   EXPECT_EQ(OK, decider.Start(config, base::TimeDelta(),
    289                           true, callback.callback()));
    290   EXPECT_EQ(rule.text(), decider.script_data()->utf16());
    291 
    292   // Verify that the effective configuration no longer contains auto detect or
    293   // any of the manual settings.
    294   EXPECT_TRUE(decider.effective_config().Equals(
    295       ProxyConfig::CreateFromCustomPacURL(GURL("http://custom/proxy.pac"))));
    296 
    297   // Check the NetLog was filled correctly.
    298   // (Note that various states are repeated since both WPAD and custom
    299   // PAC scripts are tried).
    300   CapturingNetLog::CapturedEntryList entries;
    301   log.GetEntries(&entries);
    302 
    303   EXPECT_EQ(10u, entries.size());
    304   EXPECT_TRUE(LogContainsBeginEvent(
    305       entries, 0, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
    306   // This is the DHCP phase, which fails fetching rather than parsing, so
    307   // there is no pair of SET_PAC_SCRIPT events.
    308   EXPECT_TRUE(LogContainsBeginEvent(
    309       entries, 1, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    310   EXPECT_TRUE(LogContainsEndEvent(
    311       entries, 2, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    312   EXPECT_TRUE(LogContainsEvent(
    313       entries, 3,
    314       NetLog::TYPE_PROXY_SCRIPT_DECIDER_FALLING_BACK_TO_NEXT_PAC_SOURCE,
    315       NetLog::PHASE_NONE));
    316   // This is the DNS phase, which attempts a fetch but fails.
    317   EXPECT_TRUE(LogContainsBeginEvent(
    318       entries, 4, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    319   EXPECT_TRUE(LogContainsEndEvent(
    320       entries, 5, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    321   EXPECT_TRUE(LogContainsEvent(
    322       entries, 6,
    323       NetLog::TYPE_PROXY_SCRIPT_DECIDER_FALLING_BACK_TO_NEXT_PAC_SOURCE,
    324       NetLog::PHASE_NONE));
    325   // Finally, the custom PAC URL phase.
    326   EXPECT_TRUE(LogContainsBeginEvent(
    327       entries, 7, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    328   EXPECT_TRUE(LogContainsEndEvent(
    329       entries, 8, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    330   EXPECT_TRUE(LogContainsEndEvent(
    331       entries, 9, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
    332 }
    333 
    334 // Fails at WPAD (downloading), and fails at custom PAC (downloading).
    335 TEST(ProxyScriptDeciderTest, AutodetectFailCustomFails1) {
    336   Rules rules;
    337   RuleBasedProxyScriptFetcher fetcher(&rules);
    338   DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
    339 
    340   ProxyConfig config;
    341   config.set_auto_detect(true);
    342   config.set_pac_url(GURL("http://custom/proxy.pac"));
    343 
    344   rules.AddFailDownloadRule("http://wpad/wpad.dat");
    345   rules.AddFailDownloadRule("http://custom/proxy.pac");
    346 
    347   TestCompletionCallback callback;
    348   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, NULL);
    349   EXPECT_EQ(kFailedDownloading,
    350             decider.Start(config, base::TimeDelta(), true,
    351                           callback.callback()));
    352   EXPECT_EQ(NULL, decider.script_data());
    353 }
    354 
    355 // Fails at WPAD (downloading), and fails at custom PAC (parsing).
    356 TEST(ProxyScriptDeciderTest, AutodetectFailCustomFails2) {
    357   Rules rules;
    358   RuleBasedProxyScriptFetcher fetcher(&rules);
    359   DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
    360 
    361   ProxyConfig config;
    362   config.set_auto_detect(true);
    363   config.set_pac_url(GURL("http://custom/proxy.pac"));
    364 
    365   rules.AddFailDownloadRule("http://wpad/wpad.dat");
    366   rules.AddFailParsingRule("http://custom/proxy.pac");
    367 
    368   TestCompletionCallback callback;
    369   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, NULL);
    370   EXPECT_EQ(kFailedParsing,
    371             decider.Start(config, base::TimeDelta(), true,
    372                           callback.callback()));
    373   EXPECT_EQ(NULL, decider.script_data());
    374 }
    375 
    376 // This is a copy-paste of CustomPacFails1, with the exception that we give it
    377 // a 1 millisecond delay. This means it will now complete asynchronously.
    378 // Moreover, we test the NetLog to make sure it logged the pause.
    379 TEST(ProxyScriptDeciderTest, CustomPacFails1_WithPositiveDelay) {
    380   Rules rules;
    381   RuleBasedProxyScriptFetcher fetcher(&rules);
    382   DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
    383 
    384   ProxyConfig config;
    385   config.set_pac_url(GURL("http://custom/proxy.pac"));
    386 
    387   rules.AddFailDownloadRule("http://custom/proxy.pac");
    388 
    389   TestCompletionCallback callback;
    390   CapturingNetLog log;
    391   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, &log);
    392   EXPECT_EQ(ERR_IO_PENDING,
    393             decider.Start(config, base::TimeDelta::FromMilliseconds(1),
    394                       true, callback.callback()));
    395 
    396   EXPECT_EQ(kFailedDownloading, callback.WaitForResult());
    397   EXPECT_EQ(NULL, decider.script_data());
    398 
    399   // Check the NetLog was filled correctly.
    400   CapturingNetLog::CapturedEntryList entries;
    401   log.GetEntries(&entries);
    402 
    403   EXPECT_EQ(6u, entries.size());
    404   EXPECT_TRUE(LogContainsBeginEvent(
    405       entries, 0, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
    406   EXPECT_TRUE(LogContainsBeginEvent(
    407       entries, 1, NetLog::TYPE_PROXY_SCRIPT_DECIDER_WAIT));
    408   EXPECT_TRUE(LogContainsEndEvent(
    409       entries, 2, NetLog::TYPE_PROXY_SCRIPT_DECIDER_WAIT));
    410   EXPECT_TRUE(LogContainsBeginEvent(
    411       entries, 3, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    412   EXPECT_TRUE(LogContainsEndEvent(
    413       entries, 4, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    414   EXPECT_TRUE(LogContainsEndEvent(
    415       entries, 5, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
    416 }
    417 
    418 // This is a copy-paste of CustomPacFails1, with the exception that we give it
    419 // a -5 second delay instead of a 0 ms delay. This change should have no effect
    420 // so the rest of the test is unchanged.
    421 TEST(ProxyScriptDeciderTest, CustomPacFails1_WithNegativeDelay) {
    422   Rules rules;
    423   RuleBasedProxyScriptFetcher fetcher(&rules);
    424   DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
    425 
    426   ProxyConfig config;
    427   config.set_pac_url(GURL("http://custom/proxy.pac"));
    428 
    429   rules.AddFailDownloadRule("http://custom/proxy.pac");
    430 
    431   TestCompletionCallback callback;
    432   CapturingNetLog log;
    433   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, &log);
    434   EXPECT_EQ(kFailedDownloading,
    435             decider.Start(config, base::TimeDelta::FromSeconds(-5),
    436                           true, callback.callback()));
    437   EXPECT_EQ(NULL, decider.script_data());
    438 
    439   // Check the NetLog was filled correctly.
    440   CapturingNetLog::CapturedEntryList entries;
    441   log.GetEntries(&entries);
    442 
    443   EXPECT_EQ(4u, entries.size());
    444   EXPECT_TRUE(LogContainsBeginEvent(
    445       entries, 0, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
    446   EXPECT_TRUE(LogContainsBeginEvent(
    447       entries, 1, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    448   EXPECT_TRUE(LogContainsEndEvent(
    449       entries, 2, NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT));
    450   EXPECT_TRUE(LogContainsEndEvent(
    451       entries, 3, NetLog::TYPE_PROXY_SCRIPT_DECIDER));
    452 }
    453 
    454 class SynchronousSuccessDhcpFetcher : public DhcpProxyScriptFetcher {
    455  public:
    456   explicit SynchronousSuccessDhcpFetcher(const base::string16& expected_text)
    457       : gurl_("http://dhcppac/"), expected_text_(expected_text) {
    458   }
    459 
    460   virtual int Fetch(base::string16* utf16_text,
    461                     const CompletionCallback& callback) OVERRIDE {
    462     *utf16_text = expected_text_;
    463     return OK;
    464   }
    465 
    466   virtual void Cancel() OVERRIDE {
    467   }
    468 
    469   virtual const GURL& GetPacURL() const OVERRIDE {
    470     return gurl_;
    471   }
    472 
    473   const base::string16& expected_text() const {
    474     return expected_text_;
    475   }
    476 
    477  private:
    478   GURL gurl_;
    479   base::string16 expected_text_;
    480 
    481   DISALLOW_COPY_AND_ASSIGN(SynchronousSuccessDhcpFetcher);
    482 };
    483 
    484 // All of the tests above that use ProxyScriptDecider have tested
    485 // failure to fetch a PAC file via DHCP configuration, so we now test
    486 // success at downloading and parsing, and then success at downloading,
    487 // failure at parsing.
    488 
    489 TEST(ProxyScriptDeciderTest, AutodetectDhcpSuccess) {
    490   Rules rules;
    491   RuleBasedProxyScriptFetcher fetcher(&rules);
    492   SynchronousSuccessDhcpFetcher dhcp_fetcher(
    493       WideToUTF16(L"http://bingo/!FindProxyForURL"));
    494 
    495   ProxyConfig config;
    496   config.set_auto_detect(true);
    497 
    498   rules.AddSuccessRule("http://bingo/");
    499   rules.AddFailDownloadRule("http://wpad/wpad.dat");
    500 
    501   TestCompletionCallback callback;
    502   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, NULL);
    503   EXPECT_EQ(OK, decider.Start(
    504       config, base::TimeDelta(), true, callback.callback()));
    505   EXPECT_EQ(dhcp_fetcher.expected_text(),
    506             decider.script_data()->utf16());
    507 
    508   EXPECT_TRUE(decider.effective_config().has_pac_url());
    509   EXPECT_EQ(GURL("http://dhcppac/"), decider.effective_config().pac_url());
    510 }
    511 
    512 TEST(ProxyScriptDeciderTest, AutodetectDhcpFailParse) {
    513   Rules rules;
    514   RuleBasedProxyScriptFetcher fetcher(&rules);
    515   SynchronousSuccessDhcpFetcher dhcp_fetcher(
    516       WideToUTF16(L"http://bingo/!invalid-script"));
    517 
    518   ProxyConfig config;
    519   config.set_auto_detect(true);
    520 
    521   rules.AddFailParsingRule("http://bingo/");
    522   rules.AddFailDownloadRule("http://wpad/wpad.dat");
    523 
    524   TestCompletionCallback callback;
    525   ProxyScriptDecider decider(&fetcher, &dhcp_fetcher, NULL);
    526   // Since there is fallback to DNS-based WPAD, the final error will be that
    527   // it failed downloading, not that it failed parsing.
    528   EXPECT_EQ(kFailedDownloading,
    529       decider.Start(config, base::TimeDelta(), true, callback.callback()));
    530   EXPECT_EQ(NULL, decider.script_data());
    531 
    532   EXPECT_FALSE(decider.effective_config().has_pac_url());
    533 }
    534 
    535 class AsyncFailDhcpFetcher
    536     : public DhcpProxyScriptFetcher,
    537       public base::SupportsWeakPtr<AsyncFailDhcpFetcher> {
    538  public:
    539   AsyncFailDhcpFetcher() {}
    540   virtual ~AsyncFailDhcpFetcher() {}
    541 
    542   virtual int Fetch(base::string16* utf16_text,
    543                     const CompletionCallback& callback) OVERRIDE {
    544     callback_ = callback;
    545     base::MessageLoop::current()->PostTask(
    546         FROM_HERE,
    547         base::Bind(&AsyncFailDhcpFetcher::CallbackWithFailure, AsWeakPtr()));
    548     return ERR_IO_PENDING;
    549   }
    550 
    551   virtual void Cancel() OVERRIDE {
    552     callback_.Reset();
    553   }
    554 
    555   virtual const GURL& GetPacURL() const OVERRIDE {
    556     return dummy_gurl_;
    557   }
    558 
    559   void CallbackWithFailure() {
    560     if (!callback_.is_null())
    561       callback_.Run(ERR_PAC_NOT_IN_DHCP);
    562   }
    563 
    564  private:
    565   GURL dummy_gurl_;
    566   CompletionCallback callback_;
    567 };
    568 
    569 TEST(ProxyScriptDeciderTest, DhcpCancelledByDestructor) {
    570   // This regression test would crash before
    571   // http://codereview.chromium.org/7044058/
    572   // Thus, we don't care much about actual results (hence no EXPECT or ASSERT
    573   // macros below), just that it doesn't crash.
    574   Rules rules;
    575   RuleBasedProxyScriptFetcher fetcher(&rules);
    576 
    577   scoped_ptr<AsyncFailDhcpFetcher> dhcp_fetcher(new AsyncFailDhcpFetcher());
    578 
    579   ProxyConfig config;
    580   config.set_auto_detect(true);
    581   rules.AddFailDownloadRule("http://wpad/wpad.dat");
    582 
    583   TestCompletionCallback callback;
    584 
    585   // Scope so ProxyScriptDecider gets destroyed early.
    586   {
    587     ProxyScriptDecider decider(&fetcher, dhcp_fetcher.get(), NULL);
    588     decider.Start(config, base::TimeDelta(), true, callback.callback());
    589   }
    590 
    591   // Run the message loop to let the DHCP fetch complete and post the results
    592   // back. Before the fix linked to above, this would try to invoke on
    593   // the callback object provided by ProxyScriptDecider after it was
    594   // no longer valid.
    595   base::MessageLoop::current()->RunUntilIdle();
    596 }
    597 
    598 }  // namespace
    599 }  // namespace net
    600