diff --git a/api/src/main/java/org/asynchttpclient/AsyncHttpClientConfig.java b/api/src/main/java/org/asynchttpclient/AsyncHttpClientConfig.java index 2b8ca1c84d..79143d1b00 100644 --- a/api/src/main/java/org/asynchttpclient/AsyncHttpClientConfig.java +++ b/api/src/main/java/org/asynchttpclient/AsyncHttpClientConfig.java @@ -92,7 +92,7 @@ public class AsyncHttpClientConfig { protected boolean allowPoolingConnection; protected ScheduledExecutorService reaper; protected ExecutorService applicationThreadPool; - protected ProxyServer proxyServer; + protected ProxyServerSelector proxyServerSelector; protected SSLContext sslContext; protected SSLEngineFactory sslEngineFactory; protected AsyncHttpProviderConfig, ?> providerConfig; @@ -136,7 +136,7 @@ private AsyncHttpClientConfig(int maxTotalConnections, boolean keepAlive, ScheduledExecutorService reaper, ExecutorService applicationThreadPool, - ProxyServer proxyServer, + ProxyServerSelector proxyServerSelector, SSLContext sslContext, SSLEngineFactory sslEngineFactory, AsyncHttpProviderConfig, ?> providerConfig, @@ -196,7 +196,7 @@ private AsyncHttpClientConfig(int maxTotalConnections, } else { this.applicationThreadPool = applicationThreadPool; } - this.proxyServer = proxyServer; + this.proxyServerSelector = proxyServerSelector; this.useRawUrl = useRawUrl; this.spdyEnabled = spdyEnabled; this.spdyInitialWindowSize = spdyInitialWindowSize; @@ -362,8 +362,8 @@ public boolean isManagedExecutorService() { * * @return instance of {@link ProxyServer} */ - public ProxyServer getProxyServer() { - return proxyServer; + public ProxyServerSelector getProxyServerSelector() { + return proxyServerSelector; } /** @@ -613,11 +613,12 @@ public static class Builder { private boolean compressionEnabled = Boolean.getBoolean(ASYNC_CLIENT + "compressionEnabled"); private String userAgent = System.getProperty(ASYNC_CLIENT + "userAgent", "AsyncHttpClient/" + AHC_VERSION); private boolean useProxyProperties = Boolean.getBoolean(ASYNC_CLIENT + "useProxyProperties"); + private boolean useProxySelector = Boolean.getBoolean(ASYNC_CLIENT + "useProxySelector"); private boolean allowPoolingConnection = true; private boolean useRelativeURIsWithSSLProxies = Boolean.getBoolean(ASYNC_CLIENT + "useRelativeURIsWithSSLProxies"); private ScheduledExecutorService reaper; private ExecutorService applicationThreadPool; - private ProxyServer proxyServer = null; + private ProxyServerSelector proxyServerSelector = null; private SSLContext sslContext; private SSLEngineFactory sslEngineFactory; private AsyncHttpProviderConfig, ?> providerConfig; @@ -816,6 +817,17 @@ public Builder setExecutorService(ExecutorService applicationThreadPool) { return this; } + /** + * Set an instance of {@link ProxyServerSelector} used by an {@link AsyncHttpClient} + * + * @param proxyServerSelector instance of {@link ProxyServerSelector} + * @return a {@link Builder} + */ + public Builder setProxyServerSelector(ProxyServerSelector proxyServerSelector) { + this.proxyServerSelector = proxyServerSelector; + return this; + } + /** * Set an instance of {@link ProxyServer} used by an {@link AsyncHttpClient} * @@ -823,7 +835,7 @@ public Builder setExecutorService(ExecutorService applicationThreadPool) { * @return a {@link Builder} */ public Builder setProxyServer(ProxyServer proxyServer) { - this.proxyServer = proxyServer; + this.proxyServerSelector = ProxyUtils.createProxyServerSelector(proxyServer); return this; } @@ -1024,12 +1036,27 @@ public Builder setRemoveQueryParamsOnRedirect(boolean removeQueryParamOnRedirect return this; } + /** + * Sets whether AHC should use the default JDK ProxySelector to select a proxy server. + *
+ * If useProxySelector is set totrue
but {@link #setProxyServer(ProxyServer)}
+ * was used to explicitly set a proxy server, the latter is preferred.
+ *
+ * See http://docs.oracle.com/javase/7/docs/api/java/net/ProxySelector.html
+ */
+ public Builder setUseProxySelector(boolean useProxySelector) {
+ this.useProxySelector = useProxySelector;
+ return this;
+ }
+
/**
* Sets whether AHC should use the default http.proxy* system properties
- * to obtain proxy information.
+ * to obtain proxy information. This differs from {@link #setUseProxySelector(boolean)}
+ * in that AsyncHttpClient will use its own logic to handle the system properties,
+ * potentially supporting other protocols that the the JDK ProxySelector doesn't.
*
- * If useProxyProperties is set to true
but {@link #setProxyServer(ProxyServer)} was used
- * to explicitly set a proxy server, the latter is preferred.
+ * If useProxyProperties is set to true
but {@link #setUseProxySelector(boolean)}
+ * was also set to true, the latter is preferred.
*
* See http://download.oracle.com/javase/1.4.2/docs/guide/net/properties.html
*/
@@ -1182,7 +1209,7 @@ public Builder(AsyncHttpClientConfig prototype) {
defaultMaxConnectionLifeTimeInMs = prototype.getMaxConnectionLifeTimeInMs();
maxDefaultRedirects = prototype.getMaxRedirects();
defaultMaxTotalConnections = prototype.getMaxTotalConnections();
- proxyServer = prototype.getProxyServer();
+ proxyServerSelector = prototype.getProxyServerSelector();
realm = prototype.getRealm();
defaultRequestTimeoutInMs = prototype.getRequestTimeoutInMs();
sslContext = prototype.getSSLContext();
@@ -1248,8 +1275,16 @@ public Thread newThread(Runnable r) {
throw new IllegalStateException("ExecutorServices closed");
}
- if (proxyServer == null && useProxyProperties) {
- proxyServer = ProxyUtils.createProxy(System.getProperties());
+ if (proxyServerSelector == null && useProxySelector) {
+ proxyServerSelector = ProxyUtils.getJdkDefaultProxyServerSelector();
+ }
+
+ if (proxyServerSelector == null && useProxyProperties) {
+ proxyServerSelector = ProxyUtils.createProxyServerSelector(System.getProperties());
+ }
+
+ if (proxyServerSelector == null) {
+ proxyServerSelector = ProxyServerSelector.NO_PROXY_SELECTOR;
}
return new AsyncHttpClientConfig(defaultMaxTotalConnections,
@@ -1267,7 +1302,7 @@ public Thread newThread(Runnable r) {
allowPoolingConnection,
reaper,
applicationThreadPool,
- proxyServer,
+ proxyServerSelector,
sslContext,
sslEngineFactory,
providerConfig,
diff --git a/api/src/main/java/org/asynchttpclient/AsyncHttpClientConfigBean.java b/api/src/main/java/org/asynchttpclient/AsyncHttpClientConfigBean.java
index 4e48491b34..9a7d75bf47 100644
--- a/api/src/main/java/org/asynchttpclient/AsyncHttpClientConfigBean.java
+++ b/api/src/main/java/org/asynchttpclient/AsyncHttpClientConfigBean.java
@@ -55,9 +55,12 @@ void configureDefaults() {
compressionEnabled = Boolean.getBoolean(ASYNC_CLIENT + "compressionEnabled");
userAgent = System.getProperty(ASYNC_CLIENT + "userAgent", "AsyncHttpClient/" + AHC_VERSION);
+ boolean useProxySelector = Boolean.getBoolean(ASYNC_CLIENT + "useProxySelector");
boolean useProxyProperties = Boolean.getBoolean(ASYNC_CLIENT + "useProxyProperties");
- if (useProxyProperties) {
- proxyServer = ProxyUtils.createProxy(System.getProperties());
+ if (useProxySelector) {
+ proxyServerSelector = ProxyUtils.getJdkDefaultProxyServerSelector();
+ } else if (useProxyProperties) {
+ proxyServerSelector = ProxyUtils.createProxyServerSelector(System.getProperties());
}
allowPoolingConnection = true;
@@ -163,7 +166,12 @@ public AsyncHttpClientConfigBean setApplicationThreadPool(ExecutorService applic
}
public AsyncHttpClientConfigBean setProxyServer(ProxyServer proxyServer) {
- this.proxyServer = proxyServer;
+ this.proxyServerSelector = ProxyUtils.createProxyServerSelector(proxyServer);
+ return this;
+ }
+
+ public AsyncHttpClientConfigBean setProxyServerSelector(ProxyServerSelector proxyServerSelector) {
+ this.proxyServerSelector = proxyServerSelector;
return this;
}
diff --git a/api/src/main/java/org/asynchttpclient/ProxyServerSelector.java b/api/src/main/java/org/asynchttpclient/ProxyServerSelector.java
new file mode 100644
index 0000000000..b883b1fcf9
--- /dev/null
+++ b/api/src/main/java/org/asynchttpclient/ProxyServerSelector.java
@@ -0,0 +1,27 @@
+package org.asynchttpclient;
+
+import java.net.URI;
+
+/**
+ * Selector for a proxy server
+ */
+public interface ProxyServerSelector {
+
+ /**
+ * Select a proxy server to use for the given URI.
+ *
+ * @param uri The URI to select a proxy server for.
+ * @return The proxy server to use, if any. May return null.
+ */
+ ProxyServer select(URI uri);
+
+ /**
+ * A selector that always selects no proxy.
+ */
+ static final ProxyServerSelector NO_PROXY_SELECTOR = new ProxyServerSelector() {
+ @Override
+ public ProxyServer select(URI uri) {
+ return null;
+ }
+ };
+}
diff --git a/api/src/main/java/org/asynchttpclient/util/ProxyUtils.java b/api/src/main/java/org/asynchttpclient/util/ProxyUtils.java
index 6e33588f6e..612642b524 100644
--- a/api/src/main/java/org/asynchttpclient/util/ProxyUtils.java
+++ b/api/src/main/java/org/asynchttpclient/util/ProxyUtils.java
@@ -14,13 +14,21 @@
import static org.asynchttpclient.util.MiscUtil.isNonEmpty;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.URI;
import java.util.List;
+import java.util.Locale;
import java.util.Properties;
import org.asynchttpclient.AsyncHttpClientConfig;
import org.asynchttpclient.ProxyServer;
import org.asynchttpclient.ProxyServer.Protocol;
+import org.asynchttpclient.ProxyServerSelector;
import org.asynchttpclient.Request;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Utilities for Proxy handling.
@@ -29,6 +37,8 @@
*/
public class ProxyUtils {
+ private final static Logger log = LoggerFactory.getLogger(ProxyUtils.class);
+
private static final String PROPERTY_PREFIX = "org.asynchttpclient.AsyncHttpClientConfig.proxy.";
/**
@@ -69,7 +79,10 @@ public class ProxyUtils {
public static ProxyServer getProxyServer(AsyncHttpClientConfig config, Request request) {
ProxyServer proxyServer = request.getProxyServer();
if (proxyServer == null) {
- proxyServer = config.getProxyServer();
+ ProxyServerSelector selector = config.getProxyServerSelector();
+ if (selector != null) {
+ proxyServer = selector.select(request.getOriginalURI());
+ }
}
return ProxyUtils.avoidProxy(proxyServer, request) ? null : proxyServer;
}
@@ -107,7 +120,10 @@ public static boolean avoidProxy(final ProxyServer proxyServer, final String tar
if (isNonEmpty(nonProxyHosts)) {
for (String nonProxyHost : nonProxyHosts) {
if (nonProxyHost.startsWith("*") && nonProxyHost.length() > 1
- && targetHost.endsWith(nonProxyHost.substring(1).toLowerCase())) {
+ && targetHost.endsWith(nonProxyHost.substring(1).toLowerCase(Locale.ENGLISH))) {
+ return true;
+ } else if (nonProxyHost.endsWith("*") && nonProxyHost.length() > 1
+ && targetHost.startsWith(nonProxyHost.substring(0, nonProxyHost.length() - 1).toLowerCase(Locale.ENGLISH))) {
return true;
} else if (nonProxyHost.equalsIgnoreCase(targetHost)) {
return true;
@@ -134,31 +150,91 @@ public static boolean avoidProxy(final ProxyServer proxyServer, final String tar
* @see #PROXY_PROTOCOL
* @see #PROXY_NONPROXYHOSTS
*/
- public static ProxyServer createProxy(Properties properties) {
- String host = System.getProperty(PROXY_HOST);
+ public static ProxyServerSelector createProxyServerSelector(Properties properties) {
+ String host = properties.getProperty(PROXY_HOST);
if (host != null) {
- int port = Integer.valueOf(System.getProperty(PROXY_PORT, "80"));
+ int port = Integer.valueOf(properties.getProperty(PROXY_PORT, "80"));
Protocol protocol;
try {
- protocol = Protocol.valueOf(System.getProperty(PROXY_PROTOCOL, "HTTP"));
+ protocol = Protocol.valueOf(properties.getProperty(PROXY_PROTOCOL, "HTTP"));
} catch (IllegalArgumentException e) {
protocol = Protocol.HTTP;
}
- ProxyServer proxyServer = new ProxyServer(protocol, host, port, System.getProperty(PROXY_USER), System.getProperty(PROXY_PASSWORD));
+ ProxyServer proxyServer = new ProxyServer(protocol, host, port, properties.getProperty(PROXY_USER),
+ properties.getProperty(PROXY_PASSWORD));
- String nonProxyHosts = System.getProperties().getProperty(PROXY_NONPROXYHOSTS);
+ String nonProxyHosts = properties.getProperty(PROXY_NONPROXYHOSTS);
if (nonProxyHosts != null) {
for (String spec : nonProxyHosts.split("\\|")) {
proxyServer.addNonProxyHost(spec);
}
}
- return proxyServer;
+ return createProxyServerSelector(proxyServer);
}
- return null;
+ return ProxyServerSelector.NO_PROXY_SELECTOR;
+ }
+
+ /**
+ * Get a proxy server selector based on the JDK default proxy selector.
+ *
+ * @return The proxy server selector.
+ */
+ public static ProxyServerSelector getJdkDefaultProxyServerSelector() {
+ return createProxyServerSelector(ProxySelector.getDefault());
+ }
+
+ /**
+ * Create a proxy server selector based on the passed in JDK proxy selector.
+ *
+ * @param proxySelector The proxy selector to use. Must not be null.
+ * @return The proxy server selector.
+ */
+ public static ProxyServerSelector createProxyServerSelector(final ProxySelector proxySelector) {
+ return new ProxyServerSelector() {
+ @Override
+ public ProxyServer select(URI uri) {
+ List