diff --git a/docs/src/main/asciidoc/spring-cloud-commons.adoc b/docs/src/main/asciidoc/spring-cloud-commons.adoc
index b9921306..0dc29a29 100644
--- a/docs/src/main/asciidoc/spring-cloud-commons.adoc
+++ b/docs/src/main/asciidoc/spring-cloud-commons.adoc
@@ -487,3 +487,16 @@ spring:
inetutils:
useOnlySiteLocalInterfaces: true
----
+
+[[http-clients]]
+=== HTTP Client Factories
+
+Spring Cloud Commons provides beans for creating both Apache HTTP clients (`ApacheHttpClientFactory`)
+as well as OK HTTP clients (`OkHttpClientFactory`). The `OkHttpClientFactory` bean will only be created
+if the OK HTTP jar is on the classpath. In addition, Spring Cloud Commons provides beans for creating
+the connection managers used by both clients, `ApacheHttpClientConnectionManagerFactory` for the Apache
+HTTP client and `OkHttpClientConnectionPoolFactory` for the OK HTTP client. You can provide
+your own implementation of these beans if you would like to customize how the HTTP clients are created
+in downstream projects. You can also disable the creation of these beans by setting
+`spring.cloud.httpclientfactories.apache.enabled` or `spring.cloud.httpclientfactories.ok.enabled` to
+`false`.
\ No newline at end of file
diff --git a/spring-cloud-commons/pom.xml b/spring-cloud-commons/pom.xml
index 5af3d5a7..36f49ed4 100644
--- a/spring-cloud-commons/pom.xml
+++ b/spring-cloud-commons/pom.xml
@@ -35,6 +35,11 @@
+
+
+ 3.6.0
+ 4.5.1
+
org.springframework.boot
@@ -96,6 +101,23 @@
json-path
true
+
+ com.squareup.okhttp3
+ okhttp
+ ${okhttp3.version}
+ true
+
+
+ com.squareup.okhttp3
+ logging-interceptor
+ ${okhttp3.version}
+ true
+
+
+ org.apache.httpcomponents
+ httpclient
+ ${apachehttpclient.version}
+
org.springframework.boot
spring-boot-starter-test
diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientConnectionManagerFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientConnectionManagerFactory.java
new file mode 100644
index 00000000..5c97d8e6
--- /dev/null
+++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientConnectionManagerFactory.java
@@ -0,0 +1,47 @@
+/*
+ *
+ * * Copyright 2013-2016 the original author or authors.
+ * *
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ *
+ */
+
+package org.springframework.cloud.commons.httpclient;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.HttpClientConnectionManager;
+
+/**
+ * Interface for creating an {@link HttpClientConnectionManager}.
+ * @author Ryan Baxter
+ */
+public interface ApacheHttpClientConnectionManagerFactory {
+ public static final String HTTP_SCHEME = "http";
+ public static final String HTTPS_SCHEME = "https";
+
+ /**
+ * Creates a new {@link HttpClientConnectionManager}.
+ * @param disableSslValidation True to disable SSL validation, false otherwise
+ * @param maxTotalConnections The total number of connections
+ * @param maxConnectionsPerRoute The total number of connections per route
+ * @param timeToLive The time a connection is allowed to exist
+ * @param timeUnit The time unit for the time to live value
+ * @param registryBuilder The {@link RegistryBuilder} to use in the connection manager
+ * @return A new {@link HttpClientConnectionManager}
+ */
+ public HttpClientConnectionManager newConnectionManager(boolean disableSslValidation,
+ int maxTotalConnections, int maxConnectionsPerRoute, long timeToLive,
+ TimeUnit timeUnit, RegistryBuilder registryBuilder);
+}
diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientFactory.java
new file mode 100644
index 00000000..7ef38395
--- /dev/null
+++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/ApacheHttpClientFactory.java
@@ -0,0 +1,37 @@
+/*
+ *
+ * * Copyright 2013-2016 the original author or authors.
+ * *
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ *
+ */
+
+package org.springframework.cloud.commons.httpclient;
+
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.conn.HttpClientConnectionManager;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+
+/**
+ * Factory for creating a new {@link CloseableHttpClient}.
+ * @author Ryan Baxter
+ */
+public interface ApacheHttpClientFactory {
+
+ /**
+ * Creates an {@link HttpClientBuilder} that can be used to create a new {@link CloseableHttpClient}.
+ * @return A {@link HttpClientBuilder}
+ */
+ public HttpClientBuilder createBuilder();
+}
diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactory.java
new file mode 100644
index 00000000..136ce607
--- /dev/null
+++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactory.java
@@ -0,0 +1,85 @@
+package org.springframework.cloud.commons.httpclient;
+
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.HttpClientConnectionManager;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.commons.logging.Log;
+
+/**
+ * Default implementation of {@link ApacheHttpClientConnectionManagerFactory}.
+ * @author Ryan Baxter
+ */
+public class DefaultApacheHttpClientConnectionManagerFactory
+ implements ApacheHttpClientConnectionManagerFactory {
+
+ private static final Log LOG = LogFactory
+ .getLog(DefaultApacheHttpClientConnectionManagerFactory.class);
+
+ public HttpClientConnectionManager newConnectionManager(boolean disableSslValidation,
+ int maxTotalConnections, int maxConnectionsPerRoute) {
+ return newConnectionManager(disableSslValidation, maxTotalConnections,
+ maxConnectionsPerRoute, -1, TimeUnit.MILLISECONDS, null);
+ }
+
+ @Override
+ public HttpClientConnectionManager newConnectionManager(boolean disableSslValidation,
+ int maxTotalConnections, int maxConnectionsPerRoute, long timeToLive,
+ TimeUnit timeUnit, RegistryBuilder registryBuilder) {
+ if (registryBuilder == null) {
+ registryBuilder = RegistryBuilder. create()
+ .register(HTTP_SCHEME, PlainConnectionSocketFactory.INSTANCE);
+ }
+ if (disableSslValidation) {
+ try {
+ final SSLContext sslContext = SSLContext.getInstance("SSL");
+ sslContext.init(null, new TrustManager[] { new X509TrustManager() {
+ @Override
+ public void checkClientTrusted(X509Certificate[] x509Certificates,
+ String s) throws CertificateException {
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] x509Certificates,
+ String s) throws CertificateException {
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+ } }, new SecureRandom());
+ registryBuilder.register(HTTPS_SCHEME, new SSLConnectionSocketFactory(
+ sslContext, NoopHostnameVerifier.INSTANCE));
+ }
+ catch (NoSuchAlgorithmException e) {
+ LOG.warn("Error creating SSLContext", e);
+ }
+ catch (KeyManagementException e) {
+ LOG.warn("Error creating SSLContext", e);
+ }
+ }
+ final Registry registry = registryBuilder.build();
+
+ PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(
+ registry, null, null, null, timeToLive, timeUnit);
+ connectionManager.setMaxTotal(maxTotalConnections);
+ connectionManager.setDefaultMaxPerRoute(maxConnectionsPerRoute);
+
+ return connectionManager;
+ }
+}
diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactory.java
new file mode 100644
index 00000000..66827bd8
--- /dev/null
+++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactory.java
@@ -0,0 +1,23 @@
+package org.springframework.cloud.commons.httpclient;
+
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.conn.HttpClientConnectionManager;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+
+/**
+ * Default implementation of {@link ApacheHttpClientFactory}.
+ * @author Ryan Baxter
+ */
+public class DefaultApacheHttpClientFactory implements ApacheHttpClientFactory {
+
+ /**
+ * A default {@link HttpClientBuilder}. The {@link HttpClientBuilder} returned will
+ * have content compression disabled, cookie management disabled, and use system properties.
+ */
+ @Override
+ public HttpClientBuilder createBuilder() {
+ return HttpClientBuilder.create().disableContentCompression()
+ .disableCookieManagement().useSystemProperties();
+ }
+}
diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientConnectionPoolFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientConnectionPoolFactory.java
new file mode 100644
index 00000000..9c09dfcc
--- /dev/null
+++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientConnectionPoolFactory.java
@@ -0,0 +1,17 @@
+package org.springframework.cloud.commons.httpclient;
+
+import okhttp3.ConnectionPool;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Default implementation of {@link OkHttpClientConnectionPoolFactory}.
+ * @author Ryan Baxter
+ */
+public class DefaultOkHttpClientConnectionPoolFactory implements OkHttpClientConnectionPoolFactory {
+
+ @Override
+ public ConnectionPool create(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
+ return new ConnectionPool(maxIdleConnections, keepAliveDuration, timeUnit);
+ }
+}
diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientFactory.java
new file mode 100644
index 00000000..7ea0aad8
--- /dev/null
+++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientFactory.java
@@ -0,0 +1,47 @@
+package org.springframework.cloud.commons.httpclient;
+
+import okhttp3.ConnectionPool;
+import okhttp3.OkHttpClient;
+
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Default implementation of {@link OkHttpClientFactory}.
+ * @author Ryan Baxter
+ */
+public class DefaultOkHttpClientFactory implements OkHttpClientFactory {
+
+ private static final Log LOG = LogFactory.getLog(DefaultOkHttpClientFactory.class);
+
+ @Override
+ public OkHttpClient.Builder createBuilder(boolean disableSslValidation) {
+ OkHttpClient.Builder builder = new OkHttpClient.Builder();
+ if (disableSslValidation) {
+ try {
+ X509TrustManager disabledTrustManager = new DisableValidationTrustManager();
+ TrustManager[] trustManagers = new TrustManager[1];
+ trustManagers[0] = disabledTrustManager;
+ SSLContext sslContext = SSLContext.getInstance("SSL");
+ sslContext.init(null, trustManagers, new java.security.SecureRandom());
+ SSLSocketFactory disabledSSLSocketFactory = sslContext.getSocketFactory();
+ builder.sslSocketFactory(disabledSSLSocketFactory, disabledTrustManager);
+ builder.hostnameVerifier(new TrustAllHostnames());
+ }
+ catch (NoSuchAlgorithmException e) {
+ LOG.warn("Error setting SSLSocketFactory in OKHttpClient", e);
+ }
+ catch (KeyManagementException e) {
+ LOG.warn("Error setting SSLSocketFactory in OKHttpClient", e);
+ }
+ }
+ return builder;
+ }
+}
diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/HttpClientConfiguration.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/HttpClientConfiguration.java
new file mode 100644
index 00000000..df3de178
--- /dev/null
+++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/HttpClientConfiguration.java
@@ -0,0 +1,51 @@
+package org.springframework.cloud.commons.httpclient;
+
+import okhttp3.OkHttpClient;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Ryan Baxter
+ */
+@Configuration
+public class HttpClientConfiguration {
+
+ @Configuration
+ @ConditionalOnProperty(name = "spring.cloud.httpclientfactories.apache.enabled", matchIfMissing = true)
+ static class ApacheHttpClientConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ public ApacheHttpClientConnectionManagerFactory connManFactory() {
+ return new DefaultApacheHttpClientConnectionManagerFactory();
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ public ApacheHttpClientFactory apacheHttpClientFactory() {
+ return new DefaultApacheHttpClientFactory();
+ }
+ }
+
+ @Configuration
+ @ConditionalOnProperty(name = "spring.cloud.httpclientfactories.ok.enabled", matchIfMissing = true)
+ @ConditionalOnClass(OkHttpClient.class)
+ static class OkHttpClientConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ public OkHttpClientConnectionPoolFactory connPoolFactory() {
+ return new DefaultOkHttpClientConnectionPoolFactory();
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ public OkHttpClientFactory okHttpClientFactory() {
+ return new DefaultOkHttpClientFactory();
+ }
+ }
+}
diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/OkHttpClientConnectionPoolFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/OkHttpClientConnectionPoolFactory.java
new file mode 100644
index 00000000..d7682cc1
--- /dev/null
+++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/OkHttpClientConnectionPoolFactory.java
@@ -0,0 +1,21 @@
+package org.springframework.cloud.commons.httpclient;
+
+import okhttp3.ConnectionPool;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Creates {@link ConnectionPool}s for {@link okhttp3.OkHttpClient}s
+ * @author Ryan Baxter
+ */
+public interface OkHttpClientConnectionPoolFactory {
+
+ /**
+ * Creates a new {@link ConnectionPool}.
+ * @param maxIdleConnections number of max idle connections to allow
+ * @param keepAliveDuration amount of time to keep connections alive
+ * @param timeUnit the time unit for the keep alive duration
+ * @return A new {@link ConnectionPool}
+ */
+ public ConnectionPool create(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit);
+}
diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/OkHttpClientFactory.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/OkHttpClientFactory.java
new file mode 100644
index 00000000..e4323ca0
--- /dev/null
+++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/commons/httpclient/OkHttpClientFactory.java
@@ -0,0 +1,54 @@
+package org.springframework.cloud.commons.httpclient;
+
+import okhttp3.ConnectionPool;
+import okhttp3.OkHttpClient;
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * Creates new {@link OkHttpClient}s.
+ * @author Ryan Baxter
+ */
+public interface OkHttpClientFactory {
+
+ /**
+ * Creates a {@link OkHttpClient.Builder} used to build an {@link OkHttpClient}.
+ * @param disableSslValidation Disables SSL validation
+ * @return A new {@link OkHttpClient.Builder}
+ */
+ public OkHttpClient.Builder createBuilder(boolean disableSslValidation);
+
+ /**
+ * A {@link X509TrustManager} that does not validate SSL certificates.
+ */
+ public static class DisableValidationTrustManager implements X509TrustManager {
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {}
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {}
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return new X509Certificate[0];
+ }
+ }
+
+ /**
+ * A {@link HostnameVerifier} that does not validate any hostnames.
+ */
+ public static class TrustAllHostnames implements HostnameVerifier {
+
+ @Override
+ public boolean verify(String s, SSLSession sslSession) {
+ return true;
+ }
+ }
+}
diff --git a/spring-cloud-commons/src/main/resources/META-INF/spring.factories b/spring-cloud-commons/src/main/resources/META-INF/spring.factories
index 874eb1fc..01dc663a 100644
--- a/spring-cloud-commons/src/main/resources/META-INF/spring.factories
+++ b/spring-cloud-commons/src/main/resources/META-INF/spring.factories
@@ -8,7 +8,8 @@ org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration,\
org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration,\
org.springframework.cloud.commons.util.UtilAutoConfiguration,\
org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClientAutoConfiguration,\
-org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration
+org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration,\
+org.springframework.cloud.commons.httpclient.HttpClientConfiguration
# Environment Post Processors
diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/CustomHttpClientConfigurationTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/CustomHttpClientConfigurationTests.java
new file mode 100644
index 00000000..51a1189d
--- /dev/null
+++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/CustomHttpClientConfigurationTests.java
@@ -0,0 +1,142 @@
+package org.springframework.cloud.commons.httpclient;
+
+import okhttp3.ConnectionPool;
+import okhttp3.OkHttpClient;
+
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.X509TrustManager;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.HttpClientConnectionManager;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Ryan Baxter
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = CustomApplication.class, properties = {"spring.cloud.httpclient.ok.enabled: true"})
+public class CustomHttpClientConfigurationTests {
+
+ @Autowired
+ ApacheHttpClientFactory httpClientFactory;
+
+ @Autowired
+ ApacheHttpClientConnectionManagerFactory connectionManagerFactory;
+
+ @Autowired
+ OkHttpClientFactory okHttpClientFactory;
+
+ @Autowired
+ OkHttpClientConnectionPoolFactory okHttpClientConnectionPoolFactory;
+
+ @Test
+ public void connManFactory() throws Exception {
+ assertTrue(ApacheHttpClientConnectionManagerFactory.class
+ .isInstance(connectionManagerFactory));
+ assertTrue(CustomApplication.MyApacheHttpClientConnectionManagerFactory.class
+ .isInstance(connectionManagerFactory));
+ }
+
+ @Test
+ public void apacheHttpClientFactory() throws Exception {
+ assertTrue(ApacheHttpClientFactory.class.isInstance(httpClientFactory));
+ assertTrue(CustomApplication.MyApacheHttpClientFactory.class
+ .isInstance(httpClientFactory));
+ }
+
+ @Test
+ public void connectionPoolFactory() throws Exception {
+ assertTrue(OkHttpClientConnectionPoolFactory.class.isInstance(okHttpClientConnectionPoolFactory));
+ assertTrue(CustomApplication.MyOkHttpConnectionPoolFactory.class.isInstance(okHttpClientConnectionPoolFactory));
+ }
+
+ @Test
+ public void okHttpClientFactory() throws Exception {
+ assertTrue(OkHttpClientFactory.class.isInstance(okHttpClientFactory));
+ assertTrue(CustomApplication.MyOkHttpClientFactory.class.isInstance(okHttpClientFactory));
+ }
+
+}
+
+@Configuration
+@EnableAutoConfiguration
+class CustomApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(MyApplication.class, args);
+ }
+
+ @Configuration
+ static class MyConfig {
+
+ @Bean
+ public ApacheHttpClientFactory clientFactory() {
+ return new MyApacheHttpClientFactory();
+ }
+
+ @Bean
+ public ApacheHttpClientConnectionManagerFactory connectionManagerFactory() {
+ return new MyApacheHttpClientConnectionManagerFactory();
+ }
+
+ @Bean
+ public OkHttpClientConnectionPoolFactory connectionPoolFactory() {
+ return new MyOkHttpConnectionPoolFactory();
+ }
+
+ @Bean
+ public OkHttpClientFactory okHttpClientFactory() {
+ return new MyOkHttpClientFactory();
+ }
+
+ }
+
+ static class MyApacheHttpClientFactory implements ApacheHttpClientFactory {
+
+ @Override
+ public HttpClientBuilder createBuilder() {
+ return HttpClientBuilder.create();
+ }
+ }
+
+ static class MyApacheHttpClientConnectionManagerFactory
+ implements ApacheHttpClientConnectionManagerFactory {
+
+ @Override
+ public HttpClientConnectionManager newConnectionManager(
+ boolean disableSslValidation, int maxTotalConnections,
+ int maxConnectionsPerRoute, long timeToLive, TimeUnit timeUnit,
+ RegistryBuilder registryBuilder) {
+ return null;
+ }
+ }
+
+ static class MyOkHttpClientFactory implements OkHttpClientFactory {
+ @Override
+ public OkHttpClient.Builder createBuilder(boolean disableSslValidation) {
+ return new OkHttpClient.Builder();
+ }
+ }
+
+ static class MyOkHttpConnectionPoolFactory implements OkHttpClientConnectionPoolFactory {
+
+ @Override
+ public ConnectionPool create(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
+ return null;
+ }
+ }
+}
diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactoryTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactoryTests.java
new file mode 100644
index 00000000..00355006
--- /dev/null
+++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientConnectionManagerFactoryTests.java
@@ -0,0 +1,52 @@
+package org.springframework.cloud.commons.httpclient;
+
+import java.lang.reflect.Field;
+import java.util.concurrent.TimeUnit;
+import org.apache.http.conn.HttpClientConnectionManager;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.junit.Test;
+import org.springframework.util.ReflectionUtils;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Ryan Baxter
+ */
+public class DefaultApacheHttpClientConnectionManagerFactoryTests {
+ @Test
+ public void newConnectionManager() throws Exception {
+ HttpClientConnectionManager connectionManager = new DefaultApacheHttpClientConnectionManagerFactory()
+ .newConnectionManager(false, 2, 6);
+ assertEquals(6, ((PoolingHttpClientConnectionManager) connectionManager)
+ .getDefaultMaxPerRoute());
+ assertEquals(2,
+ ((PoolingHttpClientConnectionManager) connectionManager).getMaxTotal());
+ Object pool = getField(((PoolingHttpClientConnectionManager) connectionManager),
+ "pool");
+ assertEquals(-1l, getField(pool, "timeToLive"));
+ TimeUnit timeUnit = getField(pool, "tunit");
+ assertEquals(TimeUnit.MILLISECONDS, timeUnit);
+ }
+
+ @Test
+ public void newConnectionManagerWithTTL() throws Exception {
+ HttpClientConnectionManager connectionManager = new DefaultApacheHttpClientConnectionManagerFactory()
+ .newConnectionManager(false, 2, 6, 56l, TimeUnit.DAYS, null);
+ assertEquals(6, ((PoolingHttpClientConnectionManager) connectionManager)
+ .getDefaultMaxPerRoute());
+ assertEquals(2,
+ ((PoolingHttpClientConnectionManager) connectionManager).getMaxTotal());
+ Object pool = getField(((PoolingHttpClientConnectionManager) connectionManager),
+ "pool");
+ assertEquals(56l, getField(pool, "timeToLive"));
+ TimeUnit timeUnit = getField(pool, "tunit");
+ assertEquals(TimeUnit.DAYS, timeUnit);
+ }
+
+ protected T getField(Object target, String name) {
+ Field field = ReflectionUtils.findField(target.getClass(), name);
+ ReflectionUtils.makeAccessible(field);
+ Object value = ReflectionUtils.getField(field, target);
+ return (T) value;
+ }
+}
\ No newline at end of file
diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactoryTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactoryTests.java
new file mode 100644
index 00000000..3a69ea72
--- /dev/null
+++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultApacheHttpClientFactoryTests.java
@@ -0,0 +1,42 @@
+package org.springframework.cloud.commons.httpclient;
+
+import java.lang.reflect.Field;
+import org.apache.http.client.config.CookieSpecs;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.Configurable;
+import org.apache.http.conn.HttpClientConnectionManager;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.assertj.core.api.Assertions;
+import org.junit.Test;
+import org.springframework.util.ReflectionUtils;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+/**
+ * @author Ryan Baxter
+ */
+public class DefaultApacheHttpClientFactoryTests {
+ @Test
+ public void createClient() throws Exception {
+ final RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(100)
+ .setConnectTimeout(200).setCookieSpec(CookieSpecs.IGNORE_COOKIES).build();
+ CloseableHttpClient httpClient = new DefaultApacheHttpClientFactory().createBuilder().
+ setConnectionManager(mock(HttpClientConnectionManager.class)).
+ setDefaultRequestConfig(requestConfig).build();
+ Assertions.assertThat(httpClient).isInstanceOf(Configurable.class);
+ RequestConfig config = ((Configurable) httpClient).getConfig();
+ assertEquals(100, config.getSocketTimeout());
+ assertEquals(200, config.getConnectTimeout());
+ assertEquals(CookieSpecs.IGNORE_COOKIES, config.getCookieSpec());
+ }
+
+ protected T getField(Object target, String name) {
+ Field field = ReflectionUtils.findField(target.getClass(), name);
+ ReflectionUtils.makeAccessible(field);
+ Object value = ReflectionUtils.getField(field, target);
+ return (T) value;
+ }
+
+}
\ No newline at end of file
diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultHttpClientConfigurationTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultHttpClientConfigurationTests.java
new file mode 100644
index 00000000..d3f7d04f
--- /dev/null
+++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultHttpClientConfigurationTests.java
@@ -0,0 +1,66 @@
+package org.springframework.cloud.commons.httpclient;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Ryan Baxter
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = MyApplication.class, properties = {"spring.cloud.httpclient.ok.enabled: true"})
+public class DefaultHttpClientConfigurationTests {
+ @Autowired
+ ApacheHttpClientFactory httpClientFactory;
+
+ @Autowired
+ ApacheHttpClientConnectionManagerFactory connectionManagerFactory;
+
+ @Autowired
+ OkHttpClientFactory okHttpClientFactory;
+
+ @Autowired
+ OkHttpClientConnectionPoolFactory okHttpClientConnectionPoolFactory;
+
+ @Test
+ public void connManFactory() throws Exception {
+ assertTrue(ApacheHttpClientConnectionManagerFactory.class
+ .isInstance(connectionManagerFactory));
+ assertTrue(DefaultApacheHttpClientConnectionManagerFactory.class
+ .isInstance(connectionManagerFactory));
+ }
+
+ @Test
+ public void apacheHttpClientFactory() throws Exception {
+ assertTrue(ApacheHttpClientFactory.class.isInstance(httpClientFactory));
+ assertTrue(DefaultApacheHttpClientFactory.class.isInstance(httpClientFactory));
+ }
+
+ @Test
+ public void connPoolFactory() throws Exception {
+ assertTrue(OkHttpClientConnectionPoolFactory.class.isInstance(okHttpClientConnectionPoolFactory));
+ assertTrue(DefaultOkHttpClientConnectionPoolFactory.class.isInstance(okHttpClientConnectionPoolFactory));
+ }
+
+ @Test
+ public void setOkHttpClientFactory() throws Exception {
+ assertTrue(OkHttpClientFactory.class.isInstance(okHttpClientFactory));
+ assertTrue(DefaultOkHttpClientFactory.class.isInstance(okHttpClientFactory));
+ }
+}
+
+@Configuration
+@EnableAutoConfiguration
+class MyApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(MyApplication.class, args);
+ }
+}
\ No newline at end of file
diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientConnectionPoolFactoryTest.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientConnectionPoolFactoryTest.java
new file mode 100644
index 00000000..a7658e3b
--- /dev/null
+++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientConnectionPoolFactoryTest.java
@@ -0,0 +1,34 @@
+package org.springframework.cloud.commons.httpclient;
+
+import okhttp3.ConnectionPool;
+
+import java.lang.reflect.Field;
+import java.util.concurrent.TimeUnit;
+import org.junit.Test;
+import org.springframework.util.ReflectionUtils;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Ryan Baxter
+ */
+public class DefaultOkHttpClientConnectionPoolFactoryTest {
+ @Test
+ public void create() throws Exception {
+ DefaultOkHttpClientConnectionPoolFactory connectionPoolFactory = new DefaultOkHttpClientConnectionPoolFactory();
+ ConnectionPool connectionPool = connectionPoolFactory.create(2,
+ 3, TimeUnit.MILLISECONDS);
+ int idleConnections = getField(connectionPool, "maxIdleConnections");
+ long keepAliveDuration = getField(connectionPool, "keepAliveDurationNs");
+ assertEquals(2, idleConnections);
+ assertEquals(TimeUnit.MILLISECONDS.toNanos(3), keepAliveDuration);
+ }
+
+ protected T getField(Object target, String name) {
+ Field field = ReflectionUtils.findField(target.getClass(), name);
+ ReflectionUtils.makeAccessible(field);
+ Object value = ReflectionUtils.getField(field, target);
+ return (T) value;
+ }
+
+}
\ No newline at end of file
diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientFactoryTest.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientFactoryTest.java
new file mode 100644
index 00000000..10c40516
--- /dev/null
+++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/commons/httpclient/DefaultOkHttpClientFactoryTest.java
@@ -0,0 +1,47 @@
+package org.springframework.cloud.commons.httpclient;
+
+import okhttp3.ConnectionPool;
+import okhttp3.OkHttpClient;
+
+import java.lang.reflect.Field;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.HostnameVerifier;
+import org.junit.Test;
+import org.springframework.util.ReflectionUtils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Ryan Baxter
+ */
+public class DefaultOkHttpClientFactoryTest {
+ @Test
+ public void create() throws Exception {
+ DefaultOkHttpClientFactory okHttpClientFactory = new DefaultOkHttpClientFactory();
+ DefaultOkHttpClientConnectionPoolFactory poolFactory = new DefaultOkHttpClientConnectionPoolFactory();
+ ConnectionPool pool = poolFactory.create(4, 5, TimeUnit.DAYS);
+ OkHttpClient httpClient = okHttpClientFactory.createBuilder(true).
+ connectTimeout(2, TimeUnit.MILLISECONDS).
+ readTimeout(3, TimeUnit.HOURS).
+ followRedirects(true).
+ connectionPool(pool).build();
+ int connectTimeout = getField(httpClient, "connectTimeout");
+ assertEquals(2, connectTimeout);
+ int readTimeout = getField(httpClient, "readTimeout");
+ assertEquals(TimeUnit.HOURS.toMillis(3), readTimeout);
+ boolean followRedirects = getField(httpClient, "followRedirects");
+ assertTrue(followRedirects);
+ ConnectionPool poolFromClient = getField(httpClient, "connectionPool");
+ assertEquals(pool, poolFromClient);
+ HostnameVerifier hostnameVerifier = getField(httpClient, "hostnameVerifier");
+ assertTrue(OkHttpClientFactory.TrustAllHostnames.class.isInstance(hostnameVerifier));
+ }
+
+ protected T getField(Object target, String name) {
+ Field field = ReflectionUtils.findField(target.getClass(), name);
+ ReflectionUtils.makeAccessible(field);
+ Object value = ReflectionUtils.getField(field, target);
+ return (T) value;
+ }
+}
\ No newline at end of file