Skip to content

Commit

Permalink
#270 Retry transient ssl exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
guperrot committed Dec 9, 2016
1 parent 129201d commit 99c9349
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;

import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;

import static com.microsoft.azure.mobile.ingestion.http.HttpUtils.isRecoverableError;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
Expand Down Expand Up @@ -39,5 +42,10 @@ public void isRecoverableErrorTest() {
assertFalse(isRecoverableError(new HttpException(413)));
assertTrue(isRecoverableError(new HttpException(429)));
assertTrue(isRecoverableError(new HttpException(401)));
assertTrue(isRecoverableError(new SSLException("Write error: ssl=0x59c28f90: I/O error during system call, Connection timed out")));
assertFalse(isRecoverableError(new SSLHandshakeException("java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.")));
assertFalse(isRecoverableError(new SSLException("java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty")));
assertTrue(isRecoverableError(new SSLException("Read error: ssl=0x9dd07200: I/O error during system call, Connection reset by peer")));
assertTrue(isRecoverableError(new SSLException("SSL handshake aborted: ssl=0x1cc160: I/O error during system call, Connection reset by peer")));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,55 @@
import java.io.InterruptedIOException;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.regex.Pattern;

import javax.net.ssl.SSLException;

/**
* HTTP utilities.
*/
public final class HttpUtils {

/**
* Types of exception that can be retried, no matter what the details are. Sub-classes are included.
*/
private static final Class[] RECOVERABLE_EXCEPTIONS = {
EOFException.class,
InterruptedIOException.class,
SocketException.class,
UnknownHostException.class
};

/**
* Some transient exceptions can only be detected by interpreting the message...
*/
private static Pattern CONNECTION_ISSUE_PATTERN = Pattern.compile("connection (time|reset)");

@VisibleForTesting
HttpUtils() {
}

/**
* Check whether an exception/error describes a recoverable error or not.
*
* @param t exception or error.
* @return true if the exception/error should be retried, false otherwise.
*/
public static boolean isRecoverableError(Throwable t) {

/* Check HTTP exception details. */
if (t instanceof HttpException) {
HttpException exception = (HttpException) t;
int code = exception.getStatusCode();
return code >= 500 || code == 408 || code == 429 || code == 401;
}

/* Check for a generic exception to retry. */
for (Class<?> type : RECOVERABLE_EXCEPTIONS)
if (type.isAssignableFrom(t.getClass()))
return true;
return false;

/* Check corner cases. */
return t instanceof SSLException && CONNECTION_ISSUE_PATTERN.matcher(t.getMessage().toLowerCase()).find();
}
}

0 comments on commit 99c9349

Please sign in to comment.