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/single_thread_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::SingleThreadTaskRunner* network_task_runner, | |
| 164 base::SingleThreadTaskRunner* jni_task_runner, | |
| 165 const GetPropertyCallback& get_property_callback) | |
| 166 : ALLOW_THIS_IN_INITIALIZER_LIST(jni_callback_(this)), | |
| 167 network_task_runner_(network_task_runner), | |
| 168 jni_task_runner_(jni_task_runner), | |
| 169 get_property_callback_(get_property_callback) {} | |
|
Ryan Sleevi
2012/06/01 18:10:58
style: } belongs on a new line when the ctor arg l
Philippe
2012/06/04 10:02:37
Done.
| |
| 170 | |
| 171 void SetupJNI() { | |
| 172 if (OnJNIThread()) { | |
|
Ryan Sleevi
2012/06/01 18:10:58
Is this ever not true? Should it be a DCHECK inste
Philippe
2012/06/04 10:02:37
Done. I had to keep two separate methods though, S
| |
| 173 SetupOnJNIThread(); | |
| 174 } else { | |
| 175 jni_task_runner_->PostTask( | |
| 176 FROM_HERE, | |
| 177 base::Bind(&Delegate::SetupOnJNIThread, this)); | |
| 178 } | |
| 179 } | |
| 180 | |
| 181 void FetchInitialConfig() { | |
| 182 if (OnJNIThread()) { | |
| 183 FetchInitialConfigOnJNIThread(); | |
| 184 } else { | |
| 185 jni_task_runner_->PostTask( | |
| 186 FROM_HERE, | |
| 187 base::Bind(&Delegate::FetchInitialConfigOnJNIThread, this)); | |
| 188 } | |
| 189 } | |
| 190 | |
| 191 void Shutdown() { | |
| 192 if (OnJNIThread()) { | |
|
Ryan Sleevi
2012/06/01 18:10:58
This one would stay, I would assume.
Philippe
2012/06/04 10:02:37
Indeed.
| |
| 193 ShutdownOnJNIThread(); | |
| 194 } else { | |
| 195 jni_task_runner_->PostTask( | |
| 196 FROM_HERE, | |
| 197 base::Bind(&Delegate::ShutdownOnJNIThread, this)); | |
| 198 } | |
| 199 } | |
| 200 | |
| 201 // Called only on the network thread. | |
| 202 void AddObserver(Observer* observer) { | |
| 203 DCHECK(OnNetworkThread()); | |
| 204 observers_.AddObserver(observer); | |
| 205 } | |
| 206 | |
| 207 void RemoveObserver(Observer* observer) { | |
| 208 DCHECK(OnNetworkThread()); | |
| 209 observers_.RemoveObserver(observer); | |
| 210 } | |
| 211 | |
| 212 ConfigAvailability GetLatestProxyConfig(ProxyConfig* config) { | |
| 213 DCHECK(OnNetworkThread()); | |
| 214 if (!config) | |
| 215 return ProxyConfigService::CONFIG_UNSET; | |
| 216 *config = proxy_config_; | |
| 217 return ProxyConfigService::CONFIG_VALID; | |
| 218 } | |
| 219 | |
| 220 // Called on the JNI thread. | |
| 221 void ProxySettingsChanged() { | |
| 222 DCHECK(OnJNIThread()); | |
| 223 ProxyConfig proxy_config; | |
| 224 GetLatestProxyConfigInternal(get_property_callback_, &proxy_config); | |
| 225 network_task_runner_->PostTask( | |
| 226 FROM_HERE, | |
| 227 base::Bind( | |
| 228 &Delegate::SetNewConfigOnNetworkThread, this, proxy_config)); | |
| 229 } | |
| 230 | |
| 231 private: | |
| 232 friend class base::RefCountedThreadSafe<Delegate>; | |
| 233 friend class ProxyConfigServiceAndroid::JNICallback; | |
| 234 | |
| 235 virtual ~Delegate() {} | |
| 236 | |
| 237 void SetupOnJNIThread() { | |
| 238 JNIEnv* env = AttachCurrentThread(); | |
| 239 if (java_proxy_change_listener_.is_null()) { | |
| 240 java_proxy_change_listener_.Reset( | |
| 241 Java_ProxyChangeListener_create( | |
| 242 env, base::android::GetApplicationContext())); | |
| 243 CHECK(!java_proxy_change_listener_.is_null()); | |
| 244 } | |
| 245 Java_ProxyChangeListener_start( | |
| 246 env, | |
| 247 java_proxy_change_listener_.obj(), | |
| 248 reinterpret_cast<jint>(&jni_callback_)); | |
| 249 } | |
| 250 | |
| 251 void FetchInitialConfigOnJNIThread() { | |
| 252 ProxyConfig proxy_config; | |
| 253 GetLatestProxyConfigInternal(get_property_callback_, &proxy_config); | |
| 254 network_task_runner_->PostTask( | |
| 255 FROM_HERE, | |
| 256 base::Bind(&Delegate::SetNewConfigOnNetworkThread, this, proxy_config)); | |
| 257 } | |
| 258 | |
| 259 void ShutdownOnJNIThread() { | |
| 260 if (java_proxy_change_listener_.is_null()) | |
| 261 return; | |
| 262 JNIEnv* env = AttachCurrentThread(); | |
| 263 Java_ProxyChangeListener_stop(env, java_proxy_change_listener_.obj()); | |
| 264 } | |
| 265 | |
| 266 // Called on the network thread. | |
| 267 void SetNewConfigOnNetworkThread(const ProxyConfig& proxy_config) { | |
| 268 DCHECK(OnNetworkThread()); | |
| 269 proxy_config_ = proxy_config; | |
| 270 FOR_EACH_OBSERVER(Observer, observers_, | |
| 271 OnProxyConfigChanged(proxy_config, | |
| 272 ProxyConfigService::CONFIG_VALID)); | |
| 273 } | |
| 274 | |
| 275 bool OnJNIThread() const { | |
| 276 return jni_task_runner_->BelongsToCurrentThread(); | |
| 277 } | |
| 278 | |
| 279 bool OnNetworkThread() const { | |
| 280 return network_task_runner_->BelongsToCurrentThread(); | |
| 281 } | |
| 282 | |
| 283 ScopedJavaGlobalRef<jobject> java_proxy_change_listener_; | |
| 284 | |
| 285 ProxyConfigServiceAndroid::JNICallback jni_callback_; | |
| 286 ObserverList<Observer> observers_; | |
| 287 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_; | |
| 288 scoped_refptr<base::SingleThreadTaskRunner> jni_task_runner_; | |
| 289 GetPropertyCallback get_property_callback_; | |
| 290 ProxyConfig proxy_config_; | |
| 291 | |
| 292 DISALLOW_COPY_AND_ASSIGN(Delegate); | |
| 293 }; | |
| 294 | |
| 295 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid( | |
| 296 base::SingleThreadTaskRunner* network_task_runner, | |
| 297 base::SingleThreadTaskRunner* jni_task_runner) | |
| 298 : delegate_(new Delegate( | |
| 299 network_task_runner, jni_task_runner, base::Bind(&GetJavaProperty))) { | |
| 300 delegate_->SetupJNI(); | |
| 301 delegate_->FetchInitialConfig(); | |
| 302 } | |
| 303 | |
| 304 ProxyConfigServiceAndroid::~ProxyConfigServiceAndroid() { | |
| 305 delegate_->Shutdown(); | |
| 306 } | |
| 307 | |
| 308 // static | |
| 309 bool ProxyConfigServiceAndroid::Register(JNIEnv* env) { | |
| 310 return RegisterNativesImpl(env); | |
| 311 } | |
| 312 | |
| 313 void ProxyConfigServiceAndroid::AddObserver(Observer* observer) { | |
| 314 delegate_->AddObserver(observer); | |
| 315 } | |
| 316 | |
| 317 void ProxyConfigServiceAndroid::RemoveObserver(Observer* observer) { | |
| 318 delegate_->RemoveObserver(observer); | |
| 319 } | |
| 320 | |
| 321 ProxyConfigService::ConfigAvailability | |
| 322 ProxyConfigServiceAndroid::GetLatestProxyConfig(ProxyConfig* config) { | |
| 323 return delegate_->GetLatestProxyConfig(config); | |
| 324 } | |
| 325 | |
| 326 void ProxyConfigServiceAndroid::JNICallback::ProxySettingsChanged( | |
| 327 JNIEnv*, jobject) { | |
| 328 delegate_->ProxySettingsChanged(); | |
| 329 } | |
| 330 | |
| 331 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid( | |
| 332 base::SingleThreadTaskRunner* network_task_runner, | |
| 333 base::SingleThreadTaskRunner* jni_task_runner, | |
| 334 GetPropertyCallback get_property_callback) | |
| 335 : delegate_(new Delegate( | |
| 336 network_task_runner, jni_task_runner, get_property_callback)) { | |
| 337 delegate_->FetchInitialConfig(); | |
| 338 } | |
| 339 | |
| 340 void ProxyConfigServiceAndroid::ProxySettingsChanged() { | |
| 341 delegate_->ProxySettingsChanged(); | |
| 342 } | |
| 343 | |
| 344 } // namespace net | |
| OLD | NEW |