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 "chrome/browser/net/pref_proxy_config_service.h" 6 7 #include "base/command_line.h" 8 #include "base/file_path.h" 9 #include "chrome/browser/net/chrome_url_request_context.h" 10 #include "chrome/browser/prefs/pref_service_mock_builder.h" 11 #include "chrome/browser/prefs/proxy_config_dictionary.h" 12 #include "chrome/common/chrome_switches.h" 13 #include "chrome/common/pref_names.h" 14 #include "chrome/test/testing_pref_service.h" 15 #include "net/proxy/proxy_config_service_common_unittest.h" 16 #include "testing/gmock/include/gmock/gmock.h" 17 #include "testing/gtest/include/gtest/gtest.h" 18 19 using testing::_; 20 using testing::Mock; 21 22 namespace { 23 24 const char kFixedPacUrl[] = "http://chromium.org/fixed_pac_url"; 25 26 // Testing proxy config service that allows us to fire notifications at will. 27 class TestProxyConfigService : public net::ProxyConfigService { 28 public: 29 TestProxyConfigService(const net::ProxyConfig& config, 30 ConfigAvailability availability) 31 : config_(config), 32 availability_(availability) {} 33 34 void SetProxyConfig(const net::ProxyConfig config, 35 ConfigAvailability availability) { 36 config_ = config; 37 availability_ = availability; 38 FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_, 39 OnProxyConfigChanged(config, availability)); 40 } 41 42 private: 43 virtual void AddObserver(net::ProxyConfigService::Observer* observer) { 44 observers_.AddObserver(observer); 45 } 46 47 virtual void RemoveObserver(net::ProxyConfigService::Observer* observer) { 48 observers_.RemoveObserver(observer); 49 } 50 51 virtual net::ProxyConfigService::ConfigAvailability GetLatestProxyConfig( 52 net::ProxyConfig* config) { 53 *config = config_; 54 return availability_; 55 } 56 57 net::ProxyConfig config_; 58 ConfigAvailability availability_; 59 ObserverList<net::ProxyConfigService::Observer, true> observers_; 60 }; 61 62 // A mock observer for capturing callbacks. 63 class MockObserver : public net::ProxyConfigService::Observer { 64 public: 65 MOCK_METHOD2(OnProxyConfigChanged, 66 void(const net::ProxyConfig&, 67 net::ProxyConfigService::ConfigAvailability)); 68 }; 69 70 template<typename TESTBASE> 71 class PrefProxyConfigServiceTestBase : public TESTBASE { 72 protected: 73 PrefProxyConfigServiceTestBase() 74 : ui_thread_(BrowserThread::UI, &loop_), 75 io_thread_(BrowserThread::IO, &loop_) {} 76 77 virtual void Init(PrefService* pref_service) { 78 ASSERT_TRUE(pref_service); 79 PrefProxyConfigService::RegisterPrefs(pref_service); 80 fixed_config_.set_pac_url(GURL(kFixedPacUrl)); 81 delegate_service_ = 82 new TestProxyConfigService(fixed_config_, 83 net::ProxyConfigService::CONFIG_VALID); 84 proxy_config_tracker_ = new PrefProxyConfigTracker(pref_service); 85 proxy_config_service_.reset( 86 new PrefProxyConfigService(proxy_config_tracker_.get(), 87 delegate_service_)); 88 } 89 90 virtual void TearDown() { 91 proxy_config_tracker_->DetachFromPrefService(); 92 loop_.RunAllPending(); 93 proxy_config_service_.reset(); 94 } 95 96 MessageLoop loop_; 97 TestProxyConfigService* delegate_service_; // weak 98 scoped_ptr<PrefProxyConfigService> proxy_config_service_; 99 net::ProxyConfig fixed_config_; 100 101 private: 102 scoped_refptr<PrefProxyConfigTracker> proxy_config_tracker_; 103 BrowserThread ui_thread_; 104 BrowserThread io_thread_; 105 }; 106 107 class PrefProxyConfigServiceTest 108 : public PrefProxyConfigServiceTestBase<testing::Test> { 109 protected: 110 virtual void SetUp() { 111 pref_service_.reset(new TestingPrefService()); 112 Init(pref_service_.get()); 113 } 114 115 scoped_ptr<TestingPrefService> pref_service_; 116 }; 117 118 TEST_F(PrefProxyConfigServiceTest, BaseConfiguration) { 119 net::ProxyConfig actual_config; 120 EXPECT_EQ(net::ProxyConfigService::CONFIG_VALID, 121 proxy_config_service_->GetLatestProxyConfig(&actual_config)); 122 EXPECT_EQ(GURL(kFixedPacUrl), actual_config.pac_url()); 123 } 124 125 TEST_F(PrefProxyConfigServiceTest, DynamicPrefOverrides) { 126 pref_service_->SetManagedPref( 127 prefs::kProxy, 128 ProxyConfigDictionary::CreateFixedServers("http://example.com:3128", "")); 129 loop_.RunAllPending(); 130 131 net::ProxyConfig actual_config; 132 EXPECT_EQ(net::ProxyConfigService::CONFIG_VALID, 133 proxy_config_service_->GetLatestProxyConfig(&actual_config)); 134 EXPECT_FALSE(actual_config.auto_detect()); 135 EXPECT_EQ(net::ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY, 136 actual_config.proxy_rules().type); 137 EXPECT_EQ(actual_config.proxy_rules().single_proxy, 138 net::ProxyServer::FromURI("http://example.com:3128", 139 net::ProxyServer::SCHEME_HTTP)); 140 141 pref_service_->SetManagedPref(prefs::kProxy, 142 ProxyConfigDictionary::CreateAutoDetect()); 143 loop_.RunAllPending(); 144 145 EXPECT_EQ(net::ProxyConfigService::CONFIG_VALID, 146 proxy_config_service_->GetLatestProxyConfig(&actual_config)); 147 EXPECT_TRUE(actual_config.auto_detect()); 148 } 149 150 // Compares proxy configurations, but allows different identifiers. 151 MATCHER_P(ProxyConfigMatches, config, "") { 152 net::ProxyConfig reference(config); 153 reference.set_id(arg.id()); 154 return reference.Equals(arg); 155 } 156 157 TEST_F(PrefProxyConfigServiceTest, Observers) { 158 const net::ProxyConfigService::ConfigAvailability CONFIG_VALID = 159 net::ProxyConfigService::CONFIG_VALID; 160 MockObserver observer; 161 proxy_config_service_->AddObserver(&observer); 162 163 // Firing the observers in the delegate should trigger a notification. 164 net::ProxyConfig config2; 165 config2.set_auto_detect(true); 166 EXPECT_CALL(observer, OnProxyConfigChanged(ProxyConfigMatches(config2), 167 CONFIG_VALID)).Times(1); 168 delegate_service_->SetProxyConfig(config2, CONFIG_VALID); 169 loop_.RunAllPending(); 170 Mock::VerifyAndClearExpectations(&observer); 171 172 // Override configuration, this should trigger a notification. 173 net::ProxyConfig pref_config; 174 pref_config.set_pac_url(GURL(kFixedPacUrl)); 175 176 EXPECT_CALL(observer, OnProxyConfigChanged(ProxyConfigMatches(pref_config), 177 CONFIG_VALID)).Times(1); 178 pref_service_->SetManagedPref( 179 prefs::kProxy, 180 ProxyConfigDictionary::CreatePacScript(kFixedPacUrl)); 181 loop_.RunAllPending(); 182 Mock::VerifyAndClearExpectations(&observer); 183 184 // Since there are pref overrides, delegate changes should be ignored. 185 net::ProxyConfig config3; 186 config3.proxy_rules().ParseFromString("http=config3:80"); 187 EXPECT_CALL(observer, OnProxyConfigChanged(_, _)).Times(0); 188 fixed_config_.set_auto_detect(true); 189 delegate_service_->SetProxyConfig(config3, CONFIG_VALID); 190 loop_.RunAllPending(); 191 Mock::VerifyAndClearExpectations(&observer); 192 193 // Clear the override should switch back to the fixed configuration. 194 EXPECT_CALL(observer, OnProxyConfigChanged(ProxyConfigMatches(config3), 195 CONFIG_VALID)).Times(1); 196 pref_service_->RemoveManagedPref(prefs::kProxy); 197 loop_.RunAllPending(); 198 Mock::VerifyAndClearExpectations(&observer); 199 200 // Delegate service notifications should show up again. 201 net::ProxyConfig config4; 202 config4.proxy_rules().ParseFromString("socks:config4"); 203 EXPECT_CALL(observer, OnProxyConfigChanged(ProxyConfigMatches(config4), 204 CONFIG_VALID)).Times(1); 205 delegate_service_->SetProxyConfig(config4, CONFIG_VALID); 206 loop_.RunAllPending(); 207 Mock::VerifyAndClearExpectations(&observer); 208 209 proxy_config_service_->RemoveObserver(&observer); 210 } 211 212 TEST_F(PrefProxyConfigServiceTest, Fallback) { 213 const net::ProxyConfigService::ConfigAvailability CONFIG_VALID = 214 net::ProxyConfigService::CONFIG_VALID; 215 MockObserver observer; 216 net::ProxyConfig actual_config; 217 delegate_service_->SetProxyConfig(net::ProxyConfig::CreateDirect(), 218 net::ProxyConfigService::CONFIG_UNSET); 219 proxy_config_service_->AddObserver(&observer); 220 221 // Prepare test data. 222 net::ProxyConfig recommended_config = net::ProxyConfig::CreateAutoDetect(); 223 net::ProxyConfig user_config = 224 net::ProxyConfig::CreateFromCustomPacURL(GURL(kFixedPacUrl)); 225 226 // Set a recommended pref. 227 EXPECT_CALL(observer, 228 OnProxyConfigChanged(ProxyConfigMatches(recommended_config), 229 CONFIG_VALID)).Times(1); 230 pref_service_->SetRecommendedPref( 231 prefs::kProxy, 232 ProxyConfigDictionary::CreateAutoDetect()); 233 loop_.RunAllPending(); 234 Mock::VerifyAndClearExpectations(&observer); 235 EXPECT_EQ(CONFIG_VALID, 236 proxy_config_service_->GetLatestProxyConfig(&actual_config)); 237 EXPECT_TRUE(actual_config.Equals(recommended_config)); 238 239 // Override in user prefs. 240 EXPECT_CALL(observer, 241 OnProxyConfigChanged(ProxyConfigMatches(user_config), 242 CONFIG_VALID)).Times(1); 243 pref_service_->SetManagedPref( 244 prefs::kProxy, 245 ProxyConfigDictionary::CreatePacScript(kFixedPacUrl)); 246 loop_.RunAllPending(); 247 Mock::VerifyAndClearExpectations(&observer); 248 EXPECT_EQ(CONFIG_VALID, 249 proxy_config_service_->GetLatestProxyConfig(&actual_config)); 250 EXPECT_TRUE(actual_config.Equals(user_config)); 251 252 // Go back to recommended pref. 253 EXPECT_CALL(observer, 254 OnProxyConfigChanged(ProxyConfigMatches(recommended_config), 255 CONFIG_VALID)).Times(1); 256 pref_service_->RemoveManagedPref(prefs::kProxy); 257 loop_.RunAllPending(); 258 Mock::VerifyAndClearExpectations(&observer); 259 EXPECT_EQ(CONFIG_VALID, 260 proxy_config_service_->GetLatestProxyConfig(&actual_config)); 261 EXPECT_TRUE(actual_config.Equals(recommended_config)); 262 263 proxy_config_service_->RemoveObserver(&observer); 264 } 265 266 // Test parameter object for testing command line proxy configuration. 267 struct CommandLineTestParams { 268 // Explicit assignment operator, so testing::TestWithParam works with MSVC. 269 CommandLineTestParams& operator=(const CommandLineTestParams& other) { 270 description = other.description; 271 for (unsigned int i = 0; i < arraysize(switches); i++) 272 switches[i] = other.switches[i]; 273 is_null = other.is_null; 274 auto_detect = other.auto_detect; 275 pac_url = other.pac_url; 276 proxy_rules = other.proxy_rules; 277 return *this; 278 } 279 280 // Short description to identify the test. 281 const char* description; 282 283 // The command line to build a ProxyConfig from. 284 struct SwitchValue { 285 const char* name; 286 const char* value; 287 } switches[2]; 288 289 // Expected outputs (fields of the ProxyConfig). 290 bool is_null; 291 bool auto_detect; 292 GURL pac_url; 293 net::ProxyRulesExpectation proxy_rules; 294 }; 295 296 void PrintTo(const CommandLineTestParams& params, std::ostream* os) { 297 *os << params.description; 298 } 299 300 class PrefProxyConfigServiceCommandLineTest 301 : public PrefProxyConfigServiceTestBase< 302 testing::TestWithParam<CommandLineTestParams> > { 303 protected: 304 PrefProxyConfigServiceCommandLineTest() 305 : command_line_(CommandLine::NO_PROGRAM) {} 306 307 virtual void SetUp() { 308 for (size_t i = 0; i < arraysize(GetParam().switches); i++) { 309 const char* name = GetParam().switches[i].name; 310 const char* value = GetParam().switches[i].value; 311 if (name && value) 312 command_line_.AppendSwitchASCII(name, value); 313 else if (name) 314 command_line_.AppendSwitch(name); 315 } 316 pref_service_.reset( 317 PrefServiceMockBuilder().WithCommandLine(&command_line_).Create()); 318 Init(pref_service_.get()); 319 } 320 321 private: 322 CommandLine command_line_; 323 scoped_ptr<PrefService> pref_service_; 324 }; 325 326 TEST_P(PrefProxyConfigServiceCommandLineTest, CommandLine) { 327 net::ProxyConfig config; 328 EXPECT_EQ(net::ProxyConfigService::CONFIG_VALID, 329 proxy_config_service_->GetLatestProxyConfig(&config)); 330 331 if (GetParam().is_null) { 332 EXPECT_EQ(GURL(kFixedPacUrl), config.pac_url()); 333 } else { 334 EXPECT_NE(GURL(kFixedPacUrl), config.pac_url()); 335 EXPECT_EQ(GetParam().auto_detect, config.auto_detect()); 336 EXPECT_EQ(GetParam().pac_url, config.pac_url()); 337 EXPECT_TRUE(GetParam().proxy_rules.Matches(config.proxy_rules())); 338 } 339 } 340 341 static const CommandLineTestParams kCommandLineTestParams[] = { 342 { 343 "Empty command line", 344 // Input 345 { }, 346 // Expected result 347 true, // is_null 348 false, // auto_detect 349 GURL(), // pac_url 350 net::ProxyRulesExpectation::Empty(), 351 }, 352 { 353 "No proxy", 354 // Input 355 { 356 { switches::kNoProxyServer, NULL }, 357 }, 358 // Expected result 359 false, // is_null 360 false, // auto_detect 361 GURL(), // pac_url 362 net::ProxyRulesExpectation::Empty(), 363 }, 364 { 365 "No proxy with extra parameters.", 366 // Input 367 { 368 { switches::kNoProxyServer, NULL }, 369 { switches::kProxyServer, "http://proxy:8888" }, 370 }, 371 // Expected result 372 false, // is_null 373 false, // auto_detect 374 GURL(), // pac_url 375 net::ProxyRulesExpectation::Empty(), 376 }, 377 { 378 "Single proxy.", 379 // Input 380 { 381 { switches::kProxyServer, "http://proxy:8888" }, 382 }, 383 // Expected result 384 false, // is_null 385 false, // auto_detect 386 GURL(), // pac_url 387 net::ProxyRulesExpectation::Single( 388 "proxy:8888", // single proxy 389 ""), // bypass rules 390 }, 391 { 392 "Per scheme proxy.", 393 // Input 394 { 395 { switches::kProxyServer, "http=httpproxy:8888;ftp=ftpproxy:8889" }, 396 }, 397 // Expected result 398 false, // is_null 399 false, // auto_detect 400 GURL(), // pac_url 401 net::ProxyRulesExpectation::PerScheme( 402 "httpproxy:8888", // http 403 "", // https 404 "ftpproxy:8889", // ftp 405 ""), // bypass rules 406 }, 407 { 408 "Per scheme proxy with bypass URLs.", 409 // Input 410 { 411 { switches::kProxyServer, "http=httpproxy:8888;ftp=ftpproxy:8889" }, 412 { switches::kProxyBypassList, 413 ".google.com, foo.com:99, 1.2.3.4:22, 127.0.0.1/8" }, 414 }, 415 // Expected result 416 false, // is_null 417 false, // auto_detect 418 GURL(), // pac_url 419 net::ProxyRulesExpectation::PerScheme( 420 "httpproxy:8888", // http 421 "", // https 422 "ftpproxy:8889", // ftp 423 "*.google.com,foo.com:99,1.2.3.4:22,127.0.0.1/8"), 424 }, 425 { 426 "Pac URL", 427 // Input 428 { 429 { switches::kProxyPacUrl, "http://wpad/wpad.dat" }, 430 }, 431 // Expected result 432 false, // is_null 433 false, // auto_detect 434 GURL("http://wpad/wpad.dat"), // pac_url 435 net::ProxyRulesExpectation::Empty(), 436 }, 437 { 438 "Autodetect", 439 // Input 440 { 441 { switches::kProxyAutoDetect, NULL }, 442 }, 443 // Expected result 444 false, // is_null 445 true, // auto_detect 446 GURL(), // pac_url 447 net::ProxyRulesExpectation::Empty(), 448 }, 449 }; 450 451 INSTANTIATE_TEST_CASE_P( 452 PrefProxyConfigServiceCommandLineTestInstance, 453 PrefProxyConfigServiceCommandLineTest, 454 testing::ValuesIn(kCommandLineTestParams)); 455 456 } // namespace 457