mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-06 15:58:39 +09:00
Issue #1053 - Drop support Android and remove Fennec - Part 1a: Remove mobile/android
This commit is contained in:
parent
85045bf6be
commit
c9b411d6cd
3891 changed files with 0 additions and 428084 deletions
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 "backend" 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 < 0 || index >= 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 < 0 || index >= 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 < 0 || index > 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 < 0 || index >= 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 ? e==null : 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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++;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 "backend"
|
||||
* 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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 + ']';
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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;
|
||||
Loading…
Add table
Add a link
Reference in a new issue