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: 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 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). 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 110 class ProxyConfigServiceAndroidTest : public ProxyConfigServiceAndroidTestBase { 111 public: 112 ProxyConfigServiceAndroidTest() 113 : ProxyConfigServiceAndroidTestBase(StringMap()) {} 114 }; 115 116 class 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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