Implementation of factories to provide Apache HTTP Clients
This commit is contained in:
@@ -35,6 +35,11 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<properties>
|
||||
<okhttp3.version>3.6.0</okhttp3.version>
|
||||
<apachehttpclient.version>4.5.1</apachehttpclient.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -102,6 +107,24 @@
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>${okhttp3.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>logging-interceptor</artifactId>
|
||||
<version>${okhttp3.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>${apachehttpclient.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
*
|
||||
* * 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;
|
||||
|
||||
/**
|
||||
* Factory for creating a new {@link CloseableHttpClient}.
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public interface ApacheHttpClientFactory {
|
||||
|
||||
/**
|
||||
* Creates a new {@link CloseableHttpClient}.
|
||||
* @param requestConfig Configuration to be used for all requests by default
|
||||
* @param connectionManager The {@link HttpClientConnectionManager} to use for the client
|
||||
* @return A new {@link CloseableHttpClient}
|
||||
*/
|
||||
public CloseableHttpClient createClient(RequestConfig requestConfig, HttpClientConnectionManager connectionManager);
|
||||
}
|
||||
@@ -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.<ConnectionSocketFactory> 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<ConnectionSocketFactory> registry = registryBuilder.build();
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(
|
||||
registry, null, null, null, timeToLive, timeUnit);
|
||||
connectionManager.setMaxTotal(maxTotalConnections);
|
||||
connectionManager.setDefaultMaxPerRoute(maxConnectionsPerRoute);
|
||||
|
||||
return connectionManager;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
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 {
|
||||
|
||||
public CloseableHttpClient createClient(RequestConfig requestConfig, HttpClientConnectionManager connectionManager) {
|
||||
return HttpClientBuilder.create().disableContentCompression()
|
||||
.setDefaultRequestConfig(requestConfig)
|
||||
.setConnectionManager(connectionManager)
|
||||
.disableCookieManagement()
|
||||
.useSystemProperties()
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.springframework.cloud.commons.httpclient;
|
||||
|
||||
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.httpclient.apache.enabled", matchIfMissing = true)
|
||||
static class ApacheHttpClientConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ApacheHttpClientConnectionManagerFactory connManFactory() {
|
||||
return new DefaultApacheHttpClientConnectionManagerFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ApacheHttpClientFactory apacheHttpClientFactory() {
|
||||
return new DefaultApacheHttpClientFactory();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,8 @@ org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration
|
||||
org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration,\
|
||||
org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration,\
|
||||
org.springframework.cloud.commons.util.UtilAutoConfiguration,\
|
||||
org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration
|
||||
org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration,\
|
||||
org.springframework.cloud.commons.httpclient.HttpClientConfiguration
|
||||
|
||||
|
||||
# Environment Post Processors
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package org.springframework.cloud.commons.httpclient;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
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.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)
|
||||
public class CustomHttpClientConfigurationTests {
|
||||
|
||||
@Autowired
|
||||
ApacheHttpClientFactory httpClientFactory;
|
||||
|
||||
@Autowired
|
||||
ApacheHttpClientConnectionManagerFactory connectionManagerFactory;
|
||||
|
||||
@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));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@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 ApacheHttpClientConnectionManagerFactory connectionManagerFactory() {
|
||||
return new MyApacheHttpClientConnectionManagerFactory();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class MyApacheHttpClientFactory implements ApacheHttpClientFactory {
|
||||
|
||||
@Override
|
||||
public CloseableHttpClient createClient(RequestConfig requestConfig, HttpClientConnectionManager connectionManager) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static class MyApacheHttpClientConnectionManagerFactory implements ApacheHttpClientConnectionManagerFactory {
|
||||
|
||||
@Override
|
||||
public HttpClientConnectionManager newConnectionManager(boolean disableSslValidation, int maxTotalConnections, int maxConnectionsPerRoute, long timeToLive, TimeUnit timeUnit, RegistryBuilder registryBuilder) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
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> 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;
|
||||
}
|
||||
}
|
||||
@@ -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.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().createClient(requestConfig,
|
||||
mock(HttpClientConnectionManager.class));
|
||||
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> 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
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)
|
||||
public class DefaultHttpClientConfigurationTests {
|
||||
@Autowired
|
||||
ApacheHttpClientFactory httpClientFactory;
|
||||
|
||||
@Autowired
|
||||
ApacheHttpClientConnectionManagerFactory connectionManagerFactory;
|
||||
|
||||
@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));
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
class MyApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MyApplication.class, args);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user