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