Skip to content

Added support for java.net.ProxySelector #362

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 21, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 49 additions & 14 deletions api/src/main/java/org/asynchttpclient/AsyncHttpClientConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -136,7 +136,7 @@ private AsyncHttpClientConfig(int maxTotalConnections,
boolean keepAlive,
ScheduledExecutorService reaper,
ExecutorService applicationThreadPool,
ProxyServer proxyServer,
ProxyServerSelector proxyServerSelector,
SSLContext sslContext,
SSLEngineFactory sslEngineFactory,
AsyncHttpProviderConfig<?, ?> providerConfig,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -362,8 +362,8 @@ public boolean isManagedExecutorService() {
*
* @return instance of {@link ProxyServer}
*/
public ProxyServer getProxyServer() {
return proxyServer;
public ProxyServerSelector getProxyServerSelector() {
return proxyServerSelector;
}

/**
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -816,14 +817,25 @@ 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}
*
* @param proxyServer instance of {@link ProxyServer}
* @return a {@link Builder}
*/
public Builder setProxyServer(ProxyServer proxyServer) {
this.proxyServer = proxyServer;
this.proxyServerSelector = ProxyUtils.createProxyServerSelector(proxyServer);
return this;
}

Expand Down Expand Up @@ -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.
* <p/>
* If useProxySelector is set to <code>true</code> but {@link #setProxyServer(ProxyServer)}
* was used to explicitly set a proxy server, the latter is preferred.
* <p/>
* 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.
* <p/>
* If useProxyProperties is set to <code>true</code> but {@link #setProxyServer(ProxyServer)} was used
* to explicitly set a proxy server, the latter is preferred.
* If useProxyProperties is set to <code>true</code> but {@link #setUseProxySelector(boolean)}
* was also set to true, the latter is preferred.
* <p/>
* See http://download.oracle.com/javase/1.4.2/docs/guide/net/properties.html
*/
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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,
Expand All @@ -1267,7 +1302,7 @@ public Thread newThread(Runnable r) {
allowPoolingConnection,
reaper,
applicationThreadPool,
proxyServer,
proxyServerSelector,
sslContext,
sslEngineFactory,
providerConfig,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down
27 changes: 27 additions & 0 deletions api/src/main/java/org/asynchttpclient/ProxyServerSelector.java
Original file line number Diff line number Diff line change
@@ -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;
}
};
}
96 changes: 86 additions & 10 deletions api/src/main/java/org/asynchttpclient/util/ProxyUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.";

/**
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand All @@ -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<Proxy> proxies = proxySelector.select(uri);
if (proxies != null) {
// Loop through them until we find one that we know how to use
for (Proxy proxy : proxies) {
switch (proxy.type()) {
case HTTP:
if (!(proxy.address() instanceof InetSocketAddress)) {
log.warn("Don't know how to connect to address " + proxy.address());
} else {
InetSocketAddress address = (InetSocketAddress) proxy.address();
return new ProxyServer(Protocol.HTTP, address.getHostString(), address.getPort());
}
case DIRECT:
return null;
default:
log.warn("ProxySelector returned proxy type that we don't know how to use: " + proxy.type());
break;
}
}
}
return null;
}
};
}

/**
* Create a proxy server selector that always selects a single proxy server.
*
* @param proxyServer The proxy server to select.
* @return The proxy server selector.
*/
public static ProxyServerSelector createProxyServerSelector(final ProxyServer proxyServer) {
return new ProxyServerSelector() {
@Override
public ProxyServer select(URI uri) {
return proxyServer;
}
};
}
}
Loading