Skip to content

Commit

Permalink
Add possibility to retry requests
Browse files Browse the repository at this point in the history
Fixes #83
  • Loading branch information
michel-kraemer committed Jun 23, 2019
1 parent 9f71960 commit c6d616a
Show file tree
Hide file tree
Showing 8 changed files with 346 additions and 33 deletions.
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -211,6 +211,8 @@ is interpreted as undefined. <em>(default: <code>-1</code>)</em></dd>
<dd>The maximum time in milliseconds to wait for data from the server.
A value of <code>0</code> (zero) means infinite timeout. A negative value
is interpreted as undefined. <em>(default: <code>-1</code>)</em></dd>
<dt>retries</dt>
<dd>Specifies the maximum number of retry attempts if a request has failed. By default, requests are never retried and the task fails immediately if the first request does not succeed. If the value is greater than <code>0</code>, failed requests are retried regardless of the actual error. This includes failed connection attempts and file-not-found errors (404). A negative value means infinite retries. <em>(default: <code>0</code>)</em></dd>
</dl>

### Authentication
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/de/undercouch/gradle/tasks/download/Download.java
Expand Up @@ -188,6 +188,11 @@ public void readTimeout(int milliseconds) {
action.readTimeout(milliseconds);
}

@Override
public void retries(int retries) {
action.retries(retries);
}

@Override
public void downloadTaskDir(Object dir) {
action.downloadTaskDir(dir);
Expand Down Expand Up @@ -283,6 +288,11 @@ public int getReadTimeout() {
return action.getReadTimeout();
}

@Override
public int getRetries() {
return action.getRetries();
}

@Override
public File getDownloadTaskDir() {
return action.getDownloadTaskDir();
Expand Down
Expand Up @@ -74,7 +74,7 @@ public class DownloadAction implements DownloadSpec {
GradleVersion.version("2.0");

private final Project project;
private List<URL> sources = new ArrayList<URL>(1);
private List<URL> sources = new ArrayList<>(1);
private File dest;
private boolean quiet = false;
private boolean overwrite = true;
Expand All @@ -87,6 +87,7 @@ public class DownloadAction implements DownloadSpec {
private boolean acceptAnyCertificate = false;
private int connectTimeoutMs = -1;
private int readTimeoutMs = -1;
private int retries = 0;
private File downloadTaskDir;
private boolean tempAndMove = false;
private UseETag useETag = UseETag.FALSE;
Expand Down Expand Up @@ -244,7 +245,7 @@ private void executeHttpProtocol(URL src, HttpClientFactory clientFactory,

//create HTTP client
CloseableHttpClient client = clientFactory.createHttpClient(
httpHost, acceptAnyCertificate);
httpHost, acceptAnyCertificate, retries);

//open URL connection
String etag = null;
Expand Down Expand Up @@ -875,6 +876,11 @@ public void readTimeout(int milliseconds) {
this.readTimeoutMs = milliseconds;
}

@Override
public void retries(int retries) {
this.retries = retries;
}

@Override
public void downloadTaskDir(Object dir) {
if (dir instanceof Closure) {
Expand Down Expand Up @@ -1002,6 +1008,11 @@ public int getReadTimeout() {
return readTimeoutMs;
}

@Override
public int getRetries() {
return retries;
}

@Override
public File getDownloadTaskDir() {
return downloadTaskDir;
Expand Down
Expand Up @@ -132,6 +132,14 @@ public interface DownloadSpec {
*/
void readTimeout(int milliseconds);

/**
* Specifies the maximum number of retry attempts if a request has failed.
* By default, requests are never retried and the task fails immediately if
* the first request does not succeed.
* @param retries the maximum number of retries (default: 0)
*/
void retries(int retries);

/**
* Specifies the directory where gradle-download-task stores information
* that should persist between builds
Expand Down Expand Up @@ -258,6 +266,11 @@ public interface DownloadSpec {
* server
*/
int getReadTimeout();

/**
* @return the maximum number of retries
*/
int getRetries();

/**
* @return the directory where gradle-download-task stores information
Expand Down
Expand Up @@ -20,6 +20,7 @@
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
* An implementation of {@link HttpClientFactory} that caches created clients
Expand All @@ -31,11 +32,11 @@ public class CachingHttpClientFactory extends DefaultHttpClientFactory {

@Override
public CloseableHttpClient createHttpClient(HttpHost httpHost,
boolean acceptAnyCertificate) {
CacheKey key = new CacheKey(httpHost, acceptAnyCertificate);
boolean acceptAnyCertificate, int retries) {
CacheKey key = new CacheKey(httpHost, acceptAnyCertificate, retries);
CloseableHttpClient c = cachedClients.get(key);
if (c == null) {
c = super.createHttpClient(httpHost, acceptAnyCertificate);
c = super.createHttpClient(httpHost, acceptAnyCertificate, retries);
cachedClients.put(key, c);
}
return c;
Expand All @@ -58,40 +59,31 @@ public void close() throws IOException {
private static class CacheKey {
private final HttpHost httpHost;
private final boolean acceptAnyCertificate;
private final int retries;

CacheKey(HttpHost httpHost, boolean acceptAnyCertificate) {
CacheKey(HttpHost httpHost, boolean acceptAnyCertificate, int retries) {
this.httpHost = httpHost;
this.acceptAnyCertificate = acceptAnyCertificate;
this.retries = retries;
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (acceptAnyCertificate ? 1231 : 1237);
result = prime * result + ((httpHost == null) ? 0 : httpHost.hashCode());
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (obj == null) {
if (o == null || getClass() != o.getClass()) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
CacheKey other = (CacheKey)obj;
if (acceptAnyCertificate != other.acceptAnyCertificate) {
return false;
}
if (httpHost == null) {
return other.httpHost == null;
}
return httpHost.equals(other.httpHost);
CacheKey cacheKey = (CacheKey)o;
return acceptAnyCertificate == cacheKey.acceptAnyCertificate &&
retries == cacheKey.retries &&
httpHost.equals(cacheKey.httpHost);
}

@Override
public int hashCode() {
return Objects.hash(httpHost, acceptAnyCertificate, retries);
}
}
}
Expand Up @@ -15,6 +15,7 @@
package de.undercouch.gradle.tasks.download.internal;

import de.undercouch.gradle.tasks.download.org.apache.http.HttpHost;
import de.undercouch.gradle.tasks.download.org.apache.http.client.HttpRequestRetryHandler;
import de.undercouch.gradle.tasks.download.org.apache.http.config.Registry;
import de.undercouch.gradle.tasks.download.org.apache.http.config.RegistryBuilder;
import de.undercouch.gradle.tasks.download.org.apache.http.conn.HttpClientConnectionManager;
Expand All @@ -25,17 +26,19 @@
import de.undercouch.gradle.tasks.download.org.apache.http.impl.client.HttpClientBuilder;
import de.undercouch.gradle.tasks.download.org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import de.undercouch.gradle.tasks.download.org.apache.http.impl.conn.SystemDefaultRoutePlanner;
import de.undercouch.gradle.tasks.download.org.apache.http.protocol.HttpContext;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

/**
* Default implementation of {@link HttpClientFactory}. Creates a new client
* every time {@link #createHttpClient(HttpHost, boolean)}
* every time {@link #createHttpClient(HttpHost, boolean, int)}
* is called. The caller is responsible for closing this client.
* @author Michel Kraemer
*/
Expand All @@ -49,9 +52,21 @@ public class DefaultHttpClientFactory implements HttpClientFactory {

@Override
public CloseableHttpClient createHttpClient(HttpHost httpHost,
boolean acceptAnyCertificate) {
boolean acceptAnyCertificate, final int retries) {
HttpClientBuilder builder = HttpClientBuilder.create();


//configure retries
if (retries == 0) {
builder.disableAutomaticRetries();
} else {
builder.setRetryHandler(new HttpRequestRetryHandler() {
@Override
public boolean retryRequest(IOException e, int i, HttpContext httpContext) {
return retries < 0 || i <= retries;
}
});
}

//configure proxy from system environment
builder.setRoutePlanner(new SystemDefaultRoutePlanner(null));

Expand Down
Expand Up @@ -28,8 +28,9 @@ public interface HttpClientFactory {
* @param acceptAnyCertificate true if HTTPS certificate verification
* errors should be ignored and any certificate (even an invalid one)
* should be accepted
* @param retries the number of retries to perform if an HTTP request fails
* @return the HTTP client
*/
CloseableHttpClient createHttpClient(HttpHost httpHost,
boolean acceptAnyCertificate);
boolean acceptAnyCertificate, int retries);
}

0 comments on commit c6d616a

Please sign in to comment.