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>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.13</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
|
@ -29,6 +29,7 @@ final class Error {
|
||||
static final String URI_FORMAT = "Invalid URI format";
|
||||
static final String RESPONSE_CODE = "Invalid response code";
|
||||
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.
|
||||
|
@ -4,27 +4,29 @@ import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import de.stklcode.jvault.connector.exception.*;
|
||||
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.TrustManagerFactory;
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.*;
|
||||
import java.net.URLEncoder;
|
||||
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.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
/**
|
||||
* Helper class to bundle Vault HTTP requests.
|
||||
*
|
||||
@ -74,26 +76,24 @@ public final class RequestHelper implements Serializable {
|
||||
* @since 0.8 Added {@code token} parameter.
|
||||
*/
|
||||
public String post(final String path, final Object payload, final String token) throws VaultConnectorException {
|
||||
/* Initialize post */
|
||||
HttpPost post = new HttpPost(baseURL + path);
|
||||
// Initialize POST.
|
||||
HttpRequest.Builder req = HttpRequest.newBuilder(URI.create(baseURL + path));
|
||||
|
||||
/* generate JSON from payload */
|
||||
StringEntity input;
|
||||
// Generate JSON from payload.
|
||||
try {
|
||||
input = new StringEntity(jsonMapper.writeValueAsString(payload), StandardCharsets.UTF_8);
|
||||
req.POST(HttpRequest.BodyPublishers.ofString(jsonMapper.writeValueAsString(payload), UTF_8));
|
||||
} catch (JsonProcessingException 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) {
|
||||
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.
|
||||
*/
|
||||
public String put(final String path, final Map<String, String> payload, final String token) throws VaultConnectorException {
|
||||
/* Initialize put */
|
||||
HttpPut put = new HttpPut(baseURL + path);
|
||||
// Initialize PUT.
|
||||
HttpRequest.Builder req = HttpRequest.newBuilder(URI.create(baseURL + path));
|
||||
|
||||
/* generate JSON from payload */
|
||||
StringEntity entity = null;
|
||||
// Generate JSON from payload.
|
||||
try {
|
||||
entity = new StringEntity(jsonMapper.writeValueAsString(payload));
|
||||
} catch (UnsupportedEncodingException | JsonProcessingException e) {
|
||||
req.PUT(HttpRequest.BodyPublishers.ofString(jsonMapper.writeValueAsString(payload), UTF_8));
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new InvalidRequestException("Payload serialization failed", e);
|
||||
}
|
||||
|
||||
/* Parse parameters */
|
||||
put.setEntity(entity);
|
||||
req.setHeader("Content-Type", "application/json; charset=utf-8");
|
||||
|
||||
/* Set X-Vault-Token header */
|
||||
// Set X-Vault-Token header.
|
||||
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.
|
||||
*/
|
||||
public String delete(final String path, final String token) throws VaultConnectorException {
|
||||
/* Initialize delete */
|
||||
HttpDelete delete = new HttpDelete(baseURL + path);
|
||||
// Initialize DELETE.
|
||||
HttpRequest.Builder req = HttpRequest.newBuilder(URI.create(baseURL + path)).DELETE();
|
||||
|
||||
/* Set X-Vault-Token header */
|
||||
// Set X-Vault-Token header.
|
||||
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)
|
||||
throws VaultConnectorException {
|
||||
HttpGet get;
|
||||
try {
|
||||
/* Add parameters to URI */
|
||||
URIBuilder uriBuilder = new URIBuilder(baseURL + path);
|
||||
payload.forEach(uriBuilder::addParameter);
|
||||
// Add parameters to URI.
|
||||
StringBuilder uriBuilder = new StringBuilder(baseURL + path);
|
||||
|
||||
/* Initialize request */
|
||||
get = new HttpGet(uriBuilder.build());
|
||||
if (!payload.isEmpty()) {
|
||||
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) {
|
||||
/* this should never occur and may leak sensible information */
|
||||
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.
|
||||
*
|
||||
* @param base Prepares Request
|
||||
* @param retries number of retries
|
||||
* @param requestBuilder Prepared request.
|
||||
* @param retries Number of retries.
|
||||
* @return HTTP response
|
||||
* @throws VaultConnectorException on connection error
|
||||
*/
|
||||
private String request(final HttpRequestBase base, final int retries) throws VaultConnectorException {
|
||||
/* Set JSON Header */
|
||||
base.addHeader("accept", "application/json");
|
||||
private String request(final HttpRequest.Builder requestBuilder, final int retries) throws VaultConnectorException {
|
||||
// Set JSON Header.
|
||||
requestBuilder.setHeader("accept", "application/json");
|
||||
|
||||
CloseableHttpResponse response = null;
|
||||
HttpClient.Builder clientBuilder = HttpClient.newBuilder();
|
||||
|
||||
try (CloseableHttpClient httpClient = HttpClientBuilder.create()
|
||||
.setSSLSocketFactory(createSSLSocketFactory())
|
||||
.build()) {
|
||||
/* Set custom timeout, if defined */
|
||||
// Set custom timeout, if defined.
|
||||
if (this.timeout != null) {
|
||||
base.setConfig(RequestConfig.copy(RequestConfig.DEFAULT).setConnectTimeout(timeout).build());
|
||||
clientBuilder.connectTimeout(Duration.ofMillis(timeout));
|
||||
}
|
||||
|
||||
/* Execute request */
|
||||
response = httpClient.execute(base);
|
||||
// Set custom SSL context.
|
||||
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 */
|
||||
if (response == null) {
|
||||
throw new InvalidResponseException("Response unavailable");
|
||||
}
|
||||
|
||||
switch (response.getStatusLine().getStatusCode()) {
|
||||
switch (response.statusCode()) {
|
||||
case 200:
|
||||
return handleResult(response);
|
||||
case 204:
|
||||
@ -332,42 +342,33 @@ public final class RequestHelper implements Serializable {
|
||||
case 403:
|
||||
throw new PermissionDeniedException();
|
||||
default:
|
||||
if (response.getStatusLine().getStatusCode() >= 500
|
||||
&& response.getStatusLine().getStatusCode() < 600 && retries > 0) {
|
||||
/* Retry on 5xx errors */
|
||||
return request(base, retries - 1);
|
||||
if (response.statusCode() >= 500 && response.statusCode() < 600 && retries > 0) {
|
||||
// Retry on 5xx errors.
|
||||
return request(requestBuilder, retries - 1);
|
||||
} else {
|
||||
/* Fail on different error code and/or no retries left */
|
||||
// Fail on different error code and/or no retries left.
|
||||
handleError(response);
|
||||
|
||||
/* Throw exception without details, if response entity is empty. */
|
||||
throw new InvalidResponseException(Error.RESPONSE_CODE,
|
||||
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.
|
||||
// Throw exception without details, if response entity is empty.
|
||||
throw new InvalidResponseException(Error.RESPONSE_CODE, response.statusCode());
|
||||
}
|
||||
}
|
||||
} 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.
|
||||
* @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 {
|
||||
// Create context..
|
||||
// Create context.
|
||||
SSLContext context = SSLContext.getInstance(tlsVersion);
|
||||
|
||||
if (trustedCaCert != null) {
|
||||
@ -384,12 +385,7 @@ public final class RequestHelper implements Serializable {
|
||||
context.init(null, null, null);
|
||||
}
|
||||
|
||||
return new SSLConnectionSocketFactory(
|
||||
context,
|
||||
null,
|
||||
null,
|
||||
SSLConnectionSocketFactory.getDefaultHostnameVerifier()
|
||||
);
|
||||
return context;
|
||||
} catch (CertificateException | NoSuchAlgorithmException | KeyStoreException | IOException | KeyManagementException 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
|
||||
* @throws InvalidResponseException on reading errors
|
||||
*/
|
||||
private String handleResult(final HttpResponse response) throws InvalidResponseException {
|
||||
try (BufferedReader br = new BufferedReader(
|
||||
new InputStreamReader(response.getEntity().getContent()))) {
|
||||
private String handleResult(final HttpResponse<InputStream> response) throws InvalidResponseException {
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(response.body()))) {
|
||||
return br.lines().collect(Collectors.joining("\n"));
|
||||
} catch (IOException ignored) {
|
||||
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)
|
||||
* @throws VaultConnectorException Expected exception with details to throw
|
||||
*/
|
||||
private void handleError(final HttpResponse response) throws VaultConnectorException {
|
||||
if (response.getEntity() != null) {
|
||||
try (BufferedReader br = new BufferedReader(
|
||||
new InputStreamReader(response.getEntity().getContent()))) {
|
||||
private void handleError(final HttpResponse<InputStream> response) throws VaultConnectorException {
|
||||
if (response.body() != null) {
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(response.body()))) {
|
||||
String responseString = br.lines().collect(Collectors.joining("\n"));
|
||||
ErrorResponse er = jsonMapper.readValue(responseString, ErrorResponse.class);
|
||||
/* Check for "permission denied" response */
|
||||
if (!er.getErrors().isEmpty() && er.getErrors().get(0).equals("permission denied")) {
|
||||
throw new PermissionDeniedException();
|
||||
}
|
||||
throw new InvalidResponseException(Error.RESPONSE_CODE,
|
||||
response.getStatusLine().getStatusCode(), er.toString());
|
||||
throw new InvalidResponseException(Error.RESPONSE_CODE, response.statusCode(), er.toString());
|
||||
} catch (IOException ignored) {
|
||||
// Exception ignored.
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user