1 /* 2 * ==================================================================== 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * ==================================================================== 20 * 21 * This software consists of voluntary contributions made by many 22 * individuals on behalf of the Apache Software Foundation. For more 23 * information on the Apache Software Foundation, please see 24 * <http://www.apache.org/>. 25 * 26 */ 27 28 package ch.boye.httpclientandroidlib.impl.conn; 29 30 31 import java.net.InetAddress; 32 import java.net.InetSocketAddress; 33 import java.net.Proxy; 34 import java.net.ProxySelector; 35 import java.net.URI; 36 import java.net.URISyntaxException; 37 import java.util.List; 38 39 import ch.boye.httpclientandroidlib.HttpException; 40 import ch.boye.httpclientandroidlib.HttpHost; 41 import ch.boye.httpclientandroidlib.HttpRequest; 42 import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; 43 import ch.boye.httpclientandroidlib.conn.params.ConnRouteParams; 44 import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; 45 import ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner; 46 import ch.boye.httpclientandroidlib.conn.scheme.Scheme; 47 import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; 48 import ch.boye.httpclientandroidlib.protocol.HttpContext; 49 import ch.boye.httpclientandroidlib.util.Args; 50 import ch.boye.httpclientandroidlib.util.Asserts; 51 52 53 /** 54 * Default implementation of an {@link HttpRoutePlanner}. 55 * This implementation is based on {@link java.net.ProxySelector}. 56 * By default, it will pick up the proxy settings of the JVM, either 57 * from system properties or from the browser running the application. 58 * Additionally, it interprets some 59 * {@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames parameters}, 60 * though not the {@link 61 * ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#DEFAULT_PROXY DEFAULT_PROXY}. 62 * <p> 63 * The following parameters can be used to customize the behavior of this 64 * class: 65 * <ul> 66 * <li>{@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#LOCAL_ADDRESS}</li> 67 * <li>{@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#FORCED_ROUTE}</li> 68 * </ul> 69 * 70 * @since 4.0 71 * 72 * @deprecated (4.3) use {@link SystemDefaultRoutePlanner} 73 */ 74 @NotThreadSafe // e.g [gs]etProxySelector() 75 @Deprecated 76 public class ProxySelectorRoutePlanner implements HttpRoutePlanner { 77 78 /** The scheme registry. */ 79 protected final SchemeRegistry schemeRegistry; // @ThreadSafe 80 81 /** The proxy selector to use, or <code>null</code> for system default. */ 82 protected ProxySelector proxySelector; 83 84 /** 85 * Creates a new proxy selector route planner. 86 * 87 * @param schreg the scheme registry 88 * @param prosel the proxy selector, or 89 * <code>null</code> for the system default 90 */ ProxySelectorRoutePlanner(final SchemeRegistry schreg, final ProxySelector prosel)91 public ProxySelectorRoutePlanner(final SchemeRegistry schreg, 92 final ProxySelector prosel) { 93 Args.notNull(schreg, "SchemeRegistry"); 94 schemeRegistry = schreg; 95 proxySelector = prosel; 96 } 97 98 /** 99 * Obtains the proxy selector to use. 100 * 101 * @return the proxy selector, or <code>null</code> for the system default 102 */ getProxySelector()103 public ProxySelector getProxySelector() { 104 return this.proxySelector; 105 } 106 107 /** 108 * Sets the proxy selector to use. 109 * 110 * @param prosel the proxy selector, or 111 * <code>null</code> to use the system default 112 */ setProxySelector(final ProxySelector prosel)113 public void setProxySelector(final ProxySelector prosel) { 114 this.proxySelector = prosel; 115 } 116 determineRoute(final HttpHost target, final HttpRequest request, final HttpContext context)117 public HttpRoute determineRoute(final HttpHost target, 118 final HttpRequest request, 119 final HttpContext context) 120 throws HttpException { 121 122 Args.notNull(request, "HTTP request"); 123 124 // If we have a forced route, we can do without a target. 125 HttpRoute route = 126 ConnRouteParams.getForcedRoute(request.getParams()); 127 if (route != null) { 128 return route; 129 } 130 131 // If we get here, there is no forced route. 132 // So we need a target to compute a route. 133 134 Asserts.notNull(target, "Target host"); 135 136 final InetAddress local = 137 ConnRouteParams.getLocalAddress(request.getParams()); 138 final HttpHost proxy = determineProxy(target, request, context); 139 140 final Scheme schm = 141 this.schemeRegistry.getScheme(target.getSchemeName()); 142 // as it is typically used for TLS/SSL, we assume that 143 // a layered scheme implies a secure connection 144 final boolean secure = schm.isLayered(); 145 146 if (proxy == null) { 147 route = new HttpRoute(target, local, secure); 148 } else { 149 route = new HttpRoute(target, local, proxy, secure); 150 } 151 return route; 152 } 153 154 /** 155 * Determines a proxy for the given target. 156 * 157 * @param target the planned target, never <code>null</code> 158 * @param request the request to be sent, never <code>null</code> 159 * @param context the context, or <code>null</code> 160 * 161 * @return the proxy to use, or <code>null</code> for a direct route 162 * 163 * @throws HttpException 164 * in case of system proxy settings that cannot be handled 165 */ determineProxy(final HttpHost target, final HttpRequest request, final HttpContext context)166 protected HttpHost determineProxy(final HttpHost target, 167 final HttpRequest request, 168 final HttpContext context) 169 throws HttpException { 170 171 // the proxy selector can be 'unset', so we better deal with null here 172 ProxySelector psel = this.proxySelector; 173 if (psel == null) { 174 psel = ProxySelector.getDefault(); 175 } 176 if (psel == null) { 177 return null; 178 } 179 180 URI targetURI = null; 181 try { 182 targetURI = new URI(target.toURI()); 183 } catch (final URISyntaxException usx) { 184 throw new HttpException 185 ("Cannot convert host to URI: " + target, usx); 186 } 187 final List<Proxy> proxies = psel.select(targetURI); 188 189 final Proxy p = chooseProxy(proxies, target, request, context); 190 191 HttpHost result = null; 192 if (p.type() == Proxy.Type.HTTP) { 193 // convert the socket address to an HttpHost 194 if (!(p.address() instanceof InetSocketAddress)) { 195 throw new HttpException 196 ("Unable to handle non-Inet proxy address: "+p.address()); 197 } 198 final InetSocketAddress isa = (InetSocketAddress) p.address(); 199 // assume default scheme (http) 200 result = new HttpHost(getHost(isa), isa.getPort()); 201 } 202 203 return result; 204 } 205 206 /** 207 * Obtains a host from an {@link InetSocketAddress}. 208 * 209 * @param isa the socket address 210 * 211 * @return a host string, either as a symbolic name or 212 * as a literal IP address string 213 * <br/> 214 * (TODO: determine format for IPv6 addresses, with or without [brackets]) 215 */ getHost(final InetSocketAddress isa)216 protected String getHost(final InetSocketAddress isa) { 217 218 //@@@ Will this work with literal IPv6 addresses, or do we 219 //@@@ need to wrap these in [] for the string representation? 220 //@@@ Having it in this method at least allows for easy workarounds. 221 return isa.isUnresolved() ? 222 isa.getHostName() : isa.getAddress().getHostAddress(); 223 224 } 225 226 /** 227 * Chooses a proxy from a list of available proxies. 228 * The default implementation just picks the first non-SOCKS proxy 229 * from the list. If there are only SOCKS proxies, 230 * {@link Proxy#NO_PROXY Proxy.NO_PROXY} is returned. 231 * Derived classes may implement more advanced strategies, 232 * such as proxy rotation if there are multiple options. 233 * 234 * @param proxies the list of proxies to choose from, 235 * never <code>null</code> or empty 236 * @param target the planned target, never <code>null</code> 237 * @param request the request to be sent, never <code>null</code> 238 * @param context the context, or <code>null</code> 239 * 240 * @return a proxy type 241 */ chooseProxy(final List<Proxy> proxies, final HttpHost target, final HttpRequest request, final HttpContext context)242 protected Proxy chooseProxy(final List<Proxy> proxies, 243 final HttpHost target, 244 final HttpRequest request, 245 final HttpContext context) { 246 Args.notEmpty(proxies, "List of proxies"); 247 248 Proxy result = null; 249 250 // check the list for one we can use 251 for (int i=0; (result == null) && (i < proxies.size()); i++) { 252 253 final Proxy p = proxies.get(i); 254 switch (p.type()) { 255 256 case DIRECT: 257 case HTTP: 258 result = p; 259 break; 260 261 case SOCKS: 262 // SOCKS hosts are not handled on the route level. 263 // The socket may make use of the SOCKS host though. 264 break; 265 } 266 } 267 268 if (result == null) { 269 //@@@ log as warning or info that only a socks proxy is available? 270 // result can only be null if all proxies are socks proxies 271 // socks proxies are not handled on the route planning level 272 result = Proxy.NO_PROXY; 273 } 274 275 return result; 276 } 277 278 } 279 280