1// Copyright (c) 2011 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/string_util.h" 8#include "base/utf_string_conversions.h" 9#include "net/base/net_errors.h" 10#include "net/base/net_log.h" 11#include "net/base/net_log_unittest.h" 12#include "net/base/test_completion_callback.h" 13#include "net/proxy/init_proxy_resolver.h" 14#include "net/proxy/proxy_config.h" 15#include "net/proxy/proxy_resolver.h" 16#include "net/proxy/proxy_script_fetcher.h" 17#include "testing/gtest/include/gtest/gtest.h" 18 19namespace net { 20namespace { 21 22enum Error { 23 kFailedDownloading = -100, 24 kFailedParsing = -200, 25}; 26 27class Rules { 28 public: 29 struct Rule { 30 Rule(const GURL& url, 31 int fetch_error, 32 int set_pac_error) 33 : url(url), 34 fetch_error(fetch_error), 35 set_pac_error(set_pac_error) { 36 } 37 38 string16 text() const { 39 if (set_pac_error == OK) 40 return UTF8ToUTF16(url.spec() + "!valid-script"); 41 if (fetch_error == OK) 42 return UTF8ToUTF16(url.spec() + "!invalid-script"); 43 return string16(); 44 } 45 46 GURL url; 47 int fetch_error; 48 int set_pac_error; 49 }; 50 51 Rule AddSuccessRule(const char* url) { 52 Rule rule(GURL(url), OK /*fetch_error*/, OK /*set_pac_error*/); 53 rules_.push_back(rule); 54 return rule; 55 } 56 57 void AddFailDownloadRule(const char* url) { 58 rules_.push_back(Rule(GURL(url), kFailedDownloading /*fetch_error*/, 59 ERR_UNEXPECTED /*set_pac_error*/)); 60 } 61 62 void AddFailParsingRule(const char* url) { 63 rules_.push_back(Rule(GURL(url), OK /*fetch_error*/, 64 kFailedParsing /*set_pac_error*/)); 65 } 66 67 const Rule& GetRuleByUrl(const GURL& url) const { 68 for (RuleList::const_iterator it = rules_.begin(); it != rules_.end(); 69 ++it) { 70 if (it->url == url) 71 return *it; 72 } 73 LOG(FATAL) << "Rule not found for " << url; 74 return rules_[0]; 75 } 76 77 const Rule& GetRuleByText(const string16& text) const { 78 for (RuleList::const_iterator it = rules_.begin(); it != rules_.end(); 79 ++it) { 80 if (it->text() == text) 81 return *it; 82 } 83 LOG(FATAL) << "Rule not found for " << text; 84 return rules_[0]; 85 } 86 87 private: 88 typedef std::vector<Rule> RuleList; 89 RuleList rules_; 90}; 91 92class RuleBasedProxyScriptFetcher : public ProxyScriptFetcher { 93 public: 94 explicit RuleBasedProxyScriptFetcher(const Rules* rules) : rules_(rules) {} 95 96 // ProxyScriptFetcher implementation. 97 virtual int Fetch(const GURL& url, 98 string16* text, 99 CompletionCallback* callback) { 100 const Rules::Rule& rule = rules_->GetRuleByUrl(url); 101 int rv = rule.fetch_error; 102 EXPECT_NE(ERR_UNEXPECTED, rv); 103 if (rv == OK) 104 *text = rule.text(); 105 return rv; 106 } 107 108 virtual void Cancel() {} 109 110 virtual URLRequestContext* GetRequestContext() { return NULL; } 111 112 private: 113 const Rules* rules_; 114}; 115 116class RuleBasedProxyResolver : public ProxyResolver { 117 public: 118 RuleBasedProxyResolver(const Rules* rules, bool expects_pac_bytes) 119 : ProxyResolver(expects_pac_bytes), rules_(rules) {} 120 121 // ProxyResolver implementation: 122 virtual int GetProxyForURL(const GURL& /*url*/, 123 ProxyInfo* /*results*/, 124 CompletionCallback* /*callback*/, 125 RequestHandle* /*request_handle*/, 126 const BoundNetLog& /*net_log*/) { 127 NOTREACHED(); 128 return ERR_UNEXPECTED; 129 } 130 131 virtual void CancelRequest(RequestHandle request_handle) { 132 NOTREACHED(); 133 } 134 135 virtual void CancelSetPacScript() { 136 NOTREACHED(); 137 } 138 139 virtual int SetPacScript( 140 const scoped_refptr<ProxyResolverScriptData>& script_data, 141 CompletionCallback* callback) { 142 143 const GURL url = 144 script_data->type() == ProxyResolverScriptData::TYPE_SCRIPT_URL ? 145 script_data->url() : GURL(); 146 147 const Rules::Rule& rule = expects_pac_bytes() ? 148 rules_->GetRuleByText(script_data->utf16()) : 149 rules_->GetRuleByUrl(url); 150 151 int rv = rule.set_pac_error; 152 EXPECT_NE(ERR_UNEXPECTED, rv); 153 154 if (expects_pac_bytes()) { 155 EXPECT_EQ(rule.text(), script_data->utf16()); 156 } else { 157 EXPECT_EQ(rule.url, url); 158 } 159 160 if (rv == OK) 161 script_data_ = script_data; 162 return rv; 163 } 164 165 const ProxyResolverScriptData* script_data() const { return script_data_; } 166 167 private: 168 const Rules* rules_; 169 scoped_refptr<ProxyResolverScriptData> script_data_; 170}; 171 172// Succeed using custom PAC script. 173TEST(InitProxyResolverTest, CustomPacSucceeds) { 174 Rules rules; 175 RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/); 176 RuleBasedProxyScriptFetcher fetcher(&rules); 177 178 ProxyConfig config; 179 config.set_pac_url(GURL("http://custom/proxy.pac")); 180 181 Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac"); 182 183 TestCompletionCallback callback; 184 CapturingNetLog log(CapturingNetLog::kUnbounded); 185 InitProxyResolver init(&resolver, &fetcher, &log); 186 EXPECT_EQ(OK, init.Init(config, base::TimeDelta(), NULL, &callback)); 187 EXPECT_EQ(rule.text(), resolver.script_data()->utf16()); 188 189 // Check the NetLog was filled correctly. 190 CapturingNetLog::EntryList entries; 191 log.GetEntries(&entries); 192 193 EXPECT_EQ(6u, entries.size()); 194 EXPECT_TRUE(LogContainsBeginEvent( 195 entries, 0, NetLog::TYPE_INIT_PROXY_RESOLVER)); 196 EXPECT_TRUE(LogContainsBeginEvent( 197 entries, 1, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT)); 198 EXPECT_TRUE(LogContainsEndEvent( 199 entries, 2, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT)); 200 EXPECT_TRUE(LogContainsBeginEvent( 201 entries, 3, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT)); 202 EXPECT_TRUE(LogContainsEndEvent( 203 entries, 4, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT)); 204 EXPECT_TRUE(LogContainsEndEvent( 205 entries, 5, NetLog::TYPE_INIT_PROXY_RESOLVER)); 206} 207 208// Fail downloading the custom PAC script. 209TEST(InitProxyResolverTest, CustomPacFails1) { 210 Rules rules; 211 RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/); 212 RuleBasedProxyScriptFetcher fetcher(&rules); 213 214 ProxyConfig config; 215 config.set_pac_url(GURL("http://custom/proxy.pac")); 216 217 rules.AddFailDownloadRule("http://custom/proxy.pac"); 218 219 TestCompletionCallback callback; 220 CapturingNetLog log(CapturingNetLog::kUnbounded); 221 InitProxyResolver init(&resolver, &fetcher, &log); 222 EXPECT_EQ(kFailedDownloading, 223 init.Init(config, base::TimeDelta(), NULL, &callback)); 224 EXPECT_EQ(NULL, resolver.script_data()); 225 226 // Check the NetLog was filled correctly. 227 CapturingNetLog::EntryList entries; 228 log.GetEntries(&entries); 229 230 EXPECT_EQ(4u, entries.size()); 231 EXPECT_TRUE(LogContainsBeginEvent( 232 entries, 0, NetLog::TYPE_INIT_PROXY_RESOLVER)); 233 EXPECT_TRUE(LogContainsBeginEvent( 234 entries, 1, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT)); 235 EXPECT_TRUE(LogContainsEndEvent( 236 entries, 2, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT)); 237 EXPECT_TRUE(LogContainsEndEvent( 238 entries, 3, NetLog::TYPE_INIT_PROXY_RESOLVER)); 239} 240 241// Fail parsing the custom PAC script. 242TEST(InitProxyResolverTest, CustomPacFails2) { 243 Rules rules; 244 RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/); 245 RuleBasedProxyScriptFetcher fetcher(&rules); 246 247 ProxyConfig config; 248 config.set_pac_url(GURL("http://custom/proxy.pac")); 249 250 rules.AddFailParsingRule("http://custom/proxy.pac"); 251 252 TestCompletionCallback callback; 253 InitProxyResolver init(&resolver, &fetcher, NULL); 254 EXPECT_EQ(kFailedParsing, 255 init.Init(config, base::TimeDelta(), NULL, &callback)); 256 EXPECT_EQ(NULL, resolver.script_data()); 257} 258 259// Fail downloading the custom PAC script, because the fetcher was NULL. 260TEST(InitProxyResolverTest, HasNullProxyScriptFetcher) { 261 Rules rules; 262 RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/); 263 264 ProxyConfig config; 265 config.set_pac_url(GURL("http://custom/proxy.pac")); 266 267 TestCompletionCallback callback; 268 InitProxyResolver init(&resolver, NULL, NULL); 269 EXPECT_EQ(ERR_UNEXPECTED, 270 init.Init(config, base::TimeDelta(), NULL, &callback)); 271 EXPECT_EQ(NULL, resolver.script_data()); 272} 273 274// Succeeds in choosing autodetect (wpad). 275TEST(InitProxyResolverTest, AutodetectSuccess) { 276 Rules rules; 277 RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/); 278 RuleBasedProxyScriptFetcher fetcher(&rules); 279 280 ProxyConfig config; 281 config.set_auto_detect(true); 282 283 Rules::Rule rule = rules.AddSuccessRule("http://wpad/wpad.dat"); 284 285 TestCompletionCallback callback; 286 InitProxyResolver init(&resolver, &fetcher, NULL); 287 EXPECT_EQ(OK, init.Init(config, base::TimeDelta(), NULL, &callback)); 288 EXPECT_EQ(rule.text(), resolver.script_data()->utf16()); 289} 290 291// Fails at WPAD (downloading), but succeeds in choosing the custom PAC. 292TEST(InitProxyResolverTest, AutodetectFailCustomSuccess1) { 293 Rules rules; 294 RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/); 295 RuleBasedProxyScriptFetcher fetcher(&rules); 296 297 ProxyConfig config; 298 config.set_auto_detect(true); 299 config.set_pac_url(GURL("http://custom/proxy.pac")); 300 301 rules.AddFailDownloadRule("http://wpad/wpad.dat"); 302 Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac"); 303 304 TestCompletionCallback callback; 305 InitProxyResolver init(&resolver, &fetcher, NULL); 306 EXPECT_EQ(OK, init.Init(config, base::TimeDelta(), NULL, &callback)); 307 EXPECT_EQ(rule.text(), resolver.script_data()->utf16()); 308} 309 310// Fails at WPAD (parsing), but succeeds in choosing the custom PAC. 311TEST(InitProxyResolverTest, AutodetectFailCustomSuccess2) { 312 Rules rules; 313 RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/); 314 RuleBasedProxyScriptFetcher fetcher(&rules); 315 316 ProxyConfig config; 317 config.set_auto_detect(true); 318 config.set_pac_url(GURL("http://custom/proxy.pac")); 319 config.proxy_rules().ParseFromString("unused-manual-proxy:99"); 320 321 rules.AddFailParsingRule("http://wpad/wpad.dat"); 322 Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac"); 323 324 TestCompletionCallback callback; 325 CapturingNetLog log(CapturingNetLog::kUnbounded); 326 327 ProxyConfig effective_config; 328 InitProxyResolver init(&resolver, &fetcher, &log); 329 EXPECT_EQ(OK, init.Init(config, base::TimeDelta(), 330 &effective_config, &callback)); 331 EXPECT_EQ(rule.text(), resolver.script_data()->utf16()); 332 333 // Verify that the effective configuration no longer contains auto detect or 334 // any of the manual settings. 335 EXPECT_TRUE(effective_config.Equals( 336 ProxyConfig::CreateFromCustomPacURL(GURL("http://custom/proxy.pac")))); 337 338 // Check the NetLog was filled correctly. 339 // (Note that the Fetch and Set states are repeated since both WPAD and custom 340 // PAC scripts are tried). 341 CapturingNetLog::EntryList entries; 342 log.GetEntries(&entries); 343 344 EXPECT_EQ(11u, entries.size()); 345 EXPECT_TRUE(LogContainsBeginEvent( 346 entries, 0, NetLog::TYPE_INIT_PROXY_RESOLVER)); 347 EXPECT_TRUE(LogContainsBeginEvent( 348 entries, 1, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT)); 349 EXPECT_TRUE(LogContainsEndEvent( 350 entries, 2, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT)); 351 EXPECT_TRUE(LogContainsBeginEvent( 352 entries, 3, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT)); 353 EXPECT_TRUE(LogContainsEndEvent( 354 entries, 4, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT)); 355 EXPECT_TRUE(LogContainsEvent( 356 entries, 5, 357 NetLog::TYPE_INIT_PROXY_RESOLVER_FALLING_BACK_TO_NEXT_PAC_URL, 358 NetLog::PHASE_NONE)); 359 EXPECT_TRUE(LogContainsBeginEvent( 360 entries, 6, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT)); 361 EXPECT_TRUE(LogContainsEndEvent( 362 entries, 7, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT)); 363 EXPECT_TRUE(LogContainsBeginEvent( 364 entries, 8, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT)); 365 EXPECT_TRUE(LogContainsEndEvent( 366 entries, 9, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT)); 367 EXPECT_TRUE(LogContainsEndEvent( 368 entries, 10, NetLog::TYPE_INIT_PROXY_RESOLVER)); 369} 370 371// Fails at WPAD (downloading), and fails at custom PAC (downloading). 372TEST(InitProxyResolverTest, AutodetectFailCustomFails1) { 373 Rules rules; 374 RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/); 375 RuleBasedProxyScriptFetcher fetcher(&rules); 376 377 ProxyConfig config; 378 config.set_auto_detect(true); 379 config.set_pac_url(GURL("http://custom/proxy.pac")); 380 381 rules.AddFailDownloadRule("http://wpad/wpad.dat"); 382 rules.AddFailDownloadRule("http://custom/proxy.pac"); 383 384 TestCompletionCallback callback; 385 InitProxyResolver init(&resolver, &fetcher, NULL); 386 EXPECT_EQ(kFailedDownloading, 387 init.Init(config, base::TimeDelta(), NULL, &callback)); 388 EXPECT_EQ(NULL, resolver.script_data()); 389} 390 391// Fails at WPAD (downloading), and fails at custom PAC (parsing). 392TEST(InitProxyResolverTest, AutodetectFailCustomFails2) { 393 Rules rules; 394 RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/); 395 RuleBasedProxyScriptFetcher fetcher(&rules); 396 397 ProxyConfig config; 398 config.set_auto_detect(true); 399 config.set_pac_url(GURL("http://custom/proxy.pac")); 400 401 rules.AddFailDownloadRule("http://wpad/wpad.dat"); 402 rules.AddFailParsingRule("http://custom/proxy.pac"); 403 404 TestCompletionCallback callback; 405 InitProxyResolver init(&resolver, &fetcher, NULL); 406 EXPECT_EQ(kFailedParsing, 407 init.Init(config, base::TimeDelta(), NULL, &callback)); 408 EXPECT_EQ(NULL, resolver.script_data()); 409} 410 411// Fails at WPAD (parsing), but succeeds in choosing the custom PAC. 412// This is the same as AutodetectFailCustomSuccess2, but using a ProxyResolver 413// that doesn't |expects_pac_bytes|. 414TEST(InitProxyResolverTest, AutodetectFailCustomSuccess2_NoFetch) { 415 Rules rules; 416 RuleBasedProxyResolver resolver(&rules, false /*expects_pac_bytes*/); 417 RuleBasedProxyScriptFetcher fetcher(&rules); 418 419 ProxyConfig config; 420 config.set_auto_detect(true); 421 config.set_pac_url(GURL("http://custom/proxy.pac")); 422 423 rules.AddFailParsingRule(""); // Autodetect. 424 Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac"); 425 426 TestCompletionCallback callback; 427 InitProxyResolver init(&resolver, &fetcher, NULL); 428 EXPECT_EQ(OK, init.Init(config, base::TimeDelta(), NULL, &callback)); 429 EXPECT_EQ(rule.url, resolver.script_data()->url()); 430} 431 432// This is a copy-paste of CustomPacFails1, with the exception that we give it 433// a 1 millisecond delay. This means it will now complete asynchronously. 434// Moreover, we test the NetLog to make sure it logged the pause. 435TEST(InitProxyResolverTest, CustomPacFails1_WithPositiveDelay) { 436 Rules rules; 437 RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/); 438 RuleBasedProxyScriptFetcher fetcher(&rules); 439 440 ProxyConfig config; 441 config.set_pac_url(GURL("http://custom/proxy.pac")); 442 443 rules.AddFailDownloadRule("http://custom/proxy.pac"); 444 445 TestCompletionCallback callback; 446 CapturingNetLog log(CapturingNetLog::kUnbounded); 447 InitProxyResolver init(&resolver, &fetcher, &log); 448 EXPECT_EQ(ERR_IO_PENDING, 449 init.Init(config, base::TimeDelta::FromMilliseconds(1), 450 NULL, &callback)); 451 452 EXPECT_EQ(kFailedDownloading, callback.WaitForResult()); 453 EXPECT_EQ(NULL, resolver.script_data()); 454 455 // Check the NetLog was filled correctly. 456 CapturingNetLog::EntryList entries; 457 log.GetEntries(&entries); 458 459 EXPECT_EQ(6u, entries.size()); 460 EXPECT_TRUE(LogContainsBeginEvent( 461 entries, 0, NetLog::TYPE_INIT_PROXY_RESOLVER)); 462 EXPECT_TRUE(LogContainsBeginEvent( 463 entries, 1, NetLog::TYPE_INIT_PROXY_RESOLVER_WAIT)); 464 EXPECT_TRUE(LogContainsEndEvent( 465 entries, 2, NetLog::TYPE_INIT_PROXY_RESOLVER_WAIT)); 466 EXPECT_TRUE(LogContainsBeginEvent( 467 entries, 3, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT)); 468 EXPECT_TRUE(LogContainsEndEvent( 469 entries, 4, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT)); 470 EXPECT_TRUE(LogContainsEndEvent( 471 entries, 5, NetLog::TYPE_INIT_PROXY_RESOLVER)); 472} 473 474// This is a copy-paste of CustomPacFails1, with the exception that we give it 475// a -5 second delay instead of a 0 ms delay. This change should have no effect 476// so the rest of the test is unchanged. 477TEST(InitProxyResolverTest, CustomPacFails1_WithNegativeDelay) { 478 Rules rules; 479 RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/); 480 RuleBasedProxyScriptFetcher fetcher(&rules); 481 482 ProxyConfig config; 483 config.set_pac_url(GURL("http://custom/proxy.pac")); 484 485 rules.AddFailDownloadRule("http://custom/proxy.pac"); 486 487 TestCompletionCallback callback; 488 CapturingNetLog log(CapturingNetLog::kUnbounded); 489 InitProxyResolver init(&resolver, &fetcher, &log); 490 EXPECT_EQ(kFailedDownloading, 491 init.Init(config, base::TimeDelta::FromSeconds(-5), 492 NULL, &callback)); 493 EXPECT_EQ(NULL, resolver.script_data()); 494 495 // Check the NetLog was filled correctly. 496 CapturingNetLog::EntryList entries; 497 log.GetEntries(&entries); 498 499 EXPECT_EQ(4u, entries.size()); 500 EXPECT_TRUE(LogContainsBeginEvent( 501 entries, 0, NetLog::TYPE_INIT_PROXY_RESOLVER)); 502 EXPECT_TRUE(LogContainsBeginEvent( 503 entries, 1, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT)); 504 EXPECT_TRUE(LogContainsEndEvent( 505 entries, 2, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT)); 506 EXPECT_TRUE(LogContainsEndEvent( 507 entries, 3, NetLog::TYPE_INIT_PROXY_RESOLVER)); 508} 509 510} // namespace 511} // namespace net 512