1 /* 2 * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/ProxySelectorRoutePlanner.java $ 3 * $Revision: 658785 $ 4 * $Date: 2008-05-21 10:47:40 -0700 (Wed, 21 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 32 package org.apache.http.impl.conn; 33 34 35 import java.net.InetAddress; 36 import java.net.InetSocketAddress; 37 import java.net.Proxy; 38 import java.net.ProxySelector; 39 import java.net.URI; 40 import java.net.URISyntaxException; 41 import java.util.List; 42 43 import org.apache.http.HttpException; 44 import org.apache.http.HttpHost; 45 import org.apache.http.HttpRequest; 46 import org.apache.http.protocol.HttpContext; 47 48 import org.apache.http.conn.routing.HttpRoute; 49 import org.apache.http.conn.routing.HttpRoutePlanner; 50 import org.apache.http.conn.scheme.Scheme; 51 import org.apache.http.conn.scheme.SchemeRegistry; 52 53 import org.apache.http.conn.params.ConnRouteParams; 54 import org.apache.http.conn.params.ConnRoutePNames; 55 56 57 /** 58 * Default implementation of an {@link HttpRoutePlanner}. 59 * This implementation is based on {@link java.net.ProxySelector}. 60 * By default, it will pick up the proxy settings of the JVM, either 61 * from system properties or from the browser running the application. 62 * Additionally, it interprets some 63 * {@link org.apache.http.conn.params.ConnRoutePNames parameters}, 64 * though not the {@link 65 * org.apache.http.conn.params.ConnRoutePNames#DEFAULT_PROXY DEFAULT_PROXY}. 66 * 67 * @deprecated Please use {@link java.net.URL#openConnection} instead. 68 * Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> 69 * for further details. 70 */ 71 @Deprecated 72 public class ProxySelectorRoutePlanner implements HttpRoutePlanner { 73 74 /** The scheme registry. */ 75 protected SchemeRegistry schemeRegistry; 76 77 /** The proxy selector to use, or <code>null</code> for system default. */ 78 protected ProxySelector proxySelector; 79 80 81 /** 82 * Creates a new proxy selector route planner. 83 * 84 * @param schreg the scheme registry 85 * @param prosel the proxy selector, or 86 * <code>null</code> for the system default 87 */ 88 public ProxySelectorRoutePlanner(SchemeRegistry schreg, 89 ProxySelector prosel) { 90 91 if (schreg == null) { 92 throw new IllegalArgumentException 93 ("SchemeRegistry must not be null."); 94 } 95 schemeRegistry = schreg; 96 proxySelector = prosel; 97 } 98 99 100 /** 101 * Obtains the proxy selector to use. 102 * 103 * @return the proxy selector, or <code>null</code> for the system default 104 */ 105 public ProxySelector getProxySelector() { 106 return this.proxySelector; 107 } 108 109 110 /** 111 * Sets the proxy selector to use. 112 * 113 * @param prosel the proxy selector, or 114 * <code>null</code> to use the system default 115 */ 116 public void setProxySelector(ProxySelector prosel) { 117 this.proxySelector = prosel; 118 } 119 120 121 122 // non-javadoc, see interface HttpRoutePlanner 123 public HttpRoute determineRoute(HttpHost target, 124 HttpRequest request, 125 HttpContext context) 126 throws HttpException { 127 128 if (request == null) { 129 throw new IllegalStateException 130 ("Request must not be null."); 131 } 132 133 // If we have a forced route, we can do without a target. 134 HttpRoute route = 135 ConnRouteParams.getForcedRoute(request.getParams()); 136 if (route != null) 137 return route; 138 139 // If we get here, there is no forced route. 140 // So we need a target to compute a route. 141 142 if (target == null) { 143 throw new IllegalStateException 144 ("Target host must not be null."); 145 } 146 147 final InetAddress local = 148 ConnRouteParams.getLocalAddress(request.getParams()); 149 150 // BEGIN android-changed 151 // If the client or request explicitly specifies a proxy (or no 152 // proxy), prefer that over the ProxySelector's VM-wide default. 153 HttpHost proxy = (HttpHost) request.getParams().getParameter(ConnRoutePNames.DEFAULT_PROXY); 154 if (proxy == null) { 155 proxy = determineProxy(target, request, context); 156 } else if (ConnRouteParams.NO_HOST.equals(proxy)) { 157 // value is explicitly unset 158 proxy = null; 159 } 160 // END android-changed 161 162 final Scheme schm = 163 this.schemeRegistry.getScheme(target.getSchemeName()); 164 // as it is typically used for TLS/SSL, we assume that 165 // a layered scheme implies a secure connection 166 final boolean secure = schm.isLayered(); 167 168 if (proxy == null) { 169 route = new HttpRoute(target, local, secure); 170 } else { 171 route = new HttpRoute(target, local, proxy, secure); 172 } 173 return route; 174 } 175 176 177 /** 178 * Determines a proxy for the given target. 179 * 180 * @param target the planned target, never <code>null</code> 181 * @param request the request to be sent, never <code>null</code> 182 * @param context the context, or <code>null</code> 183 * 184 * @return the proxy to use, or <code>null</code> for a direct route 185 * 186 * @throws HttpException 187 * in case of system proxy settings that cannot be handled 188 */ 189 protected HttpHost determineProxy(HttpHost target, 190 HttpRequest request, 191 HttpContext context) 192 throws HttpException { 193 194 // the proxy selector can be 'unset', so we better deal with null here 195 ProxySelector psel = this.proxySelector; 196 if (psel == null) 197 psel = ProxySelector.getDefault(); 198 if (psel == null) 199 return null; 200 201 URI targetURI = null; 202 try { 203 targetURI = new URI(target.toURI()); 204 } catch (URISyntaxException usx) { 205 throw new HttpException 206 ("Cannot convert host to URI: " + target, usx); 207 } 208 List<Proxy> proxies = psel.select(targetURI); 209 210 Proxy p = chooseProxy(proxies, target, request, context); 211 212 HttpHost result = null; 213 if (p.type() == Proxy.Type.HTTP) { 214 // convert the socket address to an HttpHost 215 if (!(p.address() instanceof InetSocketAddress)) { 216 throw new HttpException 217 ("Unable to handle non-Inet proxy address: "+p.address()); 218 } 219 final InetSocketAddress isa = (InetSocketAddress) p.address(); 220 // assume default scheme (http) 221 result = new HttpHost(getHost(isa), isa.getPort()); 222 } 223 224 return result; 225 } 226 227 228 /** 229 * Obtains a host from an {@link InetSocketAddress}. 230 * 231 * @param isa the socket address 232 * 233 * @return a host string, either as a symbolic name or 234 * as a literal IP address string 235 * <br/> 236 * (TODO: determine format for IPv6 addresses, with or without [brackets]) 237 */ 238 protected String getHost(InetSocketAddress isa) { 239 240 //@@@ Will this work with literal IPv6 addresses, or do we 241 //@@@ need to wrap these in [] for the string representation? 242 //@@@ Having it in this method at least allows for easy workarounds. 243 return isa.isUnresolved() ? 244 isa.getHostName() : isa.getAddress().getHostAddress(); 245 246 } 247 248 249 /* 250 * Chooses a proxy from a list of available proxies. 251 * The default implementation just picks the first non-SOCKS proxy 252 * from the list. If there are only SOCKS proxies, 253 * {@link Proxy#NO_PROXY Proxy.NO_PROXY} is returned. 254 * Derived classes may implement more advanced strategies, 255 * such as proxy rotation if there are multiple options. 256 * 257 * @param proxies the list of proxies to choose from, 258 * never <code>null</code> or empty 259 * @param target the planned target, never <code>null</code> 260 * @param request the request to be sent, never <code>null</code> 261 * @param context the context, or <code>null</code> 262 * 263 * @return a proxy of type {@link Proxy.Type#DIRECT DIRECT} 264 * or {@link Proxy.Type#HTTP HTTP}, never <code>null</code> 265 */ 266 protected Proxy chooseProxy(List<Proxy> proxies, 267 HttpHost target, 268 HttpRequest request, 269 HttpContext context) { 270 271 if ((proxies == null) || proxies.isEmpty()) { 272 throw new IllegalArgumentException 273 ("Proxy list must not be empty."); 274 } 275 276 Proxy result = null; 277 278 // check the list for one we can use 279 for (int i=0; (result == null) && (i < proxies.size()); i++) { 280 281 Proxy p = proxies.get(i); 282 switch (p.type()) { 283 284 case DIRECT: 285 case HTTP: 286 result = p; 287 break; 288 289 case SOCKS: 290 // SOCKS hosts are not handled on the route level. 291 // The socket may make use of the SOCKS host though. 292 break; 293 } 294 } 295 296 if (result == null) { 297 //@@@ log as warning or info that only a socks proxy is available? 298 // result can only be null if all proxies are socks proxies 299 // socks proxies are not handled on the route planning level 300 result = Proxy.NO_PROXY; 301 } 302 303 return result; 304 } 305 306 } // class ProxySelectorRoutePlanner 307 308