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/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/string_tokenizer.h" | |
| 20 #include "base/string_util.h" | |
| 21 #include "googleurl/src/url_parse.h" | |
| 22 #include "jni/proxy_change_listener_jni.h" | |
| 23 #include "net/base/host_port_pair.h" | |
| 24 #include "net/proxy/proxy_config.h" | |
| 25 | |
| 26 using base::android::AttachCurrentThread; | |
| 27 using base::android::ConvertUTF8ToJavaString; | |
| 28 using base::android::ConvertJavaStringToUTF8; | |
| 29 using base::android::CheckException; | |
| 30 using base::android::ClearException; | |
| 31 using base::android::GetMethodID; | |
| 32 using base::android::ScopedJavaGlobalRef; | |
| 33 | |
| 34 namespace net { | |
| 35 | |
| 36 namespace { | |
| 37 | |
| 38 typedef ProxyConfigServiceAndroid::GetPropertyCallback GetPropertyCallback; | |
| 39 | |
| 40 // Returns whether the provided string was successfully converted to a port. | |
| 41 bool ConvertStringToPort(const std::string& port, int* output) { | |
| 42 url_parse::Component component(0, port.size()); | |
| 43 int result = url_parse::ParsePort(port.c_str(), component); | |
| 44 if (result == url_parse::PORT_INVALID || | |
| 45 result == url_parse::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 std::string non_proxy_hosts = | |
| 102 get_property.Run(scheme + ".nonProxyHosts"); | |
| 103 if (non_proxy_hosts.empty()) | |
| 104 return; | |
| 105 StringTokenizer tokenizer(non_proxy_hosts, "|"); | |
| 106 while (tokenizer.GetNext()) { | |
| 107 std::string token = tokenizer.token(); | |
| 108 std::string pattern; | |
| 109 TrimWhitespaceASCII(token, TRIM_ALL, &pattern); | |
| 110 if (pattern.empty()) | |
| 111 continue; | |
| 112 // '?' is not one of the specified pattern characters above. | |
| 113 DCHECK_EQ(std::string::npos, pattern.find('?')); | |
| 114 bypass_rules->AddRuleForHostname(scheme, pattern, -1); | |
| 115 } | |
| 116 } | |
| 117 | |
| 118 // Returns true if a valid proxy was found. | |
| 119 bool GetProxyRules(const GetPropertyCallback& get_property, | |
| 120 ProxyConfig::ProxyRules* rules) { | |
| 121 // See libcore/luni/src/main/java/java/net/ProxySelectorImpl.java for the | |
| 122 // equivalent Android implementation. | |
| 123 rules->type = ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME; | |
| 124 rules->proxy_for_http = LookupProxy("http", get_property, | |
| 125 ProxyServer::SCHEME_HTTP); | |
| 126 rules->proxy_for_https = LookupProxy("https", get_property, | |
| 127 ProxyServer::SCHEME_HTTPS); | |
| 128 rules->proxy_for_ftp = LookupProxy("ftp", get_property, | |
| 129 ProxyServer::SCHEME_HTTP); | |
| 130 rules->fallback_proxy = LookupSocksProxy(get_property); | |
| 131 rules->bypass_rules.Clear(); | |
| 132 AddBypassRules("ftp", get_property, &rules->bypass_rules); | |
| 133 AddBypassRules("http", get_property, &rules->bypass_rules); | |
| 134 AddBypassRules("https", get_property, &rules->bypass_rules); | |
| 135 return rules->proxy_for_http.is_valid() || | |
| 136 rules->proxy_for_https.is_valid() || | |
| 137 rules->proxy_for_ftp.is_valid() || | |
| 138 rules->fallback_proxy.is_valid(); | |
| 139 }; | |
| 140 | |
| 141 void GetLatestProxyConfigInternal(const GetPropertyCallback& get_property, | |
| 142 ProxyConfig* config) { | |
| 143 if (!GetProxyRules(get_property, &config->proxy_rules())) | |
| 144 *config = ProxyConfig::CreateDirect(); | |
| 145 } | |
| 146 | |
| 147 std::string GetJavaProperty(const std::string& property) { | |
| 148 // Use Java System.getProperty to get configuration information. | |
| 149 // TODO(pliard): Conversion to/from UTF8 ok here? | |
| 150 JNIEnv* env = AttachCurrentThread(); | |
| 151 ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, property); | |
| 152 ScopedJavaLocalRef<jstring> result = | |
| 153 Java_ProxyChangeListener_getProperty(env, str.obj()); | |
| 154 return result.is_null() ? | |
| 155 std::string() : ConvertJavaStringToUTF8(env, result.obj()); | |
| 156 } | |
| 157 | |
| 158 } // namespace | |
| 159 | |
| 160 class ProxyConfigServiceAndroid::Delegate | |
| 161 : public base::RefCountedThreadSafe<Delegate> { | |
| 162 public: | |
| 163 Delegate(base::SequencedTaskRunner* network_task_runner, | |
| 164 base::SequencedTaskRunner* jni_task_runner, | |
| 165 const GetPropertyCallback& get_property_callback) | |
| 166 : ALLOW_THIS_IN_INITIALIZER_LIST(jni_delegate_(this)), | |
| 167 network_task_runner_(network_task_runner), | |
| 168 jni_task_runner_(jni_task_runner), | |
| 169 get_property_callback_(get_property_callback) { | |
| 170 } | |
| 171 | |
| 172 void SetupJNI() { | |
| 173 DCHECK(OnJNIThread()); | |
| 174 JNIEnv* env = AttachCurrentThread(); | |
| 175 if (java_proxy_change_listener_.is_null()) { | |
| 176 java_proxy_change_listener_.Reset( | |
| 177 Java_ProxyChangeListener_create( | |
| 178 env, base::android::GetApplicationContext())); | |
| 179 CHECK(!java_proxy_change_listener_.is_null()); | |
| 180 } | |
| 181 Java_ProxyChangeListener_start( | |
| 182 env, | |
| 183 java_proxy_change_listener_.obj(), | |
| 184 reinterpret_cast<jint>(&jni_delegate_)); | |
| 185 } | |
| 186 | |
| 187 void FetchInitialConfig() { | |
| 188 DCHECK(OnJNIThread()); | |
| 189 ProxyConfig proxy_config; | |
| 190 GetLatestProxyConfigInternal(get_property_callback_, &proxy_config); | |
| 191 network_task_runner_->PostTask( | |
| 192 FROM_HERE, | |
| 193 base::Bind(&Delegate::SetNewConfigOnNetworkThread, this, proxy_config)); | |
| 194 } | |
| 195 | |
| 196 void Shutdown() { | |
| 197 if (OnJNIThread()) { | |
| 198 ShutdownOnJNIThread(); | |
| 199 } else { | |
| 200 jni_task_runner_->PostTask( | |
| 201 FROM_HERE, | |
| 202 base::Bind(&Delegate::ShutdownOnJNIThread, this)); | |
| 203 } | |
| 204 } | |
| 205 | |
| 206 // Called only on the network thread. | |
| 207 void AddObserver(Observer* observer) { | |
| 208 DCHECK(OnNetworkThread()); | |
| 209 observers_.AddObserver(observer); | |
| 210 } | |
| 211 | |
| 212 void RemoveObserver(Observer* observer) { | |
| 213 DCHECK(OnNetworkThread()); | |
| 214 observers_.RemoveObserver(observer); | |
| 215 } | |
| 216 | |
| 217 ConfigAvailability GetLatestProxyConfig(ProxyConfig* config) { | |
| 218 DCHECK(OnNetworkThread()); | |
| 219 if (!config) | |
| 220 return ProxyConfigService::CONFIG_UNSET; | |
| 221 *config = proxy_config_; | |
| 222 return ProxyConfigService::CONFIG_VALID; | |
| 223 } | |
| 224 | |
| 225 // Called on the JNI thread. | |
| 226 void ProxySettingsChanged() { | |
| 227 DCHECK(OnJNIThread()); | |
| 228 ProxyConfig proxy_config; | |
| 229 GetLatestProxyConfigInternal(get_property_callback_, &proxy_config); | |
| 230 network_task_runner_->PostTask( | |
| 231 FROM_HERE, | |
| 232 base::Bind( | |
| 233 &Delegate::SetNewConfigOnNetworkThread, this, proxy_config)); | |
| 234 } | |
| 235 | |
| 236 private: | |
| 237 friend class base::RefCountedThreadSafe<Delegate>; | |
| 238 | |
| 239 class JNIDelegateImpl : public ProxyConfigServiceAndroid::JNIDelegate { | |
| 240 public: | |
| 241 JNIDelegateImpl(Delegate* delegate) : delegate_(delegate) {} | |
|
bulach
2012/06/06 10:57:38
nit: explicit
Philippe
2012/06/06 11:36:58
Done.
| |
| 242 | |
| 243 // ProxyConfigServiceAndroid::JNIDelegate overrides. | |
| 244 virtual void ProxySettingsChanged(JNIEnv*, jobject) OVERRIDE { | |
| 245 delegate_->ProxySettingsChanged(); | |
| 246 } | |
| 247 | |
| 248 private: | |
| 249 Delegate* const delegate_; | |
| 250 }; | |
| 251 | |
| 252 virtual ~Delegate() {} | |
| 253 | |
| 254 void ShutdownOnJNIThread() { | |
| 255 if (java_proxy_change_listener_.is_null()) | |
| 256 return; | |
| 257 JNIEnv* env = AttachCurrentThread(); | |
| 258 Java_ProxyChangeListener_stop(env, java_proxy_change_listener_.obj()); | |
| 259 } | |
| 260 | |
| 261 // Called on the network thread. | |
| 262 void SetNewConfigOnNetworkThread(const ProxyConfig& proxy_config) { | |
| 263 DCHECK(OnNetworkThread()); | |
| 264 proxy_config_ = proxy_config; | |
| 265 FOR_EACH_OBSERVER(Observer, observers_, | |
| 266 OnProxyConfigChanged(proxy_config, | |
| 267 ProxyConfigService::CONFIG_VALID)); | |
| 268 } | |
| 269 | |
| 270 bool OnJNIThread() const { | |
| 271 return jni_task_runner_->RunsTasksOnCurrentThread(); | |
| 272 } | |
| 273 | |
| 274 bool OnNetworkThread() const { | |
| 275 return network_task_runner_->RunsTasksOnCurrentThread(); | |
| 276 } | |
| 277 | |
| 278 ScopedJavaGlobalRef<jobject> java_proxy_change_listener_; | |
| 279 | |
| 280 JNIDelegateImpl jni_delegate_; | |
| 281 ObserverList<Observer> observers_; | |
| 282 scoped_refptr<base::SequencedTaskRunner> network_task_runner_; | |
| 283 scoped_refptr<base::SequencedTaskRunner> jni_task_runner_; | |
| 284 GetPropertyCallback get_property_callback_; | |
| 285 ProxyConfig proxy_config_; | |
| 286 | |
| 287 DISALLOW_COPY_AND_ASSIGN(Delegate); | |
| 288 }; | |
| 289 | |
| 290 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid( | |
| 291 base::SequencedTaskRunner* network_task_runner, | |
| 292 base::SequencedTaskRunner* jni_task_runner) | |
| 293 : delegate_(new Delegate( | |
| 294 network_task_runner, jni_task_runner, base::Bind(&GetJavaProperty))) { | |
| 295 delegate_->SetupJNI(); | |
| 296 delegate_->FetchInitialConfig(); | |
| 297 } | |
| 298 | |
| 299 ProxyConfigServiceAndroid::~ProxyConfigServiceAndroid() { | |
| 300 delegate_->Shutdown(); | |
| 301 } | |
| 302 | |
| 303 // static | |
| 304 bool ProxyConfigServiceAndroid::Register(JNIEnv* env) { | |
| 305 return RegisterNativesImpl(env); | |
| 306 } | |
| 307 | |
| 308 void ProxyConfigServiceAndroid::AddObserver(Observer* observer) { | |
| 309 delegate_->AddObserver(observer); | |
| 310 } | |
| 311 | |
| 312 void ProxyConfigServiceAndroid::RemoveObserver(Observer* observer) { | |
| 313 delegate_->RemoveObserver(observer); | |
| 314 } | |
| 315 | |
| 316 ProxyConfigService::ConfigAvailability | |
| 317 ProxyConfigServiceAndroid::GetLatestProxyConfig(ProxyConfig* config) { | |
| 318 return delegate_->GetLatestProxyConfig(config); | |
| 319 } | |
| 320 | |
| 321 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid( | |
| 322 base::SequencedTaskRunner* network_task_runner, | |
| 323 base::SequencedTaskRunner* jni_task_runner, | |
| 324 GetPropertyCallback get_property_callback) | |
|
bulach
2012/06/06 10:57:38
nit: move this to 298
Philippe
2012/06/06 11:36:58
I already had to move it here :) I agree that it m
| |
| 325 : delegate_(new Delegate( | |
| 326 network_task_runner, jni_task_runner, get_property_callback)) { | |
| 327 delegate_->FetchInitialConfig(); | |
| 328 } | |
| 329 | |
| 330 void ProxyConfigServiceAndroid::ProxySettingsChanged() { | |
| 331 delegate_->ProxySettingsChanged(); | |
| 332 } | |
| 333 | |
| 334 } // namespace net | |
| OLD | NEW |