Skip to content

Commit 1316688

Browse files
committed
Merge pull request AsyncHttpClient#362 from typesafehub/proxy-selector
Added support for java.net.ProxySelector
2 parents c717f06 + 2326e09 commit 1316688

File tree

7 files changed

+277
-29
lines changed

7 files changed

+277
-29
lines changed

api/src/main/java/org/asynchttpclient/AsyncHttpClientConfig.java

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public class AsyncHttpClientConfig {
9292
protected boolean allowPoolingConnection;
9393
protected ScheduledExecutorService reaper;
9494
protected ExecutorService applicationThreadPool;
95-
protected ProxyServer proxyServer;
95+
protected ProxyServerSelector proxyServerSelector;
9696
protected SSLContext sslContext;
9797
protected SSLEngineFactory sslEngineFactory;
9898
protected AsyncHttpProviderConfig<?, ?> providerConfig;
@@ -136,7 +136,7 @@ private AsyncHttpClientConfig(int maxTotalConnections,
136136
boolean keepAlive,
137137
ScheduledExecutorService reaper,
138138
ExecutorService applicationThreadPool,
139-
ProxyServer proxyServer,
139+
ProxyServerSelector proxyServerSelector,
140140
SSLContext sslContext,
141141
SSLEngineFactory sslEngineFactory,
142142
AsyncHttpProviderConfig<?, ?> providerConfig,
@@ -196,7 +196,7 @@ private AsyncHttpClientConfig(int maxTotalConnections,
196196
} else {
197197
this.applicationThreadPool = applicationThreadPool;
198198
}
199-
this.proxyServer = proxyServer;
199+
this.proxyServerSelector = proxyServerSelector;
200200
this.useRawUrl = useRawUrl;
201201
this.spdyEnabled = spdyEnabled;
202202
this.spdyInitialWindowSize = spdyInitialWindowSize;
@@ -362,8 +362,8 @@ public boolean isManagedExecutorService() {
362362
*
363363
* @return instance of {@link ProxyServer}
364364
*/
365-
public ProxyServer getProxyServer() {
366-
return proxyServer;
365+
public ProxyServerSelector getProxyServerSelector() {
366+
return proxyServerSelector;
367367
}
368368

369369
/**
@@ -613,11 +613,12 @@ public static class Builder {
613613
private boolean compressionEnabled = Boolean.getBoolean(ASYNC_CLIENT + "compressionEnabled");
614614
private String userAgent = System.getProperty(ASYNC_CLIENT + "userAgent", "AsyncHttpClient/" + AHC_VERSION);
615615
private boolean useProxyProperties = Boolean.getBoolean(ASYNC_CLIENT + "useProxyProperties");
616+
private boolean useProxySelector = Boolean.getBoolean(ASYNC_CLIENT + "useProxySelector");
616617
private boolean allowPoolingConnection = true;
617618
private boolean useRelativeURIsWithSSLProxies = Boolean.getBoolean(ASYNC_CLIENT + "useRelativeURIsWithSSLProxies");
618619
private ScheduledExecutorService reaper;
619620
private ExecutorService applicationThreadPool;
620-
private ProxyServer proxyServer = null;
621+
private ProxyServerSelector proxyServerSelector = null;
621622
private SSLContext sslContext;
622623
private SSLEngineFactory sslEngineFactory;
623624
private AsyncHttpProviderConfig<?, ?> providerConfig;
@@ -816,14 +817,25 @@ public Builder setExecutorService(ExecutorService applicationThreadPool) {
816817
return this;
817818
}
818819

820+
/**
821+
* Set an instance of {@link ProxyServerSelector} used by an {@link AsyncHttpClient}
822+
*
823+
* @param proxyServerSelector instance of {@link ProxyServerSelector}
824+
* @return a {@link Builder}
825+
*/
826+
public Builder setProxyServerSelector(ProxyServerSelector proxyServerSelector) {
827+
this.proxyServerSelector = proxyServerSelector;
828+
return this;
829+
}
830+
819831
/**
820832
* Set an instance of {@link ProxyServer} used by an {@link AsyncHttpClient}
821833
*
822834
* @param proxyServer instance of {@link ProxyServer}
823835
* @return a {@link Builder}
824836
*/
825837
public Builder setProxyServer(ProxyServer proxyServer) {
826-
this.proxyServer = proxyServer;
838+
this.proxyServerSelector = ProxyUtils.createProxyServerSelector(proxyServer);
827839
return this;
828840
}
829841

@@ -1024,12 +1036,27 @@ public Builder setRemoveQueryParamsOnRedirect(boolean removeQueryParamOnRedirect
10241036
return this;
10251037
}
10261038

1039+
/**
1040+
* Sets whether AHC should use the default JDK ProxySelector to select a proxy server.
1041+
* <p/>
1042+
* If useProxySelector is set to <code>true</code> but {@link #setProxyServer(ProxyServer)}
1043+
* was used to explicitly set a proxy server, the latter is preferred.
1044+
* <p/>
1045+
* See http://docs.oracle.com/javase/7/docs/api/java/net/ProxySelector.html
1046+
*/
1047+
public Builder setUseProxySelector(boolean useProxySelector) {
1048+
this.useProxySelector = useProxySelector;
1049+
return this;
1050+
}
1051+
10271052
/**
10281053
* Sets whether AHC should use the default http.proxy* system properties
1029-
* to obtain proxy information.
1054+
* to obtain proxy information. This differs from {@link #setUseProxySelector(boolean)}
1055+
* in that AsyncHttpClient will use its own logic to handle the system properties,
1056+
* potentially supporting other protocols that the the JDK ProxySelector doesn't.
10301057
* <p/>
1031-
* If useProxyProperties is set to <code>true</code> but {@link #setProxyServer(ProxyServer)} was used
1032-
* to explicitly set a proxy server, the latter is preferred.
1058+
* If useProxyProperties is set to <code>true</code> but {@link #setUseProxySelector(boolean)}
1059+
* was also set to true, the latter is preferred.
10331060
* <p/>
10341061
* See http://download.oracle.com/javase/1.4.2/docs/guide/net/properties.html
10351062
*/
@@ -1182,7 +1209,7 @@ public Builder(AsyncHttpClientConfig prototype) {
11821209
defaultMaxConnectionLifeTimeInMs = prototype.getMaxConnectionLifeTimeInMs();
11831210
maxDefaultRedirects = prototype.getMaxRedirects();
11841211
defaultMaxTotalConnections = prototype.getMaxTotalConnections();
1185-
proxyServer = prototype.getProxyServer();
1212+
proxyServerSelector = prototype.getProxyServerSelector();
11861213
realm = prototype.getRealm();
11871214
defaultRequestTimeoutInMs = prototype.getRequestTimeoutInMs();
11881215
sslContext = prototype.getSSLContext();
@@ -1248,8 +1275,16 @@ public Thread newThread(Runnable r) {
12481275
throw new IllegalStateException("ExecutorServices closed");
12491276
}
12501277

1251-
if (proxyServer == null && useProxyProperties) {
1252-
proxyServer = ProxyUtils.createProxy(System.getProperties());
1278+
if (proxyServerSelector == null && useProxySelector) {
1279+
proxyServerSelector = ProxyUtils.getJdkDefaultProxyServerSelector();
1280+
}
1281+
1282+
if (proxyServerSelector == null && useProxyProperties) {
1283+
proxyServerSelector = ProxyUtils.createProxyServerSelector(System.getProperties());
1284+
}
1285+
1286+
if (proxyServerSelector == null) {
1287+
proxyServerSelector = ProxyServerSelector.NO_PROXY_SELECTOR;
12531288
}
12541289

12551290
return new AsyncHttpClientConfig(defaultMaxTotalConnections,
@@ -1267,7 +1302,7 @@ public Thread newThread(Runnable r) {
12671302
allowPoolingConnection,
12681303
reaper,
12691304
applicationThreadPool,
1270-
proxyServer,
1305+
proxyServerSelector,
12711306
sslContext,
12721307
sslEngineFactory,
12731308
providerConfig,

api/src/main/java/org/asynchttpclient/AsyncHttpClientConfigBean.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,12 @@ void configureDefaults() {
5555
compressionEnabled = Boolean.getBoolean(ASYNC_CLIENT + "compressionEnabled");
5656
userAgent = System.getProperty(ASYNC_CLIENT + "userAgent", "AsyncHttpClient/" + AHC_VERSION);
5757

58+
boolean useProxySelector = Boolean.getBoolean(ASYNC_CLIENT + "useProxySelector");
5859
boolean useProxyProperties = Boolean.getBoolean(ASYNC_CLIENT + "useProxyProperties");
59-
if (useProxyProperties) {
60-
proxyServer = ProxyUtils.createProxy(System.getProperties());
60+
if (useProxySelector) {
61+
proxyServerSelector = ProxyUtils.getJdkDefaultProxyServerSelector();
62+
} else if (useProxyProperties) {
63+
proxyServerSelector = ProxyUtils.createProxyServerSelector(System.getProperties());
6164
}
6265

6366
allowPoolingConnection = true;
@@ -163,7 +166,12 @@ public AsyncHttpClientConfigBean setApplicationThreadPool(ExecutorService applic
163166
}
164167

165168
public AsyncHttpClientConfigBean setProxyServer(ProxyServer proxyServer) {
166-
this.proxyServer = proxyServer;
169+
this.proxyServerSelector = ProxyUtils.createProxyServerSelector(proxyServer);
170+
return this;
171+
}
172+
173+
public AsyncHttpClientConfigBean setProxyServerSelector(ProxyServerSelector proxyServerSelector) {
174+
this.proxyServerSelector = proxyServerSelector;
167175
return this;
168176
}
169177

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package org.asynchttpclient;
2+
3+
import java.net.URI;
4+
5+
/**
6+
* Selector for a proxy server
7+
*/
8+
public interface ProxyServerSelector {
9+
10+
/**
11+
* Select a proxy server to use for the given URI.
12+
*
13+
* @param uri The URI to select a proxy server for.
14+
* @return The proxy server to use, if any. May return null.
15+
*/
16+
ProxyServer select(URI uri);
17+
18+
/**
19+
* A selector that always selects no proxy.
20+
*/
21+
static final ProxyServerSelector NO_PROXY_SELECTOR = new ProxyServerSelector() {
22+
@Override
23+
public ProxyServer select(URI uri) {
24+
return null;
25+
}
26+
};
27+
}

api/src/main/java/org/asynchttpclient/util/ProxyUtils.java

Lines changed: 86 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,21 @@
1414

1515
import static org.asynchttpclient.util.MiscUtil.isNonEmpty;
1616

17+
import java.net.InetSocketAddress;
18+
import java.net.Proxy;
19+
import java.net.ProxySelector;
20+
import java.net.URI;
1721
import java.util.List;
22+
import java.util.Locale;
1823
import java.util.Properties;
1924

2025
import org.asynchttpclient.AsyncHttpClientConfig;
2126
import org.asynchttpclient.ProxyServer;
2227
import org.asynchttpclient.ProxyServer.Protocol;
28+
import org.asynchttpclient.ProxyServerSelector;
2329
import org.asynchttpclient.Request;
30+
import org.slf4j.Logger;
31+
import org.slf4j.LoggerFactory;
2432

2533
/**
2634
* Utilities for Proxy handling.
@@ -29,6 +37,8 @@
2937
*/
3038
public class ProxyUtils {
3139

40+
private final static Logger log = LoggerFactory.getLogger(ProxyUtils.class);
41+
3242
private static final String PROPERTY_PREFIX = "org.asynchttpclient.AsyncHttpClientConfig.proxy.";
3343

3444
/**
@@ -69,7 +79,10 @@ public class ProxyUtils {
6979
public static ProxyServer getProxyServer(AsyncHttpClientConfig config, Request request) {
7080
ProxyServer proxyServer = request.getProxyServer();
7181
if (proxyServer == null) {
72-
proxyServer = config.getProxyServer();
82+
ProxyServerSelector selector = config.getProxyServerSelector();
83+
if (selector != null) {
84+
proxyServer = selector.select(request.getOriginalURI());
85+
}
7386
}
7487
return ProxyUtils.avoidProxy(proxyServer, request) ? null : proxyServer;
7588
}
@@ -107,7 +120,10 @@ public static boolean avoidProxy(final ProxyServer proxyServer, final String tar
107120
if (isNonEmpty(nonProxyHosts)) {
108121
for (String nonProxyHost : nonProxyHosts) {
109122
if (nonProxyHost.startsWith("*") && nonProxyHost.length() > 1
110-
&& targetHost.endsWith(nonProxyHost.substring(1).toLowerCase())) {
123+
&& targetHost.endsWith(nonProxyHost.substring(1).toLowerCase(Locale.ENGLISH))) {
124+
return true;
125+
} else if (nonProxyHost.endsWith("*") && nonProxyHost.length() > 1
126+
&& targetHost.startsWith(nonProxyHost.substring(0, nonProxyHost.length() - 1).toLowerCase(Locale.ENGLISH))) {
111127
return true;
112128
} else if (nonProxyHost.equalsIgnoreCase(targetHost)) {
113129
return true;
@@ -134,31 +150,91 @@ public static boolean avoidProxy(final ProxyServer proxyServer, final String tar
134150
* @see #PROXY_PROTOCOL
135151
* @see #PROXY_NONPROXYHOSTS
136152
*/
137-
public static ProxyServer createProxy(Properties properties) {
138-
String host = System.getProperty(PROXY_HOST);
153+
public static ProxyServerSelector createProxyServerSelector(Properties properties) {
154+
String host = properties.getProperty(PROXY_HOST);
139155

140156
if (host != null) {
141-
int port = Integer.valueOf(System.getProperty(PROXY_PORT, "80"));
157+
int port = Integer.valueOf(properties.getProperty(PROXY_PORT, "80"));
142158

143159
Protocol protocol;
144160
try {
145-
protocol = Protocol.valueOf(System.getProperty(PROXY_PROTOCOL, "HTTP"));
161+
protocol = Protocol.valueOf(properties.getProperty(PROXY_PROTOCOL, "HTTP"));
146162
} catch (IllegalArgumentException e) {
147163
protocol = Protocol.HTTP;
148164
}
149165

150-
ProxyServer proxyServer = new ProxyServer(protocol, host, port, System.getProperty(PROXY_USER), System.getProperty(PROXY_PASSWORD));
166+
ProxyServer proxyServer = new ProxyServer(protocol, host, port, properties.getProperty(PROXY_USER),
167+
properties.getProperty(PROXY_PASSWORD));
151168

152-
String nonProxyHosts = System.getProperties().getProperty(PROXY_NONPROXYHOSTS);
169+
String nonProxyHosts = properties.getProperty(PROXY_NONPROXYHOSTS);
153170
if (nonProxyHosts != null) {
154171
for (String spec : nonProxyHosts.split("\\|")) {
155172
proxyServer.addNonProxyHost(spec);
156173
}
157174
}
158175

159-
return proxyServer;
176+
return createProxyServerSelector(proxyServer);
160177
}
161178

162-
return null;
179+
return ProxyServerSelector.NO_PROXY_SELECTOR;
180+
}
181+
182+
/**
183+
* Get a proxy server selector based on the JDK default proxy selector.
184+
*
185+
* @return The proxy server selector.
186+
*/
187+
public static ProxyServerSelector getJdkDefaultProxyServerSelector() {
188+
return createProxyServerSelector(ProxySelector.getDefault());
189+
}
190+
191+
/**
192+
* Create a proxy server selector based on the passed in JDK proxy selector.
193+
*
194+
* @param proxySelector The proxy selector to use. Must not be null.
195+
* @return The proxy server selector.
196+
*/
197+
public static ProxyServerSelector createProxyServerSelector(final ProxySelector proxySelector) {
198+
return new ProxyServerSelector() {
199+
@Override
200+
public ProxyServer select(URI uri) {
201+
List<Proxy> proxies = proxySelector.select(uri);
202+
if (proxies != null) {
203+
// Loop through them until we find one that we know how to use
204+
for (Proxy proxy : proxies) {
205+
switch (proxy.type()) {
206+
case HTTP:
207+
if (!(proxy.address() instanceof InetSocketAddress)) {
208+
log.warn("Don't know how to connect to address " + proxy.address());
209+
} else {
210+
InetSocketAddress address = (InetSocketAddress) proxy.address();
211+
return new ProxyServer(Protocol.HTTP, address.getHostString(), address.getPort());
212+
}
213+
case DIRECT:
214+
return null;
215+
default:
216+
log.warn("ProxySelector returned proxy type that we don't know how to use: " + proxy.type());
217+
break;
218+
}
219+
}
220+
}
221+
return null;
222+
}
223+
};
224+
}
225+
226+
/**
227+
* Create a proxy server selector that always selects a single proxy server.
228+
*
229+
* @param proxyServer The proxy server to select.
230+
* @return The proxy server selector.
231+
*/
232+
public static ProxyServerSelector createProxyServerSelector(final ProxyServer proxyServer) {
233+
return new ProxyServerSelector() {
234+
@Override
235+
public ProxyServer select(URI uri) {
236+
return proxyServer;
237+
}
238+
};
163239
}
164240
}

0 commit comments

Comments
 (0)