diff --git a/spring-vault-core/src/main/java/org/springframework/vault/client/ClientHttpConnectorFactory.java b/spring-vault-core/src/main/java/org/springframework/vault/client/ClientHttpConnectorFactory.java index 57e9090f..66804e94 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/client/ClientHttpConnectorFactory.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/client/ClientHttpConnectorFactory.java @@ -107,41 +107,26 @@ public class ClientHttpConnectorFactory { } } - private static void configureSsl(SslConfiguration sslConfiguration, SslContextBuilder sslContextBuilder) { - - try { - - if (sslConfiguration.getTrustStoreConfiguration().isPresent()) { - sslContextBuilder - .trustManager(createTrustManagerFactory(sslConfiguration.getTrustStoreConfiguration())); - } - - if (sslConfiguration.getKeyStoreConfiguration().isPresent()) { - sslContextBuilder.keyManager(createKeyManagerFactory(sslConfiguration.getKeyStoreConfiguration(), - sslConfiguration.getKeyConfiguration())); - } - - if (!sslConfiguration.getEnabledProtocols().isEmpty()) { - sslContextBuilder.protocols(sslConfiguration.getEnabledProtocols()); - } - - if (!sslConfiguration.getEnabledCipherSuites().isEmpty()) { - sslContextBuilder.ciphers(sslConfiguration.getEnabledCipherSuites()); - } - } - catch (GeneralSecurityException | IOException e) { - throw new IllegalStateException(e); - } - } - /** * {@link ClientHttpConnector} for Reactor Netty. * * @author Mark Paluch */ - static class ReactorNetty { + public static class ReactorNetty { + + /** + * Create a {@link ClientHttpConnector} using Reactor Netty. + * @param options must not be {@literal null} + * @param sslConfiguration must not be {@literal null} + * @return a new and configured {@link ReactorClientHttpConnector} instance. + */ + public static ReactorClientHttpConnector usingReactorNetty(ClientOptions options, + SslConfiguration sslConfiguration) { + return new ReactorClientHttpConnector(createClient(options, sslConfiguration)); + } + + public static HttpClient createClient(ClientOptions options, SslConfiguration sslConfiguration) { - static ClientHttpConnector usingReactorNetty(ClientOptions options, SslConfiguration sslConfiguration) { HttpClient client = HttpClient.create(); if (hasSslConfiguration(sslConfiguration)) { @@ -155,20 +140,65 @@ public class ClientHttpConnectorFactory { client = client.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, Math.toIntExact(options.getConnectionTimeout().toMillis())).proxyWithSystemProperties(); - return new ReactorClientHttpConnector(client); + return client; + } + + private static void configureSsl(SslConfiguration sslConfiguration, SslContextBuilder sslContextBuilder) { + + try { + + if (sslConfiguration.getTrustStoreConfiguration().isPresent()) { + sslContextBuilder + .trustManager(createTrustManagerFactory(sslConfiguration.getTrustStoreConfiguration())); + } + + if (sslConfiguration.getKeyStoreConfiguration().isPresent()) { + sslContextBuilder.keyManager(createKeyManagerFactory(sslConfiguration.getKeyStoreConfiguration(), + sslConfiguration.getKeyConfiguration())); + } + + if (!sslConfiguration.getEnabledProtocols().isEmpty()) { + sslContextBuilder.protocols(sslConfiguration.getEnabledProtocols()); + } + + if (!sslConfiguration.getEnabledCipherSuites().isEmpty()) { + sslContextBuilder.ciphers(sslConfiguration.getEnabledCipherSuites()); + } + } + catch (GeneralSecurityException | IOException e) { + throw new IllegalStateException(e); + } } } /** - * {@link ClientHttpRequestFactory} for Apache Http Components. + * Utility methods to create {@link ClientHttpRequestFactory} using Apache Http + * Components. * * @author Mark Paluch */ - static class HttpComponents { + public static class HttpComponents { - static ClientHttpConnector usingHttpComponents(ClientOptions options, SslConfiguration sslConfiguration) - throws GeneralSecurityException, IOException { + /** + * Create a {@link ClientHttpConnector} using Apache Http Components. + * @param options must not be {@literal null} + * @param sslConfiguration must not be {@literal null} + * @return a new and configured {@link HttpComponentsClientHttpConnector} + * instance. + * @throws GeneralSecurityException + * @throws IOException + */ + public static HttpComponentsClientHttpConnector usingHttpComponents(ClientOptions options, + SslConfiguration sslConfiguration) throws GeneralSecurityException, IOException { + + HttpAsyncClientBuilder httpClientBuilder = createHttpAsyncClientBuilder(options, sslConfiguration); + + return new HttpComponentsClientHttpConnector(httpClientBuilder.build()); + } + + public static HttpAsyncClientBuilder createHttpAsyncClientBuilder(ClientOptions options, + SslConfiguration sslConfiguration) throws GeneralSecurityException, IOException { HttpAsyncClientBuilder httpClientBuilder = HttpAsyncClientBuilder.create(); @@ -177,7 +207,7 @@ public class ClientHttpConnectorFactory { if (hasSslConfiguration(sslConfiguration)) { - SSLContext sslContext = getSSLContext(sslConfiguration, getTrustManagers(sslConfiguration)); + SSLContext sslContext = getSSLContext(sslConfiguration); String[] enabledProtocols = !sslConfiguration.getEnabledProtocols().isEmpty() ? sslConfiguration.getEnabledProtocols().toArray(new String[0]) : null; @@ -210,24 +240,32 @@ public class ClientHttpConnectorFactory { httpClientBuilder.setDefaultRequestConfig(requestConfig); - return new HttpComponentsClientHttpConnector(httpClientBuilder.build()); + return httpClientBuilder; } } - static class JettyClient { + /** + * Utility methods to create {@link ClientHttpRequestFactory} using the Jetty Client. + * + * @author Mark Paluch + */ + public static class JettyClient { - static ClientHttpConnector usingJetty(ClientOptions options, SslConfiguration sslConfiguration) { - - try { - return new JettyClientHttpConnector(configureClient(getHttpClient(sslConfiguration), options)); - } - catch (GeneralSecurityException | IOException e) { - throw new IllegalStateException(e); - } + /** + * Create a {@link ClientHttpConnector} using Jetty. + * @param options must not be {@literal null} + * @param sslConfiguration must not be {@literal null} + * @return a new and configured {@link JettyClientHttpConnector} instance. + * @throws GeneralSecurityException + * @throws IOException + */ + public static JettyClientHttpConnector usingJetty(ClientOptions options, SslConfiguration sslConfiguration) + throws GeneralSecurityException, IOException { + return new JettyClientHttpConnector(configureClient(getHttpClient(sslConfiguration), options)); } - private static org.eclipse.jetty.client.HttpClient configureClient( + public static org.eclipse.jetty.client.HttpClient configureClient( org.eclipse.jetty.client.HttpClient httpClient, ClientOptions options) { httpClient.setConnectTimeout(options.getConnectionTimeout().toMillis()); @@ -236,7 +274,7 @@ public class ClientHttpConnectorFactory { return httpClient; } - private static org.eclipse.jetty.client.HttpClient getHttpClient(SslConfiguration sslConfiguration) + public static org.eclipse.jetty.client.HttpClient getHttpClient(SslConfiguration sslConfiguration) throws IOException, GeneralSecurityException { if (hasSslConfiguration(sslConfiguration)) { @@ -291,16 +329,32 @@ public class ClientHttpConnectorFactory { * * @author Mark Paluch */ - static class JdkHttpClient { + public static class JdkHttpClient { - static ClientHttpConnector usingJdkHttpClient(ClientOptions options, SslConfiguration sslConfiguration) - throws GeneralSecurityException, IOException { + /** + * Create a {@link JdkClientHttpConnector} using the JDK's HttpClient. + * @param options must not be {@literal null} + * @param sslConfiguration must not be {@literal null} + * @return a new and configured {@link JdkClientHttpConnector} instance. + * @throws GeneralSecurityException + * @throws IOException + */ + public static JdkClientHttpConnector usingJdkHttpClient(ClientOptions options, + SslConfiguration sslConfiguration) throws GeneralSecurityException, IOException { + + java.net.http.HttpClient.Builder builder = getBuilder(options, sslConfiguration); + + return new JdkClientHttpConnector(builder.build()); + } + + public static java.net.http.HttpClient.Builder getBuilder(ClientOptions options, + SslConfiguration sslConfiguration) throws GeneralSecurityException, IOException { java.net.http.HttpClient.Builder builder = java.net.http.HttpClient.newBuilder(); if (hasSslConfiguration(sslConfiguration)) { - SSLContext sslContext = getSSLContext(sslConfiguration, getTrustManagers(sslConfiguration)); + SSLContext sslContext = getSSLContext(sslConfiguration); String[] enabledProtocols = !sslConfiguration.getEnabledProtocols().isEmpty() ? sslConfiguration.getEnabledProtocols().toArray(new String[0]) : null; @@ -308,17 +362,6 @@ public class ClientHttpConnectorFactory { String[] enabledCipherSuites = !sslConfiguration.getEnabledCipherSuites().isEmpty() ? sslConfiguration.getEnabledCipherSuites().toArray(new String[0]) : null; - BasicClientTlsStrategy tlsStrategy = new BasicClientTlsStrategy(sslContext, (endpoint, sslEngine) -> { - - if (enabledProtocols != null) { - sslEngine.setEnabledProtocols(enabledProtocols); - } - - if (enabledCipherSuites != null) { - sslEngine.setEnabledCipherSuites(enabledCipherSuites); - } - }, null); - SSLParameters parameters = new SSLParameters(); parameters.setProtocols(enabledProtocols); parameters.setCipherSuites(enabledCipherSuites); @@ -328,8 +371,7 @@ public class ClientHttpConnectorFactory { builder.proxy(ProxySelector.getDefault()).followRedirects(java.net.http.HttpClient.Redirect.ALWAYS) .connectTimeout(options.getConnectionTimeout()); - - return new JdkClientHttpConnector(builder.build()); + return builder; } } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/client/ClientHttpRequestFactoryFactory.java b/spring-vault-core/src/main/java/org/springframework/vault/client/ClientHttpRequestFactoryFactory.java index 2aa04b75..b6a21fce 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/client/ClientHttpRequestFactoryFactory.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/client/ClientHttpRequestFactoryFactory.java @@ -63,6 +63,7 @@ import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.client.OkHttp3ClientHttpRequestFactory; import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.http.client.reactive.JdkClientHttpConnector; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -131,14 +132,17 @@ public class ClientHttpRequestFactoryFactory { return new SimpleClientHttpRequestFactory(); } - static SSLContext getSSLContext(SslConfiguration sslConfiguration, TrustManager[] trustManagers) - throws GeneralSecurityException, IOException { + static SSLContext getSSLContext(SslConfiguration sslConfiguration) throws GeneralSecurityException, IOException { - KeyConfiguration keyConfiguration = sslConfiguration.getKeyConfiguration(); - KeyManager[] keyManagers = sslConfiguration.getKeyStoreConfiguration().isPresent() - ? createKeyManagerFactory(sslConfiguration.getKeyStoreConfiguration(), keyConfiguration) - .getKeyManagers() - : null; + return getSSLContext(sslConfiguration.getKeyStoreConfiguration(), sslConfiguration.getKeyConfiguration(), + getTrustManagers(sslConfiguration)); + } + + static SSLContext getSSLContext(KeyStoreConfiguration keyStoreConfiguration, KeyConfiguration keyConfiguration, + @Nullable TrustManager[] trustManagers) throws GeneralSecurityException, IOException { + + KeyManager[] keyManagers = keyStoreConfiguration.isPresent() + ? createKeyManagerFactory(keyStoreConfiguration, keyConfiguration).getKeyManagers() : null; SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(keyManagers, trustManagers, null); @@ -146,14 +150,6 @@ public class ClientHttpRequestFactoryFactory { return sslContext; } - @Nullable - static TrustManager[] getTrustManagers(SslConfiguration sslConfiguration) - throws GeneralSecurityException, IOException { - - return sslConfiguration.getTrustStoreConfiguration().isPresent() - ? createTrustManagerFactory(sslConfiguration.getTrustStoreConfiguration()).getTrustManagers() : null; - } - static KeyManagerFactory createKeyManagerFactory(KeyStoreConfiguration keyStoreConfiguration, KeyConfiguration keyConfiguration) throws GeneralSecurityException, IOException { @@ -186,6 +182,14 @@ public class ClientHttpRequestFactoryFactory { return keyStore; } + @Nullable + static TrustManager[] getTrustManagers(SslConfiguration sslConfiguration) + throws GeneralSecurityException, IOException { + + return sslConfiguration.getTrustStoreConfiguration().isPresent() + ? createTrustManagerFactory(sslConfiguration.getTrustStoreConfiguration()).getTrustManagers() : null; + } + private static String getKeyStoreType(KeyStoreConfiguration keyStoreConfiguration) { if (StringUtils.hasText(keyStoreConfiguration.getStoreType()) @@ -263,13 +267,30 @@ public class ClientHttpRequestFactoryFactory { } /** - * {@link ClientHttpRequestFactory} for Apache Http Components. + * Utilities to create a {@link ClientHttpRequestFactory} for Apache Http Components. * * @author Mark Paluch */ - static class HttpComponents { + public static class HttpComponents { - static ClientHttpRequestFactory usingHttpComponents(ClientOptions options, SslConfiguration sslConfiguration) + /** + * Create a {@link ClientHttpRequestFactory} using Apache Http Components. + * @param options must not be {@literal null} + * @param sslConfiguration must not be {@literal null} + * @return a new and configured {@link HttpComponentsClientHttpRequestFactory} + * instance. + * @throws GeneralSecurityException + * @throws IOException + */ + public static HttpComponentsClientHttpRequestFactory usingHttpComponents(ClientOptions options, + SslConfiguration sslConfiguration) throws GeneralSecurityException, IOException { + + HttpClientBuilder httpClientBuilder = getHttpClientBuilder(options, sslConfiguration); + + return new HttpComponentsClientHttpRequestFactory(httpClientBuilder.build()); + } + + public static HttpClientBuilder getHttpClientBuilder(ClientOptions options, SslConfiguration sslConfiguration) throws GeneralSecurityException, IOException { HttpClientBuilder httpClientBuilder = HttpClients.custom(); @@ -279,7 +300,7 @@ public class ClientHttpRequestFactoryFactory { if (hasSslConfiguration(sslConfiguration)) { - SSLContext sslContext = getSSLContext(sslConfiguration, getTrustManagers(sslConfiguration)); + SSLContext sslContext = getSSLContext(sslConfiguration); String[] enabledProtocols = null; @@ -309,20 +330,36 @@ public class ClientHttpRequestFactoryFactory { .setRedirectsEnabled(true).build(); httpClientBuilder.setDefaultRequestConfig(requestConfig); - - return new HttpComponentsClientHttpRequestFactory(httpClientBuilder.build()); + return httpClientBuilder; } } /** - * {@link ClientHttpRequestFactory} for the {@link okhttp3.OkHttpClient}. + * Utilities to create a {@link ClientHttpRequestFactory} for the + * {@link okhttp3.OkHttpClient}. * * @author Mark Paluch */ - static class OkHttp3 { + public static class OkHttp3 { - static ClientHttpRequestFactory usingOkHttp3(ClientOptions options, SslConfiguration sslConfiguration) + /** + * Create a {@link ClientHttpRequestFactory} using {@link okhttp3.OkHttpClient}. + * @param options must not be {@literal null} + * @param sslConfiguration must not be {@literal null} + * @return a new and configured {@link OkHttp3ClientHttpRequestFactory} instance. + * @throws GeneralSecurityException + * @throws IOException + */ + public static OkHttp3ClientHttpRequestFactory usingOkHttp3(ClientOptions options, + SslConfiguration sslConfiguration) throws GeneralSecurityException, IOException { + + Builder builder = getBuilder(options, sslConfiguration); + + return new OkHttp3ClientHttpRequestFactory(builder.build()); + } + + public static Builder getBuilder(ClientOptions options, SslConfiguration sslConfiguration) throws GeneralSecurityException, IOException { Builder builder = new Builder(); @@ -333,13 +370,14 @@ public class ClientHttpRequestFactoryFactory { TrustManager[] trustManagers = getTrustManagers(sslConfiguration); - if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { + if (trustManagers == null || trustManagers.length != 1 + || !(trustManagers[0] instanceof X509TrustManager)) { throw new IllegalStateException( "Unexpected default trust managers:" + Arrays.toString(trustManagers)); } - X509TrustManager trustManager = (X509TrustManager) trustManagers[0]; - SSLContext sslContext = getSSLContext(sslConfiguration, trustManagers); + SSLContext sslContext = getSSLContext(sslConfiguration.getKeyStoreConfiguration(), + sslConfiguration.getKeyConfiguration(), trustManagers); ConnectionSpec.Builder sslConnectionSpecBuilder = new ConnectionSpec.Builder(sslConnectionSpec); @@ -354,15 +392,14 @@ public class ClientHttpRequestFactoryFactory { sslConnectionSpec = sslConnectionSpecBuilder.build(); - builder.sslSocketFactory(sslContext.getSocketFactory(), trustManager); + builder.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagers[0]); } builder.connectionSpecs(Arrays.asList(sslConnectionSpec, ConnectionSpec.CLEARTEXT)); builder.connectTimeout(options.getConnectionTimeout().toMillis(), TimeUnit.MILLISECONDS) .readTimeout(options.getReadTimeout().toMillis(), TimeUnit.MILLISECONDS); - - return new OkHttp3ClientHttpRequestFactory(builder.build()); + return builder; } } diff --git a/spring-vault-core/src/test/java/org/springframework/vault/client/ClientHttpConnectorFactoryIntegrationTests.java b/spring-vault-core/src/test/java/org/springframework/vault/client/ClientHttpConnectorFactoryIntegrationTests.java index c1e72ed6..bafaa917 100644 --- a/spring-vault-core/src/test/java/org/springframework/vault/client/ClientHttpConnectorFactoryIntegrationTests.java +++ b/spring-vault-core/src/test/java/org/springframework/vault/client/ClientHttpConnectorFactoryIntegrationTests.java @@ -116,7 +116,7 @@ class ClientHttpConnectorFactoryIntegrationTests { } @Test - void jettyClientShouldWork() { + void jettyClientShouldWork() throws Exception { ClientHttpConnector factory = JettyClient.usingJetty(new ClientOptions(), Settings.createSslConfiguration()); @@ -128,7 +128,7 @@ class ClientHttpConnectorFactoryIntegrationTests { } @Test - void jettyClientWithExplicitEnabledCipherSuitesShouldWork() { + void jettyClientWithExplicitEnabledCipherSuitesShouldWork() throws Exception { List enabledCipherSuites = new ArrayList(); enabledCipherSuites.add("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"); @@ -145,7 +145,7 @@ class ClientHttpConnectorFactoryIntegrationTests { } @Test - void jettyClientWithExplicitEnabledProtocolsShouldWork() { + void jettyClientWithExplicitEnabledProtocolsShouldWork() throws Exception { List enabledProtocols = new ArrayList(); enabledProtocols.add("TLSv1.2");