1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ProxySelectorRoutePlanner.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,287 @@ 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.impl.conn; 1.32 + 1.33 + 1.34 +import java.net.InetAddress; 1.35 +import java.net.InetSocketAddress; 1.36 +import java.net.Proxy; 1.37 +import java.net.ProxySelector; 1.38 +import java.net.URI; 1.39 +import java.net.URISyntaxException; 1.40 +import java.util.List; 1.41 + 1.42 +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; 1.43 +import ch.boye.httpclientandroidlib.HttpException; 1.44 +import ch.boye.httpclientandroidlib.HttpHost; 1.45 +import ch.boye.httpclientandroidlib.HttpRequest; 1.46 +import ch.boye.httpclientandroidlib.protocol.HttpContext; 1.47 + 1.48 +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; 1.49 +import ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner; 1.50 +import ch.boye.httpclientandroidlib.conn.scheme.Scheme; 1.51 +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; 1.52 + 1.53 +import ch.boye.httpclientandroidlib.conn.params.ConnRouteParams; 1.54 + 1.55 + 1.56 +/** 1.57 + * Default implementation of an {@link HttpRoutePlanner}. 1.58 + * This implementation is based on {@link java.net.ProxySelector}. 1.59 + * By default, it will pick up the proxy settings of the JVM, either 1.60 + * from system properties or from the browser running the application. 1.61 + * Additionally, it interprets some 1.62 + * {@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames parameters}, 1.63 + * though not the {@link 1.64 + * ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#DEFAULT_PROXY DEFAULT_PROXY}. 1.65 + * <p> 1.66 + * The following parameters can be used to customize the behavior of this 1.67 + * class: 1.68 + * <ul> 1.69 + * <li>{@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#LOCAL_ADDRESS}</li> 1.70 + * <li>{@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#FORCED_ROUTE}</li> 1.71 + * </ul> 1.72 + * 1.73 + * @since 4.0 1.74 + */ 1.75 +@NotThreadSafe // e.g [gs]etProxySelector() 1.76 +public class ProxySelectorRoutePlanner implements HttpRoutePlanner { 1.77 + 1.78 + /** The scheme registry. */ 1.79 + protected final SchemeRegistry schemeRegistry; // @ThreadSafe 1.80 + 1.81 + /** The proxy selector to use, or <code>null</code> for system default. */ 1.82 + protected ProxySelector proxySelector; 1.83 + 1.84 + /** 1.85 + * Creates a new proxy selector route planner. 1.86 + * 1.87 + * @param schreg the scheme registry 1.88 + * @param prosel the proxy selector, or 1.89 + * <code>null</code> for the system default 1.90 + */ 1.91 + public ProxySelectorRoutePlanner(SchemeRegistry schreg, 1.92 + ProxySelector prosel) { 1.93 + 1.94 + if (schreg == null) { 1.95 + throw new IllegalArgumentException 1.96 + ("SchemeRegistry must not be null."); 1.97 + } 1.98 + schemeRegistry = schreg; 1.99 + proxySelector = prosel; 1.100 + } 1.101 + 1.102 + /** 1.103 + * Obtains the proxy selector to use. 1.104 + * 1.105 + * @return the proxy selector, or <code>null</code> for the system default 1.106 + */ 1.107 + public ProxySelector getProxySelector() { 1.108 + return this.proxySelector; 1.109 + } 1.110 + 1.111 + /** 1.112 + * Sets the proxy selector to use. 1.113 + * 1.114 + * @param prosel the proxy selector, or 1.115 + * <code>null</code> to use the system default 1.116 + */ 1.117 + public void setProxySelector(ProxySelector prosel) { 1.118 + this.proxySelector = prosel; 1.119 + } 1.120 + 1.121 + public HttpRoute determineRoute(HttpHost target, 1.122 + HttpRequest request, 1.123 + HttpContext context) 1.124 + throws HttpException { 1.125 + 1.126 + if (request == null) { 1.127 + throw new IllegalStateException 1.128 + ("Request must not be null."); 1.129 + } 1.130 + 1.131 + // If we have a forced route, we can do without a target. 1.132 + HttpRoute route = 1.133 + ConnRouteParams.getForcedRoute(request.getParams()); 1.134 + if (route != null) 1.135 + return route; 1.136 + 1.137 + // If we get here, there is no forced route. 1.138 + // So we need a target to compute a route. 1.139 + 1.140 + if (target == null) { 1.141 + throw new IllegalStateException 1.142 + ("Target host must not be null."); 1.143 + } 1.144 + 1.145 + final InetAddress local = 1.146 + ConnRouteParams.getLocalAddress(request.getParams()); 1.147 + final HttpHost proxy = determineProxy(target, request, context); 1.148 + 1.149 + final Scheme schm = 1.150 + this.schemeRegistry.getScheme(target.getSchemeName()); 1.151 + // as it is typically used for TLS/SSL, we assume that 1.152 + // a layered scheme implies a secure connection 1.153 + final boolean secure = schm.isLayered(); 1.154 + 1.155 + if (proxy == null) { 1.156 + route = new HttpRoute(target, local, secure); 1.157 + } else { 1.158 + route = new HttpRoute(target, local, proxy, secure); 1.159 + } 1.160 + return route; 1.161 + } 1.162 + 1.163 + /** 1.164 + * Determines a proxy for the given target. 1.165 + * 1.166 + * @param target the planned target, never <code>null</code> 1.167 + * @param request the request to be sent, never <code>null</code> 1.168 + * @param context the context, or <code>null</code> 1.169 + * 1.170 + * @return the proxy to use, or <code>null</code> for a direct route 1.171 + * 1.172 + * @throws HttpException 1.173 + * in case of system proxy settings that cannot be handled 1.174 + */ 1.175 + protected HttpHost determineProxy(HttpHost target, 1.176 + HttpRequest request, 1.177 + HttpContext context) 1.178 + throws HttpException { 1.179 + 1.180 + // the proxy selector can be 'unset', so we better deal with null here 1.181 + ProxySelector psel = this.proxySelector; 1.182 + if (psel == null) 1.183 + psel = ProxySelector.getDefault(); 1.184 + if (psel == null) 1.185 + return null; 1.186 + 1.187 + URI targetURI = null; 1.188 + try { 1.189 + targetURI = new URI(target.toURI()); 1.190 + } catch (URISyntaxException usx) { 1.191 + throw new HttpException 1.192 + ("Cannot convert host to URI: " + target, usx); 1.193 + } 1.194 + List<Proxy> proxies = psel.select(targetURI); 1.195 + 1.196 + Proxy p = chooseProxy(proxies, target, request, context); 1.197 + 1.198 + HttpHost result = null; 1.199 + if (p.type() == Proxy.Type.HTTP) { 1.200 + // convert the socket address to an HttpHost 1.201 + if (!(p.address() instanceof InetSocketAddress)) { 1.202 + throw new HttpException 1.203 + ("Unable to handle non-Inet proxy address: "+p.address()); 1.204 + } 1.205 + final InetSocketAddress isa = (InetSocketAddress) p.address(); 1.206 + // assume default scheme (http) 1.207 + result = new HttpHost(getHost(isa), isa.getPort()); 1.208 + } 1.209 + 1.210 + return result; 1.211 + } 1.212 + 1.213 + /** 1.214 + * Obtains a host from an {@link InetSocketAddress}. 1.215 + * 1.216 + * @param isa the socket address 1.217 + * 1.218 + * @return a host string, either as a symbolic name or 1.219 + * as a literal IP address string 1.220 + * <br/> 1.221 + * (TODO: determine format for IPv6 addresses, with or without [brackets]) 1.222 + */ 1.223 + protected String getHost(InetSocketAddress isa) { 1.224 + 1.225 + //@@@ Will this work with literal IPv6 addresses, or do we 1.226 + //@@@ need to wrap these in [] for the string representation? 1.227 + //@@@ Having it in this method at least allows for easy workarounds. 1.228 + return isa.isUnresolved() ? 1.229 + isa.getHostName() : isa.getAddress().getHostAddress(); 1.230 + 1.231 + } 1.232 + 1.233 + /** 1.234 + * Chooses a proxy from a list of available proxies. 1.235 + * The default implementation just picks the first non-SOCKS proxy 1.236 + * from the list. If there are only SOCKS proxies, 1.237 + * {@link Proxy#NO_PROXY Proxy.NO_PROXY} is returned. 1.238 + * Derived classes may implement more advanced strategies, 1.239 + * such as proxy rotation if there are multiple options. 1.240 + * 1.241 + * @param proxies the list of proxies to choose from, 1.242 + * never <code>null</code> or empty 1.243 + * @param target the planned target, never <code>null</code> 1.244 + * @param request the request to be sent, never <code>null</code> 1.245 + * @param context the context, or <code>null</code> 1.246 + * 1.247 + * @return a proxy type 1.248 + */ 1.249 + protected Proxy chooseProxy(List<Proxy> proxies, 1.250 + HttpHost target, 1.251 + HttpRequest request, 1.252 + HttpContext context) { 1.253 + 1.254 + if ((proxies == null) || proxies.isEmpty()) { 1.255 + throw new IllegalArgumentException 1.256 + ("Proxy list must not be empty."); 1.257 + } 1.258 + 1.259 + Proxy result = null; 1.260 + 1.261 + // check the list for one we can use 1.262 + for (int i=0; (result == null) && (i < proxies.size()); i++) { 1.263 + 1.264 + Proxy p = proxies.get(i); 1.265 + switch (p.type()) { 1.266 + 1.267 + case DIRECT: 1.268 + case HTTP: 1.269 + result = p; 1.270 + break; 1.271 + 1.272 + case SOCKS: 1.273 + // SOCKS hosts are not handled on the route level. 1.274 + // The socket may make use of the SOCKS host though. 1.275 + break; 1.276 + } 1.277 + } 1.278 + 1.279 + if (result == null) { 1.280 + //@@@ log as warning or info that only a socks proxy is available? 1.281 + // result can only be null if all proxies are socks proxies 1.282 + // socks proxies are not handled on the route planning level 1.283 + result = Proxy.NO_PROXY; 1.284 + } 1.285 + 1.286 + return result; 1.287 + } 1.288 + 1.289 +} 1.290 +