Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / net / proxy / proxy_config_service_android.cc
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_array.h"
10 #include "base/android/jni_string.h"
11 #include "base/basictypes.h"
12 #include "base/bind.h"
13 #include "base/callback.h"
14 #include "base/compiler_specific.h"
15 #include "base/location.h"
16 #include "base/logging.h"
17 #include "base/memory/ref_counted.h"
18 #include "base/observer_list.h"
19 #include "base/sequenced_task_runner.h"
20 #include "base/strings/string_tokenizer.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/stringprintf.h"
23 #include "jni/ProxyChangeListener_jni.h"
24 #include "net/base/host_port_pair.h"
25 #include "net/proxy/proxy_config.h"
26 #include "url/url_parse.h"
27
28 using base::android::AttachCurrentThread;
29 using base::android::ConvertUTF8ToJavaString;
30 using base::android::ConvertJavaStringToUTF8;
31 using base::android::CheckException;
32 using base::android::ClearException;
33 using base::android::ScopedJavaGlobalRef;
34
35 namespace net {
36
37 namespace {
38
39 typedef ProxyConfigServiceAndroid::GetPropertyCallback GetPropertyCallback;
40
41 // Returns whether the provided string was successfully converted to a port.
42 bool ConvertStringToPort(const std::string& port, int* output) {
43   url::Component component(0, port.size());
44   int result = url::ParsePort(port.c_str(), component);
45   if (result == url::PORT_INVALID || result == url::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   base::StringTokenizer tokenizer(non_proxy_hosts, "|");
106   while (tokenizer.GetNext()) {
107     std::string token = tokenizer.token();
108     std::string pattern;
109     base::TrimWhitespaceASCII(token, base::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   // mostly equivalent Android implementation.  There is one intentional
123   // difference: by default Chromium uses the HTTP port (80) for HTTPS
124   // connections via proxy.  This default is identical on other platforms.
125   // On the opposite, Java spec suggests to use HTTPS port (443) by default (the
126   // default value of https.proxyPort).
127   rules->type = ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
128   rules->proxies_for_http.SetSingleProxyServer(
129       LookupProxy("http", get_property, ProxyServer::SCHEME_HTTP));
130   rules->proxies_for_https.SetSingleProxyServer(
131       LookupProxy("https", get_property, ProxyServer::SCHEME_HTTP));
132   rules->proxies_for_ftp.SetSingleProxyServer(
133       LookupProxy("ftp", get_property, ProxyServer::SCHEME_HTTP));
134   rules->fallback_proxies.SetSingleProxyServer(LookupSocksProxy(get_property));
135   rules->bypass_rules.Clear();
136   AddBypassRules("ftp", get_property, &rules->bypass_rules);
137   AddBypassRules("http", get_property, &rules->bypass_rules);
138   AddBypassRules("https", get_property, &rules->bypass_rules);
139   // We know a proxy was found if not all of the proxy lists are empty.
140   return !(rules->proxies_for_http.IsEmpty() &&
141       rules->proxies_for_https.IsEmpty() &&
142       rules->proxies_for_ftp.IsEmpty() &&
143       rules->fallback_proxies.IsEmpty());
144 };
145
146 void GetLatestProxyConfigInternal(const GetPropertyCallback& get_property,
147                                   ProxyConfig* config) {
148   if (!GetProxyRules(get_property, &config->proxy_rules()))
149     *config = ProxyConfig::CreateDirect();
150 }
151
152 std::string GetJavaProperty(const std::string& property) {
153   // Use Java System.getProperty to get configuration information.
154   // TODO(pliard): Conversion to/from UTF8 ok here?
155   JNIEnv* env = AttachCurrentThread();
156   ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, property);
157   ScopedJavaLocalRef<jstring> result =
158       Java_ProxyChangeListener_getProperty(env, str.obj());
159   return result.is_null() ?
160       std::string() : ConvertJavaStringToUTF8(env, result.obj());
161 }
162
163 void CreateStaticProxyConfig(const std::string& host,
164                              int port,
165                              const std::string& pac_url,
166                              const std::vector<std::string>& exclusion_list,
167                              ProxyConfig* config) {
168   if (!pac_url.empty()) {
169     config->set_pac_url(GURL(pac_url));
170     config->set_pac_mandatory(false);
171   } else if (port != 0) {
172     std::string rules = base::StringPrintf("%s:%d", host.c_str(), port);
173     config->proxy_rules().ParseFromString(rules);
174     config->proxy_rules().bypass_rules.Clear();
175
176     std::vector<std::string>::const_iterator it;
177     for (it = exclusion_list.begin(); it != exclusion_list.end(); ++it) {
178       std::string pattern;
179       base::TrimWhitespaceASCII(*it, base::TRIM_ALL, &pattern);
180       if (pattern.empty())
181           continue;
182       config->proxy_rules().bypass_rules.AddRuleForHostname("", pattern, -1);
183     }
184   } else {
185     *config = ProxyConfig::CreateDirect();
186   }
187 }
188
189 }  // namespace
190
191 class ProxyConfigServiceAndroid::Delegate
192     : public base::RefCountedThreadSafe<Delegate> {
193  public:
194   Delegate(const scoped_refptr<base::SequencedTaskRunner>& network_task_runner,
195            const scoped_refptr<base::SequencedTaskRunner>& jni_task_runner,
196            const GetPropertyCallback& get_property_callback)
197       : jni_delegate_(this),
198         network_task_runner_(network_task_runner),
199         jni_task_runner_(jni_task_runner),
200         get_property_callback_(get_property_callback),
201         exclude_pac_url_(false) {
202   }
203
204   void SetupJNI() {
205     DCHECK(OnJNIThread());
206     JNIEnv* env = AttachCurrentThread();
207     if (java_proxy_change_listener_.is_null()) {
208       java_proxy_change_listener_.Reset(
209           Java_ProxyChangeListener_create(
210               env, base::android::GetApplicationContext()));
211       CHECK(!java_proxy_change_listener_.is_null());
212     }
213     Java_ProxyChangeListener_start(
214         env,
215         java_proxy_change_listener_.obj(),
216         reinterpret_cast<intptr_t>(&jni_delegate_));
217   }
218
219   void FetchInitialConfig() {
220     DCHECK(OnJNIThread());
221     ProxyConfig proxy_config;
222     GetLatestProxyConfigInternal(get_property_callback_, &proxy_config);
223     network_task_runner_->PostTask(
224         FROM_HERE,
225         base::Bind(&Delegate::SetNewConfigOnNetworkThread, this, proxy_config));
226   }
227
228   void Shutdown() {
229     if (OnJNIThread()) {
230       ShutdownOnJNIThread();
231     } else {
232       jni_task_runner_->PostTask(
233           FROM_HERE,
234           base::Bind(&Delegate::ShutdownOnJNIThread, this));
235     }
236   }
237
238   // Called only on the network thread.
239   void AddObserver(Observer* observer) {
240     DCHECK(OnNetworkThread());
241     observers_.AddObserver(observer);
242   }
243
244   void RemoveObserver(Observer* observer) {
245     DCHECK(OnNetworkThread());
246     observers_.RemoveObserver(observer);
247   }
248
249   ConfigAvailability GetLatestProxyConfig(ProxyConfig* config) {
250     DCHECK(OnNetworkThread());
251     if (!config)
252       return ProxyConfigService::CONFIG_UNSET;
253     *config = proxy_config_;
254     return ProxyConfigService::CONFIG_VALID;
255   }
256
257   // Called on the JNI thread.
258   void ProxySettingsChanged() {
259     DCHECK(OnJNIThread());
260     ProxyConfig proxy_config;
261     GetLatestProxyConfigInternal(get_property_callback_, &proxy_config);
262     network_task_runner_->PostTask(
263         FROM_HERE,
264         base::Bind(
265             &Delegate::SetNewConfigOnNetworkThread, this, proxy_config));
266   }
267
268   // Called on the JNI thread.
269   void ProxySettingsChangedTo(const std::string& host,
270                               int port,
271                               const std::string& pac_url,
272                               const std::vector<std::string>& exclusion_list) {
273     DCHECK(OnJNIThread());
274     ProxyConfig proxy_config;
275     if (exclude_pac_url_) {
276       CreateStaticProxyConfig(host, port, "", exclusion_list, &proxy_config);
277     } else {
278       CreateStaticProxyConfig(host, port, pac_url, exclusion_list,
279           &proxy_config);
280     }
281     network_task_runner_->PostTask(
282         FROM_HERE,
283         base::Bind(
284             &Delegate::SetNewConfigOnNetworkThread, this, proxy_config));
285   }
286
287   void set_exclude_pac_url(bool enabled) {
288     exclude_pac_url_ = enabled;
289   }
290
291  private:
292   friend class base::RefCountedThreadSafe<Delegate>;
293
294   class JNIDelegateImpl : public ProxyConfigServiceAndroid::JNIDelegate {
295    public:
296     explicit JNIDelegateImpl(Delegate* delegate) : delegate_(delegate) {}
297
298     // ProxyConfigServiceAndroid::JNIDelegate overrides.
299     virtual void ProxySettingsChangedTo(JNIEnv* env,
300                                         jobject jself,
301                                         jstring jhost,
302                                         jint jport,
303                                         jstring jpac_url,
304                                         jobjectArray jexclusion_list) override {
305       std::string host = ConvertJavaStringToUTF8(env, jhost);
306       std::string pac_url;
307       if (jpac_url)
308         ConvertJavaStringToUTF8(env, jpac_url, &pac_url);
309       std::vector<std::string> exclusion_list;
310       base::android::AppendJavaStringArrayToStringVector(
311           env, jexclusion_list, &exclusion_list);
312       delegate_->ProxySettingsChangedTo(host, jport, pac_url, exclusion_list);
313     }
314
315     virtual void ProxySettingsChanged(JNIEnv* env, jobject self) override {
316       delegate_->ProxySettingsChanged();
317     }
318
319    private:
320     Delegate* const delegate_;
321   };
322
323   virtual ~Delegate() {}
324
325   void ShutdownOnJNIThread() {
326     if (java_proxy_change_listener_.is_null())
327       return;
328     JNIEnv* env = AttachCurrentThread();
329     Java_ProxyChangeListener_stop(env, java_proxy_change_listener_.obj());
330   }
331
332   // Called on the network thread.
333   void SetNewConfigOnNetworkThread(const ProxyConfig& proxy_config) {
334     DCHECK(OnNetworkThread());
335     proxy_config_ = proxy_config;
336     FOR_EACH_OBSERVER(Observer, observers_,
337                       OnProxyConfigChanged(proxy_config,
338                                            ProxyConfigService::CONFIG_VALID));
339   }
340
341   bool OnJNIThread() const {
342     return jni_task_runner_->RunsTasksOnCurrentThread();
343   }
344
345   bool OnNetworkThread() const {
346     return network_task_runner_->RunsTasksOnCurrentThread();
347   }
348
349   ScopedJavaGlobalRef<jobject> java_proxy_change_listener_;
350
351   JNIDelegateImpl jni_delegate_;
352   ObserverList<Observer> observers_;
353   scoped_refptr<base::SequencedTaskRunner> network_task_runner_;
354   scoped_refptr<base::SequencedTaskRunner> jni_task_runner_;
355   GetPropertyCallback get_property_callback_;
356   ProxyConfig proxy_config_;
357   bool exclude_pac_url_;
358
359   DISALLOW_COPY_AND_ASSIGN(Delegate);
360 };
361
362 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid(
363     const scoped_refptr<base::SequencedTaskRunner>& network_task_runner,
364     const scoped_refptr<base::SequencedTaskRunner>& jni_task_runner)
365     : delegate_(new Delegate(
366         network_task_runner, jni_task_runner, base::Bind(&GetJavaProperty))) {
367   delegate_->SetupJNI();
368   delegate_->FetchInitialConfig();
369 }
370
371 ProxyConfigServiceAndroid::~ProxyConfigServiceAndroid() {
372   delegate_->Shutdown();
373 }
374
375 // static
376 bool ProxyConfigServiceAndroid::Register(JNIEnv* env) {
377   return RegisterNativesImpl(env);
378 }
379
380 void ProxyConfigServiceAndroid::set_exclude_pac_url(bool enabled) {
381   delegate_->set_exclude_pac_url(enabled);
382 }
383
384 void ProxyConfigServiceAndroid::AddObserver(Observer* observer) {
385   delegate_->AddObserver(observer);
386 }
387
388 void ProxyConfigServiceAndroid::RemoveObserver(Observer* observer) {
389   delegate_->RemoveObserver(observer);
390 }
391
392 ProxyConfigService::ConfigAvailability
393 ProxyConfigServiceAndroid::GetLatestProxyConfig(ProxyConfig* config) {
394   return delegate_->GetLatestProxyConfig(config);
395 }
396
397 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid(
398     const scoped_refptr<base::SequencedTaskRunner>& network_task_runner,
399     const scoped_refptr<base::SequencedTaskRunner>& jni_task_runner,
400     GetPropertyCallback get_property_callback)
401     : delegate_(new Delegate(
402         network_task_runner, jni_task_runner, get_property_callback)) {
403   delegate_->SetupJNI();
404   delegate_->FetchInitialConfig();
405 }
406
407 void ProxyConfigServiceAndroid::ProxySettingsChanged() {
408   delegate_->ProxySettingsChanged();
409 }
410
411 } // namespace net