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.hc.client5.http.impl.routing;
29
30 import java.net.InetSocketAddress;
31 import java.net.Proxy;
32 import java.net.ProxySelector;
33 import java.net.URI;
34 import java.net.URISyntaxException;
35 import java.util.List;
36
37 import org.apache.hc.client5.http.SchemePortResolver;
38 import org.apache.hc.core5.annotation.Contract;
39 import org.apache.hc.core5.annotation.ThreadingBehavior;
40 import org.apache.hc.core5.http.HttpException;
41 import org.apache.hc.core5.http.HttpHost;
42 import org.apache.hc.core5.http.protocol.HttpContext;
43
44 /**
45 * {@link org.apache.hc.client5.http.routing.HttpRoutePlanner} implementation
46 * based on {@link ProxySelector}. By default, this class will pick up
47 * the proxy settings of the JVM, either from system properties
48 * or from the browser running the application.
49 *
50 * @since 4.3
51 */
52 @Contract(threading = ThreadingBehavior.STATELESS)
53 public class SystemDefaultRoutePlanner extends DefaultRoutePlanner {
54
55 private final ProxySelector proxySelector;
56
57 /**
58 * @param proxySelector the proxy selector, or {@code null} for the system default
59 */
60 public SystemDefaultRoutePlanner(
61 final SchemePortResolver schemePortResolver,
62 final ProxySelector proxySelector) {
63 super(schemePortResolver);
64 this.proxySelector = proxySelector;
65 }
66
67 /**
68 * @param proxySelector the proxy selector, or {@code null} for the system default
69 */
70 public SystemDefaultRoutePlanner(final ProxySelector proxySelector) {
71 this(null, proxySelector);
72 }
73
74 @Override
75 protected HttpHost determineProxy(final HttpHost target, final HttpContext context) throws HttpException {
76 final URI targetURI;
77 try {
78 targetURI = new URI(target.toURI());
79 } catch (final URISyntaxException ex) {
80 throw new HttpException("Cannot convert host to URI: " + target, ex);
81 }
82 ProxySelector proxySelectorInstance = this.proxySelector;
83 if (proxySelectorInstance == null) {
84 proxySelectorInstance = ProxySelector.getDefault();
85 }
86 if (proxySelectorInstance == null) {
87 //The proxy selector can be "unset", so we must be able to deal with a null selector
88 return null;
89 }
90 final List<Proxy> proxies = proxySelectorInstance.select(targetURI);
91 final Proxy p = chooseProxy(proxies);
92 HttpHost result = null;
93 if (p.type() == Proxy.Type.HTTP) {
94 // convert the socket address to an HttpHost
95 if (!(p.address() instanceof InetSocketAddress)) {
96 throw new HttpException("Unable to handle non-Inet proxy address: " + p.address());
97 }
98 final InetSocketAddress isa = (InetSocketAddress) p.address();
99 // assume default scheme (http)
100 result = new HttpHost(null, isa.getAddress(), isa.getHostString(), isa.getPort());
101 }
102
103 return result;
104 }
105
106 private Proxy chooseProxy(final List<Proxy> proxies) {
107 Proxy result = null;
108 // check the list for one we can use
109 for (int i = 0; result == null && i < proxies.size(); i++) {
110 final Proxy p = proxies.get(i);
111 switch (p.type()) {
112
113 case DIRECT:
114 case HTTP:
115 result = p;
116 break;
117
118 case SOCKS:
119 // SOCKS hosts are not handled on the route level.
120 // The socket may make use of the SOCKS host though.
121 break;
122 }
123 }
124 if (result == null) {
125 //@@@ log as warning or info that only a socks proxy is available?
126 // result can only be null if all proxies are socks proxies
127 // socks proxies are not handled on the route planning level
128 result = Proxy.NO_PROXY;
129 }
130 return result;
131 }
132
133 }