Home | History | Annotate | Download | only in proxy
      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/strings/string_tokenizer.h"
     20 #include "base/strings/string_util.h"
     21 #include "base/strings/stringprintf.h"
     22 #include "jni/ProxyChangeListener_jni.h"
     23 #include "net/base/host_port_pair.h"
     24 #include "net/proxy/proxy_config.h"
     25 #include "url/url_parse.h"
     26 
     27 using base::android::AttachCurrentThread;
     28 using base::android::ConvertUTF8ToJavaString;
     29 using base::android::ConvertJavaStringToUTF8;
     30 using base::android::CheckException;
     31 using base::android::ClearException;
     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   base::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   // 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, int port,
    164                              ProxyConfig* config) {
    165   if (port != 0) {
    166     std::string rules = base::StringPrintf("%s:%d", host.c_str(), port);
    167     config->proxy_rules().ParseFromString(rules);
    168   } else {
    169     *config = ProxyConfig::CreateDirect();
    170   }
    171 }
    172 
    173 }  // namespace
    174 
    175 class ProxyConfigServiceAndroid::Delegate
    176     : public base::RefCountedThreadSafe<Delegate> {
    177  public:
    178   Delegate(base::SequencedTaskRunner* network_task_runner,
    179            base::SequencedTaskRunner* jni_task_runner,
    180            const GetPropertyCallback& get_property_callback)
    181       : jni_delegate_(this),
    182         network_task_runner_(network_task_runner),
    183         jni_task_runner_(jni_task_runner),
    184         get_property_callback_(get_property_callback) {
    185   }
    186 
    187   void SetupJNI() {
    188     DCHECK(OnJNIThread());
    189     JNIEnv* env = AttachCurrentThread();
    190     if (java_proxy_change_listener_.is_null()) {
    191       java_proxy_change_listener_.Reset(
    192           Java_ProxyChangeListener_create(
    193               env, base::android::GetApplicationContext()));
    194       CHECK(!java_proxy_change_listener_.is_null());
    195     }
    196     Java_ProxyChangeListener_start(
    197         env,
    198         java_proxy_change_listener_.obj(),
    199         reinterpret_cast<intptr_t>(&jni_delegate_));
    200   }
    201 
    202   void FetchInitialConfig() {
    203     DCHECK(OnJNIThread());
    204     ProxyConfig proxy_config;
    205     GetLatestProxyConfigInternal(get_property_callback_, &proxy_config);
    206     network_task_runner_->PostTask(
    207         FROM_HERE,
    208         base::Bind(&Delegate::SetNewConfigOnNetworkThread, this, proxy_config));
    209   }
    210 
    211   void Shutdown() {
    212     if (OnJNIThread()) {
    213       ShutdownOnJNIThread();
    214     } else {
    215       jni_task_runner_->PostTask(
    216           FROM_HERE,
    217           base::Bind(&Delegate::ShutdownOnJNIThread, this));
    218     }
    219   }
    220 
    221   // Called only on the network thread.
    222   void AddObserver(Observer* observer) {
    223     DCHECK(OnNetworkThread());
    224     observers_.AddObserver(observer);
    225   }
    226 
    227   void RemoveObserver(Observer* observer) {
    228     DCHECK(OnNetworkThread());
    229     observers_.RemoveObserver(observer);
    230   }
    231 
    232   ConfigAvailability GetLatestProxyConfig(ProxyConfig* config) {
    233     DCHECK(OnNetworkThread());
    234     if (!config)
    235       return ProxyConfigService::CONFIG_UNSET;
    236     *config = proxy_config_;
    237     return ProxyConfigService::CONFIG_VALID;
    238   }
    239 
    240   // Called on the JNI thread.
    241   void ProxySettingsChanged() {
    242     DCHECK(OnJNIThread());
    243     ProxyConfig proxy_config;
    244     GetLatestProxyConfigInternal(get_property_callback_, &proxy_config);
    245     network_task_runner_->PostTask(
    246         FROM_HERE,
    247         base::Bind(
    248             &Delegate::SetNewConfigOnNetworkThread, this, proxy_config));
    249   }
    250 
    251   // Called on the JNI thread.
    252   void ProxySettingsChangedTo(const std::string& host, int port) {
    253     DCHECK(OnJNIThread());
    254     ProxyConfig proxy_config;
    255     CreateStaticProxyConfig(host, port, &proxy_config);
    256     network_task_runner_->PostTask(
    257         FROM_HERE,
    258         base::Bind(
    259             &Delegate::SetNewConfigOnNetworkThread, this, proxy_config));
    260   }
    261 
    262  private:
    263   friend class base::RefCountedThreadSafe<Delegate>;
    264 
    265   class JNIDelegateImpl : public ProxyConfigServiceAndroid::JNIDelegate {
    266    public:
    267     explicit JNIDelegateImpl(Delegate* delegate) : delegate_(delegate) {}
    268 
    269     // ProxyConfigServiceAndroid::JNIDelegate overrides.
    270     virtual void ProxySettingsChangedTo(JNIEnv* env, jobject jself,
    271                                       jstring jhost, jint jport) OVERRIDE {
    272       std::string host = ConvertJavaStringToUTF8(env, jhost);
    273       delegate_->ProxySettingsChangedTo(host, jport);
    274     }
    275 
    276     virtual void ProxySettingsChanged(JNIEnv* env, jobject self) OVERRIDE {
    277       delegate_->ProxySettingsChanged();
    278     }
    279 
    280    private:
    281     Delegate* const delegate_;
    282   };
    283 
    284   virtual ~Delegate() {}
    285 
    286   void ShutdownOnJNIThread() {
    287     if (java_proxy_change_listener_.is_null())
    288       return;
    289     JNIEnv* env = AttachCurrentThread();
    290     Java_ProxyChangeListener_stop(env, java_proxy_change_listener_.obj());
    291   }
    292 
    293   // Called on the network thread.
    294   void SetNewConfigOnNetworkThread(const ProxyConfig& proxy_config) {
    295     DCHECK(OnNetworkThread());
    296     proxy_config_ = proxy_config;
    297     FOR_EACH_OBSERVER(Observer, observers_,
    298                       OnProxyConfigChanged(proxy_config,
    299                                            ProxyConfigService::CONFIG_VALID));
    300   }
    301 
    302   bool OnJNIThread() const {
    303     return jni_task_runner_->RunsTasksOnCurrentThread();
    304   }
    305 
    306   bool OnNetworkThread() const {
    307     return network_task_runner_->RunsTasksOnCurrentThread();
    308   }
    309 
    310   ScopedJavaGlobalRef<jobject> java_proxy_change_listener_;
    311 
    312   JNIDelegateImpl jni_delegate_;
    313   ObserverList<Observer> observers_;
    314   scoped_refptr<base::SequencedTaskRunner> network_task_runner_;
    315   scoped_refptr<base::SequencedTaskRunner> jni_task_runner_;
    316   GetPropertyCallback get_property_callback_;
    317   ProxyConfig proxy_config_;
    318 
    319   DISALLOW_COPY_AND_ASSIGN(Delegate);
    320 };
    321 
    322 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid(
    323     base::SequencedTaskRunner* network_task_runner,
    324     base::SequencedTaskRunner* jni_task_runner)
    325     : delegate_(new Delegate(
    326         network_task_runner, jni_task_runner, base::Bind(&GetJavaProperty))) {
    327   delegate_->SetupJNI();
    328   delegate_->FetchInitialConfig();
    329 }
    330 
    331 ProxyConfigServiceAndroid::~ProxyConfigServiceAndroid() {
    332   delegate_->Shutdown();
    333 }
    334 
    335 // static
    336 bool ProxyConfigServiceAndroid::Register(JNIEnv* env) {
    337   return RegisterNativesImpl(env);
    338 }
    339 
    340 void ProxyConfigServiceAndroid::AddObserver(Observer* observer) {
    341   delegate_->AddObserver(observer);
    342 }
    343 
    344 void ProxyConfigServiceAndroid::RemoveObserver(Observer* observer) {
    345   delegate_->RemoveObserver(observer);
    346 }
    347 
    348 ProxyConfigService::ConfigAvailability
    349 ProxyConfigServiceAndroid::GetLatestProxyConfig(ProxyConfig* config) {
    350   return delegate_->GetLatestProxyConfig(config);
    351 }
    352 
    353 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid(
    354     base::SequencedTaskRunner* network_task_runner,
    355     base::SequencedTaskRunner* jni_task_runner,
    356     GetPropertyCallback get_property_callback)
    357     : delegate_(new Delegate(
    358         network_task_runner, jni_task_runner, get_property_callback)) {
    359   delegate_->SetupJNI();
    360   delegate_->FetchInitialConfig();
    361 }
    362 
    363 void ProxyConfigServiceAndroid::ProxySettingsChanged() {
    364   delegate_->ProxySettingsChanged();
    365 }
    366 
    367 } // namespace net
    368