#9 Number of retries and custom timeout

This commit is contained in:
Stefan Kalscheuer 2017-04-10 13:22:54 +02:00
parent 5d46e75068
commit c1f6ee891b
4 changed files with 107 additions and 21 deletions

View File

@ -1,3 +1,7 @@
## 0.6.0 [work in progress]
* [feature] Custom timeout and number of retries (#9)
* [fix] `SecretResponse` does not throw NPE on `get(key)` and `getData()`
## 0.5.0 [2017-03-18] ## 0.5.0 [2017-03-18]
* [feature] Convenience methods for DB credentials (#7) * [feature] Convenience methods for DB credentials (#7)
* [fix] Minor bugfix in TokenBuilder * [fix] Minor bugfix in TokenBuilder

View File

@ -4,7 +4,7 @@
<groupId>de.stklcode.jvault</groupId> <groupId>de.stklcode.jvault</groupId>
<artifactId>connector</artifactId> <artifactId>connector</artifactId>
<version>0.5.1-SNAPSHOT</version> <version>0.6.0-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>

View File

@ -23,11 +23,13 @@ import de.stklcode.jvault.connector.model.*;
import de.stklcode.jvault.connector.model.response.*; import de.stklcode.jvault.connector.model.response.*;
import de.stklcode.jvault.connector.model.response.embedded.AuthMethod; import de.stklcode.jvault.connector.model.response.embedded.AuthMethod;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*; import org.apache.http.client.methods.*;
import org.apache.http.client.utils.URIBuilder; import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity; import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.util.EntityUtils; import org.apache.http.util.EntityUtils;
import javax.net.ssl.*; import javax.net.ssl.*;
@ -64,6 +66,8 @@ public class HTTPVaultConnector implements VaultConnector {
private final String baseURL; /* Base URL of Vault */ private final String baseURL; /* Base URL of Vault */
private final SSLContext sslContext; /* Custom SSLSocketFactory */ private final SSLContext sslContext; /* Custom SSLSocketFactory */
private final int retries; /* Number of retries on 5xx errors */
private final Integer timeout; /* Timeout in milliseconds */
private boolean authorized = false; /* authorization status */ private boolean authorized = false; /* authorization status */
private String token; /* current token */ private String token; /* current token */
@ -115,11 +119,26 @@ public class HTTPVaultConnector implements VaultConnector {
* @param sslContext Custom SSL Context * @param sslContext Custom SSL Context
*/ */
public HTTPVaultConnector(String hostname, boolean useTLS, Integer port, String prefix, SSLContext sslContext) { public HTTPVaultConnector(String hostname, boolean useTLS, Integer port, String prefix, SSLContext sslContext) {
this(hostname, useTLS, port, prefix, sslContext, 0, null);
}
/**
* Create connector using hostname, schema, port, path and trusted certificate.
*
* @param hostname The hostname
* @param useTLS If TRUE, use HTTPS, otherwise HTTP
* @param port The port
* @param prefix HTTP API prefix (default: /v1/)
* @param sslContext Custom SSL Context
*/
public HTTPVaultConnector(String hostname, boolean useTLS, Integer port, String prefix, SSLContext sslContext, int numberOfRetries, Integer timeout) {
this(((useTLS) ? "https" : "http") + this(((useTLS) ? "https" : "http") +
"://" + hostname + "://" + hostname +
((port != null) ? ":" + port : "") + ((port != null) ? ":" + port : "") +
prefix, prefix,
sslContext); sslContext,
numberOfRetries,
timeout);
} }
/** /**
@ -138,8 +157,32 @@ public class HTTPVaultConnector implements VaultConnector {
* @param sslContext Custom SSL Context * @param sslContext Custom SSL Context
*/ */
public HTTPVaultConnector(String baseURL, SSLContext sslContext) { public HTTPVaultConnector(String baseURL, SSLContext sslContext) {
this(baseURL, sslContext, 0, null);
}
/**
* Create connector using full URL and trusted certificate.
*
* @param baseURL The URL
* @param sslContext Custom SSL Context
* @param numberOfRetries number of retries on 5xx errors
*/
public HTTPVaultConnector(String baseURL, SSLContext sslContext, int numberOfRetries) {
this(baseURL, sslContext, numberOfRetries, null);
}
/**
* Create connector using full URL and trusted certificate.
*
* @param baseURL The URL
* @param sslContext Custom SSL Context
* @param numberOfRetries number of retries on 5xx errors
*/
public HTTPVaultConnector(String baseURL, SSLContext sslContext, int numberOfRetries, Integer timeout) {
this.baseURL = baseURL; this.baseURL = baseURL;
this.sslContext = sslContext; this.sslContext = sslContext;
this.retries = numberOfRetries;
this.timeout = timeout;
this.jsonMapper = new ObjectMapper(); this.jsonMapper = new ObjectMapper();
} }
@ -634,7 +677,7 @@ public class HTTPVaultConnector implements VaultConnector {
if (token != null) if (token != null)
post.addHeader("X-Vault-Token", token); post.addHeader("X-Vault-Token", token);
return request(post); return request(post, retries);
} }
/** /**
@ -661,7 +704,7 @@ public class HTTPVaultConnector implements VaultConnector {
if (token != null) if (token != null)
put.addHeader("X-Vault-Token", token); put.addHeader("X-Vault-Token", token);
return request(put); return request(put, retries);
} }
/** /**
@ -678,7 +721,7 @@ public class HTTPVaultConnector implements VaultConnector {
if (token != null) if (token != null)
delete.addHeader("X-Vault-Token", token); delete.addHeader("X-Vault-Token", token);
return request(delete); return request(delete, retries);
} }
/** /**
@ -701,22 +744,27 @@ public class HTTPVaultConnector implements VaultConnector {
if (token != null) if (token != null)
get.addHeader("X-Vault-Token", token); get.addHeader("X-Vault-Token", token);
return request(get); return request(get, retries);
} }
/** /**
* Execute prepared HTTP request and return result. * Execute prepared HTTP request and return result.
* *
* @param base Prepares Request * @param base Prepares Request
* @param retries number of retries
* @return HTTP response * @return HTTP response
* @throws VaultConnectorException on connection error * @throws VaultConnectorException on connection error
*/ */
private String request(HttpRequestBase base) throws VaultConnectorException { private String request(HttpRequestBase base, int retries) throws VaultConnectorException {
/* Set JSON Header */ /* Set JSON Header */
base.addHeader("accept", "application/json"); base.addHeader("accept", "application/json");
HttpResponse response = null; HttpResponse response = null;
try (CloseableHttpClient httpClient = HttpClientBuilder.create().setSSLContext(sslContext).build()) { try (CloseableHttpClient httpClient = HttpClientBuilder.create().setSSLContext(sslContext).build()) {
/* Set custom timeout, if defined */
if (this.timeout != null)
base.setConfig(RequestConfig.copy(RequestConfig.DEFAULT).setConnectTimeout(timeout).build());
/* Execute request */
response = httpClient.execute(base); response = httpClient.execute(base);
/* Check if response is valid */ /* Check if response is valid */
if (response == null) if (response == null)
@ -733,20 +781,26 @@ public class HTTPVaultConnector implements VaultConnector {
case 403: case 403:
throw new PermissionDeniedException(); throw new PermissionDeniedException();
default: default:
InvalidResponseException ex = new InvalidResponseException("Invalid response code") if (response.getStatusLine().getStatusCode() >= 500 && response.getStatusLine().getStatusCode() < 600 && retries > 0) {
.withStatusCode(response.getStatusLine().getStatusCode()); /* Retry on 5xx errors */
if (response.getEntity() != null) { return request(base, retries - 1);
try (BufferedReader br = new BufferedReader(new InputStreamReader(response.getEntity().getContent()))) { } else {
String responseString = br.lines().collect(Collectors.joining("\n")); /* Fail on different error code and/or no retries left */
ErrorResponse er = jsonMapper.readValue(responseString, ErrorResponse.class); InvalidResponseException ex = new InvalidResponseException("Invalid response code")
/* Check for "permission denied" response */ .withStatusCode(response.getStatusLine().getStatusCode());
if (er.getErrors().size() > 0 && er.getErrors().get(0).equals("permission denied")) if (response.getEntity() != null) {
throw new PermissionDeniedException(); try (BufferedReader br = new BufferedReader(new InputStreamReader(response.getEntity().getContent()))) {
throw ex.withResponse(er.toString()); String responseString = br.lines().collect(Collectors.joining("\n"));
} catch (IOException ignored) { ErrorResponse er = jsonMapper.readValue(responseString, ErrorResponse.class);
/* Check for "permission denied" response */
if (er.getErrors().size() > 0 && er.getErrors().get(0).equals("permission denied"))
throw new PermissionDeniedException();
throw ex.withResponse(er.toString());
} catch (IOException ignored) {
}
} }
throw ex;
} }
throw ex;
} }
} catch (IOException e) { } catch (IOException e) {
throw new InvalidResponseException("Unable to read response", e); throw new InvalidResponseException("Unable to read response", e);

View File

@ -43,12 +43,15 @@ public class HTTPVaultConnectorFactory extends VaultConnectorFactory {
public static final Integer DEFAULT_PORT = 8200; public static final Integer DEFAULT_PORT = 8200;
public static final boolean DEFAULT_TLS = true; public static final boolean DEFAULT_TLS = true;
public static final String DEFAULT_PREFIX = "/v1/"; public static final String DEFAULT_PREFIX = "/v1/";
public static final int DEFAULT_NUMBER_OF_RETRIES = 0;
private String host; private String host;
private Integer port; private Integer port;
private boolean tls; private boolean tls;
private String prefix; private String prefix;
private SSLContext sslContext; private SSLContext sslContext;
private int numberOfRetries;
private Integer timeout;
/** /**
* Default empty constructor. * Default empty constructor.
@ -59,6 +62,7 @@ public class HTTPVaultConnectorFactory extends VaultConnectorFactory {
port = DEFAULT_PORT; port = DEFAULT_PORT;
tls = DEFAULT_TLS; tls = DEFAULT_TLS;
prefix = DEFAULT_PREFIX; prefix = DEFAULT_PREFIX;
numberOfRetries = DEFAULT_NUMBER_OF_RETRIES;
} }
/** /**
@ -150,9 +154,33 @@ public class HTTPVaultConnectorFactory extends VaultConnectorFactory {
return this; return this;
} }
/**
* Define the number of retries to attempt on 5xx errors.
*
* @param numberOfRetries The number of retries to attempt on 5xx errors (default: 0)
* @return self
* @since 0.6.0
*/
public HTTPVaultConnectorFactory withNumberOfRetries(int numberOfRetries) {
this.numberOfRetries = numberOfRetries;
return this;
}
/**
* Define a custom timeout for the HTTP connection.
*
* @param milliseconds Timeout value in milliseconds.
* @return self
* @since 0.6.0
*/
public HTTPVaultConnectorFactory withTimeout(int milliseconds) {
this.timeout = milliseconds;
return this;
}
@Override @Override
public HTTPVaultConnector build() { public HTTPVaultConnector build() {
return new HTTPVaultConnector(host, tls, port, prefix, sslContext); return new HTTPVaultConnector(host, tls, port, prefix, sslContext, numberOfRetries, timeout);
} }
/** /**