Home | History | Annotate | Download | only in routing
      1 /*
      2  * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/routing/HttpRoute.java $
      3  * $Revision: 653041 $
      4  * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 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.conn.routing;
     33 
     34 import java.net.InetAddress;
     35 
     36 import org.apache.http.HttpHost;
     37 
     38 /**
     39  * The route for a request.
     40  * Instances of this class are unmodifiable and therefore suitable
     41  * for use as lookup keys.
     42  *
     43  * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
     44  *
     45  *
     46  * <!-- empty lines to avoid svn diff problems -->
     47  * @version $Revision: 653041 $
     48  *
     49  * @since 4.0
     50  */
     51 public final class HttpRoute implements RouteInfo, Cloneable {
     52 
     53     /** The target host to connect to. */
     54     private final HttpHost targetHost;
     55 
     56     /**
     57      * The local address to connect from.
     58      * <code>null</code> indicates that the default should be used.
     59      */
     60     private final InetAddress localAddress;
     61 
     62     /** The proxy servers, if any. */
     63     private final HttpHost[] proxyChain;
     64 
     65     /** Whether the the route is tunnelled through the proxy. */
     66     private final TunnelType tunnelled;
     67 
     68     /** Whether the route is layered. */
     69     private final LayerType layered;
     70 
     71     /** Whether the route is (supposed to be) secure. */
     72     private final boolean secure;
     73 
     74 
     75     /**
     76      * Internal, fully-specified constructor.
     77      * This constructor does <i>not</i> clone the proxy chain array,
     78      * nor test it for <code>null</code> elements. This conversion and
     79      * check is the responsibility of the public constructors.
     80      * The order of arguments here is different from the similar public
     81      * constructor, as required by Java.
     82      *
     83      * @param local     the local address to route from, or
     84      *                  <code>null</code> for the default
     85      * @param target    the host to which to route
     86      * @param proxies   the proxy chain to use, or
     87      *                  <code>null</code> for a direct route
     88      * @param secure    <code>true</code> if the route is (to be) secure,
     89      *                  <code>false</code> otherwise
     90      * @param tunnelled the tunnel type of this route, or
     91      *                  <code>null</code> for PLAIN
     92      * @param layered   the layering type of this route, or
     93      *                  <code>null</code> for PLAIN
     94      */
     95     private HttpRoute(InetAddress local,
     96                       HttpHost target, HttpHost[] proxies,
     97                       boolean secure,
     98                       TunnelType tunnelled, LayerType layered) {
     99         if (target == null) {
    100             throw new IllegalArgumentException
    101                 ("Target host may not be null.");
    102         }
    103         if ((tunnelled == TunnelType.TUNNELLED) && (proxies == null)) {
    104             throw new IllegalArgumentException
    105                 ("Proxy required if tunnelled.");
    106         }
    107 
    108         // tunnelled is already checked above, that is in line with the default
    109         if (tunnelled == null)
    110             tunnelled = TunnelType.PLAIN;
    111         if (layered == null)
    112             layered = LayerType.PLAIN;
    113 
    114         this.targetHost   = target;
    115         this.localAddress = local;
    116         this.proxyChain   = proxies;
    117         this.secure       = secure;
    118         this.tunnelled    = tunnelled;
    119         this.layered      = layered;
    120     }
    121 
    122 
    123     /**
    124      * Creates a new route with all attributes specified explicitly.
    125      *
    126      * @param target    the host to which to route
    127      * @param local     the local address to route from, or
    128      *                  <code>null</code> for the default
    129      * @param proxies   the proxy chain to use, or
    130      *                  <code>null</code> for a direct route
    131      * @param secure    <code>true</code> if the route is (to be) secure,
    132      *                  <code>false</code> otherwise
    133      * @param tunnelled the tunnel type of this route
    134      * @param layered   the layering type of this route
    135      */
    136     public HttpRoute(HttpHost target, InetAddress local, HttpHost[] proxies,
    137                      boolean secure, TunnelType tunnelled, LayerType layered) {
    138         this(local, target, toChain(proxies), secure, tunnelled, layered);
    139     }
    140 
    141 
    142     /**
    143      * Creates a new route with at most one proxy.
    144      *
    145      * @param target    the host to which to route
    146      * @param local     the local address to route from, or
    147      *                  <code>null</code> for the default
    148      * @param proxy     the proxy to use, or
    149      *                  <code>null</code> for a direct route
    150      * @param secure    <code>true</code> if the route is (to be) secure,
    151      *                  <code>false</code> otherwise
    152      * @param tunnelled <code>true</code> if the route is (to be) tunnelled
    153      *                  via the proxy,
    154      *                  <code>false</code> otherwise
    155      * @param layered   <code>true</code> if the route includes a
    156      *                  layered protocol,
    157      *                  <code>false</code> otherwise
    158      */
    159     public HttpRoute(HttpHost target, InetAddress local, HttpHost proxy,
    160                      boolean secure, TunnelType tunnelled, LayerType layered) {
    161         this(local, target, toChain(proxy), secure, tunnelled, layered);
    162     }
    163 
    164 
    165     /**
    166      * Creates a new direct route.
    167      * That is a route without a proxy.
    168      *
    169      * @param target    the host to which to route
    170      * @param local     the local address to route from, or
    171      *                  <code>null</code> for the default
    172      * @param secure    <code>true</code> if the route is (to be) secure,
    173      *                  <code>false</code> otherwise
    174      */
    175     public HttpRoute(HttpHost target, InetAddress local, boolean secure) {
    176         this(local, target, null, secure, TunnelType.PLAIN, LayerType.PLAIN);
    177     }
    178 
    179 
    180     /**
    181      * Creates a new direct insecure route.
    182      *
    183      * @param target    the host to which to route
    184      */
    185     public HttpRoute(HttpHost target) {
    186         this(null, target, null, false, TunnelType.PLAIN, LayerType.PLAIN);
    187     }
    188 
    189 
    190     /**
    191      * Creates a new route through a proxy.
    192      * When using this constructor, the <code>proxy</code> MUST be given.
    193      * For convenience, it is assumed that a secure connection will be
    194      * layered over a tunnel through the proxy.
    195      *
    196      * @param target    the host to which to route
    197      * @param local     the local address to route from, or
    198      *                  <code>null</code> for the default
    199      * @param proxy     the proxy to use
    200      * @param secure    <code>true</code> if the route is (to be) secure,
    201      *                  <code>false</code> otherwise
    202      */
    203     public HttpRoute(HttpHost target, InetAddress local, HttpHost proxy,
    204                      boolean secure) {
    205         this(local, target, toChain(proxy), secure,
    206              secure ? TunnelType.TUNNELLED : TunnelType.PLAIN,
    207              secure ? LayerType.LAYERED    : LayerType.PLAIN);
    208         if (proxy == null) {
    209             throw new IllegalArgumentException
    210                 ("Proxy host may not be null.");
    211         }
    212     }
    213 
    214 
    215     /**
    216      * Helper to convert a proxy to a proxy chain.
    217      *
    218      * @param proxy     the only proxy in the chain, or <code>null</code>
    219      *
    220      * @return  a proxy chain array, or <code>null</code>
    221      */
    222     private static HttpHost[] toChain(HttpHost proxy) {
    223         if (proxy == null)
    224             return null;
    225 
    226         return new HttpHost[]{ proxy };
    227     }
    228 
    229 
    230     /**
    231      * Helper to duplicate and check a proxy chain.
    232      * An empty proxy chain is converted to <code>null</code>.
    233      *
    234      * @param proxies   the proxy chain to duplicate, or <code>null</code>
    235      *
    236      * @return  a new proxy chain array, or <code>null</code>
    237      */
    238     private static HttpHost[] toChain(HttpHost[] proxies) {
    239         if ((proxies == null) || (proxies.length < 1))
    240             return null;
    241 
    242         for (HttpHost proxy : proxies) {
    243             if (proxy == null)
    244                 throw new IllegalArgumentException
    245                         ("Proxy chain may not contain null elements.");
    246         }
    247 
    248         // copy the proxy chain, the traditional way
    249         HttpHost[] result = new HttpHost[proxies.length];
    250         System.arraycopy(proxies, 0, result, 0, proxies.length);
    251 
    252         return result;
    253     }
    254 
    255 
    256 
    257     // non-JavaDoc, see interface RouteInfo
    258     public final HttpHost getTargetHost() {
    259         return this.targetHost;
    260     }
    261 
    262 
    263     // non-JavaDoc, see interface RouteInfo
    264     public final InetAddress getLocalAddress() {
    265         return this.localAddress;
    266     }
    267 
    268 
    269     // non-JavaDoc, see interface RouteInfo
    270     public final int getHopCount() {
    271         return (proxyChain == null) ? 1 : (proxyChain.length+1);
    272     }
    273 
    274 
    275     // non-JavaDoc, see interface RouteInfo
    276     public final HttpHost getHopTarget(int hop) {
    277         if (hop < 0)
    278             throw new IllegalArgumentException
    279                 ("Hop index must not be negative: " + hop);
    280         final int hopcount = getHopCount();
    281         if (hop >= hopcount)
    282             throw new IllegalArgumentException
    283                 ("Hop index " + hop +
    284                  " exceeds route length " + hopcount);
    285 
    286         HttpHost result = null;
    287         if (hop < hopcount-1)
    288             result = this.proxyChain[hop];
    289         else
    290             result = this.targetHost;
    291 
    292         return result;
    293     }
    294 
    295 
    296     // non-JavaDoc, see interface RouteInfo
    297     public final HttpHost getProxyHost() {
    298         return (this.proxyChain == null) ? null : this.proxyChain[0];
    299     }
    300 
    301 
    302     // non-JavaDoc, see interface RouteInfo
    303     public final TunnelType getTunnelType() {
    304         return this.tunnelled;
    305     }
    306 
    307 
    308     // non-JavaDoc, see interface RouteInfo
    309     public final boolean isTunnelled() {
    310         return (this.tunnelled == TunnelType.TUNNELLED);
    311     }
    312 
    313 
    314     // non-JavaDoc, see interface RouteInfo
    315     public final LayerType getLayerType() {
    316         return this.layered;
    317     }
    318 
    319 
    320     // non-JavaDoc, see interface RouteInfo
    321     public final boolean isLayered() {
    322         return (this.layered == LayerType.LAYERED);
    323     }
    324 
    325 
    326     // non-JavaDoc, see interface RouteInfo
    327     public final boolean isSecure() {
    328         return this.secure;
    329     }
    330 
    331 
    332     /**
    333      * Compares this route to another.
    334      *
    335      * @param o         the object to compare with
    336      *
    337      * @return  <code>true</code> if the argument is the same route,
    338      *          <code>false</code>
    339      */
    340     @Override
    341     public final boolean equals(Object o) {
    342         if (o == this)
    343             return true;
    344         if (!(o instanceof HttpRoute))
    345             return false;
    346 
    347         HttpRoute that = (HttpRoute) o;
    348         boolean equal = this.targetHost.equals(that.targetHost);
    349         equal &=
    350             ( this.localAddress == that.localAddress) ||
    351             ((this.localAddress != null) &&
    352               this.localAddress.equals(that.localAddress));
    353         equal &=
    354             ( this.proxyChain        == that.proxyChain) ||
    355             ((this.proxyChain        != null) &&
    356              (that.proxyChain        != null) &&
    357              (this.proxyChain.length == that.proxyChain.length));
    358         // comparison of actual proxies follows below
    359         equal &=
    360             (this.secure    == that.secure) &&
    361             (this.tunnelled == that.tunnelled) &&
    362             (this.layered   == that.layered);
    363 
    364         // chain length has been compared above, now check the proxies
    365         if (equal && (this.proxyChain != null)) {
    366             for (int i=0; equal && (i<this.proxyChain.length); i++)
    367                 equal = this.proxyChain[i].equals(that.proxyChain[i]);
    368         }
    369 
    370         return equal;
    371     }
    372 
    373 
    374     /**
    375      * Generates a hash code for this route.
    376      *
    377      * @return  the hash code
    378      */
    379     @Override
    380     public final int hashCode() {
    381 
    382         int hc = this.targetHost.hashCode();
    383 
    384         if (this.localAddress != null)
    385             hc ^= localAddress.hashCode();
    386         if (this.proxyChain != null) {
    387             hc ^= proxyChain.length;
    388             for (HttpHost aProxyChain : proxyChain) hc ^= aProxyChain.hashCode();
    389         }
    390 
    391         if (this.secure)
    392             hc ^= 0x11111111;
    393 
    394         hc ^= this.tunnelled.hashCode();
    395         hc ^= this.layered.hashCode();
    396 
    397         return hc;
    398     }
    399 
    400 
    401     /**
    402      * Obtains a description of this route.
    403      *
    404      * @return  a human-readable representation of this route
    405      */
    406     @Override
    407     public final String toString() {
    408         StringBuilder cab = new StringBuilder(50 + getHopCount()*30);
    409 
    410         cab.append("HttpRoute[");
    411         if (this.localAddress != null) {
    412             cab.append(this.localAddress);
    413             cab.append("->");
    414         }
    415         cab.append('{');
    416         if (this.tunnelled == TunnelType.TUNNELLED)
    417             cab.append('t');
    418         if (this.layered == LayerType.LAYERED)
    419             cab.append('l');
    420         if (this.secure)
    421             cab.append('s');
    422         cab.append("}->");
    423         if (this.proxyChain != null) {
    424             for (HttpHost aProxyChain : this.proxyChain) {
    425                 cab.append(aProxyChain);
    426                 cab.append("->");
    427             }
    428         }
    429         cab.append(this.targetHost);
    430         cab.append(']');
    431 
    432         return cab.toString();
    433     }
    434 
    435 
    436     // default implementation of clone() is sufficient
    437     @Override
    438     public Object clone() throws CloneNotSupportedException {
    439         return super.clone();
    440     }
    441 
    442 
    443 } // class HttpRoute
    444