| OLD | NEW |
| (Empty) |
| 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 "net/proxy/proxy_config_service_android.h" | |
| 6 | |
| 7 #include <sys/system_properties.h> | |
| 8 | |
| 9 #include "base/android/jni_array.h" | |
| 10 #include "base/android/jni_string.h" | |
| 11 #include "base/basictypes.h" | |
| 12 #include "base/bind.h" | |
| 13 #include "base/callback.h" | |
| 14 #include "base/compiler_specific.h" | |
| 15 #include "base/location.h" | |
| 16 #include "base/logging.h" | |
| 17 #include "base/memory/ref_counted.h" | |
| 18 #include "base/observer_list.h" | |
| 19 #include "base/sequenced_task_runner.h" | |
| 20 #include "base/strings/string_tokenizer.h" | |
| 21 #include "base/strings/string_util.h" | |
| 22 #include "base/strings/stringprintf.h" | |
| 23 #include "jni/ProxyChangeListener_jni.h" | |
| 24 #include "net/base/host_port_pair.h" | |
| 25 #include "net/proxy/proxy_config.h" | |
| 26 #include "url/url_parse.h" | |
| 27 | |
| 28 using base::android::AttachCurrentThread; | |
| 29 using base::android::ConvertUTF8ToJavaString; | |
| 30 using base::android::ConvertJavaStringToUTF8; | |
| 31 using base::android::CheckException; | |
| 32 using base::android::ClearException; | |
| 33 using base::android::ScopedJavaGlobalRef; | |
| 34 | |
| 35 namespace net { | |
| 36 | |
| 37 namespace { | |
| 38 | |
| 39 typedef ProxyConfigServiceAndroid::GetPropertyCallback GetPropertyCallback; | |
| 40 | |
| 41 // Returns whether the provided string was successfully converted to a port. | |
| 42 bool ConvertStringToPort(const std::string& port, int* output) { | |
| 43 url::Component component(0, port.size()); | |
| 44 int result = url::ParsePort(port.c_str(), component); | |
| 45 if (result == url::PORT_INVALID || result == url::PORT_UNSPECIFIED) | |
| 46 return false; | |
| 47 *output = result; | |
| 48 return true; | |
| 49 } | |
| 50 | |
| 51 ProxyServer ConstructProxyServer(ProxyServer::Scheme scheme, | |
| 52 const std::string& proxy_host, | |
| 53 const std::string& proxy_port) { | |
| 54 DCHECK(!proxy_host.empty()); | |
| 55 int port_as_int = 0; | |
| 56 if (proxy_port.empty()) | |
| 57 port_as_int = ProxyServer::GetDefaultPortForScheme(scheme); | |
| 58 else if (!ConvertStringToPort(proxy_port, &port_as_int)) | |
| 59 return ProxyServer(); | |
| 60 DCHECK(port_as_int > 0); | |
| 61 return ProxyServer( | |
| 62 scheme, | |
| 63 HostPortPair(proxy_host, static_cast<uint16>(port_as_int))); | |
| 64 } | |
| 65 | |
| 66 ProxyServer LookupProxy(const std::string& prefix, | |
| 67 const GetPropertyCallback& get_property, | |
| 68 ProxyServer::Scheme scheme) { | |
| 69 DCHECK(!prefix.empty()); | |
| 70 std::string proxy_host = get_property.Run(prefix + ".proxyHost"); | |
| 71 if (!proxy_host.empty()) { | |
| 72 std::string proxy_port = get_property.Run(prefix + ".proxyPort"); | |
| 73 return ConstructProxyServer(scheme, proxy_host, proxy_port); | |
| 74 } | |
| 75 // Fall back to default proxy, if any. | |
| 76 proxy_host = get_property.Run("proxyHost"); | |
| 77 if (!proxy_host.empty()) { | |
| 78 std::string proxy_port = get_property.Run("proxyPort"); | |
| 79 return ConstructProxyServer(scheme, proxy_host, proxy_port); | |
| 80 } | |
| 81 return ProxyServer(); | |
| 82 } | |
| 83 | |
| 84 ProxyServer LookupSocksProxy(const GetPropertyCallback& get_property) { | |
| 85 std::string proxy_host = get_property.Run("socksProxyHost"); | |
| 86 if (!proxy_host.empty()) { | |
| 87 std::string proxy_port = get_property.Run("socksProxyPort"); | |
| 88 return ConstructProxyServer(ProxyServer::SCHEME_SOCKS5, proxy_host, | |
| 89 proxy_port); | |
| 90 } | |
| 91 return ProxyServer(); | |
| 92 } | |
| 93 | |
| 94 void AddBypassRules(const std::string& scheme, | |
| 95 const GetPropertyCallback& get_property, | |
| 96 ProxyBypassRules* bypass_rules) { | |
| 97 // The format of a hostname pattern is a list of hostnames that are separated | |
| 98 // by | and that use * as a wildcard. For example, setting the | |
| 99 // http.nonProxyHosts property to *.android.com|*.kernel.org will cause | |
| 100 // requests to http://developer.android.com to be made without a proxy. | |
| 101 | |
| 102 // Force localhost to be on the proxy exclusion list; | |
| 103 // otherwise all localhost traffic is routed through | |
| 104 // the proxy which is not desired. | |
| 105 bypass_rules->AddRuleToBypassLocal(); | |
| 106 | |
| 107 std::string non_proxy_hosts = | |
| 108 get_property.Run(scheme + ".nonProxyHosts"); | |
| 109 if (non_proxy_hosts.empty()) | |
| 110 return; | |
| 111 base::StringTokenizer tokenizer(non_proxy_hosts, "|"); | |
| 112 while (tokenizer.GetNext()) { | |
| 113 std::string token = tokenizer.token(); | |
| 114 std::string pattern; | |
| 115 base::TrimWhitespaceASCII(token, base::TRIM_ALL, &pattern); | |
| 116 if (pattern.empty()) | |
| 117 continue; | |
| 118 // '?' is not one of the specified pattern characters above. | |
| 119 DCHECK_EQ(std::string::npos, pattern.find('?')); | |
| 120 bypass_rules->AddRuleForHostname(scheme, pattern, -1); | |
| 121 } | |
| 122 } | |
| 123 | |
| 124 // Returns true if a valid proxy was found. | |
| 125 bool GetProxyRules(const GetPropertyCallback& get_property, | |
| 126 ProxyConfig::ProxyRules* rules) { | |
| 127 // See libcore/luni/src/main/java/java/net/ProxySelectorImpl.java for the | |
| 128 // mostly equivalent Android implementation. There is one intentional | |
| 129 // difference: by default Chromium uses the HTTP port (80) for HTTPS | |
| 130 // connections via proxy. This default is identical on other platforms. | |
| 131 // On the opposite, Java spec suggests to use HTTPS port (443) by default (the | |
| 132 // default value of https.proxyPort). | |
| 133 rules->type = ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME; | |
| 134 rules->proxies_for_http.SetSingleProxyServer( | |
| 135 LookupProxy("http", get_property, ProxyServer::SCHEME_HTTP)); | |
| 136 rules->proxies_for_https.SetSingleProxyServer( | |
| 137 LookupProxy("https", get_property, ProxyServer::SCHEME_HTTP)); | |
| 138 rules->proxies_for_ftp.SetSingleProxyServer( | |
| 139 LookupProxy("ftp", get_property, ProxyServer::SCHEME_HTTP)); | |
| 140 rules->fallback_proxies.SetSingleProxyServer(LookupSocksProxy(get_property)); | |
| 141 rules->bypass_rules.Clear(); | |
| 142 AddBypassRules("ftp", get_property, &rules->bypass_rules); | |
| 143 AddBypassRules("http", get_property, &rules->bypass_rules); | |
| 144 AddBypassRules("https", get_property, &rules->bypass_rules); | |
| 145 // We know a proxy was found if not all of the proxy lists are empty. | |
| 146 return !(rules->proxies_for_http.IsEmpty() && | |
| 147 rules->proxies_for_https.IsEmpty() && | |
| 148 rules->proxies_for_ftp.IsEmpty() && | |
| 149 rules->fallback_proxies.IsEmpty()); | |
| 150 }; | |
| 151 | |
| 152 void GetLatestProxyConfigInternal(const GetPropertyCallback& get_property, | |
| 153 ProxyConfig* config) { | |
| 154 if (!GetProxyRules(get_property, &config->proxy_rules())) | |
| 155 *config = ProxyConfig::CreateDirect(); | |
| 156 } | |
| 157 | |
| 158 std::string GetJavaProperty(const std::string& property) { | |
| 159 // Use Java System.getProperty to get configuration information. | |
| 160 // TODO(pliard): Conversion to/from UTF8 ok here? | |
| 161 JNIEnv* env = AttachCurrentThread(); | |
| 162 ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, property); | |
| 163 ScopedJavaLocalRef<jstring> result = | |
| 164 Java_ProxyChangeListener_getProperty(env, str.obj()); | |
| 165 return result.is_null() ? | |
| 166 std::string() : ConvertJavaStringToUTF8(env, result.obj()); | |
| 167 } | |
| 168 | |
| 169 void CreateStaticProxyConfig(const std::string& host, | |
| 170 int port, | |
| 171 const std::string& pac_url, | |
| 172 const std::vector<std::string>& exclusion_list, | |
| 173 ProxyConfig* config) { | |
| 174 if (!pac_url.empty()) { | |
| 175 config->set_pac_url(GURL(pac_url)); | |
| 176 config->set_pac_mandatory(false); | |
| 177 } else if (port != 0) { | |
| 178 std::string rules = base::StringPrintf("%s:%d", host.c_str(), port); | |
| 179 config->proxy_rules().ParseFromString(rules); | |
| 180 config->proxy_rules().bypass_rules.Clear(); | |
| 181 | |
| 182 std::vector<std::string>::const_iterator it; | |
| 183 for (it = exclusion_list.begin(); it != exclusion_list.end(); ++it) { | |
| 184 std::string pattern; | |
| 185 base::TrimWhitespaceASCII(*it, base::TRIM_ALL, &pattern); | |
| 186 if (pattern.empty()) | |
| 187 continue; | |
| 188 config->proxy_rules().bypass_rules.AddRuleForHostname("", pattern, -1); | |
| 189 } | |
| 190 } else { | |
| 191 *config = ProxyConfig::CreateDirect(); | |
| 192 } | |
| 193 } | |
| 194 | |
| 195 } // namespace | |
| 196 | |
| 197 class ProxyConfigServiceAndroid::Delegate | |
| 198 : public base::RefCountedThreadSafe<Delegate> { | |
| 199 public: | |
| 200 Delegate(const scoped_refptr<base::SequencedTaskRunner>& network_task_runner, | |
| 201 const scoped_refptr<base::SequencedTaskRunner>& jni_task_runner, | |
| 202 const GetPropertyCallback& get_property_callback) | |
| 203 : jni_delegate_(this), | |
| 204 network_task_runner_(network_task_runner), | |
| 205 jni_task_runner_(jni_task_runner), | |
| 206 get_property_callback_(get_property_callback), | |
| 207 exclude_pac_url_(false) { | |
| 208 } | |
| 209 | |
| 210 void SetupJNI() { | |
| 211 DCHECK(OnJNIThread()); | |
| 212 JNIEnv* env = AttachCurrentThread(); | |
| 213 if (java_proxy_change_listener_.is_null()) { | |
| 214 java_proxy_change_listener_.Reset( | |
| 215 Java_ProxyChangeListener_create( | |
| 216 env, base::android::GetApplicationContext())); | |
| 217 CHECK(!java_proxy_change_listener_.is_null()); | |
| 218 } | |
| 219 Java_ProxyChangeListener_start( | |
| 220 env, | |
| 221 java_proxy_change_listener_.obj(), | |
| 222 reinterpret_cast<intptr_t>(&jni_delegate_)); | |
| 223 } | |
| 224 | |
| 225 void FetchInitialConfig() { | |
| 226 DCHECK(OnJNIThread()); | |
| 227 ProxyConfig proxy_config; | |
| 228 GetLatestProxyConfigInternal(get_property_callback_, &proxy_config); | |
| 229 network_task_runner_->PostTask( | |
| 230 FROM_HERE, | |
| 231 base::Bind(&Delegate::SetNewConfigOnNetworkThread, this, proxy_config)); | |
| 232 } | |
| 233 | |
| 234 void Shutdown() { | |
| 235 if (OnJNIThread()) { | |
| 236 ShutdownOnJNIThread(); | |
| 237 } else { | |
| 238 jni_task_runner_->PostTask( | |
| 239 FROM_HERE, | |
| 240 base::Bind(&Delegate::ShutdownOnJNIThread, this)); | |
| 241 } | |
| 242 } | |
| 243 | |
| 244 // Called only on the network thread. | |
| 245 void AddObserver(Observer* observer) { | |
| 246 DCHECK(OnNetworkThread()); | |
| 247 observers_.AddObserver(observer); | |
| 248 } | |
| 249 | |
| 250 void RemoveObserver(Observer* observer) { | |
| 251 DCHECK(OnNetworkThread()); | |
| 252 observers_.RemoveObserver(observer); | |
| 253 } | |
| 254 | |
| 255 ConfigAvailability GetLatestProxyConfig(ProxyConfig* config) { | |
| 256 DCHECK(OnNetworkThread()); | |
| 257 if (!config) | |
| 258 return ProxyConfigService::CONFIG_UNSET; | |
| 259 *config = proxy_config_; | |
| 260 return ProxyConfigService::CONFIG_VALID; | |
| 261 } | |
| 262 | |
| 263 // Called on the JNI thread. | |
| 264 void ProxySettingsChanged() { | |
| 265 DCHECK(OnJNIThread()); | |
| 266 ProxyConfig proxy_config; | |
| 267 GetLatestProxyConfigInternal(get_property_callback_, &proxy_config); | |
| 268 network_task_runner_->PostTask( | |
| 269 FROM_HERE, | |
| 270 base::Bind( | |
| 271 &Delegate::SetNewConfigOnNetworkThread, this, proxy_config)); | |
| 272 } | |
| 273 | |
| 274 // Called on the JNI thread. | |
| 275 void ProxySettingsChangedTo(const std::string& host, | |
| 276 int port, | |
| 277 const std::string& pac_url, | |
| 278 const std::vector<std::string>& exclusion_list) { | |
| 279 DCHECK(OnJNIThread()); | |
| 280 ProxyConfig proxy_config; | |
| 281 if (exclude_pac_url_) { | |
| 282 CreateStaticProxyConfig(host, port, "", exclusion_list, &proxy_config); | |
| 283 } else { | |
| 284 CreateStaticProxyConfig(host, port, pac_url, exclusion_list, | |
| 285 &proxy_config); | |
| 286 } | |
| 287 network_task_runner_->PostTask( | |
| 288 FROM_HERE, | |
| 289 base::Bind( | |
| 290 &Delegate::SetNewConfigOnNetworkThread, this, proxy_config)); | |
| 291 } | |
| 292 | |
| 293 void set_exclude_pac_url(bool enabled) { | |
| 294 exclude_pac_url_ = enabled; | |
| 295 } | |
| 296 | |
| 297 private: | |
| 298 friend class base::RefCountedThreadSafe<Delegate>; | |
| 299 | |
| 300 class JNIDelegateImpl : public ProxyConfigServiceAndroid::JNIDelegate { | |
| 301 public: | |
| 302 explicit JNIDelegateImpl(Delegate* delegate) : delegate_(delegate) {} | |
| 303 | |
| 304 // ProxyConfigServiceAndroid::JNIDelegate overrides. | |
| 305 void ProxySettingsChangedTo(JNIEnv* env, | |
| 306 jobject jself, | |
| 307 jstring jhost, | |
| 308 jint jport, | |
| 309 jstring jpac_url, | |
| 310 jobjectArray jexclusion_list) override { | |
| 311 std::string host = ConvertJavaStringToUTF8(env, jhost); | |
| 312 std::string pac_url; | |
| 313 if (jpac_url) | |
| 314 ConvertJavaStringToUTF8(env, jpac_url, &pac_url); | |
| 315 std::vector<std::string> exclusion_list; | |
| 316 base::android::AppendJavaStringArrayToStringVector( | |
| 317 env, jexclusion_list, &exclusion_list); | |
| 318 delegate_->ProxySettingsChangedTo(host, jport, pac_url, exclusion_list); | |
| 319 } | |
| 320 | |
| 321 void ProxySettingsChanged(JNIEnv* env, jobject self) override { | |
| 322 delegate_->ProxySettingsChanged(); | |
| 323 } | |
| 324 | |
| 325 private: | |
| 326 Delegate* const delegate_; | |
| 327 }; | |
| 328 | |
| 329 virtual ~Delegate() {} | |
| 330 | |
| 331 void ShutdownOnJNIThread() { | |
| 332 if (java_proxy_change_listener_.is_null()) | |
| 333 return; | |
| 334 JNIEnv* env = AttachCurrentThread(); | |
| 335 Java_ProxyChangeListener_stop(env, java_proxy_change_listener_.obj()); | |
| 336 } | |
| 337 | |
| 338 // Called on the network thread. | |
| 339 void SetNewConfigOnNetworkThread(const ProxyConfig& proxy_config) { | |
| 340 DCHECK(OnNetworkThread()); | |
| 341 proxy_config_ = proxy_config; | |
| 342 FOR_EACH_OBSERVER(Observer, observers_, | |
| 343 OnProxyConfigChanged(proxy_config, | |
| 344 ProxyConfigService::CONFIG_VALID)); | |
| 345 } | |
| 346 | |
| 347 bool OnJNIThread() const { | |
| 348 return jni_task_runner_->RunsTasksOnCurrentThread(); | |
| 349 } | |
| 350 | |
| 351 bool OnNetworkThread() const { | |
| 352 return network_task_runner_->RunsTasksOnCurrentThread(); | |
| 353 } | |
| 354 | |
| 355 ScopedJavaGlobalRef<jobject> java_proxy_change_listener_; | |
| 356 | |
| 357 JNIDelegateImpl jni_delegate_; | |
| 358 ObserverList<Observer> observers_; | |
| 359 scoped_refptr<base::SequencedTaskRunner> network_task_runner_; | |
| 360 scoped_refptr<base::SequencedTaskRunner> jni_task_runner_; | |
| 361 GetPropertyCallback get_property_callback_; | |
| 362 ProxyConfig proxy_config_; | |
| 363 bool exclude_pac_url_; | |
| 364 | |
| 365 DISALLOW_COPY_AND_ASSIGN(Delegate); | |
| 366 }; | |
| 367 | |
| 368 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid( | |
| 369 const scoped_refptr<base::SequencedTaskRunner>& network_task_runner, | |
| 370 const scoped_refptr<base::SequencedTaskRunner>& jni_task_runner) | |
| 371 : delegate_(new Delegate( | |
| 372 network_task_runner, jni_task_runner, base::Bind(&GetJavaProperty))) { | |
| 373 delegate_->SetupJNI(); | |
| 374 delegate_->FetchInitialConfig(); | |
| 375 } | |
| 376 | |
| 377 ProxyConfigServiceAndroid::~ProxyConfigServiceAndroid() { | |
| 378 delegate_->Shutdown(); | |
| 379 } | |
| 380 | |
| 381 // static | |
| 382 bool ProxyConfigServiceAndroid::Register(JNIEnv* env) { | |
| 383 return RegisterNativesImpl(env); | |
| 384 } | |
| 385 | |
| 386 void ProxyConfigServiceAndroid::set_exclude_pac_url(bool enabled) { | |
| 387 delegate_->set_exclude_pac_url(enabled); | |
| 388 } | |
| 389 | |
| 390 void ProxyConfigServiceAndroid::AddObserver(Observer* observer) { | |
| 391 delegate_->AddObserver(observer); | |
| 392 } | |
| 393 | |
| 394 void ProxyConfigServiceAndroid::RemoveObserver(Observer* observer) { | |
| 395 delegate_->RemoveObserver(observer); | |
| 396 } | |
| 397 | |
| 398 ProxyConfigService::ConfigAvailability | |
| 399 ProxyConfigServiceAndroid::GetLatestProxyConfig(ProxyConfig* config) { | |
| 400 return delegate_->GetLatestProxyConfig(config); | |
| 401 } | |
| 402 | |
| 403 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid( | |
| 404 const scoped_refptr<base::SequencedTaskRunner>& network_task_runner, | |
| 405 const scoped_refptr<base::SequencedTaskRunner>& jni_task_runner, | |
| 406 GetPropertyCallback get_property_callback) | |
| 407 : delegate_(new Delegate( | |
| 408 network_task_runner, jni_task_runner, get_property_callback)) { | |
| 409 delegate_->SetupJNI(); | |
| 410 delegate_->FetchInitialConfig(); | |
| 411 } | |
| 412 | |
| 413 void ProxyConfigServiceAndroid::ProxySettingsChanged() { | |
| 414 delegate_->ProxySettingsChanged(); | |
| 415 } | |
| 416 | |
| 417 } // namespace net | |
| OLD | NEW |