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
17namespace net {
18
19namespace {
20
21class TestObserver : public ProxyConfigService::Observer {
22 public:
23  TestObserver() : availability_(ProxyConfigService::CONFIG_UNSET) {}
24
25  // ProxyConfigService::Observer:
26  virtual void OnProxyConfigChanged(
27      const ProxyConfig& config,
28      ProxyConfigService::ConfigAvailability availability) OVERRIDE {
29    config_ = config;
30    availability_ = availability;
31  }
32
33  ProxyConfigService::ConfigAvailability availability() const {
34    return availability_;
35  }
36
37  const ProxyConfig& config() const {
38    return config_;
39  }
40
41 private:
42  ProxyConfig config_;
43  ProxyConfigService::ConfigAvailability availability_;
44};
45
46}  // namespace
47
48typedef std::map<std::string, std::string> StringMap;
49
50class 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).
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
62  virtual ~ProxyConfigServiceAndroidTestBase() {}
63
64  // testing::Test:
65  virtual void SetUp() OVERRIDE {
66    message_loop_->RunUntilIdle();
67    service_.AddObserver(&observer_);
68  }
69
70  virtual void TearDown() OVERRIDE {
71    service_.RemoveObserver(&observer_);
72  }
73
74  void ClearConfiguration() {
75    configuration_.clear();
76  }
77
78  void AddProperty(const std::string& key, const std::string& value) {
79    configuration_[key] = value;
80  }
81
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
89  void ProxySettingsChanged() {
90    service_.ProxySettingsChanged();
91    message_loop_->RunUntilIdle();
92  }
93
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
110class ProxyConfigServiceAndroidTest : public ProxyConfigServiceAndroidTestBase {
111 public:
112  ProxyConfigServiceAndroidTest()
113      : ProxyConfigServiceAndroidTestBase(StringMap()) {}
114};
115
116class ProxyConfigServiceAndroidWithInitialConfigTest
117    : public ProxyConfigServiceAndroidTestBase {
118 public:
119  ProxyConfigServiceAndroidWithInitialConfigTest()
120      : ProxyConfigServiceAndroidTestBase(MakeInitialConfiguration()) {}
121
122 private:
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
131TEST_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
145TEST_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
163TEST_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
171TEST_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
181TEST_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
190TEST_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
199TEST_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
209TEST_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
220TEST_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
231TEST_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
241TEST_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
251TEST_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
260TEST_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
270TEST_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
279TEST_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
287TEST_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
296TEST_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
305TEST_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
317TEST_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
325TEST_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
335TEST_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
343TEST_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