Allow reuse of library-specific configuration code in ClientHttpRequestFactoryFactory and ClientHttpConnectorFactory.
Closes gh-760
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<String> enabledCipherSuites = new ArrayList<String>();
|
||||
enabledCipherSuites.add("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384");
|
||||
@@ -145,7 +145,7 @@ class ClientHttpConnectorFactoryIntegrationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void jettyClientWithExplicitEnabledProtocolsShouldWork() {
|
||||
void jettyClientWithExplicitEnabledProtocolsShouldWork() throws Exception {
|
||||
|
||||
List<String> enabledProtocols = new ArrayList<String>();
|
||||
enabledProtocols.add("TLSv1.2");
|
||||
|
||||
Reference in New Issue
Block a user