refactor RequestHelper use Java 11 instead of Apache HTTPClient
This commit is contained in:
parent
c45dbf014e
commit
8dfcf02a0a
5
pom.xml
5
pom.xml
@ -105,11 +105,6 @@
|
|||||||
</build>
|
</build>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.httpcomponents</groupId>
|
|
||||||
<artifactId>httpclient</artifactId>
|
|
||||||
<version>4.5.13</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
<artifactId>jackson-databind</artifactId>
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
@ -29,6 +29,7 @@ final class Error {
|
|||||||
static final String URI_FORMAT = "Invalid URI format";
|
static final String URI_FORMAT = "Invalid URI format";
|
||||||
static final String RESPONSE_CODE = "Invalid response code";
|
static final String RESPONSE_CODE = "Invalid response code";
|
||||||
static final String INIT_SSL_CONTEXT = "Unable to initialize SSLContext";
|
static final String INIT_SSL_CONTEXT = "Unable to initialize SSLContext";
|
||||||
|
static final String CONNECTION = "Unable to connect to Vault server";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor hidden, this class should not be instantiated.
|
* Constructor hidden, this class should not be instantiated.
|
||||||
|
@ -4,27 +4,29 @@ import com.fasterxml.jackson.core.JsonProcessingException;
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import de.stklcode.jvault.connector.exception.*;
|
import de.stklcode.jvault.connector.exception.*;
|
||||||
import de.stklcode.jvault.connector.model.response.ErrorResponse;
|
import de.stklcode.jvault.connector.model.response.ErrorResponse;
|
||||||
import org.apache.http.HttpResponse;
|
|
||||||
import org.apache.http.client.config.RequestConfig;
|
|
||||||
import org.apache.http.client.methods.*;
|
|
||||||
import org.apache.http.client.utils.URIBuilder;
|
|
||||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
|
||||||
import org.apache.http.entity.StringEntity;
|
|
||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
|
||||||
import org.apache.http.impl.client.HttpClientBuilder;
|
|
||||||
import org.apache.http.util.EntityUtils;
|
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.TrustManagerFactory;
|
import javax.net.ssl.TrustManagerFactory;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.net.URLEncoder;
|
||||||
import java.security.*;
|
import java.net.http.HttpClient;
|
||||||
|
import java.net.http.HttpRequest;
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
|
import java.security.KeyManagementException;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.CompletionException;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class to bundle Vault HTTP requests.
|
* Helper class to bundle Vault HTTP requests.
|
||||||
*
|
*
|
||||||
@ -74,26 +76,24 @@ public final class RequestHelper implements Serializable {
|
|||||||
* @since 0.8 Added {@code token} parameter.
|
* @since 0.8 Added {@code token} parameter.
|
||||||
*/
|
*/
|
||||||
public String post(final String path, final Object payload, final String token) throws VaultConnectorException {
|
public String post(final String path, final Object payload, final String token) throws VaultConnectorException {
|
||||||
/* Initialize post */
|
// Initialize POST.
|
||||||
HttpPost post = new HttpPost(baseURL + path);
|
HttpRequest.Builder req = HttpRequest.newBuilder(URI.create(baseURL + path));
|
||||||
|
|
||||||
/* generate JSON from payload */
|
// Generate JSON from payload.
|
||||||
StringEntity input;
|
|
||||||
try {
|
try {
|
||||||
input = new StringEntity(jsonMapper.writeValueAsString(payload), StandardCharsets.UTF_8);
|
req.POST(HttpRequest.BodyPublishers.ofString(jsonMapper.writeValueAsString(payload), UTF_8));
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
throw new InvalidRequestException(Error.PARSE_RESPONSE, e);
|
throw new InvalidRequestException(Error.PARSE_RESPONSE, e);
|
||||||
}
|
}
|
||||||
input.setContentEncoding("UTF-8");
|
|
||||||
input.setContentType("application/json");
|
|
||||||
post.setEntity(input);
|
|
||||||
|
|
||||||
/* Set X-Vault-Token header */
|
req.setHeader("Content-Type", "application/json; charset=utf-8");
|
||||||
|
|
||||||
|
// Set X-Vault-Token header.
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
post.addHeader(HEADER_VAULT_TOKEN, token);
|
req.setHeader(HEADER_VAULT_TOKEN, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
return request(post, retries);
|
return request(req, retries);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -144,26 +144,24 @@ public final class RequestHelper implements Serializable {
|
|||||||
* @since 0.8 Added {@code token} parameter.
|
* @since 0.8 Added {@code token} parameter.
|
||||||
*/
|
*/
|
||||||
public String put(final String path, final Map<String, String> payload, final String token) throws VaultConnectorException {
|
public String put(final String path, final Map<String, String> payload, final String token) throws VaultConnectorException {
|
||||||
/* Initialize put */
|
// Initialize PUT.
|
||||||
HttpPut put = new HttpPut(baseURL + path);
|
HttpRequest.Builder req = HttpRequest.newBuilder(URI.create(baseURL + path));
|
||||||
|
|
||||||
/* generate JSON from payload */
|
// Generate JSON from payload.
|
||||||
StringEntity entity = null;
|
|
||||||
try {
|
try {
|
||||||
entity = new StringEntity(jsonMapper.writeValueAsString(payload));
|
req.PUT(HttpRequest.BodyPublishers.ofString(jsonMapper.writeValueAsString(payload), UTF_8));
|
||||||
} catch (UnsupportedEncodingException | JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
throw new InvalidRequestException("Payload serialization failed", e);
|
throw new InvalidRequestException("Payload serialization failed", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse parameters */
|
req.setHeader("Content-Type", "application/json; charset=utf-8");
|
||||||
put.setEntity(entity);
|
|
||||||
|
|
||||||
/* Set X-Vault-Token header */
|
// Set X-Vault-Token header.
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
put.addHeader(HEADER_VAULT_TOKEN, token);
|
req.setHeader(HEADER_VAULT_TOKEN, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
return request(put, retries);
|
return request(req, retries);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -214,15 +212,15 @@ public final class RequestHelper implements Serializable {
|
|||||||
* @since 0.8 Added {@code token} parameter.
|
* @since 0.8 Added {@code token} parameter.
|
||||||
*/
|
*/
|
||||||
public String delete(final String path, final String token) throws VaultConnectorException {
|
public String delete(final String path, final String token) throws VaultConnectorException {
|
||||||
/* Initialize delete */
|
// Initialize DELETE.
|
||||||
HttpDelete delete = new HttpDelete(baseURL + path);
|
HttpRequest.Builder req = HttpRequest.newBuilder(URI.create(baseURL + path)).DELETE();
|
||||||
|
|
||||||
/* Set X-Vault-Token header */
|
// Set X-Vault-Token header.
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
delete.addHeader(HEADER_VAULT_TOKEN, token);
|
req.setHeader(HEADER_VAULT_TOKEN, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
return request(delete, retries);
|
return request(req, retries);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -251,25 +249,31 @@ public final class RequestHelper implements Serializable {
|
|||||||
*/
|
*/
|
||||||
public String get(final String path, final Map<String, String> payload, final String token)
|
public String get(final String path, final Map<String, String> payload, final String token)
|
||||||
throws VaultConnectorException {
|
throws VaultConnectorException {
|
||||||
HttpGet get;
|
// Add parameters to URI.
|
||||||
try {
|
StringBuilder uriBuilder = new StringBuilder(baseURL + path);
|
||||||
/* Add parameters to URI */
|
|
||||||
URIBuilder uriBuilder = new URIBuilder(baseURL + path);
|
|
||||||
payload.forEach(uriBuilder::addParameter);
|
|
||||||
|
|
||||||
/* Initialize request */
|
if (!payload.isEmpty()) {
|
||||||
get = new HttpGet(uriBuilder.build());
|
uriBuilder.append("?").append(
|
||||||
|
payload.entrySet().stream().map(
|
||||||
|
par -> URLEncoder.encode(par.getKey(), UTF_8) + "=" + URLEncoder.encode(par.getValue(), UTF_8)
|
||||||
|
).collect(Collectors.joining("&"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize GET.
|
||||||
|
try {
|
||||||
|
HttpRequest.Builder req = HttpRequest.newBuilder(new URI(uriBuilder.toString()));
|
||||||
|
|
||||||
|
// Set X-Vault-Token header.
|
||||||
|
if (token != null) {
|
||||||
|
req.setHeader(HEADER_VAULT_TOKEN, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
return request(req, retries);
|
||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
/* this should never occur and may leak sensible information */
|
/* this should never occur and may leak sensible information */
|
||||||
throw new InvalidRequestException(Error.URI_FORMAT);
|
throw new InvalidRequestException(Error.URI_FORMAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set X-Vault-Token header */
|
|
||||||
if (token != null) {
|
|
||||||
get.addHeader(HEADER_VAULT_TOKEN, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
return request(get, retries);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -297,34 +301,40 @@ public final class RequestHelper implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* Execute prepared HTTP request and return result.
|
* Execute prepared HTTP request and return result.
|
||||||
*
|
*
|
||||||
* @param base Prepares Request
|
* @param requestBuilder Prepared request.
|
||||||
* @param retries number of retries
|
* @param retries Number of retries.
|
||||||
* @return HTTP response
|
* @return HTTP response
|
||||||
* @throws VaultConnectorException on connection error
|
* @throws VaultConnectorException on connection error
|
||||||
*/
|
*/
|
||||||
private String request(final HttpRequestBase base, final int retries) throws VaultConnectorException {
|
private String request(final HttpRequest.Builder requestBuilder, final int retries) throws VaultConnectorException {
|
||||||
/* Set JSON Header */
|
// Set JSON Header.
|
||||||
base.addHeader("accept", "application/json");
|
requestBuilder.setHeader("accept", "application/json");
|
||||||
|
|
||||||
CloseableHttpResponse response = null;
|
HttpClient.Builder clientBuilder = HttpClient.newBuilder();
|
||||||
|
|
||||||
try (CloseableHttpClient httpClient = HttpClientBuilder.create()
|
// Set custom timeout, if defined.
|
||||||
.setSSLSocketFactory(createSSLSocketFactory())
|
|
||||||
.build()) {
|
|
||||||
/* Set custom timeout, if defined */
|
|
||||||
if (this.timeout != null) {
|
if (this.timeout != null) {
|
||||||
base.setConfig(RequestConfig.copy(RequestConfig.DEFAULT).setConnectTimeout(timeout).build());
|
clientBuilder.connectTimeout(Duration.ofMillis(timeout));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Execute request */
|
// Set custom SSL context.
|
||||||
response = httpClient.execute(base);
|
clientBuilder.sslContext(createSSLContext());
|
||||||
|
|
||||||
|
HttpClient client = clientBuilder.build();
|
||||||
|
|
||||||
|
// Execute request.
|
||||||
|
try {
|
||||||
|
HttpResponse<InputStream> response = client.sendAsync(
|
||||||
|
requestBuilder.build(),
|
||||||
|
HttpResponse.BodyHandlers.ofInputStream()
|
||||||
|
).join();
|
||||||
|
|
||||||
/* Check if response is valid */
|
/* Check if response is valid */
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
throw new InvalidResponseException("Response unavailable");
|
throw new InvalidResponseException("Response unavailable");
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (response.getStatusLine().getStatusCode()) {
|
switch (response.statusCode()) {
|
||||||
case 200:
|
case 200:
|
||||||
return handleResult(response);
|
return handleResult(response);
|
||||||
case 204:
|
case 204:
|
||||||
@ -332,42 +342,33 @@ public final class RequestHelper implements Serializable {
|
|||||||
case 403:
|
case 403:
|
||||||
throw new PermissionDeniedException();
|
throw new PermissionDeniedException();
|
||||||
default:
|
default:
|
||||||
if (response.getStatusLine().getStatusCode() >= 500
|
if (response.statusCode() >= 500 && response.statusCode() < 600 && retries > 0) {
|
||||||
&& response.getStatusLine().getStatusCode() < 600 && retries > 0) {
|
// Retry on 5xx errors.
|
||||||
/* Retry on 5xx errors */
|
return request(requestBuilder, retries - 1);
|
||||||
return request(base, retries - 1);
|
|
||||||
} else {
|
} else {
|
||||||
/* Fail on different error code and/or no retries left */
|
// Fail on different error code and/or no retries left.
|
||||||
handleError(response);
|
handleError(response);
|
||||||
|
|
||||||
/* Throw exception without details, if response entity is empty. */
|
// Throw exception without details, if response entity is empty.
|
||||||
throw new InvalidResponseException(Error.RESPONSE_CODE,
|
throw new InvalidResponseException(Error.RESPONSE_CODE, response.statusCode());
|
||||||
response.getStatusLine().getStatusCode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new InvalidResponseException(Error.READ_RESPONSE, e);
|
|
||||||
} finally {
|
|
||||||
if (response != null && response.getEntity() != null) {
|
|
||||||
try {
|
|
||||||
EntityUtils.consume(response.getEntity());
|
|
||||||
} catch (IOException ignored) {
|
|
||||||
// Exception ignored.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (CompletionException e) {
|
||||||
|
throw new ConnectionException(Error.CONNECTION, e.getCause());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a custom socket factory from trusted CA certificate.
|
* Create a custom SSL context from trusted CA certificate.
|
||||||
*
|
*
|
||||||
* @return The factory.
|
* @return The context.
|
||||||
* @throws TlsException An error occurred during initialization of the SSL context.
|
* @throws TlsException An error occurred during initialization of the SSL context.
|
||||||
* @since 0.8.0
|
* @since 0.8.0
|
||||||
|
* @since 0.10 Generate {@link SSLContext} instead of Apache {@code SSLConnectionSocketFactory}
|
||||||
*/
|
*/
|
||||||
private SSLConnectionSocketFactory createSSLSocketFactory() throws TlsException {
|
private SSLContext createSSLContext() throws TlsException {
|
||||||
try {
|
try {
|
||||||
// Create context..
|
// Create context.
|
||||||
SSLContext context = SSLContext.getInstance(tlsVersion);
|
SSLContext context = SSLContext.getInstance(tlsVersion);
|
||||||
|
|
||||||
if (trustedCaCert != null) {
|
if (trustedCaCert != null) {
|
||||||
@ -384,12 +385,7 @@ public final class RequestHelper implements Serializable {
|
|||||||
context.init(null, null, null);
|
context.init(null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SSLConnectionSocketFactory(
|
return context;
|
||||||
context,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
SSLConnectionSocketFactory.getDefaultHostnameVerifier()
|
|
||||||
);
|
|
||||||
} catch (CertificateException | NoSuchAlgorithmException | KeyStoreException | IOException | KeyManagementException e) {
|
} catch (CertificateException | NoSuchAlgorithmException | KeyStoreException | IOException | KeyManagementException e) {
|
||||||
throw new TlsException(Error.INIT_SSL_CONTEXT, e);
|
throw new TlsException(Error.INIT_SSL_CONTEXT, e);
|
||||||
}
|
}
|
||||||
@ -402,9 +398,8 @@ public final class RequestHelper implements Serializable {
|
|||||||
* @return Complete response body as String
|
* @return Complete response body as String
|
||||||
* @throws InvalidResponseException on reading errors
|
* @throws InvalidResponseException on reading errors
|
||||||
*/
|
*/
|
||||||
private String handleResult(final HttpResponse response) throws InvalidResponseException {
|
private String handleResult(final HttpResponse<InputStream> response) throws InvalidResponseException {
|
||||||
try (BufferedReader br = new BufferedReader(
|
try (BufferedReader br = new BufferedReader(new InputStreamReader(response.body()))) {
|
||||||
new InputStreamReader(response.getEntity().getContent()))) {
|
|
||||||
return br.lines().collect(Collectors.joining("\n"));
|
return br.lines().collect(Collectors.joining("\n"));
|
||||||
} catch (IOException ignored) {
|
} catch (IOException ignored) {
|
||||||
throw new InvalidResponseException(Error.READ_RESPONSE, 200);
|
throw new InvalidResponseException(Error.READ_RESPONSE, 200);
|
||||||
@ -417,18 +412,16 @@ public final class RequestHelper implements Serializable {
|
|||||||
* @param response The raw HTTP response (assuming status code 5xx)
|
* @param response The raw HTTP response (assuming status code 5xx)
|
||||||
* @throws VaultConnectorException Expected exception with details to throw
|
* @throws VaultConnectorException Expected exception with details to throw
|
||||||
*/
|
*/
|
||||||
private void handleError(final HttpResponse response) throws VaultConnectorException {
|
private void handleError(final HttpResponse<InputStream> response) throws VaultConnectorException {
|
||||||
if (response.getEntity() != null) {
|
if (response.body() != null) {
|
||||||
try (BufferedReader br = new BufferedReader(
|
try (BufferedReader br = new BufferedReader(new InputStreamReader(response.body()))) {
|
||||||
new InputStreamReader(response.getEntity().getContent()))) {
|
|
||||||
String responseString = br.lines().collect(Collectors.joining("\n"));
|
String responseString = br.lines().collect(Collectors.joining("\n"));
|
||||||
ErrorResponse er = jsonMapper.readValue(responseString, ErrorResponse.class);
|
ErrorResponse er = jsonMapper.readValue(responseString, ErrorResponse.class);
|
||||||
/* Check for "permission denied" response */
|
/* Check for "permission denied" response */
|
||||||
if (!er.getErrors().isEmpty() && er.getErrors().get(0).equals("permission denied")) {
|
if (!er.getErrors().isEmpty() && er.getErrors().get(0).equals("permission denied")) {
|
||||||
throw new PermissionDeniedException();
|
throw new PermissionDeniedException();
|
||||||
}
|
}
|
||||||
throw new InvalidResponseException(Error.RESPONSE_CODE,
|
throw new InvalidResponseException(Error.RESPONSE_CODE, response.statusCode(), er.toString());
|
||||||
response.getStatusLine().getStatusCode(), er.toString());
|
|
||||||
} catch (IOException ignored) {
|
} catch (IOException ignored) {
|
||||||
// Exception ignored.
|
// Exception ignored.
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user