Chromium Code Reviews| 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_string.h" | |
| 10 #include "base/basictypes.h" | |
| 11 #include "base/bind.h" | |
| 12 #include "base/callback.h" | |
| 13 #include "base/location.h" | |
| 14 #include "base/logging.h" | |
| 15 #include "base/memory/ref_counted.h" | |
| 16 #include "base/observer_list.h" | |
| 17 #include "base/single_thread_task_runner.h" | |
| 18 #include "base/string_tokenizer.h" | |
| 19 #include "base/string_util.h" | |
| 20 #include "googleurl/src/url_parse.h" | |
| 21 #include "jni/proxy_change_listener_jni.h" | |
| 22 #include "net/base/host_port_pair.h" | |
| 23 #include "net/proxy/proxy_config.h" | |
| 24 | |
| 25 using base::android::AttachCurrentThread; | |
| 26 using base::android::ConvertUTF8ToJavaString; | |
| 27 using base::android::ConvertJavaStringToUTF8; | |
| 28 using base::android::CheckException; | |
| 29 using base::android::ClearException; | |
| 30 using base::android::GetMethodID; | |
| 31 using base::android::ScopedJavaGlobalRef; | |
| 32 | |
| 33 namespace net { | |
| 34 | |
| 35 namespace { | |
| 36 | |
| 37 typedef ProxyConfigServiceAndroid::GetPropertyCallback GetPropertyCallback; | |
| 38 | |
| 39 // Returns whether the provided string was successfully converted to a port. | |
| 40 bool 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 | |
| 50 ProxyServer 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 | |
| 65 ProxyServer 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 | |
| 83 ProxyServer 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 | |
| 93 void 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 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. | |
| 118 bool GetProxyRules(const GetPropertyCallback& get_property, | |
| 119 ProxyConfig::ProxyRules* rules) { | |
| 120 // See libcore/luni/src/main/java/java/net/ProxySelectorImpl.java for the | |
| 121 // equivalent Android implementation. | |
| 122 rules->type = ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME; | |
| 123 rules->proxy_for_http = LookupProxy("http", get_property, | |
| 124 ProxyServer::SCHEME_HTTP); | |
| 125 rules->proxy_for_https = LookupProxy("https", get_property, | |
| 126 ProxyServer::SCHEME_HTTPS); | |
| 127 rules->proxy_for_ftp = LookupProxy("ftp", get_property, | |
| 128 ProxyServer::SCHEME_HTTP); | |
| 129 rules->fallback_proxy = LookupSocksProxy(get_property); | |
| 130 rules->bypass_rules.Clear(); | |
| 131 AddBypassRules("ftp", get_property, &rules->bypass_rules); | |
| 132 AddBypassRules("http", get_property, &rules->bypass_rules); | |
| 133 AddBypassRules("https", get_property, &rules->bypass_rules); | |
| 134 return rules->proxy_for_http.is_valid() || | |
| 135 rules->proxy_for_https.is_valid() || | |
| 136 rules->proxy_for_ftp.is_valid() || | |
| 137 rules->fallback_proxy.is_valid(); | |
| 138 }; | |
| 139 | |
| 140 void GetLatestProxyConfigInternal(const GetPropertyCallback& get_property, | |
| 141 ProxyConfig* config) { | |
| 142 if (!GetProxyRules(get_property, &config->proxy_rules())) | |
| 143 *config = ProxyConfig::CreateDirect(); | |
| 144 } | |
| 145 | |
| 146 std::string GetJavaProperty(const std::string& property) { | |
| 147 // Use Java System.getProperty to get configuration information. | |
| 148 // TODO(pliard): Conversion to/from UTF8 ok here? | |
| 149 JNIEnv* env = AttachCurrentThread(); | |
| 150 ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, property); | |
| 151 ScopedJavaLocalRef<jstring> result = | |
| 152 Java_ProxyChangeListener_getProperty(env, str.obj()); | |
| 153 return result.is_null() ? | |
| 154 std::string() : ConvertJavaStringToUTF8(env, result.obj()); | |
| 155 } | |
| 156 | |
| 157 } // namespace | |
| 158 | |
| 159 class ProxyConfigServiceAndroid::Delegate | |
| 160 : public base::RefCountedThreadSafe<Delegate> { | |
| 161 public: | |
| 162 Delegate(base::SingleThreadTaskRunner* network_task_runner, | |
| 163 base::SingleThreadTaskRunner* jni_task_runner, | |
| 164 const GetPropertyCallback& get_property_callback) | |
| 165 : network_task_runner_(network_task_runner), | |
| 166 jni_task_runner_(jni_task_runner), | |
| 167 get_property_callback_(get_property_callback) {} | |
| 168 | |
| 169 void SetupJNI(const ProxyConfigServiceAndroid* service) { | |
| 170 if (OnJNIThread()) { | |
| 171 SetupOnJNIThread(service); | |
| 172 } else { | |
| 173 jni_task_runner_->PostTask( | |
| 174 FROM_HERE, | |
| 175 base::Bind(&Delegate::SetupOnJNIThread, this, service)); | |
|
Ryan Sleevi
2012/05/29 17:34:43
There is a slight timing/use-after-free issue here
Philippe
2012/06/01 13:06:37
Good catch!
I got your point for the first issue.
Philippe
2012/06/01 14:11:21
Ok, I think that I got your second point after I t
| |
| 176 } | |
| 177 } | |
| 178 | |
| 179 void FetchInitialConfig() { | |
| 180 if (OnJNIThread()) { | |
| 181 FetchInitialConfigOnJNIThread(); | |
| 182 } else { | |
| 183 jni_task_runner_->PostTask( | |
| 184 FROM_HERE, | |
| 185 base::Bind(&Delegate::FetchInitialConfigOnJNIThread, this)); | |
| 186 } | |
| 187 } | |
| 188 | |
| 189 // Called only on the network thread. | |
| 190 void AddObserver(Observer* observer) { | |
| 191 DCHECK(OnNetworkThread()); | |
| 192 observers_.AddObserver(observer); | |
| 193 } | |
| 194 | |
| 195 void RemoveObserver(Observer* observer) { | |
| 196 DCHECK(OnNetworkThread()); | |
| 197 observers_.RemoveObserver(observer); | |
| 198 } | |
| 199 | |
| 200 ConfigAvailability GetLatestProxyConfig(ProxyConfig* config) { | |
| 201 DCHECK(OnNetworkThread()); | |
| 202 if (!config) | |
| 203 return ProxyConfigService::CONFIG_UNSET; | |
| 204 *config = proxy_config_; | |
| 205 return ProxyConfigService::CONFIG_VALID; | |
| 206 } | |
| 207 | |
| 208 // Called on the JNI thread. | |
| 209 void ProxySettingsChanged() { | |
| 210 DCHECK(OnJNIThread()); | |
| 211 ProxyConfig proxy_config; | |
| 212 GetLatestProxyConfigInternal(get_property_callback_, &proxy_config); | |
| 213 network_task_runner_->PostTask( | |
| 214 FROM_HERE, | |
| 215 base::Bind( | |
| 216 &Delegate::SetNewConfigAndNotifyObservers, this, proxy_config)); | |
| 217 } | |
| 218 | |
| 219 private: | |
| 220 friend class base::RefCountedThreadSafe<Delegate>; | |
| 221 | |
| 222 virtual ~Delegate() { | |
| 223 Shutdown(); | |
| 224 } | |
| 225 | |
| 226 void SetupOnJNIThread(const ProxyConfigServiceAndroid* service) { | |
| 227 JNIEnv* env = AttachCurrentThread(); | |
| 228 if (java_proxy_change_listener_.is_null()) { | |
| 229 java_proxy_change_listener_.Reset( | |
| 230 Java_ProxyChangeListener_create( | |
| 231 env, base::android::GetApplicationContext())); | |
| 232 CHECK(!java_proxy_change_listener_.is_null()); | |
| 233 } | |
| 234 Java_ProxyChangeListener_start( | |
| 235 env, | |
| 236 java_proxy_change_listener_.obj(), | |
| 237 reinterpret_cast<jint>(service)); | |
| 238 } | |
| 239 | |
| 240 void FetchInitialConfigOnJNIThread() { | |
| 241 ProxyConfig proxy_config; | |
| 242 GetLatestProxyConfigInternal(get_property_callback_, &proxy_config); | |
| 243 SetNewConfigAndNotifyObservers(proxy_config); | |
| 244 } | |
| 245 | |
| 246 static void ShutdownOnJNIThread( | |
| 247 const ScopedJavaGlobalRef<jobject>& java_proxy_change_listener) { | |
| 248 if (java_proxy_change_listener.is_null()) | |
| 249 return; | |
| 250 JNIEnv* env = AttachCurrentThread(); | |
| 251 Java_ProxyChangeListener_stop(env, java_proxy_change_listener.obj()); | |
| 252 } | |
| 253 | |
| 254 void Shutdown() { | |
| 255 if (OnJNIThread()) { | |
| 256 ShutdownOnJNIThread(java_proxy_change_listener_); | |
| 257 } else { | |
| 258 jni_task_runner_->PostTask( | |
| 259 FROM_HERE, | |
| 260 base::Bind(&Delegate::ShutdownOnJNIThread, | |
| 261 java_proxy_change_listener_)); | |
| 262 } | |
| 263 } | |
| 264 | |
| 265 // Called on the network thread. | |
| 266 void SetNewConfigAndNotifyObservers(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_->BelongsToCurrentThread(); | |
| 276 } | |
| 277 | |
| 278 bool OnNetworkThread() const { | |
| 279 return network_task_runner_->BelongsToCurrentThread(); | |
| 280 } | |
| 281 | |
| 282 ScopedJavaGlobalRef<jobject> java_proxy_change_listener_; | |
| 283 | |
| 284 ObserverList<Observer> observers_; | |
| 285 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_; | |
| 286 scoped_refptr<base::SingleThreadTaskRunner> jni_task_runner_; | |
| 287 GetPropertyCallback get_property_callback_; | |
| 288 ProxyConfig proxy_config_; | |
| 289 | |
| 290 DISALLOW_COPY_AND_ASSIGN(Delegate); | |
| 291 }; | |
| 292 | |
| 293 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid( | |
| 294 base::SingleThreadTaskRunner* network_task_runner, | |
| 295 base::SingleThreadTaskRunner* jni_task_runner) | |
| 296 : delegate_(new Delegate( | |
| 297 network_task_runner, jni_task_runner, base::Bind(&GetJavaProperty))) { | |
| 298 delegate_->SetupJNI(this); | |
| 299 delegate_->FetchInitialConfig(); | |
| 300 } | |
| 301 | |
| 302 ProxyConfigServiceAndroid::~ProxyConfigServiceAndroid() {} | |
| 303 | |
| 304 // static | |
| 305 bool ProxyConfigServiceAndroid::Register(JNIEnv* env) { | |
| 306 return RegisterNativesImpl(env); | |
| 307 } | |
| 308 | |
| 309 void ProxyConfigServiceAndroid::AddObserver(Observer* observer) { | |
| 310 delegate_->AddObserver(observer); | |
| 311 } | |
| 312 | |
| 313 void ProxyConfigServiceAndroid::RemoveObserver(Observer* observer) { | |
| 314 delegate_->RemoveObserver(observer); | |
| 315 } | |
| 316 | |
| 317 ProxyConfigService::ConfigAvailability | |
| 318 ProxyConfigServiceAndroid::GetLatestProxyConfig(ProxyConfig* config) { | |
| 319 return delegate_->GetLatestProxyConfig(config); | |
| 320 } | |
| 321 | |
| 322 void ProxyConfigServiceAndroid::ProxySettingsChanged(JNIEnv*, jobject) { | |
| 323 delegate_->ProxySettingsChanged(); | |
| 324 } | |
| 325 | |
| 326 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid( | |
| 327 base::SingleThreadTaskRunner* network_task_runner, | |
| 328 base::SingleThreadTaskRunner* jni_task_runner, | |
| 329 GetPropertyCallback get_property_callback) | |
| 330 : delegate_(new Delegate( | |
| 331 network_task_runner, jni_task_runner, get_property_callback)) { | |
| 332 delegate_->FetchInitialConfig(); | |
| 333 } | |
| 334 | |
| 335 } // namespace net | |
| OLD | NEW |