/* * Copyright 2010 Ning, Inc. * * Ning licenses this file to you under the Apache License, version 2.0 * (the "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package org.asynchttpclient; import org.asynchttpclient.filter.IOExceptionFilter; import org.asynchttpclient.filter.RequestFilter; import org.asynchttpclient.filter.ResponseFilter; import org.asynchttpclient.util.AllowAllHostnameVerifier; import org.asynchttpclient.util.ProxyUtils; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Properties; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; /** * Configuration class to use with a {@link AsyncHttpClient}. System property can be also used to configure this * object default behavior by doing: *
* -Dorg.asynchttpclient.AsyncHttpClientConfig.nameOfTheProperty * ex: * * -Dorg.asynchttpclient.AsyncHttpClientConfig.defaultMaxTotalConnections * -Dorg.asynchttpclient.AsyncHttpClientConfig.defaultMaxTotalConnections * -Dorg.asynchttpclient.AsyncHttpClientConfig.defaultMaxConnectionsPerHost * -Dorg.asynchttpclient.AsyncHttpClientConfig.defaultConnectionTimeoutInMS * -Dorg.asynchttpclient.AsyncHttpClientConfig.defaultIdleConnectionInPoolTimeoutInMS * -Dorg.asynchttpclient.AsyncHttpClientConfig.defaultRequestTimeoutInMS * -Dorg.asynchttpclient.AsyncHttpClientConfig.defaultRedirectsEnabled * -Dorg.asynchttpclient.AsyncHttpClientConfig.defaultMaxRedirects */ public class AsyncHttpClientConfig { protected final static String ASYNC_CLIENT = AsyncHttpClientConfig.class.getName() + "."; public final static String AHC_VERSION; static { InputStream is = null; Properties prop = new Properties(); try { is = AsyncHttpClientConfig.class.getResourceAsStream("version.properties"); prop.load(is); } catch (IOException e) { e.printStackTrace(); } finally { if (is != null) { try { is.close(); } catch (IOException ignored) { } } } AHC_VERSION = prop.getProperty("ahc.version", "UNKNOWN"); } protected int maxTotalConnections; protected int maxConnectionPerHost; protected int connectionTimeOutInMs; protected int webSocketIdleTimeoutInMs; protected int idleConnectionInPoolTimeoutInMs; protected int idleConnectionTimeoutInMs; protected int requestTimeoutInMs; protected boolean redirectEnabled; protected int maxDefaultRedirects; protected boolean compressionEnabled; protected String userAgent; protected boolean allowPoolingConnection; protected ScheduledExecutorService reaper; protected ExecutorService applicationThreadPool; protected ProxyServerSelector proxyServerSelector; protected SSLContext sslContext; protected SSLEngineFactory sslEngineFactory; protected AsyncHttpProviderConfig, ?> providerConfig; protected ConnectionsPool, ?> connectionsPool; protected Realm realm; protected Listnull
*/
public ExecutorService executorService() {
return applicationThreadPool;
}
/**
* An instance of {@link ProxyServer} used by an {@link AsyncHttpClient}
*
* @return instance of {@link ProxyServer}
*/
public ProxyServerSelector getProxyServerSelector() {
return proxyServerSelector;
}
/**
* Return an instance of {@link SSLContext} used for SSL connection.
*
* @return an instance of {@link SSLContext} used for SSL connection.
*/
public SSLContext getSSLContext() {
return sslContext;
}
/**
* Return an instance of {@link ConnectionsPool}
*
* @return an instance of {@link ConnectionsPool}
*/
public ConnectionsPool, ?> getConnectionsPool() {
return connectionsPool;
}
/**
* Return an instance of {@link SSLEngineFactory} used for SSL connection.
*
* @return an instance of {@link SSLEngineFactory} used for SSL connection.
*/
public SSLEngineFactory getSSLEngineFactory() {
if (sslEngineFactory == null) {
return new SSLEngineFactory() {
public SSLEngine newSSLEngine() {
if (sslContext != null) {
SSLEngine sslEngine = sslContext.createSSLEngine();
sslEngine.setUseClientMode(true);
return sslEngine;
} else {
return null;
}
}
};
}
return sslEngineFactory;
}
/**
* Return the {@link AsyncHttpProviderConfig}
*
* @return the {@link AsyncHttpProviderConfig}
*/
public AsyncHttpProviderConfig, ?> getAsyncHttpProviderConfig() {
return providerConfig;
}
/**
* Return the current {@link Realm}}
*
* @return the current {@link Realm}}
*/
public Realm getRealm() {
return realm;
}
/**
* @return true
if {@link RequestFilter}s have been defined.
*
* @since 2.0.0
*/
public boolean hasRequestFilters() {
return !requestFilters.isEmpty();
}
/**
* Return the list of {@link RequestFilter}
*
* @return Unmodifiable list of {@link ResponseFilter}
*/
public Listtrue
if {@link ResponseFilter}s have been defined.
* @since 2.0.0
*/
public boolean hasResponseFilters() {
return !responseFilters.isEmpty();
}
/**
* Return the list of {@link ResponseFilter}
*
* @return Unmodifiable list of {@link ResponseFilter}
*/
public List* In the case of a POST/Redirect/Get scenario where the server uses a 302 * for the redirect, should AHC respond to the redirect with a GET or * whatever the original method was. Unless configured otherwise, * for a 302, AHC, will use a GET for this case. *
* * @returntrue
if string 302 handling is to be used,
* otherwise false
.
*
* @since 1.7.2
*/
public boolean isStrict302Handling() {
return strict302Handling;
}
/**
* @returntrue
if AHC should use relative URIs instead of absolute ones when talking with a SSL proxy,
* otherwise false
.
*
* @since 1.7.12
*/
public boolean isUseRelativeURIsWithSSLProxies() {
return useRelativeURIsWithSSLProxies;
}
/**
* Return the maximum time in millisecond an {@link AsyncHttpClient} will keep connection in the pool, or -1 to keep connection while possible.
*
* @return the maximum time in millisecond an {@link AsyncHttpClient} will keep connection in the pool, or -1 to keep connection while possible.
*/
public int getMaxConnectionLifeTimeInMs() {
return maxConnectionLifeTimeInMs;
}
/**
* @returntrue
if AHC should use rfc6265 for encoding client side cookies, otherwise false
.
*
* @since 1.7.18
*/
public boolean isRfc6265CookieEncoding() {
return rfc6265CookieEncoding;
}
/**
* @return true
if the underlying provider should make new connections asynchronously or not. By default
* new connections are made synchronously.
*
* @since 2.0.0
*/
public boolean isAsyncConnectMode() {
return asyncConnectMode;
}
/**
* Builder for an {@link AsyncHttpClient}
*/
public static class Builder {
private int defaultMaxTotalConnections = Integer.getInteger(ASYNC_CLIENT + "defaultMaxTotalConnections", -1);
private int defaultMaxConnectionPerHost = Integer.getInteger(ASYNC_CLIENT + "defaultMaxConnectionsPerHost", -1);
private int defaultConnectionTimeOutInMs = Integer.getInteger(ASYNC_CLIENT + "defaultConnectionTimeoutInMS", 60 * 1000);
private int defaultWebsocketIdleTimeoutInMs = Integer.getInteger(ASYNC_CLIENT + "defaultWebsocketTimoutInMS", 15 * 60 * 1000);
private int defaultIdleConnectionInPoolTimeoutInMs = Integer.getInteger(ASYNC_CLIENT + "defaultIdleConnectionInPoolTimeoutInMS", 60 * 1000);
private int defaultIdleConnectionTimeoutInMs = Integer.getInteger(ASYNC_CLIENT + "defaultIdleConnectionTimeoutInMS", 60 * 1000);
private int defaultRequestTimeoutInMs = Integer.getInteger(ASYNC_CLIENT + "defaultRequestTimeoutInMS", 60 * 1000);
private int defaultMaxConnectionLifeTimeInMs = Integer.getInteger(ASYNC_CLIENT + "defaultMaxConnectionLifeTimeInMs", -1);
private boolean redirectEnabled = Boolean.getBoolean(ASYNC_CLIENT + "defaultRedirectsEnabled");
private int maxDefaultRedirects = Integer.getInteger(ASYNC_CLIENT + "defaultMaxRedirects", 5);
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 ProxyServerSelector proxyServerSelector = null;
private SSLContext sslContext;
private SSLEngineFactory sslEngineFactory;
private AsyncHttpProviderConfig, ?> providerConfig;
private ConnectionsPool, ?> connectionsPool;
private Realm realm;
private int requestCompressionLevel = -1;
private int maxRequestRetry = 5;
private final Listtrue
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. 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 #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
*/
public Builder setUseProxyProperties(boolean useProxyProperties) {
this.useProxyProperties = useProxyProperties;
return this;
}
public Builder setIOThreadMultiplier(int multiplier) {
this.ioThreadMultiplier = multiplier;
return this;
}
/**
* Set the {@link HostnameVerifier}
*
* @param hostnameVerifier {@link HostnameVerifier}
* @return this
*/
public Builder setHostnameVerifier(HostnameVerifier hostnameVerifier) {
this.hostnameVerifier = hostnameVerifier;
return this;
}
/**
* Configures this AHC instance to be strict in it's handling of 302 redirects
* in a POST/Redirect/GET situation.
*
* @param strict302Handling strict handling
*
* @return this
*
* @since 1.7.2
*/
public Builder setStrict302Handling(final boolean strict302Handling) {
this.strict302Handling = strict302Handling;
return this;
}
/**
* Set the maximum time in millisecond connection can be added to the pool for further reuse
*
* @param maxConnectionLifeTimeInMs the maximum time in millisecond connection can be added to the pool for further reuse
* @return a {@link Builder}
*/
public Builder setMaxConnectionLifeTimeInMs(int maxConnectionLifeTimeInMs) {
this.defaultMaxConnectionLifeTimeInMs = maxConnectionLifeTimeInMs;
return this;
}
/**
* Configures this AHC instance to use relative URIs instead of absolute ones when talking with a SSL proxy.
*
* @param useRelativeURIsWithSSLProxies
* @return this
*
* @since 1.7.2
*/
public Builder setUseRelativeURIsWithSSLProxies(boolean useRelativeURIsWithSSLProxies) {
this.useRelativeURIsWithSSLProxies = useRelativeURIsWithSSLProxies;
return this;
}
/**
* Enables SPDY support. Note that doing so, will currently disable WebSocket support
* for this client instance. If not explicitly enabled, spdy will not be used.
*
* @param spdyEnabled configures spdy support.
*
* @return this
*
* @since 2.0
*/
public Builder setSpdyEnabled(boolean spdyEnabled) {
this.spdyEnabled = spdyEnabled;
return this;
}
/**
* Configures the initial window size for the SPDY session.
*
* @param spdyInitialWindowSize the initial window size.
*
* @return this
*
* @since 2.0
*/
public Builder setSpdyInitialWindowSize(int spdyInitialWindowSize) {
this.spdyInitialWindowSize = spdyInitialWindowSize;
return this;
}
/**
* Configures the maximum number of concurrent streams over a single
* SPDY session.
*
* @param spdyMaxConcurrentStreams the maximum number of concurrent
* streams over a single SPDY session.
*
* @return this
*
* @since 2.0
*/
public Builder setSpdyMaxConcurrentStreams(int spdyMaxConcurrentStreams) {
this.spdyMaxConcurrentStreams = spdyMaxConcurrentStreams;
return this;
}
/**
* Configures this AHC instance to use RFC 6265 cookie encoding style
*
* @param rfc6265CookieEncoding
* @return this
*
* @since 1.7.18
*/
public Builder setRfc6265CookieEncoding(boolean rfc6265CookieEncoding) {
this.rfc6265CookieEncoding = rfc6265CookieEncoding;
return this;
}
/**
* Configures how the underlying providers make new connections. By default,
* connections will be made synchronously.
*
* @param asyncConnectMode pass true
to enable async connect mode.
*
* @return this
*
* @since 2.0.0
*/
public Builder setAsyncConnectMode(boolean asyncConnectMode) {
this.asyncConnectMode = asyncConnectMode;
return this;
}
/**
* Create a config builder with values taken from the given prototype configuration.
*
* @param prototype the configuration to use as a prototype.
*/
public Builder(AsyncHttpClientConfig prototype) {
allowPoolingConnection = prototype.getAllowPoolingConnection();
providerConfig = prototype.getAsyncHttpProviderConfig();
connectionsPool = prototype.getConnectionsPool();
defaultConnectionTimeOutInMs = prototype.getConnectionTimeoutInMs();
defaultIdleConnectionInPoolTimeoutInMs = prototype.getIdleConnectionInPoolTimeoutInMs();
defaultIdleConnectionTimeoutInMs = prototype.getIdleConnectionTimeoutInMs();
defaultMaxConnectionPerHost = prototype.getMaxConnectionPerHost();
defaultMaxConnectionLifeTimeInMs = prototype.getMaxConnectionLifeTimeInMs();
maxDefaultRedirects = prototype.getMaxRedirects();
defaultMaxTotalConnections = prototype.getMaxTotalConnections();
proxyServerSelector = prototype.getProxyServerSelector();
realm = prototype.getRealm();
defaultRequestTimeoutInMs = prototype.getRequestTimeoutInMs();
sslContext = prototype.getSSLContext();
sslEngineFactory = prototype.getSSLEngineFactory();
userAgent = prototype.getUserAgent();
redirectEnabled = prototype.isRedirectEnabled();
compressionEnabled = prototype.isCompressionEnabled();
reaper = prototype.reaper();
applicationThreadPool = prototype.executorService();
requestFilters.clear();
responseFilters.clear();
ioExceptionFilters.clear();
requestFilters.addAll(prototype.getRequestFilters());
responseFilters.addAll(prototype.getResponseFilters());
ioExceptionFilters.addAll(prototype.getIOExceptionFilters());
requestCompressionLevel = prototype.getRequestCompressionLevel();
useRawUrl = prototype.isUseRawUrl();
ioThreadMultiplier = prototype.getIoThreadMultiplier();
maxRequestRetry = prototype.getMaxRequestRetry();
allowSslConnectionPool = prototype.getAllowPoolingConnection();
removeQueryParamOnRedirect = prototype.isRemoveQueryParamOnRedirect();
hostnameVerifier = prototype.getHostnameVerifier();
strict302Handling = prototype.isStrict302Handling();
useRelativeURIsWithSSLProxies = prototype.isUseRelativeURIsWithSSLProxies();
rfc6265CookieEncoding = prototype.isRfc6265CookieEncoding();
asyncConnectMode = prototype.isAsyncConnectMode();
}
/**
* Build an {@link AsyncHttpClientConfig}
*
* @return an {@link AsyncHttpClientConfig}
*/
public AsyncHttpClientConfig build() {
if (reaper == null) {
reaper = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "AsyncHttpClient-Reaper");
t.setDaemon(true);
return t;
}
});
}
// if (applicationThreadPool == null) {
// applicationThreadPool =
// Executors.newCachedThreadPool(new ThreadFactory() {
// final AtomicInteger counter = new AtomicInteger();
// public Thread newThread(Runnable r) {
// Thread t = new Thread(r,
// "AsyncHttpClient-Callback-" + counter.incrementAndGet());
// t.setDaemon(true);
// return t;
// }
// });
// }
//
// if (applicationThreadPool.isShutdown()) {
// throw new IllegalStateException("ExecutorServices closed");
// }
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,
defaultMaxConnectionPerHost,
defaultConnectionTimeOutInMs,
defaultWebsocketIdleTimeoutInMs,
defaultIdleConnectionInPoolTimeoutInMs,
defaultIdleConnectionTimeoutInMs,
defaultRequestTimeoutInMs,
defaultMaxConnectionLifeTimeInMs,
redirectEnabled,
maxDefaultRedirects,
compressionEnabled,
userAgent,
allowPoolingConnection,
reaper,
applicationThreadPool,
proxyServerSelector,
sslContext,
sslEngineFactory,
providerConfig,
connectionsPool,
realm,
requestFilters,
responseFilters,
ioExceptionFilters,
requestCompressionLevel,
maxRequestRetry,
allowSslConnectionPool,
useRawUrl,
removeQueryParamOnRedirect,
hostnameVerifier,
ioThreadMultiplier,
strict302Handling,
useRelativeURIsWithSSLProxies,
spdyEnabled,
spdyInitialWindowSize,
spdyMaxConcurrentStreams,
rfc6265CookieEncoding,
asyncConnectMode);
}
}
}