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