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 org.apache.http.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 org.apache.http.HttpException; 40 import org.apache.http.HttpHost; 41 import org.apache.http.HttpRequest; 42 import org.apache.http.conn.params.ConnRouteParams; 43 import org.apache.http.conn.routing.HttpRoute; 44 import org.apache.http.conn.routing.HttpRoutePlanner; 45 import org.apache.http.conn.scheme.Scheme; 46 import org.apache.http.conn.scheme.SchemeRegistry; 47 import org.apache.http.protocol.HttpContext; 48 import org.apache.http.util.Args; 49 import org.apache.http.util.Asserts; 50 51 52 /** 53 * Default implementation of an {@link HttpRoutePlanner}. 54 * This implementation is based on {@link java.net.ProxySelector}. 55 * By default, it will pick up the proxy settings of the JVM, either 56 * from system properties or from the browser running the application. 57 * Additionally, it interprets some 58 * {@link org.apache.http.conn.params.ConnRoutePNames parameters}, 59 * though not the {@link 60 * org.apache.http.conn.params.ConnRoutePNames#DEFAULT_PROXY DEFAULT_PROXY}. 61 * <p> 62 * The following parameters can be used to customize the behavior of this 63 * class: 64 * <ul> 65 * <li>{@link org.apache.http.conn.params.ConnRoutePNames#LOCAL_ADDRESS}</li> 66 * <li>{@link org.apache.http.conn.params.ConnRoutePNames#FORCED_ROUTE}</li> 67 * </ul> 68 * 69 * @since 4.0 70 * 71 * @deprecated (4.3) use {@link SystemDefaultRoutePlanner} 72 */ 73 @Deprecated 74 public class ProxySelectorRoutePlanner implements HttpRoutePlanner { 75 76 /** The scheme registry. */ 77 protected final SchemeRegistry schemeRegistry; // @Contract(threading = ThreadingBehavior.SAFE) 78 79 /** The proxy selector to use, or {@code null} for system default. */ 80 protected ProxySelector proxySelector; 81 82 /** 83 * Creates a new proxy selector route planner. 84 * 85 * @param schreg the scheme registry 86 * @param prosel the proxy selector, or 87 * {@code null} for the system default 88 */ 89 public ProxySelectorRoutePlanner(final SchemeRegistry schreg, 90 final ProxySelector prosel) { 91 Args.notNull(schreg, "SchemeRegistry"); 92 schemeRegistry = schreg; 93 proxySelector = prosel; 94 } 95 96 /** 97 * Obtains the proxy selector to use. 98 * 99 * @return the proxy selector, or {@code null} for the system default 100 */ 101 public ProxySelector getProxySelector() { 102 return this.proxySelector; 103 } 104 105 /** 106 * Sets the proxy selector to use. 107 * 108 * @param prosel the proxy selector, or 109 * {@code null} to use the system default 110 */ 111 public void setProxySelector(final ProxySelector prosel) { 112 this.proxySelector = prosel; 113 } 114 115 @Override 116 public HttpRoute determineRoute(final HttpHost target, 117 final HttpRequest request, 118 final HttpContext context) 119 throws HttpException { 120 121 Args.notNull(request, "HTTP request"); 122 123 // If we have a forced route, we can do without a target. 124 HttpRoute route = 125 ConnRouteParams.getForcedRoute(request.getParams()); 126 if (route != null) { 127 return route; 128 } 129 130 // If we get here, there is no forced route. 131 // So we need a target to compute a route. 132 133 Asserts.notNull(target, "Target host"); 134 135 final InetAddress local = 136 ConnRouteParams.getLocalAddress(request.getParams()); 137 final HttpHost proxy = determineProxy(target, request, context); 138 139 final Scheme schm = 140 this.schemeRegistry.getScheme(target.getSchemeName()); 141 // as it is typically used for TLS/SSL, we assume that 142 // a layered scheme implies a secure connection 143 final boolean secure = schm.isLayered(); 144 145 if (proxy == null) { 146 route = new HttpRoute(target, local, secure); 147 } else { 148 route = new HttpRoute(target, local, proxy, secure); 149 } 150 return route; 151 } 152 153 /** 154 * Determines a proxy for the given target. 155 * 156 * @param target the planned target, never {@code null} 157 * @param request the request to be sent, never {@code null} 158 * @param context the context, or {@code null} 159 * 160 * @return the proxy to use, or {@code null} for a direct route 161 * 162 * @throws HttpException 163 * in case of system proxy settings that cannot be handled 164 */ 165 protected HttpHost determineProxy(final HttpHost target, 166 final HttpRequest request, 167 final HttpContext context) 168 throws HttpException { 169 170 // the proxy selector can be 'unset', so we better deal with null here 171 ProxySelector psel = this.proxySelector; 172 if (psel == null) { 173 psel = ProxySelector.getDefault(); 174 } 175 if (psel == null) { 176 return null; 177 } 178 179 URI targetURI = null; 180 try { 181 targetURI = new URI(target.toURI()); 182 } catch (final URISyntaxException usx) { 183 throw new HttpException 184 ("Cannot convert host to URI: " + target, usx); 185 } 186 final List<Proxy> proxies = psel.select(targetURI); 187 188 final Proxy p = chooseProxy(proxies, target, request, context); 189 190 HttpHost result = null; 191 if (p.type() == Proxy.Type.HTTP) { 192 // convert the socket address to an HttpHost 193 if (!(p.address() instanceof InetSocketAddress)) { 194 throw new HttpException 195 ("Unable to handle non-Inet proxy address: "+p.address()); 196 } 197 final InetSocketAddress isa = (InetSocketAddress) p.address(); 198 // assume default scheme (http) 199 result = new HttpHost(getHost(isa), isa.getPort()); 200 } 201 202 return result; 203 } 204 205 /** 206 * Obtains a host from an {@link InetSocketAddress}. 207 * 208 * @param isa the socket address 209 * 210 * @return a host string, either as a symbolic name or 211 * as a literal IP address string 212 * <p> 213 * (TODO: determine format for IPv6 addresses, with or without [brackets]) 214 * </p> 215 */ 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} or empty 236 * @param target the planned target, never {@code null} 237 * @param request the request to be sent, never {@code null} 238 * @param context the context, or {@code null} 239 * 240 * @return a proxy type 241 */ 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