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