package com.github.droidfu.http;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.HashSet;
import javax.net.ssl.SSLHandshakeException;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
import android.os.SystemClock;
import android.util.Log;
public class BetterHttpRequestRetryHandler implements HttpRequestRetryHandler {
// TODO: make configurable
private static final int RETRY_SLEEP_TIME_MILLIS = 1500;
private static HashSet<Class<?>> exceptionWhitelist = new HashSet<Class<?>>();
private static HashSet<Class<?>> exceptionBlacklist = new HashSet<Class<?>>();
static {
// Retry if the server dropped connection on us
exceptionWhitelist.add(NoHttpResponseException.class);
// retry-this, since it may happens as part of a Wi-Fi to 3G failover
exceptionWhitelist.add(UnknownHostException.class);
// retry-this, since it may happens as part of a Wi-Fi to 3G failover
exceptionWhitelist.add(SocketException.class);
// never retry timeouts
// TODO: this doesn't actually capture all timeouts; I've seen timeouts being thrown as a
// plain SocketExceptiion
exceptionBlacklist.add(InterruptedIOException.class);
// never retry SSL handshake failures
exceptionBlacklist.add(SSLHandshakeException.class);
}
private int maxRetries, timesRetried;
public BetterHttpRequestRetryHandler(int maxRetries) {
this.maxRetries = maxRetries;
}
public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
boolean retry;
this.timesRetried = executionCount;
Boolean b = (Boolean) context.getAttribute(ExecutionContext.HTTP_REQ_SENT);
boolean sent = (b != null && b.booleanValue());
if (executionCount > maxRetries) {
// Do not retry if over max retry count
retry = false;
} else if (exceptionBlacklist.contains(exception.getClass())) {
// immediately cancel retry if the error is blacklisted
retry = false;
} else if (exceptionWhitelist.contains(exception.getClass())) {
// immediately retry if error is whitelisted
retry = true;
} else if (!sent) {
// for all other errors, retry only if request hasn't been fully sent yet
// TODO: refine to resend all idempotent requests
retry = true;
} else {
// otherwise do not retry
retry = false;
}
if (retry) {
Log.e(BetterHttp.LOG_TAG, "request failed (" + exception.getClass().getCanonicalName()
+ ": " + exception.getMessage() + " / attempt " + executionCount
+ "), will retry in " + RETRY_SLEEP_TIME_MILLIS / 1000.0 + " seconds");
SystemClock.sleep(RETRY_SLEEP_TIME_MILLIS);
} else {
Log.e(BetterHttp.LOG_TAG, "request failed after " + executionCount + " attempts");
exception.printStackTrace();
}
return retry;
}
public int getTimesRetried() {
return timesRetried;
}
}