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.annotation.NotThreadSafe;
43 import org.apache.http.conn.params.ConnRouteParams;
44 import org.apache.http.conn.routing.HttpRoute;
45 import org.apache.http.conn.routing.HttpRoutePlanner;
46 import org.apache.http.conn.scheme.Scheme;
47 import org.apache.http.conn.scheme.SchemeRegistry;
48 import org.apache.http.protocol.HttpContext;
49 import org.apache.http.util.Args;
50 import org.apache.http.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 org.apache.http.conn.params.ConnRoutePNames parameters},
60 * though not the {@link
61 * org.apache.http.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 org.apache.http.conn.params.ConnRoutePNames#LOCAL_ADDRESS}</li>
67 * <li>{@link org.apache.http.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 */
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 */
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 */
113 public void setProxySelector(final ProxySelector prosel) {
114 this.proxySelector = prosel;
115 }
116
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 */
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 */
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 */
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