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.
5 #include "chrome/browser/net/pref_proxy_config_tracker_impl.h"
9 #include "base/command_line.h"
10 #include "base/files/file_path.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/prefs/pref_registry_simple.h"
13 #include "base/prefs/testing_pref_service.h"
14 #include "base/test/histogram_tester.h"
15 #include "chrome/browser/prefs/pref_service_mock_factory.h"
16 #include "chrome/common/chrome_switches.h"
17 #include "chrome/common/pref_names.h"
18 #include "components/proxy_config/proxy_config_dictionary.h"
19 #include "content/public/test/test_browser_thread.h"
20 #include "net/proxy/proxy_config_service_common_unittest.h"
21 #include "net/proxy/proxy_info.h"
22 #include "net/proxy/proxy_list.h"
23 #include "testing/gmock/include/gmock/gmock.h"
24 #include "testing/gtest/include/gtest/gtest.h"
26 using content::BrowserThread
;
32 const char kFixedPacUrl
[] = "http://chromium.org/fixed_pac_url";
34 // Testing proxy config service that allows us to fire notifications at will.
35 class TestProxyConfigService
: public net::ProxyConfigService
{
37 TestProxyConfigService(const net::ProxyConfig
& config
,
38 ConfigAvailability availability
)
40 availability_(availability
) {}
42 void SetProxyConfig(const net::ProxyConfig config
,
43 ConfigAvailability availability
) {
45 availability_
= availability
;
46 FOR_EACH_OBSERVER(net::ProxyConfigService::Observer
, observers_
,
47 OnProxyConfigChanged(config
, availability
));
51 void AddObserver(net::ProxyConfigService::Observer
* observer
) override
{
52 observers_
.AddObserver(observer
);
55 void RemoveObserver(net::ProxyConfigService::Observer
* observer
) override
{
56 observers_
.RemoveObserver(observer
);
59 net::ProxyConfigService::ConfigAvailability
GetLatestProxyConfig(
60 net::ProxyConfig
* config
) override
{
65 net::ProxyConfig config_
;
66 ConfigAvailability availability_
;
67 base::ObserverList
<net::ProxyConfigService::Observer
, true> observers_
;
70 // A mock observer for capturing callbacks.
71 class MockObserver
: public net::ProxyConfigService::Observer
{
73 MOCK_METHOD2(OnProxyConfigChanged
,
74 void(const net::ProxyConfig
&,
75 net::ProxyConfigService::ConfigAvailability
));
78 template<typename TESTBASE
>
79 class PrefProxyConfigTrackerImplTestBase
: public TESTBASE
{
81 PrefProxyConfigTrackerImplTestBase()
82 : ui_thread_(BrowserThread::UI
, &loop_
),
83 io_thread_(BrowserThread::IO
, &loop_
) {}
85 virtual void Init(PrefService
* pref_service
, PrefRegistrySimple
* registry
) {
86 ASSERT_TRUE(pref_service
);
87 PrefProxyConfigTrackerImpl::RegisterPrefs(registry
);
88 fixed_config_
.set_pac_url(GURL(kFixedPacUrl
));
90 new TestProxyConfigService(fixed_config_
,
91 net::ProxyConfigService::CONFIG_VALID
);
92 proxy_config_tracker_
.reset(new PrefProxyConfigTrackerImpl(pref_service
));
93 proxy_config_service_
=
94 proxy_config_tracker_
->CreateTrackingProxyConfigService(
95 scoped_ptr
<net::ProxyConfigService
>(delegate_service_
));
96 // SetChromeProxyConfigService triggers update of initial prefs proxy
97 // config by tracker to chrome proxy config service, so flush all pending
98 // tasks so that tests start fresh.
102 virtual void TearDown() {
103 proxy_config_tracker_
->DetachFromPrefService();
104 loop_
.RunUntilIdle();
105 proxy_config_tracker_
.reset();
106 proxy_config_service_
.reset();
109 base::MessageLoop loop_
;
110 TestProxyConfigService
* delegate_service_
; // weak
111 scoped_ptr
<net::ProxyConfigService
> proxy_config_service_
;
112 net::ProxyConfig fixed_config_
;
115 scoped_ptr
<PrefProxyConfigTrackerImpl
> proxy_config_tracker_
;
116 content::TestBrowserThread ui_thread_
;
117 content::TestBrowserThread io_thread_
;
120 class PrefProxyConfigTrackerImplTest
121 : public PrefProxyConfigTrackerImplTestBase
<testing::Test
> {
123 void SetUp() override
{
124 pref_service_
.reset(new TestingPrefServiceSimple());
125 Init(pref_service_
.get(), pref_service_
->registry());
128 scoped_ptr
<TestingPrefServiceSimple
> pref_service_
;
131 TEST_F(PrefProxyConfigTrackerImplTest
, BaseConfiguration
) {
132 net::ProxyConfig actual_config
;
133 EXPECT_EQ(net::ProxyConfigService::CONFIG_VALID
,
134 proxy_config_service_
->GetLatestProxyConfig(&actual_config
));
135 EXPECT_EQ(GURL(kFixedPacUrl
), actual_config
.pac_url());
138 TEST_F(PrefProxyConfigTrackerImplTest
, DynamicPrefOverrides
) {
139 pref_service_
->SetManagedPref(prefs::kProxy
,
140 ProxyConfigDictionary::CreateFixedServers(
141 "http://example.com:3128", std::string()));
142 loop_
.RunUntilIdle();
144 net::ProxyConfig actual_config
;
145 EXPECT_EQ(net::ProxyConfigService::CONFIG_VALID
,
146 proxy_config_service_
->GetLatestProxyConfig(&actual_config
));
147 EXPECT_FALSE(actual_config
.auto_detect());
148 EXPECT_EQ(net::ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY
,
149 actual_config
.proxy_rules().type
);
150 EXPECT_EQ(actual_config
.proxy_rules().single_proxies
.Get(),
151 net::ProxyServer::FromURI("http://example.com:3128",
152 net::ProxyServer::SCHEME_HTTP
));
154 pref_service_
->SetManagedPref(prefs::kProxy
,
155 ProxyConfigDictionary::CreateAutoDetect());
156 loop_
.RunUntilIdle();
158 EXPECT_EQ(net::ProxyConfigService::CONFIG_VALID
,
159 proxy_config_service_
->GetLatestProxyConfig(&actual_config
));
160 EXPECT_TRUE(actual_config
.auto_detect());
163 // Compares proxy configurations, but allows different identifiers.
164 MATCHER_P(ProxyConfigMatches
, config
, "") {
165 net::ProxyConfig
reference(config
);
166 reference
.set_id(arg
.id());
167 return reference
.Equals(arg
);
170 TEST_F(PrefProxyConfigTrackerImplTest
, Observers
) {
171 const net::ProxyConfigService::ConfigAvailability CONFIG_VALID
=
172 net::ProxyConfigService::CONFIG_VALID
;
173 MockObserver observer
;
174 proxy_config_service_
->AddObserver(&observer
);
176 // Firing the observers in the delegate should trigger a notification.
177 net::ProxyConfig config2
;
178 config2
.set_auto_detect(true);
179 EXPECT_CALL(observer
, OnProxyConfigChanged(ProxyConfigMatches(config2
),
180 CONFIG_VALID
)).Times(1);
181 delegate_service_
->SetProxyConfig(config2
, CONFIG_VALID
);
182 loop_
.RunUntilIdle();
183 Mock::VerifyAndClearExpectations(&observer
);
185 // Override configuration, this should trigger a notification.
186 net::ProxyConfig pref_config
;
187 pref_config
.set_pac_url(GURL(kFixedPacUrl
));
189 EXPECT_CALL(observer
, OnProxyConfigChanged(ProxyConfigMatches(pref_config
),
190 CONFIG_VALID
)).Times(1);
191 pref_service_
->SetManagedPref(
193 ProxyConfigDictionary::CreatePacScript(kFixedPacUrl
, false));
194 loop_
.RunUntilIdle();
195 Mock::VerifyAndClearExpectations(&observer
);
197 // Since there are pref overrides, delegate changes should be ignored.
198 net::ProxyConfig config3
;
199 config3
.proxy_rules().ParseFromString("http=config3:80");
200 EXPECT_CALL(observer
, OnProxyConfigChanged(_
, _
)).Times(0);
201 fixed_config_
.set_auto_detect(true);
202 delegate_service_
->SetProxyConfig(config3
, CONFIG_VALID
);
203 loop_
.RunUntilIdle();
204 Mock::VerifyAndClearExpectations(&observer
);
206 // Clear the override should switch back to the fixed configuration.
207 EXPECT_CALL(observer
, OnProxyConfigChanged(ProxyConfigMatches(config3
),
208 CONFIG_VALID
)).Times(1);
209 pref_service_
->RemoveManagedPref(prefs::kProxy
);
210 loop_
.RunUntilIdle();
211 Mock::VerifyAndClearExpectations(&observer
);
213 // Delegate service notifications should show up again.
214 net::ProxyConfig config4
;
215 config4
.proxy_rules().ParseFromString("socks:config4");
216 EXPECT_CALL(observer
, OnProxyConfigChanged(ProxyConfigMatches(config4
),
217 CONFIG_VALID
)).Times(1);
218 delegate_service_
->SetProxyConfig(config4
, CONFIG_VALID
);
219 loop_
.RunUntilIdle();
220 Mock::VerifyAndClearExpectations(&observer
);
222 proxy_config_service_
->RemoveObserver(&observer
);
225 TEST_F(PrefProxyConfigTrackerImplTest
, Fallback
) {
226 const net::ProxyConfigService::ConfigAvailability CONFIG_VALID
=
227 net::ProxyConfigService::CONFIG_VALID
;
228 MockObserver observer
;
229 net::ProxyConfig actual_config
;
230 delegate_service_
->SetProxyConfig(net::ProxyConfig::CreateDirect(),
231 net::ProxyConfigService::CONFIG_UNSET
);
232 proxy_config_service_
->AddObserver(&observer
);
234 // Prepare test data.
235 net::ProxyConfig recommended_config
= net::ProxyConfig::CreateAutoDetect();
236 net::ProxyConfig user_config
=
237 net::ProxyConfig::CreateFromCustomPacURL(GURL(kFixedPacUrl
));
239 // Set a recommended pref.
240 EXPECT_CALL(observer
,
241 OnProxyConfigChanged(ProxyConfigMatches(recommended_config
),
242 CONFIG_VALID
)).Times(1);
243 pref_service_
->SetRecommendedPref(
245 ProxyConfigDictionary::CreateAutoDetect());
246 loop_
.RunUntilIdle();
247 Mock::VerifyAndClearExpectations(&observer
);
248 EXPECT_EQ(CONFIG_VALID
,
249 proxy_config_service_
->GetLatestProxyConfig(&actual_config
));
250 EXPECT_TRUE(actual_config
.Equals(recommended_config
));
252 // Override in user prefs.
253 EXPECT_CALL(observer
,
254 OnProxyConfigChanged(ProxyConfigMatches(user_config
),
255 CONFIG_VALID
)).Times(1);
256 pref_service_
->SetManagedPref(
258 ProxyConfigDictionary::CreatePacScript(kFixedPacUrl
, false));
259 loop_
.RunUntilIdle();
260 Mock::VerifyAndClearExpectations(&observer
);
261 EXPECT_EQ(CONFIG_VALID
,
262 proxy_config_service_
->GetLatestProxyConfig(&actual_config
));
263 EXPECT_TRUE(actual_config
.Equals(user_config
));
265 // Go back to recommended pref.
266 EXPECT_CALL(observer
,
267 OnProxyConfigChanged(ProxyConfigMatches(recommended_config
),
268 CONFIG_VALID
)).Times(1);
269 pref_service_
->RemoveManagedPref(prefs::kProxy
);
270 loop_
.RunUntilIdle();
271 Mock::VerifyAndClearExpectations(&observer
);
272 EXPECT_EQ(CONFIG_VALID
,
273 proxy_config_service_
->GetLatestProxyConfig(&actual_config
));
274 EXPECT_TRUE(actual_config
.Equals(recommended_config
));
276 proxy_config_service_
->RemoveObserver(&observer
);
279 TEST_F(PrefProxyConfigTrackerImplTest
, ExplicitSystemSettings
) {
280 pref_service_
->SetRecommendedPref(
282 ProxyConfigDictionary::CreateAutoDetect());
283 pref_service_
->SetUserPref(
285 ProxyConfigDictionary::CreateSystem());
286 loop_
.RunUntilIdle();
288 // Test if we actually use the system setting, which is |kFixedPacUrl|.
289 net::ProxyConfig actual_config
;
290 EXPECT_EQ(net::ProxyConfigService::CONFIG_VALID
,
291 proxy_config_service_
->GetLatestProxyConfig(&actual_config
));
292 EXPECT_EQ(GURL(kFixedPacUrl
), actual_config
.pac_url());
295 void CheckResolvedProxyMatches(net::ProxyConfig
* config
,
297 const std::string
& result_string
) {
298 net::ProxyInfo expected_result
;
299 expected_result
.UseNamedProxy(result_string
);
301 net::ProxyInfo result
;
302 config
->proxy_rules().Apply(url
, &result
);
304 EXPECT_TRUE(expected_result
.proxy_list().Equals(result
.proxy_list()))
305 << "expected: " << expected_result
.proxy_list().ToPacString()
306 << "\nactual: " << result
.proxy_list().ToPacString();
309 TEST_F(PrefProxyConfigTrackerImplTest
, ExcludeGooglezipDataReductionProxies
) {
310 const std::string kDataReductionProxies
=
311 "https://proxy.googlezip.net:443,compress.googlezip.net,"
312 "https://proxy-dev.googlezip.net:443,proxy-dev.googlezip.net,"
313 "quic://proxy.googlezip.net";
314 const int kNumDataReductionProxies
= 5;
317 std::string initial_proxy_rules
;
318 const char* http_proxy_info
;
319 const char* https_proxy_info
;
320 const char* ftp_proxy_info
;
321 int expected_num_removed_proxies
;
323 {"http=foopyhttp," + kDataReductionProxies
+
324 ",direct://;https=foopyhttps," + kDataReductionProxies
+
325 ",direct://;ftp=foopyftp," + kDataReductionProxies
+ ",direct://",
326 "foopyhttp;direct://",
327 "foopyhttps;direct://",
328 "foopyftp;direct://",
329 kNumDataReductionProxies
* 3},
331 {"foopy," + kDataReductionProxies
+ ",direct://",
335 kNumDataReductionProxies
},
337 {"http=" + kDataReductionProxies
+ ";https=" + kDataReductionProxies
+
338 ";ftp=" + kDataReductionProxies
,
342 kNumDataReductionProxies
* 3},
344 {"http=" + kDataReductionProxies
+ ",foopy,direct://",
348 kNumDataReductionProxies
},
363 // Test setting the proxy from a user pref.
364 for (const auto& test
: test_cases
) {
365 base::HistogramTester histogram_tester
;
366 pref_service_
->SetUserPref(prefs::kProxy
,
367 ProxyConfigDictionary::CreateFixedServers(
368 test
.initial_proxy_rules
, std::string()));
369 loop_
.RunUntilIdle();
371 net::ProxyConfig config
;
372 EXPECT_EQ(net::ProxyConfigService::CONFIG_VALID
,
373 proxy_config_service_
->GetLatestProxyConfig(&config
));
374 histogram_tester
.ExpectUniqueSample(
375 "Net.PrefProxyConfig.GooglezipProxyRemovalCount",
376 test
.expected_num_removed_proxies
, 1);
378 CheckResolvedProxyMatches(&config
, GURL("http://google.com"),
379 test
.http_proxy_info
);
380 CheckResolvedProxyMatches(&config
, GURL("https://google.com"),
381 test
.https_proxy_info
);
382 CheckResolvedProxyMatches(&config
, GURL("ftp://google.com"),
383 test
.ftp_proxy_info
);
387 // Test parameter object for testing command line proxy configuration.
388 struct CommandLineTestParams
{
389 // Explicit assignment operator, so testing::TestWithParam works with MSVC.
390 CommandLineTestParams
& operator=(const CommandLineTestParams
& other
) {
391 description
= other
.description
;
392 for (unsigned int i
= 0; i
< arraysize(switches
); i
++)
393 switches
[i
] = other
.switches
[i
];
394 is_null
= other
.is_null
;
395 auto_detect
= other
.auto_detect
;
396 pac_url
= other
.pac_url
;
397 proxy_rules
= other
.proxy_rules
;
401 // Short description to identify the test.
402 const char* description
;
404 // The command line to build a ProxyConfig from.
410 // Expected outputs (fields of the ProxyConfig).
414 net::ProxyRulesExpectation proxy_rules
;
417 void PrintTo(const CommandLineTestParams
& params
, std::ostream
* os
) {
418 *os
<< params
.description
;
421 class PrefProxyConfigTrackerImplCommandLineTest
422 : public PrefProxyConfigTrackerImplTestBase
<
423 testing::TestWithParam
<CommandLineTestParams
> > {
425 PrefProxyConfigTrackerImplCommandLineTest()
426 : command_line_(base::CommandLine::NO_PROGRAM
) {}
428 void SetUp() override
{
429 for (size_t i
= 0; i
< arraysize(GetParam().switches
); i
++) {
430 const char* name
= GetParam().switches
[i
].name
;
431 const char* value
= GetParam().switches
[i
].value
;
433 command_line_
.AppendSwitchASCII(name
, value
);
435 command_line_
.AppendSwitch(name
);
437 scoped_refptr
<PrefRegistrySimple
> registry
= new PrefRegistrySimple
;
438 PrefServiceMockFactory factory
;
439 factory
.SetCommandLine(&command_line_
);
440 pref_service_
= factory
.Create(registry
.get()).Pass();
441 Init(pref_service_
.get(), registry
.get());
445 base::CommandLine command_line_
;
446 scoped_ptr
<PrefService
> pref_service_
;
449 TEST_P(PrefProxyConfigTrackerImplCommandLineTest
, CommandLine
) {
450 net::ProxyConfig config
;
451 EXPECT_EQ(net::ProxyConfigService::CONFIG_VALID
,
452 proxy_config_service_
->GetLatestProxyConfig(&config
));
454 if (GetParam().is_null
) {
455 EXPECT_EQ(GURL(kFixedPacUrl
), config
.pac_url());
457 EXPECT_NE(GURL(kFixedPacUrl
), config
.pac_url());
458 EXPECT_EQ(GetParam().auto_detect
, config
.auto_detect());
459 EXPECT_EQ(GetParam().pac_url
, config
.pac_url());
460 EXPECT_TRUE(GetParam().proxy_rules
.Matches(config
.proxy_rules()));
464 static const CommandLineTestParams kCommandLineTestParams
[] = {
466 "Empty command line",
471 false, // auto_detect
473 net::ProxyRulesExpectation::Empty(),
479 { switches::kNoProxyServer
, NULL
},
483 false, // auto_detect
485 net::ProxyRulesExpectation::Empty(),
488 "No proxy with extra parameters.",
491 { switches::kNoProxyServer
, NULL
},
492 { switches::kProxyServer
, "http://proxy:8888" },
496 false, // auto_detect
498 net::ProxyRulesExpectation::Empty(),
504 { switches::kProxyServer
, "http://proxy:8888" },
508 false, // auto_detect
510 net::ProxyRulesExpectation::Single(
511 "proxy:8888", // single proxy
518 { switches::kProxyServer
, "http=httpproxy:8888;ftp=ftpproxy:8889" },
522 false, // auto_detect
524 net::ProxyRulesExpectation::PerScheme(
525 "httpproxy:8888", // http
527 "ftpproxy:8889", // ftp
531 "Per scheme proxy with bypass URLs.",
534 { switches::kProxyServer
, "http=httpproxy:8888;ftp=ftpproxy:8889" },
535 { switches::kProxyBypassList
,
536 ".google.com, foo.com:99, 1.2.3.4:22, 127.0.0.1/8" },
540 false, // auto_detect
542 net::ProxyRulesExpectation::PerScheme(
543 "httpproxy:8888", // http
545 "ftpproxy:8889", // ftp
546 "*.google.com,foo.com:99,1.2.3.4:22,127.0.0.1/8"),
552 { switches::kProxyPacUrl
, "http://wpad/wpad.dat" },
556 false, // auto_detect
557 GURL("http://wpad/wpad.dat"), // pac_url
558 net::ProxyRulesExpectation::Empty(),
564 { switches::kProxyAutoDetect
, NULL
},
570 net::ProxyRulesExpectation::Empty(),
574 INSTANTIATE_TEST_CASE_P(
575 PrefProxyConfigTrackerImplCommandLineTestInstance
,
576 PrefProxyConfigTrackerImplCommandLineTest
,
577 testing::ValuesIn(kCommandLineTestParams
));