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