Skip to content

Commit 9827238

Browse files
committed
Merge pull request #365 from typesafehub/proxy-selector-1.7.x
Backported #362 to 1.7.x branch
2 parents e61a9c0 + 78ab0b0 commit 9827238

File tree

6 files changed

+273
-29
lines changed

6 files changed

+273
-29
lines changed

src/main/java/com/ning/http/client/AsyncHttpClientConfig.java

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public class AsyncHttpClientConfig {
6767
protected boolean allowPoolingConnection;
6868
protected ScheduledExecutorService reaper;
6969
protected ExecutorService applicationThreadPool;
70-
protected ProxyServer proxyServer;
70+
protected ProxyServerSelector proxyServerSelector;
7171
protected SSLContext sslContext;
7272
protected SSLEngineFactory sslEngineFactory;
7373
protected AsyncHttpProviderConfig<?, ?> providerConfig;
@@ -106,7 +106,7 @@ private AsyncHttpClientConfig(int maxTotalConnections,
106106
boolean keepAlive,
107107
ScheduledExecutorService reaper,
108108
ExecutorService applicationThreadPool,
109-
ProxyServer proxyServer,
109+
ProxyServerSelector proxyServerSelector,
110110
SSLContext sslContext,
111111
SSLEngineFactory sslEngineFactory,
112112
AsyncHttpProviderConfig<?, ?> providerConfig,
@@ -162,7 +162,7 @@ private AsyncHttpClientConfig(int maxTotalConnections,
162162
} else {
163163
this.applicationThreadPool = applicationThreadPool;
164164
}
165-
this.proxyServer = proxyServer;
165+
this.proxyServerSelector = proxyServerSelector;
166166
this.useRawUrl = useRawUrl;
167167
}
168168

@@ -310,8 +310,8 @@ public ExecutorService executorService() {
310310
*
311311
* @return instance of {@link com.ning.http.client.ProxyServer}
312312
*/
313-
public ProxyServer getProxyServer() {
314-
return proxyServer;
313+
public ProxyServerSelector getProxyServerSelector() {
314+
return proxyServerSelector;
315315
}
316316

317317
/**
@@ -531,11 +531,12 @@ public static class Builder {
531531
private boolean compressionEnabled = Boolean.getBoolean(ASYNC_CLIENT + "compressionEnabled");
532532
private String userAgent = System.getProperty(ASYNC_CLIENT + "userAgent", "NING/1.0");
533533
private boolean useProxyProperties = Boolean.getBoolean(ASYNC_CLIENT + "useProxyProperties");
534+
private boolean useProxySelector = Boolean.getBoolean(ASYNC_CLIENT + "useProxySelector");
534535
private boolean allowPoolingConnection = true;
535536
private boolean useRelativeURIsWithSSLProxies = Boolean.getBoolean(ASYNC_CLIENT + "useRelativeURIsWithSSLProxies");
536537
private ScheduledExecutorService reaper;
537538
private ExecutorService applicationThreadPool;
538-
private ProxyServer proxyServer = null;
539+
private ProxyServerSelector proxyServerSelector = null;
539540
private SSLContext sslContext;
540541
private SSLEngineFactory sslEngineFactory;
541542
private AsyncHttpProviderConfig<?, ?> providerConfig;
@@ -731,13 +732,24 @@ public Builder setExecutorService(ExecutorService applicationThreadPool) {
731732
}
732733

733734
/**
734-
* Set an instance of {@link com.ning.http.client.ProxyServer} used by an {@link AsyncHttpClient}
735+
* Set an instance of {@link ProxyServerSelector} used by an {@link AsyncHttpClient}
736+
*
737+
* @param proxyServerSelector instance of {@link ProxyServerSelector}
738+
* @return a {@link Builder}
739+
*/
740+
public Builder setProxyServerSelector(ProxyServerSelector proxyServerSelector) {
741+
this.proxyServerSelector = proxyServerSelector;
742+
return this;
743+
}
744+
745+
/**
746+
* Set an instance of {@link ProxyServer} used by an {@link AsyncHttpClient}
735747
*
736748
* @param proxyServer instance of {@link com.ning.http.client.ProxyServer}
737749
* @return a {@link Builder}
738750
*/
739751
public Builder setProxyServer(ProxyServer proxyServer) {
740-
this.proxyServer = proxyServer;
752+
this.proxyServerSelector = ProxyUtils.createProxyServerSelector(proxyServer);
741753
return this;
742754
}
743755

@@ -938,12 +950,27 @@ public Builder setRemoveQueryParamsOnRedirect(boolean removeQueryParamOnRedirect
938950
return this;
939951
}
940952

953+
/**
954+
* Sets whether AHC should use the default JDK ProxySelector to select a proxy server.
955+
* <p/>
956+
* If useProxySelector is set to <code>true</code> but {@link #setProxyServer(ProxyServer)}
957+
* was used to explicitly set a proxy server, the latter is preferred.
958+
* <p/>
959+
* See http://docs.oracle.com/javase/7/docs/api/java/net/ProxySelector.html
960+
*/
961+
public Builder setUseProxySelector(boolean useProxySelector) {
962+
this.useProxySelector = useProxySelector;
963+
return this;
964+
}
965+
941966
/**
942967
* Sets whether AHC should use the default http.proxy* system properties
943-
* to obtain proxy information.
968+
* to obtain proxy information. This differs from {@link #setUseProxySelector(boolean)}
969+
* in that AsyncHttpClient will use its own logic to handle the system properties,
970+
* potentially supporting other protocols that the the JDK ProxySelector doesn't.
944971
* <p/>
945-
* If useProxyProperties is set to <code>true</code> but {@link #setProxyServer(ProxyServer)} was used
946-
* to explicitly set a proxy server, the latter is preferred.
972+
* If useProxyProperties is set to <code>true</code> but {@link #setUseProxySelector(boolean)}
973+
* was also set to true, the latter is preferred.
947974
* <p/>
948975
* See http://download.oracle.com/javase/1.4.2/docs/guide/net/properties.html
949976
*/
@@ -1036,7 +1063,7 @@ public Builder(AsyncHttpClientConfig prototype) {
10361063
defaultMaxConnectionLifeTimeInMs = prototype.getMaxConnectionLifeTimeInMs();
10371064
maxDefaultRedirects = prototype.getMaxRedirects();
10381065
defaultMaxTotalConnections = prototype.getMaxTotalConnections();
1039-
proxyServer = prototype.getProxyServer();
1066+
proxyServerSelector = prototype.getProxyServerSelector();
10401067
realm = prototype.getRealm();
10411068
defaultRequestTimeoutInMs = prototype.getRequestTimeoutInMs();
10421069
sslContext = prototype.getSSLContext();
@@ -1100,8 +1127,16 @@ public Thread newThread(Runnable r) {
11001127
throw new IllegalStateException("ExecutorServices closed");
11011128
}
11021129

1103-
if (proxyServer == null && useProxyProperties) {
1104-
proxyServer = ProxyUtils.createProxy(System.getProperties());
1130+
if (proxyServerSelector == null && useProxySelector) {
1131+
proxyServerSelector = ProxyUtils.getJdkDefaultProxyServerSelector();
1132+
}
1133+
1134+
if (proxyServerSelector == null && useProxyProperties) {
1135+
proxyServerSelector = ProxyUtils.createProxyServerSelector(System.getProperties());
1136+
}
1137+
1138+
if (proxyServerSelector == null) {
1139+
proxyServerSelector = ProxyServerSelector.NO_PROXY_SELECTOR;
11051140
}
11061141

11071142
return new AsyncHttpClientConfig(defaultMaxTotalConnections,
@@ -1119,7 +1154,7 @@ public Thread newThread(Runnable r) {
11191154
allowPoolingConnection,
11201155
reaper,
11211156
applicationThreadPool,
1122-
proxyServer,
1157+
proxyServerSelector,
11231158
sslContext,
11241159
sslEngineFactory,
11251160
providerConfig,

src/main/java/com/ning/http/client/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", "NING/1.0");
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: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.ning.http.client;
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+
public ProxyServer select(URI uri) {
23+
return null;
24+
}
25+
};
26+
}

src/main/java/com/ning/http/util/ProxyUtils.java

Lines changed: 83 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,33 @@
1212
*/
1313
package com.ning.http.util;
1414

15-
import static com.ning.http.util.MiscUtil.isNonEmpty;
1615

1716
import com.ning.http.client.AsyncHttpClientConfig;
1817
import com.ning.http.client.ProxyServer;
1918
import com.ning.http.client.ProxyServer.Protocol;
19+
import com.ning.http.client.ProxyServerSelector;
2020
import com.ning.http.client.Request;
2121

22+
import java.net.InetSocketAddress;
23+
import java.net.Proxy;
24+
import java.net.ProxySelector;
25+
import java.net.URI;
2226
import java.util.List;
2327
import java.util.Locale;
2428
import java.util.Properties;
2529

30+
import org.slf4j.Logger;
31+
import org.slf4j.LoggerFactory;
32+
2633
/**
2734
* Utilities for Proxy handling.
2835
*
2936
* @author cstamas
3037
*/
3138
public class ProxyUtils {
3239

40+
private final static Logger log = LoggerFactory.getLogger(ProxyUtils.class);
41+
3342
private static final String PROPERTY_PREFIX = "com.ning.http.client.AsyncHttpClientConfig.proxy.";
3443

3544
/**
@@ -70,7 +79,10 @@ public class ProxyUtils {
7079
public static ProxyServer getProxyServer(AsyncHttpClientConfig config, Request request) {
7180
ProxyServer proxyServer = request.getProxyServer();
7281
if (proxyServer == null) {
73-
proxyServer = config.getProxyServer();
82+
ProxyServerSelector selector = config.getProxyServerSelector();
83+
if (selector != null) {
84+
proxyServer = selector.select(request.getOriginalURI());
85+
}
7486
}
7587
return ProxyUtils.avoidProxy(proxyServer, request) ? null : proxyServer;
7688
}
@@ -110,6 +122,9 @@ public static boolean avoidProxy(final ProxyServer proxyServer, final String tar
110122
if (nonProxyHost.startsWith("*") && nonProxyHost.length() > 1
111123
&& targetHost.endsWith(nonProxyHost.substring(1).toLowerCase(Locale.ENGLISH))) {
112124
return true;
125+
} else if (nonProxyHost.endsWith("*") && nonProxyHost.length() > 1
126+
&& targetHost.startsWith(nonProxyHost.substring(0, nonProxyHost.length() - 1).toLowerCase(Locale.ENGLISH))) {
127+
return true;
113128
} else if (nonProxyHost.equalsIgnoreCase(targetHost)) {
114129
return true;
115130
}
@@ -135,31 +150,89 @@ public static boolean avoidProxy(final ProxyServer proxyServer, final String tar
135150
* @see #PROXY_PROTOCOL
136151
* @see #PROXY_NONPROXYHOSTS
137152
*/
138-
public static ProxyServer createProxy(Properties properties) {
139-
String host = System.getProperty(PROXY_HOST);
153+
public static ProxyServerSelector createProxyServerSelector(Properties properties) {
154+
String host = properties.getProperty(PROXY_HOST);
140155

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

144159
Protocol protocol;
145160
try {
146-
protocol = Protocol.valueOf(System.getProperty(PROXY_PROTOCOL, "HTTP"));
161+
protocol = Protocol.valueOf(properties.getProperty(PROXY_PROTOCOL, "HTTP"));
147162
} catch (IllegalArgumentException e) {
148163
protocol = Protocol.HTTP;
149164
}
150165

151-
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));
152168

153-
String nonProxyHosts = System.getProperties().getProperty(PROXY_NONPROXYHOSTS);
169+
String nonProxyHosts = properties.getProperty(PROXY_NONPROXYHOSTS);
154170
if (nonProxyHosts != null) {
155171
for (String spec : nonProxyHosts.split("\\|")) {
156172
proxyServer.addNonProxyHost(spec);
157173
}
158174
}
159175

160-
return proxyServer;
176+
return createProxyServerSelector(proxyServer);
161177
}
162178

163-
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+
public ProxyServer select(URI uri) {
200+
List<Proxy> proxies = proxySelector.select(uri);
201+
if (proxies != null) {
202+
// Loop through them until we find one that we know how to use
203+
for (Proxy proxy : proxies) {
204+
switch (proxy.type()) {
205+
case HTTP:
206+
if (!(proxy.address() instanceof InetSocketAddress)) {
207+
log.warn("Don't know how to connect to address " + proxy.address());
208+
} else {
209+
InetSocketAddress address = (InetSocketAddress) proxy.address();
210+
return new ProxyServer(Protocol.HTTP, address.getHostString(), address.getPort());
211+
}
212+
case DIRECT:
213+
return null;
214+
default:
215+
log.warn("ProxySelector returned proxy type that we don't know how to use: " + proxy.type());
216+
break;
217+
}
218+
}
219+
}
220+
return null;
221+
}
222+
};
223+
}
224+
225+
/**
226+
* Create a proxy server selector that always selects a single proxy server.
227+
*
228+
* @param proxyServer The proxy server to select.
229+
* @return The proxy server selector.
230+
*/
231+
public static ProxyServerSelector createProxyServerSelector(final ProxyServer proxyServer) {
232+
return new ProxyServerSelector() {
233+
public ProxyServer select(URI uri) {
234+
return proxyServer;
235+
}
236+
};
164237
}
165238
}

src/site/apt/proxy.apt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,26 @@ Response r = responseFuture.get();
6161

6262
You can also set the <<<ProxyServer>>> at the <<<AsyncHttpClientConfig>>> level. In that case, all request will share
6363
the same proxy information.
64+
65+
Using Java System Properties
66+
67+
The AsyncHttpClient library supports the standard
68+
{{{http://docs.oracle.com/javase/7/docs/api/java/net/doc-files/net-properties.html#Proxies}Java Proxy System Properties}}.
69+
You can configure this at a global level using the <<<setUseProxyProperties(true)>>> method on the
70+
<<<AsyncHttpClientConfig.Builder>>>, or by setting the <<<com.ning.http.client.AsyncHttpClientConfig.useProxyProperties>>>
71+
system property to true.
72+
73+
Using JDK ProxySelectors
74+
75+
The AsyncHttpClient library also supports using the default
76+
{{{http://docs.oracle.com/javase/7/docs/api/java/net/ProxySelector.html}JDK ProxySelector}}. This allows for more
77+
fine grained control over which proxies to use, for example, it can be used in combination with
78+
{{{https://code.google.com/p/proxy-vole/}Proxy Vole}} to use OS configured proxies or to use a proxy.pac file.
79+
80+
You configure this at a global level using the <<<setUseProxySelector(true)>>> method on the
81+
<<<AsyncHttpClientConfig.Builder>>>, or by setting the
82+
<<<com.ning.http.client.AsyncHttpClientConfig.useProxySelector>>> system property to true.
83+
84+
If you don't change the default JDK <<<ProxySelector>>>, this setting is very similar to the <<<useProxyProperties>>>
85+
setting, though the <<<useProxyProperties>>> setting does allow more flexibility, such as the ability to use an
86+
HTTPS proxy.

0 commit comments

Comments
 (0)