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