Home | History | Annotate | Download | only in conn
      1 /*
      2  * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/ProxySelectorRoutePlanner.java $
      3  * $Revision: 658785 $
      4  * $Date: 2008-05-21 10:47:40 -0700 (Wed, 21 May 2008) $
      5  *
      6  * ====================================================================
      7  * Licensed to the Apache Software Foundation (ASF) under one
      8  * or more contributor license agreements.  See the NOTICE file
      9  * distributed with this work for additional information
     10  * regarding copyright ownership.  The ASF licenses this file
     11  * to you under the Apache License, Version 2.0 (the
     12  * "License"); you may not use this file except in compliance
     13  * with the License.  You may obtain a copy of the License at
     14  *
     15  *   http://www.apache.org/licenses/LICENSE-2.0
     16  *
     17  * Unless required by applicable law or agreed to in writing,
     18  * software distributed under the License is distributed on an
     19  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     20  * KIND, either express or implied.  See the License for the
     21  * specific language governing permissions and limitations
     22  * under the License.
     23  * ====================================================================
     24  *
     25  * This software consists of voluntary contributions made by many
     26  * individuals on behalf of the Apache Software Foundation.  For more
     27  * information on the Apache Software Foundation, please see
     28  * <http://www.apache.org/>.
     29  *
     30  */
     31 
     32 package org.apache.http.impl.conn;
     33 
     34 
     35 import java.net.InetAddress;
     36 import java.net.InetSocketAddress;
     37 import java.net.Proxy;
     38 import java.net.ProxySelector;
     39 import java.net.URI;
     40 import java.net.URISyntaxException;
     41 import java.util.List;
     42 
     43 import org.apache.http.HttpException;
     44 import org.apache.http.HttpHost;
     45 import org.apache.http.HttpRequest;
     46 import org.apache.http.protocol.HttpContext;
     47 
     48 import org.apache.http.conn.routing.HttpRoute;
     49 import org.apache.http.conn.routing.HttpRoutePlanner;
     50 import org.apache.http.conn.scheme.Scheme;
     51 import org.apache.http.conn.scheme.SchemeRegistry;
     52 
     53 import org.apache.http.conn.params.ConnRouteParams;
     54 import org.apache.http.conn.params.ConnRoutePNames;
     55 
     56 
     57 /**
     58  * Default implementation of an {@link HttpRoutePlanner}.
     59  * This implementation is based on {@link java.net.ProxySelector}.
     60  * By default, it will pick up the proxy settings of the JVM, either
     61  * from system properties or from the browser running the application.
     62  * Additionally, it interprets some
     63  * {@link org.apache.http.conn.params.ConnRoutePNames parameters},
     64  * though not the {@link
     65  * org.apache.http.conn.params.ConnRoutePNames#DEFAULT_PROXY DEFAULT_PROXY}.
     66  *
     67  * @deprecated Please use {@link java.net.URL#openConnection} instead.
     68  *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
     69  *     for further details.
     70  */
     71 @Deprecated
     72 public class ProxySelectorRoutePlanner implements HttpRoutePlanner {
     73 
     74     /** The scheme registry. */
     75     protected SchemeRegistry schemeRegistry;
     76 
     77     /** The proxy selector to use, or <code>null</code> for system default. */
     78     protected ProxySelector proxySelector;
     79 
     80 
     81     /**
     82      * Creates a new proxy selector route planner.
     83      *
     84      * @param schreg    the scheme registry
     85      * @param prosel    the proxy selector, or
     86      *                  <code>null</code> for the system default
     87      */
     88     public ProxySelectorRoutePlanner(SchemeRegistry schreg,
     89                                      ProxySelector prosel) {
     90 
     91         if (schreg == null) {
     92             throw new IllegalArgumentException
     93                 ("SchemeRegistry must not be null.");
     94         }
     95         schemeRegistry = schreg;
     96         proxySelector  = prosel;
     97     }
     98 
     99 
    100     /**
    101      * Obtains the proxy selector to use.
    102      *
    103      * @return the proxy selector, or <code>null</code> for the system default
    104      */
    105     public ProxySelector getProxySelector() {
    106         return this.proxySelector;
    107     }
    108 
    109 
    110     /**
    111      * Sets the proxy selector to use.
    112      *
    113      * @param prosel    the proxy selector, or
    114      *                  <code>null</code> to use the system default
    115      */
    116     public void setProxySelector(ProxySelector prosel) {
    117         this.proxySelector = prosel;
    118     }
    119 
    120 
    121 
    122     // non-javadoc, see interface HttpRoutePlanner
    123     public HttpRoute determineRoute(HttpHost target,
    124                                     HttpRequest request,
    125                                     HttpContext context)
    126         throws HttpException {
    127 
    128         if (request == null) {
    129             throw new IllegalStateException
    130                 ("Request must not be null.");
    131         }
    132 
    133         // If we have a forced route, we can do without a target.
    134         HttpRoute route =
    135             ConnRouteParams.getForcedRoute(request.getParams());
    136         if (route != null)
    137             return route;
    138 
    139         // If we get here, there is no forced route.
    140         // So we need a target to compute a route.
    141 
    142         if (target == null) {
    143             throw new IllegalStateException
    144                 ("Target host must not be null.");
    145         }
    146 
    147         final InetAddress local =
    148             ConnRouteParams.getLocalAddress(request.getParams());
    149 
    150         // BEGIN android-changed
    151         //     If the client or request explicitly specifies a proxy (or no
    152         //     proxy), prefer that over the ProxySelector's VM-wide default.
    153         HttpHost proxy = (HttpHost) request.getParams().getParameter(ConnRoutePNames.DEFAULT_PROXY);
    154         if (proxy == null) {
    155             proxy = determineProxy(target, request, context);
    156         } else if (ConnRouteParams.NO_HOST.equals(proxy)) {
    157             // value is explicitly unset
    158             proxy = null;
    159         }
    160         // END android-changed
    161 
    162         final Scheme schm =
    163             this.schemeRegistry.getScheme(target.getSchemeName());
    164         // as it is typically used for TLS/SSL, we assume that
    165         // a layered scheme implies a secure connection
    166         final boolean secure = schm.isLayered();
    167 
    168         if (proxy == null) {
    169             route = new HttpRoute(target, local, secure);
    170         } else {
    171             route = new HttpRoute(target, local, proxy, secure);
    172         }
    173         return route;
    174     }
    175 
    176 
    177     /**
    178      * Determines a proxy for the given target.
    179      *
    180      * @param target    the planned target, never <code>null</code>
    181      * @param request   the request to be sent, never <code>null</code>
    182      * @param context   the context, or <code>null</code>
    183      *
    184      * @return  the proxy to use, or <code>null</code> for a direct route
    185      *
    186      * @throws HttpException
    187      *         in case of system proxy settings that cannot be handled
    188      */
    189     protected HttpHost determineProxy(HttpHost    target,
    190                                       HttpRequest request,
    191                                       HttpContext context)
    192         throws HttpException {
    193 
    194         // the proxy selector can be 'unset', so we better deal with null here
    195         ProxySelector psel = this.proxySelector;
    196         if (psel == null)
    197             psel = ProxySelector.getDefault();
    198         if (psel == null)
    199             return null;
    200 
    201         URI targetURI = null;
    202         try {
    203             targetURI = new URI(target.toURI());
    204         } catch (URISyntaxException usx) {
    205             throw new HttpException
    206                 ("Cannot convert host to URI: " + target, usx);
    207         }
    208         List<Proxy> proxies = psel.select(targetURI);
    209 
    210         Proxy p = chooseProxy(proxies, target, request, context);
    211 
    212         HttpHost result = null;
    213         if (p.type() == Proxy.Type.HTTP) {
    214             // convert the socket address to an HttpHost
    215             if (!(p.address() instanceof InetSocketAddress)) {
    216                 throw new HttpException
    217                     ("Unable to handle non-Inet proxy address: "+p.address());
    218             }
    219             final InetSocketAddress isa = (InetSocketAddress) p.address();
    220             // assume default scheme (http)
    221             result = new HttpHost(getHost(isa), isa.getPort());
    222         }
    223 
    224         return result;
    225     }
    226 
    227 
    228     /**
    229      * Obtains a host from an {@link InetSocketAddress}.
    230      *
    231      * @param isa       the socket address
    232      *
    233      * @return  a host string, either as a symbolic name or
    234      *          as a literal IP address string
    235      * <br/>
    236      * (TODO: determine format for IPv6 addresses, with or without [brackets])
    237      */
    238     protected String getHost(InetSocketAddress isa) {
    239 
    240         //@@@ Will this work with literal IPv6 addresses, or do we
    241         //@@@ need to wrap these in [] for the string representation?
    242         //@@@ Having it in this method at least allows for easy workarounds.
    243        return isa.isUnresolved() ?
    244             isa.getHostName() : isa.getAddress().getHostAddress();
    245 
    246     }
    247 
    248 
    249     /*
    250      * Chooses a proxy from a list of available proxies.
    251      * The default implementation just picks the first non-SOCKS proxy
    252      * from the list. If there are only SOCKS proxies,
    253      * {@link Proxy#NO_PROXY Proxy.NO_PROXY} is returned.
    254      * Derived classes may implement more advanced strategies,
    255      * such as proxy rotation if there are multiple options.
    256      *
    257      * @param proxies   the list of proxies to choose from,
    258      *                  never <code>null</code> or empty
    259      * @param target    the planned target, never <code>null</code>
    260      * @param request   the request to be sent, never <code>null</code>
    261      * @param context   the context, or <code>null</code>
    262      *
    263      * @return  a proxy of type {@link Proxy.Type#DIRECT DIRECT}
    264      *          or {@link Proxy.Type#HTTP HTTP}, never <code>null</code>
    265      */
    266     protected Proxy chooseProxy(List<Proxy> proxies,
    267                                 HttpHost    target,
    268                                 HttpRequest request,
    269                                 HttpContext context) {
    270 
    271         if ((proxies == null) || proxies.isEmpty()) {
    272             throw new IllegalArgumentException
    273                 ("Proxy list must not be empty.");
    274         }
    275 
    276         Proxy result = null;
    277 
    278         // check the list for one we can use
    279         for (int i=0; (result == null) && (i < proxies.size()); i++) {
    280 
    281             Proxy p = proxies.get(i);
    282             switch (p.type()) {
    283 
    284             case DIRECT:
    285             case HTTP:
    286                 result = p;
    287                 break;
    288 
    289             case SOCKS:
    290                 // SOCKS hosts are not handled on the route level.
    291                 // The socket may make use of the SOCKS host though.
    292                 break;
    293             }
    294         }
    295 
    296         if (result == null) {
    297             //@@@ log as warning or info that only a socks proxy is available?
    298             // result can only be null if all proxies are socks proxies
    299             // socks proxies are not handled on the route planning level
    300             result = Proxy.NO_PROXY;
    301         }
    302 
    303         return result;
    304     }
    305 
    306 } // class ProxySelectorRoutePlanner
    307 
    308