1/*
2 * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/routing/RouteTracker.java $
3 * $Revision: 620254 $
4 * $Date: 2008-02-10 02:18:48 -0800 (Sun, 10 Feb 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/**
40 * Helps tracking the steps in establishing a route.
41 *
42 * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
43 *
44 *
45 * <!-- empty lines to avoid svn diff problems -->
46 * @version $Revision: 620254 $
47 *
48 * @since 4.0
49 *
50 * @deprecated Please use {@link java.net.URL#openConnection} instead.
51 *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
52 *     for further details.
53 */
54@Deprecated
55public final class RouteTracker implements RouteInfo, Cloneable {
56
57    /** The target host to connect to. */
58    private final HttpHost targetHost;
59
60    /**
61     * The local address to connect from.
62     * <code>null</code> indicates that the default should be used.
63     */
64    private final InetAddress localAddress;
65
66    // the attributes above are fixed at construction time
67    // now follow attributes that indicate the established route
68
69    /** Whether the first hop of the route is established. */
70    private boolean connected;
71
72    /** The proxy chain, if any. */
73    private HttpHost[] proxyChain;
74
75    /** Whether the the route is tunnelled end-to-end through proxies. */
76    private TunnelType tunnelled;
77
78    /** Whether the route is layered over a tunnel. */
79    private LayerType layered;
80
81    /** Whether the route is secure. */
82    private boolean secure;
83
84
85    /**
86     * Creates a new route tracker.
87     * The target and origin need to be specified at creation time.
88     *
89     * @param target    the host to which to route
90     * @param local     the local address to route from, or
91     *                  <code>null</code> for the default
92     */
93    public RouteTracker(HttpHost target, InetAddress local) {
94        if (target == null) {
95            throw new IllegalArgumentException("Target host may not be null.");
96        }
97        this.targetHost   = target;
98        this.localAddress = local;
99        this.tunnelled    = TunnelType.PLAIN;
100        this.layered      = LayerType.PLAIN;
101    }
102
103
104    /**
105     * Creates a new tracker for the given route.
106     * Only target and origin are taken from the route,
107     * everything else remains to be tracked.
108     *
109     * @param route     the route to track
110     */
111    public RouteTracker(HttpRoute route) {
112        this(route.getTargetHost(), route.getLocalAddress());
113    }
114
115
116    /**
117     * Tracks connecting to the target.
118     *
119     * @param secure    <code>true</code> if the route is secure,
120     *                  <code>false</code> otherwise
121     */
122    public final void connectTarget(boolean secure) {
123        if (this.connected) {
124            throw new IllegalStateException("Already connected.");
125        }
126        this.connected = true;
127        this.secure = secure;
128    }
129
130
131    /**
132     * Tracks connecting to the first proxy.
133     *
134     * @param proxy     the proxy connected to
135     * @param secure    <code>true</code> if the route is secure,
136     *                  <code>false</code> otherwise
137     */
138    public final void connectProxy(HttpHost proxy, boolean secure) {
139        if (proxy == null) {
140            throw new IllegalArgumentException("Proxy host may not be null.");
141        }
142        if (this.connected) {
143            throw new IllegalStateException("Already connected.");
144        }
145        this.connected  = true;
146        this.proxyChain = new HttpHost[]{ proxy };
147        this.secure     = secure;
148    }
149
150
151    /**
152     * Tracks tunnelling to the target.
153     *
154     * @param secure    <code>true</code> if the route is secure,
155     *                  <code>false</code> otherwise
156     */
157    public final void tunnelTarget(boolean secure) {
158        if (!this.connected) {
159            throw new IllegalStateException("No tunnel unless connected.");
160        }
161        if (this.proxyChain == null) {
162            throw new IllegalStateException("No tunnel without proxy.");
163        }
164        this.tunnelled = TunnelType.TUNNELLED;
165        this.secure    = secure;
166    }
167
168
169    /**
170     * Tracks tunnelling to a proxy in a proxy chain.
171     * This will extend the tracked proxy chain, but it does not mark
172     * the route as tunnelled. Only end-to-end tunnels are considered there.
173     *
174     * @param proxy     the proxy tunnelled to
175     * @param secure    <code>true</code> if the route is secure,
176     *                  <code>false</code> otherwise
177     */
178    public final void tunnelProxy(HttpHost proxy, boolean secure) {
179        if (proxy == null) {
180            throw new IllegalArgumentException("Proxy host may not be null.");
181        }
182        if (!this.connected) {
183            throw new IllegalStateException("No tunnel unless connected.");
184        }
185        if (this.proxyChain == null) {
186            throw new IllegalStateException("No proxy tunnel without proxy.");
187        }
188
189        // prepare an extended proxy chain
190        HttpHost[] proxies = new HttpHost[this.proxyChain.length+1];
191        System.arraycopy(this.proxyChain, 0,
192                         proxies, 0, this.proxyChain.length);
193        proxies[proxies.length-1] = proxy;
194
195        this.proxyChain = proxies;
196        this.secure     = secure;
197    }
198
199
200    /**
201     * Tracks layering a protocol.
202     *
203     * @param secure    <code>true</code> if the route is secure,
204     *                  <code>false</code> otherwise
205     */
206    public final void layerProtocol(boolean secure) {
207        // it is possible to layer a protocol over a direct connection,
208        // although this case is probably not considered elsewhere
209        if (!this.connected) {
210            throw new IllegalStateException
211                ("No layered protocol unless connected.");
212        }
213        this.layered = LayerType.LAYERED;
214        this.secure  = secure;
215    }
216
217
218
219    // non-JavaDoc, see interface RouteInfo
220    public final HttpHost getTargetHost() {
221        return this.targetHost;
222    }
223
224
225    // non-JavaDoc, see interface RouteInfo
226    public final InetAddress getLocalAddress() {
227        return this.localAddress;
228    }
229
230
231    // non-JavaDoc, see interface RouteInfo
232    public final int getHopCount() {
233        int hops = 0;
234        if (this.connected) {
235            if (proxyChain == null)
236                hops = 1;
237            else
238                hops = proxyChain.length + 1;
239        }
240        return hops;
241    }
242
243
244    // non-JavaDoc, see interface RouteInfo
245    public final HttpHost getHopTarget(int hop) {
246        if (hop < 0)
247            throw new IllegalArgumentException
248                ("Hop index must not be negative: " + hop);
249        final int hopcount = getHopCount();
250        if (hop >= hopcount) {
251            throw new IllegalArgumentException
252                ("Hop index " + hop +
253                 " exceeds tracked route length " + hopcount +".");
254        }
255
256        HttpHost result = null;
257        if (hop < hopcount-1)
258            result = this.proxyChain[hop];
259        else
260            result = this.targetHost;
261
262        return result;
263    }
264
265
266    // non-JavaDoc, see interface RouteInfo
267    public final HttpHost getProxyHost() {
268        return (this.proxyChain == null) ? null : this.proxyChain[0];
269    }
270
271
272    // non-JavaDoc, see interface RouteInfo
273    public final boolean isConnected() {
274        return this.connected;
275    }
276
277
278    // non-JavaDoc, see interface RouteInfo
279    public final TunnelType getTunnelType() {
280        return this.tunnelled;
281    }
282
283
284    // non-JavaDoc, see interface RouteInfo
285    public final boolean isTunnelled() {
286        return (this.tunnelled == TunnelType.TUNNELLED);
287    }
288
289
290    // non-JavaDoc, see interface RouteInfo
291    public final LayerType getLayerType() {
292        return this.layered;
293    }
294
295
296    // non-JavaDoc, see interface RouteInfo
297    public final boolean isLayered() {
298        return (this.layered == LayerType.LAYERED);
299    }
300
301
302    // non-JavaDoc, see interface RouteInfo
303    public final boolean isSecure() {
304        return this.secure;
305    }
306
307
308    /**
309     * Obtains the tracked route.
310     * If a route has been tracked, it is {@link #isConnected connected}.
311     * If not connected, nothing has been tracked so far.
312     *
313     * @return  the tracked route, or
314     *          <code>null</code> if nothing has been tracked so far
315     */
316    public final HttpRoute toRoute() {
317        return !this.connected ?
318            null : new HttpRoute(this.targetHost, this.localAddress,
319                                 this.proxyChain, this.secure,
320                                 this.tunnelled, this.layered);
321    }
322
323
324    /**
325     * Compares this tracked route to another.
326     *
327     * @param o         the object to compare with
328     *
329     * @return  <code>true</code> if the argument is the same tracked route,
330     *          <code>false</code>
331     */
332    @Override
333    public final boolean equals(Object o) {
334        if (o == this)
335            return true;
336        if (!(o instanceof RouteTracker))
337            return false;
338
339        RouteTracker that = (RouteTracker) o;
340        boolean equal = this.targetHost.equals(that.targetHost);
341        equal &=
342            ( this.localAddress == that.localAddress) ||
343            ((this.localAddress != null) &&
344              this.localAddress.equals(that.localAddress));
345        equal &=
346            ( this.proxyChain        == that.proxyChain) ||
347            ((this.proxyChain        != null) &&
348             (that.proxyChain        != null) &&
349             (this.proxyChain.length == that.proxyChain.length));
350        // comparison of actual proxies follows below
351        equal &=
352            (this.connected == that.connected) &&
353            (this.secure    == that.secure) &&
354            (this.tunnelled == that.tunnelled) &&
355            (this.layered   == that.layered);
356
357        // chain length has been compared above, now check the proxies
358        if (equal && (this.proxyChain != null)) {
359            for (int i=0; equal && (i<this.proxyChain.length); i++)
360                equal = this.proxyChain[i].equals(that.proxyChain[i]);
361        }
362
363        return equal;
364    }
365
366
367    /**
368     * Generates a hash code for this tracked route.
369     * Route trackers are modifiable and should therefore not be used
370     * as lookup keys. Use {@link #toRoute toRoute} to obtain an
371     * unmodifiable representation of the tracked route.
372     *
373     * @return  the hash code
374     */
375    @Override
376    public final int hashCode() {
377
378        int hc = this.targetHost.hashCode();
379
380        if (this.localAddress != null)
381            hc ^= localAddress.hashCode();
382        if (this.proxyChain != null) {
383            hc ^= proxyChain.length;
384            for (int i=0; i<proxyChain.length; i++)
385                hc ^= proxyChain[i].hashCode();
386        }
387
388        if (this.connected)
389            hc ^= 0x11111111;
390        if (this.secure)
391            hc ^= 0x22222222;
392
393        hc ^= this.tunnelled.hashCode();
394        hc ^= this.layered.hashCode();
395
396        return hc;
397    }
398
399
400    /**
401     * Obtains a description of the tracked route.
402     *
403     * @return  a human-readable representation of the tracked route
404     */
405    @Override
406    public final String toString() {
407        StringBuilder cab = new StringBuilder(50 + getHopCount()*30);
408
409        cab.append("RouteTracker[");
410        if (this.localAddress != null) {
411            cab.append(this.localAddress);
412            cab.append("->");
413        }
414        cab.append('{');
415        if (this.connected)
416            cab.append('c');
417        if (this.tunnelled == TunnelType.TUNNELLED)
418            cab.append('t');
419        if (this.layered == LayerType.LAYERED)
420            cab.append('l');
421        if (this.secure)
422            cab.append('s');
423        cab.append("}->");
424        if (this.proxyChain != null) {
425            for (int i=0; i<this.proxyChain.length; i++) {
426                cab.append(this.proxyChain[i]);
427                cab.append("->");
428            }
429        }
430        cab.append(this.targetHost);
431        cab.append(']');
432
433        return cab.toString();
434    }
435
436
437    // default implementation of clone() is sufficient
438    @Override
439    public Object clone() throws CloneNotSupportedException {
440        return super.clone();
441    }
442
443
444} // class RouteTracker
445