Issue #1053 - Drop support Android and remove Fennec - Part 1a: Remove mobile/android

This commit is contained in:
Matt A. Tobin 2019-04-23 15:32:23 -04:00 committed by Roy Tam
commit c9b411d6cd
3891 changed files with 0 additions and 428084 deletions

View file

@ -1,164 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.util.HashMap;
import java.util.Map;
import ch.boye.httpclientandroidlib.client.BackoffManager;
import ch.boye.httpclientandroidlib.conn.routing.HttpRoute;
import ch.boye.httpclientandroidlib.pool.ConnPoolControl;
import ch.boye.httpclientandroidlib.util.Args;
/**
* <p>The <code>AIMDBackoffManager</code> applies an additive increase,
* multiplicative decrease (AIMD) to managing a dynamic limit to
* the number of connections allowed to a given host. You may want
* to experiment with the settings for the cooldown periods and the
* backoff factor to get the adaptive behavior you want.</p>
*
* <p>Generally speaking, shorter cooldowns will lead to more steady-state
* variability but faster reaction times, while longer cooldowns
* will lead to more stable equilibrium behavior but slower reaction
* times.</p>
*
* <p>Similarly, higher backoff factors promote greater
* utilization of available capacity at the expense of fairness
* among clients. Lower backoff factors allow equal distribution of
* capacity among clients (fairness) to happen faster, at the
* expense of having more server capacity unused in the short term.</p>
*
* @since 4.2
*/
public class AIMDBackoffManager implements BackoffManager {
private final ConnPoolControl<HttpRoute> connPerRoute;
private final Clock clock;
private final Map<HttpRoute,Long> lastRouteProbes;
private final Map<HttpRoute,Long> lastRouteBackoffs;
private long coolDown = 5 * 1000L;
private double backoffFactor = 0.5;
private int cap = 2; // Per RFC 2616 sec 8.1.4
/**
* Creates an <code>AIMDBackoffManager</code> to manage
* per-host connection pool sizes represented by the
* given {@link ConnPoolControl}.
* @param connPerRoute per-host routing maximums to
* be managed
*/
public AIMDBackoffManager(final ConnPoolControl<HttpRoute> connPerRoute) {
this(connPerRoute, new SystemClock());
}
AIMDBackoffManager(final ConnPoolControl<HttpRoute> connPerRoute, final Clock clock) {
this.clock = clock;
this.connPerRoute = connPerRoute;
this.lastRouteProbes = new HashMap<HttpRoute,Long>();
this.lastRouteBackoffs = new HashMap<HttpRoute,Long>();
}
public void backOff(final HttpRoute route) {
synchronized(connPerRoute) {
final int curr = connPerRoute.getMaxPerRoute(route);
final Long lastUpdate = getLastUpdate(lastRouteBackoffs, route);
final long now = clock.getCurrentTime();
if (now - lastUpdate.longValue() < coolDown) {
return;
}
connPerRoute.setMaxPerRoute(route, getBackedOffPoolSize(curr));
lastRouteBackoffs.put(route, Long.valueOf(now));
}
}
private int getBackedOffPoolSize(final int curr) {
if (curr <= 1) {
return 1;
}
return (int)(Math.floor(backoffFactor * curr));
}
public void probe(final HttpRoute route) {
synchronized(connPerRoute) {
final int curr = connPerRoute.getMaxPerRoute(route);
final int max = (curr >= cap) ? cap : curr + 1;
final Long lastProbe = getLastUpdate(lastRouteProbes, route);
final Long lastBackoff = getLastUpdate(lastRouteBackoffs, route);
final long now = clock.getCurrentTime();
if (now - lastProbe.longValue() < coolDown || now - lastBackoff.longValue() < coolDown) {
return;
}
connPerRoute.setMaxPerRoute(route, max);
lastRouteProbes.put(route, Long.valueOf(now));
}
}
private Long getLastUpdate(final Map<HttpRoute,Long> updates, final HttpRoute route) {
Long lastUpdate = updates.get(route);
if (lastUpdate == null) {
lastUpdate = Long.valueOf(0L);
}
return lastUpdate;
}
/**
* Sets the factor to use when backing off; the new
* per-host limit will be roughly the current max times
* this factor. <code>Math.floor</code> is applied in the
* case of non-integer outcomes to ensure we actually
* decrease the pool size. Pool sizes are never decreased
* below 1, however. Defaults to 0.5.
* @param d must be between 0.0 and 1.0, exclusive.
*/
public void setBackoffFactor(final double d) {
Args.check(d > 0.0 && d < 1.0, "Backoff factor must be 0.0 < f < 1.0");
backoffFactor = d;
}
/**
* Sets the amount of time, in milliseconds, to wait between
* adjustments in pool sizes for a given host, to allow
* enough time for the adjustments to take effect. Defaults
* to 5000L (5 seconds).
* @param l must be positive
*/
public void setCooldownMillis(final long l) {
Args.positive(coolDown, "Cool down");
coolDown = l;
}
/**
* Sets the absolute maximum per-host connection pool size to
* probe up to; defaults to 2 (the default per-host max).
* @param cap must be >= 1
*/
public void setPerHostConnectionCap(final int cap) {
Args.positive(cap, "Per host connection cap");
this.cap = cap;
}
}

View file

@ -1,189 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
/* LogFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.FormattedHeader;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.auth.AuthScheme;
import ch.boye.httpclientandroidlib.auth.AuthSchemeRegistry;
import ch.boye.httpclientandroidlib.auth.AuthenticationException;
import ch.boye.httpclientandroidlib.auth.MalformedChallengeException;
import ch.boye.httpclientandroidlib.client.AuthenticationHandler;
import ch.boye.httpclientandroidlib.client.params.AuthPolicy;
import ch.boye.httpclientandroidlib.client.protocol.ClientContext;
import ch.boye.httpclientandroidlib.protocol.HTTP;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
import ch.boye.httpclientandroidlib.util.Asserts;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
/**
* Base class for {@link AuthenticationHandler} implementations.
*
* @since 4.0
*
* @deprecated (4.2) use {@link ch.boye.httpclientandroidlib.client.AuthenticationStrategy}
*/
@Deprecated
@Immutable
public abstract class AbstractAuthenticationHandler implements AuthenticationHandler {
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
private static final List<String> DEFAULT_SCHEME_PRIORITY =
Collections.unmodifiableList(Arrays.asList(new String[] {
AuthPolicy.SPNEGO,
AuthPolicy.NTLM,
AuthPolicy.DIGEST,
AuthPolicy.BASIC
}));
public AbstractAuthenticationHandler() {
super();
}
protected Map<String, Header> parseChallenges(
final Header[] headers) throws MalformedChallengeException {
final Map<String, Header> map = new HashMap<String, Header>(headers.length);
for (final Header header : headers) {
final CharArrayBuffer buffer;
int pos;
if (header instanceof FormattedHeader) {
buffer = ((FormattedHeader) header).getBuffer();
pos = ((FormattedHeader) header).getValuePos();
} else {
final String s = header.getValue();
if (s == null) {
throw new MalformedChallengeException("Header value is null");
}
buffer = new CharArrayBuffer(s.length());
buffer.append(s);
pos = 0;
}
while (pos < buffer.length() && HTTP.isWhitespace(buffer.charAt(pos))) {
pos++;
}
final int beginIndex = pos;
while (pos < buffer.length() && !HTTP.isWhitespace(buffer.charAt(pos))) {
pos++;
}
final int endIndex = pos;
final String s = buffer.substring(beginIndex, endIndex);
map.put(s.toLowerCase(Locale.ENGLISH), header);
}
return map;
}
/**
* Returns default list of auth scheme names in their order of preference.
*
* @return list of auth scheme names
*/
protected List<String> getAuthPreferences() {
return DEFAULT_SCHEME_PRIORITY;
}
/**
* Returns default list of auth scheme names in their order of preference
* based on the HTTP response and the current execution context.
*
* @param response HTTP response.
* @param context HTTP execution context.
*
* @since 4.1
*/
protected List<String> getAuthPreferences(
final HttpResponse response,
final HttpContext context) {
return getAuthPreferences();
}
public AuthScheme selectScheme(
final Map<String, Header> challenges,
final HttpResponse response,
final HttpContext context) throws AuthenticationException {
final AuthSchemeRegistry registry = (AuthSchemeRegistry) context.getAttribute(
ClientContext.AUTHSCHEME_REGISTRY);
Asserts.notNull(registry, "AuthScheme registry");
Collection<String> authPrefs = getAuthPreferences(response, context);
if (authPrefs == null) {
authPrefs = DEFAULT_SCHEME_PRIORITY;
}
if (this.log.isDebugEnabled()) {
this.log.debug("Authentication schemes in the order of preference: "
+ authPrefs);
}
AuthScheme authScheme = null;
for (final String id: authPrefs) {
final Header challenge = challenges.get(id.toLowerCase(Locale.ENGLISH));
if (challenge != null) {
if (this.log.isDebugEnabled()) {
this.log.debug(id + " authentication scheme selected");
}
try {
authScheme = registry.getAuthScheme(id, response.getParams());
break;
} catch (final IllegalStateException e) {
if (this.log.isWarnEnabled()) {
this.log.warn("Authentication scheme " + id + " not supported");
// Try again
}
}
} else {
if (this.log.isDebugEnabled()) {
this.log.debug("Challenge for " + id + " authentication scheme not available");
// Try again
}
}
}
if (authScheme == null) {
// If none selected, something is wrong
throw new AuthenticationException(
"Unable to respond to any of these challenges: "
+ challenges);
}
return authScheme;
}
}

View file

@ -1,990 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.io.IOException;
import java.lang.reflect.UndeclaredThrowableException;
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
/* LogFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.ConnectionReuseStrategy;
import ch.boye.httpclientandroidlib.HttpException;
import ch.boye.httpclientandroidlib.HttpHost;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.HttpRequestInterceptor;
import ch.boye.httpclientandroidlib.HttpResponseInterceptor;
import ch.boye.httpclientandroidlib.annotation.GuardedBy;
import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
import ch.boye.httpclientandroidlib.auth.AuthSchemeRegistry;
import ch.boye.httpclientandroidlib.client.AuthenticationHandler;
import ch.boye.httpclientandroidlib.client.AuthenticationStrategy;
import ch.boye.httpclientandroidlib.client.BackoffManager;
import ch.boye.httpclientandroidlib.client.ClientProtocolException;
import ch.boye.httpclientandroidlib.client.ConnectionBackoffStrategy;
import ch.boye.httpclientandroidlib.client.CookieStore;
import ch.boye.httpclientandroidlib.client.CredentialsProvider;
import ch.boye.httpclientandroidlib.client.HttpRequestRetryHandler;
import ch.boye.httpclientandroidlib.client.RedirectHandler;
import ch.boye.httpclientandroidlib.client.RedirectStrategy;
import ch.boye.httpclientandroidlib.client.RequestDirector;
import ch.boye.httpclientandroidlib.client.UserTokenHandler;
import ch.boye.httpclientandroidlib.client.config.RequestConfig;
import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
import ch.boye.httpclientandroidlib.client.params.AuthPolicy;
import ch.boye.httpclientandroidlib.client.params.ClientPNames;
import ch.boye.httpclientandroidlib.client.params.CookiePolicy;
import ch.boye.httpclientandroidlib.client.params.HttpClientParamConfig;
import ch.boye.httpclientandroidlib.client.protocol.ClientContext;
import ch.boye.httpclientandroidlib.conn.ClientConnectionManager;
import ch.boye.httpclientandroidlib.conn.ClientConnectionManagerFactory;
import ch.boye.httpclientandroidlib.conn.ConnectionKeepAliveStrategy;
import ch.boye.httpclientandroidlib.conn.routing.HttpRoute;
import ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner;
import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry;
import ch.boye.httpclientandroidlib.cookie.CookieSpecRegistry;
import ch.boye.httpclientandroidlib.impl.DefaultConnectionReuseStrategy;
import ch.boye.httpclientandroidlib.impl.auth.BasicSchemeFactory;
import ch.boye.httpclientandroidlib.impl.auth.DigestSchemeFactory;
/* KerberosSchemeFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.impl.auth.NTLMSchemeFactory;
/* SPNegoSchemeFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.impl.conn.BasicClientConnectionManager;
import ch.boye.httpclientandroidlib.impl.conn.DefaultHttpRoutePlanner;
import ch.boye.httpclientandroidlib.impl.conn.SchemeRegistryFactory;
import ch.boye.httpclientandroidlib.impl.cookie.BestMatchSpecFactory;
import ch.boye.httpclientandroidlib.impl.cookie.BrowserCompatSpecFactory;
import ch.boye.httpclientandroidlib.impl.cookie.IgnoreSpecFactory;
import ch.boye.httpclientandroidlib.impl.cookie.NetscapeDraftSpecFactory;
import ch.boye.httpclientandroidlib.impl.cookie.RFC2109SpecFactory;
import ch.boye.httpclientandroidlib.impl.cookie.RFC2965SpecFactory;
import ch.boye.httpclientandroidlib.params.HttpParams;
import ch.boye.httpclientandroidlib.protocol.BasicHttpContext;
import ch.boye.httpclientandroidlib.protocol.BasicHttpProcessor;
import ch.boye.httpclientandroidlib.protocol.DefaultedHttpContext;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
import ch.boye.httpclientandroidlib.protocol.HttpProcessor;
import ch.boye.httpclientandroidlib.protocol.HttpRequestExecutor;
import ch.boye.httpclientandroidlib.protocol.ImmutableHttpProcessor;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Base class for {@link ch.boye.httpclientandroidlib.client.HttpClient} implementations.
* This class acts as a facade to a number of special purpose handler or
* strategy implementations responsible for handling of a particular aspect
* of the HTTP protocol such as redirect or authentication handling or
* making decision about connection persistence and keep alive duration.
* This enables the users to selectively replace default implementation
* of those aspects with custom, application specific ones. This class
* also provides factory methods to instantiate those objects:
* <ul>
* <li>{@link HttpRequestExecutor}</li> object used to transmit messages
* over HTTP connections. The {@link #createRequestExecutor()} must be
* implemented by concrete super classes to instantiate this object.
* <li>{@link BasicHttpProcessor}</li> object to manage a list of protocol
* interceptors and apply cross-cutting protocol logic to all incoming
* and outgoing HTTP messages. The {@link #createHttpProcessor()} must be
* implemented by concrete super classes to instantiate this object.
* <li>{@link HttpRequestRetryHandler}</li> object used to decide whether
* or not a failed HTTP request is safe to retry automatically.
* The {@link #createHttpRequestRetryHandler()} must be
* implemented by concrete super classes to instantiate this object.
* <li>{@link ClientConnectionManager}</li> object used to manage
* persistent HTTP connections.
* <li>{@link ConnectionReuseStrategy}</li> object used to decide whether
* or not a HTTP connection can be kept alive and re-used for subsequent
* HTTP requests. The {@link #createConnectionReuseStrategy()} must be
* implemented by concrete super classes to instantiate this object.
* <li>{@link ConnectionKeepAliveStrategy}</li> object used to decide how
* long a persistent HTTP connection can be kept alive.
* The {@link #createConnectionKeepAliveStrategy()} must be
* implemented by concrete super classes to instantiate this object.
* <li>{@link CookieSpecRegistry}</li> object used to maintain a list of
* supported cookie specifications.
* The {@link #createCookieSpecRegistry()} must be implemented by concrete
* super classes to instantiate this object.
* <li>{@link CookieStore}</li> object used to maintain a collection of
* cookies. The {@link #createCookieStore()} must be implemented by
* concrete super classes to instantiate this object.
* <li>{@link AuthSchemeRegistry}</li> object used to maintain a list of
* supported authentication schemes.
* The {@link #createAuthSchemeRegistry()} must be implemented by concrete
* super classes to instantiate this object.
* <li>{@link CredentialsProvider}</li> object used to maintain
* a collection user credentials. The {@link #createCredentialsProvider()}
* must be implemented by concrete super classes to instantiate
* this object.
* <li>{@link AuthenticationStrategy}</li> object used to authenticate
* against the target host.
* The {@link #createTargetAuthenticationStrategy()} must be implemented
* by concrete super classes to instantiate this object.
* <li>{@link AuthenticationStrategy}</li> object used to authenticate
* against the proxy host.
* The {@link #createProxyAuthenticationStrategy()} must be implemented
* by concrete super classes to instantiate this object.
* <li>{@link HttpRoutePlanner}</li> object used to calculate a route
* for establishing a connection to the target host. The route
* may involve multiple intermediate hops.
* The {@link #createHttpRoutePlanner()} must be implemented
* by concrete super classes to instantiate this object.
* <li>{@link RedirectStrategy}</li> object used to determine if an HTTP
* request should be redirected to a new location in response to an HTTP
* response received from the target server.
* <li>{@link UserTokenHandler}</li> object used to determine if the
* execution context is user identity specific.
* The {@link #createUserTokenHandler()} must be implemented by
* concrete super classes to instantiate this object.
* </ul>
* <p>
* This class also maintains a list of protocol interceptors intended
* for processing outgoing requests and incoming responses and provides
* methods for managing those interceptors. New protocol interceptors can be
* introduced to the protocol processor chain or removed from it if needed.
* Internally protocol interceptors are stored in a simple
* {@link java.util.ArrayList}. They are executed in the same natural order
* as they are added to the list.
* <p>
* AbstractHttpClient is thread safe. It is recommended that the same
* instance of this class is reused for multiple request executions.
* When an instance of DefaultHttpClient is no longer needed and is about
* to go out of scope the connection manager associated with it must be
* shut down by calling {@link ClientConnectionManager#shutdown()}!
*
* @since 4.0
*
* @deprecated (4.3) use {@link HttpClientBuilder}.
*/
@ThreadSafe
@Deprecated
public abstract class AbstractHttpClient extends CloseableHttpClient {
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
/** The parameters. */
@GuardedBy("this")
private HttpParams defaultParams;
/** The request executor. */
@GuardedBy("this")
private HttpRequestExecutor requestExec;
/** The connection manager. */
@GuardedBy("this")
private ClientConnectionManager connManager;
/** The connection re-use strategy. */
@GuardedBy("this")
private ConnectionReuseStrategy reuseStrategy;
/** The connection keep-alive strategy. */
@GuardedBy("this")
private ConnectionKeepAliveStrategy keepAliveStrategy;
/** The cookie spec registry. */
@GuardedBy("this")
private CookieSpecRegistry supportedCookieSpecs;
/** The authentication scheme registry. */
@GuardedBy("this")
private AuthSchemeRegistry supportedAuthSchemes;
/** The HTTP protocol processor and its immutable copy. */
@GuardedBy("this")
private BasicHttpProcessor mutableProcessor;
@GuardedBy("this")
private ImmutableHttpProcessor protocolProcessor;
/** The request retry handler. */
@GuardedBy("this")
private HttpRequestRetryHandler retryHandler;
/** The redirect handler. */
@GuardedBy("this")
private RedirectStrategy redirectStrategy;
/** The target authentication handler. */
@GuardedBy("this")
private AuthenticationStrategy targetAuthStrategy;
/** The proxy authentication handler. */
@GuardedBy("this")
private AuthenticationStrategy proxyAuthStrategy;
/** The cookie store. */
@GuardedBy("this")
private CookieStore cookieStore;
/** The credentials provider. */
@GuardedBy("this")
private CredentialsProvider credsProvider;
/** The route planner. */
@GuardedBy("this")
private HttpRoutePlanner routePlanner;
/** The user token handler. */
@GuardedBy("this")
private UserTokenHandler userTokenHandler;
/** The connection backoff strategy. */
@GuardedBy("this")
private ConnectionBackoffStrategy connectionBackoffStrategy;
/** The backoff manager. */
@GuardedBy("this")
private BackoffManager backoffManager;
/**
* Creates a new HTTP client.
*
* @param conman the connection manager
* @param params the parameters
*/
protected AbstractHttpClient(
final ClientConnectionManager conman,
final HttpParams params) {
super();
defaultParams = params;
connManager = conman;
} // constructor
protected abstract HttpParams createHttpParams();
protected abstract BasicHttpProcessor createHttpProcessor();
protected HttpContext createHttpContext() {
final HttpContext context = new BasicHttpContext();
context.setAttribute(
ClientContext.SCHEME_REGISTRY,
getConnectionManager().getSchemeRegistry());
context.setAttribute(
ClientContext.AUTHSCHEME_REGISTRY,
getAuthSchemes());
context.setAttribute(
ClientContext.COOKIESPEC_REGISTRY,
getCookieSpecs());
context.setAttribute(
ClientContext.COOKIE_STORE,
getCookieStore());
context.setAttribute(
ClientContext.CREDS_PROVIDER,
getCredentialsProvider());
return context;
}
protected ClientConnectionManager createClientConnectionManager() {
final SchemeRegistry registry = SchemeRegistryFactory.createDefault();
ClientConnectionManager connManager = null;
final HttpParams params = getParams();
ClientConnectionManagerFactory factory = null;
final String className = (String) params.getParameter(
ClientPNames.CONNECTION_MANAGER_FACTORY_CLASS_NAME);
if (className != null) {
try {
final Class<?> clazz = Class.forName(className);
factory = (ClientConnectionManagerFactory) clazz.newInstance();
} catch (final ClassNotFoundException ex) {
throw new IllegalStateException("Invalid class name: " + className);
} catch (final IllegalAccessException ex) {
throw new IllegalAccessError(ex.getMessage());
} catch (final InstantiationException ex) {
throw new InstantiationError(ex.getMessage());
}
}
if (factory != null) {
connManager = factory.newInstance(params, registry);
} else {
connManager = new BasicClientConnectionManager(registry);
}
return connManager;
}
protected AuthSchemeRegistry createAuthSchemeRegistry() {
final AuthSchemeRegistry registry = new AuthSchemeRegistry();
registry.register(
AuthPolicy.BASIC,
new BasicSchemeFactory());
registry.register(
AuthPolicy.DIGEST,
new DigestSchemeFactory());
registry.register(
AuthPolicy.NTLM,
new NTLMSchemeFactory());
/* SPNegoSchemeFactory removed by HttpClient for Android script. */
/* KerberosSchemeFactory removed by HttpClient for Android script. */
return registry;
}
protected CookieSpecRegistry createCookieSpecRegistry() {
final CookieSpecRegistry registry = new CookieSpecRegistry();
registry.register(
CookiePolicy.BEST_MATCH,
new BestMatchSpecFactory());
registry.register(
CookiePolicy.BROWSER_COMPATIBILITY,
new BrowserCompatSpecFactory());
registry.register(
CookiePolicy.NETSCAPE,
new NetscapeDraftSpecFactory());
registry.register(
CookiePolicy.RFC_2109,
new RFC2109SpecFactory());
registry.register(
CookiePolicy.RFC_2965,
new RFC2965SpecFactory());
registry.register(
CookiePolicy.IGNORE_COOKIES,
new IgnoreSpecFactory());
return registry;
}
protected HttpRequestExecutor createRequestExecutor() {
return new HttpRequestExecutor();
}
protected ConnectionReuseStrategy createConnectionReuseStrategy() {
return new DefaultConnectionReuseStrategy();
}
protected ConnectionKeepAliveStrategy createConnectionKeepAliveStrategy() {
return new DefaultConnectionKeepAliveStrategy();
}
protected HttpRequestRetryHandler createHttpRequestRetryHandler() {
return new DefaultHttpRequestRetryHandler();
}
/**
* @deprecated (4.1) do not use
*/
@Deprecated
protected RedirectHandler createRedirectHandler() {
return new DefaultRedirectHandler();
}
protected AuthenticationStrategy createTargetAuthenticationStrategy() {
return new TargetAuthenticationStrategy();
}
/**
* @deprecated (4.2) do not use
*/
@Deprecated
protected AuthenticationHandler createTargetAuthenticationHandler() {
return new DefaultTargetAuthenticationHandler();
}
protected AuthenticationStrategy createProxyAuthenticationStrategy() {
return new ProxyAuthenticationStrategy();
}
/**
* @deprecated (4.2) do not use
*/
@Deprecated
protected AuthenticationHandler createProxyAuthenticationHandler() {
return new DefaultProxyAuthenticationHandler();
}
protected CookieStore createCookieStore() {
return new BasicCookieStore();
}
protected CredentialsProvider createCredentialsProvider() {
return new BasicCredentialsProvider();
}
protected HttpRoutePlanner createHttpRoutePlanner() {
return new DefaultHttpRoutePlanner(getConnectionManager().getSchemeRegistry());
}
protected UserTokenHandler createUserTokenHandler() {
return new DefaultUserTokenHandler();
}
// non-javadoc, see interface HttpClient
public synchronized final HttpParams getParams() {
if (defaultParams == null) {
defaultParams = createHttpParams();
}
return defaultParams;
}
/**
* Replaces the parameters.
* The implementation here does not update parameters of dependent objects.
*
* @param params the new default parameters
*/
public synchronized void setParams(final HttpParams params) {
defaultParams = params;
}
public synchronized final ClientConnectionManager getConnectionManager() {
if (connManager == null) {
connManager = createClientConnectionManager();
}
return connManager;
}
public synchronized final HttpRequestExecutor getRequestExecutor() {
if (requestExec == null) {
requestExec = createRequestExecutor();
}
return requestExec;
}
public synchronized final AuthSchemeRegistry getAuthSchemes() {
if (supportedAuthSchemes == null) {
supportedAuthSchemes = createAuthSchemeRegistry();
}
return supportedAuthSchemes;
}
public synchronized void setAuthSchemes(final AuthSchemeRegistry registry) {
supportedAuthSchemes = registry;
}
public synchronized final ConnectionBackoffStrategy getConnectionBackoffStrategy() {
return connectionBackoffStrategy;
}
public synchronized void setConnectionBackoffStrategy(final ConnectionBackoffStrategy strategy) {
connectionBackoffStrategy = strategy;
}
public synchronized final CookieSpecRegistry getCookieSpecs() {
if (supportedCookieSpecs == null) {
supportedCookieSpecs = createCookieSpecRegistry();
}
return supportedCookieSpecs;
}
public synchronized final BackoffManager getBackoffManager() {
return backoffManager;
}
public synchronized void setBackoffManager(final BackoffManager manager) {
backoffManager = manager;
}
public synchronized void setCookieSpecs(final CookieSpecRegistry registry) {
supportedCookieSpecs = registry;
}
public synchronized final ConnectionReuseStrategy getConnectionReuseStrategy() {
if (reuseStrategy == null) {
reuseStrategy = createConnectionReuseStrategy();
}
return reuseStrategy;
}
public synchronized void setReuseStrategy(final ConnectionReuseStrategy strategy) {
this.reuseStrategy = strategy;
}
public synchronized final ConnectionKeepAliveStrategy getConnectionKeepAliveStrategy() {
if (keepAliveStrategy == null) {
keepAliveStrategy = createConnectionKeepAliveStrategy();
}
return keepAliveStrategy;
}
public synchronized void setKeepAliveStrategy(final ConnectionKeepAliveStrategy strategy) {
this.keepAliveStrategy = strategy;
}
public synchronized final HttpRequestRetryHandler getHttpRequestRetryHandler() {
if (retryHandler == null) {
retryHandler = createHttpRequestRetryHandler();
}
return retryHandler;
}
public synchronized void setHttpRequestRetryHandler(final HttpRequestRetryHandler handler) {
this.retryHandler = handler;
}
/**
* @deprecated (4.1) do not use
*/
@Deprecated
public synchronized final RedirectHandler getRedirectHandler() {
return createRedirectHandler();
}
/**
* @deprecated (4.1) do not use
*/
@Deprecated
public synchronized void setRedirectHandler(final RedirectHandler handler) {
this.redirectStrategy = new DefaultRedirectStrategyAdaptor(handler);
}
/**
* @since 4.1
*/
public synchronized final RedirectStrategy getRedirectStrategy() {
if (redirectStrategy == null) {
redirectStrategy = new DefaultRedirectStrategy();
}
return redirectStrategy;
}
/**
* @since 4.1
*/
public synchronized void setRedirectStrategy(final RedirectStrategy strategy) {
this.redirectStrategy = strategy;
}
/**
* @deprecated (4.2) do not use
*/
@Deprecated
public synchronized final AuthenticationHandler getTargetAuthenticationHandler() {
return createTargetAuthenticationHandler();
}
/**
* @deprecated (4.2) do not use
*/
@Deprecated
public synchronized void setTargetAuthenticationHandler(final AuthenticationHandler handler) {
this.targetAuthStrategy = new AuthenticationStrategyAdaptor(handler);
}
/**
* @since 4.2
*/
public synchronized final AuthenticationStrategy getTargetAuthenticationStrategy() {
if (targetAuthStrategy == null) {
targetAuthStrategy = createTargetAuthenticationStrategy();
}
return targetAuthStrategy;
}
/**
* @since 4.2
*/
public synchronized void setTargetAuthenticationStrategy(final AuthenticationStrategy strategy) {
this.targetAuthStrategy = strategy;
}
/**
* @deprecated (4.2) do not use
*/
@Deprecated
public synchronized final AuthenticationHandler getProxyAuthenticationHandler() {
return createProxyAuthenticationHandler();
}
/**
* @deprecated (4.2) do not use
*/
@Deprecated
public synchronized void setProxyAuthenticationHandler(final AuthenticationHandler handler) {
this.proxyAuthStrategy = new AuthenticationStrategyAdaptor(handler);
}
/**
* @since 4.2
*/
public synchronized final AuthenticationStrategy getProxyAuthenticationStrategy() {
if (proxyAuthStrategy == null) {
proxyAuthStrategy = createProxyAuthenticationStrategy();
}
return proxyAuthStrategy;
}
/**
* @since 4.2
*/
public synchronized void setProxyAuthenticationStrategy(final AuthenticationStrategy strategy) {
this.proxyAuthStrategy = strategy;
}
public synchronized final CookieStore getCookieStore() {
if (cookieStore == null) {
cookieStore = createCookieStore();
}
return cookieStore;
}
public synchronized void setCookieStore(final CookieStore cookieStore) {
this.cookieStore = cookieStore;
}
public synchronized final CredentialsProvider getCredentialsProvider() {
if (credsProvider == null) {
credsProvider = createCredentialsProvider();
}
return credsProvider;
}
public synchronized void setCredentialsProvider(final CredentialsProvider credsProvider) {
this.credsProvider = credsProvider;
}
public synchronized final HttpRoutePlanner getRoutePlanner() {
if (this.routePlanner == null) {
this.routePlanner = createHttpRoutePlanner();
}
return this.routePlanner;
}
public synchronized void setRoutePlanner(final HttpRoutePlanner routePlanner) {
this.routePlanner = routePlanner;
}
public synchronized final UserTokenHandler getUserTokenHandler() {
if (this.userTokenHandler == null) {
this.userTokenHandler = createUserTokenHandler();
}
return this.userTokenHandler;
}
public synchronized void setUserTokenHandler(final UserTokenHandler handler) {
this.userTokenHandler = handler;
}
protected synchronized final BasicHttpProcessor getHttpProcessor() {
if (mutableProcessor == null) {
mutableProcessor = createHttpProcessor();
}
return mutableProcessor;
}
private synchronized HttpProcessor getProtocolProcessor() {
if (protocolProcessor == null) {
// Get mutable HTTP processor
final BasicHttpProcessor proc = getHttpProcessor();
// and create an immutable copy of it
final int reqc = proc.getRequestInterceptorCount();
final HttpRequestInterceptor[] reqinterceptors = new HttpRequestInterceptor[reqc];
for (int i = 0; i < reqc; i++) {
reqinterceptors[i] = proc.getRequestInterceptor(i);
}
final int resc = proc.getResponseInterceptorCount();
final HttpResponseInterceptor[] resinterceptors = new HttpResponseInterceptor[resc];
for (int i = 0; i < resc; i++) {
resinterceptors[i] = proc.getResponseInterceptor(i);
}
protocolProcessor = new ImmutableHttpProcessor(reqinterceptors, resinterceptors);
}
return protocolProcessor;
}
public synchronized int getResponseInterceptorCount() {
return getHttpProcessor().getResponseInterceptorCount();
}
public synchronized HttpResponseInterceptor getResponseInterceptor(final int index) {
return getHttpProcessor().getResponseInterceptor(index);
}
public synchronized HttpRequestInterceptor getRequestInterceptor(final int index) {
return getHttpProcessor().getRequestInterceptor(index);
}
public synchronized int getRequestInterceptorCount() {
return getHttpProcessor().getRequestInterceptorCount();
}
public synchronized void addResponseInterceptor(final HttpResponseInterceptor itcp) {
getHttpProcessor().addInterceptor(itcp);
protocolProcessor = null;
}
public synchronized void addResponseInterceptor(final HttpResponseInterceptor itcp, final int index) {
getHttpProcessor().addInterceptor(itcp, index);
protocolProcessor = null;
}
public synchronized void clearResponseInterceptors() {
getHttpProcessor().clearResponseInterceptors();
protocolProcessor = null;
}
public synchronized void removeResponseInterceptorByClass(final Class<? extends HttpResponseInterceptor> clazz) {
getHttpProcessor().removeResponseInterceptorByClass(clazz);
protocolProcessor = null;
}
public synchronized void addRequestInterceptor(final HttpRequestInterceptor itcp) {
getHttpProcessor().addInterceptor(itcp);
protocolProcessor = null;
}
public synchronized void addRequestInterceptor(final HttpRequestInterceptor itcp, final int index) {
getHttpProcessor().addInterceptor(itcp, index);
protocolProcessor = null;
}
public synchronized void clearRequestInterceptors() {
getHttpProcessor().clearRequestInterceptors();
protocolProcessor = null;
}
public synchronized void removeRequestInterceptorByClass(final Class<? extends HttpRequestInterceptor> clazz) {
getHttpProcessor().removeRequestInterceptorByClass(clazz);
protocolProcessor = null;
}
@Override
protected final CloseableHttpResponse doExecute(final HttpHost target, final HttpRequest request,
final HttpContext context)
throws IOException, ClientProtocolException {
Args.notNull(request, "HTTP request");
// a null target may be acceptable, this depends on the route planner
// a null context is acceptable, default context created below
HttpContext execContext = null;
RequestDirector director = null;
HttpRoutePlanner routePlanner = null;
ConnectionBackoffStrategy connectionBackoffStrategy = null;
BackoffManager backoffManager = null;
// Initialize the request execution context making copies of
// all shared objects that are potentially threading unsafe.
synchronized (this) {
final HttpContext defaultContext = createHttpContext();
if (context == null) {
execContext = defaultContext;
} else {
execContext = new DefaultedHttpContext(context, defaultContext);
}
final HttpParams params = determineParams(request);
final RequestConfig config = HttpClientParamConfig.getRequestConfig(params);
execContext.setAttribute(ClientContext.REQUEST_CONFIG, config);
// Create a director for this request
director = createClientRequestDirector(
getRequestExecutor(),
getConnectionManager(),
getConnectionReuseStrategy(),
getConnectionKeepAliveStrategy(),
getRoutePlanner(),
getProtocolProcessor(),
getHttpRequestRetryHandler(),
getRedirectStrategy(),
getTargetAuthenticationStrategy(),
getProxyAuthenticationStrategy(),
getUserTokenHandler(),
params);
routePlanner = getRoutePlanner();
connectionBackoffStrategy = getConnectionBackoffStrategy();
backoffManager = getBackoffManager();
}
try {
if (connectionBackoffStrategy != null && backoffManager != null) {
final HttpHost targetForRoute = (target != null) ? target
: (HttpHost) determineParams(request).getParameter(
ClientPNames.DEFAULT_HOST);
final HttpRoute route = routePlanner.determineRoute(targetForRoute, request, execContext);
final CloseableHttpResponse out;
try {
out = CloseableHttpResponseProxy.newProxy(
director.execute(target, request, execContext));
} catch (final RuntimeException re) {
if (connectionBackoffStrategy.shouldBackoff(re)) {
backoffManager.backOff(route);
}
throw re;
} catch (final Exception e) {
if (connectionBackoffStrategy.shouldBackoff(e)) {
backoffManager.backOff(route);
}
if (e instanceof HttpException) {
throw (HttpException)e;
}
if (e instanceof IOException) {
throw (IOException)e;
}
throw new UndeclaredThrowableException(e);
}
if (connectionBackoffStrategy.shouldBackoff(out)) {
backoffManager.backOff(route);
} else {
backoffManager.probe(route);
}
return out;
} else {
return CloseableHttpResponseProxy.newProxy(
director.execute(target, request, execContext));
}
} catch(final HttpException httpException) {
throw new ClientProtocolException(httpException);
}
}
/**
* @deprecated (4.1) do not use
*/
@Deprecated
protected RequestDirector createClientRequestDirector(
final HttpRequestExecutor requestExec,
final ClientConnectionManager conman,
final ConnectionReuseStrategy reustrat,
final ConnectionKeepAliveStrategy kastrat,
final HttpRoutePlanner rouplan,
final HttpProcessor httpProcessor,
final HttpRequestRetryHandler retryHandler,
final RedirectHandler redirectHandler,
final AuthenticationHandler targetAuthHandler,
final AuthenticationHandler proxyAuthHandler,
final UserTokenHandler userTokenHandler,
final HttpParams params) {
return new DefaultRequestDirector(
requestExec,
conman,
reustrat,
kastrat,
rouplan,
httpProcessor,
retryHandler,
redirectHandler,
targetAuthHandler,
proxyAuthHandler,
userTokenHandler,
params);
}
/**
* @deprecated (4.2) do not use
*/
@Deprecated
protected RequestDirector createClientRequestDirector(
final HttpRequestExecutor requestExec,
final ClientConnectionManager conman,
final ConnectionReuseStrategy reustrat,
final ConnectionKeepAliveStrategy kastrat,
final HttpRoutePlanner rouplan,
final HttpProcessor httpProcessor,
final HttpRequestRetryHandler retryHandler,
final RedirectStrategy redirectStrategy,
final AuthenticationHandler targetAuthHandler,
final AuthenticationHandler proxyAuthHandler,
final UserTokenHandler userTokenHandler,
final HttpParams params) {
return new DefaultRequestDirector(
log,
requestExec,
conman,
reustrat,
kastrat,
rouplan,
httpProcessor,
retryHandler,
redirectStrategy,
targetAuthHandler,
proxyAuthHandler,
userTokenHandler,
params);
}
/**
* @since 4.2
*/
protected RequestDirector createClientRequestDirector(
final HttpRequestExecutor requestExec,
final ClientConnectionManager conman,
final ConnectionReuseStrategy reustrat,
final ConnectionKeepAliveStrategy kastrat,
final HttpRoutePlanner rouplan,
final HttpProcessor httpProcessor,
final HttpRequestRetryHandler retryHandler,
final RedirectStrategy redirectStrategy,
final AuthenticationStrategy targetAuthStrategy,
final AuthenticationStrategy proxyAuthStrategy,
final UserTokenHandler userTokenHandler,
final HttpParams params) {
return new DefaultRequestDirector(
log,
requestExec,
conman,
reustrat,
kastrat,
rouplan,
httpProcessor,
retryHandler,
redirectStrategy,
targetAuthStrategy,
proxyAuthStrategy,
userTokenHandler,
params);
}
/**
* Obtains parameters for executing a request.
* The default implementation in this class creates a new
* {@link ClientParamsStack} from the request parameters
* and the client parameters.
* <br/>
* This method is called by the default implementation of
* {@link #execute(HttpHost,HttpRequest,HttpContext)}
* to obtain the parameters for the
* {@link DefaultRequestDirector}.
*
* @param req the request that will be executed
*
* @return the parameters to use
*/
protected HttpParams determineParams(final HttpRequest req) {
return new ClientParamsStack
(null, getParams(), req.getParams(), null);
}
public void close() {
getConnectionManager().shutdown();
}
}

View file

@ -1,172 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Map;
import java.util.Queue;
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
/* LogFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpHost;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.auth.AuthOption;
import ch.boye.httpclientandroidlib.auth.AuthScheme;
import ch.boye.httpclientandroidlib.auth.AuthScope;
import ch.boye.httpclientandroidlib.auth.AuthenticationException;
import ch.boye.httpclientandroidlib.auth.Credentials;
import ch.boye.httpclientandroidlib.auth.MalformedChallengeException;
import ch.boye.httpclientandroidlib.client.AuthCache;
import ch.boye.httpclientandroidlib.client.AuthenticationHandler;
import ch.boye.httpclientandroidlib.client.AuthenticationStrategy;
import ch.boye.httpclientandroidlib.client.CredentialsProvider;
import ch.boye.httpclientandroidlib.client.params.AuthPolicy;
import ch.boye.httpclientandroidlib.client.protocol.ClientContext;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
import ch.boye.httpclientandroidlib.util.Args;
/**
* @deprecated (4.2) do not use
*/
@Immutable
@Deprecated
class AuthenticationStrategyAdaptor implements AuthenticationStrategy {
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
private final AuthenticationHandler handler;
public AuthenticationStrategyAdaptor(final AuthenticationHandler handler) {
super();
this.handler = handler;
}
public boolean isAuthenticationRequested(
final HttpHost authhost,
final HttpResponse response,
final HttpContext context) {
return this.handler.isAuthenticationRequested(response, context);
}
public Map<String, Header> getChallenges(
final HttpHost authhost,
final HttpResponse response,
final HttpContext context) throws MalformedChallengeException {
return this.handler.getChallenges(response, context);
}
public Queue<AuthOption> select(
final Map<String, Header> challenges,
final HttpHost authhost,
final HttpResponse response,
final HttpContext context) throws MalformedChallengeException {
Args.notNull(challenges, "Map of auth challenges");
Args.notNull(authhost, "Host");
Args.notNull(response, "HTTP response");
Args.notNull(context, "HTTP context");
final Queue<AuthOption> options = new LinkedList<AuthOption>();
final CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(
ClientContext.CREDS_PROVIDER);
if (credsProvider == null) {
this.log.debug("Credentials provider not set in the context");
return options;
}
final AuthScheme authScheme;
try {
authScheme = this.handler.selectScheme(challenges, response, context);
} catch (final AuthenticationException ex) {
if (this.log.isWarnEnabled()) {
this.log.warn(ex.getMessage(), ex);
}
return options;
}
final String id = authScheme.getSchemeName();
final Header challenge = challenges.get(id.toLowerCase(Locale.ENGLISH));
authScheme.processChallenge(challenge);
final AuthScope authScope = new AuthScope(
authhost.getHostName(),
authhost.getPort(),
authScheme.getRealm(),
authScheme.getSchemeName());
final Credentials credentials = credsProvider.getCredentials(authScope);
if (credentials != null) {
options.add(new AuthOption(authScheme, credentials));
}
return options;
}
public void authSucceeded(
final HttpHost authhost, final AuthScheme authScheme, final HttpContext context) {
AuthCache authCache = (AuthCache) context.getAttribute(ClientContext.AUTH_CACHE);
if (isCachable(authScheme)) {
if (authCache == null) {
authCache = new BasicAuthCache();
context.setAttribute(ClientContext.AUTH_CACHE, authCache);
}
if (this.log.isDebugEnabled()) {
this.log.debug("Caching '" + authScheme.getSchemeName() +
"' auth scheme for " + authhost);
}
authCache.put(authhost, authScheme);
}
}
public void authFailed(
final HttpHost authhost, final AuthScheme authScheme, final HttpContext context) {
final AuthCache authCache = (AuthCache) context.getAttribute(ClientContext.AUTH_CACHE);
if (authCache == null) {
return;
}
if (this.log.isDebugEnabled()) {
this.log.debug("Removing from cache '" + authScheme.getSchemeName() +
"' auth scheme for " + authhost);
}
authCache.remove(authhost);
}
private boolean isCachable(final AuthScheme authScheme) {
if (authScheme == null || !authScheme.isComplete()) {
return false;
}
final String schemeName = authScheme.getSchemeName();
return schemeName.equalsIgnoreCase(AuthPolicy.BASIC) ||
schemeName.equalsIgnoreCase(AuthPolicy.DIGEST);
}
public AuthenticationHandler getHandler() {
return this.handler;
}
}

View file

@ -1,245 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Queue;
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
/* LogFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.FormattedHeader;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpHost;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.auth.AuthOption;
import ch.boye.httpclientandroidlib.auth.AuthScheme;
import ch.boye.httpclientandroidlib.auth.AuthSchemeProvider;
import ch.boye.httpclientandroidlib.auth.AuthScope;
import ch.boye.httpclientandroidlib.auth.Credentials;
import ch.boye.httpclientandroidlib.auth.MalformedChallengeException;
import ch.boye.httpclientandroidlib.client.AuthCache;
import ch.boye.httpclientandroidlib.client.AuthenticationStrategy;
import ch.boye.httpclientandroidlib.client.CredentialsProvider;
import ch.boye.httpclientandroidlib.client.config.AuthSchemes;
import ch.boye.httpclientandroidlib.client.config.RequestConfig;
import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
import ch.boye.httpclientandroidlib.config.Lookup;
import ch.boye.httpclientandroidlib.protocol.HTTP;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
@Immutable
abstract class AuthenticationStrategyImpl implements AuthenticationStrategy {
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
private static final List<String> DEFAULT_SCHEME_PRIORITY =
Collections.unmodifiableList(Arrays.asList(AuthSchemes.SPNEGO,
AuthSchemes.KERBEROS,
AuthSchemes.NTLM,
AuthSchemes.DIGEST,
AuthSchemes.BASIC));
private final int challengeCode;
private final String headerName;
AuthenticationStrategyImpl(final int challengeCode, final String headerName) {
super();
this.challengeCode = challengeCode;
this.headerName = headerName;
}
public boolean isAuthenticationRequested(
final HttpHost authhost,
final HttpResponse response,
final HttpContext context) {
Args.notNull(response, "HTTP response");
final int status = response.getStatusLine().getStatusCode();
return status == this.challengeCode;
}
public Map<String, Header> getChallenges(
final HttpHost authhost,
final HttpResponse response,
final HttpContext context) throws MalformedChallengeException {
Args.notNull(response, "HTTP response");
final Header[] headers = response.getHeaders(this.headerName);
final Map<String, Header> map = new HashMap<String, Header>(headers.length);
for (final Header header : headers) {
final CharArrayBuffer buffer;
int pos;
if (header instanceof FormattedHeader) {
buffer = ((FormattedHeader) header).getBuffer();
pos = ((FormattedHeader) header).getValuePos();
} else {
final String s = header.getValue();
if (s == null) {
throw new MalformedChallengeException("Header value is null");
}
buffer = new CharArrayBuffer(s.length());
buffer.append(s);
pos = 0;
}
while (pos < buffer.length() && HTTP.isWhitespace(buffer.charAt(pos))) {
pos++;
}
final int beginIndex = pos;
while (pos < buffer.length() && !HTTP.isWhitespace(buffer.charAt(pos))) {
pos++;
}
final int endIndex = pos;
final String s = buffer.substring(beginIndex, endIndex);
map.put(s.toLowerCase(Locale.ENGLISH), header);
}
return map;
}
abstract Collection<String> getPreferredAuthSchemes(RequestConfig config);
public Queue<AuthOption> select(
final Map<String, Header> challenges,
final HttpHost authhost,
final HttpResponse response,
final HttpContext context) throws MalformedChallengeException {
Args.notNull(challenges, "Map of auth challenges");
Args.notNull(authhost, "Host");
Args.notNull(response, "HTTP response");
Args.notNull(context, "HTTP context");
final HttpClientContext clientContext = HttpClientContext.adapt(context);
final Queue<AuthOption> options = new LinkedList<AuthOption>();
final Lookup<AuthSchemeProvider> registry = clientContext.getAuthSchemeRegistry();
if (registry == null) {
this.log.debug("Auth scheme registry not set in the context");
return options;
}
final CredentialsProvider credsProvider = clientContext.getCredentialsProvider();
if (credsProvider == null) {
this.log.debug("Credentials provider not set in the context");
return options;
}
final RequestConfig config = clientContext.getRequestConfig();
Collection<String> authPrefs = getPreferredAuthSchemes(config);
if (authPrefs == null) {
authPrefs = DEFAULT_SCHEME_PRIORITY;
}
if (this.log.isDebugEnabled()) {
this.log.debug("Authentication schemes in the order of preference: " + authPrefs);
}
for (final String id: authPrefs) {
final Header challenge = challenges.get(id.toLowerCase(Locale.ENGLISH));
if (challenge != null) {
final AuthSchemeProvider authSchemeProvider = registry.lookup(id);
if (authSchemeProvider == null) {
if (this.log.isWarnEnabled()) {
this.log.warn("Authentication scheme " + id + " not supported");
// Try again
}
continue;
}
final AuthScheme authScheme = authSchemeProvider.create(context);
authScheme.processChallenge(challenge);
final AuthScope authScope = new AuthScope(
authhost.getHostName(),
authhost.getPort(),
authScheme.getRealm(),
authScheme.getSchemeName());
final Credentials credentials = credsProvider.getCredentials(authScope);
if (credentials != null) {
options.add(new AuthOption(authScheme, credentials));
}
} else {
if (this.log.isDebugEnabled()) {
this.log.debug("Challenge for " + id + " authentication scheme not available");
// Try again
}
}
}
return options;
}
public void authSucceeded(
final HttpHost authhost, final AuthScheme authScheme, final HttpContext context) {
Args.notNull(authhost, "Host");
Args.notNull(authScheme, "Auth scheme");
Args.notNull(context, "HTTP context");
final HttpClientContext clientContext = HttpClientContext.adapt(context);
if (isCachable(authScheme)) {
AuthCache authCache = clientContext.getAuthCache();
if (authCache == null) {
authCache = new BasicAuthCache();
clientContext.setAuthCache(authCache);
}
if (this.log.isDebugEnabled()) {
this.log.debug("Caching '" + authScheme.getSchemeName() +
"' auth scheme for " + authhost);
}
authCache.put(authhost, authScheme);
}
}
protected boolean isCachable(final AuthScheme authScheme) {
if (authScheme == null || !authScheme.isComplete()) {
return false;
}
final String schemeName = authScheme.getSchemeName();
return schemeName.equalsIgnoreCase(AuthSchemes.BASIC) ||
schemeName.equalsIgnoreCase(AuthSchemes.DIGEST);
}
public void authFailed(
final HttpHost authhost, final AuthScheme authScheme, final HttpContext context) {
Args.notNull(authhost, "Host");
Args.notNull(context, "HTTP context");
final HttpClientContext clientContext = HttpClientContext.adapt(context);
final AuthCache authCache = clientContext.getAuthCache();
if (authCache != null) {
if (this.log.isDebugEnabled()) {
this.log.debug("Clearing cached auth scheme for " + authhost);
}
authCache.remove(authhost);
}
}
}

View file

@ -1,190 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.URI;
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
/* LogFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.HttpHost;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
import ch.boye.httpclientandroidlib.client.HttpClient;
import ch.boye.httpclientandroidlib.client.ResponseHandler;
import ch.boye.httpclientandroidlib.client.ServiceUnavailableRetryStrategy;
import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
import ch.boye.httpclientandroidlib.conn.ClientConnectionManager;
import ch.boye.httpclientandroidlib.params.HttpParams;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.EntityUtils;
/**
* {@link HttpClient} implementation that can automatically retry the request in case of
* a non-2xx response using the {@link ServiceUnavailableRetryStrategy} interface.
*
* @since 4.2
*
* @deprecated (4.3) use {@link HttpClientBuilder}.
*/
@Deprecated
@ThreadSafe
public class AutoRetryHttpClient implements HttpClient {
private final HttpClient backend;
private final ServiceUnavailableRetryStrategy retryStrategy;
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
public AutoRetryHttpClient(
final HttpClient client, final ServiceUnavailableRetryStrategy retryStrategy) {
super();
Args.notNull(client, "HttpClient");
Args.notNull(retryStrategy, "ServiceUnavailableRetryStrategy");
this.backend = client;
this.retryStrategy = retryStrategy;
}
/**
* Constructs a {@code AutoRetryHttpClient} with default caching settings that
* stores cache entries in memory and uses a vanilla
* {@link DefaultHttpClient} for backend requests.
*/
public AutoRetryHttpClient() {
this(new DefaultHttpClient(), new DefaultServiceUnavailableRetryStrategy());
}
/**
* Constructs a {@code AutoRetryHttpClient} with the given caching options that
* stores cache entries in memory and uses a vanilla
* {@link DefaultHttpClient} for backend requests.
*
* @param config
* retry configuration module options
*/
public AutoRetryHttpClient(final ServiceUnavailableRetryStrategy config) {
this(new DefaultHttpClient(), config);
}
/**
* Constructs a {@code AutoRetryHttpClient} with default caching settings that
* stores cache entries in memory and uses the given {@link HttpClient} for
* backend requests.
*
* @param client
* used to make origin requests
*/
public AutoRetryHttpClient(final HttpClient client) {
this(client, new DefaultServiceUnavailableRetryStrategy());
}
public HttpResponse execute(final HttpHost target, final HttpRequest request)
throws IOException {
final HttpContext defaultContext = null;
return execute(target, request, defaultContext);
}
public <T> T execute(final HttpHost target, final HttpRequest request,
final ResponseHandler<? extends T> responseHandler) throws IOException {
return execute(target, request, responseHandler, null);
}
public <T> T execute(final HttpHost target, final HttpRequest request,
final ResponseHandler<? extends T> responseHandler, final HttpContext context)
throws IOException {
final HttpResponse resp = execute(target, request, context);
return responseHandler.handleResponse(resp);
}
public HttpResponse execute(final HttpUriRequest request) throws IOException {
final HttpContext context = null;
return execute(request, context);
}
public HttpResponse execute(final HttpUriRequest request, final HttpContext context)
throws IOException {
final URI uri = request.getURI();
final HttpHost httpHost = new HttpHost(uri.getHost(), uri.getPort(),
uri.getScheme());
return execute(httpHost, request, context);
}
public <T> T execute(final HttpUriRequest request,
final ResponseHandler<? extends T> responseHandler) throws IOException {
return execute(request, responseHandler, null);
}
public <T> T execute(final HttpUriRequest request,
final ResponseHandler<? extends T> responseHandler, final HttpContext context)
throws IOException {
final HttpResponse resp = execute(request, context);
return responseHandler.handleResponse(resp);
}
public HttpResponse execute(final HttpHost target, final HttpRequest request,
final HttpContext context) throws IOException {
for (int c = 1;; c++) {
final HttpResponse response = backend.execute(target, request, context);
try {
if (retryStrategy.retryRequest(response, c, context)) {
EntityUtils.consume(response.getEntity());
final long nextInterval = retryStrategy.getRetryInterval();
try {
log.trace("Wait for " + nextInterval);
Thread.sleep(nextInterval);
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
throw new InterruptedIOException();
}
} else {
return response;
}
} catch (final RuntimeException ex) {
try {
EntityUtils.consume(response.getEntity());
} catch (final IOException ioex) {
log.warn("I/O error consuming response content", ioex);
}
throw ex;
}
}
}
public ClientConnectionManager getConnectionManager() {
return backend.getConnectionManager();
}
public HttpParams getParams() {
return backend.getParams();
}
}

View file

@ -1,105 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.util.HashMap;
import ch.boye.httpclientandroidlib.HttpHost;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.auth.AuthScheme;
import ch.boye.httpclientandroidlib.client.AuthCache;
import ch.boye.httpclientandroidlib.conn.SchemePortResolver;
import ch.boye.httpclientandroidlib.conn.UnsupportedSchemeException;
import ch.boye.httpclientandroidlib.impl.conn.DefaultSchemePortResolver;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Default implementation of {@link AuthCache}.
*
* @since 4.0
*/
@NotThreadSafe
public class BasicAuthCache implements AuthCache {
private final HashMap<HttpHost, AuthScheme> map;
private final SchemePortResolver schemePortResolver;
/**
* Default constructor.
*
* @since 4.3
*/
public BasicAuthCache(final SchemePortResolver schemePortResolver) {
super();
this.map = new HashMap<HttpHost, AuthScheme>();
this.schemePortResolver = schemePortResolver != null ? schemePortResolver :
DefaultSchemePortResolver.INSTANCE;
}
public BasicAuthCache() {
this(null);
}
protected HttpHost getKey(final HttpHost host) {
if (host.getPort() <= 0) {
final int port;
try {
port = schemePortResolver.resolve(host);
} catch (final UnsupportedSchemeException ignore) {
return host;
}
return new HttpHost(host.getHostName(), port, host.getSchemeName());
} else {
return host;
}
}
public void put(final HttpHost host, final AuthScheme authScheme) {
Args.notNull(host, "HTTP host");
this.map.put(getKey(host), authScheme);
}
public AuthScheme get(final HttpHost host) {
Args.notNull(host, "HTTP host");
return this.map.get(getKey(host));
}
public void remove(final HttpHost host) {
Args.notNull(host, "HTTP host");
this.map.remove(getKey(host));
}
public void clear() {
this.map.clear();
}
@Override
public String toString() {
return this.map.toString();
}
}

View file

@ -1,144 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
import ch.boye.httpclientandroidlib.annotation.GuardedBy;
import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
import ch.boye.httpclientandroidlib.client.CookieStore;
import ch.boye.httpclientandroidlib.cookie.Cookie;
import ch.boye.httpclientandroidlib.cookie.CookieIdentityComparator;
/**
* Default implementation of {@link CookieStore}
*
*
* @since 4.0
*/
@ThreadSafe
public class BasicCookieStore implements CookieStore, Serializable {
private static final long serialVersionUID = -7581093305228232025L;
@GuardedBy("this")
private final TreeSet<Cookie> cookies;
public BasicCookieStore() {
super();
this.cookies = new TreeSet<Cookie>(new CookieIdentityComparator());
}
/**
* Adds an {@link Cookie HTTP cookie}, replacing any existing equivalent cookies.
* If the given cookie has already expired it will not be added, but existing
* values will still be removed.
*
* @param cookie the {@link Cookie cookie} to be added
*
* @see #addCookies(Cookie[])
*
*/
public synchronized void addCookie(final Cookie cookie) {
if (cookie != null) {
// first remove any old cookie that is equivalent
cookies.remove(cookie);
if (!cookie.isExpired(new Date())) {
cookies.add(cookie);
}
}
}
/**
* Adds an array of {@link Cookie HTTP cookies}. Cookies are added individually and
* in the given array order. If any of the given cookies has already expired it will
* not be added, but existing values will still be removed.
*
* @param cookies the {@link Cookie cookies} to be added
*
* @see #addCookie(Cookie)
*
*/
public synchronized void addCookies(final Cookie[] cookies) {
if (cookies != null) {
for (final Cookie cooky : cookies) {
this.addCookie(cooky);
}
}
}
/**
* Returns an immutable array of {@link Cookie cookies} that this HTTP
* state currently contains.
*
* @return an array of {@link Cookie cookies}.
*/
public synchronized List<Cookie> getCookies() {
//create defensive copy so it won't be concurrently modified
return new ArrayList<Cookie>(cookies);
}
/**
* Removes all of {@link Cookie cookies} in this HTTP state
* that have expired by the specified {@link java.util.Date date}.
*
* @return true if any cookies were purged.
*
* @see Cookie#isExpired(Date)
*/
public synchronized boolean clearExpired(final Date date) {
if (date == null) {
return false;
}
boolean removed = false;
for (final Iterator<Cookie> it = cookies.iterator(); it.hasNext();) {
if (it.next().isExpired(date)) {
it.remove();
removed = true;
}
}
return removed;
}
/**
* Clears all cookies.
*/
public synchronized void clear() {
cookies.clear();
}
@Override
public synchronized String toString() {
return cookies.toString();
}
}

View file

@ -1,109 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
import ch.boye.httpclientandroidlib.auth.AuthScope;
import ch.boye.httpclientandroidlib.auth.Credentials;
import ch.boye.httpclientandroidlib.client.CredentialsProvider;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Default implementation of {@link CredentialsProvider}.
*
* @since 4.0
*/
@ThreadSafe
public class BasicCredentialsProvider implements CredentialsProvider {
private final ConcurrentHashMap<AuthScope, Credentials> credMap;
/**
* Default constructor.
*/
public BasicCredentialsProvider() {
super();
this.credMap = new ConcurrentHashMap<AuthScope, Credentials>();
}
public void setCredentials(
final AuthScope authscope,
final Credentials credentials) {
Args.notNull(authscope, "Authentication scope");
credMap.put(authscope, credentials);
}
/**
* Find matching {@link Credentials credentials} for the given authentication scope.
*
* @param map the credentials hash map
* @param authscope the {@link AuthScope authentication scope}
* @return the credentials
*
*/
private static Credentials matchCredentials(
final Map<AuthScope, Credentials> map,
final AuthScope authscope) {
// see if we get a direct hit
Credentials creds = map.get(authscope);
if (creds == null) {
// Nope.
// Do a full scan
int bestMatchFactor = -1;
AuthScope bestMatch = null;
for (final AuthScope current: map.keySet()) {
final int factor = authscope.match(current);
if (factor > bestMatchFactor) {
bestMatchFactor = factor;
bestMatch = current;
}
}
if (bestMatch != null) {
creds = map.get(bestMatch);
}
}
return creds;
}
public Credentials getCredentials(final AuthScope authscope) {
Args.notNull(authscope, "Authentication scope");
return matchCredentials(this.credMap, authscope);
}
public void clear() {
this.credMap.clear();
}
@Override
public String toString() {
return credMap.toString();
}
}

View file

@ -1,73 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.io.IOException;
import ch.boye.httpclientandroidlib.HttpEntity;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.StatusLine;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.HttpResponseException;
import ch.boye.httpclientandroidlib.client.ResponseHandler;
import ch.boye.httpclientandroidlib.util.EntityUtils;
/**
* A {@link ResponseHandler} that returns the response body as a String
* for successful (2xx) responses. If the response code was >= 300, the response
* body is consumed and an {@link HttpResponseException} is thrown.
* <p/>
* If this is used with
* {@link ch.boye.httpclientandroidlib.client.HttpClient#execute(
* ch.boye.httpclientandroidlib.client.methods.HttpUriRequest, ResponseHandler)},
* HttpClient may handle redirects (3xx responses) internally.
*
* @since 4.0
*/
@Immutable
public class BasicResponseHandler implements ResponseHandler<String> {
/**
* Returns the response body as a String if the response was successful (a
* 2xx status code). If no response body exists, this returns null. If the
* response was unsuccessful (>= 300 status code), throws an
* {@link HttpResponseException}.
*/
public String handleResponse(final HttpResponse response)
throws HttpResponseException, IOException {
final StatusLine statusLine = response.getStatusLine();
final HttpEntity entity = response.getEntity();
if (statusLine.getStatusCode() >= 300) {
EntityUtils.consume(entity);
throw new HttpResponseException(statusLine.getStatusCode(),
statusLine.getReasonPhrase());
}
return entity == null ? null : EntityUtils.toString(entity);
}
}

View file

@ -1,269 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.params.AbstractHttpParams;
import ch.boye.httpclientandroidlib.params.HttpParams;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Represents a stack of parameter collections.
* When retrieving a parameter, the stack is searched in a fixed order
* and the first match returned. Setting parameters via the stack is
* not supported. To minimize overhead, the stack has a fixed size and
* does not maintain an internal array.
* The supported stack entries, sorted by increasing priority, are:
* <ol>
* <li>Application parameters:
* expected to be the same for all clients used by an application.
* These provide "global", that is application-wide, defaults.
* </li>
* <li>Client parameters:
* specific to an instance of
* {@link ch.boye.httpclientandroidlib.client.HttpClient HttpClient}.
* These provide client specific defaults.
* </li>
* <li>Request parameters:
* specific to a single request execution.
* For overriding client and global defaults.
* </li>
* <li>Override parameters:
* specific to an instance of
* {@link ch.boye.httpclientandroidlib.client.HttpClient HttpClient}.
* These can be used to set parameters that cannot be overridden
* on a per-request basis.
* </li>
* </ol>
* Each stack entry may be <code>null</code>. That is preferable over
* an empty params collection, since it avoids searching the empty collection
* when looking up parameters.
*
* @since 4.0
*
* @deprecated (4.3) use configuration classes provided 'ch.boye.httpclientandroidlib.config'
* and 'ch.boye.httpclientandroidlib.client.config'
*/
@NotThreadSafe
@Deprecated
public class ClientParamsStack extends AbstractHttpParams {
/** The application parameter collection, or <code>null</code>. */
protected final HttpParams applicationParams;
/** The client parameter collection, or <code>null</code>. */
protected final HttpParams clientParams;
/** The request parameter collection, or <code>null</code>. */
protected final HttpParams requestParams;
/** The override parameter collection, or <code>null</code>. */
protected final HttpParams overrideParams;
/**
* Creates a new parameter stack from elements.
* The arguments will be stored as-is, there is no copying to
* prevent modification.
*
* @param aparams application parameters, or <code>null</code>
* @param cparams client parameters, or <code>null</code>
* @param rparams request parameters, or <code>null</code>
* @param oparams override parameters, or <code>null</code>
*/
public ClientParamsStack(final HttpParams aparams, final HttpParams cparams,
final HttpParams rparams, final HttpParams oparams) {
applicationParams = aparams;
clientParams = cparams;
requestParams = rparams;
overrideParams = oparams;
}
/**
* Creates a copy of a parameter stack.
* The new stack will have the exact same entries as the argument stack.
* There is no copying of parameters.
*
* @param stack the stack to copy
*/
public ClientParamsStack(final ClientParamsStack stack) {
this(stack.getApplicationParams(),
stack.getClientParams(),
stack.getRequestParams(),
stack.getOverrideParams());
}
/**
* Creates a modified copy of a parameter stack.
* The new stack will contain the explicitly passed elements.
* For elements where the explicit argument is <code>null</code>,
* the corresponding element from the argument stack is used.
* There is no copying of parameters.
*
* @param stack the stack to modify
* @param aparams application parameters, or <code>null</code>
* @param cparams client parameters, or <code>null</code>
* @param rparams request parameters, or <code>null</code>
* @param oparams override parameters, or <code>null</code>
*/
public ClientParamsStack(final ClientParamsStack stack,
final HttpParams aparams, final HttpParams cparams,
final HttpParams rparams, final HttpParams oparams) {
this((aparams != null) ? aparams : stack.getApplicationParams(),
(cparams != null) ? cparams : stack.getClientParams(),
(rparams != null) ? rparams : stack.getRequestParams(),
(oparams != null) ? oparams : stack.getOverrideParams());
}
/**
* Obtains the application parameters of this stack.
*
* @return the application parameters, or <code>null</code>
*/
public final HttpParams getApplicationParams() {
return applicationParams;
}
/**
* Obtains the client parameters of this stack.
*
* @return the client parameters, or <code>null</code>
*/
public final HttpParams getClientParams() {
return clientParams;
}
/**
* Obtains the request parameters of this stack.
*
* @return the request parameters, or <code>null</code>
*/
public final HttpParams getRequestParams() {
return requestParams;
}
/**
* Obtains the override parameters of this stack.
*
* @return the override parameters, or <code>null</code>
*/
public final HttpParams getOverrideParams() {
return overrideParams;
}
/**
* Obtains a parameter from this stack.
* See class comment for search order.
*
* @param name the name of the parameter to obtain
*
* @return the highest-priority value for that parameter, or
* <code>null</code> if it is not set anywhere in this stack
*/
public Object getParameter(final String name) {
Args.notNull(name, "Parameter name");
Object result = null;
if (overrideParams != null) {
result = overrideParams.getParameter(name);
}
if ((result == null) && (requestParams != null)) {
result = requestParams.getParameter(name);
}
if ((result == null) && (clientParams != null)) {
result = clientParams.getParameter(name);
}
if ((result == null) && (applicationParams != null)) {
result = applicationParams.getParameter(name);
}
return result;
}
/**
* Does <i>not</i> set a parameter.
* Parameter stacks are read-only. It is possible, though discouraged,
* to access and modify specific stack entries.
* Derived classes may change this behavior.
*
* @param name ignored
* @param value ignored
*
* @return nothing
*
* @throws UnsupportedOperationException always
*/
public HttpParams setParameter(final String name, final Object value)
throws UnsupportedOperationException {
throw new UnsupportedOperationException
("Setting parameters in a stack is not supported.");
}
/**
* Does <i>not</i> remove a parameter.
* Parameter stacks are read-only. It is possible, though discouraged,
* to access and modify specific stack entries.
* Derived classes may change this behavior.
*
* @param name ignored
*
* @return nothing
*
* @throws UnsupportedOperationException always
*/
public boolean removeParameter(final String name) {
throw new UnsupportedOperationException
("Removing parameters in a stack is not supported.");
}
/**
* Does <i>not</i> copy parameters.
* Parameter stacks are lightweight objects, expected to be instantiated
* as needed and to be used only in a very specific context. On top of
* that, they are read-only. The typical copy operation to prevent
* accidental modification of parameters passed by the application to
* a framework object is therefore pointless and disabled.
* Create a new stack if you really need a copy.
* <br/>
* Derived classes may change this behavior.
*
* @return <code>this</code> parameter stack
*/
public HttpParams copy() {
return this;
}
}

View file

@ -1,43 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
/**
* Interface used to enable easier testing of time-related behavior.
*
* @since 4.2
*
*/
interface Clock {
/**
* Returns the current time, expressed as the number of
* milliseconds since the epoch.
* @return current time
*/
long getCurrentTime();
}

View file

@ -1,244 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.UndeclaredThrowableException;
import java.net.URI;
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
/* LogFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.HttpEntity;
import ch.boye.httpclientandroidlib.HttpHost;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
import ch.boye.httpclientandroidlib.client.ClientProtocolException;
import ch.boye.httpclientandroidlib.client.HttpClient;
import ch.boye.httpclientandroidlib.client.ResponseHandler;
import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
import ch.boye.httpclientandroidlib.client.utils.URIUtils;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.EntityUtils;
/**
* Base implementation of {@link HttpClient} that also implements {@link Closeable}.
*
* @since 4.3
*/
@ThreadSafe
public abstract class CloseableHttpClient implements HttpClient, Closeable {
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
protected abstract CloseableHttpResponse doExecute(HttpHost target, HttpRequest request,
HttpContext context) throws IOException, ClientProtocolException;
/**
* {@inheritDoc}
*/
public CloseableHttpResponse execute(
final HttpHost target,
final HttpRequest request,
final HttpContext context) throws IOException, ClientProtocolException {
return doExecute(target, request, context);
}
/**
* {@inheritDoc}
*/
public CloseableHttpResponse execute(
final HttpUriRequest request,
final HttpContext context) throws IOException, ClientProtocolException {
Args.notNull(request, "HTTP request");
return doExecute(determineTarget(request), request, context);
}
private static HttpHost determineTarget(final HttpUriRequest request) throws ClientProtocolException {
// A null target may be acceptable if there is a default target.
// Otherwise, the null target is detected in the director.
HttpHost target = null;
final URI requestURI = request.getURI();
if (requestURI.isAbsolute()) {
target = URIUtils.extractHost(requestURI);
if (target == null) {
throw new ClientProtocolException("URI does not specify a valid host name: "
+ requestURI);
}
}
return target;
}
/**
* {@inheritDoc}
*/
public CloseableHttpResponse execute(
final HttpUriRequest request) throws IOException, ClientProtocolException {
return execute(request, (HttpContext) null);
}
/**
* {@inheritDoc}
*/
public CloseableHttpResponse execute(
final HttpHost target,
final HttpRequest request) throws IOException, ClientProtocolException {
return doExecute(target, request, (HttpContext) null);
}
/**
* Executes a request using the default context and processes the
* response using the given response handler. The content entity associated
* with the response is fully consumed and the underlying connection is
* released back to the connection manager automatically in all cases
* relieving individual {@link ResponseHandler}s from having to manage
* resource deallocation internally.
*
* @param request the request to execute
* @param responseHandler the response handler
*
* @return the response object as generated by the response handler.
* @throws IOException in case of a problem or the connection was aborted
* @throws ClientProtocolException in case of an http protocol error
*/
public <T> T execute(final HttpUriRequest request,
final ResponseHandler<? extends T> responseHandler) throws IOException,
ClientProtocolException {
return execute(request, responseHandler, null);
}
/**
* Executes a request using the default context and processes the
* response using the given response handler. The content entity associated
* with the response is fully consumed and the underlying connection is
* released back to the connection manager automatically in all cases
* relieving individual {@link ResponseHandler}s from having to manage
* resource deallocation internally.
*
* @param request the request to execute
* @param responseHandler the response handler
* @param context the context to use for the execution, or
* <code>null</code> to use the default context
*
* @return the response object as generated by the response handler.
* @throws IOException in case of a problem or the connection was aborted
* @throws ClientProtocolException in case of an http protocol error
*/
public <T> T execute(final HttpUriRequest request,
final ResponseHandler<? extends T> responseHandler, final HttpContext context)
throws IOException, ClientProtocolException {
final HttpHost target = determineTarget(request);
return execute(target, request, responseHandler, context);
}
/**
* Executes a request using the default context and processes the
* response using the given response handler. The content entity associated
* with the response is fully consumed and the underlying connection is
* released back to the connection manager automatically in all cases
* relieving individual {@link ResponseHandler}s from having to manage
* resource deallocation internally.
*
* @param target the target host for the request.
* Implementations may accept <code>null</code>
* if they can still determine a route, for example
* to a default target or by inspecting the request.
* @param request the request to execute
* @param responseHandler the response handler
*
* @return the response object as generated by the response handler.
* @throws IOException in case of a problem or the connection was aborted
* @throws ClientProtocolException in case of an http protocol error
*/
public <T> T execute(final HttpHost target, final HttpRequest request,
final ResponseHandler<? extends T> responseHandler) throws IOException,
ClientProtocolException {
return execute(target, request, responseHandler, null);
}
/**
* Executes a request using the default context and processes the
* response using the given response handler. The content entity associated
* with the response is fully consumed and the underlying connection is
* released back to the connection manager automatically in all cases
* relieving individual {@link ResponseHandler}s from having to manage
* resource deallocation internally.
*
* @param target the target host for the request.
* Implementations may accept <code>null</code>
* if they can still determine a route, for example
* to a default target or by inspecting the request.
* @param request the request to execute
* @param responseHandler the response handler
* @param context the context to use for the execution, or
* <code>null</code> to use the default context
*
* @return the response object as generated by the response handler.
* @throws IOException in case of a problem or the connection was aborted
* @throws ClientProtocolException in case of an http protocol error
*/
public <T> T execute(final HttpHost target, final HttpRequest request,
final ResponseHandler<? extends T> responseHandler, final HttpContext context)
throws IOException, ClientProtocolException {
Args.notNull(responseHandler, "Response handler");
final HttpResponse response = execute(target, request, context);
final T result;
try {
result = responseHandler.handleResponse(response);
} catch (final Exception t) {
final HttpEntity entity = response.getEntity();
try {
EntityUtils.consume(entity);
} catch (final Exception t2) {
// Log this exception. The original exception is more
// important and will be thrown to the caller.
this.log.warn("Error consuming content after an exception.", t2);
}
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
}
if (t instanceof IOException) {
throw (IOException) t;
}
throw new UndeclaredThrowableException(t);
}
// Handling the response was successful. Ensure that the content has
// been fully consumed.
final HttpEntity entity = response.getEntity();
EntityUtils.consume(entity);
return result;
}
}

View file

@ -1,87 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import ch.boye.httpclientandroidlib.HttpEntity;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
import ch.boye.httpclientandroidlib.util.EntityUtils;
/**
* @since 4.3
*/
@NotThreadSafe
class CloseableHttpResponseProxy implements InvocationHandler {
private final HttpResponse original;
CloseableHttpResponseProxy(final HttpResponse original) {
super();
this.original = original;
}
public void close() throws IOException {
final HttpEntity entity = this.original.getEntity();
EntityUtils.consume(entity);
}
public Object invoke(
final Object proxy, final Method method, final Object[] args) throws Throwable {
final String mname = method.getName();
if (mname.equals("close")) {
close();
return null;
} else {
try {
return method.invoke(original, args);
} catch (final InvocationTargetException ex) {
final Throwable cause = ex.getCause();
if (cause != null) {
throw cause;
} else {
throw ex;
}
}
}
}
public static CloseableHttpResponse newProxy(final HttpResponse original) {
return (CloseableHttpResponse) Proxy.newProxyInstance(
CloseableHttpResponseProxy.class.getClassLoader(),
new Class<?>[] { CloseableHttpResponse.class },
new CloseableHttpResponseProxy(original));
}
}

View file

@ -1,93 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
import ch.boye.httpclientandroidlib.client.protocol.RequestAcceptEncoding;
import ch.boye.httpclientandroidlib.client.protocol.ResponseContentEncoding;
import ch.boye.httpclientandroidlib.conn.ClientConnectionManager;
import ch.boye.httpclientandroidlib.params.HttpParams;
import ch.boye.httpclientandroidlib.protocol.BasicHttpProcessor;
/**
* {@link DefaultHttpClient} sub-class which includes a {@link RequestAcceptEncoding}
* for the request and response.
*
* <b>Deprecation note:</b> due to the way this class modifies a response body
* without changing the response headers to reflect the entity changes, it cannot
* be used as the &quot;backend&quot; for a caching {@link
* ch.boye.httpclientandroidlib.client.HttpClient} and still have uncompressed responses be cached.
* Users are encouraged to use the {@link DecompressingHttpClient} instead
* of this class, which can be wired in either before or after caching, depending on
* whether you want to cache responses in compressed or uncompressed form.
*
* @since 4.1
*
* @deprecated (4.2) use {@link HttpClientBuilder}
*/
@Deprecated
@ThreadSafe // since DefaultHttpClient is
public class ContentEncodingHttpClient extends DefaultHttpClient {
/**
* Creates a new HTTP client from parameters and a connection manager.
*
* @param params the parameters
* @param conman the connection manager
*/
public ContentEncodingHttpClient(final ClientConnectionManager conman, final HttpParams params) {
super(conman, params);
}
/**
* @param params
*/
public ContentEncodingHttpClient(final HttpParams params) {
this(null, params);
}
/**
*
*/
public ContentEncodingHttpClient() {
this(null);
}
/**
* {@inheritDoc}
*/
@Override
protected BasicHttpProcessor createHttpProcessor() {
final BasicHttpProcessor result = super.createHttpProcessor();
result.addRequestInterceptor(new RequestAcceptEncoding());
result.addResponseInterceptor(new ResponseContentEncoding());
return result;
}
}

View file

@ -1,214 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.io.IOException;
import java.net.URI;
import ch.boye.httpclientandroidlib.HttpEntity;
import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest;
import ch.boye.httpclientandroidlib.HttpException;
import ch.boye.httpclientandroidlib.HttpHost;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.HttpRequestInterceptor;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.HttpResponseInterceptor;
import ch.boye.httpclientandroidlib.client.ClientProtocolException;
import ch.boye.httpclientandroidlib.client.HttpClient;
import ch.boye.httpclientandroidlib.client.ResponseHandler;
import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
import ch.boye.httpclientandroidlib.client.protocol.RequestAcceptEncoding;
import ch.boye.httpclientandroidlib.client.protocol.ResponseContentEncoding;
import ch.boye.httpclientandroidlib.client.utils.URIUtils;
import ch.boye.httpclientandroidlib.conn.ClientConnectionManager;
import ch.boye.httpclientandroidlib.params.HttpParams;
import ch.boye.httpclientandroidlib.protocol.BasicHttpContext;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
import ch.boye.httpclientandroidlib.util.EntityUtils;
/**
* <p>Decorator adding support for compressed responses. This class sets
* the <code>Accept-Encoding</code> header on requests to indicate
* support for the <code>gzip</code> and <code>deflate</code>
* compression schemes; it then checks the <code>Content-Encoding</code>
* header on the response to uncompress any compressed response bodies.
* The {@link java.io.InputStream} of the entity will contain the uncompressed
* content.</p>
*
* <p><b>N.B.</b> Any upstream clients of this class need to be aware that
* this effectively obscures visibility into the length of a server
* response body, since the <code>Content-Length</code> header will
* correspond to the compressed entity length received from the server,
* but the content length experienced by reading the response body may
* be different (hopefully higher!).</p>
*
* <p>That said, this decorator is compatible with the
* <code>CachingHttpClient</code> in that the two decorators can be added
* in either order and still have cacheable responses be cached.</p>
*
* @since 4.2
*
* @deprecated (4.3) use {@link HttpClientBuilder}
*/
@Deprecated
public class DecompressingHttpClient implements HttpClient {
private final HttpClient backend;
private final HttpRequestInterceptor acceptEncodingInterceptor;
private final HttpResponseInterceptor contentEncodingInterceptor;
/**
* Constructs a decorator to ask for and handle compressed
* entities on the fly.
*/
public DecompressingHttpClient() {
this(new DefaultHttpClient());
}
/**
* Constructs a decorator to ask for and handle compressed
* entities on the fly.
* @param backend the {@link HttpClient} to use for actually
* issuing requests
*/
public DecompressingHttpClient(final HttpClient backend) {
this(backend, new RequestAcceptEncoding(), new ResponseContentEncoding());
}
DecompressingHttpClient(final HttpClient backend,
final HttpRequestInterceptor requestInterceptor,
final HttpResponseInterceptor responseInterceptor) {
this.backend = backend;
this.acceptEncodingInterceptor = requestInterceptor;
this.contentEncodingInterceptor = responseInterceptor;
}
public HttpParams getParams() {
return backend.getParams();
}
public ClientConnectionManager getConnectionManager() {
return backend.getConnectionManager();
}
public HttpResponse execute(final HttpUriRequest request) throws IOException,
ClientProtocolException {
return execute(getHttpHost(request), request, (HttpContext)null);
}
/**
* Gets the HttpClient to issue request.
*
* @return the HttpClient to issue request
*/
public HttpClient getHttpClient() {
return this.backend;
}
HttpHost getHttpHost(final HttpUriRequest request) {
final URI uri = request.getURI();
return URIUtils.extractHost(uri);
}
public HttpResponse execute(final HttpUriRequest request, final HttpContext context)
throws IOException, ClientProtocolException {
return execute(getHttpHost(request), request, context);
}
public HttpResponse execute(final HttpHost target, final HttpRequest request)
throws IOException, ClientProtocolException {
return execute(target, request, (HttpContext)null);
}
public HttpResponse execute(final HttpHost target, final HttpRequest request,
final HttpContext context) throws IOException, ClientProtocolException {
try {
final HttpContext localContext = context != null ? context : new BasicHttpContext();
final HttpRequest wrapped;
if (request instanceof HttpEntityEnclosingRequest) {
wrapped = new EntityEnclosingRequestWrapper((HttpEntityEnclosingRequest) request);
} else {
wrapped = new RequestWrapper(request);
}
acceptEncodingInterceptor.process(wrapped, localContext);
final HttpResponse response = backend.execute(target, wrapped, localContext);
try {
contentEncodingInterceptor.process(response, localContext);
if (Boolean.TRUE.equals(localContext.getAttribute(ResponseContentEncoding.UNCOMPRESSED))) {
response.removeHeaders("Content-Length");
response.removeHeaders("Content-Encoding");
response.removeHeaders("Content-MD5");
}
return response;
} catch (final HttpException ex) {
EntityUtils.consume(response.getEntity());
throw ex;
} catch (final IOException ex) {
EntityUtils.consume(response.getEntity());
throw ex;
} catch (final RuntimeException ex) {
EntityUtils.consume(response.getEntity());
throw ex;
}
} catch (final HttpException e) {
throw new ClientProtocolException(e);
}
}
public <T> T execute(final HttpUriRequest request,
final ResponseHandler<? extends T> responseHandler) throws IOException,
ClientProtocolException {
return execute(getHttpHost(request), request, responseHandler);
}
public <T> T execute(final HttpUriRequest request,
final ResponseHandler<? extends T> responseHandler, final HttpContext context)
throws IOException, ClientProtocolException {
return execute(getHttpHost(request), request, responseHandler, context);
}
public <T> T execute(final HttpHost target, final HttpRequest request,
final ResponseHandler<? extends T> responseHandler) throws IOException,
ClientProtocolException {
return execute(target, request, responseHandler, null);
}
public <T> T execute(final HttpHost target, final HttpRequest request,
final ResponseHandler<? extends T> responseHandler, final HttpContext context)
throws IOException, ClientProtocolException {
final HttpResponse response = execute(target, request, context);
try {
return responseHandler.handleResponse(response);
} finally {
final HttpEntity entity = response.getEntity();
if (entity != null) {
EntityUtils.consume(entity);
}
}
}
}

View file

@ -1,54 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.HttpStatus;
import ch.boye.httpclientandroidlib.client.ConnectionBackoffStrategy;
/**
* This {@link ConnectionBackoffStrategy} backs off either for a raw
* network socket or connection timeout or if the server explicitly
* sends a 503 (Service Unavailable) response.
*
* @since 4.2
*/
public class DefaultBackoffStrategy implements ConnectionBackoffStrategy {
public boolean shouldBackoff(final Throwable t) {
return (t instanceof SocketTimeoutException
|| t instanceof ConnectException);
}
public boolean shouldBackoff(final HttpResponse resp) {
return (resp.getStatusLine().getStatusCode() == HttpStatus.SC_SERVICE_UNAVAILABLE);
}
}

View file

@ -1,71 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import ch.boye.httpclientandroidlib.HeaderElement;
import ch.boye.httpclientandroidlib.HeaderElementIterator;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.conn.ConnectionKeepAliveStrategy;
import ch.boye.httpclientandroidlib.message.BasicHeaderElementIterator;
import ch.boye.httpclientandroidlib.protocol.HTTP;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Default implementation of a strategy deciding duration
* that a connection can remain idle.
*
* The default implementation looks solely at the 'Keep-Alive'
* header's timeout token.
*
* @since 4.0
*/
@Immutable
public class DefaultConnectionKeepAliveStrategy implements ConnectionKeepAliveStrategy {
public static final DefaultConnectionKeepAliveStrategy INSTANCE = new DefaultConnectionKeepAliveStrategy();
public long getKeepAliveDuration(final HttpResponse response, final HttpContext context) {
Args.notNull(response, "HTTP response");
final HeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
final HeaderElement he = it.nextElement();
final String param = he.getName();
final String value = he.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
try {
return Long.parseLong(value) * 1000;
} catch(final NumberFormatException ignore) {
}
}
}
return -1;
}
}

View file

@ -1,225 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import ch.boye.httpclientandroidlib.HttpVersion;
import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
import ch.boye.httpclientandroidlib.client.protocol.RequestAddCookies;
import ch.boye.httpclientandroidlib.client.protocol.RequestAuthCache;
import ch.boye.httpclientandroidlib.client.protocol.RequestClientConnControl;
import ch.boye.httpclientandroidlib.client.protocol.RequestDefaultHeaders;
import ch.boye.httpclientandroidlib.client.protocol.RequestProxyAuthentication;
import ch.boye.httpclientandroidlib.client.protocol.RequestTargetAuthentication;
import ch.boye.httpclientandroidlib.client.protocol.ResponseProcessCookies;
import ch.boye.httpclientandroidlib.conn.ClientConnectionManager;
import ch.boye.httpclientandroidlib.params.HttpConnectionParams;
import ch.boye.httpclientandroidlib.params.HttpParams;
import ch.boye.httpclientandroidlib.params.HttpProtocolParams;
import ch.boye.httpclientandroidlib.params.SyncBasicHttpParams;
import ch.boye.httpclientandroidlib.protocol.BasicHttpProcessor;
import ch.boye.httpclientandroidlib.protocol.HTTP;
import ch.boye.httpclientandroidlib.protocol.RequestContent;
import ch.boye.httpclientandroidlib.protocol.RequestExpectContinue;
import ch.boye.httpclientandroidlib.protocol.RequestTargetHost;
import ch.boye.httpclientandroidlib.protocol.RequestUserAgent;
/**
* Default implementation of {@link ch.boye.httpclientandroidlib.client.HttpClient} pre-configured
* for most common use scenarios.
* <p>
* Please see the Javadoc for {@link #createHttpProcessor()} for the details of the interceptors
* that are set up by default.
* <p>
* Additional interceptors can be added as follows, but
* take care not to add the same interceptor more than once.
* <pre>
* DefaultHttpClient httpclient = new DefaultHttpClient();
* httpclient.addRequestInterceptor(new RequestAcceptEncoding());
* httpclient.addResponseInterceptor(new ResponseContentEncoding());
* </pre>
* <p>
* This class sets up the following parameters if not explicitly set:
* <ul>
* <li>Version: HttpVersion.HTTP_1_1</li>
* <li>ContentCharset: HTTP.DEFAULT_CONTENT_CHARSET</li>
* <li>NoTcpDelay: true</li>
* <li>SocketBufferSize: 8192</li>
* <li>UserAgent: Apache-HttpClient/release (java 1.5)</li>
* </ul>
* <p>
* The following parameters can be used to customize the behavior of this
* class:
* <ul>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#PROTOCOL_VERSION}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#STRICT_TRANSFER_ENCODING}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#HTTP_ELEMENT_CHARSET}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#USE_EXPECT_CONTINUE}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#WAIT_FOR_CONTINUE}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#USER_AGENT}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#TCP_NODELAY}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_TIMEOUT}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_LINGER}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_REUSEADDR}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SOCKET_BUFFER_SIZE}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#CONNECTION_TIMEOUT}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_LINE_LENGTH}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_HEADER_COUNT}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#STALE_CONNECTION_CHECK}</li>
* <li>{@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#FORCED_ROUTE}</li>
* <li>{@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#LOCAL_ADDRESS}</li>
* <li>{@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#DEFAULT_PROXY}</li>
* <li>{@link ch.boye.httpclientandroidlib.cookie.params.CookieSpecPNames#DATE_PATTERNS}</li>
* <li>{@link ch.boye.httpclientandroidlib.cookie.params.CookieSpecPNames#SINGLE_COOKIE_HEADER}</li>
* <li>{@link ch.boye.httpclientandroidlib.auth.params.AuthPNames#CREDENTIAL_CHARSET}</li>
* <li>{@link ch.boye.httpclientandroidlib.client.params.ClientPNames#COOKIE_POLICY}</li>
* <li>{@link ch.boye.httpclientandroidlib.client.params.ClientPNames#HANDLE_AUTHENTICATION}</li>
* <li>{@link ch.boye.httpclientandroidlib.client.params.ClientPNames#HANDLE_REDIRECTS}</li>
* <li>{@link ch.boye.httpclientandroidlib.client.params.ClientPNames#MAX_REDIRECTS}</li>
* <li>{@link ch.boye.httpclientandroidlib.client.params.ClientPNames#ALLOW_CIRCULAR_REDIRECTS}</li>
* <li>{@link ch.boye.httpclientandroidlib.client.params.ClientPNames#VIRTUAL_HOST}</li>
* <li>{@link ch.boye.httpclientandroidlib.client.params.ClientPNames#DEFAULT_HOST}</li>
* <li>{@link ch.boye.httpclientandroidlib.client.params.ClientPNames#DEFAULT_HEADERS}</li>
* <li>{@link ch.boye.httpclientandroidlib.client.params.ClientPNames#CONN_MANAGER_TIMEOUT}</li>
* </ul>
*
* @since 4.0
*
* @deprecated (4.3) use {@link HttpClientBuilder}.
*/
@ThreadSafe
@Deprecated
public class DefaultHttpClient extends AbstractHttpClient {
/**
* Creates a new HTTP client from parameters and a connection manager.
*
* @param params the parameters
* @param conman the connection manager
*/
public DefaultHttpClient(
final ClientConnectionManager conman,
final HttpParams params) {
super(conman, params);
}
/**
* @since 4.1
*/
public DefaultHttpClient(
final ClientConnectionManager conman) {
super(conman, null);
}
public DefaultHttpClient(final HttpParams params) {
super(null, params);
}
public DefaultHttpClient() {
super(null, null);
}
/**
* Creates the default set of HttpParams by invoking {@link DefaultHttpClient#setDefaultHttpParams(HttpParams)}
*
* @return a new instance of {@link SyncBasicHttpParams} with the defaults applied to it.
*/
@Override
protected HttpParams createHttpParams() {
final HttpParams params = new SyncBasicHttpParams();
setDefaultHttpParams(params);
return params;
}
/**
* Saves the default set of HttpParams in the provided parameter.
* These are:
* <ul>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#PROTOCOL_VERSION}:
* 1.1</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#HTTP_CONTENT_CHARSET}:
* ISO-8859-1</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#TCP_NODELAY}:
* true</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SOCKET_BUFFER_SIZE}:
* 8192</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#USER_AGENT}:
* Apache-HttpClient/<release> (java 1.5)</li>
* </ul>
*/
public static void setDefaultHttpParams(final HttpParams params) {
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.DEF_CONTENT_CHARSET.name());
HttpConnectionParams.setTcpNoDelay(params, true);
HttpConnectionParams.setSocketBufferSize(params, 8192);
HttpProtocolParams.setUserAgent(params, HttpClientBuilder.DEFAULT_USER_AGENT);
}
/**
* Create the processor with the following interceptors:
* <ul>
* <li>{@link RequestDefaultHeaders}</li>
* <li>{@link RequestContent}</li>
* <li>{@link RequestTargetHost}</li>
* <li>{@link RequestClientConnControl}</li>
* <li>{@link RequestUserAgent}</li>
* <li>{@link RequestExpectContinue}</li>
* <li>{@link RequestAddCookies}</li>
* <li>{@link ResponseProcessCookies}</li>
* <li>{@link RequestAuthCache}</li>
* <li>{@link RequestTargetAuthentication}</li>
* <li>{@link RequestProxyAuthentication}</li>
* </ul>
* <p>
* @return the processor with the added interceptors.
*/
@Override
protected BasicHttpProcessor createHttpProcessor() {
final BasicHttpProcessor httpproc = new BasicHttpProcessor();
httpproc.addInterceptor(new RequestDefaultHeaders());
// Required protocol interceptors
httpproc.addInterceptor(new RequestContent());
httpproc.addInterceptor(new RequestTargetHost());
// Recommended protocol interceptors
httpproc.addInterceptor(new RequestClientConnControl());
httpproc.addInterceptor(new RequestUserAgent());
httpproc.addInterceptor(new RequestExpectContinue());
// HTTP state management interceptors
httpproc.addInterceptor(new RequestAddCookies());
httpproc.addInterceptor(new ResponseProcessCookies());
// HTTP authentication interceptors
httpproc.addInterceptor(new RequestAuthCache());
httpproc.addInterceptor(new RequestTargetAuthentication());
httpproc.addInterceptor(new RequestProxyAuthentication());
return httpproc;
}
}

View file

@ -1,203 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.ConnectException;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import javax.net.ssl.SSLException;
import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.HttpRequestRetryHandler;
import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
import ch.boye.httpclientandroidlib.util.Args;
/**
* The default {@link HttpRequestRetryHandler} used by request executors.
*
* @since 4.0
*/
@Immutable
public class DefaultHttpRequestRetryHandler implements HttpRequestRetryHandler {
public static final DefaultHttpRequestRetryHandler INSTANCE = new DefaultHttpRequestRetryHandler();
/** the number of times a method will be retried */
private final int retryCount;
/** Whether or not methods that have successfully sent their request will be retried */
private final boolean requestSentRetryEnabled;
private final Set<Class<? extends IOException>> nonRetriableClasses;
/**
* Create the request retry handler using the specified IOException classes
*
* @param retryCount how many times to retry; 0 means no retries
* @param requestSentRetryEnabled true if it's OK to retry requests that have been sent
* @param clazzes the IOException types that should not be retried
* @since 4.3
*/
protected DefaultHttpRequestRetryHandler(
final int retryCount,
final boolean requestSentRetryEnabled,
final Collection<Class<? extends IOException>> clazzes) {
super();
this.retryCount = retryCount;
this.requestSentRetryEnabled = requestSentRetryEnabled;
this.nonRetriableClasses = new HashSet<Class<? extends IOException>>();
for (final Class<? extends IOException> clazz: clazzes) {
this.nonRetriableClasses.add(clazz);
}
}
/**
* Create the request retry handler using the following list of
* non-retriable IOException classes: <br>
* <ul>
* <li>InterruptedIOException</li>
* <li>UnknownHostException</li>
* <li>ConnectException</li>
* <li>SSLException</li>
* </ul>
* @param retryCount how many times to retry; 0 means no retries
* @param requestSentRetryEnabled true if it's OK to retry requests that have been sent
*/
@SuppressWarnings("unchecked")
public DefaultHttpRequestRetryHandler(final int retryCount, final boolean requestSentRetryEnabled) {
this(retryCount, requestSentRetryEnabled, Arrays.asList(
InterruptedIOException.class,
UnknownHostException.class,
ConnectException.class,
SSLException.class));
}
/**
* Create the request retry handler with a retry count of 3, requestSentRetryEnabled false
* and using the following list of non-retriable IOException classes: <br>
* <ul>
* <li>InterruptedIOException</li>
* <li>UnknownHostException</li>
* <li>ConnectException</li>
* <li>SSLException</li>
* </ul>
*/
public DefaultHttpRequestRetryHandler() {
this(3, false);
}
/**
* Used <code>retryCount</code> and <code>requestSentRetryEnabled</code> to determine
* if the given method should be retried.
*/
public boolean retryRequest(
final IOException exception,
final int executionCount,
final HttpContext context) {
Args.notNull(exception, "Exception parameter");
Args.notNull(context, "HTTP context");
if (executionCount > this.retryCount) {
// Do not retry if over max retry count
return false;
}
if (this.nonRetriableClasses.contains(exception.getClass())) {
return false;
} else {
for (final Class<? extends IOException> rejectException : this.nonRetriableClasses) {
if (rejectException.isInstance(exception)) {
return false;
}
}
}
final HttpClientContext clientContext = HttpClientContext.adapt(context);
final HttpRequest request = clientContext.getRequest();
if(requestIsAborted(request)){
return false;
}
if (handleAsIdempotent(request)) {
// Retry if the request is considered idempotent
return true;
}
if (!clientContext.isRequestSent() || this.requestSentRetryEnabled) {
// Retry if the request has not been sent fully or
// if it's OK to retry methods that have been sent
return true;
}
// otherwise do not retry
return false;
}
/**
* @return <code>true</code> if this handler will retry methods that have
* successfully sent their request, <code>false</code> otherwise
*/
public boolean isRequestSentRetryEnabled() {
return requestSentRetryEnabled;
}
/**
* @return the maximum number of times a method will be retried
*/
public int getRetryCount() {
return retryCount;
}
/**
* @since 4.2
*/
protected boolean handleAsIdempotent(final HttpRequest request) {
return !(request instanceof HttpEntityEnclosingRequest);
}
/**
* @since 4.2
*
* @deprecated (4.3)
*/
@Deprecated
protected boolean requestIsAborted(final HttpRequest request) {
HttpRequest req = request;
if (request instanceof RequestWrapper) { // does not forward request to original
req = ((RequestWrapper) request).getOriginal();
}
return (req instanceof HttpUriRequest && ((HttpUriRequest)req).isAborted());
}
}

View file

@ -1,90 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.util.List;
import java.util.Map;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.HttpStatus;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.auth.AUTH;
import ch.boye.httpclientandroidlib.auth.MalformedChallengeException;
import ch.boye.httpclientandroidlib.auth.params.AuthPNames;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Default {@link ch.boye.httpclientandroidlib.client.AuthenticationHandler} implementation
* for proxy host authentication.
*
* @since 4.0
*
* @deprecated (4.2) use {@link ProxyAuthenticationStrategy}
*/
@Deprecated
@Immutable
public class DefaultProxyAuthenticationHandler extends AbstractAuthenticationHandler {
public DefaultProxyAuthenticationHandler() {
super();
}
public boolean isAuthenticationRequested(
final HttpResponse response,
final HttpContext context) {
Args.notNull(response, "HTTP response");
final int status = response.getStatusLine().getStatusCode();
return status == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED;
}
public Map<String, Header> getChallenges(
final HttpResponse response,
final HttpContext context) throws MalformedChallengeException {
Args.notNull(response, "HTTP response");
final Header[] headers = response.getHeaders(AUTH.PROXY_AUTH);
return parseChallenges(headers);
}
@Override
protected List<String> getAuthPreferences(
final HttpResponse response,
final HttpContext context) {
@SuppressWarnings("unchecked")
final
List<String> authpref = (List<String>) response.getParams().getParameter(
AuthPNames.PROXY_AUTH_PREF);
if (authpref != null) {
return authpref;
} else {
return super.getAuthPreferences(response, context);
}
}
}

View file

@ -1,180 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.net.URI;
import java.net.URISyntaxException;
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
/* LogFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpHost;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.HttpStatus;
import ch.boye.httpclientandroidlib.ProtocolException;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.CircularRedirectException;
import ch.boye.httpclientandroidlib.client.RedirectHandler;
import ch.boye.httpclientandroidlib.client.methods.HttpGet;
import ch.boye.httpclientandroidlib.client.methods.HttpHead;
import ch.boye.httpclientandroidlib.client.params.ClientPNames;
import ch.boye.httpclientandroidlib.client.utils.URIUtils;
import ch.boye.httpclientandroidlib.params.HttpParams;
import ch.boye.httpclientandroidlib.protocol.ExecutionContext;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.Asserts;
/**
* Default implementation of {@link RedirectHandler}.
*
* @since 4.0
*
* @deprecated (4.1) use {@link DefaultRedirectStrategy}.
*/
@Immutable
@Deprecated
public class DefaultRedirectHandler implements RedirectHandler {
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
private static final String REDIRECT_LOCATIONS = "http.protocol.redirect-locations";
public DefaultRedirectHandler() {
super();
}
public boolean isRedirectRequested(
final HttpResponse response,
final HttpContext context) {
Args.notNull(response, "HTTP response");
final int statusCode = response.getStatusLine().getStatusCode();
switch (statusCode) {
case HttpStatus.SC_MOVED_TEMPORARILY:
case HttpStatus.SC_MOVED_PERMANENTLY:
case HttpStatus.SC_TEMPORARY_REDIRECT:
final HttpRequest request = (HttpRequest) context.getAttribute(
ExecutionContext.HTTP_REQUEST);
final String method = request.getRequestLine().getMethod();
return method.equalsIgnoreCase(HttpGet.METHOD_NAME)
|| method.equalsIgnoreCase(HttpHead.METHOD_NAME);
case HttpStatus.SC_SEE_OTHER:
return true;
default:
return false;
} //end of switch
}
public URI getLocationURI(
final HttpResponse response,
final HttpContext context) throws ProtocolException {
Args.notNull(response, "HTTP response");
//get the location header to find out where to redirect to
final Header locationHeader = response.getFirstHeader("location");
if (locationHeader == null) {
// got a redirect response, but no location header
throw new ProtocolException(
"Received redirect response " + response.getStatusLine()
+ " but no location header");
}
final String location = locationHeader.getValue();
if (this.log.isDebugEnabled()) {
this.log.debug("Redirect requested to location '" + location + "'");
}
URI uri;
try {
uri = new URI(location);
} catch (final URISyntaxException ex) {
throw new ProtocolException("Invalid redirect URI: " + location, ex);
}
final HttpParams params = response.getParams();
// rfc2616 demands the location value be a complete URI
// Location = "Location" ":" absoluteURI
if (!uri.isAbsolute()) {
if (params.isParameterTrue(ClientPNames.REJECT_RELATIVE_REDIRECT)) {
throw new ProtocolException("Relative redirect location '"
+ uri + "' not allowed");
}
// Adjust location URI
final HttpHost target = (HttpHost) context.getAttribute(
ExecutionContext.HTTP_TARGET_HOST);
Asserts.notNull(target, "Target host");
final HttpRequest request = (HttpRequest) context.getAttribute(
ExecutionContext.HTTP_REQUEST);
try {
final URI requestURI = new URI(request.getRequestLine().getUri());
final URI absoluteRequestURI = URIUtils.rewriteURI(requestURI, target, true);
uri = URIUtils.resolve(absoluteRequestURI, uri);
} catch (final URISyntaxException ex) {
throw new ProtocolException(ex.getMessage(), ex);
}
}
if (params.isParameterFalse(ClientPNames.ALLOW_CIRCULAR_REDIRECTS)) {
RedirectLocations redirectLocations = (RedirectLocations) context.getAttribute(
REDIRECT_LOCATIONS);
if (redirectLocations == null) {
redirectLocations = new RedirectLocations();
context.setAttribute(REDIRECT_LOCATIONS, redirectLocations);
}
final URI redirectURI;
if (uri.getFragment() != null) {
try {
final HttpHost target = new HttpHost(
uri.getHost(),
uri.getPort(),
uri.getScheme());
redirectURI = URIUtils.rewriteURI(uri, target, true);
} catch (final URISyntaxException ex) {
throw new ProtocolException(ex.getMessage(), ex);
}
} else {
redirectURI = uri;
}
if (redirectLocations.contains(redirectURI)) {
throw new CircularRedirectException("Circular redirect to '" +
redirectURI + "'");
} else {
redirectLocations.add(redirectURI);
}
}
return uri;
}
}

View file

@ -1,233 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Locale;
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
/* LogFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpHost;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.HttpStatus;
import ch.boye.httpclientandroidlib.ProtocolException;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.CircularRedirectException;
import ch.boye.httpclientandroidlib.client.RedirectStrategy;
import ch.boye.httpclientandroidlib.client.config.RequestConfig;
import ch.boye.httpclientandroidlib.client.methods.HttpGet;
import ch.boye.httpclientandroidlib.client.methods.HttpHead;
import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
import ch.boye.httpclientandroidlib.client.methods.RequestBuilder;
import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
import ch.boye.httpclientandroidlib.client.utils.URIBuilder;
import ch.boye.httpclientandroidlib.client.utils.URIUtils;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.Asserts;
import ch.boye.httpclientandroidlib.util.TextUtils;
/**
* Default implementation of {@link RedirectStrategy}. This strategy honors the restrictions
* on automatic redirection of entity enclosing methods such as POST and PUT imposed by the
* HTTP specification. <tt>302 Moved Temporarily</tt>, <tt>301 Moved Permanently</tt> and
* <tt>307 Temporary Redirect</tt> status codes will result in an automatic redirect of
* HEAD and GET methods only. POST and PUT methods will not be automatically redirected
* as requiring user confirmation.
* <p/>
* The restriction on automatic redirection of POST methods can be relaxed by using
* {@link LaxRedirectStrategy} instead of {@link DefaultRedirectStrategy}.
*
* @see LaxRedirectStrategy
* @since 4.1
*/
@Immutable
public class DefaultRedirectStrategy implements RedirectStrategy {
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
/**
* @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.client.protocol.HttpClientContext#REDIRECT_LOCATIONS}.
*/
@Deprecated
public static final String REDIRECT_LOCATIONS = "http.protocol.redirect-locations";
public static final DefaultRedirectStrategy INSTANCE = new DefaultRedirectStrategy();
/**
* Redirectable methods.
*/
private static final String[] REDIRECT_METHODS = new String[] {
HttpGet.METHOD_NAME,
HttpHead.METHOD_NAME
};
public DefaultRedirectStrategy() {
super();
}
public boolean isRedirected(
final HttpRequest request,
final HttpResponse response,
final HttpContext context) throws ProtocolException {
Args.notNull(request, "HTTP request");
Args.notNull(response, "HTTP response");
final int statusCode = response.getStatusLine().getStatusCode();
final String method = request.getRequestLine().getMethod();
final Header locationHeader = response.getFirstHeader("location");
switch (statusCode) {
case HttpStatus.SC_MOVED_TEMPORARILY:
return isRedirectable(method) && locationHeader != null;
case HttpStatus.SC_MOVED_PERMANENTLY:
case HttpStatus.SC_TEMPORARY_REDIRECT:
return isRedirectable(method);
case HttpStatus.SC_SEE_OTHER:
return true;
default:
return false;
} //end of switch
}
public URI getLocationURI(
final HttpRequest request,
final HttpResponse response,
final HttpContext context) throws ProtocolException {
Args.notNull(request, "HTTP request");
Args.notNull(response, "HTTP response");
Args.notNull(context, "HTTP context");
final HttpClientContext clientContext = HttpClientContext.adapt(context);
//get the location header to find out where to redirect to
final Header locationHeader = response.getFirstHeader("location");
if (locationHeader == null) {
// got a redirect response, but no location header
throw new ProtocolException(
"Received redirect response " + response.getStatusLine()
+ " but no location header");
}
final String location = locationHeader.getValue();
if (this.log.isDebugEnabled()) {
this.log.debug("Redirect requested to location '" + location + "'");
}
final RequestConfig config = clientContext.getRequestConfig();
URI uri = createLocationURI(location);
// rfc2616 demands the location value be a complete URI
// Location = "Location" ":" absoluteURI
try {
if (!uri.isAbsolute()) {
if (!config.isRelativeRedirectsAllowed()) {
throw new ProtocolException("Relative redirect location '"
+ uri + "' not allowed");
}
// Adjust location URI
final HttpHost target = clientContext.getTargetHost();
Asserts.notNull(target, "Target host");
final URI requestURI = new URI(request.getRequestLine().getUri());
final URI absoluteRequestURI = URIUtils.rewriteURI(requestURI, target, false);
uri = URIUtils.resolve(absoluteRequestURI, uri);
}
} catch (final URISyntaxException ex) {
throw new ProtocolException(ex.getMessage(), ex);
}
RedirectLocations redirectLocations = (RedirectLocations) clientContext.getAttribute(
HttpClientContext.REDIRECT_LOCATIONS);
if (redirectLocations == null) {
redirectLocations = new RedirectLocations();
context.setAttribute(HttpClientContext.REDIRECT_LOCATIONS, redirectLocations);
}
if (!config.isCircularRedirectsAllowed()) {
if (redirectLocations.contains(uri)) {
throw new CircularRedirectException("Circular redirect to '" + uri + "'");
}
}
redirectLocations.add(uri);
return uri;
}
/**
* @since 4.1
*/
protected URI createLocationURI(final String location) throws ProtocolException {
try {
final URIBuilder b = new URIBuilder(new URI(location).normalize());
final String host = b.getHost();
if (host != null) {
b.setHost(host.toLowerCase(Locale.ENGLISH));
}
final String path = b.getPath();
if (TextUtils.isEmpty(path)) {
b.setPath("/");
}
return b.build();
} catch (final URISyntaxException ex) {
throw new ProtocolException("Invalid redirect URI: " + location, ex);
}
}
/**
* @since 4.2
*/
protected boolean isRedirectable(final String method) {
for (final String m: REDIRECT_METHODS) {
if (m.equalsIgnoreCase(method)) {
return true;
}
}
return false;
}
public HttpUriRequest getRedirect(
final HttpRequest request,
final HttpResponse response,
final HttpContext context) throws ProtocolException {
final URI uri = getLocationURI(request, response, context);
final String method = request.getRequestLine().getMethod();
if (method.equalsIgnoreCase(HttpHead.METHOD_NAME)) {
return new HttpHead(uri);
} else if (method.equalsIgnoreCase(HttpGet.METHOD_NAME)) {
return new HttpGet(uri);
} else {
final int status = response.getStatusLine().getStatusCode();
if (status == HttpStatus.SC_TEMPORARY_REDIRECT) {
return RequestBuilder.copy(request).setUri(uri).build();
} else {
return new HttpGet(uri);
}
}
}
}

View file

@ -1,81 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.net.URI;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.ProtocolException;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.RedirectHandler;
import ch.boye.httpclientandroidlib.client.RedirectStrategy;
import ch.boye.httpclientandroidlib.client.methods.HttpGet;
import ch.boye.httpclientandroidlib.client.methods.HttpHead;
import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
/**
* @deprecated (4.1) do not use
*/
@Immutable
@Deprecated
class DefaultRedirectStrategyAdaptor implements RedirectStrategy {
private final RedirectHandler handler;
public DefaultRedirectStrategyAdaptor(final RedirectHandler handler) {
super();
this.handler = handler;
}
public boolean isRedirected(
final HttpRequest request,
final HttpResponse response,
final HttpContext context) throws ProtocolException {
return this.handler.isRedirectRequested(response, context);
}
public HttpUriRequest getRedirect(
final HttpRequest request,
final HttpResponse response,
final HttpContext context) throws ProtocolException {
final URI uri = this.handler.getLocationURI(response, context);
final String method = request.getRequestLine().getMethod();
if (method.equalsIgnoreCase(HttpHead.METHOD_NAME)) {
return new HttpHead(uri);
} else {
return new HttpGet(uri);
}
}
public RedirectHandler getHandler() {
return this.handler;
}
}

View file

@ -1,80 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.HttpStatus;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.ServiceUnavailableRetryStrategy;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Default implementation of the {@link ServiceUnavailableRetryStrategy} interface.
* that retries <code>503</code> (Service Unavailable) responses for a fixed number of times
* at a fixed interval.
*
* @since 4.2
*/
@Immutable
public class DefaultServiceUnavailableRetryStrategy implements ServiceUnavailableRetryStrategy {
/**
* Maximum number of allowed retries if the server responds with a HTTP code
* in our retry code list. Default value is 1.
*/
private final int maxRetries;
/**
* Retry interval between subsequent requests, in milliseconds. Default
* value is 1 second.
*/
private final long retryInterval;
public DefaultServiceUnavailableRetryStrategy(final int maxRetries, final int retryInterval) {
super();
Args.positive(maxRetries, "Max retries");
Args.positive(retryInterval, "Retry interval");
this.maxRetries = maxRetries;
this.retryInterval = retryInterval;
}
public DefaultServiceUnavailableRetryStrategy() {
this(1, 1000);
}
public boolean retryRequest(final HttpResponse response, final int executionCount, final HttpContext context) {
return executionCount <= maxRetries &&
response.getStatusLine().getStatusCode() == HttpStatus.SC_SERVICE_UNAVAILABLE;
}
public long getRetryInterval() {
return retryInterval;
}
}

View file

@ -1,91 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.util.List;
import java.util.Map;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.HttpStatus;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.auth.AUTH;
import ch.boye.httpclientandroidlib.auth.MalformedChallengeException;
import ch.boye.httpclientandroidlib.auth.params.AuthPNames;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Default {@link ch.boye.httpclientandroidlib.client.AuthenticationHandler} implementation
* for target host authentication.
*
* @since 4.0
*
* @deprecated (4.2) use {@link TargetAuthenticationStrategy}
*/
@Deprecated
@Immutable
public class DefaultTargetAuthenticationHandler extends AbstractAuthenticationHandler {
public DefaultTargetAuthenticationHandler() {
super();
}
public boolean isAuthenticationRequested(
final HttpResponse response,
final HttpContext context) {
Args.notNull(response, "HTTP response");
final int status = response.getStatusLine().getStatusCode();
return status == HttpStatus.SC_UNAUTHORIZED;
}
public Map<String, Header> getChallenges(
final HttpResponse response,
final HttpContext context) throws MalformedChallengeException {
Args.notNull(response, "HTTP response");
final Header[] headers = response.getHeaders(AUTH.WWW_AUTH);
return parseChallenges(headers);
}
@Override
protected List<String> getAuthPreferences(
final HttpResponse response,
final HttpContext context) {
@SuppressWarnings("unchecked")
final
List<String> authpref = (List<String>) response.getParams().getParameter(
AuthPNames.TARGET_AUTH_PREF);
if (authpref != null) {
return authpref;
} else {
return super.getAuthPreferences(response, context);
}
}
}

View file

@ -1,101 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.security.Principal;
import javax.net.ssl.SSLSession;
import ch.boye.httpclientandroidlib.HttpConnection;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.auth.AuthScheme;
import ch.boye.httpclientandroidlib.auth.AuthState;
import ch.boye.httpclientandroidlib.auth.Credentials;
import ch.boye.httpclientandroidlib.client.UserTokenHandler;
import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
import ch.boye.httpclientandroidlib.conn.ManagedHttpClientConnection;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
/**
* Default implementation of {@link UserTokenHandler}. This class will use
* an instance of {@link Principal} as a state object for HTTP connections,
* if it can be obtained from the given execution context. This helps ensure
* persistent connections created with a particular user identity within
* a particular security context can be reused by the same user only.
* <p>
* DefaultUserTokenHandler will use the user principle of connection
* based authentication schemes such as NTLM or that of the SSL session
* with the client authentication turned on. If both are unavailable,
* <code>null</code> token will be returned.
*
* @since 4.0
*/
@Immutable
public class DefaultUserTokenHandler implements UserTokenHandler {
public static final DefaultUserTokenHandler INSTANCE = new DefaultUserTokenHandler();
public Object getUserToken(final HttpContext context) {
final HttpClientContext clientContext = HttpClientContext.adapt(context);
Principal userPrincipal = null;
final AuthState targetAuthState = clientContext.getTargetAuthState();
if (targetAuthState != null) {
userPrincipal = getAuthPrincipal(targetAuthState);
if (userPrincipal == null) {
final AuthState proxyAuthState = clientContext.getProxyAuthState();
userPrincipal = getAuthPrincipal(proxyAuthState);
}
}
if (userPrincipal == null) {
final HttpConnection conn = clientContext.getConnection();
if (conn.isOpen() && conn instanceof ManagedHttpClientConnection) {
final SSLSession sslsession = ((ManagedHttpClientConnection) conn).getSSLSession();
if (sslsession != null) {
userPrincipal = sslsession.getLocalPrincipal();
}
}
}
return userPrincipal;
}
private static Principal getAuthPrincipal(final AuthState authState) {
final AuthScheme scheme = authState.getAuthScheme();
if (scheme != null && scheme.isComplete() && scheme.isConnectionBased()) {
final Credentials creds = authState.getCredentials();
if (creds != null) {
return creds.getUserPrincipal();
}
}
return null;
}
}

View file

@ -1,113 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpEntity;
import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest;
import ch.boye.httpclientandroidlib.ProtocolException;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.entity.HttpEntityWrapper;
import ch.boye.httpclientandroidlib.protocol.HTTP;
/**
* A wrapper class for {@link HttpEntityEnclosingRequest}s that can
* be used to change properties of the current request without
* modifying the original object.
* </p>
* This class is also capable of resetting the request headers to
* the state of the original request.
*
* @since 4.0
*
* @deprecated (4.3) do not use.
*/
@Deprecated
@NotThreadSafe // e.g. [gs]etEntity()
public class EntityEnclosingRequestWrapper extends RequestWrapper
implements HttpEntityEnclosingRequest {
private HttpEntity entity;
private boolean consumed;
public EntityEnclosingRequestWrapper(final HttpEntityEnclosingRequest request)
throws ProtocolException {
super(request);
setEntity(request.getEntity());
}
public HttpEntity getEntity() {
return this.entity;
}
public void setEntity(final HttpEntity entity) {
this.entity = entity != null ? new EntityWrapper(entity) : null;
this.consumed = false;
}
public boolean expectContinue() {
final Header expect = getFirstHeader(HTTP.EXPECT_DIRECTIVE);
return expect != null && HTTP.EXPECT_CONTINUE.equalsIgnoreCase(expect.getValue());
}
@Override
public boolean isRepeatable() {
return this.entity == null || this.entity.isRepeatable() || !this.consumed;
}
class EntityWrapper extends HttpEntityWrapper {
EntityWrapper(final HttpEntity entity) {
super(entity);
}
@Override
public void consumeContent() throws IOException {
consumed = true;
super.consumeContent();
}
@Override
public InputStream getContent() throws IOException {
consumed = true;
return super.getContent();
}
@Override
public void writeTo(final OutputStream outstream) throws IOException {
consumed = true;
super.writeTo(outstream);
}
}
}

View file

@ -1,156 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.util.concurrent.atomic.AtomicLong;
/**
* Collection of different counters used to gather metrics for {@link FutureRequestExecutionService}.
*/
public final class FutureRequestExecutionMetrics {
private final AtomicLong activeConnections = new AtomicLong();
private final AtomicLong scheduledConnections = new AtomicLong();
private final DurationCounter successfulConnections = new DurationCounter();
private final DurationCounter failedConnections = new DurationCounter();
private final DurationCounter requests = new DurationCounter();
private final DurationCounter tasks = new DurationCounter();
FutureRequestExecutionMetrics() {
}
AtomicLong getActiveConnections() {
return activeConnections;
}
AtomicLong getScheduledConnections() {
return scheduledConnections;
}
DurationCounter getSuccessfulConnections() {
return successfulConnections;
}
DurationCounter getFailedConnections() {
return failedConnections;
}
DurationCounter getRequests() {
return requests;
}
DurationCounter getTasks() {
return tasks;
}
public long getActiveConnectionCount() {
return activeConnections.get();
}
public long getScheduledConnectionCount() {
return scheduledConnections.get();
}
public long getSuccessfulConnectionCount() {
return successfulConnections.count();
}
public long getSuccessfulConnectionAverageDuration() {
return successfulConnections.averageDuration();
}
public long getFailedConnectionCount() {
return failedConnections.count();
}
public long getFailedConnectionAverageDuration() {
return failedConnections.averageDuration();
}
public long getRequestCount() {
return requests.count();
}
public long getRequestAverageDuration() {
return requests.averageDuration();
}
public long getTaskCount() {
return tasks.count();
}
public long getTaskAverageDuration() {
return tasks.averageDuration();
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("[activeConnections=").append(activeConnections)
.append(", scheduledConnections=").append(scheduledConnections)
.append(", successfulConnections=").append(successfulConnections)
.append(", failedConnections=").append(failedConnections)
.append(", requests=").append(requests)
.append(", tasks=").append(tasks)
.append("]");
return builder.toString();
}
/**
* A counter that can measure duration and number of events.
*/
static class DurationCounter {
private final AtomicLong count = new AtomicLong(0);
private final AtomicLong cumulativeDuration = new AtomicLong(0);
public void increment(final long startTime) {
count.incrementAndGet();
cumulativeDuration.addAndGet(System.currentTimeMillis() - startTime);
}
public long count() {
return count.get();
}
public long averageDuration() {
final long counter = count.get();
return counter > 0 ? cumulativeDuration.get() / counter : 0;
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("[count=").append(count())
.append(", averageDuration=").append(averageDuration())
.append("]");
return builder.toString();
}
}
}

View file

@ -1,142 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.io.Closeable;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
import ch.boye.httpclientandroidlib.client.HttpClient;
import ch.boye.httpclientandroidlib.client.ResponseHandler;
import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
import ch.boye.httpclientandroidlib.concurrent.FutureCallback;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
/**
* HttpAsyncClientWithFuture wraps calls to execute with a {@link HttpRequestFutureTask}
* and schedules them using the provided executor service. Scheduled calls may be cancelled.
*/
@ThreadSafe
public class FutureRequestExecutionService implements Closeable {
private final HttpClient httpclient;
private final ExecutorService executorService;
private final FutureRequestExecutionMetrics metrics = new FutureRequestExecutionMetrics();
private final AtomicBoolean closed = new AtomicBoolean(false);
/**
* Create a new FutureRequestExecutionService.
*
* @param httpclient
* you should tune your httpclient instance to match your needs. You should
* align the max number of connections in the pool and the number of threads
* in the executor; it doesn't make sense to have more threads than connections
* and if you have less connections than threads, the threads will just end up
* blocking on getting a connection from the pool.
* @param executorService
* any executorService will do here. E.g.
* {@link java.util.concurrent.Executors#newFixedThreadPool(int)}
*/
public FutureRequestExecutionService(
final HttpClient httpclient,
final ExecutorService executorService) {
this.httpclient = httpclient;
this.executorService = executorService;
}
/**
* Schedule a request for execution.
*
* @param <T>
*
* @param request
* request to execute
* @param responseHandler
* handler that will process the response.
* @return HttpAsyncClientFutureTask for the scheduled request.
* @throws InterruptedException
*/
public <T> HttpRequestFutureTask<T> execute(
final HttpUriRequest request,
final HttpContext context,
final ResponseHandler<T> responseHandler) {
return execute(request, context, responseHandler, null);
}
/**
* Schedule a request for execution.
*
* @param <T>
*
* @param request
* request to execute
* @param context
* optional context; use null if not needed.
* @param responseHandler
* handler that will process the response.
* @param callback
* callback handler that will be called when the request is scheduled,
* started, completed, failed, or cancelled.
* @return HttpAsyncClientFutureTask for the scheduled request.
* @throws InterruptedException
*/
public <T> HttpRequestFutureTask<T> execute(
final HttpUriRequest request,
final HttpContext context,
final ResponseHandler<T> responseHandler,
final FutureCallback<T> callback) {
if(closed.get()) {
throw new IllegalStateException("Close has been called on this httpclient instance.");
}
metrics.getScheduledConnections().incrementAndGet();
final HttpRequestTaskCallable<T> callable = new HttpRequestTaskCallable<T>(
httpclient, request, context, responseHandler, callback, metrics);
final HttpRequestFutureTask<T> httpRequestFutureTask = new HttpRequestFutureTask<T>(
request, callable);
executorService.execute(httpRequestFutureTask);
return httpRequestFutureTask;
}
/**
* @return metrics gathered for this instance.
* @see FutureRequestExecutionMetrics
*/
public FutureRequestExecutionMetrics metrics() {
return metrics;
}
public void close() throws IOException {
closed.set(true);
executorService.shutdownNow();
if (httpclient instanceof Closeable) {
((Closeable) httpclient).close();
}
}
}

View file

@ -1,61 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
import ch.boye.httpclientandroidlib.HttpHost;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.auth.AuthState;
import ch.boye.httpclientandroidlib.client.AuthenticationStrategy;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
/**
* @deprecated (4.3) reserved for internal use.
*
*/
@Deprecated
public class HttpAuthenticator extends ch.boye.httpclientandroidlib.impl.auth.HttpAuthenticator {
public HttpAuthenticator(final HttpClientAndroidLog log) {
super(log);
}
public HttpAuthenticator() {
super();
}
public boolean authenticate (
final HttpHost host,
final HttpResponse response,
final AuthenticationStrategy authStrategy,
final AuthState authState,
final HttpContext context) {
return handleAuthChallenge(host, response, authStrategy, authState, context);
}
}

View file

@ -1,954 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.io.Closeable;
import java.net.ProxySelector;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import ch.boye.httpclientandroidlib.ConnectionReuseStrategy;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpHost;
import ch.boye.httpclientandroidlib.HttpRequestInterceptor;
import ch.boye.httpclientandroidlib.HttpResponseInterceptor;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.auth.AuthSchemeProvider;
import ch.boye.httpclientandroidlib.client.AuthenticationStrategy;
import ch.boye.httpclientandroidlib.client.BackoffManager;
import ch.boye.httpclientandroidlib.client.ConnectionBackoffStrategy;
import ch.boye.httpclientandroidlib.client.CookieStore;
import ch.boye.httpclientandroidlib.client.CredentialsProvider;
import ch.boye.httpclientandroidlib.client.HttpRequestRetryHandler;
import ch.boye.httpclientandroidlib.client.RedirectStrategy;
import ch.boye.httpclientandroidlib.client.ServiceUnavailableRetryStrategy;
import ch.boye.httpclientandroidlib.client.UserTokenHandler;
import ch.boye.httpclientandroidlib.client.config.AuthSchemes;
import ch.boye.httpclientandroidlib.client.config.CookieSpecs;
import ch.boye.httpclientandroidlib.client.config.RequestConfig;
import ch.boye.httpclientandroidlib.client.protocol.RequestAcceptEncoding;
import ch.boye.httpclientandroidlib.client.protocol.RequestAddCookies;
import ch.boye.httpclientandroidlib.client.protocol.RequestAuthCache;
import ch.boye.httpclientandroidlib.client.protocol.RequestClientConnControl;
import ch.boye.httpclientandroidlib.client.protocol.RequestDefaultHeaders;
import ch.boye.httpclientandroidlib.client.protocol.RequestExpectContinue;
import ch.boye.httpclientandroidlib.client.protocol.ResponseContentEncoding;
import ch.boye.httpclientandroidlib.client.protocol.ResponseProcessCookies;
import ch.boye.httpclientandroidlib.config.ConnectionConfig;
import ch.boye.httpclientandroidlib.config.Lookup;
import ch.boye.httpclientandroidlib.config.RegistryBuilder;
import ch.boye.httpclientandroidlib.config.SocketConfig;
import ch.boye.httpclientandroidlib.conn.ConnectionKeepAliveStrategy;
import ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager;
import ch.boye.httpclientandroidlib.conn.SchemePortResolver;
import ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner;
import ch.boye.httpclientandroidlib.conn.socket.ConnectionSocketFactory;
import ch.boye.httpclientandroidlib.conn.socket.LayeredConnectionSocketFactory;
import ch.boye.httpclientandroidlib.conn.socket.PlainConnectionSocketFactory;
import ch.boye.httpclientandroidlib.conn.ssl.SSLConnectionSocketFactory;
import ch.boye.httpclientandroidlib.conn.ssl.SSLContexts;
import ch.boye.httpclientandroidlib.conn.ssl.X509HostnameVerifier;
import ch.boye.httpclientandroidlib.cookie.CookieSpecProvider;
import ch.boye.httpclientandroidlib.impl.DefaultConnectionReuseStrategy;
import ch.boye.httpclientandroidlib.impl.NoConnectionReuseStrategy;
import ch.boye.httpclientandroidlib.impl.auth.BasicSchemeFactory;
import ch.boye.httpclientandroidlib.impl.auth.DigestSchemeFactory;
/* KerberosSchemeFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.impl.auth.NTLMSchemeFactory;
/* SPNegoSchemeFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.impl.conn.DefaultProxyRoutePlanner;
import ch.boye.httpclientandroidlib.impl.conn.DefaultRoutePlanner;
import ch.boye.httpclientandroidlib.impl.conn.DefaultSchemePortResolver;
import ch.boye.httpclientandroidlib.impl.conn.PoolingHttpClientConnectionManager;
import ch.boye.httpclientandroidlib.impl.conn.SystemDefaultRoutePlanner;
import ch.boye.httpclientandroidlib.impl.cookie.BestMatchSpecFactory;
import ch.boye.httpclientandroidlib.impl.cookie.BrowserCompatSpecFactory;
import ch.boye.httpclientandroidlib.impl.cookie.IgnoreSpecFactory;
import ch.boye.httpclientandroidlib.impl.cookie.NetscapeDraftSpecFactory;
import ch.boye.httpclientandroidlib.impl.cookie.RFC2109SpecFactory;
import ch.boye.httpclientandroidlib.impl.cookie.RFC2965SpecFactory;
import ch.boye.httpclientandroidlib.impl.execchain.BackoffStrategyExec;
import ch.boye.httpclientandroidlib.impl.execchain.ClientExecChain;
import ch.boye.httpclientandroidlib.impl.execchain.MainClientExec;
import ch.boye.httpclientandroidlib.impl.execchain.ProtocolExec;
import ch.boye.httpclientandroidlib.impl.execchain.RedirectExec;
import ch.boye.httpclientandroidlib.impl.execchain.RetryExec;
import ch.boye.httpclientandroidlib.impl.execchain.ServiceUnavailableRetryExec;
import ch.boye.httpclientandroidlib.protocol.HttpProcessor;
import ch.boye.httpclientandroidlib.protocol.HttpProcessorBuilder;
import ch.boye.httpclientandroidlib.protocol.HttpRequestExecutor;
import ch.boye.httpclientandroidlib.protocol.RequestContent;
import ch.boye.httpclientandroidlib.protocol.RequestTargetHost;
import ch.boye.httpclientandroidlib.protocol.RequestUserAgent;
import ch.boye.httpclientandroidlib.util.TextUtils;
import ch.boye.httpclientandroidlib.util.VersionInfo;
/**
* Builder for {@link CloseableHttpClient} instances.
* <p/>
* When a particular component is not explicitly this class will
* use its default implementation. System properties will be taken
* into account when configuring the default implementations when
* {@link #useSystemProperties()} method is called prior to calling
* {@link #build()}.
* <ul>
* <li>ssl.TrustManagerFactory.algorithm</li>
* <li>javax.net.ssl.trustStoreType</li>
* <li>javax.net.ssl.trustStore</li>
* <li>javax.net.ssl.trustStoreProvider</li>
* <li>javax.net.ssl.trustStorePassword</li>
* <li>ssl.KeyManagerFactory.algorithm</li>
* <li>javax.net.ssl.keyStoreType</li>
* <li>javax.net.ssl.keyStore</li>
* <li>javax.net.ssl.keyStoreProvider</li>
* <li>javax.net.ssl.keyStorePassword</li>
* <li>https.protocols</li>
* <li>https.cipherSuites</li>
* <li>http.proxyHost</li>
* <li>http.proxyPort</li>
* <li>http.nonProxyHosts</li>
* <li>http.keepAlive</li>
* <li>http.maxConnections</li>
* <li>http.agent</li>
* </ul>
* <p/>
* Please note that some settings used by this class can be mutually
* exclusive and may not apply when building {@link CloseableHttpClient}
* instances.
*
* @since 4.3
*/
@NotThreadSafe
public class HttpClientBuilder {
private HttpRequestExecutor requestExec;
private X509HostnameVerifier hostnameVerifier;
private LayeredConnectionSocketFactory sslSocketFactory;
private SSLContext sslcontext;
private HttpClientConnectionManager connManager;
private SchemePortResolver schemePortResolver;
private ConnectionReuseStrategy reuseStrategy;
private ConnectionKeepAliveStrategy keepAliveStrategy;
private AuthenticationStrategy targetAuthStrategy;
private AuthenticationStrategy proxyAuthStrategy;
private UserTokenHandler userTokenHandler;
private HttpProcessor httpprocessor;
private LinkedList<HttpRequestInterceptor> requestFirst;
private LinkedList<HttpRequestInterceptor> requestLast;
private LinkedList<HttpResponseInterceptor> responseFirst;
private LinkedList<HttpResponseInterceptor> responseLast;
private HttpRequestRetryHandler retryHandler;
private HttpRoutePlanner routePlanner;
private RedirectStrategy redirectStrategy;
private ConnectionBackoffStrategy connectionBackoffStrategy;
private BackoffManager backoffManager;
private ServiceUnavailableRetryStrategy serviceUnavailStrategy;
private Lookup<AuthSchemeProvider> authSchemeRegistry;
private Lookup<CookieSpecProvider> cookieSpecRegistry;
private CookieStore cookieStore;
private CredentialsProvider credentialsProvider;
private String userAgent;
private HttpHost proxy;
private Collection<? extends Header> defaultHeaders;
private SocketConfig defaultSocketConfig;
private ConnectionConfig defaultConnectionConfig;
private RequestConfig defaultRequestConfig;
private boolean systemProperties;
private boolean redirectHandlingDisabled;
private boolean automaticRetriesDisabled;
private boolean contentCompressionDisabled;
private boolean cookieManagementDisabled;
private boolean authCachingDisabled;
private boolean connectionStateDisabled;
private int maxConnTotal = 0;
private int maxConnPerRoute = 0;
private List<Closeable> closeables;
static final String DEFAULT_USER_AGENT;
static {
final VersionInfo vi = VersionInfo.loadVersionInfo
("ch.boye.httpclientandroidlib.client", HttpClientBuilder.class.getClassLoader());
final String release = (vi != null) ?
vi.getRelease() : VersionInfo.UNAVAILABLE;
DEFAULT_USER_AGENT = "Apache-HttpClient/" + release + " (java 1.5)";
}
public static HttpClientBuilder create() {
return new HttpClientBuilder();
}
protected HttpClientBuilder() {
super();
}
/**
* Assigns {@link HttpRequestExecutor} instance.
*/
public final HttpClientBuilder setRequestExecutor(final HttpRequestExecutor requestExec) {
this.requestExec = requestExec;
return this;
}
/**
* Assigns {@link X509HostnameVerifier} instance.
* <p/>
* Please note this value can be overridden by the {@link #setConnectionManager(
* ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager)} and the {@link #setSSLSocketFactory(
* ch.boye.httpclientandroidlib.conn.socket.LayeredConnectionSocketFactory)} methods.
*/
public final HttpClientBuilder setHostnameVerifier(final X509HostnameVerifier hostnameVerifier) {
this.hostnameVerifier = hostnameVerifier;
return this;
}
/**
* Assigns {@link SSLContext} instance.
* <p/>
* <p/>
* Please note this value can be overridden by the {@link #setConnectionManager(
* ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager)} and the {@link #setSSLSocketFactory(
* ch.boye.httpclientandroidlib.conn.socket.LayeredConnectionSocketFactory)} methods.
*/
public final HttpClientBuilder setSslcontext(final SSLContext sslcontext) {
this.sslcontext = sslcontext;
return this;
}
/**
* Assigns {@link LayeredConnectionSocketFactory} instance.
* <p/>
* Please note this value can be overridden by the {@link #setConnectionManager(
* ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager)} method.
*/
public final HttpClientBuilder setSSLSocketFactory(
final LayeredConnectionSocketFactory sslSocketFactory) {
this.sslSocketFactory = sslSocketFactory;
return this;
}
/**
* Assigns maximum total connection value.
* <p/>
* Please note this value can be overridden by the {@link #setConnectionManager(
* ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager)} method.
*/
public final HttpClientBuilder setMaxConnTotal(final int maxConnTotal) {
this.maxConnTotal = maxConnTotal;
return this;
}
/**
* Assigns maximum connection per route value.
* <p/>
* Please note this value can be overridden by the {@link #setConnectionManager(
* ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager)} method.
*/
public final HttpClientBuilder setMaxConnPerRoute(final int maxConnPerRoute) {
this.maxConnPerRoute = maxConnPerRoute;
return this;
}
/**
* Assigns default {@link SocketConfig}.
* <p/>
* Please note this value can be overridden by the {@link #setConnectionManager(
* ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager)} method.
*/
public final HttpClientBuilder setDefaultSocketConfig(final SocketConfig config) {
this.defaultSocketConfig = config;
return this;
}
/**
* Assigns default {@link ConnectionConfig}.
* <p/>
* Please note this value can be overridden by the {@link #setConnectionManager(
* ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager)} method.
*/
public final HttpClientBuilder setDefaultConnectionConfig(final ConnectionConfig config) {
this.defaultConnectionConfig = config;
return this;
}
/**
* Assigns {@link HttpClientConnectionManager} instance.
*/
public final HttpClientBuilder setConnectionManager(
final HttpClientConnectionManager connManager) {
this.connManager = connManager;
return this;
}
/**
* Assigns {@link ConnectionReuseStrategy} instance.
*/
public final HttpClientBuilder setConnectionReuseStrategy(
final ConnectionReuseStrategy reuseStrategy) {
this.reuseStrategy = reuseStrategy;
return this;
}
/**
* Assigns {@link ConnectionKeepAliveStrategy} instance.
*/
public final HttpClientBuilder setKeepAliveStrategy(
final ConnectionKeepAliveStrategy keepAliveStrategy) {
this.keepAliveStrategy = keepAliveStrategy;
return this;
}
/**
* Assigns {@link AuthenticationStrategy} instance for proxy
* authentication.
*/
public final HttpClientBuilder setTargetAuthenticationStrategy(
final AuthenticationStrategy targetAuthStrategy) {
this.targetAuthStrategy = targetAuthStrategy;
return this;
}
/**
* Assigns {@link AuthenticationStrategy} instance for target
* host authentication.
*/
public final HttpClientBuilder setProxyAuthenticationStrategy(
final AuthenticationStrategy proxyAuthStrategy) {
this.proxyAuthStrategy = proxyAuthStrategy;
return this;
}
/**
* Assigns {@link UserTokenHandler} instance.
* <p/>
* Please note this value can be overridden by the {@link #disableConnectionState()}
* method.
*/
public final HttpClientBuilder setUserTokenHandler(final UserTokenHandler userTokenHandler) {
this.userTokenHandler = userTokenHandler;
return this;
}
/**
* Disables connection state tracking.
*/
public final HttpClientBuilder disableConnectionState() {
connectionStateDisabled = true;
return this;
}
/**
* Assigns {@link SchemePortResolver} instance.
*/
public final HttpClientBuilder setSchemePortResolver(
final SchemePortResolver schemePortResolver) {
this.schemePortResolver = schemePortResolver;
return this;
}
/**
* Assigns <tt>User-Agent</tt> value.
* <p/>
* Please note this value can be overridden by the {@link #setHttpProcessor(
* ch.boye.httpclientandroidlib.protocol.HttpProcessor)} method.
*/
public final HttpClientBuilder setUserAgent(final String userAgent) {
this.userAgent = userAgent;
return this;
}
/**
* Assigns default request header values.
* <p/>
* Please note this value can be overridden by the {@link #setHttpProcessor(
* ch.boye.httpclientandroidlib.protocol.HttpProcessor)} method.
*/
public final HttpClientBuilder setDefaultHeaders(final Collection<? extends Header> defaultHeaders) {
this.defaultHeaders = defaultHeaders;
return this;
}
/**
* Adds this protocol interceptor to the head of the protocol processing list.
* <p/>
* Please note this value can be overridden by the {@link #setHttpProcessor(
* ch.boye.httpclientandroidlib.protocol.HttpProcessor)} method.
*/
public final HttpClientBuilder addInterceptorFirst(final HttpResponseInterceptor itcp) {
if (itcp == null) {
return this;
}
if (responseFirst == null) {
responseFirst = new LinkedList<HttpResponseInterceptor>();
}
responseFirst.addFirst(itcp);
return this;
}
/**
* Adds this protocol interceptor to the tail of the protocol processing list.
* <p/>
* Please note this value can be overridden by the {@link #setHttpProcessor(
* ch.boye.httpclientandroidlib.protocol.HttpProcessor)} method.
*/
public final HttpClientBuilder addInterceptorLast(final HttpResponseInterceptor itcp) {
if (itcp == null) {
return this;
}
if (responseLast == null) {
responseLast = new LinkedList<HttpResponseInterceptor>();
}
responseLast.addLast(itcp);
return this;
}
/**
* Adds this protocol interceptor to the head of the protocol processing list.
* <p/>
* Please note this value can be overridden by the {@link #setHttpProcessor(
* ch.boye.httpclientandroidlib.protocol.HttpProcessor)} method.
*/
public final HttpClientBuilder addInterceptorFirst(final HttpRequestInterceptor itcp) {
if (itcp == null) {
return this;
}
if (requestFirst == null) {
requestFirst = new LinkedList<HttpRequestInterceptor>();
}
requestFirst.addFirst(itcp);
return this;
}
/**
* Adds this protocol interceptor to the tail of the protocol processing list.
* <p/>
* Please note this value can be overridden by the {@link #setHttpProcessor(
* ch.boye.httpclientandroidlib.protocol.HttpProcessor)} method.
*/
public final HttpClientBuilder addInterceptorLast(final HttpRequestInterceptor itcp) {
if (itcp == null) {
return this;
}
if (requestLast == null) {
requestLast = new LinkedList<HttpRequestInterceptor>();
}
requestLast.addLast(itcp);
return this;
}
/**
* Disables state (cookie) management.
* <p/>
* Please note this value can be overridden by the {@link #setHttpProcessor(
* ch.boye.httpclientandroidlib.protocol.HttpProcessor)} method.
*/
public final HttpClientBuilder disableCookieManagement() {
this.cookieManagementDisabled = true;
return this;
}
/**
* Disables automatic content decompression.
* <p/>
* Please note this value can be overridden by the {@link #setHttpProcessor(
* ch.boye.httpclientandroidlib.protocol.HttpProcessor)} method.
*/
public final HttpClientBuilder disableContentCompression() {
contentCompressionDisabled = true;
return this;
}
/**
* Disables authentication scheme caching.
* <p/>
* Please note this value can be overridden by the {@link #setHttpProcessor(
* ch.boye.httpclientandroidlib.protocol.HttpProcessor)} method.
*/
public final HttpClientBuilder disableAuthCaching() {
this.authCachingDisabled = true;
return this;
}
/**
* Assigns {@link HttpProcessor} instance.
*/
public final HttpClientBuilder setHttpProcessor(final HttpProcessor httpprocessor) {
this.httpprocessor = httpprocessor;
return this;
}
/**
* Assigns {@link HttpRequestRetryHandler} instance.
* <p/>
* Please note this value can be overridden by the {@link #disableAutomaticRetries()}
* method.
*/
public final HttpClientBuilder setRetryHandler(final HttpRequestRetryHandler retryHandler) {
this.retryHandler = retryHandler;
return this;
}
/**
* Disables automatic request recovery and re-execution.
*/
public final HttpClientBuilder disableAutomaticRetries() {
automaticRetriesDisabled = true;
return this;
}
/**
* Assigns default proxy value.
* <p/>
* Please note this value can be overridden by the {@link #setRoutePlanner(
* ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner)} method.
*/
public final HttpClientBuilder setProxy(final HttpHost proxy) {
this.proxy = proxy;
return this;
}
/**
* Assigns {@link HttpRoutePlanner} instance.
*/
public final HttpClientBuilder setRoutePlanner(final HttpRoutePlanner routePlanner) {
this.routePlanner = routePlanner;
return this;
}
/**
* Assigns {@link RedirectStrategy} instance.
* <p/>
* Please note this value can be overridden by the {@link #disableRedirectHandling()}
* method.
` */
public final HttpClientBuilder setRedirectStrategy(final RedirectStrategy redirectStrategy) {
this.redirectStrategy = redirectStrategy;
return this;
}
/**
* Disables automatic redirect handling.
*/
public final HttpClientBuilder disableRedirectHandling() {
redirectHandlingDisabled = true;
return this;
}
/**
* Assigns {@link ConnectionBackoffStrategy} instance.
*/
public final HttpClientBuilder setConnectionBackoffStrategy(
final ConnectionBackoffStrategy connectionBackoffStrategy) {
this.connectionBackoffStrategy = connectionBackoffStrategy;
return this;
}
/**
* Assigns {@link BackoffManager} instance.
*/
public final HttpClientBuilder setBackoffManager(final BackoffManager backoffManager) {
this.backoffManager = backoffManager;
return this;
}
/**
* Assigns {@link ServiceUnavailableRetryStrategy} instance.
*/
public final HttpClientBuilder setServiceUnavailableRetryStrategy(
final ServiceUnavailableRetryStrategy serviceUnavailStrategy) {
this.serviceUnavailStrategy = serviceUnavailStrategy;
return this;
}
/**
* Assigns default {@link CookieStore} instance which will be used for
* request execution if not explicitly set in the client execution context.
*/
public final HttpClientBuilder setDefaultCookieStore(final CookieStore cookieStore) {
this.cookieStore = cookieStore;
return this;
}
/**
* Assigns default {@link CredentialsProvider} instance which will be used
* for request execution if not explicitly set in the client execution
* context.
*/
public final HttpClientBuilder setDefaultCredentialsProvider(
final CredentialsProvider credentialsProvider) {
this.credentialsProvider = credentialsProvider;
return this;
}
/**
* Assigns default {@link ch.boye.httpclientandroidlib.auth.AuthScheme} registry which will
* be used for request execution if not explicitly set in the client execution
* context.
*/
public final HttpClientBuilder setDefaultAuthSchemeRegistry(
final Lookup<AuthSchemeProvider> authSchemeRegistry) {
this.authSchemeRegistry = authSchemeRegistry;
return this;
}
/**
* Assigns default {@link ch.boye.httpclientandroidlib.cookie.CookieSpec} registry which will
* be used for request execution if not explicitly set in the client execution
* context.
*/
public final HttpClientBuilder setDefaultCookieSpecRegistry(
final Lookup<CookieSpecProvider> cookieSpecRegistry) {
this.cookieSpecRegistry = cookieSpecRegistry;
return this;
}
/**
* Assigns default {@link RequestConfig} instance which will be used
* for request execution if not explicitly set in the client execution
* context.
*/
public final HttpClientBuilder setDefaultRequestConfig(final RequestConfig config) {
this.defaultRequestConfig = config;
return this;
}
/**
* Use system properties when creating and configuring default
* implementations.
*/
public final HttpClientBuilder useSystemProperties() {
systemProperties = true;
return this;
}
/**
* For internal use.
*/
protected ClientExecChain decorateMainExec(final ClientExecChain mainExec) {
return mainExec;
}
/**
* For internal use.
*/
protected ClientExecChain decorateProtocolExec(final ClientExecChain protocolExec) {
return protocolExec;
}
/**
* For internal use.
*/
protected void addCloseable(final Closeable closeable) {
if (closeable == null) {
return;
}
if (closeables == null) {
closeables = new ArrayList<Closeable>();
}
closeables.add(closeable);
}
private static String[] split(final String s) {
if (TextUtils.isBlank(s)) {
return null;
}
return s.split(" *, *");
}
public CloseableHttpClient build() {
// Create main request executor
HttpRequestExecutor requestExec = this.requestExec;
if (requestExec == null) {
requestExec = new HttpRequestExecutor();
}
HttpClientConnectionManager connManager = this.connManager;
if (connManager == null) {
LayeredConnectionSocketFactory sslSocketFactory = this.sslSocketFactory;
if (sslSocketFactory == null) {
final String[] supportedProtocols = systemProperties ? split(
System.getProperty("https.protocols")) : null;
final String[] supportedCipherSuites = systemProperties ? split(
System.getProperty("https.cipherSuites")) : null;
X509HostnameVerifier hostnameVerifier = this.hostnameVerifier;
if (hostnameVerifier == null) {
hostnameVerifier = SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER;
}
if (sslcontext != null) {
sslSocketFactory = new SSLConnectionSocketFactory(
sslcontext, supportedProtocols, supportedCipherSuites, hostnameVerifier);
} else {
if (systemProperties) {
sslSocketFactory = new SSLConnectionSocketFactory(
(SSLSocketFactory) SSLSocketFactory.getDefault(),
supportedProtocols, supportedCipherSuites, hostnameVerifier);
} else {
sslSocketFactory = new SSLConnectionSocketFactory(
SSLContexts.createDefault(),
hostnameVerifier);
}
}
}
@SuppressWarnings("resource")
final PoolingHttpClientConnectionManager poolingmgr = new PoolingHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslSocketFactory)
.build());
if (defaultSocketConfig != null) {
poolingmgr.setDefaultSocketConfig(defaultSocketConfig);
}
if (defaultConnectionConfig != null) {
poolingmgr.setDefaultConnectionConfig(defaultConnectionConfig);
}
if (systemProperties) {
String s = System.getProperty("http.keepAlive", "true");
if ("true".equalsIgnoreCase(s)) {
s = System.getProperty("http.maxConnections", "5");
final int max = Integer.parseInt(s);
poolingmgr.setDefaultMaxPerRoute(max);
poolingmgr.setMaxTotal(2 * max);
}
}
if (maxConnTotal > 0) {
poolingmgr.setMaxTotal(maxConnTotal);
}
if (maxConnPerRoute > 0) {
poolingmgr.setDefaultMaxPerRoute(maxConnPerRoute);
}
connManager = poolingmgr;
}
ConnectionReuseStrategy reuseStrategy = this.reuseStrategy;
if (reuseStrategy == null) {
if (systemProperties) {
final String s = System.getProperty("http.keepAlive", "true");
if ("true".equalsIgnoreCase(s)) {
reuseStrategy = DefaultConnectionReuseStrategy.INSTANCE;
} else {
reuseStrategy = NoConnectionReuseStrategy.INSTANCE;
}
} else {
reuseStrategy = DefaultConnectionReuseStrategy.INSTANCE;
}
}
ConnectionKeepAliveStrategy keepAliveStrategy = this.keepAliveStrategy;
if (keepAliveStrategy == null) {
keepAliveStrategy = DefaultConnectionKeepAliveStrategy.INSTANCE;
}
AuthenticationStrategy targetAuthStrategy = this.targetAuthStrategy;
if (targetAuthStrategy == null) {
targetAuthStrategy = TargetAuthenticationStrategy.INSTANCE;
}
AuthenticationStrategy proxyAuthStrategy = this.proxyAuthStrategy;
if (proxyAuthStrategy == null) {
proxyAuthStrategy = ProxyAuthenticationStrategy.INSTANCE;
}
UserTokenHandler userTokenHandler = this.userTokenHandler;
if (userTokenHandler == null) {
if (!connectionStateDisabled) {
userTokenHandler = DefaultUserTokenHandler.INSTANCE;
} else {
userTokenHandler = NoopUserTokenHandler.INSTANCE;
}
}
ClientExecChain execChain = new MainClientExec(
requestExec,
connManager,
reuseStrategy,
keepAliveStrategy,
targetAuthStrategy,
proxyAuthStrategy,
userTokenHandler);
execChain = decorateMainExec(execChain);
HttpProcessor httpprocessor = this.httpprocessor;
if (httpprocessor == null) {
String userAgent = this.userAgent;
if (userAgent == null) {
if (systemProperties) {
userAgent = System.getProperty("http.agent");
}
if (userAgent == null) {
userAgent = DEFAULT_USER_AGENT;
}
}
final HttpProcessorBuilder b = HttpProcessorBuilder.create();
if (requestFirst != null) {
for (final HttpRequestInterceptor i: requestFirst) {
b.addFirst(i);
}
}
if (responseFirst != null) {
for (final HttpResponseInterceptor i: responseFirst) {
b.addFirst(i);
}
}
b.addAll(
new RequestDefaultHeaders(defaultHeaders),
new RequestContent(),
new RequestTargetHost(),
new RequestClientConnControl(),
new RequestUserAgent(userAgent),
new RequestExpectContinue());
if (!cookieManagementDisabled) {
b.add(new RequestAddCookies());
}
if (!contentCompressionDisabled) {
b.add(new RequestAcceptEncoding());
}
if (!authCachingDisabled) {
b.add(new RequestAuthCache());
}
if (!cookieManagementDisabled) {
b.add(new ResponseProcessCookies());
}
if (!contentCompressionDisabled) {
b.add(new ResponseContentEncoding());
}
if (requestLast != null) {
for (final HttpRequestInterceptor i: requestLast) {
b.addLast(i);
}
}
if (responseLast != null) {
for (final HttpResponseInterceptor i: responseLast) {
b.addLast(i);
}
}
httpprocessor = b.build();
}
execChain = new ProtocolExec(execChain, httpprocessor);
execChain = decorateProtocolExec(execChain);
// Add request retry executor, if not disabled
if (!automaticRetriesDisabled) {
HttpRequestRetryHandler retryHandler = this.retryHandler;
if (retryHandler == null) {
retryHandler = DefaultHttpRequestRetryHandler.INSTANCE;
}
execChain = new RetryExec(execChain, retryHandler);
}
HttpRoutePlanner routePlanner = this.routePlanner;
if (routePlanner == null) {
SchemePortResolver schemePortResolver = this.schemePortResolver;
if (schemePortResolver == null) {
schemePortResolver = DefaultSchemePortResolver.INSTANCE;
}
if (proxy != null) {
routePlanner = new DefaultProxyRoutePlanner(proxy, schemePortResolver);
} else if (systemProperties) {
routePlanner = new SystemDefaultRoutePlanner(
schemePortResolver, ProxySelector.getDefault());
} else {
routePlanner = new DefaultRoutePlanner(schemePortResolver);
}
}
// Add redirect executor, if not disabled
if (!redirectHandlingDisabled) {
RedirectStrategy redirectStrategy = this.redirectStrategy;
if (redirectStrategy == null) {
redirectStrategy = DefaultRedirectStrategy.INSTANCE;
}
execChain = new RedirectExec(execChain, routePlanner, redirectStrategy);
}
// Optionally, add service unavailable retry executor
final ServiceUnavailableRetryStrategy serviceUnavailStrategy = this.serviceUnavailStrategy;
if (serviceUnavailStrategy != null) {
execChain = new ServiceUnavailableRetryExec(execChain, serviceUnavailStrategy);
}
// Optionally, add connection back-off executor
final BackoffManager backoffManager = this.backoffManager;
final ConnectionBackoffStrategy connectionBackoffStrategy = this.connectionBackoffStrategy;
if (backoffManager != null && connectionBackoffStrategy != null) {
execChain = new BackoffStrategyExec(execChain, connectionBackoffStrategy, backoffManager);
}
Lookup<AuthSchemeProvider> authSchemeRegistry = this.authSchemeRegistry;
if (authSchemeRegistry == null) {
authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
.register(AuthSchemes.BASIC, new BasicSchemeFactory())
.register(AuthSchemes.DIGEST, new DigestSchemeFactory())
.register(AuthSchemes.NTLM, new NTLMSchemeFactory())
/* SPNegoSchemeFactory removed by HttpClient for Android script. */
/* KerberosSchemeFactory removed by HttpClient for Android script. */
.build();
}
Lookup<CookieSpecProvider> cookieSpecRegistry = this.cookieSpecRegistry;
if (cookieSpecRegistry == null) {
cookieSpecRegistry = RegistryBuilder.<CookieSpecProvider>create()
.register(CookieSpecs.BEST_MATCH, new BestMatchSpecFactory())
.register(CookieSpecs.STANDARD, new RFC2965SpecFactory())
.register(CookieSpecs.BROWSER_COMPATIBILITY, new BrowserCompatSpecFactory())
.register(CookieSpecs.NETSCAPE, new NetscapeDraftSpecFactory())
.register(CookieSpecs.IGNORE_COOKIES, new IgnoreSpecFactory())
.register("rfc2109", new RFC2109SpecFactory())
.register("rfc2965", new RFC2965SpecFactory())
.build();
}
CookieStore defaultCookieStore = this.cookieStore;
if (defaultCookieStore == null) {
defaultCookieStore = new BasicCookieStore();
}
CredentialsProvider defaultCredentialsProvider = this.credentialsProvider;
if (defaultCredentialsProvider == null) {
if (systemProperties) {
defaultCredentialsProvider = new SystemDefaultCredentialsProvider();
} else {
defaultCredentialsProvider = new BasicCredentialsProvider();
}
}
return new InternalHttpClient(
execChain,
connManager,
routePlanner,
cookieSpecRegistry,
authSchemeRegistry,
defaultCookieStore,
defaultCredentialsProvider,
defaultRequestConfig != null ? defaultRequestConfig : RequestConfig.DEFAULT,
closeables != null ? new ArrayList<Closeable>(closeables) : null);
}
}

View file

@ -1,85 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager;
import ch.boye.httpclientandroidlib.impl.conn.PoolingHttpClientConnectionManager;
/**
* Factory methods for {@link CloseableHttpClient} instances.
* @since 4.3
*/
@Immutable
public class HttpClients {
private HttpClients() {
super();
}
/**
* Creates builder object for construction of custom
* {@link CloseableHttpClient} instances.
*/
public static HttpClientBuilder custom() {
return HttpClientBuilder.create();
}
/**
* Creates {@link CloseableHttpClient} instance with default
* configuration.
*/
public static CloseableHttpClient createDefault() {
return HttpClientBuilder.create().build();
}
/**
* Creates {@link CloseableHttpClient} instance with default
* configuration based on ssytem properties.
*/
public static CloseableHttpClient createSystem() {
return HttpClientBuilder.create().useSystemProperties().build();
}
/**
* Creates {@link CloseableHttpClient} instance that implements
* the most basic HTTP protocol support.
*/
public static CloseableHttpClient createMinimal() {
return new MinimalHttpClient(new PoolingHttpClientConnectionManager());
}
/**
* Creates {@link CloseableHttpClient} instance that implements
* the most basic HTTP protocol support.
*/
public static CloseableHttpClient createMinimal(final HttpClientConnectionManager connManager) {
return new MinimalHttpClient(connManager);
}
}

View file

@ -1,118 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.util.concurrent.FutureTask;
import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
/**
* FutureTask implementation that wraps a HttpAsyncClientCallable and exposes various task
* specific metrics.
*
* @param <V>
*/
public class HttpRequestFutureTask<V> extends FutureTask<V> {
private final HttpUriRequest request;
private final HttpRequestTaskCallable<V> callable;
public HttpRequestFutureTask(
final HttpUriRequest request,
final HttpRequestTaskCallable<V> httpCallable) {
super(httpCallable);
this.request = request;
this.callable = httpCallable;
}
/*
* (non-Javadoc)
* @see java.util.concurrent.FutureTask#cancel(boolean)
*/
@Override
public boolean cancel(final boolean mayInterruptIfRunning) {
callable.cancel();
if (mayInterruptIfRunning) {
request.abort();
}
return super.cancel(mayInterruptIfRunning);
}
/**
* @return the time in millis the task was scheduled.
*/
public long scheduledTime() {
return callable.getScheduled();
}
/**
* @return the time in millis the task was started.
*/
public long startedTime() {
return callable.getStarted();
}
/**
* @return the time in millis the task was finished/cancelled.
*/
public long endedTime() {
if (isDone()) {
return callable.getEnded();
} else {
throw new IllegalStateException("Task is not done yet");
}
}
/**
* @return the time in millis it took to make the request (excluding the time it was
* scheduled to be executed).
*/
public long requestDuration() {
if (isDone()) {
return endedTime() - startedTime();
} else {
throw new IllegalStateException("Task is not done yet");
}
}
/**
* @return the time in millis it took to execute the task from the moment it was scheduled.
*/
public long taskDuration() {
if (isDone()) {
return endedTime() - scheduledTime();
} else {
throw new IllegalStateException("Task is not done yet");
}
}
@Override
public String toString() {
return request.getRequestLine().getUri();
}
}

View file

@ -1,119 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
import ch.boye.httpclientandroidlib.client.HttpClient;
import ch.boye.httpclientandroidlib.client.ResponseHandler;
import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
import ch.boye.httpclientandroidlib.concurrent.FutureCallback;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
class HttpRequestTaskCallable<V> implements Callable<V> {
private final HttpUriRequest request;
private final HttpClient httpclient;
private final AtomicBoolean cancelled = new AtomicBoolean(false);
private final long scheduled = System.currentTimeMillis();
private long started = -1;
private long ended = -1;
private final HttpContext context;
private final ResponseHandler<V> responseHandler;
private final FutureCallback<V> callback;
private final FutureRequestExecutionMetrics metrics;
HttpRequestTaskCallable(
final HttpClient httpClient,
final HttpUriRequest request,
final HttpContext context,
final ResponseHandler<V> responseHandler,
final FutureCallback<V> callback,
final FutureRequestExecutionMetrics metrics) {
this.httpclient = httpClient;
this.responseHandler = responseHandler;
this.request = request;
this.context = context;
this.callback = callback;
this.metrics = metrics;
}
public long getScheduled() {
return scheduled;
}
public long getStarted() {
return started;
}
public long getEnded() {
return ended;
}
public V call() throws Exception {
if (!cancelled.get()) {
try {
metrics.getActiveConnections().incrementAndGet();
started = System.currentTimeMillis();
try {
metrics.getScheduledConnections().decrementAndGet();
final V result = httpclient.execute(request, responseHandler, context);
ended = System.currentTimeMillis();
metrics.getSuccessfulConnections().increment(started);
if (callback != null) {
callback.completed(result);
}
return result;
} catch (final Exception e) {
metrics.getFailedConnections().increment(started);
ended = System.currentTimeMillis();
if (callback != null) {
callback.failed(e);
}
throw e;
}
} finally {
metrics.getRequests().increment(started);
metrics.getTasks().increment(started);
metrics.getActiveConnections().decrementAndGet();
}
} else {
throw new IllegalStateException("call has been cancelled for request " + request.getURI());
}
}
public void cancel() {
cancelled.set(true);
if (callback != null) {
callback.cancelled();
}
}
}

View file

@ -1,242 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.io.Closeable;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
/* LogFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.HttpException;
import ch.boye.httpclientandroidlib.HttpHost;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
import ch.boye.httpclientandroidlib.auth.AuthSchemeProvider;
import ch.boye.httpclientandroidlib.auth.AuthState;
import ch.boye.httpclientandroidlib.client.ClientProtocolException;
import ch.boye.httpclientandroidlib.client.CookieStore;
import ch.boye.httpclientandroidlib.client.CredentialsProvider;
import ch.boye.httpclientandroidlib.client.config.RequestConfig;
import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
import ch.boye.httpclientandroidlib.client.methods.Configurable;
import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware;
import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper;
import ch.boye.httpclientandroidlib.client.params.ClientPNames;
import ch.boye.httpclientandroidlib.client.params.HttpClientParamConfig;
import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
import ch.boye.httpclientandroidlib.config.Lookup;
import ch.boye.httpclientandroidlib.conn.ClientConnectionManager;
import ch.boye.httpclientandroidlib.conn.ClientConnectionRequest;
import ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager;
import ch.boye.httpclientandroidlib.conn.ManagedClientConnection;
import ch.boye.httpclientandroidlib.conn.routing.HttpRoute;
import ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner;
import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry;
import ch.boye.httpclientandroidlib.cookie.CookieSpecProvider;
import ch.boye.httpclientandroidlib.impl.execchain.ClientExecChain;
import ch.boye.httpclientandroidlib.params.HttpParams;
import ch.boye.httpclientandroidlib.params.HttpParamsNames;
import ch.boye.httpclientandroidlib.protocol.BasicHttpContext;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Internal class.
*
* @since 4.3
*/
@ThreadSafe
@SuppressWarnings("deprecation")
class InternalHttpClient extends CloseableHttpClient {
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
private final ClientExecChain execChain;
private final HttpClientConnectionManager connManager;
private final HttpRoutePlanner routePlanner;
private final Lookup<CookieSpecProvider> cookieSpecRegistry;
private final Lookup<AuthSchemeProvider> authSchemeRegistry;
private final CookieStore cookieStore;
private final CredentialsProvider credentialsProvider;
private final RequestConfig defaultConfig;
private final List<Closeable> closeables;
public InternalHttpClient(
final ClientExecChain execChain,
final HttpClientConnectionManager connManager,
final HttpRoutePlanner routePlanner,
final Lookup<CookieSpecProvider> cookieSpecRegistry,
final Lookup<AuthSchemeProvider> authSchemeRegistry,
final CookieStore cookieStore,
final CredentialsProvider credentialsProvider,
final RequestConfig defaultConfig,
final List<Closeable> closeables) {
super();
Args.notNull(execChain, "HTTP client exec chain");
Args.notNull(connManager, "HTTP connection manager");
Args.notNull(routePlanner, "HTTP route planner");
this.execChain = execChain;
this.connManager = connManager;
this.routePlanner = routePlanner;
this.cookieSpecRegistry = cookieSpecRegistry;
this.authSchemeRegistry = authSchemeRegistry;
this.cookieStore = cookieStore;
this.credentialsProvider = credentialsProvider;
this.defaultConfig = defaultConfig;
this.closeables = closeables;
}
private HttpRoute determineRoute(
final HttpHost target,
final HttpRequest request,
final HttpContext context) throws HttpException {
HttpHost host = target;
if (host == null) {
host = (HttpHost) request.getParams().getParameter(ClientPNames.DEFAULT_HOST);
}
return this.routePlanner.determineRoute(host, request, context);
}
private void setupContext(final HttpClientContext context) {
if (context.getAttribute(HttpClientContext.TARGET_AUTH_STATE) == null) {
context.setAttribute(HttpClientContext.TARGET_AUTH_STATE, new AuthState());
}
if (context.getAttribute(HttpClientContext.PROXY_AUTH_STATE) == null) {
context.setAttribute(HttpClientContext.PROXY_AUTH_STATE, new AuthState());
}
if (context.getAttribute(HttpClientContext.AUTHSCHEME_REGISTRY) == null) {
context.setAttribute(HttpClientContext.AUTHSCHEME_REGISTRY, this.authSchemeRegistry);
}
if (context.getAttribute(HttpClientContext.COOKIESPEC_REGISTRY) == null) {
context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry);
}
if (context.getAttribute(HttpClientContext.COOKIE_STORE) == null) {
context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore);
}
if (context.getAttribute(HttpClientContext.CREDS_PROVIDER) == null) {
context.setAttribute(HttpClientContext.CREDS_PROVIDER, this.credentialsProvider);
}
if (context.getAttribute(HttpClientContext.REQUEST_CONFIG) == null) {
context.setAttribute(HttpClientContext.REQUEST_CONFIG, this.defaultConfig);
}
}
@Override
protected CloseableHttpResponse doExecute(
final HttpHost target,
final HttpRequest request,
final HttpContext context) throws IOException, ClientProtocolException {
Args.notNull(request, "HTTP request");
HttpExecutionAware execAware = null;
if (request instanceof HttpExecutionAware) {
execAware = (HttpExecutionAware) request;
}
try {
final HttpRequestWrapper wrapper = HttpRequestWrapper.wrap(request);
final HttpClientContext localcontext = HttpClientContext.adapt(
context != null ? context : new BasicHttpContext());
RequestConfig config = null;
if (request instanceof Configurable) {
config = ((Configurable) request).getConfig();
}
if (config == null) {
final HttpParams params = request.getParams();
if (params instanceof HttpParamsNames) {
if (!((HttpParamsNames) params).getNames().isEmpty()) {
config = HttpClientParamConfig.getRequestConfig(params);
}
} else {
config = HttpClientParamConfig.getRequestConfig(params);
}
}
if (config != null) {
localcontext.setRequestConfig(config);
}
setupContext(localcontext);
final HttpRoute route = determineRoute(target, wrapper, localcontext);
return this.execChain.execute(route, wrapper, localcontext, execAware);
} catch (final HttpException httpException) {
throw new ClientProtocolException(httpException);
}
}
public void close() {
this.connManager.shutdown();
if (this.closeables != null) {
for (final Closeable closeable: this.closeables) {
try {
closeable.close();
} catch (final IOException ex) {
this.log.error(ex.getMessage(), ex);
}
}
}
}
public HttpParams getParams() {
throw new UnsupportedOperationException();
}
public ClientConnectionManager getConnectionManager() {
return new ClientConnectionManager() {
public void shutdown() {
connManager.shutdown();
}
public ClientConnectionRequest requestConnection(
final HttpRoute route, final Object state) {
throw new UnsupportedOperationException();
}
public void releaseConnection(
final ManagedClientConnection conn,
final long validDuration, final TimeUnit timeUnit) {
throw new UnsupportedOperationException();
}
public SchemeRegistry getSchemeRegistry() {
throw new UnsupportedOperationException();
}
public void closeIdleConnections(final long idletime, final TimeUnit tunit) {
connManager.closeIdleConnections(idletime, tunit);
}
public void closeExpiredConnections() {
connManager.closeExpiredConnections();
}
};
}
}

View file

@ -1,65 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.methods.HttpGet;
import ch.boye.httpclientandroidlib.client.methods.HttpHead;
import ch.boye.httpclientandroidlib.client.methods.HttpPost;
/**
* Lax {@link ch.boye.httpclientandroidlib.client.RedirectStrategy} implementation
* that automatically redirects all HEAD, GET and POST requests.
* This strategy relaxes restrictions on automatic redirection of
* POST methods imposed by the HTTP specification.
*
* @since 4.2
*/
@Immutable
public class LaxRedirectStrategy extends DefaultRedirectStrategy {
/**
* Redirectable methods.
*/
private static final String[] REDIRECT_METHODS = new String[] {
HttpGet.METHOD_NAME,
HttpPost.METHOD_NAME,
HttpHead.METHOD_NAME
};
@Override
protected boolean isRedirectable(final String method) {
for (final String m: REDIRECT_METHODS) {
if (m.equalsIgnoreCase(method)) {
return true;
}
}
return false;
}
}

View file

@ -1,156 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import ch.boye.httpclientandroidlib.HttpException;
import ch.boye.httpclientandroidlib.HttpHost;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
import ch.boye.httpclientandroidlib.client.ClientProtocolException;
import ch.boye.httpclientandroidlib.client.config.RequestConfig;
import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
import ch.boye.httpclientandroidlib.client.methods.Configurable;
import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware;
import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper;
import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
import ch.boye.httpclientandroidlib.conn.ClientConnectionManager;
import ch.boye.httpclientandroidlib.conn.ClientConnectionRequest;
import ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager;
import ch.boye.httpclientandroidlib.conn.ManagedClientConnection;
import ch.boye.httpclientandroidlib.conn.routing.HttpRoute;
import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry;
import ch.boye.httpclientandroidlib.impl.DefaultConnectionReuseStrategy;
import ch.boye.httpclientandroidlib.impl.execchain.MinimalClientExec;
import ch.boye.httpclientandroidlib.params.BasicHttpParams;
import ch.boye.httpclientandroidlib.params.HttpParams;
import ch.boye.httpclientandroidlib.protocol.BasicHttpContext;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
import ch.boye.httpclientandroidlib.protocol.HttpRequestExecutor;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Internal class.
*
* @since 4.3
*/
@ThreadSafe
@SuppressWarnings("deprecation")
class MinimalHttpClient extends CloseableHttpClient {
private final HttpClientConnectionManager connManager;
private final MinimalClientExec requestExecutor;
private final HttpParams params;
public MinimalHttpClient(
final HttpClientConnectionManager connManager) {
super();
this.connManager = Args.notNull(connManager, "HTTP connection manager");
this.requestExecutor = new MinimalClientExec(
new HttpRequestExecutor(),
connManager,
DefaultConnectionReuseStrategy.INSTANCE,
DefaultConnectionKeepAliveStrategy.INSTANCE);
this.params = new BasicHttpParams();
}
@Override
protected CloseableHttpResponse doExecute(
final HttpHost target,
final HttpRequest request,
final HttpContext context) throws IOException, ClientProtocolException {
Args.notNull(target, "Target host");
Args.notNull(request, "HTTP request");
HttpExecutionAware execAware = null;
if (request instanceof HttpExecutionAware) {
execAware = (HttpExecutionAware) request;
}
try {
final HttpRequestWrapper wrapper = HttpRequestWrapper.wrap(request);
final HttpClientContext localcontext = HttpClientContext.adapt(
context != null ? context : new BasicHttpContext());
final HttpRoute route = new HttpRoute(target);
RequestConfig config = null;
if (request instanceof Configurable) {
config = ((Configurable) request).getConfig();
}
if (config != null) {
localcontext.setRequestConfig(config);
}
return this.requestExecutor.execute(route, wrapper, localcontext, execAware);
} catch (final HttpException httpException) {
throw new ClientProtocolException(httpException);
}
}
public HttpParams getParams() {
return this.params;
}
public void close() {
this.connManager.shutdown();
}
public ClientConnectionManager getConnectionManager() {
return new ClientConnectionManager() {
public void shutdown() {
connManager.shutdown();
}
public ClientConnectionRequest requestConnection(
final HttpRoute route, final Object state) {
throw new UnsupportedOperationException();
}
public void releaseConnection(
final ManagedClientConnection conn,
final long validDuration, final TimeUnit timeUnit) {
throw new UnsupportedOperationException();
}
public SchemeRegistry getSchemeRegistry() {
throw new UnsupportedOperationException();
}
public void closeIdleConnections(final long idletime, final TimeUnit tunit) {
connManager.closeIdleConnections(idletime, tunit);
}
public void closeExpiredConnections() {
connManager.closeExpiredConnections();
}
};
}
}

View file

@ -1,47 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.UserTokenHandler;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
/**
* Noop implementation of {@link UserTokenHandler} that always returns <code>null</code>.
*
* @since 4.3
*/
@Immutable
public class NoopUserTokenHandler implements UserTokenHandler {
public static final NoopUserTokenHandler INSTANCE = new NoopUserTokenHandler();
public Object getUserToken(final HttpContext context) {
return null;
}
}

View file

@ -1,47 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.client.ConnectionBackoffStrategy;
/**
* This is a {@link ConnectionBackoffStrategy} that never backs off,
* for compatibility with existing behavior.
*
* @since 4.2
*/
public class NullBackoffStrategy implements ConnectionBackoffStrategy {
public boolean shouldBackoff(final Throwable t) {
return false;
}
public boolean shouldBackoff(final HttpResponse resp) {
return false;
}
}

View file

@ -1,57 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.util.Collection;
import ch.boye.httpclientandroidlib.HttpStatus;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.auth.AUTH;
import ch.boye.httpclientandroidlib.client.config.RequestConfig;
/**
* Default {@link ch.boye.httpclientandroidlib.client.AuthenticationStrategy} implementation
* for proxy host authentication.
*
* @since 4.2
*/
@Immutable
public class ProxyAuthenticationStrategy extends AuthenticationStrategyImpl {
public static final ProxyAuthenticationStrategy INSTANCE = new ProxyAuthenticationStrategy();
public ProxyAuthenticationStrategy() {
super(HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED, AUTH.PROXY_AUTH);
}
@Override
Collection<String> getPreferredAuthSchemes(final RequestConfig config) {
return config.getProxyPreferredAuthSchemes();
}
}

View file

@ -1,254 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.io.IOException;
import java.net.Socket;
import ch.boye.httpclientandroidlib.ConnectionReuseStrategy;
import ch.boye.httpclientandroidlib.HttpEntity;
import ch.boye.httpclientandroidlib.HttpException;
import ch.boye.httpclientandroidlib.HttpHost;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.HttpVersion;
import ch.boye.httpclientandroidlib.auth.AUTH;
import ch.boye.httpclientandroidlib.auth.AuthSchemeRegistry;
import ch.boye.httpclientandroidlib.auth.AuthScope;
import ch.boye.httpclientandroidlib.auth.AuthState;
import ch.boye.httpclientandroidlib.auth.Credentials;
import ch.boye.httpclientandroidlib.client.config.AuthSchemes;
import ch.boye.httpclientandroidlib.client.config.RequestConfig;
import ch.boye.httpclientandroidlib.client.params.HttpClientParamConfig;
import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
import ch.boye.httpclientandroidlib.client.protocol.RequestClientConnControl;
import ch.boye.httpclientandroidlib.config.ConnectionConfig;
import ch.boye.httpclientandroidlib.conn.HttpConnectionFactory;
import ch.boye.httpclientandroidlib.conn.ManagedHttpClientConnection;
import ch.boye.httpclientandroidlib.conn.routing.HttpRoute;
import ch.boye.httpclientandroidlib.conn.routing.RouteInfo.LayerType;
import ch.boye.httpclientandroidlib.conn.routing.RouteInfo.TunnelType;
import ch.boye.httpclientandroidlib.entity.BufferedHttpEntity;
import ch.boye.httpclientandroidlib.impl.DefaultConnectionReuseStrategy;
import ch.boye.httpclientandroidlib.impl.auth.BasicSchemeFactory;
import ch.boye.httpclientandroidlib.impl.auth.DigestSchemeFactory;
import ch.boye.httpclientandroidlib.impl.auth.HttpAuthenticator;
/* KerberosSchemeFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.impl.auth.NTLMSchemeFactory;
/* SPNegoSchemeFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.impl.conn.ManagedHttpClientConnectionFactory;
import ch.boye.httpclientandroidlib.impl.execchain.TunnelRefusedException;
import ch.boye.httpclientandroidlib.message.BasicHttpRequest;
import ch.boye.httpclientandroidlib.params.BasicHttpParams;
import ch.boye.httpclientandroidlib.params.HttpParamConfig;
import ch.boye.httpclientandroidlib.params.HttpParams;
import ch.boye.httpclientandroidlib.protocol.BasicHttpContext;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
import ch.boye.httpclientandroidlib.protocol.HttpCoreContext;
import ch.boye.httpclientandroidlib.protocol.HttpProcessor;
import ch.boye.httpclientandroidlib.protocol.HttpRequestExecutor;
import ch.boye.httpclientandroidlib.protocol.ImmutableHttpProcessor;
import ch.boye.httpclientandroidlib.protocol.RequestTargetHost;
import ch.boye.httpclientandroidlib.protocol.RequestUserAgent;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.EntityUtils;
/**
* ProxyClient can be used to establish a tunnel via an HTTP proxy.
*/
@SuppressWarnings("deprecation")
public class ProxyClient {
private final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory;
private final ConnectionConfig connectionConfig;
private final RequestConfig requestConfig;
private final HttpProcessor httpProcessor;
private final HttpRequestExecutor requestExec;
private final ProxyAuthenticationStrategy proxyAuthStrategy;
private final HttpAuthenticator authenticator;
private final AuthState proxyAuthState;
private final AuthSchemeRegistry authSchemeRegistry;
private final ConnectionReuseStrategy reuseStrategy;
/**
* @since 4.3
*/
public ProxyClient(
final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory,
final ConnectionConfig connectionConfig,
final RequestConfig requestConfig) {
super();
this.connFactory = connFactory != null ? connFactory : ManagedHttpClientConnectionFactory.INSTANCE;
this.connectionConfig = connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT;
this.requestConfig = requestConfig != null ? requestConfig : RequestConfig.DEFAULT;
this.httpProcessor = new ImmutableHttpProcessor(
new RequestTargetHost(), new RequestClientConnControl(), new RequestUserAgent());
this.requestExec = new HttpRequestExecutor();
this.proxyAuthStrategy = new ProxyAuthenticationStrategy();
this.authenticator = new HttpAuthenticator();
this.proxyAuthState = new AuthState();
this.authSchemeRegistry = new AuthSchemeRegistry();
this.authSchemeRegistry.register(AuthSchemes.BASIC, new BasicSchemeFactory());
this.authSchemeRegistry.register(AuthSchemes.DIGEST, new DigestSchemeFactory());
this.authSchemeRegistry.register(AuthSchemes.NTLM, new NTLMSchemeFactory());
/* SPNegoSchemeFactory removed by HttpClient for Android script. */
/* KerberosSchemeFactory removed by HttpClient for Android script. */
this.reuseStrategy = new DefaultConnectionReuseStrategy();
}
/**
* @deprecated (4.3) use {@link ProxyClient#ProxyClient(HttpConnectionFactory, ConnectionConfig, RequestConfig)}
*/
@Deprecated
public ProxyClient(final HttpParams params) {
this(null,
HttpParamConfig.getConnectionConfig(params),
HttpClientParamConfig.getRequestConfig(params));
}
/**
* @since 4.3
*/
public ProxyClient(final RequestConfig requestConfig) {
this(null, null, requestConfig);
}
public ProxyClient() {
this(null, null, null);
}
/**
* @deprecated (4.3) do not use.
*/
@Deprecated
public HttpParams getParams() {
return new BasicHttpParams();
}
/**
* @deprecated (4.3) do not use.
*/
@Deprecated
public AuthSchemeRegistry getAuthSchemeRegistry() {
return this.authSchemeRegistry;
}
public Socket tunnel(
final HttpHost proxy,
final HttpHost target,
final Credentials credentials) throws IOException, HttpException {
Args.notNull(proxy, "Proxy host");
Args.notNull(target, "Target host");
Args.notNull(credentials, "Credentials");
HttpHost host = target;
if (host.getPort() <= 0) {
host = new HttpHost(host.getHostName(), 80, host.getSchemeName());
}
final HttpRoute route = new HttpRoute(
host,
this.requestConfig.getLocalAddress(),
proxy, false, TunnelType.TUNNELLED, LayerType.PLAIN);
final ManagedHttpClientConnection conn = this.connFactory.create(
route, this.connectionConfig);
final HttpContext context = new BasicHttpContext();
HttpResponse response;
final HttpRequest connect = new BasicHttpRequest(
"CONNECT", host.toHostString(), HttpVersion.HTTP_1_1);
final BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope(proxy), credentials);
// Populate the execution context
context.setAttribute(HttpCoreContext.HTTP_TARGET_HOST, target);
context.setAttribute(HttpCoreContext.HTTP_CONNECTION, conn);
context.setAttribute(HttpCoreContext.HTTP_REQUEST, connect);
context.setAttribute(HttpClientContext.HTTP_ROUTE, route);
context.setAttribute(HttpClientContext.PROXY_AUTH_STATE, this.proxyAuthState);
context.setAttribute(HttpClientContext.CREDS_PROVIDER, credsProvider);
context.setAttribute(HttpClientContext.AUTHSCHEME_REGISTRY, this.authSchemeRegistry);
context.setAttribute(HttpClientContext.REQUEST_CONFIG, this.requestConfig);
this.requestExec.preProcess(connect, this.httpProcessor, context);
for (;;) {
if (!conn.isOpen()) {
final Socket socket = new Socket(proxy.getHostName(), proxy.getPort());
conn.bind(socket);
}
this.authenticator.generateAuthResponse(connect, this.proxyAuthState, context);
response = this.requestExec.execute(connect, conn, context);
final int status = response.getStatusLine().getStatusCode();
if (status < 200) {
throw new HttpException("Unexpected response to CONNECT request: " +
response.getStatusLine());
}
if (this.authenticator.isAuthenticationRequested(proxy, response,
this.proxyAuthStrategy, this.proxyAuthState, context)) {
if (this.authenticator.handleAuthChallenge(proxy, response,
this.proxyAuthStrategy, this.proxyAuthState, context)) {
// Retry request
if (this.reuseStrategy.keepAlive(response, context)) {
// Consume response content
final HttpEntity entity = response.getEntity();
EntityUtils.consume(entity);
} else {
conn.close();
}
// discard previous auth header
connect.removeHeaders(AUTH.PROXY_AUTH_RESP);
} else {
break;
}
} else {
break;
}
}
final int status = response.getStatusLine().getStatusCode();
if (status > 299) {
// Buffer response content
final HttpEntity entity = response.getEntity();
if (entity != null) {
response.setEntity(new BufferedHttpEntity(entity));
}
conn.close();
throw new TunnelRefusedException("CONNECT refused by proxy: " +
response.getStatusLine(), response);
}
return conn.getSocket();
}
}

View file

@ -1,227 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.net.URI;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
/**
* This class represents a collection of {@link java.net.URI}s used
* as redirect locations.
*
* @since 4.0
*/
@NotThreadSafe // HashSet/ArrayList are not synch.
public class RedirectLocations extends AbstractList<Object> {
private final Set<URI> unique;
private final List<URI> all;
public RedirectLocations() {
super();
this.unique = new HashSet<URI>();
this.all = new ArrayList<URI>();
}
/**
* Test if the URI is present in the collection.
*/
public boolean contains(final URI uri) {
return this.unique.contains(uri);
}
/**
* Adds a new URI to the collection.
*/
public void add(final URI uri) {
this.unique.add(uri);
this.all.add(uri);
}
/**
* Removes a URI from the collection.
*/
public boolean remove(final URI uri) {
final boolean removed = this.unique.remove(uri);
if (removed) {
final Iterator<URI> it = this.all.iterator();
while (it.hasNext()) {
final URI current = it.next();
if (current.equals(uri)) {
it.remove();
}
}
}
return removed;
}
/**
* Returns all redirect {@link URI}s in the order they were added to the collection.
*
* @return list of all URIs
*
* @since 4.1
*/
public List<URI> getAll() {
return new ArrayList<URI>(this.all);
}
/**
* Returns the URI at the specified position in this list.
*
* @param index
* index of the location to return
* @return the URI at the specified position in this list
* @throws IndexOutOfBoundsException
* if the index is out of range (
* <tt>index &lt; 0 || index &gt;= size()</tt>)
* @since 4.3
*/
@Override
public URI get(final int index) {
return this.all.get(index);
}
/**
* Returns the number of elements in this list. If this list contains more
* than <tt>Integer.MAX_VALUE</tt> elements, returns
* <tt>Integer.MAX_VALUE</tt>.
*
* @return the number of elements in this list
* @since 4.3
*/
@Override
public int size() {
return this.all.size();
}
/**
* Replaces the URI at the specified position in this list with the
* specified element (must be a URI).
*
* @param index
* index of the element to replace
* @param element
* URI to be stored at the specified position
* @return the URI previously at the specified position
* @throws UnsupportedOperationException
* if the <tt>set</tt> operation is not supported by this list
* @throws ClassCastException
* if the element is not a {@link URI}
* @throws NullPointerException
* if the specified element is null and this list does not
* permit null elements
* @throws IndexOutOfBoundsException
* if the index is out of range (
* <tt>index &lt; 0 || index &gt;= size()</tt>)
* @since 4.3
*/
@Override
public Object set(final int index, final Object element) {
final URI removed = this.all.set(index, (URI) element);
this.unique.remove(removed);
this.unique.add((URI) element);
if (this.all.size() != this.unique.size()) {
this.unique.addAll(this.all);
}
return removed;
}
/**
* Inserts the specified element at the specified position in this list
* (must be a URI). Shifts the URI currently at that position (if any) and
* any subsequent URIs to the right (adds one to their indices).
*
* @param index
* index at which the specified element is to be inserted
* @param element
* URI to be inserted
* @throws UnsupportedOperationException
* if the <tt>add</tt> operation is not supported by this list
* @throws ClassCastException
* if the element is not a {@link URI}
* @throws NullPointerException
* if the specified element is null and this list does not
* permit null elements
* @throws IndexOutOfBoundsException
* if the index is out of range (
* <tt>index &lt; 0 || index &gt; size()</tt>)
* @since 4.3
*/
@Override
public void add(final int index, final Object element) {
this.all.add(index, (URI) element);
this.unique.add((URI) element);
}
/**
* Removes the URI at the specified position in this list. Shifts any
* subsequent URIs to the left (subtracts one from their indices). Returns
* the URI that was removed from the list.
*
* @param index
* the index of the URI to be removed
* @return the URI previously at the specified position
* @throws IndexOutOfBoundsException
* if the index is out of range (
* <tt>index &lt; 0 || index &gt;= size()</tt>)
* @since 4.3
*/
@Override
public URI remove(final int index) {
final URI removed = this.all.remove(index);
this.unique.remove(removed);
if (this.all.size() != this.unique.size()) {
this.unique.addAll(this.all);
}
return removed;
}
/**
* Returns <tt>true</tt> if this collection contains the specified element.
* More formally, returns <tt>true</tt> if and only if this collection
* contains at least one element <tt>e</tt> such that
* <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.
*
* @param o element whose presence in this collection is to be tested
* @return <tt>true</tt> if this collection contains the specified
* element
*/
@Override
public boolean contains(final Object o) {
return this.unique.contains(o);
}
}

View file

@ -1,164 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.net.URI;
import java.net.URISyntaxException;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.ProtocolException;
import ch.boye.httpclientandroidlib.ProtocolVersion;
import ch.boye.httpclientandroidlib.RequestLine;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
import ch.boye.httpclientandroidlib.message.AbstractHttpMessage;
import ch.boye.httpclientandroidlib.message.BasicRequestLine;
import ch.boye.httpclientandroidlib.params.HttpProtocolParams;
import ch.boye.httpclientandroidlib.util.Args;
/**
* A wrapper class for {@link HttpRequest}s that can be used to change
* properties of the current request without modifying the original
* object.
* </p>
* This class is also capable of resetting the request headers to
* the state of the original request.
*
* @since 4.0
*
* @deprecated (4.3) do not use.
*/
@NotThreadSafe
@Deprecated
public class RequestWrapper extends AbstractHttpMessage implements HttpUriRequest {
private final HttpRequest original;
private URI uri;
private String method;
private ProtocolVersion version;
private int execCount;
public RequestWrapper(final HttpRequest request) throws ProtocolException {
super();
Args.notNull(request, "HTTP request");
this.original = request;
setParams(request.getParams());
setHeaders(request.getAllHeaders());
// Make a copy of the original URI
if (request instanceof HttpUriRequest) {
this.uri = ((HttpUriRequest) request).getURI();
this.method = ((HttpUriRequest) request).getMethod();
this.version = null;
} else {
final RequestLine requestLine = request.getRequestLine();
try {
this.uri = new URI(requestLine.getUri());
} catch (final URISyntaxException ex) {
throw new ProtocolException("Invalid request URI: "
+ requestLine.getUri(), ex);
}
this.method = requestLine.getMethod();
this.version = request.getProtocolVersion();
}
this.execCount = 0;
}
public void resetHeaders() {
// Make a copy of original headers
this.headergroup.clear();
setHeaders(this.original.getAllHeaders());
}
public String getMethod() {
return this.method;
}
public void setMethod(final String method) {
Args.notNull(method, "Method name");
this.method = method;
}
public ProtocolVersion getProtocolVersion() {
if (this.version == null) {
this.version = HttpProtocolParams.getVersion(getParams());
}
return this.version;
}
public void setProtocolVersion(final ProtocolVersion version) {
this.version = version;
}
public URI getURI() {
return this.uri;
}
public void setURI(final URI uri) {
this.uri = uri;
}
public RequestLine getRequestLine() {
final String method = getMethod();
final ProtocolVersion ver = getProtocolVersion();
String uritext = null;
if (uri != null) {
uritext = uri.toASCIIString();
}
if (uritext == null || uritext.length() == 0) {
uritext = "/";
}
return new BasicRequestLine(method, uritext, ver);
}
public void abort() throws UnsupportedOperationException {
throw new UnsupportedOperationException();
}
public boolean isAborted() {
return false;
}
public HttpRequest getOriginal() {
return this.original;
}
public boolean isRepeatable() {
return true;
}
public int getExecCount() {
return this.execCount;
}
public void incrementExecCount() {
this.execCount++;
}
}

View file

@ -1,67 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.conn.routing.HttpRoute;
/**
* A request with the route along which it should be sent.
*
* @since 4.0
*
* @deprecated (4.3) do not use.
*/
@Deprecated
@NotThreadSafe // RequestWrapper is @NotThreadSafe
public class RoutedRequest {
protected final RequestWrapper request; // @NotThreadSafe
protected final HttpRoute route; // @Immutable
/**
* Creates a new routed request.
*
* @param req the request
* @param route the route
*/
public RoutedRequest(final RequestWrapper req, final HttpRoute route) {
super();
this.request = req;
this.route = route;
}
public final RequestWrapper getRequest() {
return request;
}
public final HttpRoute getRoute() {
return route;
}
}

View file

@ -1,80 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.annotation.Immutable;
/**
* {@link ch.boye.httpclientandroidlib.client.HttpRequestRetryHandler} which assumes
* that all requested HTTP methods which should be idempotent according
* to RFC-2616 are in fact idempotent and can be retried.
* <p/>
* According to RFC-2616 section 9.1.2 the idempotent HTTP methods are:
* GET, HEAD, PUT, DELETE, OPTIONS, and TRACE
*
* @since 4.2
*/
@Immutable
public class StandardHttpRequestRetryHandler extends DefaultHttpRequestRetryHandler {
private final Map<String, Boolean> idempotentMethods;
/**
* Default constructor
*/
public StandardHttpRequestRetryHandler(final int retryCount, final boolean requestSentRetryEnabled) {
super(retryCount, requestSentRetryEnabled);
this.idempotentMethods = new ConcurrentHashMap<String, Boolean>();
this.idempotentMethods.put("GET", Boolean.TRUE);
this.idempotentMethods.put("HEAD", Boolean.TRUE);
this.idempotentMethods.put("PUT", Boolean.TRUE);
this.idempotentMethods.put("DELETE", Boolean.TRUE);
this.idempotentMethods.put("OPTIONS", Boolean.TRUE);
this.idempotentMethods.put("TRACE", Boolean.TRUE);
}
/**
* Default constructor
*/
public StandardHttpRequestRetryHandler() {
this(3, false);
}
@Override
protected boolean handleAsIdempotent(final HttpRequest request) {
final String method = request.getRequestLine().getMethod().toUpperCase(Locale.ENGLISH);
final Boolean b = this.idempotentMethods.get(method);
return b != null && b.booleanValue();
}
}

View file

@ -1,40 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
/**
* The actual system clock.
*
* @since 4.2
*/
class SystemClock implements Clock {
public long getCurrentTime() {
return System.currentTimeMillis();
}
}

View file

@ -1,145 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
import ch.boye.httpclientandroidlib.auth.AuthScope;
import ch.boye.httpclientandroidlib.auth.Credentials;
import ch.boye.httpclientandroidlib.auth.NTCredentials;
import ch.boye.httpclientandroidlib.auth.UsernamePasswordCredentials;
import ch.boye.httpclientandroidlib.client.CredentialsProvider;
import ch.boye.httpclientandroidlib.client.config.AuthSchemes;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Implementation of {@link CredentialsProvider} backed by standard
* JRE {@link Authenticator}.
*
* @since 4.3
*/
@ThreadSafe
public class SystemDefaultCredentialsProvider implements CredentialsProvider {
private static final Map<String, String> SCHEME_MAP;
static {
SCHEME_MAP = new ConcurrentHashMap<String, String>();
SCHEME_MAP.put(AuthSchemes.BASIC.toUpperCase(Locale.ENGLISH), "Basic");
SCHEME_MAP.put(AuthSchemes.DIGEST.toUpperCase(Locale.ENGLISH), "Digest");
SCHEME_MAP.put(AuthSchemes.NTLM.toUpperCase(Locale.ENGLISH), "NTLM");
SCHEME_MAP.put(AuthSchemes.SPNEGO.toUpperCase(Locale.ENGLISH), "SPNEGO");
SCHEME_MAP.put(AuthSchemes.KERBEROS.toUpperCase(Locale.ENGLISH), "Kerberos");
}
private static String translateScheme(final String key) {
if (key == null) {
return null;
}
final String s = SCHEME_MAP.get(key);
return s != null ? s : key;
}
private final BasicCredentialsProvider internal;
/**
* Default constructor.
*/
public SystemDefaultCredentialsProvider() {
super();
this.internal = new BasicCredentialsProvider();
}
public void setCredentials(final AuthScope authscope, final Credentials credentials) {
internal.setCredentials(authscope, credentials);
}
private static PasswordAuthentication getSystemCreds(
final AuthScope authscope,
final Authenticator.RequestorType requestorType) {
final String hostname = authscope.getHost();
final int port = authscope.getPort();
final String protocol = port == 443 ? "https" : "http";
return Authenticator.requestPasswordAuthentication(
hostname,
null,
port,
protocol,
null,
translateScheme(authscope.getScheme()),
null,
requestorType);
}
public Credentials getCredentials(final AuthScope authscope) {
Args.notNull(authscope, "Auth scope");
final Credentials localcreds = internal.getCredentials(authscope);
if (localcreds != null) {
return localcreds;
}
if (authscope.getHost() != null) {
PasswordAuthentication systemcreds = getSystemCreds(
authscope, Authenticator.RequestorType.SERVER);
if (systemcreds == null) {
systemcreds = getSystemCreds(
authscope, Authenticator.RequestorType.PROXY);
}
if (systemcreds != null) {
final String domain = System.getProperty("http.auth.ntlm.domain");
if (domain != null) {
return new NTCredentials(
systemcreds.getUserName(),
new String(systemcreds.getPassword()),
null, domain);
} else {
if (AuthSchemes.NTLM.equalsIgnoreCase(authscope.getScheme())) {
// Domian may be specified in a fully qualified user name
return new NTCredentials(
systemcreds.getUserName(),
new String(systemcreds.getPassword()),
null, null);
} else {
return new UsernamePasswordCredentials(
systemcreds.getUserName(),
new String(systemcreds.getPassword()));
}
}
}
}
return null;
}
public void clear() {
internal.clear();
}
}

View file

@ -1,149 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.net.ProxySelector;
import ch.boye.httpclientandroidlib.ConnectionReuseStrategy;
import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
import ch.boye.httpclientandroidlib.conn.ClientConnectionManager;
import ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner;
import ch.boye.httpclientandroidlib.impl.DefaultConnectionReuseStrategy;
import ch.boye.httpclientandroidlib.impl.NoConnectionReuseStrategy;
import ch.boye.httpclientandroidlib.impl.conn.PoolingClientConnectionManager;
import ch.boye.httpclientandroidlib.impl.conn.ProxySelectorRoutePlanner;
import ch.boye.httpclientandroidlib.impl.conn.SchemeRegistryFactory;
import ch.boye.httpclientandroidlib.params.HttpParams;
/**
* An extension of {@link DefaultHttpClient} pre-configured using system properties.
* <p>
* The following system properties are taken into account by this class:
* <ul>
* <li>ssl.TrustManagerFactory.algorithm</li>
* <li>javax.net.ssl.trustStoreType</li>
* <li>javax.net.ssl.trustStore</li>
* <li>javax.net.ssl.trustStoreProvider</li>
* <li>javax.net.ssl.trustStorePassword</li>
* <li>java.home</li>
* <li>ssl.KeyManagerFactory.algorithm</li>
* <li>javax.net.ssl.keyStoreType</li>
* <li>javax.net.ssl.keyStore</li>
* <li>javax.net.ssl.keyStoreProvider</li>
* <li>javax.net.ssl.keyStorePassword</li>
* <li>http.proxyHost</li>
* <li>http.proxyPort</li>
* <li>http.nonProxyHosts</li>
* <li>http.keepAlive</li>
* <li>http.maxConnections</li>
* </ul>
* <p>
* <p>
* The following parameters can be used to customize the behavior of this
* class:
* <ul>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#PROTOCOL_VERSION}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#STRICT_TRANSFER_ENCODING}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#HTTP_ELEMENT_CHARSET}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#USE_EXPECT_CONTINUE}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#WAIT_FOR_CONTINUE}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#USER_AGENT}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#TCP_NODELAY}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_TIMEOUT}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_LINGER}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_REUSEADDR}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SOCKET_BUFFER_SIZE}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#CONNECTION_TIMEOUT}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_LINE_LENGTH}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_HEADER_COUNT}</li>
* <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#STALE_CONNECTION_CHECK}</li>
* <li>{@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#FORCED_ROUTE}</li>
* <li>{@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#LOCAL_ADDRESS}</li>
* <li>{@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#DEFAULT_PROXY}</li>
* <li>{@link ch.boye.httpclientandroidlib.cookie.params.CookieSpecPNames#DATE_PATTERNS}</li>
* <li>{@link ch.boye.httpclientandroidlib.cookie.params.CookieSpecPNames#SINGLE_COOKIE_HEADER}</li>
* <li>{@link ch.boye.httpclientandroidlib.auth.params.AuthPNames#CREDENTIAL_CHARSET}</li>
* <li>{@link ch.boye.httpclientandroidlib.client.params.ClientPNames#COOKIE_POLICY}</li>
* <li>{@link ch.boye.httpclientandroidlib.client.params.ClientPNames#HANDLE_AUTHENTICATION}</li>
* <li>{@link ch.boye.httpclientandroidlib.client.params.ClientPNames#HANDLE_REDIRECTS}</li>
* <li>{@link ch.boye.httpclientandroidlib.client.params.ClientPNames#MAX_REDIRECTS}</li>
* <li>{@link ch.boye.httpclientandroidlib.client.params.ClientPNames#ALLOW_CIRCULAR_REDIRECTS}</li>
* <li>{@link ch.boye.httpclientandroidlib.client.params.ClientPNames#VIRTUAL_HOST}</li>
* <li>{@link ch.boye.httpclientandroidlib.client.params.ClientPNames#DEFAULT_HOST}</li>
* <li>{@link ch.boye.httpclientandroidlib.client.params.ClientPNames#DEFAULT_HEADERS}</li>
* <li>{@link ch.boye.httpclientandroidlib.client.params.ClientPNames#CONN_MANAGER_TIMEOUT}</li>
* </ul>
* </p>
*
* @since 4.2
*
* @deprecated (4.3) use {@link HttpClientBuilder}
*/
@ThreadSafe
@Deprecated
public class SystemDefaultHttpClient extends DefaultHttpClient {
public SystemDefaultHttpClient(final HttpParams params) {
super(null, params);
}
public SystemDefaultHttpClient() {
super(null, null);
}
@Override
protected ClientConnectionManager createClientConnectionManager() {
final PoolingClientConnectionManager connmgr = new PoolingClientConnectionManager(
SchemeRegistryFactory.createSystemDefault());
String s = System.getProperty("http.keepAlive", "true");
if ("true".equalsIgnoreCase(s)) {
s = System.getProperty("http.maxConnections", "5");
final int max = Integer.parseInt(s);
connmgr.setDefaultMaxPerRoute(max);
connmgr.setMaxTotal(2 * max);
}
return connmgr;
}
@Override
protected HttpRoutePlanner createHttpRoutePlanner() {
return new ProxySelectorRoutePlanner(getConnectionManager().getSchemeRegistry(),
ProxySelector.getDefault());
}
@Override
protected ConnectionReuseStrategy createConnectionReuseStrategy() {
final String s = System.getProperty("http.keepAlive", "true");
if ("true".equalsIgnoreCase(s)) {
return new DefaultConnectionReuseStrategy();
} else {
return new NoConnectionReuseStrategy();
}
}
}

View file

@ -1,57 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import java.util.Collection;
import ch.boye.httpclientandroidlib.HttpStatus;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.auth.AUTH;
import ch.boye.httpclientandroidlib.client.config.RequestConfig;
/**
* Default {@link ch.boye.httpclientandroidlib.client.AuthenticationStrategy} implementation
* for proxy host authentication.
*
* @since 4.2
*/
@Immutable
public class TargetAuthenticationStrategy extends AuthenticationStrategyImpl {
public static final TargetAuthenticationStrategy INSTANCE = new TargetAuthenticationStrategy();
public TargetAuthenticationStrategy() {
super(HttpStatus.SC_UNAUTHORIZED, AUTH.WWW_AUTH);
}
@Override
Collection<String> getPreferredAuthSchemes(final RequestConfig config) {
return config.getTargetPreferredAuthSchemes();
}
}

View file

@ -1,58 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client;
import ch.boye.httpclientandroidlib.HttpException;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.annotation.Immutable;
/**
* Signals that the tunnel request was rejected by the proxy host.
*
* @since 4.0
*
* @deprecated (4.3) reserved for internal use.
*/
@Deprecated
@Immutable
public class TunnelRefusedException extends HttpException {
private static final long serialVersionUID = -8646722842745617323L;
private final HttpResponse response;
public TunnelRefusedException(final String message, final HttpResponse response) {
super(message);
this.response = response;
}
public HttpResponse getResponse() {
return this.response;
}
}

View file

@ -1,178 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.IOException;
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
/* LogFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpException;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.client.cache.HeaderConstants;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry;
import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware;
import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper;
import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
import ch.boye.httpclientandroidlib.conn.routing.HttpRoute;
/**
* Class used to represent an asynchronous revalidation event, such as with
* "stale-while-revalidate"
*/
class AsynchronousValidationRequest implements Runnable {
private final AsynchronousValidator parent;
private final CachingExec cachingExec;
private final HttpRoute route;
private final HttpRequestWrapper request;
private final HttpClientContext context;
private final HttpExecutionAware execAware;
private final HttpCacheEntry cacheEntry;
private final String identifier;
private final int consecutiveFailedAttempts;
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
/**
* Used internally by {@link AsynchronousValidator} to schedule a
* revalidation.
* @param request
* @param context
* @param cacheEntry
* @param identifier
* @param consecutiveFailedAttempts
*/
AsynchronousValidationRequest(
final AsynchronousValidator parent,
final CachingExec cachingExec,
final HttpRoute route,
final HttpRequestWrapper request,
final HttpClientContext context,
final HttpExecutionAware execAware,
final HttpCacheEntry cacheEntry,
final String identifier,
final int consecutiveFailedAttempts) {
this.parent = parent;
this.cachingExec = cachingExec;
this.route = route;
this.request = request;
this.context = context;
this.execAware = execAware;
this.cacheEntry = cacheEntry;
this.identifier = identifier;
this.consecutiveFailedAttempts = consecutiveFailedAttempts;
}
public void run() {
try {
if (revalidateCacheEntry()) {
parent.jobSuccessful(identifier);
} else {
parent.jobFailed(identifier);
}
} finally {
parent.markComplete(identifier);
}
}
/**
* Revalidate the cache entry and return if the operation was successful.
* Success means a connection to the server was established and replay did
* not indicate a server error.
* @return <code>true</code> if the cache entry was successfully validated;
* otherwise <code>false</code>
*/
protected boolean revalidateCacheEntry() {
try {
final CloseableHttpResponse httpResponse = cachingExec.revalidateCacheEntry(route, request, context, execAware, cacheEntry);
try {
final int statusCode = httpResponse.getStatusLine().getStatusCode();
return isNotServerError(statusCode) && isNotStale(httpResponse);
} finally {
httpResponse.close();
}
} catch (final IOException ioe) {
log.debug("Asynchronous revalidation failed due to I/O error", ioe);
return false;
} catch (final HttpException pe) {
log.error("HTTP protocol exception during asynchronous revalidation", pe);
return false;
} catch (final RuntimeException re) {
log.error("RuntimeException thrown during asynchronous revalidation: " + re);
return false;
}
}
/**
* Return whether the status code indicates a server error or not.
* @param statusCode the status code to be checked
* @return if the status code indicates a server error or not
*/
private boolean isNotServerError(final int statusCode) {
return statusCode < 500;
}
/**
* Try to detect if the returned response is generated from a stale cache entry.
* @param httpResponse the response to be checked
* @return whether the response is stale or not
*/
private boolean isNotStale(final HttpResponse httpResponse) {
final Header[] warnings = httpResponse.getHeaders(HeaderConstants.WARNING);
if (warnings != null)
{
for (final Header warning : warnings)
{
/**
* warn-codes
* 110 = Response is stale
* 111 = Revalidation failed
*/
final String warningValue = warning.getValue();
if (warningValue.startsWith("110") || warningValue.startsWith("111"))
{
return false;
}
}
}
return true;
}
String getIdentifier() {
return identifier;
}
/**
* The number of consecutively failed revalidation attempts.
* @return the number of consecutively failed revalidation attempts.
*/
public int getConsecutiveFailedAttempts() {
return consecutiveFailedAttempts;
}
}

View file

@ -1,150 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.Closeable;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.RejectedExecutionException;
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
/* LogFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry;
import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware;
import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper;
import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
import ch.boye.httpclientandroidlib.conn.routing.HttpRoute;
/**
* Class used for asynchronous revalidations to be used when the "stale-
* while-revalidate" directive is present
*/
class AsynchronousValidator implements Closeable {
private final SchedulingStrategy schedulingStrategy;
private final Set<String> queued;
private final CacheKeyGenerator cacheKeyGenerator;
private final FailureCache failureCache;
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
/**
* Create AsynchronousValidator which will make revalidation requests
* using an {@link ImmediateSchedulingStrategy}. Its thread
* pool will be configured according to the given {@link CacheConfig}.
* @param config specifies thread pool settings. See
* {@link CacheConfig#getAsynchronousWorkersMax()},
* {@link CacheConfig#getAsynchronousWorkersCore()},
* {@link CacheConfig#getAsynchronousWorkerIdleLifetimeSecs()},
* and {@link CacheConfig#getRevalidationQueueSize()}.
*/
public AsynchronousValidator(final CacheConfig config) {
this(new ImmediateSchedulingStrategy(config));
}
/**
* Create AsynchronousValidator which will make revalidation requests
* using the supplied {@link SchedulingStrategy}. Closing the validator
* will also close the given schedulingStrategy.
* @param schedulingStrategy used to maintain a pool of worker threads and
* schedules when requests are executed
*/
AsynchronousValidator(final SchedulingStrategy schedulingStrategy) {
this.schedulingStrategy = schedulingStrategy;
this.queued = new HashSet<String>();
this.cacheKeyGenerator = new CacheKeyGenerator();
this.failureCache = new DefaultFailureCache();
}
public void close() throws IOException {
schedulingStrategy.close();
}
/**
* Schedules an asynchronous revalidation
*/
public synchronized void revalidateCacheEntry(
final CachingExec cachingExec,
final HttpRoute route,
final HttpRequestWrapper request,
final HttpClientContext context,
final HttpExecutionAware execAware,
final HttpCacheEntry entry) {
// getVariantURI will fall back on getURI if no variants exist
final String uri = cacheKeyGenerator.getVariantURI(context.getTargetHost(), request, entry);
if (!queued.contains(uri)) {
final int consecutiveFailedAttempts = failureCache.getErrorCount(uri);
final AsynchronousValidationRequest revalidationRequest =
new AsynchronousValidationRequest(
this, cachingExec, route, request, context, execAware, entry, uri, consecutiveFailedAttempts);
try {
schedulingStrategy.schedule(revalidationRequest);
queued.add(uri);
} catch (final RejectedExecutionException ree) {
log.debug("Revalidation for [" + uri + "] not scheduled: " + ree);
}
}
}
/**
* Removes an identifier from the internal list of revalidation jobs in
* progress. This is meant to be called by
* {@link AsynchronousValidationRequest#run()} once the revalidation is
* complete, using the identifier passed in during constructions.
* @param identifier
*/
synchronized void markComplete(final String identifier) {
queued.remove(identifier);
}
/**
* The revalidation job was successful thus the number of consecutive
* failed attempts will be reset to zero. Should be called by
* {@link AsynchronousValidationRequest#run()}.
* @param identifier the revalidation job's unique identifier
*/
void jobSuccessful(final String identifier) {
failureCache.resetErrorCount(identifier);
}
/**
* The revalidation job did fail and thus the number of consecutive failed
* attempts will be increased. Should be called by
* {@link AsynchronousValidationRequest#run()}.
* @param identifier the revalidation job's unique identifier
*/
void jobFailed(final String identifier) {
failureCache.increaseErrorCount(identifier);
}
Set<String> getScheduledIdentifiers() {
return Collections.unmodifiableSet(queued);
}
}

View file

@ -1,376 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
/* LogFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpHost;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.HttpStatus;
import ch.boye.httpclientandroidlib.HttpVersion;
import ch.boye.httpclientandroidlib.client.cache.HeaderConstants;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheInvalidator;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheStorage;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheUpdateCallback;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheUpdateException;
import ch.boye.httpclientandroidlib.client.cache.Resource;
import ch.boye.httpclientandroidlib.client.cache.ResourceFactory;
import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
import ch.boye.httpclientandroidlib.entity.ByteArrayEntity;
import ch.boye.httpclientandroidlib.message.BasicHttpResponse;
import ch.boye.httpclientandroidlib.protocol.HTTP;
class BasicHttpCache implements HttpCache {
private static final Set<String> safeRequestMethods = new HashSet<String>(
Arrays.asList(HeaderConstants.HEAD_METHOD,
HeaderConstants.GET_METHOD, HeaderConstants.OPTIONS_METHOD,
HeaderConstants.TRACE_METHOD));
private final CacheKeyGenerator uriExtractor;
private final ResourceFactory resourceFactory;
private final long maxObjectSizeBytes;
private final CacheEntryUpdater cacheEntryUpdater;
private final CachedHttpResponseGenerator responseGenerator;
private final HttpCacheInvalidator cacheInvalidator;
private final HttpCacheStorage storage;
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
public BasicHttpCache(
final ResourceFactory resourceFactory,
final HttpCacheStorage storage,
final CacheConfig config,
final CacheKeyGenerator uriExtractor,
final HttpCacheInvalidator cacheInvalidator) {
this.resourceFactory = resourceFactory;
this.uriExtractor = uriExtractor;
this.cacheEntryUpdater = new CacheEntryUpdater(resourceFactory);
this.maxObjectSizeBytes = config.getMaxObjectSize();
this.responseGenerator = new CachedHttpResponseGenerator();
this.storage = storage;
this.cacheInvalidator = cacheInvalidator;
}
public BasicHttpCache(
final ResourceFactory resourceFactory,
final HttpCacheStorage storage,
final CacheConfig config,
final CacheKeyGenerator uriExtractor) {
this( resourceFactory, storage, config, uriExtractor,
new CacheInvalidator(uriExtractor, storage));
}
public BasicHttpCache(
final ResourceFactory resourceFactory,
final HttpCacheStorage storage,
final CacheConfig config) {
this( resourceFactory, storage, config, new CacheKeyGenerator());
}
public BasicHttpCache(final CacheConfig config) {
this(new HeapResourceFactory(), new BasicHttpCacheStorage(config), config);
}
public BasicHttpCache() {
this(CacheConfig.DEFAULT);
}
public void flushCacheEntriesFor(final HttpHost host, final HttpRequest request)
throws IOException {
if (!safeRequestMethods.contains(request.getRequestLine().getMethod())) {
final String uri = uriExtractor.getURI(host, request);
storage.removeEntry(uri);
}
}
public void flushInvalidatedCacheEntriesFor(final HttpHost host, final HttpRequest request, final HttpResponse response) {
if (!safeRequestMethods.contains(request.getRequestLine().getMethod())) {
cacheInvalidator.flushInvalidatedCacheEntries(host, request, response);
}
}
void storeInCache(
final HttpHost target, final HttpRequest request, final HttpCacheEntry entry) throws IOException {
if (entry.hasVariants()) {
storeVariantEntry(target, request, entry);
} else {
storeNonVariantEntry(target, request, entry);
}
}
void storeNonVariantEntry(
final HttpHost target, final HttpRequest req, final HttpCacheEntry entry) throws IOException {
final String uri = uriExtractor.getURI(target, req);
storage.putEntry(uri, entry);
}
void storeVariantEntry(
final HttpHost target,
final HttpRequest req,
final HttpCacheEntry entry) throws IOException {
final String parentURI = uriExtractor.getURI(target, req);
final String variantURI = uriExtractor.getVariantURI(target, req, entry);
storage.putEntry(variantURI, entry);
final HttpCacheUpdateCallback callback = new HttpCacheUpdateCallback() {
public HttpCacheEntry update(final HttpCacheEntry existing) throws IOException {
return doGetUpdatedParentEntry(
req.getRequestLine().getUri(), existing, entry,
uriExtractor.getVariantKey(req, entry),
variantURI);
}
};
try {
storage.updateEntry(parentURI, callback);
} catch (final HttpCacheUpdateException e) {
log.warn("Could not update key [" + parentURI + "]", e);
}
}
public void reuseVariantEntryFor(final HttpHost target, final HttpRequest req,
final Variant variant) throws IOException {
final String parentCacheKey = uriExtractor.getURI(target, req);
final HttpCacheEntry entry = variant.getEntry();
final String variantKey = uriExtractor.getVariantKey(req, entry);
final String variantCacheKey = variant.getCacheKey();
final HttpCacheUpdateCallback callback = new HttpCacheUpdateCallback() {
public HttpCacheEntry update(final HttpCacheEntry existing)
throws IOException {
return doGetUpdatedParentEntry(req.getRequestLine().getUri(),
existing, entry, variantKey, variantCacheKey);
}
};
try {
storage.updateEntry(parentCacheKey, callback);
} catch (final HttpCacheUpdateException e) {
log.warn("Could not update key [" + parentCacheKey + "]", e);
}
}
boolean isIncompleteResponse(final HttpResponse resp, final Resource resource) {
final int status = resp.getStatusLine().getStatusCode();
if (status != HttpStatus.SC_OK
&& status != HttpStatus.SC_PARTIAL_CONTENT) {
return false;
}
final Header hdr = resp.getFirstHeader(HTTP.CONTENT_LEN);
if (hdr == null) {
return false;
}
final int contentLength;
try {
contentLength = Integer.parseInt(hdr.getValue());
} catch (final NumberFormatException nfe) {
return false;
}
return (resource.length() < contentLength);
}
CloseableHttpResponse generateIncompleteResponseError(
final HttpResponse response, final Resource resource) {
final int contentLength = Integer.parseInt(response.getFirstHeader(HTTP.CONTENT_LEN).getValue());
final HttpResponse error =
new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_BAD_GATEWAY, "Bad Gateway");
error.setHeader("Content-Type","text/plain;charset=UTF-8");
final String msg = String.format("Received incomplete response " +
"with Content-Length %d but actual body length %d",
contentLength, resource.length());
final byte[] msgBytes = msg.getBytes();
error.setHeader("Content-Length", Integer.toString(msgBytes.length));
error.setEntity(new ByteArrayEntity(msgBytes));
return Proxies.enhanceResponse(error);
}
HttpCacheEntry doGetUpdatedParentEntry(
final String requestId,
final HttpCacheEntry existing,
final HttpCacheEntry entry,
final String variantKey,
final String variantCacheKey) throws IOException {
HttpCacheEntry src = existing;
if (src == null) {
src = entry;
}
Resource resource = null;
if (src.getResource() != null) {
resource = resourceFactory.copy(requestId, src.getResource());
}
final Map<String,String> variantMap = new HashMap<String,String>(src.getVariantMap());
variantMap.put(variantKey, variantCacheKey);
return new HttpCacheEntry(
src.getRequestDate(),
src.getResponseDate(),
src.getStatusLine(),
src.getAllHeaders(),
resource,
variantMap);
}
public HttpCacheEntry updateCacheEntry(final HttpHost target, final HttpRequest request,
final HttpCacheEntry stale, final HttpResponse originResponse,
final Date requestSent, final Date responseReceived) throws IOException {
final HttpCacheEntry updatedEntry = cacheEntryUpdater.updateCacheEntry(
request.getRequestLine().getUri(),
stale,
requestSent,
responseReceived,
originResponse);
storeInCache(target, request, updatedEntry);
return updatedEntry;
}
public HttpCacheEntry updateVariantCacheEntry(final HttpHost target, final HttpRequest request,
final HttpCacheEntry stale, final HttpResponse originResponse,
final Date requestSent, final Date responseReceived, final String cacheKey) throws IOException {
final HttpCacheEntry updatedEntry = cacheEntryUpdater.updateCacheEntry(
request.getRequestLine().getUri(),
stale,
requestSent,
responseReceived,
originResponse);
storage.putEntry(cacheKey, updatedEntry);
return updatedEntry;
}
public HttpResponse cacheAndReturnResponse(final HttpHost host, final HttpRequest request,
final HttpResponse originResponse, final Date requestSent, final Date responseReceived)
throws IOException {
return cacheAndReturnResponse(host, request,
Proxies.enhanceResponse(originResponse), requestSent,
responseReceived);
}
public CloseableHttpResponse cacheAndReturnResponse(
final HttpHost host,
final HttpRequest request,
final CloseableHttpResponse originResponse,
final Date requestSent,
final Date responseReceived) throws IOException {
boolean closeOriginResponse = true;
final SizeLimitedResponseReader responseReader = getResponseReader(request, originResponse);
try {
responseReader.readResponse();
if (responseReader.isLimitReached()) {
closeOriginResponse = false;
return responseReader.getReconstructedResponse();
}
final Resource resource = responseReader.getResource();
if (isIncompleteResponse(originResponse, resource)) {
return generateIncompleteResponseError(originResponse, resource);
}
final HttpCacheEntry entry = new HttpCacheEntry(
requestSent,
responseReceived,
originResponse.getStatusLine(),
originResponse.getAllHeaders(),
resource);
storeInCache(host, request, entry);
return responseGenerator.generateResponse(entry);
} finally {
if (closeOriginResponse) {
originResponse.close();
}
}
}
SizeLimitedResponseReader getResponseReader(final HttpRequest request,
final CloseableHttpResponse backEndResponse) {
return new SizeLimitedResponseReader(
resourceFactory, maxObjectSizeBytes, request, backEndResponse);
}
public HttpCacheEntry getCacheEntry(final HttpHost host, final HttpRequest request) throws IOException {
final HttpCacheEntry root = storage.getEntry(uriExtractor.getURI(host, request));
if (root == null) {
return null;
}
if (!root.hasVariants()) {
return root;
}
final String variantCacheKey = root.getVariantMap().get(uriExtractor.getVariantKey(request, root));
if (variantCacheKey == null) {
return null;
}
return storage.getEntry(variantCacheKey);
}
public void flushInvalidatedCacheEntriesFor(final HttpHost host,
final HttpRequest request) throws IOException {
cacheInvalidator.flushInvalidatedCacheEntries(host, request);
}
public Map<String, Variant> getVariantCacheEntriesWithEtags(final HttpHost host, final HttpRequest request)
throws IOException {
final Map<String,Variant> variants = new HashMap<String,Variant>();
final HttpCacheEntry root = storage.getEntry(uriExtractor.getURI(host, request));
if (root == null || !root.hasVariants()) {
return variants;
}
for(final Map.Entry<String, String> variant : root.getVariantMap().entrySet()) {
final String variantKey = variant.getKey();
final String variantCacheKey = variant.getValue();
addVariantWithEtag(variantKey, variantCacheKey, variants);
}
return variants;
}
private void addVariantWithEtag(final String variantKey,
final String variantCacheKey, final Map<String, Variant> variants)
throws IOException {
final HttpCacheEntry entry = storage.getEntry(variantCacheKey);
if (entry == null) {
return;
}
final Header etagHeader = entry.getFirstHeader(HeaderConstants.ETAG);
if (etagHeader == null) {
return;
}
variants.put(etagHeader.getValue(), new Variant(variantKey, variantCacheKey, entry));
}
}

View file

@ -1,96 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.IOException;
import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheStorage;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheUpdateCallback;
/**
* Basic {@link HttpCacheStorage} implementation backed by an instance of
* {@link java.util.LinkedHashMap}. In other words, cache entries and
* the cached response bodies are held in-memory. This cache does NOT
* deallocate resources associated with the cache entries; it is intended
* for use with {@link HeapResource} and similar. This is the default cache
* storage backend used by {@link CachingHttpClients}.
*
* @since 4.1
*/
@ThreadSafe
public class BasicHttpCacheStorage implements HttpCacheStorage {
private final CacheMap entries;
public BasicHttpCacheStorage(final CacheConfig config) {
super();
this.entries = new CacheMap(config.getMaxCacheEntries());
}
/**
* Places a HttpCacheEntry in the cache
*
* @param url
* Url to use as the cache key
* @param entry
* HttpCacheEntry to place in the cache
*/
public synchronized void putEntry(final String url, final HttpCacheEntry entry) throws IOException {
entries.put(url, entry);
}
/**
* Gets an entry from the cache, if it exists
*
* @param url
* Url that is the cache key
* @return HttpCacheEntry if one exists, or null for cache miss
*/
public synchronized HttpCacheEntry getEntry(final String url) throws IOException {
return entries.get(url);
}
/**
* Removes a HttpCacheEntry from the cache
*
* @param url
* Url that is the cache key
*/
public synchronized void removeEntry(final String url) throws IOException {
entries.remove(url);
}
public synchronized void updateEntry(
final String url,
final HttpCacheUpdateCallback callback) throws IOException {
final HttpCacheEntry existingEntry = entries.get(url);
entries.put(url, callback.update(existingEntry));
}
}

View file

@ -1,86 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Formatter;
import java.util.Locale;
import ch.boye.httpclientandroidlib.annotation.GuardedBy;
import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
/**
* Should produce reasonably unique tokens.
*/
@ThreadSafe
class BasicIdGenerator {
private final String hostname;
private final SecureRandom rnd;
@GuardedBy("this")
private long count;
public BasicIdGenerator() {
super();
String hostname;
try {
hostname = InetAddress.getLocalHost().getHostName();
} catch (final UnknownHostException ex) {
hostname = "localhost";
}
this.hostname = hostname;
try {
this.rnd = SecureRandom.getInstance("SHA1PRNG");
} catch (final NoSuchAlgorithmException ex) {
throw new Error(ex);
}
this.rnd.setSeed(System.currentTimeMillis());
}
public synchronized void generate(final StringBuilder buffer) {
this.count++;
final int rndnum = this.rnd.nextInt();
buffer.append(System.currentTimeMillis());
buffer.append('.');
final Formatter formatter = new Formatter(buffer, Locale.US);
formatter.format("%1$016x-%2$08x", this.count, rndnum);
formatter.close();
buffer.append('.');
buffer.append(this.hostname);
}
public String generate() {
final StringBuilder buffer = new StringBuilder();
generate(buffer);
return buffer.toString();
}
}

View file

@ -1,764 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import ch.boye.httpclientandroidlib.util.Args;
/**
* <p>Java Beans-style configuration for a {@link CachingHttpClient}. Any class
* in the caching module that has configuration options should take a
* {@link CacheConfig} argument in one of its constructors. A
* {@code CacheConfig} instance has sane and conservative defaults, so the
* easiest way to specify options is to get an instance and then set just
* the options you want to modify from their defaults.</p>
*
* <p><b>N.B.</b> This class is only for caching-specific configuration; to
* configure the behavior of the rest of the client, configure the
* {@link ch.boye.httpclientandroidlib.client.HttpClient} used as the &quot;backend&quot;
* for the {@code CachingHttpClient}.</p>
*
* <p>Cache configuration can be grouped into the following categories:</p>
*
* <p><b>Cache size.</b> If the backend storage supports these limits, you
* can specify the {@link CacheConfig#getMaxCacheEntries maximum number of
* cache entries} as well as the {@link CacheConfig#getMaxObjectSizeBytes
* maximum cacheable response body size}.</p>
*
* <p><b>Public/private caching.</b> By default, the caching module considers
* itself to be a shared (public) cache, and will not, for example, cache
* responses to requests with {@code Authorization} headers or responses
* marked with {@code Cache-Control: private}. If, however, the cache
* is only going to be used by one logical "user" (behaving similarly to a
* browser cache), then you will want to {@link
* CacheConfig#setSharedCache(boolean) turn off the shared cache setting}.</p>
*
* <p><b>303 caching</b>. RFC2616 explicitly disallows caching 303 responses;
* however, the HTTPbis working group says they can be cached
* if explicitly indicated in the response headers and permitted by the request method.
* (They also indicate that disallowing 303 caching is actually an unintended
* spec error in RFC2616).
* This behavior is off by default, to err on the side of a conservative
* adherence to the existing standard, but you may want to
* {@link Builder#setAllow303Caching(boolean) enable it}.
*
* <p><b>Weak ETags on PUT/DELETE If-Match requests</b>. RFC2616 explicitly
* prohibits the use of weak validators in non-GET requests, however, the
* HTTPbis working group says while the limitation for weak validators on ranged
* requests makes sense, weak ETag validation is useful on full non-GET
* requests; e.g., PUT with If-Match. This behavior is off by default, to err on
* the side of a conservative adherence to the existing standard, but you may
* want to {@link Builder#setWeakETagOnPutDeleteAllowed(boolean) enable it}.
*
* <p><b>Heuristic caching</b>. Per RFC2616, a cache may cache certain cache
* entries even if no explicit cache control headers are set by the origin.
* This behavior is off by default, but you may want to turn this on if you
* are working with an origin that doesn't set proper headers but where you
* still want to cache the responses. You will want to {@link
* CacheConfig#setHeuristicCachingEnabled(boolean) enable heuristic caching},
* then specify either a {@link CacheConfig#getHeuristicDefaultLifetime()
* default freshness lifetime} and/or a {@link
* CacheConfig#setHeuristicCoefficient(float) fraction of the time since
* the resource was last modified}. See Sections
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.2.2">
* 13.2.2</a> and <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.2.4">
* 13.2.4</a> of the HTTP/1.1 RFC for more details on heuristic caching.</p>
*
* <p><b>Background validation</b>. The cache module supports the
* {@code stale-while-revalidate} directive of
* <a href="http://tools.ietf.org/html/rfc5861">RFC5861</a>, which allows
* certain cache entry revalidations to happen in the background. You may
* want to tweak the settings for the {@link
* CacheConfig#getAsynchronousWorkersCore() minimum} and {@link
* CacheConfig#getAsynchronousWorkersMax() maximum} number of background
* worker threads, as well as the {@link
* CacheConfig#getAsynchronousWorkerIdleLifetimeSecs() maximum time they
* can be idle before being reclaimed}. You can also control the {@link
* CacheConfig#getRevalidationQueueSize() size of the queue} used for
* revalidations when there aren't enough workers to keep up with demand.</b>
*/
public class CacheConfig implements Cloneable {
/** Default setting for the maximum object size that will be
* cached, in bytes.
*/
public final static int DEFAULT_MAX_OBJECT_SIZE_BYTES = 8192;
/** Default setting for the maximum number of cache entries
* that will be retained.
*/
public final static int DEFAULT_MAX_CACHE_ENTRIES = 1000;
/** Default setting for the number of retries on a failed
* cache update
*/
public final static int DEFAULT_MAX_UPDATE_RETRIES = 1;
/** Default setting for 303 caching
*/
public final static boolean DEFAULT_303_CACHING_ENABLED = false;
/** Default setting to allow weak tags on PUT/DELETE methods
*/
public final static boolean DEFAULT_WEAK_ETAG_ON_PUTDELETE_ALLOWED = false;
/** Default setting for heuristic caching
*/
public final static boolean DEFAULT_HEURISTIC_CACHING_ENABLED = false;
/** Default coefficient used to heuristically determine freshness
* lifetime from the Last-Modified time of a cache entry.
*/
public final static float DEFAULT_HEURISTIC_COEFFICIENT = 0.1f;
/** Default lifetime in seconds to be assumed when we cannot calculate
* freshness heuristically.
*/
public final static long DEFAULT_HEURISTIC_LIFETIME = 0;
/** Default number of worker threads to allow for background revalidations
* resulting from the stale-while-revalidate directive.
*/
public static final int DEFAULT_ASYNCHRONOUS_WORKERS_MAX = 1;
/** Default minimum number of worker threads to allow for background
* revalidations resulting from the stale-while-revalidate directive.
*/
public static final int DEFAULT_ASYNCHRONOUS_WORKERS_CORE = 1;
/** Default maximum idle lifetime for a background revalidation thread
* before it gets reclaimed.
*/
public static final int DEFAULT_ASYNCHRONOUS_WORKER_IDLE_LIFETIME_SECS = 60;
/** Default maximum queue length for background revalidation requests.
*/
public static final int DEFAULT_REVALIDATION_QUEUE_SIZE = 100;
public static final CacheConfig DEFAULT = new Builder().build();
// TODO: make final
private long maxObjectSize;
private int maxCacheEntries;
private int maxUpdateRetries;
private boolean allow303Caching;
private boolean weakETagOnPutDeleteAllowed;
private boolean heuristicCachingEnabled;
private float heuristicCoefficient;
private long heuristicDefaultLifetime;
private boolean isSharedCache;
private int asynchronousWorkersMax;
private int asynchronousWorkersCore;
private int asynchronousWorkerIdleLifetimeSecs;
private int revalidationQueueSize;
private boolean neverCacheHTTP10ResponsesWithQuery;
/**
* @deprecated (4.3) use {@link Builder}.
*/
@Deprecated
public CacheConfig() {
super();
this.maxObjectSize = DEFAULT_MAX_OBJECT_SIZE_BYTES;
this.maxCacheEntries = DEFAULT_MAX_CACHE_ENTRIES;
this.maxUpdateRetries = DEFAULT_MAX_UPDATE_RETRIES;
this.allow303Caching = DEFAULT_303_CACHING_ENABLED;
this.weakETagOnPutDeleteAllowed = DEFAULT_WEAK_ETAG_ON_PUTDELETE_ALLOWED;
this.heuristicCachingEnabled = DEFAULT_HEURISTIC_CACHING_ENABLED;
this.heuristicCoefficient = DEFAULT_HEURISTIC_COEFFICIENT;
this.heuristicDefaultLifetime = DEFAULT_HEURISTIC_LIFETIME;
this.isSharedCache = true;
this.asynchronousWorkersMax = DEFAULT_ASYNCHRONOUS_WORKERS_MAX;
this.asynchronousWorkersCore = DEFAULT_ASYNCHRONOUS_WORKERS_CORE;
this.asynchronousWorkerIdleLifetimeSecs = DEFAULT_ASYNCHRONOUS_WORKER_IDLE_LIFETIME_SECS;
this.revalidationQueueSize = DEFAULT_REVALIDATION_QUEUE_SIZE;
}
CacheConfig(
final long maxObjectSize,
final int maxCacheEntries,
final int maxUpdateRetries,
final boolean allow303Caching,
final boolean weakETagOnPutDeleteAllowed,
final boolean heuristicCachingEnabled,
final float heuristicCoefficient,
final long heuristicDefaultLifetime,
final boolean isSharedCache,
final int asynchronousWorkersMax,
final int asynchronousWorkersCore,
final int asynchronousWorkerIdleLifetimeSecs,
final int revalidationQueueSize,
final boolean neverCacheHTTP10ResponsesWithQuery) {
super();
this.maxObjectSize = maxObjectSize;
this.maxCacheEntries = maxCacheEntries;
this.maxUpdateRetries = maxUpdateRetries;
this.allow303Caching = allow303Caching;
this.weakETagOnPutDeleteAllowed = weakETagOnPutDeleteAllowed;
this.heuristicCachingEnabled = heuristicCachingEnabled;
this.heuristicCoefficient = heuristicCoefficient;
this.heuristicDefaultLifetime = heuristicDefaultLifetime;
this.isSharedCache = isSharedCache;
this.asynchronousWorkersMax = asynchronousWorkersMax;
this.asynchronousWorkersCore = asynchronousWorkersCore;
this.asynchronousWorkerIdleLifetimeSecs = asynchronousWorkerIdleLifetimeSecs;
this.revalidationQueueSize = revalidationQueueSize;
}
/**
* Returns the current maximum response body size that will be cached.
* @return size in bytes
*
* @deprecated (4.2) use {@link #getMaxObjectSize()}
*/
@Deprecated
public int getMaxObjectSizeBytes() {
return maxObjectSize > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) maxObjectSize;
}
/**
* Specifies the maximum response body size that will be eligible for caching.
* @param maxObjectSizeBytes size in bytes
*
* @deprecated (4.2) use {@link Builder}.
*/
@Deprecated
public void setMaxObjectSizeBytes(final int maxObjectSizeBytes) {
if (maxObjectSizeBytes > Integer.MAX_VALUE) {
this.maxObjectSize = Integer.MAX_VALUE;
} else {
this.maxObjectSize = maxObjectSizeBytes;
}
}
/**
* Returns the current maximum response body size that will be cached.
* @return size in bytes
*
* @since 4.2
*/
public long getMaxObjectSize() {
return maxObjectSize;
}
/**
* Specifies the maximum response body size that will be eligible for caching.
* @param maxObjectSize size in bytes
*
* @since 4.2
*
* @deprecated (4.3) use {@link Builder}.
*/
@Deprecated
public void setMaxObjectSize(final long maxObjectSize) {
this.maxObjectSize = maxObjectSize;
}
/**
* Returns whether the cache will never cache HTTP 1.0 responses with a query string or not.
* @return {@code true} to not cache query string responses, {@code false} to cache if explicit cache headers are
* found
*/
public boolean isNeverCacheHTTP10ResponsesWithQuery() {
return neverCacheHTTP10ResponsesWithQuery;
}
/**
* Returns the maximum number of cache entries the cache will retain.
*/
public int getMaxCacheEntries() {
return maxCacheEntries;
}
/**
* Sets the maximum number of cache entries the cache will retain.
*
* @deprecated (4.3) use {@link Builder}.
*/
@Deprecated
public void setMaxCacheEntries(final int maxCacheEntries) {
this.maxCacheEntries = maxCacheEntries;
}
/**
* Returns the number of times to retry a cache update on failure
*/
public int getMaxUpdateRetries(){
return maxUpdateRetries;
}
/**
* Sets the number of times to retry a cache update on failure
*
* @deprecated (4.3) use {@link Builder}.
*/
@Deprecated
public void setMaxUpdateRetries(final int maxUpdateRetries){
this.maxUpdateRetries = maxUpdateRetries;
}
/**
* Returns whether 303 caching is enabled.
* @return {@code true} if it is enabled.
*/
public boolean is303CachingEnabled() {
return allow303Caching;
}
/**
* Returns whether weak etags is allowed with PUT/DELETE methods.
* @return {@code true} if it is allowed.
*/
public boolean isWeakETagOnPutDeleteAllowed() {
return weakETagOnPutDeleteAllowed;
}
/**
* Returns whether heuristic caching is enabled.
* @return {@code true} if it is enabled.
*/
public boolean isHeuristicCachingEnabled() {
return heuristicCachingEnabled;
}
/**
* Enables or disables heuristic caching.
* @param heuristicCachingEnabled should be {@code true} to
* permit heuristic caching, {@code false} to disable it.
*
* @deprecated (4.3) use {@link Builder}.
*/
@Deprecated
public void setHeuristicCachingEnabled(final boolean heuristicCachingEnabled) {
this.heuristicCachingEnabled = heuristicCachingEnabled;
}
/**
* Returns lifetime coefficient used in heuristic freshness caching.
*/
public float getHeuristicCoefficient() {
return heuristicCoefficient;
}
/**
* Sets coefficient to be used in heuristic freshness caching. This is
* interpreted as the fraction of the time between the {@code Last-Modified}
* and {@code Date} headers of a cached response during which the cached
* response will be considered heuristically fresh.
* @param heuristicCoefficient should be between {@code 0.0} and
* {@code 1.0}.
*
* @deprecated (4.3) use {@link Builder}.
*/
@Deprecated
public void setHeuristicCoefficient(final float heuristicCoefficient) {
this.heuristicCoefficient = heuristicCoefficient;
}
/**
* Get the default lifetime to be used if heuristic freshness calculation is
* not possible.
*/
public long getHeuristicDefaultLifetime() {
return heuristicDefaultLifetime;
}
/**
* Sets default lifetime in seconds to be used if heuristic freshness
* calculation is not possible. Explicit cache control directives on
* either the request or origin response will override this, as will
* the heuristic {@code Last-Modified} freshness calculation if it is
* available.
* @param heuristicDefaultLifetimeSecs is the number of seconds to
* consider a cache-eligible response fresh in the absence of other
* information. Set this to {@code 0} to disable this style of
* heuristic caching.
*
* @deprecated (4.3) use {@link Builder}.
*/
@Deprecated
public void setHeuristicDefaultLifetime(final long heuristicDefaultLifetimeSecs) {
this.heuristicDefaultLifetime = heuristicDefaultLifetimeSecs;
}
/**
* Returns whether the cache will behave as a shared cache or not.
* @return {@code true} for a shared cache, {@code false} for a non-
* shared (private) cache
*/
public boolean isSharedCache() {
return isSharedCache;
}
/**
* Sets whether the cache should behave as a shared cache or not.
* @param isSharedCache true to behave as a shared cache, false to
* behave as a non-shared (private) cache. To have the cache
* behave like a browser cache, you want to set this to {@code false}.
*
* @deprecated (4.3) use {@link Builder}.
*/
@Deprecated
public void setSharedCache(final boolean isSharedCache) {
this.isSharedCache = isSharedCache;
}
/**
* Returns the maximum number of threads to allow for background
* revalidations due to the {@code stale-while-revalidate} directive. A
* value of 0 means background revalidations are disabled.
*/
public int getAsynchronousWorkersMax() {
return asynchronousWorkersMax;
}
/**
* Sets the maximum number of threads to allow for background
* revalidations due to the {@code stale-while-revalidate} directive.
* @param max number of threads; a value of 0 disables background
* revalidations.
*
* @deprecated (4.3) use {@link Builder}.
*/
@Deprecated
public void setAsynchronousWorkersMax(final int max) {
this.asynchronousWorkersMax = max;
}
/**
* Returns the minimum number of threads to keep alive for background
* revalidations due to the {@code stale-while-revalidate} directive.
*/
public int getAsynchronousWorkersCore() {
return asynchronousWorkersCore;
}
/**
* Sets the minimum number of threads to keep alive for background
* revalidations due to the {@code stale-while-revalidate} directive.
* @param min should be greater than zero and less than or equal
* to <code>getAsynchronousWorkersMax()</code>
*
* @deprecated (4.3) use {@link Builder}.
*/
@Deprecated
public void setAsynchronousWorkersCore(final int min) {
this.asynchronousWorkersCore = min;
}
/**
* Returns the current maximum idle lifetime in seconds for a
* background revalidation worker thread. If a worker thread is idle
* for this long, and there are more than the core number of worker
* threads alive, the worker will be reclaimed.
*/
public int getAsynchronousWorkerIdleLifetimeSecs() {
return asynchronousWorkerIdleLifetimeSecs;
}
/**
* Sets the current maximum idle lifetime in seconds for a
* background revalidation worker thread. If a worker thread is idle
* for this long, and there are more than the core number of worker
* threads alive, the worker will be reclaimed.
* @param secs idle lifetime in seconds
*
* @deprecated (4.3) use {@link Builder}.
*/
@Deprecated
public void setAsynchronousWorkerIdleLifetimeSecs(final int secs) {
this.asynchronousWorkerIdleLifetimeSecs = secs;
}
/**
* Returns the current maximum queue size for background revalidations.
*/
public int getRevalidationQueueSize() {
return revalidationQueueSize;
}
/**
* Sets the current maximum queue size for background revalidations.
*
* @deprecated (4.3) use {@link Builder}.
*/
@Deprecated
public void setRevalidationQueueSize(final int size) {
this.revalidationQueueSize = size;
}
@Override
protected CacheConfig clone() throws CloneNotSupportedException {
return (CacheConfig) super.clone();
}
public static Builder custom() {
return new Builder();
}
public static Builder copy(final CacheConfig config) {
Args.notNull(config, "Cache config");
return new Builder()
.setMaxObjectSize(config.getMaxObjectSize())
.setMaxCacheEntries(config.getMaxCacheEntries())
.setMaxUpdateRetries(config.getMaxUpdateRetries())
.setHeuristicCachingEnabled(config.isHeuristicCachingEnabled())
.setHeuristicCoefficient(config.getHeuristicCoefficient())
.setHeuristicDefaultLifetime(config.getHeuristicDefaultLifetime())
.setSharedCache(config.isSharedCache())
.setAsynchronousWorkersMax(config.getAsynchronousWorkersMax())
.setAsynchronousWorkersCore(config.getAsynchronousWorkersCore())
.setAsynchronousWorkerIdleLifetimeSecs(config.getAsynchronousWorkerIdleLifetimeSecs())
.setRevalidationQueueSize(config.getRevalidationQueueSize())
.setNeverCacheHTTP10ResponsesWithQueryString(config.isNeverCacheHTTP10ResponsesWithQuery());
}
public static class Builder {
private long maxObjectSize;
private int maxCacheEntries;
private int maxUpdateRetries;
private boolean allow303Caching;
private boolean weakETagOnPutDeleteAllowed;
private boolean heuristicCachingEnabled;
private float heuristicCoefficient;
private long heuristicDefaultLifetime;
private boolean isSharedCache;
private int asynchronousWorkersMax;
private int asynchronousWorkersCore;
private int asynchronousWorkerIdleLifetimeSecs;
private int revalidationQueueSize;
private boolean neverCacheHTTP10ResponsesWithQuery;
Builder() {
this.maxObjectSize = DEFAULT_MAX_OBJECT_SIZE_BYTES;
this.maxCacheEntries = DEFAULT_MAX_CACHE_ENTRIES;
this.maxUpdateRetries = DEFAULT_MAX_UPDATE_RETRIES;
this.allow303Caching = DEFAULT_303_CACHING_ENABLED;
this.weakETagOnPutDeleteAllowed = DEFAULT_WEAK_ETAG_ON_PUTDELETE_ALLOWED;
this.heuristicCachingEnabled = false;
this.heuristicCoefficient = DEFAULT_HEURISTIC_COEFFICIENT;
this.heuristicDefaultLifetime = DEFAULT_HEURISTIC_LIFETIME;
this.isSharedCache = true;
this.asynchronousWorkersMax = DEFAULT_ASYNCHRONOUS_WORKERS_MAX;
this.asynchronousWorkersCore = DEFAULT_ASYNCHRONOUS_WORKERS_CORE;
this.asynchronousWorkerIdleLifetimeSecs = DEFAULT_ASYNCHRONOUS_WORKER_IDLE_LIFETIME_SECS;
this.revalidationQueueSize = DEFAULT_REVALIDATION_QUEUE_SIZE;
}
/**
* Specifies the maximum response body size that will be eligible for caching.
* @param maxObjectSize size in bytes
*/
public Builder setMaxObjectSize(final long maxObjectSize) {
this.maxObjectSize = maxObjectSize;
return this;
}
/**
* Sets the maximum number of cache entries the cache will retain.
*/
public Builder setMaxCacheEntries(final int maxCacheEntries) {
this.maxCacheEntries = maxCacheEntries;
return this;
}
/**
* Sets the number of times to retry a cache update on failure
*/
public Builder setMaxUpdateRetries(final int maxUpdateRetries) {
this.maxUpdateRetries = maxUpdateRetries;
return this;
}
/**
* Enables or disables 303 caching.
* @param allow303Caching should be {@code true} to
* permit 303 caching, {@code false} to disable it.
*/
public Builder setAllow303Caching(final boolean allow303Caching) {
this.allow303Caching = allow303Caching;
return this;
}
/**
* Allows or disallows weak etags to be used with PUT/DELETE If-Match requests.
* @param weakETagOnPutDeleteAllowed should be {@code true} to
* permit weak etags, {@code false} to reject them.
*/
public Builder setWeakETagOnPutDeleteAllowed(final boolean weakETagOnPutDeleteAllowed) {
this.weakETagOnPutDeleteAllowed = weakETagOnPutDeleteAllowed;
return this;
}
/**
* Enables or disables heuristic caching.
* @param heuristicCachingEnabled should be {@code true} to
* permit heuristic caching, {@code false} to enable it.
*/
public Builder setHeuristicCachingEnabled(final boolean heuristicCachingEnabled) {
this.heuristicCachingEnabled = heuristicCachingEnabled;
return this;
}
/**
* Sets coefficient to be used in heuristic freshness caching. This is
* interpreted as the fraction of the time between the {@code Last-Modified}
* and {@code Date} headers of a cached response during which the cached
* response will be considered heuristically fresh.
* @param heuristicCoefficient should be between {@code 0.0} and
* {@code 1.0}.
*/
public Builder setHeuristicCoefficient(final float heuristicCoefficient) {
this.heuristicCoefficient = heuristicCoefficient;
return this;
}
/**
* Sets default lifetime in seconds to be used if heuristic freshness
* calculation is not possible. Explicit cache control directives on
* either the request or origin response will override this, as will
* the heuristic {@code Last-Modified} freshness calculation if it is
* available.
* @param heuristicDefaultLifetime is the number of seconds to
* consider a cache-eligible response fresh in the absence of other
* information. Set this to {@code 0} to disable this style of
* heuristic caching.
*/
public Builder setHeuristicDefaultLifetime(final long heuristicDefaultLifetime) {
this.heuristicDefaultLifetime = heuristicDefaultLifetime;
return this;
}
/**
* Sets whether the cache should behave as a shared cache or not.
* @param isSharedCache true to behave as a shared cache, false to
* behave as a non-shared (private) cache. To have the cache
* behave like a browser cache, you want to set this to {@code false}.
*/
public Builder setSharedCache(final boolean isSharedCache) {
this.isSharedCache = isSharedCache;
return this;
}
/**
* Sets the maximum number of threads to allow for background
* revalidations due to the {@code stale-while-revalidate} directive.
* @param asynchronousWorkersMax number of threads; a value of 0 disables background
* revalidations.
*/
public Builder setAsynchronousWorkersMax(final int asynchronousWorkersMax) {
this.asynchronousWorkersMax = asynchronousWorkersMax;
return this;
}
/**
* Sets the minimum number of threads to keep alive for background
* revalidations due to the {@code stale-while-revalidate} directive.
* @param asynchronousWorkersCore should be greater than zero and less than or equal
* to <code>getAsynchronousWorkersMax()</code>
*/
public Builder setAsynchronousWorkersCore(final int asynchronousWorkersCore) {
this.asynchronousWorkersCore = asynchronousWorkersCore;
return this;
}
/**
* Sets the current maximum idle lifetime in seconds for a
* background revalidation worker thread. If a worker thread is idle
* for this long, and there are more than the core number of worker
* threads alive, the worker will be reclaimed.
* @param asynchronousWorkerIdleLifetimeSecs idle lifetime in seconds
*/
public Builder setAsynchronousWorkerIdleLifetimeSecs(final int asynchronousWorkerIdleLifetimeSecs) {
this.asynchronousWorkerIdleLifetimeSecs = asynchronousWorkerIdleLifetimeSecs;
return this;
}
/**
* Sets the current maximum queue size for background revalidations.
*/
public Builder setRevalidationQueueSize(final int revalidationQueueSize) {
this.revalidationQueueSize = revalidationQueueSize;
return this;
}
/**
* Sets whether the cache should never cache HTTP 1.0 responses with a query string or not.
* @param neverCacheHTTP10ResponsesWithQuery true to never cache responses with a query
* string, false to cache if explicit cache headers are found. Set this to {@code true}
* to better emulate IE, which also never caches responses, regardless of what caching
* headers may be present.
*/
public Builder setNeverCacheHTTP10ResponsesWithQueryString(
final boolean neverCacheHTTP10ResponsesWithQuery) {
this.neverCacheHTTP10ResponsesWithQuery = neverCacheHTTP10ResponsesWithQuery;
return this;
}
public CacheConfig build() {
return new CacheConfig(
maxObjectSize,
maxCacheEntries,
maxUpdateRetries,
allow303Caching,
weakETagOnPutDeleteAllowed,
heuristicCachingEnabled,
heuristicCoefficient,
heuristicDefaultLifetime,
isSharedCache,
asynchronousWorkersMax,
asynchronousWorkersCore,
asynchronousWorkerIdleLifetimeSecs,
revalidationQueueSize,
neverCacheHTTP10ResponsesWithQuery);
}
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("[maxObjectSize=").append(this.maxObjectSize)
.append(", maxCacheEntries=").append(this.maxCacheEntries)
.append(", maxUpdateRetries=").append(this.maxUpdateRetries)
.append(", 303CachingEnabled=").append(this.allow303Caching)
.append(", weakETagOnPutDeleteAllowed=").append(this.weakETagOnPutDeleteAllowed)
.append(", heuristicCachingEnabled=").append(this.heuristicCachingEnabled)
.append(", heuristicCoefficient=").append(this.heuristicCoefficient)
.append(", heuristicDefaultLifetime=").append(this.heuristicDefaultLifetime)
.append(", isSharedCache=").append(this.isSharedCache)
.append(", asynchronousWorkersMax=").append(this.asynchronousWorkersMax)
.append(", asynchronousWorkersCore=").append(this.asynchronousWorkersCore)
.append(", asynchronousWorkerIdleLifetimeSecs=").append(this.asynchronousWorkerIdleLifetimeSecs)
.append(", revalidationQueueSize=").append(this.revalidationQueueSize)
.append(", neverCacheHTTP10ResponsesWithQuery=").append(this.neverCacheHTTP10ResponsesWithQuery)
.append("]");
return builder.toString();
}
}

View file

@ -1,99 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpEntity;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry;
import ch.boye.httpclientandroidlib.protocol.HTTP;
import ch.boye.httpclientandroidlib.util.Args;
@Immutable
class CacheEntity implements HttpEntity, Serializable {
private static final long serialVersionUID = -3467082284120936233L;
private final HttpCacheEntry cacheEntry;
public CacheEntity(final HttpCacheEntry cacheEntry) {
super();
this.cacheEntry = cacheEntry;
}
public Header getContentType() {
return this.cacheEntry.getFirstHeader(HTTP.CONTENT_TYPE);
}
public Header getContentEncoding() {
return this.cacheEntry.getFirstHeader(HTTP.CONTENT_ENCODING);
}
public boolean isChunked() {
return false;
}
public boolean isRepeatable() {
return true;
}
public long getContentLength() {
return this.cacheEntry.getResource().length();
}
public InputStream getContent() throws IOException {
return this.cacheEntry.getResource().getInputStream();
}
public void writeTo(final OutputStream outstream) throws IOException {
Args.notNull(outstream, "Output stream");
final InputStream instream = this.cacheEntry.getResource().getInputStream();
try {
IOUtils.copy(instream, outstream);
} finally {
instream.close();
}
}
public boolean isStreaming() {
return false;
}
public void consumeContent() throws IOException {
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

View file

@ -1,173 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.ListIterator;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.HttpStatus;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.cache.HeaderConstants;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry;
import ch.boye.httpclientandroidlib.client.cache.Resource;
import ch.boye.httpclientandroidlib.client.cache.ResourceFactory;
import ch.boye.httpclientandroidlib.client.utils.DateUtils;
import ch.boye.httpclientandroidlib.protocol.HTTP;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Update a {@link HttpCacheEntry} with new or updated information based on the latest
* 304 status response from the Server. Use the {@link HttpResponse} to perform
* the update.
*
* @since 4.1
*/
@Immutable
class CacheEntryUpdater {
private final ResourceFactory resourceFactory;
CacheEntryUpdater() {
this(new HeapResourceFactory());
}
CacheEntryUpdater(final ResourceFactory resourceFactory) {
super();
this.resourceFactory = resourceFactory;
}
/**
* Update the entry with the new information from the response. Should only be used for
* 304 responses.
*
* @param requestId
* @param entry The cache Entry to be updated
* @param requestDate When the request was performed
* @param responseDate When the response was gotten
* @param response The HttpResponse from the backend server call
* @return HttpCacheEntry an updated version of the cache entry
* @throws java.io.IOException if something bad happens while trying to read the body from the original entry
*/
public HttpCacheEntry updateCacheEntry(
final String requestId,
final HttpCacheEntry entry,
final Date requestDate,
final Date responseDate,
final HttpResponse response) throws IOException {
Args.check(response.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_MODIFIED,
"Response must have 304 status code");
final Header[] mergedHeaders = mergeHeaders(entry, response);
Resource resource = null;
if (entry.getResource() != null) {
resource = resourceFactory.copy(requestId, entry.getResource());
}
return new HttpCacheEntry(
requestDate,
responseDate,
entry.getStatusLine(),
mergedHeaders,
resource);
}
protected Header[] mergeHeaders(final HttpCacheEntry entry, final HttpResponse response) {
if (entryAndResponseHaveDateHeader(entry, response)
&& entryDateHeaderNewerThenResponse(entry, response)) {
// Don't merge headers, keep the entry's headers as they are newer.
return entry.getAllHeaders();
}
final List<Header> cacheEntryHeaderList = new ArrayList<Header>(Arrays.asList(entry
.getAllHeaders()));
removeCacheHeadersThatMatchResponse(cacheEntryHeaderList, response);
removeCacheEntry1xxWarnings(cacheEntryHeaderList, entry);
cacheEntryHeaderList.addAll(Arrays.asList(response.getAllHeaders()));
return cacheEntryHeaderList.toArray(new Header[cacheEntryHeaderList.size()]);
}
private void removeCacheHeadersThatMatchResponse(final List<Header> cacheEntryHeaderList,
final HttpResponse response) {
for (final Header responseHeader : response.getAllHeaders()) {
final ListIterator<Header> cacheEntryHeaderListIter = cacheEntryHeaderList.listIterator();
while (cacheEntryHeaderListIter.hasNext()) {
final String cacheEntryHeaderName = cacheEntryHeaderListIter.next().getName();
if (cacheEntryHeaderName.equals(responseHeader.getName())) {
cacheEntryHeaderListIter.remove();
}
}
}
}
private void removeCacheEntry1xxWarnings(final List<Header> cacheEntryHeaderList, final HttpCacheEntry entry) {
final ListIterator<Header> cacheEntryHeaderListIter = cacheEntryHeaderList.listIterator();
while (cacheEntryHeaderListIter.hasNext()) {
final String cacheEntryHeaderName = cacheEntryHeaderListIter.next().getName();
if (HeaderConstants.WARNING.equals(cacheEntryHeaderName)) {
for (final Header cacheEntryWarning : entry.getHeaders(HeaderConstants.WARNING)) {
if (cacheEntryWarning.getValue().startsWith("1")) {
cacheEntryHeaderListIter.remove();
}
}
}
}
}
private boolean entryDateHeaderNewerThenResponse(final HttpCacheEntry entry, final HttpResponse response) {
final Date entryDate = DateUtils.parseDate(entry.getFirstHeader(HTTP.DATE_HEADER)
.getValue());
final Date responseDate = DateUtils.parseDate(response.getFirstHeader(HTTP.DATE_HEADER)
.getValue());
if (entryDate == null || responseDate == null) {
return false;
}
if (!entryDate.after(responseDate)) {
return false;
}
return true;
}
private boolean entryAndResponseHaveDateHeader(final HttpCacheEntry entry, final HttpResponse response) {
if (entry.getFirstHeader(HTTP.DATE_HEADER) != null
&& response.getFirstHeader(HTTP.DATE_HEADER) != null) {
return true;
}
return false;
}
}

View file

@ -1,288 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
/* LogFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpHost;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.cache.HeaderConstants;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheInvalidator;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheStorage;
import ch.boye.httpclientandroidlib.client.utils.DateUtils;
import ch.boye.httpclientandroidlib.protocol.HTTP;
/**
* Given a particular HttpRequest, flush any cache entries that this request
* would invalidate.
*
* @since 4.1
*/
@Immutable
class CacheInvalidator implements HttpCacheInvalidator {
private final HttpCacheStorage storage;
private final CacheKeyGenerator cacheKeyGenerator;
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
/**
* Create a new {@link CacheInvalidator} for a given {@link HttpCache} and
* {@link CacheKeyGenerator}.
*
* @param uriExtractor Provides identifiers for the keys to store cache entries
* @param storage the cache to store items away in
*/
public CacheInvalidator(
final CacheKeyGenerator uriExtractor,
final HttpCacheStorage storage) {
this.cacheKeyGenerator = uriExtractor;
this.storage = storage;
}
/**
* Remove cache entries from the cache that are no longer fresh or
* have been invalidated in some way.
*
* @param host The backend host we are talking to
* @param req The HttpRequest to that host
*/
public void flushInvalidatedCacheEntries(final HttpHost host, final HttpRequest req) {
if (requestShouldNotBeCached(req)) {
log.debug("Request should not be cached");
final String theUri = cacheKeyGenerator.getURI(host, req);
final HttpCacheEntry parent = getEntry(theUri);
log.debug("parent entry: " + parent);
if (parent != null) {
for (final String variantURI : parent.getVariantMap().values()) {
flushEntry(variantURI);
}
flushEntry(theUri);
}
final URL reqURL = getAbsoluteURL(theUri);
if (reqURL == null) {
log.error("Couldn't transform request into valid URL");
return;
}
final Header clHdr = req.getFirstHeader("Content-Location");
if (clHdr != null) {
final String contentLocation = clHdr.getValue();
if (!flushAbsoluteUriFromSameHost(reqURL, contentLocation)) {
flushRelativeUriFromSameHost(reqURL, contentLocation);
}
}
final Header lHdr = req.getFirstHeader("Location");
if (lHdr != null) {
flushAbsoluteUriFromSameHost(reqURL, lHdr.getValue());
}
}
}
private void flushEntry(final String uri) {
try {
storage.removeEntry(uri);
} catch (final IOException ioe) {
log.warn("unable to flush cache entry", ioe);
}
}
private HttpCacheEntry getEntry(final String theUri) {
try {
return storage.getEntry(theUri);
} catch (final IOException ioe) {
log.warn("could not retrieve entry from storage", ioe);
}
return null;
}
protected void flushUriIfSameHost(final URL requestURL, final URL targetURL) {
final URL canonicalTarget = getAbsoluteURL(cacheKeyGenerator.canonicalizeUri(targetURL.toString()));
if (canonicalTarget == null) {
return;
}
if (canonicalTarget.getAuthority().equalsIgnoreCase(requestURL.getAuthority())) {
flushEntry(canonicalTarget.toString());
}
}
protected void flushRelativeUriFromSameHost(final URL reqURL, final String relUri) {
final URL relURL = getRelativeURL(reqURL, relUri);
if (relURL == null) {
return;
}
flushUriIfSameHost(reqURL, relURL);
}
protected boolean flushAbsoluteUriFromSameHost(final URL reqURL, final String uri) {
final URL absURL = getAbsoluteURL(uri);
if (absURL == null) {
return false;
}
flushUriIfSameHost(reqURL,absURL);
return true;
}
private URL getAbsoluteURL(final String uri) {
URL absURL = null;
try {
absURL = new URL(uri);
} catch (final MalformedURLException mue) {
// nop
}
return absURL;
}
private URL getRelativeURL(final URL reqURL, final String relUri) {
URL relURL = null;
try {
relURL = new URL(reqURL,relUri);
} catch (final MalformedURLException e) {
// nop
}
return relURL;
}
protected boolean requestShouldNotBeCached(final HttpRequest req) {
final String method = req.getRequestLine().getMethod();
return notGetOrHeadRequest(method);
}
private boolean notGetOrHeadRequest(final String method) {
return !(HeaderConstants.GET_METHOD.equals(method) || HeaderConstants.HEAD_METHOD
.equals(method));
}
/** Flushes entries that were invalidated by the given response
* received for the given host/request pair.
*/
public void flushInvalidatedCacheEntries(final HttpHost host,
final HttpRequest request, final HttpResponse response) {
final int status = response.getStatusLine().getStatusCode();
if (status < 200 || status > 299) {
return;
}
final URL reqURL = getAbsoluteURL(cacheKeyGenerator.getURI(host, request));
if (reqURL == null) {
return;
}
final URL contentLocation = getContentLocationURL(reqURL, response);
if (contentLocation != null) {
flushLocationCacheEntry(reqURL, response, contentLocation);
}
final URL location = getLocationURL(reqURL, response);
if (location != null) {
flushLocationCacheEntry(reqURL, response, location);
}
}
private void flushLocationCacheEntry(final URL reqURL,
final HttpResponse response, final URL location) {
final String cacheKey = cacheKeyGenerator.canonicalizeUri(location.toString());
final HttpCacheEntry entry = getEntry(cacheKey);
if (entry == null) {
return;
}
// do not invalidate if response is strictly older than entry
// or if the etags match
if (responseDateOlderThanEntryDate(response, entry)) {
return;
}
if (!responseAndEntryEtagsDiffer(response, entry)) {
return;
}
flushUriIfSameHost(reqURL, location);
}
private URL getContentLocationURL(final URL reqURL, final HttpResponse response) {
final Header clHeader = response.getFirstHeader("Content-Location");
if (clHeader == null) {
return null;
}
final String contentLocation = clHeader.getValue();
final URL canonURL = getAbsoluteURL(contentLocation);
if (canonURL != null) {
return canonURL;
}
return getRelativeURL(reqURL, contentLocation);
}
private URL getLocationURL(final URL reqURL, final HttpResponse response) {
final Header clHeader = response.getFirstHeader("Location");
if (clHeader == null) {
return null;
}
final String location = clHeader.getValue();
final URL canonURL = getAbsoluteURL(location);
if (canonURL != null) {
return canonURL;
}
return getRelativeURL(reqURL, location);
}
private boolean responseAndEntryEtagsDiffer(final HttpResponse response,
final HttpCacheEntry entry) {
final Header entryEtag = entry.getFirstHeader(HeaderConstants.ETAG);
final Header responseEtag = response.getFirstHeader(HeaderConstants.ETAG);
if (entryEtag == null || responseEtag == null) {
return false;
}
return (!entryEtag.getValue().equals(responseEtag.getValue()));
}
private boolean responseDateOlderThanEntryDate(final HttpResponse response,
final HttpCacheEntry entry) {
final Header entryDateHeader = entry.getFirstHeader(HTTP.DATE_HEADER);
final Header responseDateHeader = response.getFirstHeader(HTTP.DATE_HEADER);
if (entryDateHeader == null || responseDateHeader == null) {
/* be conservative; should probably flush */
return false;
}
final Date entryDate = DateUtils.parseDate(entryDateHeader.getValue());
final Date responseDate = DateUtils.parseDate(responseDateHeader.getValue());
if (entryDate == null || responseDate == null) {
return false;
}
return responseDate.before(entryDate);
}
}

View file

@ -1,178 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import ch.boye.httpclientandroidlib.Consts;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HeaderElement;
import ch.boye.httpclientandroidlib.HttpHost;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.cache.HeaderConstants;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry;
import ch.boye.httpclientandroidlib.client.utils.URIUtils;
/**
* @since 4.1
*/
@Immutable
class CacheKeyGenerator {
private static final URI BASE_URI = URI.create("http://example.com/");
/**
* For a given {@link HttpHost} and {@link HttpRequest} get a URI from the
* pair that I can use as an identifier KEY into my HttpCache
*
* @param host The host for this request
* @param req the {@link HttpRequest}
* @return String the extracted URI
*/
public String getURI(final HttpHost host, final HttpRequest req) {
if (isRelativeRequest(req)) {
return canonicalizeUri(String.format("%s%s", host.toString(), req.getRequestLine().getUri()));
}
return canonicalizeUri(req.getRequestLine().getUri());
}
public String canonicalizeUri(final String uri) {
try {
final URI normalized = URIUtils.resolve(BASE_URI, uri);
final URL u = new URL(normalized.toASCIIString());
final String protocol = u.getProtocol();
final String hostname = u.getHost();
final int port = canonicalizePort(u.getPort(), protocol);
final String path = u.getPath();
final String query = u.getQuery();
final String file = (query != null) ? (path + "?" + query) : path;
final URL out = new URL(protocol, hostname, port, file);
return out.toString();
} catch (final IllegalArgumentException e) {
return uri;
} catch (final MalformedURLException e) {
return uri;
}
}
private int canonicalizePort(final int port, final String protocol) {
if (port == -1 && "http".equalsIgnoreCase(protocol)) {
return 80;
} else if (port == -1 && "https".equalsIgnoreCase(protocol)) {
return 443;
}
return port;
}
private boolean isRelativeRequest(final HttpRequest req) {
final String requestUri = req.getRequestLine().getUri();
return ("*".equals(requestUri) || requestUri.startsWith("/"));
}
protected String getFullHeaderValue(final Header[] headers) {
if (headers == null) {
return "";
}
final StringBuilder buf = new StringBuilder("");
boolean first = true;
for (final Header hdr : headers) {
if (!first) {
buf.append(", ");
}
buf.append(hdr.getValue().trim());
first = false;
}
return buf.toString();
}
/**
* For a given {@link HttpHost} and {@link HttpRequest} if the request has a
* VARY header - I need to get an additional URI from the pair of host and
* request so that I can also store the variant into my HttpCache.
*
* @param host The host for this request
* @param req the {@link HttpRequest}
* @param entry the parent entry used to track the variants
* @return String the extracted variant URI
*/
public String getVariantURI(final HttpHost host, final HttpRequest req, final HttpCacheEntry entry) {
if (!entry.hasVariants()) {
return getURI(host, req);
}
return getVariantKey(req, entry) + getURI(host, req);
}
/**
* Compute a "variant key" from the headers of a given request that are
* covered by the Vary header of a given cache entry. Any request whose
* varying headers match those of this request should have the same
* variant key.
* @param req originating request
* @param entry cache entry in question that has variants
* @return a <code>String</code> variant key
*/
public String getVariantKey(final HttpRequest req, final HttpCacheEntry entry) {
final List<String> variantHeaderNames = new ArrayList<String>();
for (final Header varyHdr : entry.getHeaders(HeaderConstants.VARY)) {
for (final HeaderElement elt : varyHdr.getElements()) {
variantHeaderNames.add(elt.getName());
}
}
Collections.sort(variantHeaderNames);
StringBuilder buf;
try {
buf = new StringBuilder("{");
boolean first = true;
for (final String headerName : variantHeaderNames) {
if (!first) {
buf.append("&");
}
buf.append(URLEncoder.encode(headerName, Consts.UTF_8.name()));
buf.append("=");
buf.append(URLEncoder.encode(getFullHeaderValue(req.getHeaders(headerName)),
Consts.UTF_8.name()));
first = false;
}
buf.append("}");
} catch (final UnsupportedEncodingException uee) {
throw new RuntimeException("couldn't encode to UTF-8", uee);
}
return buf.toString();
}
}

View file

@ -1,50 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.util.LinkedHashMap;
import java.util.Map;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry;
final class CacheMap extends LinkedHashMap<String, HttpCacheEntry> {
private static final long serialVersionUID = -7750025207539768511L;
private final int maxEntries;
CacheMap(final int maxEntries) {
super(20, 0.75f, true);
this.maxEntries = maxEntries;
}
@Override
protected boolean removeEldestEntry(final Map.Entry<String, HttpCacheEntry> eldest) {
return size() > this.maxEntries;
}
}

View file

@ -1,320 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.util.Date;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HeaderElement;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.cache.HeaderConstants;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry;
import ch.boye.httpclientandroidlib.client.utils.DateUtils;
import ch.boye.httpclientandroidlib.protocol.HTTP;
/**
* @since 4.1
*/
@Immutable
class CacheValidityPolicy {
public static final long MAX_AGE = 2147483648L;
CacheValidityPolicy() {
super();
}
public long getCurrentAgeSecs(final HttpCacheEntry entry, final Date now) {
return getCorrectedInitialAgeSecs(entry) + getResidentTimeSecs(entry, now);
}
public long getFreshnessLifetimeSecs(final HttpCacheEntry entry) {
final long maxage = getMaxAge(entry);
if (maxage > -1) {
return maxage;
}
final Date dateValue = entry.getDate();
if (dateValue == null) {
return 0L;
}
final Date expiry = getExpirationDate(entry);
if (expiry == null) {
return 0;
}
final long diff = expiry.getTime() - dateValue.getTime();
return (diff / 1000);
}
public boolean isResponseFresh(final HttpCacheEntry entry, final Date now) {
return (getCurrentAgeSecs(entry, now) < getFreshnessLifetimeSecs(entry));
}
/**
* Decides if this response is fresh enough based Last-Modified and Date, if available.
* This entry is meant to be used when isResponseFresh returns false. The algorithm is as follows:
*
* if last-modified and date are defined, freshness lifetime is coefficient*(date-lastModified),
* else freshness lifetime is defaultLifetime
*
* @param entry the cache entry
* @param now what time is it currently (When is right NOW)
* @param coefficient Part of the heuristic for cache entry freshness
* @param defaultLifetime How long can I assume a cache entry is default TTL
* @return {@code true} if the response is fresh
*/
public boolean isResponseHeuristicallyFresh(final HttpCacheEntry entry,
final Date now, final float coefficient, final long defaultLifetime) {
return (getCurrentAgeSecs(entry, now) < getHeuristicFreshnessLifetimeSecs(entry, coefficient, defaultLifetime));
}
public long getHeuristicFreshnessLifetimeSecs(final HttpCacheEntry entry,
final float coefficient, final long defaultLifetime) {
final Date dateValue = entry.getDate();
final Date lastModifiedValue = getLastModifiedValue(entry);
if (dateValue != null && lastModifiedValue != null) {
final long diff = dateValue.getTime() - lastModifiedValue.getTime();
if (diff < 0) {
return 0;
}
return (long)(coefficient * (diff / 1000));
}
return defaultLifetime;
}
public boolean isRevalidatable(final HttpCacheEntry entry) {
return entry.getFirstHeader(HeaderConstants.ETAG) != null
|| entry.getFirstHeader(HeaderConstants.LAST_MODIFIED) != null;
}
public boolean mustRevalidate(final HttpCacheEntry entry) {
return hasCacheControlDirective(entry, HeaderConstants.CACHE_CONTROL_MUST_REVALIDATE);
}
public boolean proxyRevalidate(final HttpCacheEntry entry) {
return hasCacheControlDirective(entry, HeaderConstants.CACHE_CONTROL_PROXY_REVALIDATE);
}
public boolean mayReturnStaleWhileRevalidating(final HttpCacheEntry entry, final Date now) {
for (final Header h : entry.getHeaders(HeaderConstants.CACHE_CONTROL)) {
for(final HeaderElement elt : h.getElements()) {
if (HeaderConstants.STALE_WHILE_REVALIDATE.equalsIgnoreCase(elt.getName())) {
try {
final int allowedStalenessLifetime = Integer.parseInt(elt.getValue());
if (getStalenessSecs(entry, now) <= allowedStalenessLifetime) {
return true;
}
} catch (final NumberFormatException nfe) {
// skip malformed directive
}
}
}
}
return false;
}
public boolean mayReturnStaleIfError(final HttpRequest request,
final HttpCacheEntry entry, final Date now) {
final long stalenessSecs = getStalenessSecs(entry, now);
return mayReturnStaleIfError(request.getHeaders(HeaderConstants.CACHE_CONTROL),
stalenessSecs)
|| mayReturnStaleIfError(entry.getHeaders(HeaderConstants.CACHE_CONTROL),
stalenessSecs);
}
private boolean mayReturnStaleIfError(final Header[] headers, final long stalenessSecs) {
boolean result = false;
for(final Header h : headers) {
for(final HeaderElement elt : h.getElements()) {
if (HeaderConstants.STALE_IF_ERROR.equals(elt.getName())) {
try {
final int staleIfErrorSecs = Integer.parseInt(elt.getValue());
if (stalenessSecs <= staleIfErrorSecs) {
result = true;
break;
}
} catch (final NumberFormatException nfe) {
// skip malformed directive
}
}
}
}
return result;
}
/**
* @deprecated (4.3) use {@link HttpCacheEntry#getDate()}.
* @param entry
* @return the Date of the entry
*/
@Deprecated
protected Date getDateValue(final HttpCacheEntry entry) {
return entry.getDate();
}
protected Date getLastModifiedValue(final HttpCacheEntry entry) {
final Header dateHdr = entry.getFirstHeader(HeaderConstants.LAST_MODIFIED);
if (dateHdr == null) {
return null;
}
return DateUtils.parseDate(dateHdr.getValue());
}
protected long getContentLengthValue(final HttpCacheEntry entry) {
final Header cl = entry.getFirstHeader(HTTP.CONTENT_LEN);
if (cl == null) {
return -1;
}
try {
return Long.parseLong(cl.getValue());
} catch (final NumberFormatException ex) {
return -1;
}
}
protected boolean hasContentLengthHeader(final HttpCacheEntry entry) {
return null != entry.getFirstHeader(HTTP.CONTENT_LEN);
}
/**
* This matters for deciding whether the cache entry is valid to serve as a
* response. If these values do not match, we might have a partial response
*
* @param entry The cache entry we are currently working with
* @return boolean indicating whether actual length matches Content-Length
*/
protected boolean contentLengthHeaderMatchesActualLength(final HttpCacheEntry entry) {
return !hasContentLengthHeader(entry) || getContentLengthValue(entry) == entry.getResource().length();
}
protected long getApparentAgeSecs(final HttpCacheEntry entry) {
final Date dateValue = entry.getDate();
if (dateValue == null) {
return MAX_AGE;
}
final long diff = entry.getResponseDate().getTime() - dateValue.getTime();
if (diff < 0L) {
return 0;
}
return (diff / 1000);
}
protected long getAgeValue(final HttpCacheEntry entry) {
long ageValue = 0;
for (final Header hdr : entry.getHeaders(HeaderConstants.AGE)) {
long hdrAge;
try {
hdrAge = Long.parseLong(hdr.getValue());
if (hdrAge < 0) {
hdrAge = MAX_AGE;
}
} catch (final NumberFormatException nfe) {
hdrAge = MAX_AGE;
}
ageValue = (hdrAge > ageValue) ? hdrAge : ageValue;
}
return ageValue;
}
protected long getCorrectedReceivedAgeSecs(final HttpCacheEntry entry) {
final long apparentAge = getApparentAgeSecs(entry);
final long ageValue = getAgeValue(entry);
return (apparentAge > ageValue) ? apparentAge : ageValue;
}
protected long getResponseDelaySecs(final HttpCacheEntry entry) {
final long diff = entry.getResponseDate().getTime() - entry.getRequestDate().getTime();
return (diff / 1000L);
}
protected long getCorrectedInitialAgeSecs(final HttpCacheEntry entry) {
return getCorrectedReceivedAgeSecs(entry) + getResponseDelaySecs(entry);
}
protected long getResidentTimeSecs(final HttpCacheEntry entry, final Date now) {
final long diff = now.getTime() - entry.getResponseDate().getTime();
return (diff / 1000L);
}
protected long getMaxAge(final HttpCacheEntry entry) {
long maxage = -1;
for (final Header hdr : entry.getHeaders(HeaderConstants.CACHE_CONTROL)) {
for (final HeaderElement elt : hdr.getElements()) {
if (HeaderConstants.CACHE_CONTROL_MAX_AGE.equals(elt.getName())
|| "s-maxage".equals(elt.getName())) {
try {
final long currMaxAge = Long.parseLong(elt.getValue());
if (maxage == -1 || currMaxAge < maxage) {
maxage = currMaxAge;
}
} catch (final NumberFormatException nfe) {
// be conservative if can't parse
maxage = 0;
}
}
}
}
return maxage;
}
protected Date getExpirationDate(final HttpCacheEntry entry) {
final Header expiresHeader = entry.getFirstHeader(HeaderConstants.EXPIRES);
if (expiresHeader == null) {
return null;
}
return DateUtils.parseDate(expiresHeader.getValue());
}
public boolean hasCacheControlDirective(final HttpCacheEntry entry,
final String directive) {
for (final Header h : entry.getHeaders(HeaderConstants.CACHE_CONTROL)) {
for(final HeaderElement elt : h.getElements()) {
if (directive.equalsIgnoreCase(elt.getName())) {
return true;
}
}
}
return false;
}
public long getStalenessSecs(final HttpCacheEntry entry, final Date now) {
final long age = getCurrentAgeSecs(entry, now);
final long freshness = getFreshnessLifetimeSecs(entry);
if (age <= freshness) {
return 0L;
}
return (age - freshness);
}
}

View file

@ -1,96 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
/* LogFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HeaderElement;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.HttpVersion;
import ch.boye.httpclientandroidlib.ProtocolVersion;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.cache.HeaderConstants;
/**
* Determines if an HttpRequest is allowed to be served from the cache.
*
* @since 4.1
*/
@Immutable
class CacheableRequestPolicy {
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
/**
* Determines if an HttpRequest can be served from the cache.
*
* @param request
* an HttpRequest
* @return boolean Is it possible to serve this request from cache
*/
public boolean isServableFromCache(final HttpRequest request) {
final String method = request.getRequestLine().getMethod();
final ProtocolVersion pv = request.getRequestLine().getProtocolVersion();
if (HttpVersion.HTTP_1_1.compareToVersion(pv) != 0) {
log.trace("non-HTTP/1.1 request was not serveable from cache");
return false;
}
if (!method.equals(HeaderConstants.GET_METHOD)) {
log.trace("non-GET request was not serveable from cache");
return false;
}
if (request.getHeaders(HeaderConstants.PRAGMA).length > 0) {
log.trace("request with Pragma header was not serveable from cache");
return false;
}
final Header[] cacheControlHeaders = request.getHeaders(HeaderConstants.CACHE_CONTROL);
for (final Header cacheControl : cacheControlHeaders) {
for (final HeaderElement cacheControlElement : cacheControl.getElements()) {
if (HeaderConstants.CACHE_CONTROL_NO_STORE.equalsIgnoreCase(cacheControlElement
.getName())) {
log.trace("Request with no-store was not serveable from cache");
return false;
}
if (HeaderConstants.CACHE_CONTROL_NO_CACHE.equalsIgnoreCase(cacheControlElement
.getName())) {
log.trace("Request with no-cache was not serveable from cache");
return false;
}
}
}
log.trace("Request was serveable from cache");
return true;
}
}

View file

@ -1,166 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.util.Date;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpEntity;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.HttpStatus;
import ch.boye.httpclientandroidlib.HttpVersion;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.cache.HeaderConstants;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry;
import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
import ch.boye.httpclientandroidlib.client.utils.DateUtils;
import ch.boye.httpclientandroidlib.message.BasicHeader;
import ch.boye.httpclientandroidlib.message.BasicHttpResponse;
import ch.boye.httpclientandroidlib.protocol.HTTP;
/**
* Rebuilds an {@link HttpResponse} from a {@link net.sf.ehcache.CacheEntry}
*
* @since 4.1
*/
@Immutable
class CachedHttpResponseGenerator {
private final CacheValidityPolicy validityStrategy;
CachedHttpResponseGenerator(final CacheValidityPolicy validityStrategy) {
super();
this.validityStrategy = validityStrategy;
}
CachedHttpResponseGenerator() {
this(new CacheValidityPolicy());
}
/**
* If I was able to use a {@link CacheEntity} to response to the {@link ch.boye.httpclientandroidlib.HttpRequest} then
* generate an {@link HttpResponse} based on the cache entry.
* @param entry
* {@link CacheEntity} to transform into an {@link HttpResponse}
* @return {@link HttpResponse} that was constructed
*/
CloseableHttpResponse generateResponse(final HttpCacheEntry entry) {
final Date now = new Date();
final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, entry
.getStatusCode(), entry.getReasonPhrase());
response.setHeaders(entry.getAllHeaders());
if (entry.getResource() != null) {
final HttpEntity entity = new CacheEntity(entry);
addMissingContentLengthHeader(response, entity);
response.setEntity(entity);
}
final long age = this.validityStrategy.getCurrentAgeSecs(entry, now);
if (age > 0) {
if (age >= Integer.MAX_VALUE) {
response.setHeader(HeaderConstants.AGE, "2147483648");
} else {
response.setHeader(HeaderConstants.AGE, "" + ((int) age));
}
}
return Proxies.enhanceResponse(response);
}
/**
* Generate a 304 - Not Modified response from a {@link CacheEntity}. This should be
* used to respond to conditional requests, when the entry exists or has been re-validated.
*/
CloseableHttpResponse generateNotModifiedResponse(final HttpCacheEntry entry) {
final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
HttpStatus.SC_NOT_MODIFIED, "Not Modified");
// The response MUST include the following headers
// (http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)
// - Date, unless its omission is required by section 14.8.1
Header dateHeader = entry.getFirstHeader(HTTP.DATE_HEADER);
if (dateHeader == null) {
dateHeader = new BasicHeader(HTTP.DATE_HEADER, DateUtils.formatDate(new Date()));
}
response.addHeader(dateHeader);
// - ETag and/or Content-Location, if the header would have been sent
// in a 200 response to the same request
final Header etagHeader = entry.getFirstHeader(HeaderConstants.ETAG);
if (etagHeader != null) {
response.addHeader(etagHeader);
}
final Header contentLocationHeader = entry.getFirstHeader("Content-Location");
if (contentLocationHeader != null) {
response.addHeader(contentLocationHeader);
}
// - Expires, Cache-Control, and/or Vary, if the field-value might
// differ from that sent in any previous response for the same
// variant
final Header expiresHeader = entry.getFirstHeader(HeaderConstants.EXPIRES);
if (expiresHeader != null) {
response.addHeader(expiresHeader);
}
final Header cacheControlHeader = entry.getFirstHeader(HeaderConstants.CACHE_CONTROL);
if (cacheControlHeader != null) {
response.addHeader(cacheControlHeader);
}
final Header varyHeader = entry.getFirstHeader(HeaderConstants.VARY);
if (varyHeader != null) {
response.addHeader(varyHeader);
}
return Proxies.enhanceResponse(response);
}
private void addMissingContentLengthHeader(final HttpResponse response, final HttpEntity entity) {
if (transferEncodingIsPresent(response)) {
return;
}
Header contentLength = response.getFirstHeader(HTTP.CONTENT_LEN);
if (contentLength == null) {
contentLength = new BasicHeader(HTTP.CONTENT_LEN, Long.toString(entity
.getContentLength()));
response.setHeader(contentLength);
}
}
private boolean transferEncodingIsPresent(final HttpResponse response) {
final Header hdr = response.getFirstHeader(HTTP.TRANSFER_ENCODING);
return hdr != null;
}
}

View file

@ -1,346 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.util.Date;
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
/* LogFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HeaderElement;
import ch.boye.httpclientandroidlib.HttpHost;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.HttpStatus;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.cache.HeaderConstants;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry;
import ch.boye.httpclientandroidlib.client.utils.DateUtils;
/**
* Determines whether a given {@link HttpCacheEntry} is suitable to be
* used as a response for a given {@link HttpRequest}.
*
* @since 4.1
*/
@Immutable
class CachedResponseSuitabilityChecker {
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
private final boolean sharedCache;
private final boolean useHeuristicCaching;
private final float heuristicCoefficient;
private final long heuristicDefaultLifetime;
private final CacheValidityPolicy validityStrategy;
CachedResponseSuitabilityChecker(final CacheValidityPolicy validityStrategy,
final CacheConfig config) {
super();
this.validityStrategy = validityStrategy;
this.sharedCache = config.isSharedCache();
this.useHeuristicCaching = config.isHeuristicCachingEnabled();
this.heuristicCoefficient = config.getHeuristicCoefficient();
this.heuristicDefaultLifetime = config.getHeuristicDefaultLifetime();
}
CachedResponseSuitabilityChecker(final CacheConfig config) {
this(new CacheValidityPolicy(), config);
}
private boolean isFreshEnough(final HttpCacheEntry entry, final HttpRequest request, final Date now) {
if (validityStrategy.isResponseFresh(entry, now)) {
return true;
}
if (useHeuristicCaching &&
validityStrategy.isResponseHeuristicallyFresh(entry, now, heuristicCoefficient, heuristicDefaultLifetime)) {
return true;
}
if (originInsistsOnFreshness(entry)) {
return false;
}
final long maxstale = getMaxStale(request);
if (maxstale == -1) {
return false;
}
return (maxstale > validityStrategy.getStalenessSecs(entry, now));
}
private boolean originInsistsOnFreshness(final HttpCacheEntry entry) {
if (validityStrategy.mustRevalidate(entry)) {
return true;
}
if (!sharedCache) {
return false;
}
return validityStrategy.proxyRevalidate(entry) ||
validityStrategy.hasCacheControlDirective(entry, "s-maxage");
}
private long getMaxStale(final HttpRequest request) {
long maxstale = -1;
for(final Header h : request.getHeaders(HeaderConstants.CACHE_CONTROL)) {
for(final HeaderElement elt : h.getElements()) {
if (HeaderConstants.CACHE_CONTROL_MAX_STALE.equals(elt.getName())) {
if ((elt.getValue() == null || "".equals(elt.getValue().trim()))
&& maxstale == -1) {
maxstale = Long.MAX_VALUE;
} else {
try {
long val = Long.parseLong(elt.getValue());
if (val < 0) {
val = 0;
}
if (maxstale == -1 || val < maxstale) {
maxstale = val;
}
} catch (final NumberFormatException nfe) {
// err on the side of preserving semantic transparency
maxstale = 0;
}
}
}
}
}
return maxstale;
}
/**
* Determine if I can utilize a {@link HttpCacheEntry} to respond to the given
* {@link HttpRequest}
*
* @param host
* {@link HttpHost}
* @param request
* {@link HttpRequest}
* @param entry
* {@link HttpCacheEntry}
* @param now
* Right now in time
* @return boolean yes/no answer
*/
public boolean canCachedResponseBeUsed(final HttpHost host, final HttpRequest request, final HttpCacheEntry entry, final Date now) {
if (!isFreshEnough(entry, request, now)) {
log.trace("Cache entry was not fresh enough");
return false;
}
if (!validityStrategy.contentLengthHeaderMatchesActualLength(entry)) {
log.debug("Cache entry Content-Length and header information do not match");
return false;
}
if (hasUnsupportedConditionalHeaders(request)) {
log.debug("Request contained conditional headers we don't handle");
return false;
}
if (!isConditional(request) && entry.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
return false;
}
if (isConditional(request) && !allConditionalsMatch(request, entry, now)) {
return false;
}
for (final Header ccHdr : request.getHeaders(HeaderConstants.CACHE_CONTROL)) {
for (final HeaderElement elt : ccHdr.getElements()) {
if (HeaderConstants.CACHE_CONTROL_NO_CACHE.equals(elt.getName())) {
log.trace("Response contained NO CACHE directive, cache was not suitable");
return false;
}
if (HeaderConstants.CACHE_CONTROL_NO_STORE.equals(elt.getName())) {
log.trace("Response contained NO STORE directive, cache was not suitable");
return false;
}
if (HeaderConstants.CACHE_CONTROL_MAX_AGE.equals(elt.getName())) {
try {
final int maxage = Integer.parseInt(elt.getValue());
if (validityStrategy.getCurrentAgeSecs(entry, now) > maxage) {
log.trace("Response from cache was NOT suitable due to max age");
return false;
}
} catch (final NumberFormatException ex) {
// err conservatively
log.debug("Response from cache was malformed" + ex.getMessage());
return false;
}
}
if (HeaderConstants.CACHE_CONTROL_MAX_STALE.equals(elt.getName())) {
try {
final int maxstale = Integer.parseInt(elt.getValue());
if (validityStrategy.getFreshnessLifetimeSecs(entry) > maxstale) {
log.trace("Response from cache was not suitable due to Max stale freshness");
return false;
}
} catch (final NumberFormatException ex) {
// err conservatively
log.debug("Response from cache was malformed: " + ex.getMessage());
return false;
}
}
if (HeaderConstants.CACHE_CONTROL_MIN_FRESH.equals(elt.getName())) {
try {
final long minfresh = Long.parseLong(elt.getValue());
if (minfresh < 0L) {
return false;
}
final long age = validityStrategy.getCurrentAgeSecs(entry, now);
final long freshness = validityStrategy.getFreshnessLifetimeSecs(entry);
if (freshness - age < minfresh) {
log.trace("Response from cache was not suitable due to min fresh " +
"freshness requirement");
return false;
}
} catch (final NumberFormatException ex) {
// err conservatively
log.debug("Response from cache was malformed: " + ex.getMessage());
return false;
}
}
}
}
log.trace("Response from cache was suitable");
return true;
}
/**
* Is this request the type of conditional request we support?
* @param request The current httpRequest being made
* @return {@code true} if the request is supported
*/
public boolean isConditional(final HttpRequest request) {
return hasSupportedEtagValidator(request) || hasSupportedLastModifiedValidator(request);
}
/**
* Check that conditionals that are part of this request match
* @param request The current httpRequest being made
* @param entry the cache entry
* @param now right NOW in time
* @return {@code true} if the request matches all conditionals
*/
public boolean allConditionalsMatch(final HttpRequest request, final HttpCacheEntry entry, final Date now) {
final boolean hasEtagValidator = hasSupportedEtagValidator(request);
final boolean hasLastModifiedValidator = hasSupportedLastModifiedValidator(request);
final boolean etagValidatorMatches = (hasEtagValidator) && etagValidatorMatches(request, entry);
final boolean lastModifiedValidatorMatches = (hasLastModifiedValidator) && lastModifiedValidatorMatches(request, entry, now);
if ((hasEtagValidator && hasLastModifiedValidator)
&& !(etagValidatorMatches && lastModifiedValidatorMatches)) {
return false;
} else if (hasEtagValidator && !etagValidatorMatches) {
return false;
}
if (hasLastModifiedValidator && !lastModifiedValidatorMatches) {
return false;
}
return true;
}
private boolean hasUnsupportedConditionalHeaders(final HttpRequest request) {
return (request.getFirstHeader(HeaderConstants.IF_RANGE) != null
|| request.getFirstHeader(HeaderConstants.IF_MATCH) != null
|| hasValidDateField(request, HeaderConstants.IF_UNMODIFIED_SINCE));
}
private boolean hasSupportedEtagValidator(final HttpRequest request) {
return request.containsHeader(HeaderConstants.IF_NONE_MATCH);
}
private boolean hasSupportedLastModifiedValidator(final HttpRequest request) {
return hasValidDateField(request, HeaderConstants.IF_MODIFIED_SINCE);
}
/**
* Check entry against If-None-Match
* @param request The current httpRequest being made
* @param entry the cache entry
* @return boolean does the etag validator match
*/
private boolean etagValidatorMatches(final HttpRequest request, final HttpCacheEntry entry) {
final Header etagHeader = entry.getFirstHeader(HeaderConstants.ETAG);
final String etag = (etagHeader != null) ? etagHeader.getValue() : null;
final Header[] ifNoneMatch = request.getHeaders(HeaderConstants.IF_NONE_MATCH);
if (ifNoneMatch != null) {
for (final Header h : ifNoneMatch) {
for (final HeaderElement elt : h.getElements()) {
final String reqEtag = elt.toString();
if (("*".equals(reqEtag) && etag != null)
|| reqEtag.equals(etag)) {
return true;
}
}
}
}
return false;
}
/**
* Check entry against If-Modified-Since, if If-Modified-Since is in the future it is invalid as per
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
* @param request The current httpRequest being made
* @param entry the cache entry
* @param now right NOW in time
* @return boolean Does the last modified header match
*/
private boolean lastModifiedValidatorMatches(final HttpRequest request, final HttpCacheEntry entry, final Date now) {
final Header lastModifiedHeader = entry.getFirstHeader(HeaderConstants.LAST_MODIFIED);
Date lastModified = null;
if (lastModifiedHeader != null) {
lastModified = DateUtils.parseDate(lastModifiedHeader.getValue());
}
if (lastModified == null) {
return false;
}
for (final Header h : request.getHeaders(HeaderConstants.IF_MODIFIED_SINCE)) {
final Date ifModifiedSince = DateUtils.parseDate(h.getValue());
if (ifModifiedSince != null) {
if (ifModifiedSince.after(now) || lastModified.after(ifModifiedSince)) {
return false;
}
}
}
return true;
}
private boolean hasValidDateField(final HttpRequest request, final String headerName) {
for(final Header h : request.getHeaders(headerName)) {
final Date date = DateUtils.parseDate(h.getValue());
return date != null;
}
return false;
}
}

View file

@ -1,870 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
/* LogFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HeaderElement;
import ch.boye.httpclientandroidlib.HttpException;
import ch.boye.httpclientandroidlib.HttpHost;
import ch.boye.httpclientandroidlib.HttpMessage;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.HttpStatus;
import ch.boye.httpclientandroidlib.HttpVersion;
import ch.boye.httpclientandroidlib.ProtocolVersion;
import ch.boye.httpclientandroidlib.RequestLine;
import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
import ch.boye.httpclientandroidlib.client.cache.CacheResponseStatus;
import ch.boye.httpclientandroidlib.client.cache.HeaderConstants;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheContext;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheStorage;
import ch.boye.httpclientandroidlib.client.cache.ResourceFactory;
import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware;
import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper;
import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
import ch.boye.httpclientandroidlib.client.utils.DateUtils;
import ch.boye.httpclientandroidlib.conn.routing.HttpRoute;
import ch.boye.httpclientandroidlib.impl.execchain.ClientExecChain;
import ch.boye.httpclientandroidlib.message.BasicHttpResponse;
import ch.boye.httpclientandroidlib.protocol.HTTP;
import ch.boye.httpclientandroidlib.protocol.HttpContext;
import ch.boye.httpclientandroidlib.util.Args;
import ch.boye.httpclientandroidlib.util.VersionInfo;
/**
* Request executor in the request execution chain that is responsible for
* transparent client-side caching. The current implementation is conditionally
* compliant with HTTP/1.1 (meaning all the MUST and MUST NOTs are obeyed),
* although quite a lot, though not all, of the SHOULDs and SHOULD NOTs
* are obeyed too.
* <p/>
* Folks that would like to experiment with alternative storage backends
* should look at the {@link HttpCacheStorage} interface and the related
* package documentation there. You may also be interested in the provided
* {@link ch.boye.httpclientandroidlib.impl.client.cache.ehcache.EhcacheHttpCacheStorage
* EhCache} and {@link
* ch.boye.httpclientandroidlib.impl.client.cache.memcached.MemcachedHttpCacheStorage
* memcached} storage backends.
* <p/>
* Further responsibilities such as communication with the opposite
* endpoint is delegated to the next executor in the request execution
* chain.
*
* @since 4.3
*/
@ThreadSafe // So long as the responseCache implementation is threadsafe
public class CachingExec implements ClientExecChain {
private final static boolean SUPPORTS_RANGE_AND_CONTENT_RANGE_HEADERS = false;
private final AtomicLong cacheHits = new AtomicLong();
private final AtomicLong cacheMisses = new AtomicLong();
private final AtomicLong cacheUpdates = new AtomicLong();
private final Map<ProtocolVersion, String> viaHeaders = new HashMap<ProtocolVersion, String>(4);
private final CacheConfig cacheConfig;
private final ClientExecChain backend;
private final HttpCache responseCache;
private final CacheValidityPolicy validityPolicy;
private final CachedHttpResponseGenerator responseGenerator;
private final CacheableRequestPolicy cacheableRequestPolicy;
private final CachedResponseSuitabilityChecker suitabilityChecker;
private final ConditionalRequestBuilder conditionalRequestBuilder;
private final ResponseProtocolCompliance responseCompliance;
private final RequestProtocolCompliance requestCompliance;
private final ResponseCachingPolicy responseCachingPolicy;
private final AsynchronousValidator asynchRevalidator;
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
public CachingExec(
final ClientExecChain backend,
final HttpCache cache,
final CacheConfig config) {
this(backend, cache, config, null);
}
public CachingExec(
final ClientExecChain backend,
final HttpCache cache,
final CacheConfig config,
final AsynchronousValidator asynchRevalidator) {
super();
Args.notNull(backend, "HTTP backend");
Args.notNull(cache, "HttpCache");
this.cacheConfig = config != null ? config : CacheConfig.DEFAULT;
this.backend = backend;
this.responseCache = cache;
this.validityPolicy = new CacheValidityPolicy();
this.responseGenerator = new CachedHttpResponseGenerator(this.validityPolicy);
this.cacheableRequestPolicy = new CacheableRequestPolicy();
this.suitabilityChecker = new CachedResponseSuitabilityChecker(this.validityPolicy, this.cacheConfig);
this.conditionalRequestBuilder = new ConditionalRequestBuilder();
this.responseCompliance = new ResponseProtocolCompliance();
this.requestCompliance = new RequestProtocolCompliance(this.cacheConfig.isWeakETagOnPutDeleteAllowed());
this.responseCachingPolicy = new ResponseCachingPolicy(
this.cacheConfig.getMaxObjectSize(), this.cacheConfig.isSharedCache(),
this.cacheConfig.isNeverCacheHTTP10ResponsesWithQuery(), this.cacheConfig.is303CachingEnabled());
this.asynchRevalidator = asynchRevalidator;
}
public CachingExec(
final ClientExecChain backend,
final ResourceFactory resourceFactory,
final HttpCacheStorage storage,
final CacheConfig config) {
this(backend, new BasicHttpCache(resourceFactory, storage, config), config);
}
public CachingExec(final ClientExecChain backend) {
this(backend, new BasicHttpCache(), CacheConfig.DEFAULT);
}
CachingExec(
final ClientExecChain backend,
final HttpCache responseCache,
final CacheValidityPolicy validityPolicy,
final ResponseCachingPolicy responseCachingPolicy,
final CachedHttpResponseGenerator responseGenerator,
final CacheableRequestPolicy cacheableRequestPolicy,
final CachedResponseSuitabilityChecker suitabilityChecker,
final ConditionalRequestBuilder conditionalRequestBuilder,
final ResponseProtocolCompliance responseCompliance,
final RequestProtocolCompliance requestCompliance,
final CacheConfig config,
final AsynchronousValidator asynchRevalidator) {
this.cacheConfig = config != null ? config : CacheConfig.DEFAULT;
this.backend = backend;
this.responseCache = responseCache;
this.validityPolicy = validityPolicy;
this.responseCachingPolicy = responseCachingPolicy;
this.responseGenerator = responseGenerator;
this.cacheableRequestPolicy = cacheableRequestPolicy;
this.suitabilityChecker = suitabilityChecker;
this.conditionalRequestBuilder = conditionalRequestBuilder;
this.responseCompliance = responseCompliance;
this.requestCompliance = requestCompliance;
this.asynchRevalidator = asynchRevalidator;
}
/**
* Reports the number of times that the cache successfully responded
* to an {@link HttpRequest} without contacting the origin server.
* @return the number of cache hits
*/
public long getCacheHits() {
return cacheHits.get();
}
/**
* Reports the number of times that the cache contacted the origin
* server because it had no appropriate response cached.
* @return the number of cache misses
*/
public long getCacheMisses() {
return cacheMisses.get();
}
/**
* Reports the number of times that the cache was able to satisfy
* a response by revalidating an existing but stale cache entry.
* @return the number of cache revalidations
*/
public long getCacheUpdates() {
return cacheUpdates.get();
}
public CloseableHttpResponse execute(
final HttpRoute route,
final HttpRequestWrapper request) throws IOException, HttpException {
return execute(route, request, HttpClientContext.create(), null);
}
public CloseableHttpResponse execute(
final HttpRoute route,
final HttpRequestWrapper request,
final HttpClientContext context) throws IOException, HttpException {
return execute(route, request, context, null);
}
public CloseableHttpResponse execute(
final HttpRoute route,
final HttpRequestWrapper request,
final HttpClientContext context,
final HttpExecutionAware execAware) throws IOException, HttpException {
final HttpHost target = context.getTargetHost();
final String via = generateViaHeader(request.getOriginal());
// default response context
setResponseStatus(context, CacheResponseStatus.CACHE_MISS);
if (clientRequestsOurOptions(request)) {
setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE);
return Proxies.enhanceResponse(new OptionsHttp11Response());
}
final HttpResponse fatalErrorResponse = getFatallyNoncompliantResponse(request, context);
if (fatalErrorResponse != null) {
return Proxies.enhanceResponse(fatalErrorResponse);
}
requestCompliance.makeRequestCompliant(request);
request.addHeader("Via",via);
flushEntriesInvalidatedByRequest(context.getTargetHost(), request);
if (!cacheableRequestPolicy.isServableFromCache(request)) {
log.debug("Request is not servable from cache");
return callBackend(route, request, context, execAware);
}
final HttpCacheEntry entry = satisfyFromCache(target, request);
if (entry == null) {
log.debug("Cache miss");
return handleCacheMiss(route, request, context, execAware);
} else {
return handleCacheHit(route, request, context, execAware, entry);
}
}
private CloseableHttpResponse handleCacheHit(
final HttpRoute route,
final HttpRequestWrapper request,
final HttpClientContext context,
final HttpExecutionAware execAware,
final HttpCacheEntry entry) throws IOException, HttpException {
final HttpHost target = context.getTargetHost();
recordCacheHit(target, request);
CloseableHttpResponse out = null;
final Date now = getCurrentDate();
if (suitabilityChecker.canCachedResponseBeUsed(target, request, entry, now)) {
log.debug("Cache hit");
out = generateCachedResponse(request, context, entry, now);
} else if (!mayCallBackend(request)) {
log.debug("Cache entry not suitable but only-if-cached requested");
out = generateGatewayTimeout(context);
} else if (!(entry.getStatusCode() == HttpStatus.SC_NOT_MODIFIED
&& !suitabilityChecker.isConditional(request))) {
log.debug("Revalidating cache entry");
return revalidateCacheEntry(route, request, context, execAware, entry, now);
} else {
log.debug("Cache entry not usable; calling backend");
return callBackend(route, request, context, execAware);
}
context.setAttribute(HttpClientContext.HTTP_ROUTE, route);
context.setAttribute(HttpClientContext.HTTP_TARGET_HOST, target);
context.setAttribute(HttpClientContext.HTTP_REQUEST, request);
context.setAttribute(HttpClientContext.HTTP_RESPONSE, out);
context.setAttribute(HttpClientContext.HTTP_REQ_SENT, Boolean.TRUE);
return out;
}
private CloseableHttpResponse revalidateCacheEntry(
final HttpRoute route,
final HttpRequestWrapper request,
final HttpClientContext context,
final HttpExecutionAware execAware,
final HttpCacheEntry entry,
final Date now) throws HttpException {
try {
if (asynchRevalidator != null
&& !staleResponseNotAllowed(request, entry, now)
&& validityPolicy.mayReturnStaleWhileRevalidating(entry, now)) {
log.trace("Serving stale with asynchronous revalidation");
final CloseableHttpResponse resp = generateCachedResponse(request, context, entry, now);
asynchRevalidator.revalidateCacheEntry(this, route, request, context, execAware, entry);
return resp;
}
return revalidateCacheEntry(route, request, context, execAware, entry);
} catch (final IOException ioex) {
return handleRevalidationFailure(request, context, entry, now);
}
}
private CloseableHttpResponse handleCacheMiss(
final HttpRoute route,
final HttpRequestWrapper request,
final HttpClientContext context,
final HttpExecutionAware execAware) throws IOException, HttpException {
final HttpHost target = context.getTargetHost();
recordCacheMiss(target, request);
if (!mayCallBackend(request)) {
return Proxies.enhanceResponse(
new BasicHttpResponse(
HttpVersion.HTTP_1_1, HttpStatus.SC_GATEWAY_TIMEOUT, "Gateway Timeout"));
}
final Map<String, Variant> variants = getExistingCacheVariants(target, request);
if (variants != null && variants.size() > 0) {
return negotiateResponseFromVariants(route, request, context,
execAware, variants);
}
return callBackend(route, request, context, execAware);
}
private HttpCacheEntry satisfyFromCache(
final HttpHost target, final HttpRequestWrapper request) {
HttpCacheEntry entry = null;
try {
entry = responseCache.getCacheEntry(target, request);
} catch (final IOException ioe) {
log.warn("Unable to retrieve entries from cache", ioe);
}
return entry;
}
private HttpResponse getFatallyNoncompliantResponse(
final HttpRequestWrapper request,
final HttpContext context) {
HttpResponse fatalErrorResponse = null;
final List<RequestProtocolError> fatalError = requestCompliance.requestIsFatallyNonCompliant(request);
for (final RequestProtocolError error : fatalError) {
setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE);
fatalErrorResponse = requestCompliance.getErrorForRequest(error);
}
return fatalErrorResponse;
}
private Map<String, Variant> getExistingCacheVariants(
final HttpHost target,
final HttpRequestWrapper request) {
Map<String,Variant> variants = null;
try {
variants = responseCache.getVariantCacheEntriesWithEtags(target, request);
} catch (final IOException ioe) {
log.warn("Unable to retrieve variant entries from cache", ioe);
}
return variants;
}
private void recordCacheMiss(final HttpHost target, final HttpRequestWrapper request) {
cacheMisses.getAndIncrement();
if (log.isTraceEnabled()) {
final RequestLine rl = request.getRequestLine();
log.trace("Cache miss [host: " + target + "; uri: " + rl.getUri() + "]");
}
}
private void recordCacheHit(final HttpHost target, final HttpRequestWrapper request) {
cacheHits.getAndIncrement();
if (log.isTraceEnabled()) {
final RequestLine rl = request.getRequestLine();
log.trace("Cache hit [host: " + target + "; uri: " + rl.getUri() + "]");
}
}
private void recordCacheUpdate(final HttpContext context) {
cacheUpdates.getAndIncrement();
setResponseStatus(context, CacheResponseStatus.VALIDATED);
}
private void flushEntriesInvalidatedByRequest(
final HttpHost target,
final HttpRequestWrapper request) {
try {
responseCache.flushInvalidatedCacheEntriesFor(target, request);
} catch (final IOException ioe) {
log.warn("Unable to flush invalidated entries from cache", ioe);
}
}
private CloseableHttpResponse generateCachedResponse(final HttpRequestWrapper request,
final HttpContext context, final HttpCacheEntry entry, final Date now) {
final CloseableHttpResponse cachedResponse;
if (request.containsHeader(HeaderConstants.IF_NONE_MATCH)
|| request.containsHeader(HeaderConstants.IF_MODIFIED_SINCE)) {
cachedResponse = responseGenerator.generateNotModifiedResponse(entry);
} else {
cachedResponse = responseGenerator.generateResponse(entry);
}
setResponseStatus(context, CacheResponseStatus.CACHE_HIT);
if (validityPolicy.getStalenessSecs(entry, now) > 0L) {
cachedResponse.addHeader(HeaderConstants.WARNING,"110 localhost \"Response is stale\"");
}
return cachedResponse;
}
private CloseableHttpResponse handleRevalidationFailure(
final HttpRequestWrapper request,
final HttpContext context,
final HttpCacheEntry entry,
final Date now) {
if (staleResponseNotAllowed(request, entry, now)) {
return generateGatewayTimeout(context);
} else {
return unvalidatedCacheHit(context, entry);
}
}
private CloseableHttpResponse generateGatewayTimeout(
final HttpContext context) {
setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE);
return Proxies.enhanceResponse(new BasicHttpResponse(
HttpVersion.HTTP_1_1, HttpStatus.SC_GATEWAY_TIMEOUT,
"Gateway Timeout"));
}
private CloseableHttpResponse unvalidatedCacheHit(
final HttpContext context, final HttpCacheEntry entry) {
final CloseableHttpResponse cachedResponse = responseGenerator.generateResponse(entry);
setResponseStatus(context, CacheResponseStatus.CACHE_HIT);
cachedResponse.addHeader(HeaderConstants.WARNING, "111 localhost \"Revalidation failed\"");
return cachedResponse;
}
private boolean staleResponseNotAllowed(
final HttpRequestWrapper request,
final HttpCacheEntry entry,
final Date now) {
return validityPolicy.mustRevalidate(entry)
|| (cacheConfig.isSharedCache() && validityPolicy.proxyRevalidate(entry))
|| explicitFreshnessRequest(request, entry, now);
}
private boolean mayCallBackend(final HttpRequestWrapper request) {
for (final Header h: request.getHeaders(HeaderConstants.CACHE_CONTROL)) {
for (final HeaderElement elt : h.getElements()) {
if ("only-if-cached".equals(elt.getName())) {
log.trace("Request marked only-if-cached");
return false;
}
}
}
return true;
}
private boolean explicitFreshnessRequest(
final HttpRequestWrapper request,
final HttpCacheEntry entry,
final Date now) {
for(final Header h : request.getHeaders(HeaderConstants.CACHE_CONTROL)) {
for(final HeaderElement elt : h.getElements()) {
if (HeaderConstants.CACHE_CONTROL_MAX_STALE.equals(elt.getName())) {
try {
final int maxstale = Integer.parseInt(elt.getValue());
final long age = validityPolicy.getCurrentAgeSecs(entry, now);
final long lifetime = validityPolicy.getFreshnessLifetimeSecs(entry);
if (age - lifetime > maxstale) {
return true;
}
} catch (final NumberFormatException nfe) {
return true;
}
} else if (HeaderConstants.CACHE_CONTROL_MIN_FRESH.equals(elt.getName())
|| HeaderConstants.CACHE_CONTROL_MAX_AGE.equals(elt.getName())) {
return true;
}
}
}
return false;
}
private String generateViaHeader(final HttpMessage msg) {
final ProtocolVersion pv = msg.getProtocolVersion();
final String existingEntry = viaHeaders.get(pv);
if (existingEntry != null) {
return existingEntry;
}
final VersionInfo vi = VersionInfo.loadVersionInfo("ch.boye.httpclientandroidlib.client", getClass().getClassLoader());
final String release = (vi != null) ? vi.getRelease() : VersionInfo.UNAVAILABLE;
String value;
if ("http".equalsIgnoreCase(pv.getProtocol())) {
value = String.format("%d.%d localhost (Apache-HttpClient/%s (cache))", pv.getMajor(), pv.getMinor(),
release);
} else {
value = String.format("%s/%d.%d localhost (Apache-HttpClient/%s (cache))", pv.getProtocol(), pv.getMajor(),
pv.getMinor(), release);
}
viaHeaders.put(pv, value);
return value;
}
private void setResponseStatus(final HttpContext context, final CacheResponseStatus value) {
if (context != null) {
context.setAttribute(HttpCacheContext.CACHE_RESPONSE_STATUS, value);
}
}
/**
* Reports whether this {@code CachingHttpClient} implementation
* supports byte-range requests as specified by the {@code Range}
* and {@code Content-Range} headers.
* @return {@code true} if byte-range requests are supported
*/
public boolean supportsRangeAndContentRangeHeaders() {
return SUPPORTS_RANGE_AND_CONTENT_RANGE_HEADERS;
}
Date getCurrentDate() {
return new Date();
}
boolean clientRequestsOurOptions(final HttpRequest request) {
final RequestLine line = request.getRequestLine();
if (!HeaderConstants.OPTIONS_METHOD.equals(line.getMethod())) {
return false;
}
if (!"*".equals(line.getUri())) {
return false;
}
if (!"0".equals(request.getFirstHeader(HeaderConstants.MAX_FORWARDS).getValue())) {
return false;
}
return true;
}
CloseableHttpResponse callBackend(
final HttpRoute route,
final HttpRequestWrapper request,
final HttpClientContext context,
final HttpExecutionAware execAware) throws IOException, HttpException {
final Date requestDate = getCurrentDate();
log.trace("Calling the backend");
final CloseableHttpResponse backendResponse = backend.execute(route, request, context, execAware);
try {
backendResponse.addHeader("Via", generateViaHeader(backendResponse));
return handleBackendResponse(route, request, context, execAware,
requestDate, getCurrentDate(), backendResponse);
} catch (final IOException ex) {
backendResponse.close();
throw ex;
} catch (final RuntimeException ex) {
backendResponse.close();
throw ex;
}
}
private boolean revalidationResponseIsTooOld(final HttpResponse backendResponse,
final HttpCacheEntry cacheEntry) {
final Header entryDateHeader = cacheEntry.getFirstHeader(HTTP.DATE_HEADER);
final Header responseDateHeader = backendResponse.getFirstHeader(HTTP.DATE_HEADER);
if (entryDateHeader != null && responseDateHeader != null) {
final Date entryDate = DateUtils.parseDate(entryDateHeader.getValue());
final Date respDate = DateUtils.parseDate(responseDateHeader.getValue());
if (entryDate == null || respDate == null) {
// either backend response or cached entry did not have a valid
// Date header, so we can't tell if they are out of order
// according to the origin clock; thus we can skip the
// unconditional retry recommended in 13.2.6 of RFC 2616.
return false;
}
if (respDate.before(entryDate)) {
return true;
}
}
return false;
}
CloseableHttpResponse negotiateResponseFromVariants(
final HttpRoute route,
final HttpRequestWrapper request,
final HttpClientContext context,
final HttpExecutionAware execAware,
final Map<String, Variant> variants) throws IOException, HttpException {
final HttpRequestWrapper conditionalRequest = conditionalRequestBuilder
.buildConditionalRequestFromVariants(request, variants);
final Date requestDate = getCurrentDate();
final CloseableHttpResponse backendResponse = backend.execute(
route, conditionalRequest, context, execAware);
try {
final Date responseDate = getCurrentDate();
backendResponse.addHeader("Via", generateViaHeader(backendResponse));
if (backendResponse.getStatusLine().getStatusCode() != HttpStatus.SC_NOT_MODIFIED) {
return handleBackendResponse(
route, request, context, execAware,
requestDate, responseDate, backendResponse);
}
final Header resultEtagHeader = backendResponse.getFirstHeader(HeaderConstants.ETAG);
if (resultEtagHeader == null) {
log.warn("304 response did not contain ETag");
IOUtils.consume(backendResponse.getEntity());
backendResponse.close();
return callBackend(route, request, context, execAware);
}
final String resultEtag = resultEtagHeader.getValue();
final Variant matchingVariant = variants.get(resultEtag);
if (matchingVariant == null) {
log.debug("304 response did not contain ETag matching one sent in If-None-Match");
IOUtils.consume(backendResponse.getEntity());
backendResponse.close();
return callBackend(route, request, context, execAware);
}
final HttpCacheEntry matchedEntry = matchingVariant.getEntry();
if (revalidationResponseIsTooOld(backendResponse, matchedEntry)) {
IOUtils.consume(backendResponse.getEntity());
backendResponse.close();
return retryRequestUnconditionally(route, request, context, execAware, matchedEntry);
}
recordCacheUpdate(context);
final HttpCacheEntry responseEntry = getUpdatedVariantEntry(
context.getTargetHost(), conditionalRequest, requestDate, responseDate,
backendResponse, matchingVariant, matchedEntry);
backendResponse.close();
final CloseableHttpResponse resp = responseGenerator.generateResponse(responseEntry);
tryToUpdateVariantMap(context.getTargetHost(), request, matchingVariant);
if (shouldSendNotModifiedResponse(request, responseEntry)) {
return responseGenerator.generateNotModifiedResponse(responseEntry);
}
return resp;
} catch (final IOException ex) {
backendResponse.close();
throw ex;
} catch (final RuntimeException ex) {
backendResponse.close();
throw ex;
}
}
private CloseableHttpResponse retryRequestUnconditionally(
final HttpRoute route,
final HttpRequestWrapper request,
final HttpClientContext context,
final HttpExecutionAware execAware,
final HttpCacheEntry matchedEntry) throws IOException, HttpException {
final HttpRequestWrapper unconditional = conditionalRequestBuilder
.buildUnconditionalRequest(request, matchedEntry);
return callBackend(route, unconditional, context, execAware);
}
private HttpCacheEntry getUpdatedVariantEntry(
final HttpHost target,
final HttpRequestWrapper conditionalRequest,
final Date requestDate,
final Date responseDate,
final CloseableHttpResponse backendResponse,
final Variant matchingVariant,
final HttpCacheEntry matchedEntry) throws IOException {
HttpCacheEntry responseEntry = matchedEntry;
try {
responseEntry = responseCache.updateVariantCacheEntry(target, conditionalRequest,
matchedEntry, backendResponse, requestDate, responseDate, matchingVariant.getCacheKey());
} catch (final IOException ioe) {
log.warn("Could not update cache entry", ioe);
} finally {
backendResponse.close();
}
return responseEntry;
}
private void tryToUpdateVariantMap(
final HttpHost target,
final HttpRequestWrapper request,
final Variant matchingVariant) {
try {
responseCache.reuseVariantEntryFor(target, request, matchingVariant);
} catch (final IOException ioe) {
log.warn("Could not update cache entry to reuse variant", ioe);
}
}
private boolean shouldSendNotModifiedResponse(
final HttpRequestWrapper request,
final HttpCacheEntry responseEntry) {
return (suitabilityChecker.isConditional(request)
&& suitabilityChecker.allConditionalsMatch(request, responseEntry, new Date()));
}
CloseableHttpResponse revalidateCacheEntry(
final HttpRoute route,
final HttpRequestWrapper request,
final HttpClientContext context,
final HttpExecutionAware execAware,
final HttpCacheEntry cacheEntry) throws IOException, HttpException {
final HttpRequestWrapper conditionalRequest = conditionalRequestBuilder.buildConditionalRequest(request, cacheEntry);
Date requestDate = getCurrentDate();
CloseableHttpResponse backendResponse = backend.execute(
route, conditionalRequest, context, execAware);
Date responseDate = getCurrentDate();
if (revalidationResponseIsTooOld(backendResponse, cacheEntry)) {
backendResponse.close();
final HttpRequestWrapper unconditional = conditionalRequestBuilder
.buildUnconditionalRequest(request, cacheEntry);
requestDate = getCurrentDate();
backendResponse = backend.execute(route, unconditional, context, execAware);
responseDate = getCurrentDate();
}
backendResponse.addHeader(HeaderConstants.VIA, generateViaHeader(backendResponse));
final int statusCode = backendResponse.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_NOT_MODIFIED || statusCode == HttpStatus.SC_OK) {
recordCacheUpdate(context);
}
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
final HttpCacheEntry updatedEntry = responseCache.updateCacheEntry(
context.getTargetHost(), request, cacheEntry,
backendResponse, requestDate, responseDate);
if (suitabilityChecker.isConditional(request)
&& suitabilityChecker.allConditionalsMatch(request, updatedEntry, new Date())) {
return responseGenerator
.generateNotModifiedResponse(updatedEntry);
}
return responseGenerator.generateResponse(updatedEntry);
}
if (staleIfErrorAppliesTo(statusCode)
&& !staleResponseNotAllowed(request, cacheEntry, getCurrentDate())
&& validityPolicy.mayReturnStaleIfError(request, cacheEntry, responseDate)) {
try {
final CloseableHttpResponse cachedResponse = responseGenerator.generateResponse(cacheEntry);
cachedResponse.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\"");
return cachedResponse;
} finally {
backendResponse.close();
}
}
return handleBackendResponse(
route, conditionalRequest, context, execAware,
requestDate, responseDate, backendResponse);
}
private boolean staleIfErrorAppliesTo(final int statusCode) {
return statusCode == HttpStatus.SC_INTERNAL_SERVER_ERROR
|| statusCode == HttpStatus.SC_BAD_GATEWAY
|| statusCode == HttpStatus.SC_SERVICE_UNAVAILABLE
|| statusCode == HttpStatus.SC_GATEWAY_TIMEOUT;
}
CloseableHttpResponse handleBackendResponse(
final HttpRoute route,
final HttpRequestWrapper request,
final HttpClientContext context,
final HttpExecutionAware execAware,
final Date requestDate,
final Date responseDate,
final CloseableHttpResponse backendResponse) throws IOException {
log.trace("Handling Backend response");
responseCompliance.ensureProtocolCompliance(request, backendResponse);
final HttpHost target = context.getTargetHost();
final boolean cacheable = responseCachingPolicy.isResponseCacheable(request, backendResponse);
responseCache.flushInvalidatedCacheEntriesFor(target, request, backendResponse);
if (cacheable && !alreadyHaveNewerCacheEntry(target, request, backendResponse)) {
storeRequestIfModifiedSinceFor304Response(request, backendResponse);
return responseCache.cacheAndReturnResponse(target, request,
backendResponse, requestDate, responseDate);
}
if (!cacheable) {
try {
responseCache.flushCacheEntriesFor(target, request);
} catch (final IOException ioe) {
log.warn("Unable to flush invalid cache entries", ioe);
}
}
return backendResponse;
}
/**
* For 304 Not modified responses, adds a "Last-Modified" header with the
* value of the "If-Modified-Since" header passed in the request. This
* header is required to be able to reuse match the cache entry for
* subsequent requests but as defined in http specifications it is not
* included in 304 responses by backend servers. This header will not be
* included in the resulting response.
*/
private void storeRequestIfModifiedSinceFor304Response(
final HttpRequest request, final HttpResponse backendResponse) {
if (backendResponse.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
final Header h = request.getFirstHeader("If-Modified-Since");
if (h != null) {
backendResponse.addHeader("Last-Modified", h.getValue());
}
}
}
private boolean alreadyHaveNewerCacheEntry(final HttpHost target, final HttpRequestWrapper request,
final HttpResponse backendResponse) {
HttpCacheEntry existing = null;
try {
existing = responseCache.getCacheEntry(target, request);
} catch (final IOException ioe) {
// nop
}
if (existing == null) {
return false;
}
final Header entryDateHeader = existing.getFirstHeader(HTTP.DATE_HEADER);
if (entryDateHeader == null) {
return false;
}
final Header responseDateHeader = backendResponse.getFirstHeader(HTTP.DATE_HEADER);
if (responseDateHeader == null) {
return false;
}
final Date entryDate = DateUtils.parseDate(entryDateHeader.getValue());
final Date responseDate = DateUtils.parseDate(responseDateHeader.getValue());
if (entryDate == null || responseDate == null) {
return false;
}
return responseDate.before(entryDate);
}
}

View file

@ -1,149 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.File;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheInvalidator;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheStorage;
import ch.boye.httpclientandroidlib.client.cache.ResourceFactory;
import ch.boye.httpclientandroidlib.impl.client.HttpClientBuilder;
import ch.boye.httpclientandroidlib.impl.execchain.ClientExecChain;
/**
* Builder for {@link ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient}
* instances capable of client-side caching.
*
* @since 4.3
*/
public class CachingHttpClientBuilder extends HttpClientBuilder {
private ResourceFactory resourceFactory;
private HttpCacheStorage storage;
private File cacheDir;
private CacheConfig cacheConfig;
private SchedulingStrategy schedulingStrategy;
private HttpCacheInvalidator httpCacheInvalidator;
public static CachingHttpClientBuilder create() {
return new CachingHttpClientBuilder();
}
protected CachingHttpClientBuilder() {
super();
}
public final CachingHttpClientBuilder setResourceFactory(
final ResourceFactory resourceFactory) {
this.resourceFactory = resourceFactory;
return this;
}
public final CachingHttpClientBuilder setHttpCacheStorage(
final HttpCacheStorage storage) {
this.storage = storage;
return this;
}
public final CachingHttpClientBuilder setCacheDir(
final File cacheDir) {
this.cacheDir = cacheDir;
return this;
}
public final CachingHttpClientBuilder setCacheConfig(
final CacheConfig cacheConfig) {
this.cacheConfig = cacheConfig;
return this;
}
public final CachingHttpClientBuilder setSchedulingStrategy(
final SchedulingStrategy schedulingStrategy) {
this.schedulingStrategy = schedulingStrategy;
return this;
}
public final CachingHttpClientBuilder setHttpCacheInvalidator(
final HttpCacheInvalidator cacheInvalidator) {
this.httpCacheInvalidator = cacheInvalidator;
return this;
}
@Override
protected ClientExecChain decorateMainExec(final ClientExecChain mainExec) {
final CacheConfig config = this.cacheConfig != null ? this.cacheConfig : CacheConfig.DEFAULT;
ResourceFactory resourceFactory = this.resourceFactory;
if (resourceFactory == null) {
if (this.cacheDir == null) {
resourceFactory = new HeapResourceFactory();
} else {
resourceFactory = new FileResourceFactory(cacheDir);
}
}
HttpCacheStorage storage = this.storage;
if (storage == null) {
if (this.cacheDir == null) {
storage = new BasicHttpCacheStorage(config);
} else {
final ManagedHttpCacheStorage managedStorage = new ManagedHttpCacheStorage(config);
addCloseable(managedStorage);
storage = managedStorage;
}
}
final AsynchronousValidator revalidator = createAsynchronousRevalidator(config);
final CacheKeyGenerator uriExtractor = new CacheKeyGenerator();
HttpCacheInvalidator cacheInvalidator = this.httpCacheInvalidator;
if (cacheInvalidator == null) {
cacheInvalidator = new CacheInvalidator(uriExtractor, storage);
}
return new CachingExec(mainExec,
new BasicHttpCache(
resourceFactory,
storage, config,
uriExtractor,
cacheInvalidator), config, revalidator);
}
private AsynchronousValidator createAsynchronousRevalidator(final CacheConfig config) {
if (config.getAsynchronousWorkersMax() > 0) {
final SchedulingStrategy configuredSchedulingStrategy = createSchedulingStrategy(config);
final AsynchronousValidator revalidator = new AsynchronousValidator(
configuredSchedulingStrategy);
addCloseable(revalidator);
return revalidator;
}
return null;
}
@SuppressWarnings("resource")
private SchedulingStrategy createSchedulingStrategy(final CacheConfig config) {
return schedulingStrategy != null ? schedulingStrategy : new ImmediateSchedulingStrategy(config);
}
}

View file

@ -1,74 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.File;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient;
/**
* Factory methods for {@link CloseableHttpClient} instances
* capable of client-side caching.
*
* @since 4.3
*/
@Immutable
public class CachingHttpClients {
private CachingHttpClients() {
super();
}
/**
* Creates builder object for construction of custom
* {@link CloseableHttpClient} instances.
*/
public static CachingHttpClientBuilder custom() {
return CachingHttpClientBuilder.create();
}
/**
* Creates {@link CloseableHttpClient} instance that uses a memory bound
* response cache.
*/
public static CloseableHttpClient createMemoryBound() {
return CachingHttpClientBuilder.create().build();
}
/**
* Creates {@link CloseableHttpClient} instance that uses a file system
* bound response cache.
*
* @param cacheDir location of response cache.
*/
public static CloseableHttpClient createFileBound(final File cacheDir) {
return CachingHttpClientBuilder.create().setCacheDir(cacheDir).build();
}
}

View file

@ -1,104 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.SequenceInputStream;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.client.cache.Resource;
import ch.boye.httpclientandroidlib.entity.AbstractHttpEntity;
import ch.boye.httpclientandroidlib.util.Args;
@NotThreadSafe
class CombinedEntity extends AbstractHttpEntity {
private final Resource resource;
private final InputStream combinedStream;
CombinedEntity(final Resource resource, final InputStream instream) throws IOException {
super();
this.resource = resource;
this.combinedStream = new SequenceInputStream(
new ResourceStream(resource.getInputStream()), instream);
}
public long getContentLength() {
return -1;
}
public boolean isRepeatable() {
return false;
}
public boolean isStreaming() {
return true;
}
public InputStream getContent() throws IOException, IllegalStateException {
return this.combinedStream;
}
public void writeTo(final OutputStream outstream) throws IOException {
Args.notNull(outstream, "Output stream");
final InputStream instream = getContent();
try {
int l;
final byte[] tmp = new byte[2048];
while ((l = instream.read(tmp)) != -1) {
outstream.write(tmp, 0, l);
}
} finally {
instream.close();
}
}
private void dispose() {
this.resource.dispose();
}
class ResourceStream extends FilterInputStream {
protected ResourceStream(final InputStream in) {
super(in);
}
@Override
public void close() throws IOException {
try {
super.close();
} finally {
dispose();
}
}
}
}

View file

@ -1,140 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.util.Map;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HeaderElement;
import ch.boye.httpclientandroidlib.ProtocolException;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.cache.HeaderConstants;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry;
import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper;
/**
* @since 4.1
*/
@Immutable
class ConditionalRequestBuilder {
/**
* When a {@link HttpCacheEntry} is stale but 'might' be used as a response
* to an {@link ch.boye.httpclientandroidlib.HttpRequest} we will attempt to revalidate
* the entry with the origin. Build the origin {@link ch.boye.httpclientandroidlib.HttpRequest}
* here and return it.
*
* @param request the original request from the caller
* @param cacheEntry the entry that needs to be re-validated
* @return the wrapped request
* @throws ProtocolException when I am unable to build a new origin request.
*/
public HttpRequestWrapper buildConditionalRequest(final HttpRequestWrapper request, final HttpCacheEntry cacheEntry)
throws ProtocolException {
final HttpRequestWrapper newRequest = HttpRequestWrapper.wrap(request.getOriginal());
newRequest.setHeaders(request.getAllHeaders());
final Header eTag = cacheEntry.getFirstHeader(HeaderConstants.ETAG);
if (eTag != null) {
newRequest.setHeader(HeaderConstants.IF_NONE_MATCH, eTag.getValue());
}
final Header lastModified = cacheEntry.getFirstHeader(HeaderConstants.LAST_MODIFIED);
if (lastModified != null) {
newRequest.setHeader(HeaderConstants.IF_MODIFIED_SINCE, lastModified.getValue());
}
boolean mustRevalidate = false;
for(final Header h : cacheEntry.getHeaders(HeaderConstants.CACHE_CONTROL)) {
for(final HeaderElement elt : h.getElements()) {
if (HeaderConstants.CACHE_CONTROL_MUST_REVALIDATE.equalsIgnoreCase(elt.getName())
|| HeaderConstants.CACHE_CONTROL_PROXY_REVALIDATE.equalsIgnoreCase(elt.getName())) {
mustRevalidate = true;
break;
}
}
}
if (mustRevalidate) {
newRequest.addHeader(HeaderConstants.CACHE_CONTROL, HeaderConstants.CACHE_CONTROL_MAX_AGE + "=0");
}
return newRequest;
}
/**
* When a {@link HttpCacheEntry} does not exist for a specific
* {@link ch.boye.httpclientandroidlib.HttpRequest} we attempt to see if an existing
* {@link HttpCacheEntry} is appropriate by building a conditional
* {@link ch.boye.httpclientandroidlib.HttpRequest} using the variants' ETag values.
* If no such values exist, the request is unmodified
*
* @param request the original request from the caller
* @param variants
* @return the wrapped request
*/
public HttpRequestWrapper buildConditionalRequestFromVariants(final HttpRequestWrapper request,
final Map<String, Variant> variants) {
final HttpRequestWrapper newRequest = HttpRequestWrapper.wrap(request.getOriginal());
newRequest.setHeaders(request.getAllHeaders());
// we do not support partial content so all etags are used
final StringBuilder etags = new StringBuilder();
boolean first = true;
for(final String etag : variants.keySet()) {
if (!first) {
etags.append(",");
}
first = false;
etags.append(etag);
}
newRequest.setHeader(HeaderConstants.IF_NONE_MATCH, etags.toString());
return newRequest;
}
/**
* Returns a request to unconditionally validate a cache entry with
* the origin. In certain cases (due to multiple intervening caches)
* our cache may actually receive a response to a normal conditional
* validation where the Date header is actually older than that of
* our current cache entry. In this case, the protocol recommendation
* is to retry the validation and force syncup with the origin.
* @param request client request we are trying to satisfy
* @param entry existing cache entry we are trying to validate
* @return an unconditional validation request
*/
public HttpRequestWrapper buildUnconditionalRequest(final HttpRequestWrapper request, final HttpCacheEntry entry) {
final HttpRequestWrapper newRequest = HttpRequestWrapper.wrap(request.getOriginal());
newRequest.setHeaders(request.getAllHeaders());
newRequest.addHeader(HeaderConstants.CACHE_CONTROL,HeaderConstants.CACHE_CONTROL_NO_CACHE);
newRequest.addHeader(HeaderConstants.PRAGMA,HeaderConstants.CACHE_CONTROL_NO_CACHE);
newRequest.removeHeaders(HeaderConstants.IF_RANGE);
newRequest.removeHeaders(HeaderConstants.IF_MATCH);
newRequest.removeHeaders(HeaderConstants.IF_NONE_MATCH);
newRequest.removeHeaders(HeaderConstants.IF_UNMODIFIED_SINCE);
newRequest.removeHeaders(HeaderConstants.IF_MODIFIED_SINCE);
return newRequest;
}
}

View file

@ -1,143 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Implements a bounded failure cache. The oldest entries are discarded when
* the maximum size is exceeded.
*
* @since 4.3
*/
@ThreadSafe
public class DefaultFailureCache implements FailureCache {
static final int DEFAULT_MAX_SIZE = 1000;
static final int MAX_UPDATE_TRIES = 10;
private final int maxSize;
private final ConcurrentMap<String, FailureCacheValue> storage;
/**
* Create a new failure cache with the maximum size of
* {@link #DEFAULT_MAX_SIZE}.
*/
public DefaultFailureCache() {
this(DEFAULT_MAX_SIZE);
}
/**
* Creates a new failure cache with the specified maximum size.
* @param maxSize the maximum number of entries the cache should store
*/
public DefaultFailureCache(final int maxSize) {
this.maxSize = maxSize;
this.storage = new ConcurrentHashMap<String, FailureCacheValue>();
}
public int getErrorCount(final String identifier) {
if (identifier == null) {
throw new IllegalArgumentException("identifier may not be null");
}
final FailureCacheValue storedErrorCode = storage.get(identifier);
return storedErrorCode != null ? storedErrorCode.getErrorCount() : 0;
}
public void resetErrorCount(final String identifier) {
if (identifier == null) {
throw new IllegalArgumentException("identifier may not be null");
}
storage.remove(identifier);
}
public void increaseErrorCount(final String identifier) {
if (identifier == null) {
throw new IllegalArgumentException("identifier may not be null");
}
updateValue(identifier);
removeOldestEntryIfMapSizeExceeded();
}
private void updateValue(final String identifier) {
/**
* Due to concurrency it is possible that someone else is modifying an
* entry before we could write back our updated value. So we keep
* trying until it is our turn.
*
* In case there is a lot of contention on that identifier, a thread
* might starve. Thus it gives up after a certain number of failed
* update tries.
*/
for (int i = 0; i < MAX_UPDATE_TRIES; i++) {
final FailureCacheValue oldValue = storage.get(identifier);
if (oldValue == null) {
final FailureCacheValue newValue = new FailureCacheValue(identifier, 1);
if (storage.putIfAbsent(identifier, newValue) == null) {
return;
}
}
else {
final int errorCount = oldValue.getErrorCount();
if (errorCount == Integer.MAX_VALUE) {
return;
}
final FailureCacheValue newValue = new FailureCacheValue(identifier, errorCount + 1);
if (storage.replace(identifier, oldValue, newValue)) {
return;
}
}
}
}
private void removeOldestEntryIfMapSizeExceeded() {
if (storage.size() > maxSize) {
final FailureCacheValue valueWithOldestTimestamp = findValueWithOldestTimestamp();
if (valueWithOldestTimestamp != null) {
storage.remove(valueWithOldestTimestamp.getKey(), valueWithOldestTimestamp);
}
}
}
private FailureCacheValue findValueWithOldestTimestamp() {
long oldestTimestamp = Long.MAX_VALUE;
FailureCacheValue oldestValue = null;
for (final Map.Entry<String, FailureCacheValue> storageEntry : storage.entrySet()) {
final FailureCacheValue value = storageEntry.getValue();
final long creationTimeInNanos = value.getCreationTimeInNanos();
if (creationTimeInNanos < oldestTimestamp) {
oldestTimestamp = creationTimeInNanos;
oldestValue = storageEntry.getValue();
}
}
return oldestValue;
}
}

View file

@ -1,71 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntrySerializationException;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntrySerializer;
/**
* {@link HttpCacheEntrySerializer} implementation that uses the default (native)
* serialization.
*
* @see java.io.Serializable
*
* @since 4.1
*/
@Immutable
public class DefaultHttpCacheEntrySerializer implements HttpCacheEntrySerializer {
public void writeTo(final HttpCacheEntry cacheEntry, final OutputStream os) throws IOException {
final ObjectOutputStream oos = new ObjectOutputStream(os);
try {
oos.writeObject(cacheEntry);
} finally {
oos.close();
}
}
public HttpCacheEntry readFrom(final InputStream is) throws IOException {
final ObjectInputStream ois = new ObjectInputStream(is);
try {
return (HttpCacheEntry) ois.readObject();
} catch (final ClassNotFoundException ex) {
throw new HttpCacheEntrySerializationException("Class not found: " + ex.getMessage(), ex);
} finally {
ois.close();
}
}
}

View file

@ -1,174 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* An implementation that backs off exponentially based on the number of
* consecutive failed attempts stored in the
* {@link AsynchronousValidationRequest}. It uses the following defaults:
* <pre>
* no delay in case it was never tried or didn't fail so far
* 6 secs delay for one failed attempt (= {@link #getInitialExpiryInMillis()})
* 60 secs delay for two failed attempts
* 10 mins delay for three failed attempts
* 100 mins delay for four failed attempts
* ~16 hours delay for five failed attempts
* 24 hours delay for six or more failed attempts (= {@link #getMaxExpiryInMillis()})
* </pre>
*
* The following equation is used to calculate the delay for a specific revalidation request:
* <pre>
* delay = {@link #getInitialExpiryInMillis()} * Math.pow({@link #getBackOffRate()}, {@link AsynchronousValidationRequest#getConsecutiveFailedAttempts()} - 1))
* </pre>
* The resulting delay won't exceed {@link #getMaxExpiryInMillis()}.
*
* @since 4.3
*/
@ThreadSafe
public class ExponentialBackOffSchedulingStrategy implements SchedulingStrategy {
public static final long DEFAULT_BACK_OFF_RATE = 10;
public static final long DEFAULT_INITIAL_EXPIRY_IN_MILLIS = TimeUnit.SECONDS.toMillis(6);
public static final long DEFAULT_MAX_EXPIRY_IN_MILLIS = TimeUnit.SECONDS.toMillis(86400);
private final long backOffRate;
private final long initialExpiryInMillis;
private final long maxExpiryInMillis;
private final ScheduledExecutorService executor;
/**
* Create a new scheduling strategy using a fixed pool of worker threads.
* @param cacheConfig the thread pool configuration to be used; not <code>null</code>
* @see ch.boye.httpclientandroidlib.impl.client.cache.CacheConfig#getAsynchronousWorkersMax()
* @see #DEFAULT_BACK_OFF_RATE
* @see #DEFAULT_INITIAL_EXPIRY_IN_MILLIS
* @see #DEFAULT_MAX_EXPIRY_IN_MILLIS
*/
public ExponentialBackOffSchedulingStrategy(final CacheConfig cacheConfig) {
this(cacheConfig,
DEFAULT_BACK_OFF_RATE,
DEFAULT_INITIAL_EXPIRY_IN_MILLIS,
DEFAULT_MAX_EXPIRY_IN_MILLIS);
}
/**
* Create a new scheduling strategy by using a fixed pool of worker threads and the
* given parameters to calculated the delay.
*
* @param cacheConfig the thread pool configuration to be used; not <code>null</code>
* @param backOffRate the back off rate to be used; not negative
* @param initialExpiryInMillis the initial expiry in milli seconds; not negative
* @param maxExpiryInMillis the upper limit of the delay in milli seconds; not negative
* @see ch.boye.httpclientandroidlib.impl.client.cache.CacheConfig#getAsynchronousWorkersMax()
* @see ExponentialBackOffSchedulingStrategy
*/
public ExponentialBackOffSchedulingStrategy(
final CacheConfig cacheConfig,
final long backOffRate,
final long initialExpiryInMillis,
final long maxExpiryInMillis) {
this(createThreadPoolFromCacheConfig(cacheConfig),
backOffRate,
initialExpiryInMillis,
maxExpiryInMillis);
}
private static ScheduledThreadPoolExecutor createThreadPoolFromCacheConfig(
final CacheConfig cacheConfig) {
final ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(
cacheConfig.getAsynchronousWorkersMax());
scheduledThreadPoolExecutor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
return scheduledThreadPoolExecutor;
}
ExponentialBackOffSchedulingStrategy(
final ScheduledExecutorService executor,
final long backOffRate,
final long initialExpiryInMillis,
final long maxExpiryInMillis) {
this.executor = checkNotNull("executor", executor);
this.backOffRate = checkNotNegative("backOffRate", backOffRate);
this.initialExpiryInMillis = checkNotNegative("initialExpiryInMillis", initialExpiryInMillis);
this.maxExpiryInMillis = checkNotNegative("maxExpiryInMillis", maxExpiryInMillis);
}
public void schedule(
final AsynchronousValidationRequest revalidationRequest) {
checkNotNull("revalidationRequest", revalidationRequest);
final int consecutiveFailedAttempts = revalidationRequest.getConsecutiveFailedAttempts();
final long delayInMillis = calculateDelayInMillis(consecutiveFailedAttempts);
executor.schedule(revalidationRequest, delayInMillis, TimeUnit.MILLISECONDS);
}
public void close() {
executor.shutdown();
}
public long getBackOffRate() {
return backOffRate;
}
public long getInitialExpiryInMillis() {
return initialExpiryInMillis;
}
public long getMaxExpiryInMillis() {
return maxExpiryInMillis;
}
protected long calculateDelayInMillis(final int consecutiveFailedAttempts) {
if (consecutiveFailedAttempts > 0) {
final long delayInSeconds = (long) (initialExpiryInMillis *
Math.pow(backOffRate, consecutiveFailedAttempts - 1));
return Math.min(delayInSeconds, maxExpiryInMillis);
}
else {
return 0;
}
}
protected static <T> T checkNotNull(final String parameterName, final T value) {
if (value == null) {
throw new IllegalArgumentException(parameterName + " may not be null");
}
return value;
}
protected static long checkNotNegative(final String parameterName, final long value) {
if (value < 0) {
throw new IllegalArgumentException(parameterName + " may not be negative");
}
return value;
}
}

View file

@ -1,57 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
/**
* Increase and reset the number of errors associated with a specific
* identifier.
*
* @since 4.3
*/
public interface FailureCache {
/**
* Get the current error count.
* @param identifier the identifier for which the error count is requested
* @return the currently known error count or zero if there is no record
*/
int getErrorCount(String identifier);
/**
* Reset the error count back to zero.
* @param identifier the identifier for which the error count should be
* reset
*/
void resetErrorCount(String identifier);
/**
* Increases the error count by one.
* @param identifier the identifier for which the error count should be
* increased
*/
void increaseErrorCount(String identifier);
}

View file

@ -1,67 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import ch.boye.httpclientandroidlib.annotation.Immutable;
/**
* The error count with a creation timestamp and its associated key.
*
* @since 4.3
*/
@Immutable
public class FailureCacheValue {
private final long creationTimeInNanos;
private final String key;
private final int errorCount;
public FailureCacheValue(final String key, final int errorCount) {
this.creationTimeInNanos = System.nanoTime();
this.key = key;
this.errorCount = errorCount;
}
public long getCreationTimeInNanos() {
return creationTimeInNanos;
}
public String getKey()
{
return key;
}
public int getErrorCount() {
return errorCount;
}
@Override
public String toString() {
return "[entry creationTimeInNanos=" + creationTimeInNanos + "; " +
"key=" + key + "; errorCount=" + errorCount + ']';
}
}

View file

@ -1,77 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
import ch.boye.httpclientandroidlib.client.cache.Resource;
/**
* Cache resource backed by a file.
*
* @since 4.1
*/
@ThreadSafe
public class FileResource implements Resource {
private static final long serialVersionUID = 4132244415919043397L;
private final File file;
private volatile boolean disposed;
public FileResource(final File file) {
super();
this.file = file;
this.disposed = false;
}
synchronized File getFile() {
return this.file;
}
public synchronized InputStream getInputStream() throws IOException {
return new FileInputStream(this.file);
}
public synchronized long length() {
return this.file.length();
}
public synchronized void dispose() {
if (this.disposed) {
return;
}
this.disposed = true;
this.file.delete();
}
}

View file

@ -1,111 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.cache.InputLimit;
import ch.boye.httpclientandroidlib.client.cache.Resource;
import ch.boye.httpclientandroidlib.client.cache.ResourceFactory;
/**
* Generates {@link Resource} instances whose body is stored in a temporary file.
*
* @since 4.1
*/
@Immutable
public class FileResourceFactory implements ResourceFactory {
private final File cacheDir;
private final BasicIdGenerator idgen;
public FileResourceFactory(final File cacheDir) {
super();
this.cacheDir = cacheDir;
this.idgen = new BasicIdGenerator();
}
private File generateUniqueCacheFile(final String requestId) {
final StringBuilder buffer = new StringBuilder();
this.idgen.generate(buffer);
buffer.append('.');
final int len = Math.min(requestId.length(), 100);
for (int i = 0; i < len; i++) {
final char ch = requestId.charAt(i);
if (Character.isLetterOrDigit(ch) || ch == '.') {
buffer.append(ch);
} else {
buffer.append('-');
}
}
return new File(this.cacheDir, buffer.toString());
}
public Resource generate(
final String requestId,
final InputStream instream,
final InputLimit limit) throws IOException {
final File file = generateUniqueCacheFile(requestId);
final FileOutputStream outstream = new FileOutputStream(file);
try {
final byte[] buf = new byte[2048];
long total = 0;
int l;
while ((l = instream.read(buf)) != -1) {
outstream.write(buf, 0, l);
total += l;
if (limit != null && total > limit.getValue()) {
limit.reached();
break;
}
}
} finally {
outstream.close();
}
return new FileResource(file);
}
public Resource copy(
final String requestId,
final Resource resource) throws IOException {
final File file = generateUniqueCacheFile(requestId);
if (resource instanceof FileResource) {
final File src = ((FileResource) resource).getFile();
IOUtils.copyFile(src, file);
} else {
final FileOutputStream out = new FileOutputStream(file);
IOUtils.copyAndClose(resource.getInputStream(), out);
}
return new FileResource(file);
}
}

View file

@ -1,67 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.cache.Resource;
/**
* Cache resource backed by a byte array on the heap.
*
* @since 4.1
*/
@Immutable
public class HeapResource implements Resource {
private static final long serialVersionUID = -2078599905620463394L;
private final byte[] b;
public HeapResource(final byte[] b) {
super();
this.b = b;
}
byte[] getByteArray() {
return this.b;
}
public InputStream getInputStream() {
return new ByteArrayInputStream(this.b);
}
public long length() {
return this.b.length;
}
public void dispose() {
}
}

View file

@ -1,83 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.cache.InputLimit;
import ch.boye.httpclientandroidlib.client.cache.Resource;
import ch.boye.httpclientandroidlib.client.cache.ResourceFactory;
/**
* Generates {@link Resource} instances stored entirely in heap.
*
* @since 4.1
*/
@Immutable
public class HeapResourceFactory implements ResourceFactory {
public Resource generate(
final String requestId,
final InputStream instream,
final InputLimit limit) throws IOException {
final ByteArrayOutputStream outstream = new ByteArrayOutputStream();
final byte[] buf = new byte[2048];
long total = 0;
int l;
while ((l = instream.read(buf)) != -1) {
outstream.write(buf, 0, l);
total += l;
if (limit != null && total > limit.getValue()) {
limit.reached();
break;
}
}
return createResource(outstream.toByteArray());
}
public Resource copy(
final String requestId,
final Resource resource) throws IOException {
byte[] body;
if (resource instanceof HeapResource) {
body = ((HeapResource) resource).getByteArray();
} else {
final ByteArrayOutputStream outstream = new ByteArrayOutputStream();
IOUtils.copyAndClose(resource.getInputStream(), outstream);
body = outstream.toByteArray();
}
return createResource(body);
}
Resource createResource(final byte[] buf) {
return new HeapResource(buf);
}
}

View file

@ -1,166 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.IOException;
import java.util.Date;
import java.util.Map;
import ch.boye.httpclientandroidlib.HttpHost;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry;
import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
/**
* @since 4.1
*/
interface HttpCache {
/**
* Clear all matching {@link HttpCacheEntry}s.
* @param host
* @param request
* @throws IOException
*/
void flushCacheEntriesFor(HttpHost host, HttpRequest request)
throws IOException;
/**
* Clear invalidated matching {@link HttpCacheEntry}s
* @param host
* @param request
* @throws IOException
*/
void flushInvalidatedCacheEntriesFor(HttpHost host, HttpRequest request)
throws IOException;
/** Clear any entries that may be invalidated by the given response to
* a particular request.
* @param host
* @param request
* @param response
*/
void flushInvalidatedCacheEntriesFor(HttpHost host, HttpRequest request,
HttpResponse response);
/**
* Retrieve matching {@link HttpCacheEntry} from the cache if it exists
* @param host
* @param request
* @return the matching {@link HttpCacheEntry} or {@code null}
* @throws IOException
*/
HttpCacheEntry getCacheEntry(HttpHost host, HttpRequest request)
throws IOException;
/**
* Retrieve all variants from the cache, if there are no variants then an empty
* {@link Map} is returned
* @param host
* @param request
* @return a <code>Map</code> mapping Etags to variant cache entries
* @throws IOException
*/
Map<String,Variant> getVariantCacheEntriesWithEtags(HttpHost host, HttpRequest request)
throws IOException;
/**
* Store a {@link HttpResponse} in the cache if possible, and return
* @param host
* @param request
* @param originResponse
* @param requestSent
* @param responseReceived
* @return the {@link HttpResponse}
* @throws IOException
*/
HttpResponse cacheAndReturnResponse(
HttpHost host, HttpRequest request, HttpResponse originResponse,
Date requestSent, Date responseReceived)
throws IOException;
/**
* Store a {@link HttpResponse} in the cache if possible, and return
* @param host
* @param request
* @param originResponse
* @param requestSent
* @param responseReceived
* @return the {@link HttpResponse}
* @throws IOException
*/
CloseableHttpResponse cacheAndReturnResponse(HttpHost host,
HttpRequest request, CloseableHttpResponse originResponse,
Date requestSent, Date responseReceived)
throws IOException;
/**
* Update a {@link HttpCacheEntry} using a 304 {@link HttpResponse}.
* @param target
* @param request
* @param stale
* @param originResponse
* @param requestSent
* @param responseReceived
* @return the updated {@link HttpCacheEntry}
* @throws IOException
*/
HttpCacheEntry updateCacheEntry(
HttpHost target, HttpRequest request, HttpCacheEntry stale, HttpResponse originResponse,
Date requestSent, Date responseReceived)
throws IOException;
/**
* Update a specific {@link HttpCacheEntry} representing a cached variant
* using a 304 {@link HttpResponse}.
* @param target host for client request
* @param request actual request from upstream client
* @param stale current variant cache entry
* @param originResponse 304 response received from origin
* @param requestSent when the validating request was sent
* @param responseReceived when the validating response was received
* @param cacheKey where in the cache this entry is currently stored
* @return the updated {@link HttpCacheEntry}
* @throws IOException
*/
HttpCacheEntry updateVariantCacheEntry(HttpHost target, HttpRequest request,
HttpCacheEntry stale, HttpResponse originResponse, Date requestSent,
Date responseReceived, String cacheKey)
throws IOException;
/**
* Specifies cache should reuse the given cached variant to satisfy
* requests whose varying headers match those of the given client request.
* @param target host of the upstream client request
* @param req request sent by upstream client
* @param variant variant cache entry to reuse
* @throws IOException may be thrown during cache update
*/
void reuseVariantEntryFor(HttpHost target, final HttpRequest req,
final Variant variant) throws IOException;
}

View file

@ -1,109 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import ch.boye.httpclientandroidlib.HttpEntity;
import ch.boye.httpclientandroidlib.annotation.Immutable;
@Immutable
class IOUtils {
static void consume(final HttpEntity entity) throws IOException {
if (entity == null) {
return;
}
if (entity.isStreaming()) {
final InputStream instream = entity.getContent();
if (instream != null) {
instream.close();
}
}
}
static void copy(final InputStream in, final OutputStream out) throws IOException {
final byte[] buf = new byte[2048];
int len;
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len);
}
}
static void closeSilently(final Closeable closable) {
try {
closable.close();
} catch (final IOException ignore) {
}
}
static void copyAndClose(final InputStream in, final OutputStream out) throws IOException {
try {
copy(in, out);
in.close();
out.close();
} catch (final IOException ex) {
closeSilently(in);
closeSilently(out);
// Propagate the original exception
throw ex;
}
}
static void copyFile(final File in, final File out) throws IOException {
final RandomAccessFile f1 = new RandomAccessFile(in, "r");
final RandomAccessFile f2 = new RandomAccessFile(out, "rw");
try {
final FileChannel c1 = f1.getChannel();
final FileChannel c2 = f2.getChannel();
try {
c1.transferTo(0, f1.length(), c2);
c1.close();
c2.close();
} catch (final IOException ex) {
closeSilently(c1);
closeSilently(c2);
// Propagate the original exception
throw ex;
}
f1.close();
f2.close();
} catch (final IOException ex) {
closeSilently(f1);
closeSilently(f2);
// Propagate the original exception
throw ex;
}
}
}

View file

@ -1,88 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Immediately schedules any incoming validation request. Relies on
* {@link CacheConfig} to configure the used {@link java.util.concurrent.ThreadPoolExecutor}.
*
* @since 4.3
*/
@ThreadSafe
public class ImmediateSchedulingStrategy implements SchedulingStrategy {
private final ExecutorService executor;
/**
* Uses a {@link java.util.concurrent.ThreadPoolExecutor} which is configured according to the
* given {@link CacheConfig}.
* @param cacheConfig specifies thread pool settings. See
* {@link CacheConfig#getAsynchronousWorkersMax()},
* {@link CacheConfig#getAsynchronousWorkersCore()},
* {@link CacheConfig#getAsynchronousWorkerIdleLifetimeSecs()},
* and {@link CacheConfig#getRevalidationQueueSize()}.
*/
public ImmediateSchedulingStrategy(final CacheConfig cacheConfig) {
this(new ThreadPoolExecutor(
cacheConfig.getAsynchronousWorkersCore(),
cacheConfig.getAsynchronousWorkersMax(),
cacheConfig.getAsynchronousWorkerIdleLifetimeSecs(),
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(cacheConfig.getRevalidationQueueSize()))
);
}
ImmediateSchedulingStrategy(final ExecutorService executor) {
this.executor = executor;
}
public void schedule(final AsynchronousValidationRequest revalidationRequest) {
if (revalidationRequest == null) {
throw new IllegalArgumentException("AsynchronousValidationRequest may not be null");
}
executor.execute(revalidationRequest);
}
public void close() {
executor.shutdown();
}
/**
* Visible for testing.
*/
void awaitTermination(final long timeout, final TimeUnit unit) throws InterruptedException {
executor.awaitTermination(timeout, unit);
}
}

View file

@ -1,163 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.Closeable;
import java.io.IOException;
import java.lang.ref.ReferenceQueue;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheStorage;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheUpdateCallback;
import ch.boye.httpclientandroidlib.client.cache.Resource;
import ch.boye.httpclientandroidlib.util.Args;
/**
* {@link HttpCacheStorage} implementation capable of deallocating resources associated with
* the cache entries. This cache keeps track of cache entries using
* {@link java.lang.ref.PhantomReference} and maintains a collection of all resources that
* are no longer in use. The cache, however, does not automatically deallocates associated
* resources by invoking {@link Resource#dispose()} method. The consumer MUST periodically
* call {@link #cleanResources()} method to trigger resource deallocation. The cache can be
* permanently shut down using {@link #shutdown()} method. All resources associated with
* the entries used by the cache will be deallocated.
*
* This {@link HttpCacheStorage} implementation is intended for use with {@link FileResource}
* and similar.
*
* @since 4.1
*/
@ThreadSafe
public class ManagedHttpCacheStorage implements HttpCacheStorage, Closeable {
private final CacheMap entries;
private final ReferenceQueue<HttpCacheEntry> morque;
private final Set<ResourceReference> resources;
private final AtomicBoolean active;
public ManagedHttpCacheStorage(final CacheConfig config) {
super();
this.entries = new CacheMap(config.getMaxCacheEntries());
this.morque = new ReferenceQueue<HttpCacheEntry>();
this.resources = new HashSet<ResourceReference>();
this.active = new AtomicBoolean(true);
}
private void ensureValidState() throws IllegalStateException {
if (!this.active.get()) {
throw new IllegalStateException("Cache has been shut down");
}
}
private void keepResourceReference(final HttpCacheEntry entry) {
final Resource resource = entry.getResource();
if (resource != null) {
// Must deallocate the resource when the entry is no longer in used
final ResourceReference ref = new ResourceReference(entry, this.morque);
this.resources.add(ref);
}
}
public void putEntry(final String url, final HttpCacheEntry entry) throws IOException {
Args.notNull(url, "URL");
Args.notNull(entry, "Cache entry");
ensureValidState();
synchronized (this) {
this.entries.put(url, entry);
keepResourceReference(entry);
}
}
public HttpCacheEntry getEntry(final String url) throws IOException {
Args.notNull(url, "URL");
ensureValidState();
synchronized (this) {
return this.entries.get(url);
}
}
public void removeEntry(final String url) throws IOException {
Args.notNull(url, "URL");
ensureValidState();
synchronized (this) {
// Cannot deallocate the associated resources immediately as the
// cache entry may still be in use
this.entries.remove(url);
}
}
public void updateEntry(
final String url,
final HttpCacheUpdateCallback callback) throws IOException {
Args.notNull(url, "URL");
Args.notNull(callback, "Callback");
ensureValidState();
synchronized (this) {
final HttpCacheEntry existing = this.entries.get(url);
final HttpCacheEntry updated = callback.update(existing);
this.entries.put(url, updated);
if (existing != updated) {
keepResourceReference(updated);
}
}
}
public void cleanResources() {
if (this.active.get()) {
ResourceReference ref;
while ((ref = (ResourceReference) this.morque.poll()) != null) {
synchronized (this) {
this.resources.remove(ref);
}
ref.getResource().dispose();
}
}
}
public void shutdown() {
if (this.active.compareAndSet(true, false)) {
synchronized (this) {
this.entries.clear();
for (final ResourceReference ref: this.resources) {
ref.getResource().dispose();
}
this.resources.clear();
while (this.morque.poll() != null) {
}
}
}
}
public void close() {
shutdown();
}
}

View file

@ -1,182 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.util.Locale;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HeaderIterator;
import ch.boye.httpclientandroidlib.HttpEntity;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.HttpStatus;
import ch.boye.httpclientandroidlib.HttpVersion;
import ch.boye.httpclientandroidlib.ProtocolVersion;
import ch.boye.httpclientandroidlib.StatusLine;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.message.AbstractHttpMessage;
import ch.boye.httpclientandroidlib.message.BasicStatusLine;
import ch.boye.httpclientandroidlib.params.BasicHttpParams;
import ch.boye.httpclientandroidlib.params.HttpParams;
/**
* @since 4.1
*/
@SuppressWarnings("deprecation")
@Immutable
final class OptionsHttp11Response extends AbstractHttpMessage implements HttpResponse {
private final StatusLine statusLine = new BasicStatusLine(HttpVersion.HTTP_1_1,
HttpStatus.SC_NOT_IMPLEMENTED, "");
private final ProtocolVersion version = HttpVersion.HTTP_1_1;
public StatusLine getStatusLine() {
return statusLine;
}
public void setStatusLine(final StatusLine statusline) {
// No-op on purpose, this class is not going to be doing any work.
}
public void setStatusLine(final ProtocolVersion ver, final int code) {
// No-op on purpose, this class is not going to be doing any work.
}
public void setStatusLine(final ProtocolVersion ver, final int code, final String reason) {
// No-op on purpose, this class is not going to be doing any work.
}
public void setStatusCode(final int code) throws IllegalStateException {
// No-op on purpose, this class is not going to be doing any work.
}
public void setReasonPhrase(final String reason) throws IllegalStateException {
// No-op on purpose, this class is not going to be doing any work.
}
public HttpEntity getEntity() {
return null;
}
public void setEntity(final HttpEntity entity) {
// No-op on purpose, this class is not going to be doing any work.
}
public Locale getLocale() {
return null;
}
public void setLocale(final Locale loc) {
// No-op on purpose, this class is not going to be doing any work.
}
public ProtocolVersion getProtocolVersion() {
return version;
}
@Override
public boolean containsHeader(final String name) {
return this.headergroup.containsHeader(name);
}
@Override
public Header[] getHeaders(final String name) {
return this.headergroup.getHeaders(name);
}
@Override
public Header getFirstHeader(final String name) {
return this.headergroup.getFirstHeader(name);
}
@Override
public Header getLastHeader(final String name) {
return this.headergroup.getLastHeader(name);
}
@Override
public Header[] getAllHeaders() {
return this.headergroup.getAllHeaders();
}
@Override
public void addHeader(final Header header) {
// No-op on purpose, this class is not going to be doing any work.
}
@Override
public void addHeader(final String name, final String value) {
// No-op on purpose, this class is not going to be doing any work.
}
@Override
public void setHeader(final Header header) {
// No-op on purpose, this class is not going to be doing any work.
}
@Override
public void setHeader(final String name, final String value) {
// No-op on purpose, this class is not going to be doing any work.
}
@Override
public void setHeaders(final Header[] headers) {
// No-op on purpose, this class is not going to be doing any work.
}
@Override
public void removeHeader(final Header header) {
// No-op on purpose, this class is not going to be doing any work.
}
@Override
public void removeHeaders(final String name) {
// No-op on purpose, this class is not going to be doing any work.
}
@Override
public HeaderIterator headerIterator() {
return this.headergroup.iterator();
}
@Override
public HeaderIterator headerIterator(final String name) {
return this.headergroup.iterator(name);
}
@Override
public HttpParams getParams() {
if (this.params == null) {
this.params = new BasicHttpParams();
}
return this.params;
}
@Override
public void setParams(final HttpParams params) {
// No-op on purpose, this class is not going to be doing any work.
}
}

View file

@ -1,56 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.lang.reflect.Proxy;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
import ch.boye.httpclientandroidlib.util.Args;
/**
* Proxies for HTTP message objects.
*
* @since 4.3
*/
@NotThreadSafe
class Proxies {
public static CloseableHttpResponse enhanceResponse(final HttpResponse original) {
Args.notNull(original, "HTTP response");
if (original instanceof CloseableHttpResponse) {
return (CloseableHttpResponse) original;
} else {
return (CloseableHttpResponse) Proxy.newProxyInstance(
ResponseProxyHandler.class.getClassLoader(),
new Class<?>[] { CloseableHttpResponse.class },
new ResponseProxyHandler(original));
}
}
}

View file

@ -1,376 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HeaderElement;
import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.HttpStatus;
import ch.boye.httpclientandroidlib.HttpVersion;
import ch.boye.httpclientandroidlib.ProtocolVersion;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.ClientProtocolException;
import ch.boye.httpclientandroidlib.client.cache.HeaderConstants;
import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper;
import ch.boye.httpclientandroidlib.entity.AbstractHttpEntity;
import ch.boye.httpclientandroidlib.entity.ContentType;
import ch.boye.httpclientandroidlib.message.BasicHeader;
import ch.boye.httpclientandroidlib.message.BasicHttpResponse;
import ch.boye.httpclientandroidlib.message.BasicStatusLine;
import ch.boye.httpclientandroidlib.protocol.HTTP;
/**
* @since 4.1
*/
@Immutable
class RequestProtocolCompliance {
private final boolean weakETagOnPutDeleteAllowed;
public RequestProtocolCompliance() {
super();
this.weakETagOnPutDeleteAllowed = false;
}
public RequestProtocolCompliance(final boolean weakETagOnPutDeleteAllowed) {
super();
this.weakETagOnPutDeleteAllowed = weakETagOnPutDeleteAllowed;
}
private static final List<String> disallowedWithNoCache =
Arrays.asList(HeaderConstants.CACHE_CONTROL_MIN_FRESH, HeaderConstants.CACHE_CONTROL_MAX_STALE, HeaderConstants.CACHE_CONTROL_MAX_AGE);
/**
* Test to see if the {@link HttpRequest} is HTTP1.1 compliant or not
* and if not, we can not continue.
*
* @param request the HttpRequest Object
* @return list of {@link RequestProtocolError}
*/
public List<RequestProtocolError> requestIsFatallyNonCompliant(final HttpRequest request) {
final List<RequestProtocolError> theErrors = new ArrayList<RequestProtocolError>();
RequestProtocolError anError = requestHasWeakETagAndRange(request);
if (anError != null) {
theErrors.add(anError);
}
if (!weakETagOnPutDeleteAllowed) {
anError = requestHasWeekETagForPUTOrDELETEIfMatch(request);
if (anError != null) {
theErrors.add(anError);
}
}
anError = requestContainsNoCacheDirectiveWithFieldName(request);
if (anError != null) {
theErrors.add(anError);
}
return theErrors;
}
/**
* If the {@link HttpRequest} is non-compliant but 'fixable' we go ahead and
* fix the request here.
*
* @param request the request to check for compliance
* @throws ClientProtocolException when we have trouble making the request compliant
*/
public void makeRequestCompliant(final HttpRequestWrapper request)
throws ClientProtocolException {
if (requestMustNotHaveEntity(request)) {
((HttpEntityEnclosingRequest) request).setEntity(null);
}
verifyRequestWithExpectContinueFlagHas100continueHeader(request);
verifyOPTIONSRequestWithBodyHasContentType(request);
decrementOPTIONSMaxForwardsIfGreaterThen0(request);
stripOtherFreshnessDirectivesWithNoCache(request);
if (requestVersionIsTooLow(request)
|| requestMinorVersionIsTooHighMajorVersionsMatch(request)) {
request.setProtocolVersion(HttpVersion.HTTP_1_1);
}
}
private void stripOtherFreshnessDirectivesWithNoCache(final HttpRequest request) {
final List<HeaderElement> outElts = new ArrayList<HeaderElement>();
boolean shouldStrip = false;
for(final Header h : request.getHeaders(HeaderConstants.CACHE_CONTROL)) {
for(final HeaderElement elt : h.getElements()) {
if (!disallowedWithNoCache.contains(elt.getName())) {
outElts.add(elt);
}
if (HeaderConstants.CACHE_CONTROL_NO_CACHE.equals(elt.getName())) {
shouldStrip = true;
}
}
}
if (!shouldStrip) {
return;
}
request.removeHeaders(HeaderConstants.CACHE_CONTROL);
request.setHeader(HeaderConstants.CACHE_CONTROL, buildHeaderFromElements(outElts));
}
private String buildHeaderFromElements(final List<HeaderElement> outElts) {
final StringBuilder newHdr = new StringBuilder("");
boolean first = true;
for(final HeaderElement elt : outElts) {
if (!first) {
newHdr.append(",");
} else {
first = false;
}
newHdr.append(elt.toString());
}
return newHdr.toString();
}
private boolean requestMustNotHaveEntity(final HttpRequest request) {
return HeaderConstants.TRACE_METHOD.equals(request.getRequestLine().getMethod())
&& request instanceof HttpEntityEnclosingRequest;
}
private void decrementOPTIONSMaxForwardsIfGreaterThen0(final HttpRequest request) {
if (!HeaderConstants.OPTIONS_METHOD.equals(request.getRequestLine().getMethod())) {
return;
}
final Header maxForwards = request.getFirstHeader(HeaderConstants.MAX_FORWARDS);
if (maxForwards == null) {
return;
}
request.removeHeaders(HeaderConstants.MAX_FORWARDS);
final int currentMaxForwards = Integer.parseInt(maxForwards.getValue());
request.setHeader(HeaderConstants.MAX_FORWARDS, Integer.toString(currentMaxForwards - 1));
}
private void verifyOPTIONSRequestWithBodyHasContentType(final HttpRequest request) {
if (!HeaderConstants.OPTIONS_METHOD.equals(request.getRequestLine().getMethod())) {
return;
}
if (!(request instanceof HttpEntityEnclosingRequest)) {
return;
}
addContentTypeHeaderIfMissing((HttpEntityEnclosingRequest) request);
}
private void addContentTypeHeaderIfMissing(final HttpEntityEnclosingRequest request) {
if (request.getEntity().getContentType() == null) {
((AbstractHttpEntity) request.getEntity()).setContentType(
ContentType.APPLICATION_OCTET_STREAM.getMimeType());
}
}
private void verifyRequestWithExpectContinueFlagHas100continueHeader(final HttpRequest request) {
if (request instanceof HttpEntityEnclosingRequest) {
if (((HttpEntityEnclosingRequest) request).expectContinue()
&& ((HttpEntityEnclosingRequest) request).getEntity() != null) {
add100ContinueHeaderIfMissing(request);
} else {
remove100ContinueHeaderIfExists(request);
}
} else {
remove100ContinueHeaderIfExists(request);
}
}
private void remove100ContinueHeaderIfExists(final HttpRequest request) {
boolean hasHeader = false;
final Header[] expectHeaders = request.getHeaders(HTTP.EXPECT_DIRECTIVE);
List<HeaderElement> expectElementsThatAreNot100Continue = new ArrayList<HeaderElement>();
for (final Header h : expectHeaders) {
for (final HeaderElement elt : h.getElements()) {
if (!(HTTP.EXPECT_CONTINUE.equalsIgnoreCase(elt.getName()))) {
expectElementsThatAreNot100Continue.add(elt);
} else {
hasHeader = true;
}
}
if (hasHeader) {
request.removeHeader(h);
for (final HeaderElement elt : expectElementsThatAreNot100Continue) {
final BasicHeader newHeader = new BasicHeader(HTTP.EXPECT_DIRECTIVE, elt.getName());
request.addHeader(newHeader);
}
return;
} else {
expectElementsThatAreNot100Continue = new ArrayList<HeaderElement>();
}
}
}
private void add100ContinueHeaderIfMissing(final HttpRequest request) {
boolean hasHeader = false;
for (final Header h : request.getHeaders(HTTP.EXPECT_DIRECTIVE)) {
for (final HeaderElement elt : h.getElements()) {
if (HTTP.EXPECT_CONTINUE.equalsIgnoreCase(elt.getName())) {
hasHeader = true;
}
}
}
if (!hasHeader) {
request.addHeader(HTTP.EXPECT_DIRECTIVE, HTTP.EXPECT_CONTINUE);
}
}
protected boolean requestMinorVersionIsTooHighMajorVersionsMatch(final HttpRequest request) {
final ProtocolVersion requestProtocol = request.getProtocolVersion();
if (requestProtocol.getMajor() != HttpVersion.HTTP_1_1.getMajor()) {
return false;
}
if (requestProtocol.getMinor() > HttpVersion.HTTP_1_1.getMinor()) {
return true;
}
return false;
}
protected boolean requestVersionIsTooLow(final HttpRequest request) {
return request.getProtocolVersion().compareToVersion(HttpVersion.HTTP_1_1) < 0;
}
/**
* Extract error information about the {@link HttpRequest} telling the 'caller'
* that a problem occured.
*
* @param errorCheck What type of error should I get
* @return The {@link HttpResponse} that is the error generated
*/
public HttpResponse getErrorForRequest(final RequestProtocolError errorCheck) {
switch (errorCheck) {
case BODY_BUT_NO_LENGTH_ERROR:
return new BasicHttpResponse(new BasicStatusLine(HttpVersion.HTTP_1_1,
HttpStatus.SC_LENGTH_REQUIRED, ""));
case WEAK_ETAG_AND_RANGE_ERROR:
return new BasicHttpResponse(new BasicStatusLine(HttpVersion.HTTP_1_1,
HttpStatus.SC_BAD_REQUEST, "Weak eTag not compatible with byte range"));
case WEAK_ETAG_ON_PUTDELETE_METHOD_ERROR:
return new BasicHttpResponse(new BasicStatusLine(HttpVersion.HTTP_1_1,
HttpStatus.SC_BAD_REQUEST,
"Weak eTag not compatible with PUT or DELETE requests"));
case NO_CACHE_DIRECTIVE_WITH_FIELD_NAME:
return new BasicHttpResponse(new BasicStatusLine(HttpVersion.HTTP_1_1,
HttpStatus.SC_BAD_REQUEST,
"No-Cache directive MUST NOT include a field name"));
default:
throw new IllegalStateException(
"The request was compliant, therefore no error can be generated for it.");
}
}
private RequestProtocolError requestHasWeakETagAndRange(final HttpRequest request) {
// TODO: Should these be looking at all the headers marked as Range?
final String method = request.getRequestLine().getMethod();
if (!(HeaderConstants.GET_METHOD.equals(method))) {
return null;
}
final Header range = request.getFirstHeader(HeaderConstants.RANGE);
if (range == null) {
return null;
}
final Header ifRange = request.getFirstHeader(HeaderConstants.IF_RANGE);
if (ifRange == null) {
return null;
}
final String val = ifRange.getValue();
if (val.startsWith("W/")) {
return RequestProtocolError.WEAK_ETAG_AND_RANGE_ERROR;
}
return null;
}
private RequestProtocolError requestHasWeekETagForPUTOrDELETEIfMatch(final HttpRequest request) {
// TODO: Should these be looking at all the headers marked as If-Match/If-None-Match?
final String method = request.getRequestLine().getMethod();
if (!(HeaderConstants.PUT_METHOD.equals(method) || HeaderConstants.DELETE_METHOD
.equals(method))) {
return null;
}
final Header ifMatch = request.getFirstHeader(HeaderConstants.IF_MATCH);
if (ifMatch != null) {
final String val = ifMatch.getValue();
if (val.startsWith("W/")) {
return RequestProtocolError.WEAK_ETAG_ON_PUTDELETE_METHOD_ERROR;
}
} else {
final Header ifNoneMatch = request.getFirstHeader(HeaderConstants.IF_NONE_MATCH);
if (ifNoneMatch == null) {
return null;
}
final String val2 = ifNoneMatch.getValue();
if (val2.startsWith("W/")) {
return RequestProtocolError.WEAK_ETAG_ON_PUTDELETE_METHOD_ERROR;
}
}
return null;
}
private RequestProtocolError requestContainsNoCacheDirectiveWithFieldName(final HttpRequest request) {
for(final Header h : request.getHeaders(HeaderConstants.CACHE_CONTROL)) {
for(final HeaderElement elt : h.getElements()) {
if (HeaderConstants.CACHE_CONTROL_NO_CACHE.equalsIgnoreCase(elt.getName())
&& elt.getValue() != null) {
return RequestProtocolError.NO_CACHE_DIRECTIVE_WITH_FIELD_NAME;
}
}
}
return null;
}
}

View file

@ -1,40 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
/**
* @since 4.1
*/
enum RequestProtocolError {
UNKNOWN,
BODY_BUT_NO_LENGTH_ERROR,
WEAK_ETAG_ON_PUTDELETE_METHOD_ERROR,
WEAK_ETAG_AND_RANGE_ERROR,
NO_CACHE_DIRECTIVE_WITH_FIELD_NAME
}

View file

@ -1,62 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry;
import ch.boye.httpclientandroidlib.client.cache.Resource;
import ch.boye.httpclientandroidlib.util.Args;
@Immutable
class ResourceReference extends PhantomReference<HttpCacheEntry> {
private final Resource resource;
public ResourceReference(final HttpCacheEntry entry, final ReferenceQueue<HttpCacheEntry> q) {
super(entry, q);
Args.notNull(entry.getResource(), "Resource");
this.resource = entry.getResource();
}
public Resource getResource() {
return this.resource;
}
@Override
public int hashCode() {
return this.resource.hashCode();
}
@Override
public boolean equals(final Object obj) {
return this.resource.equals(obj);
}
}

View file

@ -1,311 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
/* LogFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HeaderElement;
import ch.boye.httpclientandroidlib.HttpMessage;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.HttpStatus;
import ch.boye.httpclientandroidlib.HttpVersion;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.cache.HeaderConstants;
import ch.boye.httpclientandroidlib.client.utils.DateUtils;
import ch.boye.httpclientandroidlib.protocol.HTTP;
/**
* Determines if an HttpResponse can be cached.
*
* @since 4.1
*/
@Immutable
class ResponseCachingPolicy {
private static final String[] AUTH_CACHEABLE_PARAMS = {
"s-maxage", HeaderConstants.CACHE_CONTROL_MUST_REVALIDATE, HeaderConstants.PUBLIC
};
private final long maxObjectSizeBytes;
private final boolean sharedCache;
private final boolean neverCache1_0ResponsesWithQueryString;
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
private static final Set<Integer> cacheableStatuses =
new HashSet<Integer>(Arrays.asList(HttpStatus.SC_OK,
HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION,
HttpStatus.SC_MULTIPLE_CHOICES,
HttpStatus.SC_MOVED_PERMANENTLY,
HttpStatus.SC_GONE));
private final Set<Integer> uncacheableStatuses;
/**
* Define a cache policy that limits the size of things that should be stored
* in the cache to a maximum of {@link HttpResponse} bytes in size.
*
* @param maxObjectSizeBytes the size to limit items into the cache
* @param sharedCache whether to behave as a shared cache (true) or a
* non-shared/private cache (false)
* @param neverCache1_0ResponsesWithQueryString true to never cache HTTP 1.0 responses with a query string, false
* to cache if explicit cache headers are found.
* @param allow303Caching if this policy is permitted to cache 303 response
*/
public ResponseCachingPolicy(final long maxObjectSizeBytes,
final boolean sharedCache,
final boolean neverCache1_0ResponsesWithQueryString,
final boolean allow303Caching) {
this.maxObjectSizeBytes = maxObjectSizeBytes;
this.sharedCache = sharedCache;
this.neverCache1_0ResponsesWithQueryString = neverCache1_0ResponsesWithQueryString;
if (allow303Caching) {
uncacheableStatuses = new HashSet<Integer>(
Arrays.asList(HttpStatus.SC_PARTIAL_CONTENT));
} else {
uncacheableStatuses = new HashSet<Integer>(Arrays.asList(
HttpStatus.SC_PARTIAL_CONTENT, HttpStatus.SC_SEE_OTHER));
}
}
/**
* Determines if an HttpResponse can be cached.
*
* @param httpMethod What type of request was this, a GET, PUT, other?
* @param response The origin response
* @return <code>true</code> if response is cacheable
*/
public boolean isResponseCacheable(final String httpMethod, final HttpResponse response) {
boolean cacheable = false;
if (!HeaderConstants.GET_METHOD.equals(httpMethod)) {
log.debug("Response was not cacheable.");
return false;
}
final int status = response.getStatusLine().getStatusCode();
if (cacheableStatuses.contains(status)) {
// these response codes MAY be cached
cacheable = true;
} else if (uncacheableStatuses.contains(status)) {
return false;
} else if (unknownStatusCode(status)) {
// a response with an unknown status code MUST NOT be
// cached
return false;
}
final Header contentLength = response.getFirstHeader(HTTP.CONTENT_LEN);
if (contentLength != null) {
final int contentLengthValue = Integer.parseInt(contentLength.getValue());
if (contentLengthValue > this.maxObjectSizeBytes) {
return false;
}
}
final Header[] ageHeaders = response.getHeaders(HeaderConstants.AGE);
if (ageHeaders.length > 1) {
return false;
}
final Header[] expiresHeaders = response.getHeaders(HeaderConstants.EXPIRES);
if (expiresHeaders.length > 1) {
return false;
}
final Header[] dateHeaders = response.getHeaders(HTTP.DATE_HEADER);
if (dateHeaders.length != 1) {
return false;
}
final Date date = DateUtils.parseDate(dateHeaders[0].getValue());
if (date == null) {
return false;
}
for (final Header varyHdr : response.getHeaders(HeaderConstants.VARY)) {
for (final HeaderElement elem : varyHdr.getElements()) {
if ("*".equals(elem.getName())) {
return false;
}
}
}
if (isExplicitlyNonCacheable(response)) {
return false;
}
return (cacheable || isExplicitlyCacheable(response));
}
private boolean unknownStatusCode(final int status) {
if (status >= 100 && status <= 101) {
return false;
}
if (status >= 200 && status <= 206) {
return false;
}
if (status >= 300 && status <= 307) {
return false;
}
if (status >= 400 && status <= 417) {
return false;
}
if (status >= 500 && status <= 505) {
return false;
}
return true;
}
protected boolean isExplicitlyNonCacheable(final HttpResponse response) {
final Header[] cacheControlHeaders = response.getHeaders(HeaderConstants.CACHE_CONTROL);
for (final Header header : cacheControlHeaders) {
for (final HeaderElement elem : header.getElements()) {
if (HeaderConstants.CACHE_CONTROL_NO_STORE.equals(elem.getName())
|| HeaderConstants.CACHE_CONTROL_NO_CACHE.equals(elem.getName())
|| (sharedCache && HeaderConstants.PRIVATE.equals(elem.getName()))) {
return true;
}
}
}
return false;
}
protected boolean hasCacheControlParameterFrom(final HttpMessage msg, final String[] params) {
final Header[] cacheControlHeaders = msg.getHeaders(HeaderConstants.CACHE_CONTROL);
for (final Header header : cacheControlHeaders) {
for (final HeaderElement elem : header.getElements()) {
for (final String param : params) {
if (param.equalsIgnoreCase(elem.getName())) {
return true;
}
}
}
}
return false;
}
protected boolean isExplicitlyCacheable(final HttpResponse response) {
if (response.getFirstHeader(HeaderConstants.EXPIRES) != null) {
return true;
}
final String[] cacheableParams = { HeaderConstants.CACHE_CONTROL_MAX_AGE, "s-maxage",
HeaderConstants.CACHE_CONTROL_MUST_REVALIDATE,
HeaderConstants.CACHE_CONTROL_PROXY_REVALIDATE,
HeaderConstants.PUBLIC
};
return hasCacheControlParameterFrom(response, cacheableParams);
}
/**
* Determine if the {@link HttpResponse} gotten from the origin is a
* cacheable response.
*
* @param request the {@link HttpRequest} that generated an origin hit
* @param response the {@link HttpResponse} from the origin
* @return <code>true</code> if response is cacheable
*/
public boolean isResponseCacheable(final HttpRequest request, final HttpResponse response) {
if (requestProtocolGreaterThanAccepted(request)) {
log.debug("Response was not cacheable.");
return false;
}
final String[] uncacheableRequestDirectives = { HeaderConstants.CACHE_CONTROL_NO_STORE };
if (hasCacheControlParameterFrom(request,uncacheableRequestDirectives)) {
return false;
}
if (request.getRequestLine().getUri().contains("?")) {
if (neverCache1_0ResponsesWithQueryString && from1_0Origin(response)) {
log.debug("Response was not cacheable as it had a query string.");
return false;
} else if (!isExplicitlyCacheable(response)) {
log.debug("Response was not cacheable as it is missing explicit caching headers.");
return false;
}
}
if (expiresHeaderLessOrEqualToDateHeaderAndNoCacheControl(response)) {
return false;
}
if (sharedCache) {
final Header[] authNHeaders = request.getHeaders(HeaderConstants.AUTHORIZATION);
if (authNHeaders != null && authNHeaders.length > 0
&& !hasCacheControlParameterFrom(response, AUTH_CACHEABLE_PARAMS)) {
return false;
}
}
final String method = request.getRequestLine().getMethod();
return isResponseCacheable(method, response);
}
private boolean expiresHeaderLessOrEqualToDateHeaderAndNoCacheControl(
final HttpResponse response) {
if (response.getFirstHeader(HeaderConstants.CACHE_CONTROL) != null) {
return false;
}
final Header expiresHdr = response.getFirstHeader(HeaderConstants.EXPIRES);
final Header dateHdr = response.getFirstHeader(HTTP.DATE_HEADER);
if (expiresHdr == null || dateHdr == null) {
return false;
}
final Date expires = DateUtils.parseDate(expiresHdr.getValue());
final Date date = DateUtils.parseDate(dateHdr.getValue());
if (expires == null || date == null) {
return false;
}
return expires.equals(date) || expires.before(date);
}
private boolean from1_0Origin(final HttpResponse response) {
final Header via = response.getFirstHeader(HeaderConstants.VIA);
if (via != null) {
for(final HeaderElement elt : via.getElements()) {
final String proto = elt.toString().split("\\s")[0];
if (proto.contains("/")) {
return proto.equals("HTTP/1.0");
} else {
return proto.equals("1.0");
}
}
}
return HttpVersion.HTTP_1_0.equals(response.getProtocolVersion());
}
private boolean requestProtocolGreaterThanAccepted(final HttpRequest req) {
return req.getProtocolVersion().compareToVersion(HttpVersion.HTTP_1_1) > 0;
}
}

View file

@ -1,251 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HeaderElement;
import ch.boye.httpclientandroidlib.HttpEntity;
import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.HttpStatus;
import ch.boye.httpclientandroidlib.HttpVersion;
import ch.boye.httpclientandroidlib.annotation.Immutable;
import ch.boye.httpclientandroidlib.client.ClientProtocolException;
import ch.boye.httpclientandroidlib.client.cache.HeaderConstants;
import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper;
import ch.boye.httpclientandroidlib.client.utils.DateUtils;
import ch.boye.httpclientandroidlib.message.BasicHeader;
import ch.boye.httpclientandroidlib.protocol.HTTP;
/**
* @since 4.1
*/
@Immutable
class ResponseProtocolCompliance {
private static final String UNEXPECTED_100_CONTINUE = "The incoming request did not contain a "
+ "100-continue header, but the response was a Status 100, continue.";
private static final String UNEXPECTED_PARTIAL_CONTENT = "partial content was returned for a request that did not ask for it";
/**
* When we get a response from a down stream server (Origin Server)
* we attempt to see if it is HTTP 1.1 Compliant and if not, attempt to
* make it so.
*
* @param request The {@link HttpRequest} that generated an origin hit and response
* @param response The {@link HttpResponse} from the origin server
* @throws IOException Bad things happened
*/
public void ensureProtocolCompliance(final HttpRequestWrapper request, final HttpResponse response)
throws IOException {
if (backendResponseMustNotHaveBody(request, response)) {
consumeBody(response);
response.setEntity(null);
}
requestDidNotExpect100ContinueButResponseIsOne(request, response);
transferEncodingIsNotReturnedTo1_0Client(request, response);
ensurePartialContentIsNotSentToAClientThatDidNotRequestIt(request, response);
ensure200ForOPTIONSRequestWithNoBodyHasContentLengthZero(request, response);
ensure206ContainsDateHeader(response);
ensure304DoesNotContainExtraEntityHeaders(response);
identityIsNotUsedInContentEncoding(response);
warningsWithNonMatchingWarnDatesAreRemoved(response);
}
private void consumeBody(final HttpResponse response) throws IOException {
final HttpEntity body = response.getEntity();
if (body != null) {
IOUtils.consume(body);
}
}
private void warningsWithNonMatchingWarnDatesAreRemoved(
final HttpResponse response) {
final Date responseDate = DateUtils.parseDate(response.getFirstHeader(HTTP.DATE_HEADER).getValue());
if (responseDate == null) {
return;
}
final Header[] warningHeaders = response.getHeaders(HeaderConstants.WARNING);
if (warningHeaders == null || warningHeaders.length == 0) {
return;
}
final List<Header> newWarningHeaders = new ArrayList<Header>();
boolean modified = false;
for(final Header h : warningHeaders) {
for(final WarningValue wv : WarningValue.getWarningValues(h)) {
final Date warnDate = wv.getWarnDate();
if (warnDate == null || warnDate.equals(responseDate)) {
newWarningHeaders.add(new BasicHeader(HeaderConstants.WARNING,wv.toString()));
} else {
modified = true;
}
}
}
if (modified) {
response.removeHeaders(HeaderConstants.WARNING);
for(final Header h : newWarningHeaders) {
response.addHeader(h);
}
}
}
private void identityIsNotUsedInContentEncoding(final HttpResponse response) {
final Header[] hdrs = response.getHeaders(HTTP.CONTENT_ENCODING);
if (hdrs == null || hdrs.length == 0) {
return;
}
final List<Header> newHeaders = new ArrayList<Header>();
boolean modified = false;
for (final Header h : hdrs) {
final StringBuilder buf = new StringBuilder();
boolean first = true;
for (final HeaderElement elt : h.getElements()) {
if ("identity".equalsIgnoreCase(elt.getName())) {
modified = true;
} else {
if (!first) {
buf.append(",");
}
buf.append(elt.toString());
first = false;
}
}
final String newHeaderValue = buf.toString();
if (!"".equals(newHeaderValue)) {
newHeaders.add(new BasicHeader(HTTP.CONTENT_ENCODING, newHeaderValue));
}
}
if (!modified) {
return;
}
response.removeHeaders(HTTP.CONTENT_ENCODING);
for (final Header h : newHeaders) {
response.addHeader(h);
}
}
private void ensure206ContainsDateHeader(final HttpResponse response) {
if (response.getFirstHeader(HTTP.DATE_HEADER) == null) {
response.addHeader(HTTP.DATE_HEADER, DateUtils.formatDate(new Date()));
}
}
private void ensurePartialContentIsNotSentToAClientThatDidNotRequestIt(final HttpRequest request,
final HttpResponse response) throws IOException {
if (request.getFirstHeader(HeaderConstants.RANGE) != null
|| response.getStatusLine().getStatusCode() != HttpStatus.SC_PARTIAL_CONTENT) {
return;
}
consumeBody(response);
throw new ClientProtocolException(UNEXPECTED_PARTIAL_CONTENT);
}
private void ensure200ForOPTIONSRequestWithNoBodyHasContentLengthZero(final HttpRequest request,
final HttpResponse response) {
if (!request.getRequestLine().getMethod().equalsIgnoreCase(HeaderConstants.OPTIONS_METHOD)) {
return;
}
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
return;
}
if (response.getFirstHeader(HTTP.CONTENT_LEN) == null) {
response.addHeader(HTTP.CONTENT_LEN, "0");
}
}
private void ensure304DoesNotContainExtraEntityHeaders(final HttpResponse response) {
final String[] disallowedEntityHeaders = { HeaderConstants.ALLOW, HTTP.CONTENT_ENCODING,
"Content-Language", HTTP.CONTENT_LEN, "Content-MD5",
"Content-Range", HTTP.CONTENT_TYPE, HeaderConstants.LAST_MODIFIED
};
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
for(final String hdr : disallowedEntityHeaders) {
response.removeHeaders(hdr);
}
}
}
private boolean backendResponseMustNotHaveBody(final HttpRequest request, final HttpResponse backendResponse) {
return HeaderConstants.HEAD_METHOD.equals(request.getRequestLine().getMethod())
|| backendResponse.getStatusLine().getStatusCode() == HttpStatus.SC_NO_CONTENT
|| backendResponse.getStatusLine().getStatusCode() == HttpStatus.SC_RESET_CONTENT
|| backendResponse.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_MODIFIED;
}
private void requestDidNotExpect100ContinueButResponseIsOne(final HttpRequestWrapper request,
final HttpResponse response) throws IOException {
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_CONTINUE) {
return;
}
final HttpRequest originalRequest = request.getOriginal();
if (originalRequest instanceof HttpEntityEnclosingRequest) {
if (((HttpEntityEnclosingRequest)originalRequest).expectContinue()) {
return;
}
}
consumeBody(response);
throw new ClientProtocolException(UNEXPECTED_100_CONTINUE);
}
private void transferEncodingIsNotReturnedTo1_0Client(final HttpRequestWrapper request,
final HttpResponse response) {
final HttpRequest originalRequest = request.getOriginal();
if (originalRequest.getProtocolVersion().compareToVersion(HttpVersion.HTTP_1_1) >= 0) {
return;
}
removeResponseTransferEncoding(response);
}
private void removeResponseTransferEncoding(final HttpResponse response) {
response.removeHeaders("TE");
response.removeHeaders(HTTP.TRANSFER_ENCODING);
}
}

View file

@ -1,88 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
/**
* A proxy class that can enhance an arbitrary {@link HttpResponse} with
* {@link Closeable#close()} method.
*
* @since 4.3
*/
@NotThreadSafe
class ResponseProxyHandler implements InvocationHandler {
private static final Method CLOSE_METHOD;
static {
try {
CLOSE_METHOD = Closeable.class.getMethod("close");
} catch (final NoSuchMethodException ex) {
throw new Error(ex);
}
}
private final HttpResponse original;
ResponseProxyHandler(final HttpResponse original) {
super();
this.original = original;
}
public void close() throws IOException {
IOUtils.consume(original.getEntity());
}
public Object invoke(
final Object proxy, final Method method, final Object[] args) throws Throwable {
if (method.equals(CLOSE_METHOD)) {
close();
return null;
} else {
try {
return method.invoke(this.original, args);
} catch (final InvocationTargetException ex) {
final Throwable cause = ex.getCause();
if (cause != null) {
throw cause;
} else {
throw ex;
}
}
}
}
}

View file

@ -1,45 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.Closeable;
/**
* Specifies when revalidation requests are scheduled.
*
* @since 4.3
*/
public interface SchedulingStrategy extends Closeable
{
/**
* Schedule an {@link AsynchronousValidationRequest} to be executed.
*
* @param revalidationRequest the request to be executed; not <code>null</code>
* @throws java.util.concurrent.RejectedExecutionException if the request could not be scheduled for execution
*/
void schedule(AsynchronousValidationRequest revalidationRequest);
}

View file

@ -1,150 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Proxy;
import ch.boye.httpclientandroidlib.HttpEntity;
import ch.boye.httpclientandroidlib.HttpRequest;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
import ch.boye.httpclientandroidlib.client.cache.InputLimit;
import ch.boye.httpclientandroidlib.client.cache.Resource;
import ch.boye.httpclientandroidlib.client.cache.ResourceFactory;
import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
import ch.boye.httpclientandroidlib.message.BasicHttpResponse;
/**
* @since 4.1
*/
@NotThreadSafe
class SizeLimitedResponseReader {
private final ResourceFactory resourceFactory;
private final long maxResponseSizeBytes;
private final HttpRequest request;
private final CloseableHttpResponse response;
private InputStream instream;
private InputLimit limit;
private Resource resource;
private boolean consumed;
/**
* Create an {@link HttpResponse} that is limited in size, this allows for checking
* the size of objects that will be stored in the cache.
*/
public SizeLimitedResponseReader(
final ResourceFactory resourceFactory,
final long maxResponseSizeBytes,
final HttpRequest request,
final CloseableHttpResponse response) {
super();
this.resourceFactory = resourceFactory;
this.maxResponseSizeBytes = maxResponseSizeBytes;
this.request = request;
this.response = response;
}
protected void readResponse() throws IOException {
if (!consumed) {
doConsume();
}
}
private void ensureNotConsumed() {
if (consumed) {
throw new IllegalStateException("Response has already been consumed");
}
}
private void ensureConsumed() {
if (!consumed) {
throw new IllegalStateException("Response has not been consumed");
}
}
private void doConsume() throws IOException {
ensureNotConsumed();
consumed = true;
limit = new InputLimit(maxResponseSizeBytes);
final HttpEntity entity = response.getEntity();
if (entity == null) {
return;
}
final String uri = request.getRequestLine().getUri();
instream = entity.getContent();
try {
resource = resourceFactory.generate(uri, instream, limit);
} finally {
if (!limit.isReached()) {
instream.close();
}
}
}
boolean isLimitReached() {
ensureConsumed();
return limit.isReached();
}
Resource getResource() {
ensureConsumed();
return resource;
}
CloseableHttpResponse getReconstructedResponse() throws IOException {
ensureConsumed();
final HttpResponse reconstructed = new BasicHttpResponse(response.getStatusLine());
reconstructed.setHeaders(response.getAllHeaders());
final CombinedEntity combinedEntity = new CombinedEntity(resource, instream);
final HttpEntity entity = response.getEntity();
if (entity != null) {
combinedEntity.setContentType(entity.getContentType());
combinedEntity.setContentEncoding(entity.getContentEncoding());
combinedEntity.setChunked(entity.isChunked());
}
reconstructed.setEntity(combinedEntity);
return (CloseableHttpResponse) Proxy.newProxyInstance(
ResponseProxyHandler.class.getClassLoader(),
new Class<?>[] { CloseableHttpResponse.class },
new ResponseProxyHandler(reconstructed) {
@Override
public void close() throws IOException {
response.close();
}
});
}
}

View file

@ -1,55 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry;
/** Records a set of information describing a cached variant. */
class Variant {
private final String variantKey;
private final String cacheKey;
private final HttpCacheEntry entry;
public Variant(final String variantKey, final String cacheKey, final HttpCacheEntry entry) {
this.variantKey = variantKey;
this.cacheKey = cacheKey;
this.entry = entry;
}
public String getVariantKey() {
return variantKey;
}
public String getCacheKey() {
return cacheKey;
}
public HttpCacheEntry getEntry() {
return entry;
}
}

View file

@ -1,370 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package ch.boye.httpclientandroidlib.impl.client.cache;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.client.utils.DateUtils;
/** This class provides for parsing and understanding Warning headers. As
* the Warning header can be multi-valued, but the values can contain
* separators like commas inside quoted strings, we cannot use the regular
* {@link Header#getElements()} call to access the values.
*/
class WarningValue {
private int offs;
private int init_offs;
private final String src;
private int warnCode;
private String warnAgent;
private String warnText;
private Date warnDate;
WarningValue(final String s) {
this(s, 0);
}
WarningValue(final String s, final int offs) {
this.offs = this.init_offs = offs;
this.src = s;
consumeWarnValue();
}
/** Returns an array of the parseable warning values contained
* in the given header value, which is assumed to be a
* Warning header. Improperly formatted warning values will be
* skipped, in keeping with the philosophy of "ignore what you
* cannot understand."
* @param h Warning {@link Header} to parse
* @return array of <code>WarnValue</code> objects
*/
public static WarningValue[] getWarningValues(final Header h) {
final List<WarningValue> out = new ArrayList<WarningValue>();
final String src = h.getValue();
int offs = 0;
while(offs < src.length()) {
try {
final WarningValue wv = new WarningValue(src, offs);
out.add(wv);
offs = wv.offs;
} catch (final IllegalArgumentException e) {
final int nextComma = src.indexOf(',', offs);
if (nextComma == -1) {
break;
}
offs = nextComma + 1;
}
}
final WarningValue[] wvs = {};
return out.toArray(wvs);
}
/*
* LWS = [CRLF] 1*( SP | HT )
* CRLF = CR LF
*/
protected void consumeLinearWhitespace() {
while(offs < src.length()) {
switch(src.charAt(offs)) {
case '\r':
if (offs+2 >= src.length()
|| src.charAt(offs+1) != '\n'
|| (src.charAt(offs+2) != ' '
&& src.charAt(offs+2) != '\t')) {
return;
}
offs += 2;
break;
case ' ':
case '\t':
break;
default:
return;
}
offs++;
}
}
/*
* CHAR = <any US-ASCII character (octets 0 - 127)>
*/
private boolean isChar(final char c) {
final int i = c;
return (i >= 0 && i <= 127);
}
/*
* CTL = <any US-ASCII control character
(octets 0 - 31) and DEL (127)>
*/
private boolean isControl(final char c) {
final int i = c;
return (i == 127 || (i >=0 && i <= 31));
}
/*
* separators = "(" | ")" | "<" | ">" | "@"
* | "," | ";" | ":" | "\" | <">
* | "/" | "[" | "]" | "?" | "="
* | "{" | "}" | SP | HT
*/
private boolean isSeparator(final char c) {
return (c == '(' || c == ')' || c == '<' || c == '>'
|| c == '@' || c == ',' || c == ';' || c == ':'
|| c == '\\' || c == '\"' || c == '/'
|| c == '[' || c == ']' || c == '?' || c == '='
|| c == '{' || c == '}' || c == ' ' || c == '\t');
}
/*
* token = 1*<any CHAR except CTLs or separators>
*/
protected void consumeToken() {
if (!isTokenChar(src.charAt(offs))) {
parseError();
}
while(offs < src.length()) {
if (!isTokenChar(src.charAt(offs))) {
break;
}
offs++;
}
}
private boolean isTokenChar(final char c) {
return (isChar(c) && !isControl(c) && !isSeparator(c));
}
private static final String TOPLABEL = "\\p{Alpha}([\\p{Alnum}-]*\\p{Alnum})?";
private static final String DOMAINLABEL = "\\p{Alnum}([\\p{Alnum}-]*\\p{Alnum})?";
private static final String HOSTNAME = "(" + DOMAINLABEL + "\\.)*" + TOPLABEL + "\\.?";
private static final String IPV4ADDRESS = "\\d+\\.\\d+\\.\\d+\\.\\d+";
private static final String HOST = "(" + HOSTNAME + ")|(" + IPV4ADDRESS + ")";
private static final String PORT = "\\d*";
private static final String HOSTPORT = "(" + HOST + ")(\\:" + PORT + ")?";
private static final Pattern HOSTPORT_PATTERN = Pattern.compile(HOSTPORT);
protected void consumeHostPort() {
final Matcher m = HOSTPORT_PATTERN.matcher(src.substring(offs));
if (!m.find()) {
parseError();
}
if (m.start() != 0) {
parseError();
}
offs += m.end();
}
/*
* warn-agent = ( host [ ":" port ] ) | pseudonym
* pseudonym = token
*/
protected void consumeWarnAgent() {
final int curr_offs = offs;
try {
consumeHostPort();
warnAgent = src.substring(curr_offs, offs);
consumeCharacter(' ');
return;
} catch (final IllegalArgumentException e) {
offs = curr_offs;
}
consumeToken();
warnAgent = src.substring(curr_offs, offs);
consumeCharacter(' ');
}
/*
* quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
* qdtext = <any TEXT except <">>
*/
protected void consumeQuotedString() {
if (src.charAt(offs) != '\"') {
parseError();
}
offs++;
boolean foundEnd = false;
while(offs < src.length() && !foundEnd) {
final char c = src.charAt(offs);
if (offs + 1 < src.length() && c == '\\'
&& isChar(src.charAt(offs+1))) {
offs += 2; // consume quoted-pair
} else if (c == '\"') {
foundEnd = true;
offs++;
} else if (c != '\"' && !isControl(c)) {
offs++;
} else {
parseError();
}
}
if (!foundEnd) {
parseError();
}
}
/*
* warn-text = quoted-string
*/
protected void consumeWarnText() {
final int curr = offs;
consumeQuotedString();
warnText = src.substring(curr, offs);
}
private static final String MONTH = "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec";
private static final String WEEKDAY = "Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday";
private static final String WKDAY = "Mon|Tue|Wed|Thu|Fri|Sat|Sun";
private static final String TIME = "\\d{2}:\\d{2}:\\d{2}";
private static final String DATE3 = "(" + MONTH + ") ( |\\d)\\d";
private static final String DATE2 = "\\d{2}-(" + MONTH + ")-\\d{2}";
private static final String DATE1 = "\\d{2} (" + MONTH + ") \\d{4}";
private static final String ASCTIME_DATE = "(" + WKDAY + ") (" + DATE3 + ") (" + TIME + ") \\d{4}";
private static final String RFC850_DATE = "(" + WEEKDAY + "), (" + DATE2 + ") (" + TIME + ") GMT";
private static final String RFC1123_DATE = "(" + WKDAY + "), (" + DATE1 + ") (" + TIME + ") GMT";
private static final String HTTP_DATE = "(" + RFC1123_DATE + ")|(" + RFC850_DATE + ")|(" + ASCTIME_DATE + ")";
private static final String WARN_DATE = "\"(" + HTTP_DATE + ")\"";
private static final Pattern WARN_DATE_PATTERN = Pattern.compile(WARN_DATE);
/*
* warn-date = <"> HTTP-date <">
*/
protected void consumeWarnDate() {
final int curr = offs;
final Matcher m = WARN_DATE_PATTERN.matcher(src.substring(offs));
if (!m.lookingAt()) {
parseError();
}
offs += m.end();
warnDate = DateUtils.parseDate(src.substring(curr+1,offs-1));
}
/*
* warning-value = warn-code SP warn-agent SP warn-text [SP warn-date]
*/
protected void consumeWarnValue() {
consumeLinearWhitespace();
consumeWarnCode();
consumeWarnAgent();
consumeWarnText();
if (offs + 1 < src.length() && src.charAt(offs) == ' ' && src.charAt(offs+1) == '\"') {
consumeCharacter(' ');
consumeWarnDate();
}
consumeLinearWhitespace();
if (offs != src.length()) {
consumeCharacter(',');
}
}
protected void consumeCharacter(final char c) {
if (offs + 1 > src.length()
|| c != src.charAt(offs)) {
parseError();
}
offs++;
}
/*
* warn-code = 3DIGIT
*/
protected void consumeWarnCode() {
if (offs + 4 > src.length()
|| !Character.isDigit(src.charAt(offs))
|| !Character.isDigit(src.charAt(offs + 1))
|| !Character.isDigit(src.charAt(offs + 2))
|| src.charAt(offs + 3) != ' ') {
parseError();
}
warnCode = Integer.parseInt(src.substring(offs,offs+3));
offs += 4;
}
private void parseError() {
final String s = src.substring(init_offs);
throw new IllegalArgumentException("Bad warn code \"" + s + "\"");
}
/** Returns the 3-digit code associated with this warning.
* @return <code>int</code>
*/
public int getWarnCode() { return warnCode; }
/** Returns the "warn-agent" string associated with this warning,
* which is either the name or pseudonym of the server that added
* this particular Warning header.
* @return {@link String}
*/
public String getWarnAgent() { return warnAgent; }
/** Returns the human-readable warning text for this warning. Note
* that the original quoted-string is returned here, including
* escaping for any contained characters. In other words, if the
* header was:
* <pre>
* Warning: 110 fred "Response is stale"
* </pre>
* then this method will return <code>"\"Response is stale\""</code>
* (surrounding quotes included).
* @return {@link String}
*/
public String getWarnText() { return warnText; }
/** Returns the date and time when this warning was added, or
* <code>null</code> if a warning date was not supplied in the
* header.
* @return {@link Date}
*/
public Date getWarnDate() { return warnDate; }
/** Formats a <code>WarningValue</code> as a {@link String}
* suitable for including in a header. For example, you can:
* <pre>
* WarningValue wv = ...;
* HttpResponse resp = ...;
* resp.addHeader("Warning", wv.toString());
* </pre>
* @return {@link String}
*/
@Override
public String toString() {
if (warnDate != null) {
return String.format("%d %s %s \"%s\"", warnCode,
warnAgent, warnText, DateUtils.formatDate(warnDate));
} else {
return String.format("%d %s %s", warnCode, warnAgent, warnText);
}
}
}

View file

@ -1,42 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<!--
====================================================================
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF 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.
====================================================================
This software consists of voluntary contributions made by many
individuals on behalf of the Apache Software Foundation. For more
information on the Apache Software Foundation, please see
<http://www.apache.org/>.
-->
</head>
<body bgcolor="white">
<p>
This package contains a cache module that can be used for HTTP/1.1
client-side caching. The primary classes in this package are the
{@link org.apache.http.impl.client.cache.CachingHttpClient},
which is a drop-in replacement for
a {@link org.apache.http.impl.client.DefaultHttpClient} that adds
caching, and the {@link org.apache.http.impl.client.cache.CacheConfig}
class that can be used for configuring it.
</p>
</body>
</html>

View file

@ -1,51 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
/**
* Default HTTP client implementation.
* <p/>
* The usual execution flow can be demonstrated by the code snippet below:
* <pre>
* CloseableHttpClient httpclient = HttpClients.createDefault();
* try {
* HttpGet httpGet = new HttpGet("http://targethost/homepage");
* CloseableHttpResponse response = httpclient.execute(httpGet);
* try {
* System.out.println(response.getStatusLine());
* HttpEntity entity = response.getEntity();
* // do something useful with the response body
* // and ensure it is fully consumed
* EntityUtils.consume(entity);
* } finally {
* response.close();
* }
* } finally {
* httpclient.close();
* }
* </pre>
*/
package ch.boye.httpclientandroidlib.impl.client;