Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(226)

Side by Side Diff: net/proxy/proxy_config_service_android.cc

Issue 10206014: Upstream Android proxy config service. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix two threading issues + Sync Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698