package com.litesuits.http.impl.apache;
import android.content.Context;
import com.litesuits.http.exception.HttpNetException;
import com.litesuits.http.exception.NetException;
import com.litesuits.http.log.HttpLog;
import com.litesuits.http.network.Network;
import org.apache.http.NoHttpResponseException;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.protocol.HttpContext;
import javax.net.ssl.SSLException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.*;
import java.util.HashSet;
/**
* determine whether to send request try again.
* 试验发现,当url非法时,{@link HttpURLConnection}报MalformedURLException异常,
* 而apache httpclient会报IllegalStateException异常。
* 当url非法,和ssl错误时,不用重试。 当有可用网络但连接不稳定时,一般会报IO异常,此种情况尝试重试,以提高成功率。
* 继承StandardHttpRequestRetryHandler因用到其判断请求方式是否幂等和连接是否取消等方法。
*
* @author MaTianyu
* 2014-1-1下午5:03:46
*/
public class HttpRetryHandler extends StandardHttpRequestRetryHandler {
public static final String TAG = HttpRetryHandler.class.getSimpleName();
private HashSet<Class<?>> exceptionWhitelist = new HashSet<Class<?>>();
private HashSet<Class<?>> exceptionBlacklist = new HashSet<Class<?>>();
public final int retrySleepTimeMS;
/**
* @param retrySleepTimeMS if the network is unstable, wait retrySleepTimeMS then start retry.
* @param requestSentRetryEnabled true if it's OK to retry requests that have been sent
*/
public HttpRetryHandler(int retrySleepTimeMS, boolean requestSentRetryEnabled) {
super(0, requestSentRetryEnabled);
this.retrySleepTimeMS = retrySleepTimeMS;
exceptionWhitelist.add(NoHttpResponseException.class);
exceptionWhitelist.add(SocketException.class);
exceptionWhitelist.add(SocketTimeoutException.class);
exceptionWhitelist.add(ConnectTimeoutException.class);
exceptionBlacklist.add(UnknownHostException.class);
exceptionBlacklist.add(FileNotFoundException.class);
exceptionBlacklist.add(SSLException.class);
exceptionBlacklist.add(ConnectException.class);
}
public boolean retryRequest(IOException exception, int retryCount, int maxRetries, HttpContext context,
Context appContext) throws HttpNetException, InterruptedException {
boolean retry = true;
if (retryCount > maxRetries) {
if (HttpLog.isPrint) {
HttpLog.w(TAG, "retry count > max retry times..");
}
throw new HttpNetException(exception);
} else if (isInList(exceptionBlacklist, exception)) {
if (HttpLog.isPrint) {
HttpLog.w(TAG, "exception in blacklist..");
}
retry = false;
} else if (isInList(exceptionWhitelist, exception)) {
if (HttpLog.isPrint) {
HttpLog.w(TAG, "exception in whitelist..");
}
retry = true;
}
if (retry) {
// 判断连接是否取消,非否幂等请求是否重试
retry = retryRequest(context);
}
if (retry) {
if (appContext != null) {
if (Network.isConnected(appContext)) {
HttpLog.d(TAG, "Network isConnected, retry now");
} else if (Network.isConnectedOrConnecting(appContext)) {
if (HttpLog.isPrint) {
HttpLog.v(TAG, "Network is Connected Or Connecting, wait for retey : "
+ retrySleepTimeMS + " ms");
}
Thread.sleep(retrySleepTimeMS);
} else {
HttpLog.d(TAG, "Without any Network , immediately cancel retry");
throw new HttpNetException(NetException.NetworkNotAvilable);
}
} else {
if (HttpLog.isPrint) {
HttpLog.v(TAG, "app context is null..");
HttpLog.v(TAG, "wait for retry : " + retrySleepTimeMS + " ms");
}
Thread.sleep(retrySleepTimeMS);
}
}
if (HttpLog.isPrint) {
HttpLog.i(TAG, "retry: " + retry + " , retryCount: " + retryCount + " , exception: " + exception);
}
return retry;
}
protected boolean isInList(HashSet<Class<?>> list, Throwable error) {
for (Class<?> aList : list) {
if (aList.isInstance(error)) {
return true;
}
}
return false;
}
}