1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/conn/routing/RouteTracker.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,376 @@ 1.4 +/* 1.5 + * ==================================================================== 1.6 + * Licensed to the Apache Software Foundation (ASF) under one 1.7 + * or more contributor license agreements. See the NOTICE file 1.8 + * distributed with this work for additional information 1.9 + * regarding copyright ownership. The ASF licenses this file 1.10 + * to you under the Apache License, Version 2.0 (the 1.11 + * "License"); you may not use this file except in compliance 1.12 + * with the License. You may obtain a copy of the License at 1.13 + * 1.14 + * http://www.apache.org/licenses/LICENSE-2.0 1.15 + * 1.16 + * Unless required by applicable law or agreed to in writing, 1.17 + * software distributed under the License is distributed on an 1.18 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 1.19 + * KIND, either express or implied. See the License for the 1.20 + * specific language governing permissions and limitations 1.21 + * under the License. 1.22 + * ==================================================================== 1.23 + * 1.24 + * This software consists of voluntary contributions made by many 1.25 + * individuals on behalf of the Apache Software Foundation. For more 1.26 + * information on the Apache Software Foundation, please see 1.27 + * <http://www.apache.org/>. 1.28 + * 1.29 + */ 1.30 + 1.31 +package ch.boye.httpclientandroidlib.conn.routing; 1.32 + 1.33 +import java.net.InetAddress; 1.34 + 1.35 +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; 1.36 +import ch.boye.httpclientandroidlib.util.LangUtils; 1.37 + 1.38 +import ch.boye.httpclientandroidlib.HttpHost; 1.39 + 1.40 +/** 1.41 + * Helps tracking the steps in establishing a route. 1.42 + * 1.43 + * @since 4.0 1.44 + */ 1.45 +@NotThreadSafe 1.46 +public final class RouteTracker implements RouteInfo, Cloneable { 1.47 + 1.48 + /** The target host to connect to. */ 1.49 + private final HttpHost targetHost; 1.50 + 1.51 + /** 1.52 + * The local address to connect from. 1.53 + * <code>null</code> indicates that the default should be used. 1.54 + */ 1.55 + private final InetAddress localAddress; 1.56 + 1.57 + // the attributes above are fixed at construction time 1.58 + // now follow attributes that indicate the established route 1.59 + 1.60 + /** Whether the first hop of the route is established. */ 1.61 + private boolean connected; 1.62 + 1.63 + /** The proxy chain, if any. */ 1.64 + private HttpHost[] proxyChain; 1.65 + 1.66 + /** Whether the the route is tunnelled end-to-end through proxies. */ 1.67 + private TunnelType tunnelled; 1.68 + 1.69 + /** Whether the route is layered over a tunnel. */ 1.70 + private LayerType layered; 1.71 + 1.72 + /** Whether the route is secure. */ 1.73 + private boolean secure; 1.74 + 1.75 + /** 1.76 + * Creates a new route tracker. 1.77 + * The target and origin need to be specified at creation time. 1.78 + * 1.79 + * @param target the host to which to route 1.80 + * @param local the local address to route from, or 1.81 + * <code>null</code> for the default 1.82 + */ 1.83 + public RouteTracker(HttpHost target, InetAddress local) { 1.84 + if (target == null) { 1.85 + throw new IllegalArgumentException("Target host may not be null."); 1.86 + } 1.87 + this.targetHost = target; 1.88 + this.localAddress = local; 1.89 + this.tunnelled = TunnelType.PLAIN; 1.90 + this.layered = LayerType.PLAIN; 1.91 + } 1.92 + 1.93 + 1.94 + /** 1.95 + * Creates a new tracker for the given route. 1.96 + * Only target and origin are taken from the route, 1.97 + * everything else remains to be tracked. 1.98 + * 1.99 + * @param route the route to track 1.100 + */ 1.101 + public RouteTracker(HttpRoute route) { 1.102 + this(route.getTargetHost(), route.getLocalAddress()); 1.103 + } 1.104 + 1.105 + /** 1.106 + * Tracks connecting to the target. 1.107 + * 1.108 + * @param secure <code>true</code> if the route is secure, 1.109 + * <code>false</code> otherwise 1.110 + */ 1.111 + public final void connectTarget(boolean secure) { 1.112 + if (this.connected) { 1.113 + throw new IllegalStateException("Already connected."); 1.114 + } 1.115 + this.connected = true; 1.116 + this.secure = secure; 1.117 + } 1.118 + 1.119 + /** 1.120 + * Tracks connecting to the first proxy. 1.121 + * 1.122 + * @param proxy the proxy connected to 1.123 + * @param secure <code>true</code> if the route is secure, 1.124 + * <code>false</code> otherwise 1.125 + */ 1.126 + public final void connectProxy(HttpHost proxy, boolean secure) { 1.127 + if (proxy == null) { 1.128 + throw new IllegalArgumentException("Proxy host may not be null."); 1.129 + } 1.130 + if (this.connected) { 1.131 + throw new IllegalStateException("Already connected."); 1.132 + } 1.133 + this.connected = true; 1.134 + this.proxyChain = new HttpHost[]{ proxy }; 1.135 + this.secure = secure; 1.136 + } 1.137 + 1.138 + /** 1.139 + * Tracks tunnelling to the target. 1.140 + * 1.141 + * @param secure <code>true</code> if the route is secure, 1.142 + * <code>false</code> otherwise 1.143 + */ 1.144 + public final void tunnelTarget(boolean secure) { 1.145 + if (!this.connected) { 1.146 + throw new IllegalStateException("No tunnel unless connected."); 1.147 + } 1.148 + if (this.proxyChain == null) { 1.149 + throw new IllegalStateException("No tunnel without proxy."); 1.150 + } 1.151 + this.tunnelled = TunnelType.TUNNELLED; 1.152 + this.secure = secure; 1.153 + } 1.154 + 1.155 + /** 1.156 + * Tracks tunnelling to a proxy in a proxy chain. 1.157 + * This will extend the tracked proxy chain, but it does not mark 1.158 + * the route as tunnelled. Only end-to-end tunnels are considered there. 1.159 + * 1.160 + * @param proxy the proxy tunnelled to 1.161 + * @param secure <code>true</code> if the route is secure, 1.162 + * <code>false</code> otherwise 1.163 + */ 1.164 + public final void tunnelProxy(HttpHost proxy, boolean secure) { 1.165 + if (proxy == null) { 1.166 + throw new IllegalArgumentException("Proxy host may not be null."); 1.167 + } 1.168 + if (!this.connected) { 1.169 + throw new IllegalStateException("No tunnel unless connected."); 1.170 + } 1.171 + if (this.proxyChain == null) { 1.172 + throw new IllegalStateException("No proxy tunnel without proxy."); 1.173 + } 1.174 + 1.175 + // prepare an extended proxy chain 1.176 + HttpHost[] proxies = new HttpHost[this.proxyChain.length+1]; 1.177 + System.arraycopy(this.proxyChain, 0, 1.178 + proxies, 0, this.proxyChain.length); 1.179 + proxies[proxies.length-1] = proxy; 1.180 + 1.181 + this.proxyChain = proxies; 1.182 + this.secure = secure; 1.183 + } 1.184 + 1.185 + /** 1.186 + * Tracks layering a protocol. 1.187 + * 1.188 + * @param secure <code>true</code> if the route is secure, 1.189 + * <code>false</code> otherwise 1.190 + */ 1.191 + public final void layerProtocol(boolean secure) { 1.192 + // it is possible to layer a protocol over a direct connection, 1.193 + // although this case is probably not considered elsewhere 1.194 + if (!this.connected) { 1.195 + throw new IllegalStateException 1.196 + ("No layered protocol unless connected."); 1.197 + } 1.198 + this.layered = LayerType.LAYERED; 1.199 + this.secure = secure; 1.200 + } 1.201 + 1.202 + public final HttpHost getTargetHost() { 1.203 + return this.targetHost; 1.204 + } 1.205 + 1.206 + public final InetAddress getLocalAddress() { 1.207 + return this.localAddress; 1.208 + } 1.209 + 1.210 + public final int getHopCount() { 1.211 + int hops = 0; 1.212 + if (this.connected) { 1.213 + if (proxyChain == null) 1.214 + hops = 1; 1.215 + else 1.216 + hops = proxyChain.length + 1; 1.217 + } 1.218 + return hops; 1.219 + } 1.220 + 1.221 + public final HttpHost getHopTarget(int hop) { 1.222 + if (hop < 0) 1.223 + throw new IllegalArgumentException 1.224 + ("Hop index must not be negative: " + hop); 1.225 + final int hopcount = getHopCount(); 1.226 + if (hop >= hopcount) { 1.227 + throw new IllegalArgumentException 1.228 + ("Hop index " + hop + 1.229 + " exceeds tracked route length " + hopcount +"."); 1.230 + } 1.231 + 1.232 + HttpHost result = null; 1.233 + if (hop < hopcount-1) 1.234 + result = this.proxyChain[hop]; 1.235 + else 1.236 + result = this.targetHost; 1.237 + 1.238 + return result; 1.239 + } 1.240 + 1.241 + public final HttpHost getProxyHost() { 1.242 + return (this.proxyChain == null) ? null : this.proxyChain[0]; 1.243 + } 1.244 + 1.245 + public final boolean isConnected() { 1.246 + return this.connected; 1.247 + } 1.248 + 1.249 + public final TunnelType getTunnelType() { 1.250 + return this.tunnelled; 1.251 + } 1.252 + 1.253 + public final boolean isTunnelled() { 1.254 + return (this.tunnelled == TunnelType.TUNNELLED); 1.255 + } 1.256 + 1.257 + public final LayerType getLayerType() { 1.258 + return this.layered; 1.259 + } 1.260 + 1.261 + public final boolean isLayered() { 1.262 + return (this.layered == LayerType.LAYERED); 1.263 + } 1.264 + 1.265 + public final boolean isSecure() { 1.266 + return this.secure; 1.267 + } 1.268 + 1.269 + /** 1.270 + * Obtains the tracked route. 1.271 + * If a route has been tracked, it is {@link #isConnected connected}. 1.272 + * If not connected, nothing has been tracked so far. 1.273 + * 1.274 + * @return the tracked route, or 1.275 + * <code>null</code> if nothing has been tracked so far 1.276 + */ 1.277 + public final HttpRoute toRoute() { 1.278 + return !this.connected ? 1.279 + null : new HttpRoute(this.targetHost, this.localAddress, 1.280 + this.proxyChain, this.secure, 1.281 + this.tunnelled, this.layered); 1.282 + } 1.283 + 1.284 + /** 1.285 + * Compares this tracked route to another. 1.286 + * 1.287 + * @param o the object to compare with 1.288 + * 1.289 + * @return <code>true</code> if the argument is the same tracked route, 1.290 + * <code>false</code> 1.291 + */ 1.292 + @Override 1.293 + public final boolean equals(Object o) { 1.294 + if (o == this) 1.295 + return true; 1.296 + if (!(o instanceof RouteTracker)) 1.297 + return false; 1.298 + 1.299 + RouteTracker that = (RouteTracker) o; 1.300 + return 1.301 + // Do the cheapest checks first 1.302 + (this.connected == that.connected) && 1.303 + (this.secure == that.secure) && 1.304 + (this.tunnelled == that.tunnelled) && 1.305 + (this.layered == that.layered) && 1.306 + LangUtils.equals(this.targetHost, that.targetHost) && 1.307 + LangUtils.equals(this.localAddress, that.localAddress) && 1.308 + LangUtils.equals(this.proxyChain, that.proxyChain); 1.309 + } 1.310 + 1.311 + /** 1.312 + * Generates a hash code for this tracked route. 1.313 + * Route trackers are modifiable and should therefore not be used 1.314 + * as lookup keys. Use {@link #toRoute toRoute} to obtain an 1.315 + * unmodifiable representation of the tracked route. 1.316 + * 1.317 + * @return the hash code 1.318 + */ 1.319 + @Override 1.320 + public final int hashCode() { 1.321 + int hash = LangUtils.HASH_SEED; 1.322 + hash = LangUtils.hashCode(hash, this.targetHost); 1.323 + hash = LangUtils.hashCode(hash, this.localAddress); 1.324 + if (this.proxyChain != null) { 1.325 + for (int i = 0; i < this.proxyChain.length; i++) { 1.326 + hash = LangUtils.hashCode(hash, this.proxyChain[i]); 1.327 + } 1.328 + } 1.329 + hash = LangUtils.hashCode(hash, this.connected); 1.330 + hash = LangUtils.hashCode(hash, this.secure); 1.331 + hash = LangUtils.hashCode(hash, this.tunnelled); 1.332 + hash = LangUtils.hashCode(hash, this.layered); 1.333 + return hash; 1.334 + } 1.335 + 1.336 + /** 1.337 + * Obtains a description of the tracked route. 1.338 + * 1.339 + * @return a human-readable representation of the tracked route 1.340 + */ 1.341 + @Override 1.342 + public final String toString() { 1.343 + StringBuilder cab = new StringBuilder(50 + getHopCount()*30); 1.344 + 1.345 + cab.append("RouteTracker["); 1.346 + if (this.localAddress != null) { 1.347 + cab.append(this.localAddress); 1.348 + cab.append("->"); 1.349 + } 1.350 + cab.append('{'); 1.351 + if (this.connected) 1.352 + cab.append('c'); 1.353 + if (this.tunnelled == TunnelType.TUNNELLED) 1.354 + cab.append('t'); 1.355 + if (this.layered == LayerType.LAYERED) 1.356 + cab.append('l'); 1.357 + if (this.secure) 1.358 + cab.append('s'); 1.359 + cab.append("}->"); 1.360 + if (this.proxyChain != null) { 1.361 + for (int i=0; i<this.proxyChain.length; i++) { 1.362 + cab.append(this.proxyChain[i]); 1.363 + cab.append("->"); 1.364 + } 1.365 + } 1.366 + cab.append(this.targetHost); 1.367 + cab.append(']'); 1.368 + 1.369 + return cab.toString(); 1.370 + } 1.371 + 1.372 + 1.373 + // default implementation of clone() is sufficient 1.374 + @Override 1.375 + public Object clone() throws CloneNotSupportedException { 1.376 + return super.clone(); 1.377 + } 1.378 + 1.379 +}