OK HTTP client factory implementations

This commit is contained in:
Ryan Baxter
2017-06-29 15:11:27 -04:00
parent 473c278983
commit 787bea18e9
9 changed files with 332 additions and 3 deletions

View File

@@ -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);
}
}

View File

@@ -0,0 +1,57 @@
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 create(boolean disableSslValidation, long connectTimeout,
TimeUnit connectTimeoutUnit, boolean followRedirects, long readTimeout,
TimeUnit readTimeoutUnit, ConnectionPool connectionPool,
SSLSocketFactory sslSocketFactory, X509TrustManager x509TrustManager) {
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(connectTimeout, connectTimeoutUnit)
.followRedirects(followRedirects)
.readTimeout(readTimeout, readTimeoutUnit).connectionPool(connectionPool)
.connectionPool(connectionPool);
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);
}
}
if (sslSocketFactory != null && x509TrustManager != null) {
builder.sslSocketFactory(sslSocketFactory, x509TrustManager);
}
return builder.build();
}
}

View File

@@ -1,5 +1,8 @@
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;
@@ -27,4 +30,22 @@ public class HttpClientConfiguration {
return new DefaultApacheHttpClientFactory();
}
}
@Configuration
@ConditionalOnProperty(name = "spring.cloud.httpclient.ok.enabled")
@ConditionalOnClass(OkHttpClient.class)
static class OkHttpClientConfiguration {
@Bean
@ConditionalOnMissingBean
public OkHttpClientConnectionPoolFactory connPoolFactory() {
return new DefaultOkHttpClientConnectionPoolFactory();
}
@Bean
@ConditionalOnMissingBean
public OkHttpClientFactory okHttpClientFactory() {
return new DefaultOkHttpClientFactory();
}
}
}

View File

@@ -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);
}

View File

@@ -0,0 +1,65 @@
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 new {@link OkHttpClient}.
* @param disableSslValidation To disable SSL validation
* @param connectTimeout Connection timeout duration
* @param connectTimeoutUnit Connection timeout time unit
* @param followRedirects Whether to follow redirects
* @param readTimeout Read timeout duration
* @param readTimeoutUnit Read timeout time unit
* @param connectionPool The connection pool to use
* @param sslSocketFactory The socket factory to use, can be {@code null}
* @param x509TrustManager The trust manager to use, can be {@code null}
* @return A new {@link OkHttpClient}
*/
public OkHttpClient create(boolean disableSslValidation, long connectTimeout,
TimeUnit connectTimeoutUnit, boolean followRedirects, long readTimeout,
TimeUnit readTimeoutUnit, ConnectionPool connectionPool,
SSLSocketFactory sslSocketFactory, X509TrustManager x509TrustManager);
/**
* 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;
}
}
}

View File

@@ -1,6 +1,11 @@
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.config.RequestConfig;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
@@ -21,7 +26,7 @@ import static org.junit.Assert.assertTrue;
* @author Ryan Baxter
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = CustomApplication.class)
@SpringBootTest(classes = CustomApplication.class, properties = {"spring.cloud.httpclient.ok.enabled: true"})
public class CustomHttpClientConfigurationTests {
@Autowired
@@ -30,6 +35,12 @@ public class CustomHttpClientConfigurationTests {
@Autowired
ApacheHttpClientConnectionManagerFactory connectionManagerFactory;
@Autowired
OkHttpClientFactory okHttpClientFactory;
@Autowired
OkHttpClientConnectionPoolFactory okHttpClientConnectionPoolFactory;
@Test
public void connManFactory() throws Exception {
assertTrue(ApacheHttpClientConnectionManagerFactory.class
@@ -45,6 +56,18 @@ public class CustomHttpClientConfigurationTests {
.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
@@ -64,10 +87,20 @@ class CustomApplication {
}
@Bean
ApacheHttpClientConnectionManagerFactory connectionManagerFactory() {
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 {
@@ -90,4 +123,23 @@ class CustomApplication {
return null;
}
}
static class MyOkHttpClientFactory implements OkHttpClientFactory {
@Override
public OkHttpClient create(boolean disableSslValidation, long connectTimeout,
TimeUnit connectTimeoutUnit, boolean followRedirects, long readTimeout,
TimeUnit readTimeoutUnit, ConnectionPool connectionPool,
SSLSocketFactory sslSocketFactory, X509TrustManager x509TrustManager) {
return null;
}
}
static class MyOkHttpConnectionPoolFactory implements OkHttpClientConnectionPoolFactory {
@Override
public ConnectionPool create(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
return null;
}
}
}

View File

@@ -15,7 +15,7 @@ import static org.junit.Assert.*;
* @author Ryan Baxter
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MyApplication.class)
@SpringBootTest(classes = MyApplication.class, properties = {"spring.cloud.httpclient.ok.enabled: true"})
public class DefaultHttpClientConfigurationTests {
@Autowired
ApacheHttpClientFactory httpClientFactory;
@@ -23,6 +23,12 @@ public class DefaultHttpClientConfigurationTests {
@Autowired
ApacheHttpClientConnectionManagerFactory connectionManagerFactory;
@Autowired
OkHttpClientFactory okHttpClientFactory;
@Autowired
OkHttpClientConnectionPoolFactory okHttpClientConnectionPoolFactory;
@Test
public void connManFactory() throws Exception {
assertTrue(ApacheHttpClientConnectionManagerFactory.class
@@ -36,6 +42,18 @@ public class DefaultHttpClientConfigurationTests {
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

View File

@@ -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> 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;
}
}

View File

@@ -0,0 +1,44 @@
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.create(true, 2,
TimeUnit.MILLISECONDS, true, 3, TimeUnit.HOURS, pool, null, null);
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> 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;
}
}