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