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 <map>
6 #include <string>
7
8 #include "base/bind.h"
9 #include "base/compiler_specific.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/message_loop/message_loop.h"
12 #include "net/proxy/proxy_config.h"
13 #include "net/proxy/proxy_config_service_android.h"
14 #include "net/proxy/proxy_info.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
17 namespace net {
18
19 namespace {
20
21 class TestObserver : public ProxyConfigService::Observer {
22 public:
TestObserver()23 TestObserver() : availability_(ProxyConfigService::CONFIG_UNSET) {}
24
25 // ProxyConfigService::Observer:
OnProxyConfigChanged(const ProxyConfig & config,ProxyConfigService::ConfigAvailability availability)26 virtual void OnProxyConfigChanged(
27 const ProxyConfig& config,
28 ProxyConfigService::ConfigAvailability availability) OVERRIDE {
29 config_ = config;
30 availability_ = availability;
31 }
32
availability() const33 ProxyConfigService::ConfigAvailability availability() const {
34 return availability_;
35 }
36
config() const37 const ProxyConfig& config() const {
38 return config_;
39 }
40
41 private:
42 ProxyConfig config_;
43 ProxyConfigService::ConfigAvailability availability_;
44 };
45
46 } // namespace
47
48 typedef std::map<std::string, std::string> StringMap;
49
50 class ProxyConfigServiceAndroidTestBase : public testing::Test {
51 protected:
52 // Note that the current thread's message loop is initialized by the test
53 // suite (see net/test/net_test_suite.cc).
ProxyConfigServiceAndroidTestBase(const StringMap & initial_configuration)54 ProxyConfigServiceAndroidTestBase(const StringMap& initial_configuration)
55 : configuration_(initial_configuration),
56 message_loop_(base::MessageLoop::current()),
57 service_(message_loop_->message_loop_proxy(),
58 message_loop_->message_loop_proxy(),
59 base::Bind(&ProxyConfigServiceAndroidTestBase::GetProperty,
60 base::Unretained(this))) {}
61
~ProxyConfigServiceAndroidTestBase()62 virtual ~ProxyConfigServiceAndroidTestBase() {}
63
64 // testing::Test:
SetUp()65 virtual void SetUp() OVERRIDE {
66 message_loop_->RunUntilIdle();
67 service_.AddObserver(&observer_);
68 }
69
TearDown()70 virtual void TearDown() OVERRIDE {
71 service_.RemoveObserver(&observer_);
72 }
73
ClearConfiguration()74 void ClearConfiguration() {
75 configuration_.clear();
76 }
77
AddProperty(const std::string & key,const std::string & value)78 void AddProperty(const std::string& key, const std::string& value) {
79 configuration_[key] = value;
80 }
81
GetProperty(const std::string & key)82 std::string GetProperty(const std::string& key) {
83 StringMap::const_iterator it = configuration_.find(key);
84 if (it == configuration_.end())
85 return std::string();
86 return it->second;
87 }
88
ProxySettingsChanged()89 void ProxySettingsChanged() {
90 service_.ProxySettingsChanged();
91 message_loop_->RunUntilIdle();
92 }
93
TestMapping(const std::string & url,const std::string & expected)94 void TestMapping(const std::string& url, const std::string& expected) {
95 ProxyConfigService::ConfigAvailability availability;
96 ProxyConfig proxy_config;
97 availability = service_.GetLatestProxyConfig(&proxy_config);
98 EXPECT_EQ(ProxyConfigService::CONFIG_VALID, availability);
99 ProxyInfo proxy_info;
100 proxy_config.proxy_rules().Apply(GURL(url), &proxy_info);
101 EXPECT_EQ(expected, proxy_info.ToPacString());
102 }
103
104 StringMap configuration_;
105 TestObserver observer_;
106 base::MessageLoop* const message_loop_;
107 ProxyConfigServiceAndroid service_;
108 };
109
110 class ProxyConfigServiceAndroidTest : public ProxyConfigServiceAndroidTestBase {
111 public:
ProxyConfigServiceAndroidTest()112 ProxyConfigServiceAndroidTest()
113 : ProxyConfigServiceAndroidTestBase(StringMap()) {}
114 };
115
116 class ProxyConfigServiceAndroidWithInitialConfigTest
117 : public ProxyConfigServiceAndroidTestBase {
118 public:
ProxyConfigServiceAndroidWithInitialConfigTest()119 ProxyConfigServiceAndroidWithInitialConfigTest()
120 : ProxyConfigServiceAndroidTestBase(MakeInitialConfiguration()) {}
121
122 private:
MakeInitialConfiguration()123 StringMap MakeInitialConfiguration() {
124 StringMap initial_configuration;
125 initial_configuration["http.proxyHost"] = "httpproxy.com";
126 initial_configuration["http.proxyPort"] = "8080";
127 return initial_configuration;
128 }
129 };
130
TEST_F(ProxyConfigServiceAndroidTest,TestChangePropertiesNotification)131 TEST_F(ProxyConfigServiceAndroidTest, TestChangePropertiesNotification) {
132 // Set up a non-empty configuration
133 AddProperty("http.proxyHost", "localhost");
134 ProxySettingsChanged();
135 EXPECT_EQ(ProxyConfigService::CONFIG_VALID, observer_.availability());
136 EXPECT_FALSE(observer_.config().proxy_rules().empty());
137
138 // Set up an empty configuration
139 ClearConfiguration();
140 ProxySettingsChanged();
141 EXPECT_EQ(ProxyConfigService::CONFIG_VALID, observer_.availability());
142 EXPECT_TRUE(observer_.config().proxy_rules().empty());
143 }
144
TEST_F(ProxyConfigServiceAndroidWithInitialConfigTest,TestInitialConfig)145 TEST_F(ProxyConfigServiceAndroidWithInitialConfigTest, TestInitialConfig) {
146 // Make sure that the initial config is set.
147 TestMapping("ftp://example.com/", "DIRECT");
148 TestMapping("http://example.com/", "PROXY httpproxy.com:8080");
149
150 // Override the initial configuration.
151 ClearConfiguration();
152 AddProperty("http.proxyHost", "httpproxy.com");
153 ProxySettingsChanged();
154 TestMapping("http://example.com/", "PROXY httpproxy.com:80");
155 }
156
157 // !! The following test cases are automatically generated from
158 // !! net/android/tools/proxy_test_cases.py.
159 // !! Please edit that file instead of editing the test cases below and
160 // !! update also the corresponding Java unit tests in
161 // !! AndroidProxySelectorTest.java
162
TEST_F(ProxyConfigServiceAndroidTest,NoProxy)163 TEST_F(ProxyConfigServiceAndroidTest, NoProxy) {
164 // Test direct mapping when no proxy defined.
165 ProxySettingsChanged();
166 TestMapping("ftp://example.com/", "DIRECT");
167 TestMapping("http://example.com/", "DIRECT");
168 TestMapping("https://example.com/", "DIRECT");
169 }
170
TEST_F(ProxyConfigServiceAndroidTest,HttpProxyHostAndPort)171 TEST_F(ProxyConfigServiceAndroidTest, HttpProxyHostAndPort) {
172 // Test http.proxyHost and http.proxyPort works.
173 AddProperty("http.proxyHost", "httpproxy.com");
174 AddProperty("http.proxyPort", "8080");
175 ProxySettingsChanged();
176 TestMapping("ftp://example.com/", "DIRECT");
177 TestMapping("http://example.com/", "PROXY httpproxy.com:8080");
178 TestMapping("https://example.com/", "DIRECT");
179 }
180
TEST_F(ProxyConfigServiceAndroidTest,HttpProxyHostOnly)181 TEST_F(ProxyConfigServiceAndroidTest, HttpProxyHostOnly) {
182 // We should get the default port (80) for proxied hosts.
183 AddProperty("http.proxyHost", "httpproxy.com");
184 ProxySettingsChanged();
185 TestMapping("ftp://example.com/", "DIRECT");
186 TestMapping("http://example.com/", "PROXY httpproxy.com:80");
187 TestMapping("https://example.com/", "DIRECT");
188 }
189
TEST_F(ProxyConfigServiceAndroidTest,HttpProxyPortOnly)190 TEST_F(ProxyConfigServiceAndroidTest, HttpProxyPortOnly) {
191 // http.proxyPort only should not result in any hosts being proxied.
192 AddProperty("http.proxyPort", "8080");
193 ProxySettingsChanged();
194 TestMapping("ftp://example.com/", "DIRECT");
195 TestMapping("http://example.com/", "DIRECT");
196 TestMapping("https://example.com/", "DIRECT");
197 }
198
TEST_F(ProxyConfigServiceAndroidTest,HttpNonProxyHosts1)199 TEST_F(ProxyConfigServiceAndroidTest, HttpNonProxyHosts1) {
200 // Test that HTTP non proxy hosts are mapped correctly
201 AddProperty("http.nonProxyHosts", "slashdot.org");
202 AddProperty("http.proxyHost", "httpproxy.com");
203 AddProperty("http.proxyPort", "8080");
204 ProxySettingsChanged();
205 TestMapping("http://example.com/", "PROXY httpproxy.com:8080");
206 TestMapping("http://slashdot.org/", "DIRECT");
207 }
208
TEST_F(ProxyConfigServiceAndroidTest,HttpNonProxyHosts2)209 TEST_F(ProxyConfigServiceAndroidTest, HttpNonProxyHosts2) {
210 // Test that | pattern works.
211 AddProperty("http.nonProxyHosts", "slashdot.org|freecode.net");
212 AddProperty("http.proxyHost", "httpproxy.com");
213 AddProperty("http.proxyPort", "8080");
214 ProxySettingsChanged();
215 TestMapping("http://example.com/", "PROXY httpproxy.com:8080");
216 TestMapping("http://freecode.net/", "DIRECT");
217 TestMapping("http://slashdot.org/", "DIRECT");
218 }
219
TEST_F(ProxyConfigServiceAndroidTest,HttpNonProxyHosts3)220 TEST_F(ProxyConfigServiceAndroidTest, HttpNonProxyHosts3) {
221 // Test that * pattern works.
222 AddProperty("http.nonProxyHosts", "*example.com");
223 AddProperty("http.proxyHost", "httpproxy.com");
224 AddProperty("http.proxyPort", "8080");
225 ProxySettingsChanged();
226 TestMapping("http://example.com/", "DIRECT");
227 TestMapping("http://slashdot.org/", "PROXY httpproxy.com:8080");
228 TestMapping("http://www.example.com/", "DIRECT");
229 }
230
TEST_F(ProxyConfigServiceAndroidTest,FtpNonProxyHosts)231 TEST_F(ProxyConfigServiceAndroidTest, FtpNonProxyHosts) {
232 // Test that FTP non proxy hosts are mapped correctly
233 AddProperty("ftp.nonProxyHosts", "slashdot.org");
234 AddProperty("ftp.proxyHost", "httpproxy.com");
235 AddProperty("ftp.proxyPort", "8080");
236 ProxySettingsChanged();
237 TestMapping("ftp://example.com/", "PROXY httpproxy.com:8080");
238 TestMapping("http://example.com/", "DIRECT");
239 }
240
TEST_F(ProxyConfigServiceAndroidTest,FtpProxyHostAndPort)241 TEST_F(ProxyConfigServiceAndroidTest, FtpProxyHostAndPort) {
242 // Test ftp.proxyHost and ftp.proxyPort works.
243 AddProperty("ftp.proxyHost", "httpproxy.com");
244 AddProperty("ftp.proxyPort", "8080");
245 ProxySettingsChanged();
246 TestMapping("ftp://example.com/", "PROXY httpproxy.com:8080");
247 TestMapping("http://example.com/", "DIRECT");
248 TestMapping("https://example.com/", "DIRECT");
249 }
250
TEST_F(ProxyConfigServiceAndroidTest,FtpProxyHostOnly)251 TEST_F(ProxyConfigServiceAndroidTest, FtpProxyHostOnly) {
252 // Test ftp.proxyHost and default port.
253 AddProperty("ftp.proxyHost", "httpproxy.com");
254 ProxySettingsChanged();
255 TestMapping("ftp://example.com/", "PROXY httpproxy.com:80");
256 TestMapping("http://example.com/", "DIRECT");
257 TestMapping("https://example.com/", "DIRECT");
258 }
259
TEST_F(ProxyConfigServiceAndroidTest,HttpsProxyHostAndPort)260 TEST_F(ProxyConfigServiceAndroidTest, HttpsProxyHostAndPort) {
261 // Test https.proxyHost and https.proxyPort works.
262 AddProperty("https.proxyHost", "httpproxy.com");
263 AddProperty("https.proxyPort", "8080");
264 ProxySettingsChanged();
265 TestMapping("ftp://example.com/", "DIRECT");
266 TestMapping("http://example.com/", "DIRECT");
267 TestMapping("https://example.com/", "PROXY httpproxy.com:8080");
268 }
269
TEST_F(ProxyConfigServiceAndroidTest,HttpsProxyHostOnly)270 TEST_F(ProxyConfigServiceAndroidTest, HttpsProxyHostOnly) {
271 // Test https.proxyHost and default port.
272 AddProperty("https.proxyHost", "httpproxy.com");
273 ProxySettingsChanged();
274 TestMapping("ftp://example.com/", "DIRECT");
275 TestMapping("http://example.com/", "DIRECT");
276 TestMapping("https://example.com/", "PROXY httpproxy.com:80");
277 }
278
TEST_F(ProxyConfigServiceAndroidTest,HttpProxyHostIPv6)279 TEST_F(ProxyConfigServiceAndroidTest, HttpProxyHostIPv6) {
280 // Test IPv6 https.proxyHost and default port.
281 AddProperty("http.proxyHost", "a:b:c::d:1");
282 ProxySettingsChanged();
283 TestMapping("ftp://example.com/", "DIRECT");
284 TestMapping("http://example.com/", "PROXY [a:b:c::d:1]:80");
285 }
286
TEST_F(ProxyConfigServiceAndroidTest,HttpProxyHostAndPortIPv6)287 TEST_F(ProxyConfigServiceAndroidTest, HttpProxyHostAndPortIPv6) {
288 // Test IPv6 http.proxyHost and http.proxyPort works.
289 AddProperty("http.proxyHost", "a:b:c::d:1");
290 AddProperty("http.proxyPort", "8080");
291 ProxySettingsChanged();
292 TestMapping("ftp://example.com/", "DIRECT");
293 TestMapping("http://example.com/", "PROXY [a:b:c::d:1]:8080");
294 }
295
TEST_F(ProxyConfigServiceAndroidTest,HttpProxyHostAndInvalidPort)296 TEST_F(ProxyConfigServiceAndroidTest, HttpProxyHostAndInvalidPort) {
297 // Test invalid http.proxyPort does not crash.
298 AddProperty("http.proxyHost", "a:b:c::d:1");
299 AddProperty("http.proxyPort", "65536");
300 ProxySettingsChanged();
301 TestMapping("ftp://example.com/", "DIRECT");
302 TestMapping("http://example.com/", "DIRECT");
303 }
304
TEST_F(ProxyConfigServiceAndroidTest,DefaultProxyExplictPort)305 TEST_F(ProxyConfigServiceAndroidTest, DefaultProxyExplictPort) {
306 // Default http proxy is used if a scheme-specific one is not found.
307 AddProperty("ftp.proxyHost", "httpproxy.com");
308 AddProperty("ftp.proxyPort", "8080");
309 AddProperty("proxyHost", "defaultproxy.com");
310 AddProperty("proxyPort", "8080");
311 ProxySettingsChanged();
312 TestMapping("ftp://example.com/", "PROXY httpproxy.com:8080");
313 TestMapping("http://example.com/", "PROXY defaultproxy.com:8080");
314 TestMapping("https://example.com/", "PROXY defaultproxy.com:8080");
315 }
316
TEST_F(ProxyConfigServiceAndroidTest,DefaultProxyDefaultPort)317 TEST_F(ProxyConfigServiceAndroidTest, DefaultProxyDefaultPort) {
318 // Check that the default proxy port is as expected.
319 AddProperty("proxyHost", "defaultproxy.com");
320 ProxySettingsChanged();
321 TestMapping("http://example.com/", "PROXY defaultproxy.com:80");
322 TestMapping("https://example.com/", "PROXY defaultproxy.com:80");
323 }
324
TEST_F(ProxyConfigServiceAndroidTest,FallbackToSocks)325 TEST_F(ProxyConfigServiceAndroidTest, FallbackToSocks) {
326 // SOCKS proxy is used if scheme-specific one is not found.
327 AddProperty("http.proxyHost", "defaultproxy.com");
328 AddProperty("socksProxyHost", "socksproxy.com");
329 ProxySettingsChanged();
330 TestMapping("ftp://example.com", "SOCKS5 socksproxy.com:1080");
331 TestMapping("http://example.com/", "PROXY defaultproxy.com:80");
332 TestMapping("https://example.com/", "SOCKS5 socksproxy.com:1080");
333 }
334
TEST_F(ProxyConfigServiceAndroidTest,SocksExplicitPort)335 TEST_F(ProxyConfigServiceAndroidTest, SocksExplicitPort) {
336 // SOCKS proxy port is used if specified
337 AddProperty("socksProxyHost", "socksproxy.com");
338 AddProperty("socksProxyPort", "9000");
339 ProxySettingsChanged();
340 TestMapping("http://example.com/", "SOCKS5 socksproxy.com:9000");
341 }
342
TEST_F(ProxyConfigServiceAndroidTest,HttpProxySupercedesSocks)343 TEST_F(ProxyConfigServiceAndroidTest, HttpProxySupercedesSocks) {
344 // SOCKS proxy is ignored if default HTTP proxy defined.
345 AddProperty("proxyHost", "defaultproxy.com");
346 AddProperty("socksProxyHost", "socksproxy.com");
347 AddProperty("socksProxyPort", "9000");
348 ProxySettingsChanged();
349 TestMapping("http://example.com/", "PROXY defaultproxy.com:80");
350 }
351
352 } // namespace net
353